├── .github └── workflows │ └── rust.yml ├── .gitignore ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE.md ├── README.md ├── REFERENCE.md ├── alkyne.sublime-syntax ├── rust-toolchain.toml ├── rustfmt.toml └── src ├── eval ├── conformance │ ├── blocks.ak │ ├── bools.ak │ ├── conditionals.ak │ ├── fns.ak │ ├── lists.ak │ ├── loops.ak │ ├── mod.rs │ ├── must_pass.ak │ ├── nulls.ak │ ├── numbers.ak │ ├── objects.ak │ ├── strings.ak │ └── variables.ak ├── encode │ ├── alkyne.rs │ ├── json.rs │ └── mod.rs ├── error.rs ├── escaping.rs ├── mod.rs ├── stdlib.rs └── value │ ├── convert.rs │ ├── fns.rs │ ├── mod.rs │ ├── native_macros.rs │ ├── object.rs │ └── seq.rs ├── exec ├── fs.rs ├── mod.rs ├── parallel.rs └── repl.rs ├── main.rs └── syn ├── alkyne.pest ├── mod.rs └── parser.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Rust 16 | 17 | on: 18 | push: 19 | branches: [ main ] 20 | pull_request: 21 | branches: [ main ] 22 | 23 | env: 24 | CARGO_TERM_COLOR: always 25 | 26 | jobs: 27 | check_lints: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v2 31 | 32 | - name: Check format 33 | run: cargo fmt -- --check --files-with-diff 34 | - name: Check clippy lints 35 | run: cargo clippy --all-targets --verbose 36 | 37 | build_and_test: 38 | runs-on: ubuntu-latest 39 | steps: 40 | - uses: actions/checkout@v2 41 | 42 | - name: Build with default settings 43 | run: cargo build -v 44 | 45 | - name: Build docs 46 | run: cargo doc --verbose 47 | 48 | - name: Run tests 49 | run: cargo test --verbose 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code Reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [package] 16 | name = "alkyne" 17 | version = "0.1.0" 18 | edition = "2018" 19 | 20 | authors = ["Miguel Young "] 21 | description = "A simple scripting language for generating JSON blobs" 22 | homepage = "https://github.com/mcy/alkyne" 23 | repository = "https://github.com/mcy/alkyne" 24 | 25 | license = "Apache-2.0" 26 | 27 | [dependencies] 28 | crossbeam = "0.7.2" 29 | chashmap = "2.2.2" 30 | pest = "2.1.2" 31 | pest_derive = "2.1.0" 32 | rustc_version = "0.2.3" 33 | rustyline = "5.0.5" 34 | termion = "1.1.1" 35 | toolshed = "0.8.1" 36 | thread_local = "1.0.1" 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Alkyne Language 2 | 3 | Alkyne is a non-Turing-complete scripting language that can be used for generating 4 | JSON-like blobs. 5 | 6 | See the [REFERENCE.md](REFERENCE.md) for more information on language semantics. 7 | 8 | --- 9 | 10 | This is not an officially supported Google product. 11 | -------------------------------------------------------------------------------- /alkyne.sublime-syntax: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | %YAML 1.2 15 | --- 16 | name: Alkyne 17 | file_extensions: [ak] 18 | scope: source.alkyne 19 | contexts: 20 | main: 21 | # Double- and single-quoted strings. 22 | - match: '"' 23 | scope: punctuation.definition.string.double.begin.alkyne 24 | push: dstring 25 | - match: "'" 26 | scope: punctuation.definition.string.single.begin.alkyne 27 | push: sstring 28 | 29 | # Comments. 30 | - match: '//' 31 | scope: punctuation.definition.comment.alkyne 32 | push: line_comment 33 | - match: '/\*' 34 | scope: punctuation.definition.comment.alkyne 35 | push: block_comment 36 | 37 | # Syntactic control keywords. 38 | - match: 39 | '\b(break|continue|else|fn|for|if|in|let|return|switch|use|yield)\b' 40 | scope: keyword.control.alkyne 41 | 42 | # Simple operators. 43 | - match: '(!|%|&|\*|-|=|\+|<|>|\?|/)' 44 | scope: keyword.operator.alkyne 45 | # `oper` indentifiers. 46 | - match: '\boper(\+|-|\*|/|%|==|!)' 47 | scope: keyword.operator.alkyne 48 | 49 | # Constant keywords. 50 | - match: '\bnull\b' 51 | scope: constant.language.null.alkyne 52 | 53 | - match: '\bfalse\b' 54 | scope: constant.language.boolean.false.alkyne 55 | - match: '\btrue\b' 56 | scope: constant.language.boolean.true.alkyne 57 | 58 | - match: '\bself\b' 59 | scope: constant.language.self.alkyne 60 | - match: '\bsuper\b' 61 | scope: constant.language.super.alkyne 62 | 63 | - match: '\btop\b' 64 | scope: constant.language.top.alkyne 65 | - match: '\bhere\b' 66 | scope: constant.language.here.alkyne 67 | - match: '\bstd\b' 68 | scope: constant.language.std.alkyne 69 | 70 | # Numbers 71 | - match: '\b(-)?[0-9][0-9.]*\b' 72 | scope: constant.numeric.alkyne 73 | 74 | # Bracket matching. 75 | - match: '[(\[{]' 76 | scope: punctuation.definition.group.begin.alkyne 77 | push: bracket_match 78 | - match: '[)\]}]' 79 | scope: invalid.illegal.stray-bracket-end 80 | 81 | # Separators. 82 | - match: '(\.|,|:|;)' 83 | scope: punctuation.separator.alkyne 84 | 85 | # Function calls. 86 | - match: '(\w+)(?=\()' 87 | captures: 88 | 1: variable.function.alkyne 89 | 90 | bracket_match: 91 | - match: '[)\]}]' 92 | scope: punctuation.definition.group.end.alkyne 93 | pop: true 94 | - include: main 95 | 96 | string_escapes: 97 | - match: "\\[0ntrbf\\\"\']" 98 | scope: constant.character.escape.alkyne 99 | - match: '\\x[0-9a-fA-F]{2}' 100 | scope: constant.character.escape.alkyne 101 | - match: '\\u[0-9a-fA-F]{4}' 102 | scope: constant.character.escape.alkyne 103 | - match: '\\U[0-9a-fA-F]{8}' 104 | scope: constant.character.escape.alkyne 105 | - match: "\\[^0ntrbfxuU\\\"\']" 106 | scope: invalid.illegal.unknown-escape.alkyne 107 | string_format_specs: 108 | - match: '%[%dsqf]' 109 | scope: constant.character.other.alkyne 110 | dstring: 111 | - meta_scope: string.quoted.double.alkyne 112 | - include: string_escapes 113 | - match: '"' 114 | scope: punctuation.definition.string.double.end.alkyne 115 | pop: true 116 | sstring: 117 | - meta_scope: string.quoted.single.alkyne 118 | - include: string_escapes 119 | - match: "'" 120 | scope: punctuation.definition.string.single.end.alkyne 121 | pop: true 122 | 123 | line_comment: 124 | - meta_scope: comment.line.alkyne 125 | - match: $ 126 | pop: true 127 | block_comment: 128 | - meta_scope: comment.block.alkyne 129 | - match: '/\*' 130 | scope: punctuation.definition.comment.alkyne 131 | push: block_comment 132 | - match: '\*/' 133 | scope: punctuation.definition.comment.alkyne 134 | pop: true 135 | 136 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [toolchain] 16 | channel = "1.52.1" 17 | profile = "default" 18 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | max_width = 80 16 | tab_spaces = 2 17 | -------------------------------------------------------------------------------- /src/eval/conformance/blocks.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that block expressions and scopes work correctly. 16 | 17 | use t = "test_lib.ak"; 18 | 19 | let x = {5}; 20 | t.expect(x == 5); 21 | 22 | let x = { 23 | let y = x; 24 | { let y = 11; } 25 | y * 2 26 | }; 27 | t.expect(x == 10); 28 | t.expect_death(fn() y); 29 | 30 | let z = { 31 | let foo = 5; 32 | here 33 | }; 34 | t.expect(z.foo == 5); 35 | 36 | let baz = { 37 | let z = 7; 38 | top.z.foo 39 | }; 40 | t.expect(baz == 5); 41 | 42 | let none = { 43 | let x = 0; 44 | }; 45 | t.expect(none == null); 46 | 47 | let obj = {{}}; 48 | t.expect(obj != null); -------------------------------------------------------------------------------- /src/eval/conformance/bools.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that all valid bool operations work as expected. 16 | 17 | use t = "test_lib.ak"; 18 | 19 | t.expect(true == true); 20 | t.expect(false == false); 21 | t.expect(true != false); 22 | t.expect(false != true); 23 | 24 | t.expect(true == !false); 25 | t.expect(false == !true); 26 | 27 | t.expect((true || false) == true); 28 | t.expect((true && true) == true); 29 | t.expect((true && false) == false); 30 | t.expect((false || false) == false); 31 | 32 | t.expect((false && null) == false); 33 | t.expect((true || t.check(false)) == true); 34 | t.expect_death(fn() null && false); 35 | -------------------------------------------------------------------------------- /src/eval/conformance/conditionals.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that conditional expressions and scopes work correctly. 16 | 17 | use t = "test_lib.ak"; 18 | 19 | let x = if true { 42 }; 20 | t.expect(x == 42); 21 | 22 | let y = if false { 42 }; 23 | t.expect(y == null); 24 | 25 | let z = if false { 42 } else { 55 }; 26 | t.expect(z == 55); 27 | 28 | if true { null } else { 29 | t.fail() 30 | } 31 | 32 | if true { 33 | null 34 | } else if t.fail() { 35 | null 36 | } 37 | 38 | let foo = switch "foo" { 39 | "bar" -> 0, 40 | "foo", "baz" -> 1, 41 | }; 42 | t.expect(foo == 1); 43 | 44 | let bar = switch "bar" { 45 | "baz" -> 5, 46 | else -> 7, 47 | }; 48 | t.expect(bar == 7); 49 | 50 | let baz = switch "baz" { 51 | "baz" -> 5, 52 | "baz" -> 6, 53 | "baz" -> t.fail(), 54 | }; 55 | 56 | t.expect_death(fn() switch "foo" { 0 -> 0, }); 57 | -------------------------------------------------------------------------------- /src/eval/conformance/fns.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that all valid list operations work as expected. 16 | 17 | use t = "test_lib.ak"; 18 | 19 | fn one() = 1; 20 | t.expect(one() == 1); 21 | 22 | fn plus_5() = one() + 5; 23 | t.expect(plus_5() == 6); 24 | 25 | fn make_scope(n) { 26 | let x = n; 27 | here 28 | } 29 | let scope1 = make_scope(4); 30 | let scope2 = make_scope(2); 31 | t.expect(scope1.x == 4); 32 | t.expect(scope2.x == 2); 33 | 34 | let x = 0; 35 | fn get_x() = x; 36 | t.expect(get_x() == 0); 37 | let x = 1; 38 | t.expect(get_x() == 1); 39 | 40 | 41 | fn forever() = forever; 42 | forever()()()()()()()()()()(); 43 | 44 | fn recurse() = recurse(); 45 | t.expect_death(fn() recurse()); 46 | 47 | fn clever_recurse(f) = f(f); 48 | t.expect_death(fn() clever_recurse(clever_recurse)); 49 | 50 | fn sum(a, b, c) = a + b + c; 51 | 52 | t.expect(sum(1, 2, 3) == 6); 53 | t.expect_death(fn() sum(1)); 54 | 55 | fn maybe(cond) if cond { 56 | 5 57 | } else { 58 | t.fail() 59 | } 60 | t.expect(maybe(true) == 5); 61 | 62 | fn early_ret(cond) { 63 | if !cond { return 20 } 64 | t.fail() 65 | } 66 | early_ret(false); 67 | 68 | fn break_out() = break; 69 | fn cont_out() = continue; 70 | 71 | t.expect_death(fn() break_out()); 72 | t.expect_death(fn() cont_out()); 73 | 74 | let len = "foo" do { std.len(it) }; 75 | t.expect(len == 3); 76 | 77 | fn do_ret() { 78 | 42 do { return it + 5 } 79 | } 80 | t.expect(do_ret() == 47); 81 | 82 | let x = 5 do? if it == 5 { "foo" } else { "bar" }; 83 | t.expect(x == "foo"); 84 | 85 | null do? { t.fail() }; 86 | 87 | let obj = { 88 | bound: fn(x) self.member + x, 89 | member: 24, 90 | }; 91 | 92 | t.expect(obj.bound(5) == 29); 93 | let bound = obj.bound; 94 | t.expect(obj.bound(7) == 31); 95 | 96 | fn unbound(x) = self.member - x; 97 | t.expect(std.bind(unbound, obj)(4) == 20); 98 | -------------------------------------------------------------------------------- /src/eval/conformance/lists.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that all valid list operations work as expected. 16 | 17 | use t = "test_lib.ak"; 18 | 19 | t.expect([1, 2, 3][0] == 1); 20 | t.expect([1, 2, 3][1, 3][0] == 2); 21 | 22 | t.expect_death(fn() [1, 2, 3][-1]); 23 | t.expect_death(fn() [1, 2, 3][5]); 24 | t.expect_death(fn() [1, 2, 3][0.9]); 25 | t.expect_death(fn() [1, 2, 3][0, 1.3]); 26 | t.expect_death(fn() [1, 2, 3][0, 0, 0]); 27 | t.expect_death(fn() [1, 2, 3][]); 28 | t.expect_death(fn() [][]); 29 | 30 | t.expect(std.len([1, 2, 3]) == 3); 31 | t.expect(std.len([]) == 0); 32 | t.expect(std.len([[[[[]]]]][0]) == 1); 33 | 34 | t.expect_death(fn() [] == []); 35 | -------------------------------------------------------------------------------- /src/eval/conformance/loops.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that loops and comprehensions work as expected. 16 | 17 | use t = "test_lib.ak"; 18 | 19 | let xs = for i in std.range(0, 5) yield i * 2; 20 | t.expect(xs[2] == 4); 21 | t.expect(xs[4] == 8); 22 | 23 | let xs_map = for i, v in xs yield [std.fmt('%d', [i])]: v; 24 | t.expect(xs_map."3" == 6); 25 | 26 | let obj = { abc: 0, xyz: 5 }; 27 | let obj2 = for k, v, _ in obj { 28 | let key = k + k[0]; 29 | yield [key]: k; 30 | }; 31 | t.expect(obj2.xyzx == "xyz"); 32 | 33 | let keys = for k, _, _ in obj { z: 4 } yield k; 34 | t.expect(std.len(keys) == 3); 35 | 36 | let chopped = for i, c in "hello!" { 37 | if c == "l" { continue } 38 | yield std.fmt('%d%s', [i, c]); 39 | }; 40 | t.expect(std.concat(chopped) == "0h1e4o5!"); 41 | 42 | let early = for _, v in ["a", "b"] { 43 | if v == "b" { 44 | break 45 | } 46 | yield [v]: v; 47 | }; 48 | t.expect(early?.a == "a"); 49 | t.expect(early?.b == null); 50 | 51 | let no_yield = for _ in std.range(0, 5) { 42 }; 52 | t.expect(no_yield == null); 53 | 54 | for _ in std.range(0, 0) { 55 | t.fail() 56 | } 57 | 58 | fn return_in_loop(xs) for _, x in xs { 59 | if x == 10 { 60 | return 42 61 | } 62 | yield 0; 63 | } 64 | 65 | t.expect(return_in_loop([1, 2, 4, 10, 6]) == 42); 66 | t.expect(return_in_loop([1, 2, 3])[1] == 0); -------------------------------------------------------------------------------- /src/eval/conformance/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Conformance tests for the interpreter. 16 | //! 17 | //! A conformance test consists of a Alkyne file; for each conformance conformance, the 18 | //! file is parsed, loaded into the interpreter, and executed. 19 | //! 20 | //! Tests should import the magic "test lib" file to get access to assertions. 21 | //! This can be done with 22 | //! ``` 23 | //! use t = "test_lib.ak"; 24 | //! ``` 25 | 26 | use std::cell::Cell; 27 | 28 | use crate::eval; 29 | use crate::eval::value::Object; 30 | use crate::eval::value::Value; 31 | use crate::syn; 32 | 33 | const TEST_LIB: &str = "test_lib.ak"; 34 | 35 | /// Macro for generating conformance tests. 36 | macro_rules! conf_test { 37 | ($test_name:ident) => { 38 | #[test] 39 | fn $test_name() { 40 | conformance_test( 41 | concat!(stringify!($test_name), ".ak"), 42 | include_str!(concat!(stringify!($test_name), ".ak")), 43 | ) 44 | } 45 | }; 46 | } 47 | 48 | thread_local! { 49 | static FAILURE_FLAG: Cell = Cell::new(false); 50 | } 51 | 52 | fn test_lib<'a>() -> Value<'a> { 53 | let test_lib = Object::new(); 54 | 55 | test_lib.define( 56 | "fail", 57 | native_fn!((ctx) { 58 | FAILURE_FLAG.with(|f| f.set(true)); 59 | native_err_no_return!(ctx, "unconditional failure") 60 | }), 61 | false, 62 | ); 63 | 64 | test_lib.define( 65 | "expect", 66 | native_fn!((ctx, cond: bool) { 67 | if !cond { 68 | FAILURE_FLAG.with(|f| f.set(true)); 69 | native_err_no_return!(ctx, "assertion failed") 70 | } 71 | }), 72 | false, 73 | ); 74 | 75 | test_lib.define( 76 | "assert", 77 | native_fn!((ctx, cond: bool) { 78 | if !cond { 79 | native_err!(ctx, "assertion failed") 80 | } 81 | }), 82 | false, 83 | ); 84 | 85 | test_lib.define( 86 | "expect_death", 87 | native_fn!((ctx, body: fn) { 88 | if ctx.call(native_span!(), body, vec![]).is_ok() { 89 | FAILURE_FLAG.with(|f| f.set(true)); 90 | native_err_no_return!(ctx, "body failed to die") 91 | } 92 | }), 93 | false, 94 | ); 95 | 96 | Value::Object(test_lib) 97 | } 98 | 99 | /// Basic fixture for all conformance conformance. 100 | fn conformance_test(name: &'static str, text: &'static str) { 101 | FAILURE_FLAG.with(|f| f.set(false)); 102 | 103 | let arena = syn::Arena::new(); 104 | let ast = match syn::parse(name.as_ref(), text, &arena) { 105 | Ok(ast) => ast, 106 | Err(e) => { 107 | eprintln!("{}", e); 108 | panic!("failed to parse testcase") 109 | } 110 | }; 111 | 112 | let mut ctx = eval::Context::new(); 113 | let result = ctx.eval(&ast, |file| match file { 114 | TEST_LIB => Some(test_lib()), 115 | _ => None, 116 | }); 117 | 118 | if let Err(e) = result { 119 | eprintln!("{}", e); 120 | FAILURE_FLAG.with(|f| f.set(true)); 121 | } 122 | 123 | if FAILURE_FLAG.with(|f| f.get()) { 124 | panic!("unexpected failure") 125 | } 126 | } 127 | 128 | conf_test!(must_pass); 129 | 130 | conf_test!(variables); 131 | conf_test!(fns); 132 | conf_test!(blocks); 133 | conf_test!(conditionals); 134 | conf_test!(loops); 135 | 136 | conf_test!(nulls); 137 | conf_test!(bools); 138 | conf_test!(numbers); 139 | conf_test!(strings); 140 | conf_test!(lists); 141 | conf_test!(objects); 142 | -------------------------------------------------------------------------------- /src/eval/conformance/must_pass.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // This test is a sanity test for the testing system; 16 | // it does nothing interesting and should always pass. 17 | 18 | use t = "test_lib.ak"; 19 | 20 | t.expect(true); 21 | t.assert(true); 22 | t.expect_death(fn() t.assert(false)); 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/eval/conformance/nulls.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that all valid null-value operations work as expected. 16 | 17 | use t = "test_lib.ak"; 18 | 19 | t.expect(null == null); 20 | t.expect(null != 0); 21 | t.expect(false != null); 22 | t.expect("null" != null); 23 | t.expect([null] != null); 24 | t.expect(null != {}); 25 | 26 | t.expect(null ?? 0 == 0); 27 | t.expect(null ?? 1 ?? 0 == 1); 28 | t.expect("a" ?? "b" == "a"); 29 | -------------------------------------------------------------------------------- /src/eval/conformance/numbers.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that number operations work correctly. 16 | // We don't go too deeply into float testing, though. 17 | 18 | use t = "test_lib.ak"; 19 | 20 | t.expect(0 == 0.0); 21 | t.expect(1 != 0); 22 | 23 | t.expect(1 + 1 == 2); 24 | t.expect(1 - 3 == -2); 25 | t.expect(5 * 3 == 15); 26 | t.expect(4 / 2 == 2); 27 | t.expect(5 / 2 == 2.5); 28 | 29 | t.expect(6 % 4 == 2); 30 | t.expect(-1 % 4 == 3); 31 | 32 | t.expect(1/0 == 2/0); 33 | t.expect(1/0 != (-2)/0); 34 | 35 | let nan = 0/0; 36 | t.expect(nan != nan); 37 | 38 | t.expect(0 < 1); 39 | t.expect(-1 > -3); 40 | t.expect(-4 <= -4); 41 | t.expect(!(-4 < -4)); 42 | -------------------------------------------------------------------------------- /src/eval/conformance/objects.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that all valid objct operations work as expected. 16 | 17 | use t = "test_lib.ak"; 18 | 19 | let obj = { 20 | foo: 0, 21 | "bar": "abc", 22 | 'baz': [1, 2], 23 | ['fu' + 'nc']: fn() self['baz'], 24 | "self": fn() self, 25 | }; 26 | 27 | t.expect(obj.foo == 0); 28 | t.expect(obj["bar"] == "abc"); 29 | t.expect(obj.'baz'[1] == 2); 30 | t.expect(obj.func()[0] == 1); 31 | t.expect(obj.'self'()."self"()['foo'] == 0); 32 | 33 | t.expect_death(fn() obj.missing); 34 | t.expect_death(fn() obj['nope']); 35 | 36 | 37 | let proto = { a: 1, b: 2, c: 3 }; 38 | let sub = proto { a: 2, d: 4 }; 39 | 40 | t.expect(proto.a == 1); 41 | t.expect(sub.a == 2); 42 | t.expect(proto.c == 3); 43 | t.expect(sub.c == 3); 44 | t.expect_death(fn() proto.d); 45 | t.expect(sub.d == 4); 46 | 47 | 48 | let proto = { foo: { a: 1, b: 'c' } }; 49 | let sub = proto { super.foo: { a: 'z' } }; 50 | 51 | t.expect(proto.foo.a == 1); 52 | t.expect(sub.foo.a == 'z'); 53 | t.expect(proto.foo.b == 'c'); 54 | t.expect(sub.foo.b == 'c'); 55 | 56 | 57 | let obj = { a: 'a', e: 5 }; 58 | t.expect(obj?.a == 'a'); 59 | t.expect(obj?.b == null); 60 | t.expect(obj?['a'] == 'a'); 61 | t.expect(obj?['b'] == null); 62 | t.expect(obj?.a?.b == null); 63 | 64 | let sub = obj { 65 | a?: super?.b, 66 | b: super?.c, 67 | c?: 0, 68 | d?: null, 69 | e: null, 70 | }; 71 | 72 | t.expect(sub?.a == 'a'); 73 | t.expect(sub.a == 'a'); 74 | 75 | t.expect(sub?.b == null); 76 | t.expect(sub.b == null); 77 | 78 | t.expect(sub?.c == 0); 79 | t.expect(sub.c == 0); 80 | 81 | t.expect(sub?.d == null); 82 | t.expect_death(fn() sub.d); 83 | 84 | t.expect(obj?.e == 5); 85 | t.expect(sub?.e == null); 86 | t.expect(sub.e == null); 87 | 88 | let obj = { a: {} }; 89 | 90 | let sub = obj { 91 | super?.a: { x: 5 }, 92 | super?.b: { x: 5 }, 93 | }; 94 | t.expect(sub?.a?.x == 5); 95 | t.expect(sub?.b?.x == null); 96 | 97 | let add = { 98 | val: 1, 99 | oper+: fn(that) self { val: self.val + that.val }, 100 | }; 101 | 102 | t.expect((add + add + add).val == 3); 103 | 104 | t.expect_death(fn() {} == {}); 105 | -------------------------------------------------------------------------------- /src/eval/conformance/strings.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that string operations work correctly. 16 | 17 | use t = "test_lib.ak"; 18 | 19 | t.expect("abc" == "abc"); 20 | t.expect("abc" != "abd"); 21 | t.expect("\n" == " 22 | "); 23 | t.expect('"' == "\""); 24 | t.expect("'" == '\''); 25 | t.expect("\x20" == " "); 26 | t.expect("\u6587" == "文"); 27 | t.expect("\U0001F914" == "🤔"); 28 | 29 | t.expect("" == ""); 30 | t.expect("abc" + "" == "abc"); 31 | t.expect("ab" + "abc" == "ababc"); 32 | 33 | t.expect("abcdef"[3] == "d"); 34 | t.expect("abcdef"[2, 4] == "cd"); 35 | t.expect("abcdef"[5, 5] == ""); 36 | t.expect("abcdef"[0] == "a"); 37 | 38 | t.expect_death(fn() "abc"[-1]); 39 | t.expect_death(fn() "abc"[5]); 40 | t.expect_death(fn() "abc"[0.9]); 41 | t.expect_death(fn() "abc"[0, 1.3]); 42 | t.expect_death(fn() "abc"[0, 0, 0]); 43 | t.expect_death(fn() "abc"[]); 44 | 45 | t.expect(std.len("") == 0); 46 | t.expect(std.len("abc") == 3); -------------------------------------------------------------------------------- /src/eval/conformance/variables.ak: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Check that variable references and pattern matching work correctly. 16 | 17 | use t = "test_lib.ak"; 18 | 19 | let x = 0; 20 | t.expect(x == 0); 21 | 22 | let y = 1; 23 | t.expect(x == 0); 24 | t.expect(y == 1); 25 | 26 | let x = 2; 27 | t.expect(x == 2); 28 | t.expect(y == 1); 29 | 30 | t.expect_death(fn() z); 31 | t.expect_death(fn() { let x = z; }); 32 | 33 | let _ = 0; 34 | t.expect_death(fn() _); 35 | 36 | let null = null; 37 | let "let" = "let"; 38 | t.expect_death(fn() { let "let" = "lett"; }); 39 | 40 | let [] = []; 41 | 42 | let [a, b, _] = [1, 2, 3]; 43 | t.expect(a == 1); 44 | 45 | let [x, y, ..] = [1, 2]; 46 | t.expect(x == 1); 47 | 48 | let [x, y, ..] = [3, 4, 5]; 49 | t.expect(x == 3); 50 | 51 | let [x, y, ..zs] = [6, 7, 8, 9]; 52 | t.expect(x == 6); 53 | t.expect(zs[1] == 9); 54 | 55 | let [x, .., y] = ["10", "11", "12", "13"]; 56 | t.expect(y == "13"); 57 | 58 | let [c] = [0]; 59 | t.expect(c == 0); 60 | 61 | t.expect_death(fn() { let [x, y] = 0; }); 62 | t.expect_death(fn() { let [x, y] = [0]; }); 63 | t.expect_death(fn() { let [x, x] = [0, 0]; }); 64 | 65 | let {} = {}; 66 | let {..} = {}; 67 | 68 | let { x } = { x: 5 }; 69 | t.expect(x == 5); 70 | 71 | let { no_match: _ } = { no_match: 20 }; 72 | t.expect_death(fn() no_match); 73 | 74 | t.expect_death(fn() { let { x } = { x: 4, y: 5 }; }); 75 | 76 | let { list: [first, ..] } = { list: [5, 4, 3] }; 77 | t.expect(first == 5); 78 | t.expect_death(fn() list); 79 | 80 | let { maybe? } = { maybe: 5 }; 81 | t.expect(maybe == 5); 82 | 83 | let { maybe? } = {}; 84 | t.expect(maybe == null); 85 | 86 | let { x0, .. } = { x0: 7, x1: 77, x2: 777 }; 87 | t.expect(x0 == 7); 88 | t.expect_death(fn() x2); 89 | 90 | let { "quoted key" } = { "quoted key": "q" }; 91 | t.expect_death(fn() here."quoted key" ); 92 | 93 | let { "quoted key": q } = { "quoted key": "q" }; 94 | t.expect(q == "q"); 95 | 96 | let { "quoted_id" } = { quoted_id: "qq" }; 97 | t.expect(quoted_id == "qq"); -------------------------------------------------------------------------------- /src/eval/encode/alkyne.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Alkyne-like encoding for Alkyne values. 16 | 17 | use std::collections::HashSet; 18 | 19 | use crate::eval::encode::Context; 20 | use crate::eval::encode::Encode; 21 | use crate::eval::encode::EncodeError; 22 | use crate::eval::encode::PathComponent; 23 | use crate::eval::escaping; 24 | use crate::eval::value::DebugInfo; 25 | use crate::eval::value::Value; 26 | 27 | #[derive(Default)] 28 | pub struct UclEncoding(()); 29 | 30 | impl<'i> Encode<'i> for UclEncoding { 31 | fn encode( 32 | mut ctx: Context, 33 | val: &Value<'i>, 34 | ) -> Result<(), EncodeError<'i>> { 35 | use std::fmt::Write; 36 | match val { 37 | Value::Null => { 38 | let _ = write!(ctx.buf(), "null"); 39 | } 40 | Value::Bool(b) => { 41 | let _ = write!(ctx.buf(), "{}", b); 42 | } 43 | Value::Number(n) => { 44 | let _ = write!(ctx.buf(), "{}", n); 45 | } 46 | Value::String(s) => { 47 | escaping::escape_alkyne_string(s.as_ref(), false, ctx.buf()); 48 | } 49 | Value::List(val) => { 50 | let _ = write!(ctx.buf(), "["); 51 | for (i, v) in val.iter().enumerate() { 52 | if i != 0 { 53 | let _ = write!(ctx.buf(), ", "); 54 | } 55 | match ctx.recurse(PathComponent::Index(i), v) { 56 | Ok(()) => {} 57 | Err(EncodeError::Cycle { .. }) => { 58 | let _ = write!(ctx.buf(), ""); 59 | } 60 | e @ Err(_) => return e, 61 | } 62 | } 63 | let _ = write!(ctx.buf(), "]"); 64 | } 65 | Value::Object(val) => { 66 | let mut first = true; 67 | let _ = write!(ctx.buf(), "{{"); 68 | // Ensure that we only encode the *latest* version of a particular 69 | // key. 70 | let mut encoded_keys = HashSet::new(); 71 | for (k, v, _) in val.iter() { 72 | if encoded_keys.contains(&*k) { 73 | continue; 74 | } 75 | encoded_keys.insert(&*k); 76 | 77 | if !first { 78 | let _ = write!(ctx.buf(), ", "); 79 | } else { 80 | let _ = write!(ctx.buf(), " "); 81 | } 82 | first = false; 83 | 84 | escaping::escape_alkyne_string(k, true, ctx.buf()); 85 | let _ = write!(ctx.buf(), ": "); 86 | match ctx.recurse(PathComponent::Ident(k.to_string()), v) { 87 | Ok(()) => {} 88 | Err(EncodeError::Cycle { .. }) => { 89 | let _ = write!(ctx.buf(), ""); 90 | } 91 | e @ Err(_) => return e, 92 | } 93 | } 94 | if !first { 95 | let _ = write!(ctx.buf(), " "); 96 | } 97 | let _ = write!(ctx.buf(), "}}"); 98 | } 99 | Value::Fn(fnc) => { 100 | let _ = write!(ctx.buf(), "{}", fnc); 101 | } 102 | Value::NativeFn(fnc) => { 103 | let _ = write!(ctx.buf(), "{}", fnc); 104 | } 105 | Value::Std => { 106 | let _ = write!(ctx.buf(), ""); 107 | } 108 | _ => { 109 | let _ = write!(ctx.buf(), ""); 110 | } 111 | } 112 | Ok(()) 113 | } 114 | } 115 | 116 | #[derive(Default)] 117 | pub struct VerboseEncoding { 118 | indent: usize, 119 | } 120 | 121 | impl<'i> Encode<'i> for VerboseEncoding { 122 | fn encode( 123 | mut ctx: Context, 124 | val: &Value<'i>, 125 | ) -> Result<(), EncodeError<'i>> { 126 | use std::fmt::Write; 127 | match val { 128 | Value::Null => { 129 | let _ = write!(ctx.buf(), "{}null", ().debug_info()); 130 | } 131 | Value::Bool(b) => { 132 | let _ = write!(ctx.buf(), "{}{}", b.debug_info(), b); 133 | } 134 | Value::Number(n) => { 135 | let _ = write!(ctx.buf(), "{}{}", n.debug_info(), n); 136 | } 137 | Value::String(s) => { 138 | let _ = write!(ctx.buf(), "{}", s.debug_info()); 139 | escaping::escape_alkyne_string(s.as_ref(), false, ctx.buf()); 140 | } 141 | Value::List(val) => { 142 | let _ = write!(ctx.buf(), "{}", val.debug_info()); 143 | let _ = write!(ctx.buf(), "["); 144 | 145 | let indent = " ".repeat(ctx.indent); 146 | ctx.indent += 1; 147 | 148 | for (i, v) in val.iter().enumerate() { 149 | if i == 0 { 150 | let _ = writeln!(ctx.buf()); 151 | } 152 | let _ = write!(ctx.buf(), "{} [{}]: ", indent, i); 153 | match ctx.recurse(PathComponent::Index(i), v) { 154 | Ok(()) => {} 155 | Err(EncodeError::Cycle { .. }) => { 156 | let _ = write!(ctx.buf(), ""); 157 | } 158 | e @ Err(_) => return e, 159 | } 160 | 161 | let _ = writeln!(ctx.buf(), ","); 162 | } 163 | 164 | if !val.is_empty() { 165 | let _ = write!(ctx.buf(), "{}", indent); 166 | } 167 | 168 | ctx.indent -= 1; 169 | let _ = write!(ctx.buf(), "]"); 170 | } 171 | Value::Object(val) => { 172 | let _ = write!(ctx.buf(), "{}", val.debug_info()); 173 | let _ = write!(ctx.buf(), "{{"); 174 | let indent = " ".repeat(ctx.indent); 175 | ctx.indent += 1; 176 | 177 | let mut looped = false; 178 | for (k, v, o) in val.iter() { 179 | looped = true; 180 | let _ = writeln!(ctx.buf()); 181 | 182 | let _ = write!(ctx.buf(), "{} key: {}", indent, k.debug_info()); 183 | escaping::escape_alkyne_string(k, false, ctx.buf()); 184 | let _ = writeln!(ctx.buf(), ","); 185 | 186 | let _ = write!(ctx.buf(), "{} val: ", indent); 187 | match ctx.recurse(PathComponent::Ident(k.to_string()), v) { 188 | Ok(()) => {} 189 | Err(EncodeError::Cycle { .. }) => { 190 | let _ = write!(ctx.buf(), ""); 191 | } 192 | e @ Err(_) => return e, 193 | } 194 | let _ = writeln!(ctx.buf(), ","); 195 | 196 | let _ = 197 | writeln!(ctx.buf(), "{} own: {}{{..}},", indent, o.debug_info()); 198 | } 199 | 200 | ctx.indent -= 1; 201 | if looped { 202 | let _ = write!(ctx.buf(), "{}", indent); 203 | } 204 | let _ = write!(ctx.buf(), "}}"); 205 | } 206 | Value::Fn(fnc) => { 207 | let indent = " ".repeat(ctx.indent); 208 | let _ = writeln!(ctx.buf(), "<{}>{{", fnc); 209 | ctx.indent += 1; 210 | 211 | let _ = writeln!(ctx.buf(), "{} src: {:p},", indent, fnc.src()); 212 | 213 | let _ = write!(ctx.buf(), "{} name: ", indent); 214 | match ctx.recurse( 215 | PathComponent::Ident("name".to_string()), 216 | &fnc.name().map(Value::String).unwrap_or(Value::Null), 217 | ) { 218 | Ok(()) => {} 219 | Err(EncodeError::Cycle { .. }) => { 220 | let _ = write!(ctx.buf(), ""); 221 | } 222 | e @ Err(_) => return e, 223 | } 224 | let _ = writeln!(ctx.buf(), ","); 225 | 226 | let _ = write!(ctx.buf(), "{} self: ", indent); 227 | match ctx.recurse( 228 | PathComponent::Ident("self".to_string()), 229 | &fnc.referent().map(Value::Object).unwrap_or(Value::Null), 230 | ) { 231 | Ok(()) => {} 232 | Err(EncodeError::Cycle { .. }) => { 233 | let _ = write!(ctx.buf(), ""); 234 | } 235 | e @ Err(_) => return e, 236 | } 237 | let _ = writeln!(ctx.buf(), ","); 238 | 239 | let _ = write!(ctx.buf(), "{} here: ", indent); 240 | match ctx.recurse( 241 | PathComponent::Ident("here".to_string()), 242 | &Value::Object(fnc.captures()), 243 | ) { 244 | Ok(()) => {} 245 | Err(EncodeError::Cycle { .. }) => { 246 | let _ = write!(ctx.buf(), ""); 247 | } 248 | e @ Err(_) => return e, 249 | } 250 | let _ = writeln!(ctx.buf(), ","); 251 | 252 | ctx.indent -= 1; 253 | let _ = write!(ctx.buf(), "{}}}", indent); 254 | } 255 | Value::NativeFn(fnc) => { 256 | let indent = " ".repeat(ctx.indent); 257 | let _ = writeln!(ctx.buf(), "<{}>{{", fnc); 258 | ctx.indent += 1; 259 | 260 | let _ = writeln!(ctx.buf(), "{} src: {:#x},", indent, fnc.ptr_value()); 261 | 262 | let _ = write!(ctx.buf(), "{} name: ", indent); 263 | match ctx.recurse( 264 | PathComponent::Ident("name".to_string()), 265 | &fnc.name().map(Value::String).unwrap_or(Value::Null), 266 | ) { 267 | Ok(()) => {} 268 | Err(EncodeError::Cycle { .. }) => { 269 | let _ = write!(ctx.buf(), ""); 270 | } 271 | e @ Err(_) => return e, 272 | } 273 | let _ = writeln!(ctx.buf(), ","); 274 | 275 | ctx.indent -= 1; 276 | let _ = write!(ctx.buf(), "{}}}", indent); 277 | } 278 | Value::Std => { 279 | let _ = write!(ctx.buf(), ""); 280 | } 281 | _ => { 282 | let _ = write!(ctx.buf(), ""); 283 | } 284 | } 285 | Ok(()) 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /src/eval/encode/json.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Json encoding for Alkyne values. 16 | 17 | use std::collections::HashSet; 18 | 19 | use crate::eval::encode::Context; 20 | use crate::eval::encode::Encode; 21 | use crate::eval::encode::EncodeError; 22 | use crate::eval::encode::PathComponent; 23 | use crate::eval::escaping; 24 | use crate::eval::value::Value; 25 | 26 | #[derive(Default)] 27 | pub struct JsonEncoding(()); 28 | 29 | impl<'i> Encode<'i> for JsonEncoding { 30 | fn encode( 31 | mut ctx: Context, 32 | val: &Value<'i>, 33 | ) -> Result<(), EncodeError<'i>> { 34 | use std::fmt::Write; 35 | match val { 36 | Value::Null => { 37 | let _ = write!(ctx.buf(), "null"); 38 | } 39 | Value::Bool(b) => { 40 | let _ = write!(ctx.buf(), "{}", b); 41 | } 42 | Value::Number(n) => { 43 | let _ = write!(ctx.buf(), "{}", n); 44 | } 45 | Value::String(s) => { 46 | escaping::escape_json_string(s.as_ref(), ctx.buf()); 47 | } 48 | Value::List(val) => { 49 | let _ = write!(ctx.buf(), "["); 50 | for (i, v) in val.iter().enumerate() { 51 | if i != 0 { 52 | let _ = write!(ctx.buf(), ","); 53 | } 54 | ctx.recurse(PathComponent::Index(i), v)? 55 | } 56 | let _ = write!(ctx.buf(), "]"); 57 | } 58 | Value::Object(val) => { 59 | let mut first = true; 60 | let _ = write!(ctx.buf(), "{{"); 61 | // Ensure that we only encode the *latest* version of a particular 62 | // key. 63 | let mut encoded_keys = HashSet::new(); 64 | for (k, v, _) in val.iter() { 65 | if encoded_keys.contains(&*k) { 66 | continue; 67 | } 68 | encoded_keys.insert(&*k); 69 | 70 | if !first { 71 | let _ = write!(ctx.buf(), ","); 72 | } 73 | first = false; 74 | 75 | escaping::escape_json_string(k, &mut ctx.buf()); 76 | let _ = write!(ctx.buf(), ":"); 77 | ctx.recurse(PathComponent::Ident(k.to_string()), v)? 78 | } 79 | let _ = write!(ctx.buf(), "}}"); 80 | } 81 | v => { 82 | return Err(EncodeError::BadType { 83 | bad_type: v.ty(), 84 | path: ctx.path().to_vec(), 85 | }) 86 | } 87 | } 88 | Ok(()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/eval/encode/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Encoding functions for JSON. 16 | 17 | use std::collections::HashSet; 18 | use std::fmt; 19 | use std::ops::Deref; 20 | use std::ops::DerefMut; 21 | 22 | use crate::eval::error::EvalError; 23 | use crate::eval::escaping; 24 | use crate::eval::stdlib::ENCODE_AS; 25 | use crate::eval::value::Type; 26 | use crate::eval::value::Value; 27 | use crate::eval::Context as UclContext; 28 | use crate::eval::ControlFlow; 29 | use crate::syn::Span; 30 | 31 | pub mod alkyne; 32 | pub mod json; 33 | 34 | /// `Encode` represents an encoding of Alkyne values. Types which implement 35 | /// `Encode` can be used to create a textual representation of a Alkyne value. 36 | pub trait Encode<'i>: Sized { 37 | /// Convert `val` into text. 38 | /// 39 | /// The `Context` value can be used to access `self`, as well as to recurse 40 | /// further into the Alkyne value tree. 41 | fn encode(ctx: Context, val: &Value<'i>) 42 | -> Result<(), EncodeError<'i>>; 43 | } 44 | 45 | /// An `Encoder` gradually constructs a textual representation of a Alkyne value. 46 | /// 47 | /// The type parameter `E` represents the choice of encoding. 48 | pub struct Encoder { 49 | buf: String, 50 | cycle_detector: HashSet, 51 | path: Vec, 52 | encode: E, 53 | } 54 | 55 | /// A `Context` is an encoding context, granting limited access to the inner 56 | /// state of an `Encoder`. 57 | /// 58 | /// It is also a smart pointer over `E`, so fields and methods of `E` can be 59 | /// accessed directly in the `Encode::encode()` implementation. 60 | pub struct Context<'a, E>(&'a mut Encoder); 61 | 62 | impl<'i, 'a, E: Encode<'i>> Context<'a, E> { 63 | /// Returns the buffer to which text should be written to. 64 | pub fn buf(&mut self) -> &mut String { 65 | &mut self.0.buf 66 | } 67 | 68 | /// Returns the current path down the Alkyne tree. 69 | pub fn path(&self) -> &[PathComponent] { 70 | &self.0.path[..] 71 | } 72 | 73 | /// Recurse the encoding process, encoding `val` at the given `key`. 74 | /// 75 | /// This function should be called instead of doing manual recursion in 76 | /// `Encode::encode`, since it will automatically handle cycles and 77 | /// `std.EncodeAs` functions. 78 | pub fn recurse( 79 | &mut self, 80 | key: PathComponent, 81 | val: &Value<'i>, 82 | ) -> Result<(), EncodeError<'i>> { 83 | self.0.with_path(key, |this| this.subencode(val)) 84 | } 85 | } 86 | 87 | impl Deref for Context<'_, E> { 88 | type Target = E; 89 | 90 | fn deref(&self) -> &E { 91 | &self.0.encode 92 | } 93 | } 94 | 95 | impl DerefMut for Context<'_, E> { 96 | fn deref_mut(&mut self) -> &mut E { 97 | &mut self.0.encode 98 | } 99 | } 100 | 101 | /// A `PathComponent` is used to track the path down a Alkyne value, consisting 102 | /// of the keys and indices of objects and lists. 103 | #[derive(Clone, Debug)] 104 | pub enum PathComponent { 105 | Index(usize), 106 | Ident(String), 107 | } 108 | 109 | impl<'i, E: Encode<'i> + Default> Encoder { 110 | /// Creates a new `Encoder`, with the given encoding. 111 | pub fn new() -> Self { 112 | Self::with(E::default()) 113 | } 114 | } 115 | 116 | impl<'i, E: Encode<'i>> Encoder { 117 | /// Creates a new `Encoder` with the given encoding value. 118 | pub fn with(encode: E) -> Self { 119 | Encoder { 120 | buf: String::new(), 121 | cycle_detector: HashSet::new(), 122 | path: Vec::new(), 123 | encode, 124 | } 125 | } 126 | 127 | /// Encode the given value, consuming this encoder. 128 | pub fn encode(mut self, val: &Value<'i>) -> Result> { 129 | self.subencode(val)?; 130 | Ok(self.buf) 131 | } 132 | 133 | /// Call `f`, but with `ptr` marked as "visited". This function is used 134 | /// for cycle detection. 135 | #[inline] 136 | fn with_indirection( 137 | &mut self, 138 | ptr: usize, 139 | f: impl FnOnce(&mut Self) -> Result<(), EncodeError<'i>>, 140 | ) -> Result<(), EncodeError<'i>> { 141 | if self.cycle_detector.contains(&ptr) { 142 | return Err(EncodeError::Cycle { 143 | path: self.path.clone(), 144 | }); 145 | } 146 | self.cycle_detector.insert(ptr); 147 | let res = f(self); 148 | self.cycle_detector.remove(&ptr); 149 | res 150 | } 151 | 152 | /// Call `f`, but with `path` pushed onto the path stack. 153 | #[inline] 154 | fn with_path( 155 | &mut self, 156 | path: PathComponent, 157 | f: impl FnOnce(&mut Self) -> Result<(), EncodeError<'i>>, 158 | ) -> Result<(), EncodeError<'i>> { 159 | self.path.push(path); 160 | let res = f(self); 161 | self.path.pop(); 162 | res 163 | } 164 | 165 | /// Encode `v` by performing cycle detection and then calling into 166 | /// `Encode::encode()`. 167 | fn subencode(&mut self, v: &Value<'i>) -> Result<(), EncodeError<'i>> { 168 | match v { 169 | Value::Object(val) => { 170 | let ptr = val.ptr_value(); 171 | self.with_indirection(ptr, |this| { 172 | if let Some(mut encode_as) = val.lookup(ENCODE_AS) { 173 | if let Value::Fn(fnc) = &mut encode_as { 174 | fnc.bind(val.clone()); 175 | fnc.rename(ENCODE_AS.into()); 176 | } 177 | let mut ctx = UclContext::new(); 178 | return match ctx.call( 179 | Span::synthetic("encode.alkyne".as_ref()), 180 | encode_as, 181 | Vec::new(), 182 | ) { 183 | Ok(obj) => this.subencode(&obj), 184 | Err(ControlFlow::Error(e)) => Err(EncodeError::EvalError { 185 | eval_error: e, 186 | path: this.path.clone(), 187 | }), 188 | _ => unreachable!(), 189 | }; 190 | } 191 | Encode::encode(Context(this), v) 192 | }) 193 | } 194 | v => Encode::encode(Context(self), v), 195 | } 196 | } 197 | } 198 | 199 | /// An `EncodeError` is an error produced during encoding. 200 | #[derive(Clone, Debug)] 201 | pub enum EncodeError<'i> { 202 | /// Represents a cycle; obviously, cycles cannot be cleanly encoded without 203 | /// using infinite space. 204 | Cycle { path: Vec }, 205 | /// Represents encountering a type which the given encoding does not support. 206 | BadType { 207 | bad_type: Type, 208 | path: Vec, 209 | }, 210 | /// Represents an evaluation error that happened during encoding. 211 | EvalError { 212 | eval_error: EvalError<'i>, 213 | path: Vec, 214 | }, 215 | } 216 | 217 | impl fmt::Display for EncodeError<'_> { 218 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 219 | fn write_path( 220 | path: &[PathComponent], 221 | f: &mut fmt::Formatter, 222 | ) -> fmt::Result { 223 | if path.is_empty() { 224 | return write!(f, "."); 225 | } 226 | 227 | for p in path { 228 | match p { 229 | PathComponent::Index(n) => write!(f, "[{}]", n)?, 230 | PathComponent::Ident(i) => { 231 | let mut buf = String::new(); 232 | escaping::escape_alkyne_string(i, true, &mut buf); 233 | write!(f, ".{}", buf)? 234 | } 235 | } 236 | } 237 | 238 | Ok(()) 239 | } 240 | 241 | match self { 242 | EncodeError::Cycle { path } => { 243 | write!(f, "error: cycle detected at ")?; 244 | write_path(path, f) 245 | } 246 | EncodeError::BadType { bad_type, path } => { 247 | write!(f, "error: unsupported type {} at ", bad_type)?; 248 | write_path(path, f) 249 | } 250 | EncodeError::EvalError { eval_error, path } => { 251 | write!(f, "error: std.EncodeAs evaluation error at ")?; 252 | write_path(path, f)?; 253 | write!(f, "\n{}", eval_error) 254 | } 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/eval/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Error generation for Alkyne evalution. 16 | 17 | #![allow(dead_code)] 18 | 19 | use std::fmt; 20 | 21 | use crate::eval::value::Value; 22 | use crate::eval::Context; 23 | use crate::syn::Span; 24 | 25 | /// An `EvalError` is any error raised during evaluation; it records an error 26 | /// message and a stack trace. 27 | #[derive(Clone, Debug)] 28 | pub struct EvalError<'i> { 29 | pub message: String, 30 | pub trace: Vec>, 31 | } 32 | 33 | impl<'i> EvalError<'i> { 34 | /// Creates a new `EvalError`, recording an error at the given span with the 35 | /// given message. 36 | pub fn new(ctx: &Context<'i>, span: Span<'i>, message: String) -> Self { 37 | let height = ctx.call_stack.len(); 38 | let mut trace = Vec::with_capacity(height); 39 | 40 | // Each stack frame (except the first) contains (fnc, caller_span). 41 | // We want to get (fnc, callee_span). 42 | for i in (0..ctx.call_stack.len()).rev() { 43 | let fn_name = if i == 0 { 44 | " ()".to_string() 45 | } else { 46 | match &ctx.call_stack[i].fnc { 47 | Value::Fn(f) => format!("{}", f), 48 | Value::NativeFn(f) => format!("{}", f), 49 | _ => " (??)".to_string(), 50 | } 51 | }; 52 | 53 | let error_span = if i == height - 1 { 54 | span 55 | } else { 56 | ctx.call_stack[i + 1].call_site 57 | }; 58 | 59 | trace.push(Frame { 60 | fn_name, 61 | error_span, 62 | }) 63 | } 64 | 65 | EvalError { message, trace } 66 | } 67 | 68 | pub fn symbolize(&self, w: &mut impl fmt::Write) -> fmt::Result { 69 | writeln!(w, "error: {}", self.message)?; 70 | // Find the first "ok" frame. 71 | if self 72 | .trace 73 | .iter() 74 | .map(|f| f.error_span) 75 | .any(|s| !s.is_synthetic()) 76 | { 77 | //span.pretty(w)?; 78 | } 79 | writeln!(w, "stack dump:")?; 80 | 81 | for frame in &self.trace { 82 | write!(w, "* {} at ", frame.fn_name)?; 83 | if frame.error_span.is_synthetic() { 84 | writeln!(w, "({})", frame.error_span.file_name().display())?; 85 | } else { 86 | let (line, col) = frame.error_span.start_position().unwrap(); 87 | writeln!( 88 | w, 89 | "({}:{}:{})", 90 | frame.error_span.file_name().display(), 91 | line, 92 | col 93 | )?; 94 | } 95 | } 96 | 97 | Ok(()) 98 | } 99 | } 100 | 101 | impl fmt::Display for EvalError<'_> { 102 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 103 | self.symbolize(f) 104 | } 105 | } 106 | 107 | /// A `Frame` is stack frame information relating to an evaluation error. 108 | #[derive(Clone, Debug)] 109 | pub struct Frame<'i> { 110 | fn_name: String, 111 | error_span: Span<'i>, 112 | } 113 | 114 | /// Generates, and returns, an `EvalError` given a `Context`, a `Spanned`, and 115 | /// a formatted message. 116 | macro_rules! error { 117 | ($ctx:expr, $expr:expr, $($tt:tt)*) => {{ 118 | #[allow(unused_imports)] 119 | use $crate::syn::Spanned; 120 | #[allow(unused_imports)] 121 | use $crate::eval::error::*; 122 | return Err(EvalError::new($ctx, $expr.span(), format!($($tt)*)).into()) 123 | }} 124 | } 125 | 126 | macro_rules! bug { 127 | ($($tt:tt)*) => {{ 128 | eprintln!("error: internal interpreter error; this is a bug\nerror: "); 129 | eprintln!($($tt)*); 130 | panic!() 131 | }} 132 | } 133 | -------------------------------------------------------------------------------- /src/eval/escaping.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Miscellaneous escaping functions. 16 | 17 | use std::fmt; 18 | 19 | /// The error returned by `unescape_utf8_literal()`. 20 | #[derive(Clone, Copy, Debug)] 21 | pub enum UnescapeError { 22 | UnexpectedEof, 23 | UnknownEscape(usize, char), 24 | BadHexDigit(usize, char), 25 | BadUnicode(usize, u32), 26 | } 27 | 28 | impl fmt::Display for UnescapeError { 29 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 30 | match self { 31 | UnescapeError::UnexpectedEof => write!(f, "input ended during escape"), 32 | UnescapeError::UnknownEscape(_, c) => { 33 | write!(f, "unknown escape sequence: '\\{}'", c) 34 | } 35 | UnescapeError::BadHexDigit(_, c) => { 36 | write!(f, "bad hex digit while parsing escape: '{}'", c) 37 | } 38 | UnescapeError::BadUnicode(_, x) => { 39 | write!(f, "bad Unicode codepoint generated by escape: {:#x}", x) 40 | } 41 | } 42 | } 43 | } 44 | 45 | /// Unescapes the string `s`. 46 | /// 47 | /// This function recognizes the following escape sequences: 48 | /// - `\0`, a NUL character. 49 | /// - `\n`, a newline. 50 | /// - `\t`, a tab character. 51 | /// - `\r`, a carriage return. 52 | /// - `\xNN`, a hex-encoded ASCII character. 53 | /// - `\uNNNN`, a hex-encoded Unicode codepoint in the Basic Multilingual 54 | /// Plane. 55 | /// - `\UNNNNNNNN`, any hex-encoded Unicode codepoint. 56 | pub fn unescape_utf8_literal(s: &str) -> Result { 57 | let mut buf = String::with_capacity(s.len()); 58 | let mut chars = s.char_indices(); 59 | while let Some((_, c)) = chars.next() { 60 | if c != '\\' { 61 | buf.push(c); 62 | continue; 63 | } 64 | 65 | let (escape_idx, escape) = match chars.next() { 66 | Some(c) => c, 67 | None => return Err(UnescapeError::UnexpectedEof), 68 | }; 69 | 70 | fn parse_hex( 71 | chars: &mut impl Iterator, 72 | digits: usize, 73 | ) -> Result { 74 | fn hex_digit(c: char) -> Option { 75 | let nybble = match c { 76 | '0'..='9' => (c as u8) - b'0', 77 | 'a'..='f' => (c as u8) - b'a' + 10, 78 | 'A'..='F' => (c as u8) - b'A' + 10, 79 | _ => return None, 80 | }; 81 | Some(nybble as u32) 82 | } 83 | 84 | let mut total: u32 = 0; 85 | 86 | for _ in 0..digits { 87 | let nybble = match chars.next().map(|(i, c)| (i, c, hex_digit(c))) { 88 | Some((_, _, Some(n))) => n, 89 | Some((idx, c, _)) => return Err(UnescapeError::BadHexDigit(idx, c)), 90 | None => return Err(UnescapeError::UnexpectedEof), 91 | }; 92 | 93 | total <<= 4; 94 | total |= nybble; 95 | } 96 | 97 | Ok(total) 98 | } 99 | 100 | let unescaped: char = match escape { 101 | '"' | '\'' | '\\' => escape, 102 | '0' => '\0', 103 | 'n' => '\n', 104 | 't' => '\t', 105 | 'r' => '\r', 106 | 'b' => '\x08', 107 | 'f' => '\x0c', 108 | 'x' => { 109 | let codepoint = parse_hex(&mut chars, 2)?; 110 | match std::char::from_u32(codepoint) { 111 | Some(c) => c, 112 | None => return Err(UnescapeError::BadUnicode(escape_idx, codepoint)), 113 | } 114 | } 115 | 'u' => { 116 | let codepoint = parse_hex(&mut chars, 4)?; 117 | match std::char::from_u32(codepoint) { 118 | Some(c) => c, 119 | None => return Err(UnescapeError::BadUnicode(escape_idx, codepoint)), 120 | } 121 | } 122 | 'U' => { 123 | let codepoint = parse_hex(&mut chars, 8)?; 124 | match std::char::from_u32(codepoint) { 125 | Some(c) => c, 126 | None => return Err(UnescapeError::BadUnicode(escape_idx, codepoint)), 127 | } 128 | } 129 | _ => return Err(UnescapeError::UnknownEscape(escape_idx, escape)), 130 | }; 131 | 132 | buf.push(unescaped) 133 | } 134 | 135 | Ok(buf) 136 | } 137 | 138 | /// Checks whether `s` is a valid Alkyne identifier, including `_`. 139 | pub fn is_identifier(s: &str) -> bool { 140 | for (i, c) in s.chars().enumerate() { 141 | match (i, c) { 142 | (0, '0'..='9') => return false, 143 | (_, '0'..='9') | (_, 'a'..='z') | (_, 'A'..='Z') | (_, '_') => {} 144 | _ => return false, 145 | } 146 | } 147 | true 148 | } 149 | 150 | /// Escapes the string `s` according to Alkyne syntax. 151 | /// 152 | /// The quotation marks are included, unless `maybe_id` is true and the string 153 | /// is a valid Alkyne identifier other than `_`. 154 | pub fn escape_alkyne_string(s: &str, maybe_id: bool, buf: &mut String) { 155 | if maybe_id && s != "_" && is_identifier(s) { 156 | buf.push_str(s); 157 | return; 158 | } 159 | 160 | let use_single_quotes = s.contains('"') && !s.contains('\''); 161 | if use_single_quotes { 162 | buf.push('\''); 163 | } else { 164 | buf.push('"'); 165 | } 166 | 167 | for c in s.chars() { 168 | use std::fmt::Write; 169 | match c { 170 | '\x00' => buf.push_str("\\0"), 171 | '\x08' => buf.push_str("\\b"), 172 | // Tab. 173 | '\t' => buf.push_str("\\t"), 174 | '\n' => buf.push_str("\\n"), 175 | // Form feed. 176 | '\x0c' => buf.push_str("\\f"), 177 | '\r' => buf.push_str("\\r"), 178 | '"' if !use_single_quotes => buf.push_str("\\\""), 179 | '\'' if use_single_quotes => buf.push_str("\\'"), 180 | '\\' => buf.push_str("\\\\"), 181 | c if !c.is_control() => buf.push(c), 182 | c if (c as u32) < 256 => { 183 | let _ = write!(buf, "\\x{:02x}", c as u32); 184 | } 185 | c if (c as u32) < 65536 => { 186 | let _ = write!(buf, "\\u{:04x}", c as u32); 187 | } 188 | c => { 189 | let _ = write!(buf, "\\U{:08x}", c as u32); 190 | } 191 | } 192 | } 193 | 194 | if use_single_quotes { 195 | buf.push('\''); 196 | } else { 197 | buf.push('"'); 198 | } 199 | } 200 | 201 | /// Escapes the string `s` according to JSON syntax. 202 | /// 203 | /// The quotation marks are included. 204 | pub fn escape_json_string(s: &str, buf: &mut String) { 205 | buf.push('"'); 206 | for c in s.encode_utf16() { 207 | match c { 208 | // Backspace. 209 | 0x08 => buf.push_str("\\b"), 210 | // Tab. 211 | 0x09 => buf.push_str("\\t"), 212 | // Newline. 213 | 0x0a => buf.push_str("\\n"), 214 | // Form feed. 215 | 0x0c => buf.push_str("\\f"), 216 | // Carriage return. 217 | 0x0d => buf.push_str("\\r"), 218 | // Double quote. 219 | 0x22 => buf.push_str("\\\""), 220 | // Backslash. 221 | 0x5c => buf.push_str("\\\\"), 222 | // Printable ASCII. 223 | 0x20..=0x7e => buf.push(c as u8 as char), 224 | // Everything else, as \uXXXX. 225 | _ => { 226 | use std::fmt::Write; 227 | let _ = write!(buf, "\\u{:04x}", c); 228 | } 229 | } 230 | } 231 | buf.push('"'); 232 | } 233 | -------------------------------------------------------------------------------- /src/eval/stdlib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Native code implementations of the Alkyne standard library. 16 | 17 | use std::cmp::Ordering; 18 | 19 | use crate::eval::value::Object; 20 | use crate::eval::value::Type; 21 | use crate::eval::value::Value; 22 | use crate::syn::Spanned; 23 | 24 | pub const ENCODE_AS: &str = "__encode_as"; 25 | 26 | /// Macro for generating the giant stdlib switch-case below. 27 | macro_rules! stdlib { 28 | ($key:expr => $( 29 | fn $fn_name:ident ($($args:tt)*) $body:block 30 | $(let $let_name:ident = $expr:expr;)* 31 | )*) => { 32 | match $key {$( 33 | stringify!($fn_name) => stdlib!(@fn $fn_name ($($args)*) $body), 34 | $(stringify!($let_name) => stdlib!(@let $let_name = $expr;),)* 35 | )* _ => None, } 36 | }; 37 | 38 | (@fn $ident:ident($($args:tt)*) $body:block) => {{ 39 | let mut fnc = native_fn!(($($args)*) $body); 40 | fnc.rename($crate::eval::value::Str::new_static(stringify!($ident))); 41 | Some(fnc.into()) 42 | }}; 43 | 44 | (@let $ident:ident = $expr:expr;) => {Some($expr.into())}; 45 | } 46 | 47 | /// Looks up `key` in the Alkyne standard library. 48 | /// 49 | /// Returns `None` if `key` is undefined. 50 | pub fn lookup_std<'i>(key: &str) -> Option> { 51 | stdlib! { key => 52 | // -- General reflection utilities. -- 53 | 54 | // Returns the name of the file currently being executed. Note 55 | // that this information is derived from the stack, so functions will only 56 | // ever see the name of the file they were defined in. 57 | fn file_name(ctx) { 58 | let prev_frame = &ctx.call_stack[ctx.call_stack.len() - 2]; 59 | match &prev_frame.fnc { 60 | Value::Fn(fnc) => fnc.src().span().file_name().to_str().unwrap_or(""), 61 | _ => "", 62 | } 63 | } 64 | 65 | // EncodeAs represents an implementation-defined object key. If this key is 66 | // set on an object, at encoding time, instead of encoding the object 67 | // directly, this key will be looked up, called with no arguments, and the 68 | // return value will be encoded, instead. 69 | let EncodeAs = ENCODE_AS; 70 | 71 | // Returns an implementation-defined string representing the type of `x`. 72 | fn type_of(x: any) { x.ty().name() } 73 | 74 | // Returns whether `x` has a JSON encoding value, i.e., any 75 | // of null, bool, number, string, list, or object. 76 | fn is_json(x: any) { 77 | matches!(x.ty(), 78 | Type::Null | Type::Bool | Type::Number | 79 | Type::String | Type::List | Type::Object) 80 | } 81 | 82 | // Returns whether `x` is of type null. 83 | fn is_null(x: any) { x.ty() == Type::Null } 84 | 85 | // Returns whether `x` is of type bool. 86 | fn is_bool(x: any) { x.ty() == Type::Bool } 87 | 88 | // Returns whether `x` is of type number. 89 | fn is_number(x: any) { x.ty() == Type::Number } 90 | 91 | // Returns whether `x` is of type string. 92 | fn is_string(x: any) { x.ty() == Type::String } 93 | 94 | // Returns whether `x` is of type list. 95 | fn is_list(x: any) { x.ty() == Type::List } 96 | 97 | // Returns whether `x` is of type object. 98 | fn is_object(x: any) { x.ty() == Type::Object } 99 | 100 | // Returns whether `x` is of type function. 101 | fn is_fn(x: any) { x.ty() == Type::Fn } 102 | 103 | // Returns whether `x` has a non-opaque type 104 | fn has_type(x: any) { x.ty() != Type::Opaque } 105 | 106 | // Compares `a` and `b` for "pointer equality". What this means is 107 | // implementation-defined, but if `same(a, b)` holds, then so must 108 | // `a == b` if they are primitives. Furthermore, for JSON-like types, 109 | // `same(x, x)` always holds. 110 | fn same(a: any, b: any) { 111 | match (a, b) { 112 | (Value::Null, Value::Null) => true, 113 | (Value::Bool(a), Value::Bool(b)) => a == b, 114 | #[allow(clippy::float_cmp)] 115 | (Value::Number(a), Value::Number(b)) => a == b, 116 | (Value::String(a), Value::String(b)) => a.ptr_eq(&b), 117 | (Value::List(a), Value::List(b)) => a.ptr_eq(&b), 118 | (Value::Object(a), Value::Object(b)) => a.ptr_value() == b.ptr_value(), 119 | _ => false, 120 | } 121 | } 122 | 123 | // Binds `zelf` as the referent of `fnc`, so that it is the object 124 | // produced by `self` when `fnc` is called. Returns the re-bound `fnc`. 125 | fn bind(ctx, fnc: any, zelf: object) { 126 | match fnc { 127 | Value::Fn(mut fnc) => { 128 | fnc.bind(zelf); 129 | Value::Fn(fnc) 130 | } 131 | f @ Value::NativeFn(..) => f, 132 | t => native_err!(ctx, "expected function, got {}", t), 133 | } 134 | } 135 | 136 | // Constants for the various type names. 137 | let Null = Type::Null.name(); 138 | let Bool = Type::Bool.name(); 139 | let Number = Type::Number.name(); 140 | let String = Type::String.name(); 141 | let List = Type::List.name(); 142 | let Object = Type::Object.name(); 143 | let Function = Type::Fn.name(); 144 | 145 | // -- General functions. -- 146 | // Triggers an error when `cond` is not satisfied, printing 147 | // `msg`. Otherwise, it returns `null`. 148 | fn assert(ctx, cond: bool, msg: string) { 149 | if !cond { 150 | native_err!(ctx, "assertion failed: {}", msg) 151 | } 152 | } 153 | 154 | // Just like `assert`, but does not display an error message. 155 | fn check(ctx, cond: bool) { 156 | if !cond { 157 | native_err!(ctx, "assertion failed") 158 | } 159 | } 160 | 161 | // Unconditionally returns an error with message `msg`. 162 | fn error(ctx, msg: string) { native_err!(ctx, "{}", msg) } 163 | 164 | // Returns true if `c[idx]` would succeed, i.e., if `c?[idx]` is nonnull. 165 | fn has(ctx, container: any, index: any) { 166 | ctx.index(native_span!(), container, vec![index]).is_ok() 167 | } 168 | 169 | // Returns a range with step one, form `start` to `end`, exclusive. 170 | // The arguments to this function *must* be integers. 171 | fn range(start: integer, end: integer) { 172 | Value::Range { start, end, step: 1 } 173 | } 174 | 175 | // Returns a range with step one, form `start` to `end`, 176 | // exclusive, with step `step`. The arguments to this function *must* be 177 | // integers; `step` must also be positive. 178 | fn range_step(ctx, start: integer, end: integer, step: integer) { 179 | if step <= 0 { 180 | native_err!(ctx, "expected positive step; got {}", step) 181 | } 182 | 183 | Value::Range { start, end, step } 184 | } 185 | 186 | // Returns the first index at which `needle` appears in `haystack`. 187 | // This function will execute a comparision against each element of 188 | // `haystack`; comparing incomparable values will error. If the element is 189 | // not found, returns null. 190 | fn index_of(ctx, haystack: list, needle: any) { 191 | for (i, val) in haystack.iter().enumerate() { 192 | if ctx.compare_eq(native_span!(), val.clone(), needle.clone())? { 193 | return Ok(Value::Number(i as f64)) 194 | } 195 | } 196 | Value::Null 197 | } 198 | 199 | // Returns the number of elements in the list `xs` or the number of 200 | // bytes in the string `xs`, and returns an error in all other cases. 201 | fn len(ctx, x: any) { 202 | let len = match x { 203 | Value::String(s) => s.len(), 204 | Value::List(xs) => xs.len(), 205 | v => native_err!(ctx, "cannot take length of value of type {}", v.ty()), 206 | }; 207 | len as f64 208 | } 209 | 210 | // Takes a list of lists, and flattens it into a single list. 211 | fn flatten(ctx, xss: list) { 212 | let mut buf = Vec::new(); 213 | for v in xss.iter() { 214 | match v { 215 | Value::List(xs) => buf.extend_from_slice(&*xs), 216 | v => native_err!(ctx, "expected list of lists, got {}", v.ty()), 217 | } 218 | } 219 | buf 220 | } 221 | 222 | // Concatenates a list of strings into one string. 223 | fn concat(ctx, strs: list) { 224 | let mut buf = String::new(); 225 | for v in strs.iter() { 226 | match v { 227 | Value::String(s) => buf.push_str(&*s), 228 | v => native_err!(ctx, "expected list of strings, got {}", v.ty()), 229 | } 230 | } 231 | buf 232 | } 233 | 234 | // Takes a format string, and a list of arguments, and printf-formats them. 235 | // Supported verbs are: 236 | // - %%: a literal % character. 237 | // - %s: a string, as-is. 238 | // - %q: a string, but quoted and escaped. 239 | // - %d: an integer. 240 | // - %f: a number. 241 | // 242 | // All other cases will scribble some error message on the resulting 243 | // string, but this function itself will always succeed. 244 | fn fmt(fmt_str: string, args: list) { 245 | use std::fmt::Write; 246 | let mut buf = String::with_capacity(fmt_str.len()); 247 | 248 | let mut arg_idx = 0; 249 | let mut chars = fmt_str.chars().peekable(); 250 | while let Some(c) = chars.next() { 251 | match (c, chars.peek(), args.get(arg_idx)) { 252 | ('%', Some('%'), _) => buf.push('%'), 253 | 254 | ('%', Some('s'), Some(Value::String(val))) => { 255 | arg_idx += 1; 256 | let _ = write!(buf, "{}", val.as_sliced()); 257 | } 258 | 259 | ('%', Some('q'), Some(Value::String(val))) => { 260 | arg_idx += 1; 261 | let _ = write!(buf, "{}", val); 262 | } 263 | 264 | ('%', Some('d'), Some(val)) if val.as_int().is_ok() => { 265 | arg_idx += 1; 266 | let _ = write!(buf, "{}", val.as_int().unwrap()); 267 | } 268 | 269 | ('%', Some('f'), Some(Value::Number(val))) => { 270 | arg_idx += 1; 271 | let _ = write!(buf, "{}", val); 272 | } 273 | 274 | ('%', Some(c), Some(v)) => { 275 | arg_idx += 1; 276 | let _ = write!(buf, "%{}", c, v.ty()); 277 | } 278 | 279 | ('%', Some(c), None) => { 280 | arg_idx += 1; 281 | let _ = write!(buf, "%{}", c,); 282 | } 283 | 284 | ('%', None, _) => { 285 | let _ = write!(buf, "%"); 286 | } 287 | 288 | (c, _, _) => buf.push(c), 289 | } 290 | 291 | if c == '%' { 292 | let _ = chars.next(); 293 | } 294 | } 295 | if arg_idx < args.len() { 296 | let _ = write!(buf, "%", args.len() - arg_idx); 297 | } 298 | buf 299 | } 300 | 301 | // Merges the objects `a` and `b`, resulting in a super-less object with all 302 | // of the transitive fields of `a` and `b`. 303 | fn merge(a: object, b: object) { 304 | let new_obj = Object::new(); 305 | 306 | for (k, v, _) in a.iter() { 307 | new_obj.define(k.clone(), v.clone(), true); 308 | } 309 | 310 | for (k, v, _) in b.iter() { 311 | new_obj.define(k.clone(), v.clone(), true); 312 | } 313 | 314 | new_obj.freeze(); 315 | new_obj 316 | } 317 | 318 | // -- Math stuff. -- 319 | // All mathematical functions return an indeterminate value when called 320 | // with values outside of their domain. 321 | 322 | // Computes the minimum of `a` and `b`, if they are comparable. 323 | fn min(ctx, a: any, b: any) { 324 | match ctx.compare_ord(native_span!(), a.clone(), b.clone())? { 325 | Some(Ordering::Greater) => b, 326 | _ => a, 327 | } 328 | } 329 | 330 | // Computes the maximum of `a` and `b`, if they are comparable. 331 | fn max(ctx, a: any, b: any) { 332 | match ctx.compare_ord(native_span!(), a.clone(), b.clone())? { 333 | Some(Ordering::Less) => b, 334 | _ => a, 335 | } 336 | } 337 | 338 | // Archimedes' constant to some precision. 339 | let Pi = std::f64::consts::PI; 340 | // Euler's constant to some precision. 341 | let Euler = std::f64::consts::E; 342 | 343 | // Computes the absolute value of `x`. 344 | fn abs(x: number) { x.abs() } 345 | 346 | // Computes the square root of `x`. 347 | fn sqrt(x: number) { x.sqrt() } 348 | 349 | // Computes the exponential of `x`. 350 | fn exp(x: number) { x.exp() } 351 | // Computes the natural logarithm of `x`. 352 | fn ln(x: number) { x.ln() } 353 | 354 | // Computes `x` raised to the `e`th power. 355 | fn pow(x: number, e: number) { x.powf(e) } 356 | // Computes the logarithm of `x` at base `b`. 357 | fn log(x: number, b: number) { x.log(b) } 358 | 359 | // Converts the angle `t` to degrees (as if it were in radians). 360 | fn deg(x: number) { x.to_degrees() } 361 | // Converts the angle `t` to radians (as if it were in degrees). 362 | fn rad(x: number) { x.to_radians() } 363 | 364 | // Computes the sine of `x`. 365 | fn sin(x: number) { x.sin() } 366 | // Computes the cosine of `x`. 367 | fn cos(x: number) { x.cos() } 368 | // Computes the tangent of `x`. 369 | fn tan(x: number) { x.tan() } 370 | 371 | // Computes the cosecant of `x`. 372 | fn csc(x: number) { x.sin().recip() } 373 | // Computes the secant of `x`. 374 | fn sec(x: number) { x.cos().recip() } 375 | // Computes the cotangent of `x`. 376 | fn cot(x: number) { x.tan().recip() } 377 | 378 | // Computes the arcsine of `x`. 379 | fn asin(x: number) { x.asin() } 380 | // Computes the arccosine of `x`. 381 | fn acos(x: number) { x.acos() } 382 | // Computes the arctangent of `x`. 383 | fn atan(x: number) { x.atan() } 384 | 385 | // Computes the arccosecant of `x`. 386 | fn acsc(x: number) { x.recip().asin() } 387 | // Computes the arcsecant of `x`. 388 | fn asec(x: number) { x.recip().acos() } 389 | // Computes the arccotangent of `x`. 390 | fn acot(x: number) { x.recip().atan() } 391 | 392 | // Computes the hyperbolic sine of `x`. 393 | fn sinh(x: number) { x.sinh() } 394 | // Computes the hyperbolic cosine of `x`. 395 | fn cosh(x: number) { x.cosh() } 396 | // Computes the hyperbolic tangent of `x`. 397 | fn tanh(x: number) { x.tanh() } 398 | 399 | // Computes the hyperbolic cosecant of `x`. 400 | fn csch(x: number) { x.sinh().recip() } 401 | // Computes the hyperbolic secant of `x`. 402 | fn sech(x: number) { x.cosh().recip() } 403 | // Computes the hyperbolic cotangent of `x`. 404 | fn coth(x: number) { x.tanh().recip() } 405 | 406 | // Computes the hyperbolic arcsine of `x`. 407 | fn asinh(x: number) { x.asinh() } 408 | // Computes the hyperbolic arccosine of `x`. 409 | fn acosh(x: number) { x.acosh() } 410 | // Computes the hyperbolic arctangent of `x`. 411 | fn atanh(x: number) { x.atanh() } 412 | 413 | // Computes the hyperbolic arccosecant of `x`. 414 | fn acsch(x: number) { x.recip().asinh() } 415 | // Computes the hyperbolic arcsecant of `x`. 416 | fn asech(x: number) { x.recip().acosh() } 417 | // Computes the hyperbolic arccotangent of `x`. 418 | fn acoth(x: number) { x.recip().atanh() } 419 | } 420 | } 421 | -------------------------------------------------------------------------------- /src/eval/value/convert.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Traits for converting Rust types into Alkyne values. 16 | 17 | use crate::eval::value::Fn; 18 | use crate::eval::value::List; 19 | use crate::eval::value::NativeFn; 20 | use crate::eval::value::Object; 21 | use crate::eval::value::Str; 22 | use crate::eval::value::Value; 23 | use crate::eval::Result; 24 | 25 | /// Represents a type that can be converted into a `Value`. 26 | /// 27 | /// This trait mostly exists to be different from `Into` to avoid coherence 28 | /// issues, and `Into::into()` should generally be preferred instead. 29 | pub trait IntoValue<'i> { 30 | fn into_value(self) -> Value<'i>; 31 | } 32 | 33 | impl<'i, T: IntoValue<'i>> From for Value<'i> { 34 | #[inline] 35 | fn from(x: T) -> Self { 36 | x.into_value() 37 | } 38 | } 39 | 40 | impl<'i> IntoValue<'i> for () { 41 | fn into_value(self) -> Value<'i> { 42 | Value::Null 43 | } 44 | } 45 | 46 | impl<'i> IntoValue<'i> for bool { 47 | fn into_value(self) -> Value<'i> { 48 | Value::Bool(self) 49 | } 50 | } 51 | 52 | impl<'i> IntoValue<'i> for f64 { 53 | fn into_value(self) -> Value<'i> { 54 | Value::Number(self) 55 | } 56 | } 57 | 58 | impl<'i> IntoValue<'i> for String { 59 | fn into_value(self) -> Value<'i> { 60 | Value::String(self.into()) 61 | } 62 | } 63 | 64 | impl<'i> IntoValue<'i> for &'i str { 65 | fn into_value(self) -> Value<'i> { 66 | Value::String(Str::new_static(self)) 67 | } 68 | } 69 | 70 | impl<'i> IntoValue<'i> for Str<'i> { 71 | fn into_value(self) -> Value<'i> { 72 | Value::String(self) 73 | } 74 | } 75 | 76 | impl<'i> IntoValue<'i> for Vec> { 77 | fn into_value(self) -> Value<'i> { 78 | Value::List(self.into()) 79 | } 80 | } 81 | 82 | impl<'i> IntoValue<'i> for &'i [Value<'i>] { 83 | fn into_value(self) -> Value<'i> { 84 | Value::List(List::new_static(self)) 85 | } 86 | } 87 | 88 | impl<'i> IntoValue<'i> for List<'i> { 89 | fn into_value(self) -> Value<'i> { 90 | Value::List(self) 91 | } 92 | } 93 | 94 | impl<'i> IntoValue<'i> for Object<'i> { 95 | fn into_value(self) -> Value<'i> { 96 | Value::Object(self) 97 | } 98 | } 99 | 100 | impl<'i> IntoValue<'i> for Fn<'i> { 101 | fn into_value(self) -> Value<'i> { 102 | Value::Fn(self) 103 | } 104 | } 105 | 106 | impl<'i> IntoValue<'i> for NativeFn<'i> { 107 | fn into_value(self) -> Value<'i> { 108 | Value::NativeFn(self) 109 | } 110 | } 111 | 112 | /// Represents a type that can be converted into a `Result`. 113 | /// 114 | /// This trait mostly exists to be different from `Into` to avoid coherence 115 | /// issues. It is exclusively used by `native_fn!()`. 116 | #[doc(hidden)] 117 | pub trait IntoValueResult<'i> { 118 | fn into_value_result(self) -> Result<'i, Value<'i>>; 119 | } 120 | 121 | impl<'i, I> IntoValueResult<'i> for I 122 | where 123 | I: IntoValue<'i>, 124 | { 125 | fn into_value_result(self) -> Result<'i, Value<'i>> { 126 | Ok(self.into_value()) 127 | } 128 | } 129 | 130 | impl<'i> IntoValueResult<'i> for Value<'i> { 131 | fn into_value_result(self) -> Result<'i, Value<'i>> { 132 | Ok(self) 133 | } 134 | } 135 | 136 | impl<'i> IntoValueResult<'i> for Result<'i, Value<'i>> { 137 | fn into_value_result(self) -> Result<'i, Value<'i>> { 138 | self 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/eval/value/fns.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Alkyne Functions 16 | 17 | use std::fmt::Debug; 18 | use std::fmt::Display; 19 | 20 | use crate::eval::escaping; 21 | use crate::eval::value::Object; 22 | use crate::eval::value::Str; 23 | use crate::eval::value::Value; 24 | use crate::eval::Context; 25 | use crate::eval::Result; 26 | use crate::syn; 27 | use crate::syn::Spanned; 28 | 29 | /// A `Fn` is a Alkyne function. It is implemented as a capture of the scope it 30 | /// was defined in, and a copy of its AST for later evaluation. 31 | #[derive(Clone, Debug)] 32 | pub struct Fn<'i> { 33 | src: &'i syn::Fn<'i>, 34 | name: Option>, 35 | captures: Object<'i>, 36 | referent: Option>, 37 | } 38 | 39 | impl<'i> Fn<'i> { 40 | /// Creates a new `Fn` with the given source code and captured scope. 41 | pub fn new(src: &'i syn::Fn<'i>, captures: Object<'i>) -> Self { 42 | Fn { 43 | src, 44 | name: None, 45 | referent: None, 46 | captures, 47 | } 48 | } 49 | 50 | /// Returns the source code for this function. 51 | pub fn src(&self) -> &'i syn::Fn<'i> { 52 | self.src 53 | } 54 | 55 | /// Returns this function's name. 56 | pub fn name(&self) -> Option> { 57 | self.name.clone() 58 | } 59 | 60 | /// Returns this function's captured scope. 61 | pub fn captures(&self) -> Object<'i> { 62 | self.captures.clone() 63 | } 64 | 65 | /// Renames this function to the given name. 66 | pub fn rename(&mut self, name: Str<'i>) { 67 | self.name = Some(name); 68 | } 69 | 70 | /// Returns this function's referent, i.e., the value of `self` during the 71 | /// function call. 72 | pub fn referent(&self) -> Option> { 73 | self.referent.clone() 74 | } 75 | 76 | /// Binds the given object to this function, giving it a referent. 77 | pub fn bind(&mut self, referent: Object<'i>) { 78 | self.referent = Some(referent); 79 | } 80 | } 81 | 82 | impl Display for Fn<'_> { 83 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 84 | if let Some(name) = &self.name { 85 | let mut buf = String::new(); 86 | escaping::escape_alkyne_string(name, true, &mut buf); 87 | write!(f, "{}() ", buf)?; 88 | } else { 89 | write!(f, " ")?; 90 | } 91 | 92 | let span = self.src.span(); 93 | if !span.is_synthetic() { 94 | let (line, col) = span.start_position().unwrap(); 95 | write!(f, "({}:{}:{})", span.file_name().display(), line, col) 96 | } else { 97 | write!(f, "({})", span.file_name().display()) 98 | } 99 | } 100 | } 101 | 102 | type NativeFnHandle = 103 | for<'i> fn(&mut Context<'i>, Vec>) -> Result<'i, Value<'i>>; 104 | 105 | /// A `NativeFn` is a Alkyne function implemented as a native Rust function. 106 | #[derive(Clone)] 107 | pub struct NativeFn<'i> { 108 | name: Option>, 109 | fnc: NativeFnHandle, 110 | location: (&'static str, u32, u32), 111 | } 112 | 113 | impl<'i> NativeFn<'i> { 114 | /// Creates a new `NativeFn`. 115 | /// 116 | /// The preferred method to bind native functions is the `native_fn!()` macro. 117 | pub fn new(fnc: NativeFnHandle, location: (&'static str, u32, u32)) -> Self { 118 | Self { 119 | name: None, 120 | fnc, 121 | location, 122 | } 123 | } 124 | 125 | /// Returns this function's name. 126 | pub fn name(&self) -> Option> { 127 | self.name.clone() 128 | } 129 | 130 | /// Renames this function to the given name. 131 | pub fn rename(&mut self, name: Str<'i>) { 132 | self.name = Some(name); 133 | } 134 | 135 | /// Calls this function's handle. 136 | /// 137 | /// Note that this function does not do anything to the stack in `ctx`; 138 | /// setting up a sane stack is the caller's responsibility. 139 | pub fn call( 140 | &self, 141 | ctx: &mut Context<'i>, 142 | args: Vec>, 143 | ) -> Result<'i, Value<'i>> { 144 | (self.fnc)(ctx, args) 145 | } 146 | 147 | /// Returns a unique value identifying the contained function pointer, which 148 | /// is used for recursion detection. 149 | pub fn ptr_value(&self) -> usize { 150 | self.fnc as usize 151 | } 152 | } 153 | 154 | impl<'i> Debug for NativeFn<'i> { 155 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 156 | f.debug_struct("NativeFn") 157 | .field("name", &self.name) 158 | .field("location", &self.location) 159 | .finish() 160 | } 161 | } 162 | 163 | impl<'i> Display for NativeFn<'i> { 164 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 165 | if let Some(name) = &self.name { 166 | let mut buf = String::new(); 167 | escaping::escape_alkyne_string(name, true, &mut buf); 168 | write!( 169 | f, 170 | "{}() ({}:{}:{})", 171 | buf, self.location.0, self.location.1, self.location.2 172 | ) 173 | } else { 174 | write!( 175 | f, 176 | " ({}:{}:{})", 177 | self.location.0, self.location.1, self.location.2 178 | ) 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/eval/value/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Alkyne runtime values. These consist of eight types: 16 | //! - `null`, the singleton, default value. 17 | //! - `bool`, a boolean. 18 | //! - `number`, a floating-point number. 19 | //! - `string`, an immutable string. 20 | //! - `list`, an array of values. 21 | //! - `object`, a hashtable with inheritance. 22 | //! - `fn`, a callable function. 23 | //! - `opaque`, an opaque value with a fixed set of operations. 24 | 25 | use std::fmt::Debug; 26 | use std::fmt::Display; 27 | 28 | use crate::eval::encode::alkyne::UclEncoding; 29 | use crate::eval::encode::Encoder; 30 | 31 | #[macro_use] 32 | pub mod native_macros; 33 | 34 | pub mod fns; 35 | pub use fns::Fn; 36 | pub use fns::NativeFn; 37 | 38 | pub mod seq; 39 | pub use seq::List; 40 | pub use seq::Str; 41 | 42 | pub mod object; 43 | pub use object::Object; 44 | 45 | pub mod convert; 46 | pub use convert::IntoValue; 47 | 48 | /// A `Value` is a Alkyne value. `Value`s can be safely shared across threads, 49 | /// though execution of a Alkyne file is single-threaded. 50 | #[derive(Clone, Debug)] 51 | pub enum Value<'i> { 52 | Null, 53 | Bool(bool), 54 | Number(f64), 55 | String(Str<'i>), 56 | List(List<'i>), 57 | Object(Object<'i>), 58 | Fn(Fn<'i>), 59 | NativeFn(NativeFn<'i>), 60 | Std, 61 | Range { start: i64, end: i64, step: i64 }, 62 | } 63 | 64 | impl<'i> Value<'i> { 65 | /// Returns the `Type` of this `Value`. 66 | pub fn ty(&self) -> Type { 67 | match self { 68 | Value::Null => Type::Null, 69 | Value::Bool(..) => Type::Bool, 70 | Value::Number(..) => Type::Number, 71 | Value::String(..) => Type::String, 72 | Value::List(..) => Type::List, 73 | Value::Object(..) => Type::Object, 74 | Value::Fn(..) | Value::NativeFn(..) => Type::Fn, 75 | _ => Type::Opaque, 76 | } 77 | } 78 | 79 | /// The "machine epsilon", the smallest value tolerated for the fractional 80 | /// part of an "integer" number. 81 | pub const EPSILON: f64 = 1e-12; 82 | 83 | /// Attempts to convert this value into an integer, assuming that it is a 84 | /// number with sufficiently small fractional part. 85 | /// 86 | /// If the conversion fails, the type of the value is returned instead. 87 | pub fn as_int(&self) -> Result { 88 | match self { 89 | Value::Number(f) if f.fract().abs() < Self::EPSILON => Ok(*f as i64), 90 | v => Err(v.ty()), 91 | } 92 | } 93 | } 94 | 95 | impl Display for Value<'_> { 96 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 97 | let buf = Encoder::::new().encode(self); 98 | write!( 99 | f, 100 | "{}", 101 | buf 102 | .as_ref() 103 | .map(|s| s.as_str()) 104 | .unwrap_or("") 105 | ) 106 | } 107 | } 108 | 109 | /// A type implementing `DebugInfo` can provide a user-displayable value 110 | /// containing debug information about itself (such as pointer values and 111 | /// ref-count information). 112 | pub trait DebugInfo { 113 | /// A user-displayable record type containing type-specific debug information. 114 | type Info: Display; 115 | 116 | /// Creates a value of `Info` describing `self` at this instant in time. 117 | fn debug_info(&self) -> Self::Info; 118 | } 119 | 120 | /// A `DebugInfoScalar` provides debug information for all of Alkyne's "scalar" 121 | /// types (null, bool, and number). 122 | #[derive(Copy, Clone, Debug)] 123 | pub struct DebugInfoScalar { 124 | /// A pile of bytes derived from a scalar value. 125 | pub bytes: u64, 126 | } 127 | 128 | impl Display for DebugInfoScalar { 129 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 130 | write!(f, "<{:#x}>", self.bytes) 131 | } 132 | } 133 | 134 | impl DebugInfo for () { 135 | type Info = DebugInfoScalar; 136 | 137 | fn debug_info(&self) -> Self::Info { 138 | DebugInfoScalar { bytes: 0 } 139 | } 140 | } 141 | 142 | impl DebugInfo for bool { 143 | type Info = DebugInfoScalar; 144 | 145 | fn debug_info(&self) -> Self::Info { 146 | DebugInfoScalar { bytes: *self as _ } 147 | } 148 | } 149 | 150 | impl DebugInfo for f64 { 151 | type Info = DebugInfoScalar; 152 | 153 | fn debug_info(&self) -> Self::Info { 154 | DebugInfoScalar { 155 | bytes: self.to_bits(), 156 | } 157 | } 158 | } 159 | 160 | /// A `Type` represents one of the seven Alkyne value types. 161 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] 162 | pub enum Type { 163 | Null, 164 | Bool, 165 | Number, 166 | String, 167 | List, 168 | Object, 169 | Fn, 170 | Opaque, 171 | } 172 | 173 | impl Type { 174 | pub fn name(self) -> &'static str { 175 | match self { 176 | Type::Null => "null", 177 | Type::Bool => "bool", 178 | Type::Number => "number", 179 | Type::String => "string", 180 | Type::List => "list", 181 | Type::Object => "object", 182 | Type::Fn => "function", 183 | Type::Opaque => "opaque", 184 | } 185 | } 186 | } 187 | 188 | impl Display for Type { 189 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 190 | write!(f, "{}", self.name()) 191 | } 192 | } 193 | 194 | /// A Alkyne-wrapped native Rust object. 195 | /// 196 | /// Types implementing this trait can be inserted into a Alkyne runtime as vtables, 197 | /// allowing for the runtime to interact with the underlying platform. 198 | /// 199 | /// The reported type is always `opaque`. 200 | pub trait Native: Send + Sync { 201 | /// Performs an indexing operation into `self`. 202 | /// 203 | /// Returns `None` if the operation fails for any reason. 204 | #[allow(unused)] 205 | fn index<'i>(&self, args: &[Value<'i>]) -> Option> { 206 | None 207 | } 208 | 209 | /// Performs a function call on `self`. 210 | /// 211 | /// Returns `None` if the function call is not supported. 212 | #[allow(unused)] 213 | fn call<'i>(&self, args: &[Value<'i>]) -> Option> { 214 | None 215 | } 216 | 217 | /// Begins an iteration operation on `self`, returning an iterator over 218 | /// itself. 219 | /// 220 | /// Returns `None` if this type does not support iteration. 221 | #[allow(unused)] 222 | fn iter<'i>(&self) -> Option>>>> { 223 | None 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/eval/value/native_macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Macros for creating native functions. 16 | 17 | /// Generates a span pointing at the current Rust source location, for use in 18 | /// errors originating from native code. 19 | #[macro_export] 20 | macro_rules! native_span { 21 | () => { 22 | $crate::syn::Span::synthetic(file!().as_ref()) 23 | }; 24 | } 25 | 26 | /// Generates a specialized native error, and returns immediately. 27 | /// 28 | /// Compare `alkyne::eval::value::error!()`. 29 | #[macro_export] 30 | macro_rules! native_err { 31 | ($ctx:expr, $($tt:tt)*) => {{ 32 | error!($ctx, native_span!(), $($tt)*) 33 | }} 34 | } 35 | 36 | /// Generates a specialized native error, but emits it to stderr rather than 37 | /// returning immediately. 38 | #[macro_export] 39 | macro_rules! native_err_no_return { 40 | ($ctx:expr, $($tt:tt)*) => {{ 41 | eprintln!("{}", $crate::eval::error::EvalError::new( 42 | $ctx, native_span!(), format!($($tt)*))); 43 | }} 44 | } 45 | 46 | /// Asserts that a Alkyne value is of a particular type: 47 | /// ``` 48 | /// let my_obj = assert_alkyne_type!(ctx, val: obj); 49 | /// ``` 50 | /// Note that `val` must be an identifier. 51 | /// 52 | /// Supported types are: `any`, `null`, `bool`, `number`, `integer`, `string`, 53 | /// `list`, `object`, and `fn`. Of these, `any` simply always succeeds, 54 | /// `integer` tests for a number with zero functional part, and `fn` matches 55 | /// both Alkyne and native functions (and as such returns a `Value`). 56 | #[macro_export] 57 | macro_rules! assert_alkyne_type { 58 | ($ctx:ident, $arg:ident: any) => {$arg}; 59 | ($ctx:ident, $arg:ident: null) => { 60 | assert_alkyne_type!(@match $ctx, $arg, Null, null) 61 | }; 62 | ($ctx:ident, $arg:ident: bool) => { 63 | assert_alkyne_type!(@match $ctx, $arg, Bool, bool) 64 | }; 65 | ($ctx:ident, $arg:ident: number) => { 66 | assert_alkyne_type!(@match $ctx, $arg, Number, number) 67 | }; 68 | ($ctx:ident, $arg:ident: integer) => { 69 | match $arg.as_int() { 70 | Ok(x) => x, 71 | Err(t) => native_err!($ctx, "expected integer, got {}", t), 72 | } 73 | }; 74 | ($ctx:ident, $arg:ident: string) => { 75 | assert_alkyne_type!(@match $ctx, $arg, String, string) 76 | }; 77 | ($ctx:ident, $arg:ident: list) => { 78 | assert_alkyne_type!(@match $ctx, $arg, List, list) 79 | }; 80 | ($ctx:ident, $arg:ident: object) => { 81 | assert_alkyne_type!(@match $ctx, $arg, Object, object) 82 | }; 83 | 84 | ($ctx:ident, $arg:ident: fn) => { 85 | match $arg { 86 | f @ $crate::eval::value::Value::Fn(_) => f, 87 | f @ $crate::eval::value::Value::NativeFn(_) => f, 88 | v => native_err!($ctx, "expected fn, got {}", v.ty()), 89 | } 90 | }; 91 | 92 | (@match $ctx:ident, $arg:ident, $variant:ident, $expected:ident) => { 93 | match $arg { 94 | $crate::eval::value::Value::$variant(x) => x, 95 | v => native_err!($ctx, "expected {}, got {}", 96 | stringify!($expected), v.ty()), 97 | } 98 | } 99 | } 100 | 101 | /// Generates a native Alkyne function, saving users significant boilerplate. 102 | /// 103 | /// Syntax is as follows: 104 | /// ``` 105 | /// native_fn!((ctx, arg1: number, arg2: string) { 106 | /// // `ctx` is the current Alkyne interpreter context, and is optional. 107 | /// // `arg1` and `arg2` are of types `f64` and `Str`, respectively. 108 | /// // ... 109 | /// }) 110 | /// ``` 111 | /// 112 | /// The generated function automatically performs type-assertions on arguments, 113 | /// and checks that the argument list is of the right size. The body may have 114 | /// any type which can be converted to a `Value` (by `IntoValueResult`), or it 115 | /// can explictly `return` a `Result`. 116 | #[macro_export] 117 | macro_rules! native_fn { 118 | (($($arg:ident: $ty:ident),* $(,)?) $block:block) => { 119 | native_fn!((_, $($arg: $ty,)*) $block) 120 | }; 121 | (($ctx:tt $(, $arg:ident: $ty:ident)* $(,)?) $block:block) => {{ 122 | #[allow(unreachable_code)] 123 | let f = $crate::eval::value::NativeFn::new(|ctx, args| { 124 | const __ARG_COUNT: usize = native_fn!(@arg_count $($arg)*); 125 | if args.len() != __ARG_COUNT { 126 | native_err!(ctx, "expected {} arguments, got {}", 127 | __ARG_COUNT, args.len()) 128 | } 129 | 130 | let count = 0; 131 | let _ = count; 132 | native_fn!(@arg_bind count, ctx, args, $($arg $ty)*); 133 | 134 | let $ctx = ctx; 135 | $crate::eval::value::convert::IntoValueResult::into_value_result($block) 136 | }, (file!(), line!(), column!())); 137 | f 138 | }}; 139 | 140 | (@arg_bind $count:ident, $ctx:ident, $args:ident,) => {}; 141 | (@arg_bind $count:ident, $ctx:ident, $args:ident, 142 | $arg:ident $ty:ident $($rest:tt)*) => { 143 | let $arg = { 144 | let arg = $args[$count].clone(); 145 | assert_alkyne_type!($ctx, arg: $ty) 146 | }; 147 | let $count = $count + 1; 148 | let _ = $count; 149 | native_fn!(@arg_bind $count, $ctx, $args, $($rest)*) 150 | }; 151 | 152 | (@arg_count) => {0}; 153 | (@arg_count $_:ident $($rest:ident)*) => { 154 | 1 + native_fn!(@arg_count $($rest)*) 155 | }; 156 | } 157 | -------------------------------------------------------------------------------- /src/eval/value/object.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Alkyne Objects. 16 | 17 | use std::collections::hash_map::Iter as MapIter; 18 | use std::collections::HashMap; 19 | use std::fmt; 20 | use std::sync::atomic::AtomicBool; 21 | use std::sync::atomic::Ordering; 22 | use std::sync::Arc; 23 | use std::sync::RwLock; 24 | use std::sync::RwLockReadGuard; 25 | 26 | use crate::eval::value::seq::Str; 27 | use crate::eval::value::DebugInfo; 28 | use crate::eval::value::Value; 29 | 30 | /// An `Object` is a Alkyne object, consisting of a set of fields, and a potential 31 | /// super object, wrapped up in an `Arc` pointer. 32 | /// 33 | /// `Object`s are also used as the value representation of variable scopes. 34 | #[derive(Clone, Debug)] 35 | pub struct Object<'i>(Arc>); 36 | 37 | #[derive(Debug)] 38 | struct Inner<'i> { 39 | root: Option>, 40 | zuper: Option>, 41 | fields: RwLock, Value<'i>>>, 42 | frozen: AtomicBool, 43 | } 44 | 45 | impl<'i> Object<'i> { 46 | /// Creates a new `Object` with no super object. 47 | pub fn new() -> Self { 48 | Object(Arc::new(Inner { 49 | root: None, 50 | zuper: None, 51 | fields: RwLock::new(HashMap::new()), 52 | frozen: AtomicBool::new(false), 53 | })) 54 | } 55 | 56 | /// Creates a new `Object` with `self` as the super object. 57 | pub fn extend(self) -> Self { 58 | Object(Arc::new(Inner { 59 | zuper: Some(self.clone()), 60 | root: Some(self.root()), 61 | fields: RwLock::new(HashMap::new()), 62 | frozen: AtomicBool::new(false), 63 | })) 64 | } 65 | 66 | /// Returns the root `Object` of this object, i.e., the top of the 67 | /// super-object chain. 68 | pub fn root(&self) -> Self { 69 | match &self.0.root { 70 | Some(root) => root.clone(), 71 | None => self.clone(), 72 | } 73 | } 74 | 75 | /// Looks up `key` in this object and all of its parents. 76 | pub fn lookup(&self, key: &str) -> Option> { 77 | let mut current = self; 78 | loop { 79 | if let Some(v) = current.0.fields.read().unwrap().get(key) { 80 | return Some(v.clone()); 81 | } 82 | 83 | if let Some(zuper) = ¤t.0.zuper { 84 | current = zuper 85 | } else { 86 | return None; 87 | } 88 | } 89 | } 90 | 91 | /// Defines `key` in this object. This function will panic if this object 92 | /// has been frozen. 93 | pub fn define( 94 | &self, 95 | key: impl Into>, 96 | val: impl Into>, 97 | allow_underscore: bool, 98 | ) { 99 | if self.0.frozen.load(Ordering::SeqCst) { 100 | panic!("tried to mutate frozen object; this is a bug") 101 | } 102 | 103 | let key = key.into(); 104 | let mut val = val.into(); 105 | 106 | if key.as_sliced() == "_" && !allow_underscore { 107 | return; 108 | } 109 | 110 | if let Value::Fn(fnc) = &mut val { 111 | fnc.rename(key.clone()) 112 | } 113 | 114 | if let Value::NativeFn(fnc) = &mut val { 115 | fnc.rename(key.clone()) 116 | } 117 | 118 | self.0.fields.write().unwrap().insert(key, val); 119 | } 120 | 121 | /// Freezes this `Object`; should be called one all fields have been evaluated, 122 | /// to catch any future attempts to define more fields. 123 | pub fn freeze(&self) { 124 | self.0.frozen.store(true, Ordering::SeqCst) 125 | } 126 | 127 | /// Checks whether this `Object` has been frozen. 128 | pub fn frozen(&self) -> bool { 129 | self.0.frozen.load(Ordering::SeqCst) 130 | } 131 | 132 | /// Returns the super object of this `Object`, if it has one. 133 | pub fn zuper(&self) -> Option> { 134 | self.0.zuper.clone() 135 | } 136 | 137 | /// Returns an iterator over the fields of this `Object`. 138 | pub fn fields<'a>( 139 | &'a self, 140 | ) -> impl Iterator, &'a Value<'i>)> { 141 | let mut fields = Fields { 142 | lock: self.0.fields.read().unwrap(), 143 | iter: None, 144 | }; 145 | unsafe { 146 | // This is safe, because `lock` gets moved into the same struct as the 147 | // iterator, and it will never be moved from, so they have equal 148 | // lifetimes. 149 | use std::mem::transmute; 150 | fields.iter = Some(transmute(fields.lock.iter())); 151 | } 152 | fields 153 | } 154 | 155 | /// Returns an iterator over all the recursive fields of this `Object`. 156 | pub fn iter<'a>( 157 | &'a self, 158 | ) -> impl Iterator, &'a Value<'i>, &'a Object<'i>)> { 159 | Iter { 160 | obj: Some(self), 161 | field_lock: None, 162 | field_iter: None, 163 | } 164 | } 165 | 166 | pub fn ptr_value(&self) -> usize { 167 | self.0.as_ref() as *const _ as usize 168 | } 169 | } 170 | 171 | /// A `DebugInfoObj` provides debug information for a Alkyne `Object`. 172 | #[derive(Copy, Clone, Debug)] 173 | pub struct DebugInfoObj { 174 | /// The address of this object. 175 | pub addr: usize, 176 | /// The number of ref-counts for this object. 177 | pub refs: usize, 178 | /// Whether this object has been frozen. 179 | pub frozen: bool, 180 | /// The address of the super-object, if any. 181 | pub zuper_addr: Option, 182 | /// The address of the root object, if any. 183 | pub root_addr: Option, 184 | } 185 | 186 | impl fmt::Display for DebugInfoObj { 187 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 188 | write!(f, "<{:#x}", self.addr)?; 189 | if let Some(zuper_addr) = self.zuper_addr { 190 | write!(f, " -> {:#x}", zuper_addr)?; 191 | match self.root_addr { 192 | Some(root_addr) if root_addr != zuper_addr => { 193 | write!(f, "..{:#x}", root_addr)? 194 | } 195 | _ => {} 196 | } 197 | } 198 | 199 | let mutability = if self.frozen { "fzn" } else { "mut" }; 200 | write!(f, ", arc = {}, {}>", self.refs, mutability) 201 | } 202 | } 203 | 204 | impl DebugInfo for Object<'_> { 205 | type Info = DebugInfoObj; 206 | 207 | fn debug_info(&self) -> Self::Info { 208 | DebugInfoObj { 209 | addr: self.ptr_value(), 210 | refs: Arc::strong_count(&self.0), 211 | frozen: self.frozen(), 212 | zuper_addr: self.0.zuper.as_ref().map(Object::ptr_value), 213 | root_addr: self.0.root.as_ref().map(Object::ptr_value), 214 | } 215 | } 216 | } 217 | 218 | struct Fields<'i, 'a> { 219 | lock: RwLockReadGuard<'a, HashMap, Value<'i>>>, 220 | iter: Option, Value<'i>>>, 221 | } 222 | 223 | impl<'i, 'a> Iterator for Fields<'i, 'a> { 224 | type Item = (&'a Str<'i>, &'a Value<'i>); 225 | 226 | fn next(&mut self) -> Option { 227 | self.iter.as_mut().unwrap().next() 228 | } 229 | } 230 | 231 | struct Iter<'i, 'a> { 232 | obj: Option<&'a Object<'i>>, 233 | field_lock: Option, Value<'i>>>>, 234 | field_iter: Option, Value<'i>>>, 235 | } 236 | 237 | impl<'i, 'a> Iterator for Iter<'i, 'a> { 238 | type Item = (&'a Str<'i>, &'a Value<'i>, &'a Object<'i>); 239 | 240 | fn next(&mut self) -> Option { 241 | loop { 242 | self.obj?; 243 | let obj = self.obj.as_mut().unwrap(); 244 | 245 | if self.field_iter.is_none() { 246 | if self.field_lock.is_none() { 247 | self.field_lock = Some(obj.0.fields.read().unwrap()); 248 | } 249 | let field_lock = self.field_lock.as_ref().unwrap(); 250 | 251 | unsafe { 252 | // Extend the lifetime of `iter()` to last until the lifetime 253 | // of `field_lock`. This is safe, because because the extended 254 | // references all point into `field_lock`, which is semantically 255 | // a shared reference of lifetime 'a. 256 | use std::mem::transmute; 257 | self.field_iter = Some(transmute(field_lock.iter())); 258 | } 259 | } 260 | let iter = self.field_iter.as_mut().unwrap(); 261 | let item = iter.next().map(|(k, v)| (k, v, *obj)); 262 | 263 | if item.is_none() { 264 | self.obj = obj.0.zuper.as_ref(); 265 | self.field_lock = None; 266 | self.field_iter = None; 267 | } else { 268 | break item; 269 | } 270 | } 271 | } 272 | } 273 | 274 | impl fmt::Display for Object<'_> { 275 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 276 | write!(f, "{{")?; 277 | let mut first = false; 278 | for (k, v) in self.fields() { 279 | if first { 280 | write!(f, "{}: {}", k, v)?; 281 | first = false; 282 | } else { 283 | write!(f, ", {}: {}", k, v)?; 284 | } 285 | } 286 | write!(f, "}}") 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /src/eval/value/seq.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Alkyne list-like values. 16 | 17 | use std::borrow::Borrow; 18 | use std::fmt; 19 | use std::hash::Hash; 20 | use std::hash::Hasher; 21 | use std::ops::Deref; 22 | use std::ops::Range; 23 | use std::sync::Arc; 24 | 25 | use crate::eval::escaping; 26 | use crate::eval::value::DebugInfo; 27 | use crate::eval::value::Value; 28 | 29 | /// A Alkyne string, i.e., an immutable, reference-counted slice of UTF-8 bytes. 30 | pub type Str<'i> = ArcSlice<'i, str>; 31 | 32 | /// A Alkyne list, i.e., an immutable, reference-counted slice of Alkyne values. 33 | pub type List<'i> = ArcSlice<'i, [Value<'i>]>; 34 | 35 | /// Represents a block of data that can be put into an `ArcSlice`. 36 | /// 37 | /// For example, `[T]` implements this trait. 38 | #[allow(clippy::len_without_is_empty)] 39 | pub trait Seq { 40 | /// Slices along `range`. 41 | /// 42 | /// Returns `None` if the range is invalid. 43 | fn slice(&self, range: Range) -> Option<&Self>; 44 | 45 | /// Returns the length of the underlying data block. 46 | fn len(&self) -> usize; 47 | 48 | /// Creates an empty value of `Self` with any lifetime. 49 | fn empty<'a>() -> &'a Self; 50 | 51 | /// Converts a reference to `Self` into a unique integer. 52 | fn ptr_value(&self) -> usize; 53 | } 54 | 55 | impl Seq for [T] { 56 | #[inline] 57 | fn slice(&self, range: Range) -> Option<&Self> { 58 | self.get(range) 59 | } 60 | 61 | #[inline] 62 | fn len(&self) -> usize { 63 | <[T]>::len(self) 64 | } 65 | 66 | #[inline] 67 | fn empty<'a>() -> &'a Self { 68 | &[] 69 | } 70 | 71 | #[inline] 72 | fn ptr_value(&self) -> usize { 73 | self.as_ptr() as usize 74 | } 75 | } 76 | 77 | impl Seq for str { 78 | #[inline] 79 | fn slice(&self, range: Range) -> Option<&Self> { 80 | self.get(range) 81 | } 82 | 83 | #[inline] 84 | fn len(&self) -> usize { 85 | str::len(self) 86 | } 87 | 88 | #[inline] 89 | fn empty<'a>() -> &'a Self { 90 | "" 91 | } 92 | 93 | #[inline] 94 | fn ptr_value(&self) -> usize { 95 | self.as_ptr() as usize 96 | } 97 | } 98 | 99 | /// An `ArcSlice` is a slice of a reference-counted block of data. 100 | /// 101 | /// `ArcSlice`es contain an `Arc` pointing to the complete block of data, and 102 | /// a range of that data. This means that slicing an `ArcSlice` is as cheap as 103 | /// am atomic increment. 104 | /// 105 | /// An `ArcSlice` can also point to static data, bounded by the lifetime `'i`. 106 | /// 107 | /// `ArcSlice` implements `Deref`, but only returns the subrange it 108 | /// points to, not the whole block of data. 109 | #[derive(Debug)] 110 | pub struct ArcSlice<'i, S: ?Sized> { 111 | inner: Inner<'i, S>, 112 | start: usize, 113 | end: usize, 114 | } 115 | 116 | #[derive(Debug)] 117 | enum Inner<'i, S: ?Sized> { 118 | Empty, 119 | Static(&'i S), 120 | Dynamic(Arc), 121 | } 122 | 123 | impl<'i, S: Seq + ?Sized> Inner<'i, S> { 124 | fn as_ref(&self) -> &S { 125 | match self { 126 | Inner::Empty => S::empty(), 127 | Inner::Static(s) => s, 128 | Inner::Dynamic(s) => s.as_ref(), 129 | } 130 | } 131 | } 132 | 133 | /// The allocation type of an `ArcSlice`. 134 | #[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)] 135 | pub enum AllocType { 136 | /// An empty data block. 137 | Empty, 138 | /// A "static" data block, whose lifetime outlives the interpreter lifetime. 139 | Static, 140 | /// A "dynamic" data block, which was created by the interpreter and needs to 141 | /// be ref-counted; includes the current ref-count. 142 | Dynamic(usize), 143 | } 144 | 145 | impl fmt::Display for AllocType { 146 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 147 | match self { 148 | AllocType::Empty => write!(f, "empty"), 149 | AllocType::Static => write!(f, "static"), 150 | AllocType::Dynamic(count) => write!(f, "arc = {}", count), 151 | } 152 | } 153 | } 154 | 155 | /// A `DebugInfoSeq` provides debug information for a Alkyne "sequence" type. 156 | #[derive(Copy, Clone, Debug)] 157 | pub struct DebugInfoSeq { 158 | /// The address of the underlying data. 159 | pub addr: usize, 160 | /// The type of allocation this `ArcSlice` is using. 161 | pub alloc_ty: AllocType, 162 | /// The start of the subrange of the allocation this `ArcSlice` points to. 163 | pub start: usize, 164 | /// The end of the subrange of the allocation this `ArcSlice` points to. 165 | pub end: usize, 166 | } 167 | 168 | impl fmt::Display for DebugInfoSeq { 169 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 170 | write!( 171 | f, 172 | "<{:#x}[{}..{}], {}>", 173 | self.addr, self.start, self.end, self.alloc_ty 174 | ) 175 | } 176 | } 177 | 178 | impl DebugInfo for ArcSlice<'_, S> { 179 | type Info = DebugInfoSeq; 180 | 181 | fn debug_info(&self) -> Self::Info { 182 | DebugInfoSeq { 183 | addr: self.inner.as_ref().ptr_value(), 184 | alloc_ty: match &self.inner { 185 | Inner::Empty => AllocType::Empty, 186 | Inner::Static(..) => AllocType::Static, 187 | Inner::Dynamic(arc) => AllocType::Dynamic(Arc::strong_count(arc)), 188 | }, 189 | start: self.start, 190 | end: self.end, 191 | } 192 | } 193 | } 194 | 195 | impl<'i, S: Seq + ?Sized> ArcSlice<'i, S> { 196 | /// Creates a new `ArcSlice` pointing to all of `values`. 197 | pub fn new(values: impl Into>) -> Self { 198 | let values = values.into(); 199 | let len = values.len(); 200 | if len == 0 { 201 | return Self::empty(); 202 | } 203 | Self { 204 | inner: Inner::Dynamic(values), 205 | start: 0, 206 | end: len, 207 | } 208 | } 209 | 210 | /// Creates an empty `ArcSlice`. 211 | pub fn empty() -> Self { 212 | Self { 213 | inner: Inner::Empty, 214 | start: 0, 215 | end: 0, 216 | } 217 | } 218 | 219 | /// Creates an `ArcSlice` that points to a static, rather than ref-counted, 220 | /// value. 221 | pub fn new_static(ptr: &'i S) -> Self { 222 | if ptr.len() == 0 { 223 | return Self::empty(); 224 | } 225 | Self { 226 | inner: Inner::Static(ptr), 227 | start: 0, 228 | end: ptr.len(), 229 | } 230 | } 231 | 232 | /// Slices the underlying data according to the start and end indices. 233 | pub fn as_sliced(&self) -> &S { 234 | self 235 | .inner 236 | .as_ref() 237 | .slice(self.start..self.end) 238 | .expect("ArcSlice range cannot be out of bounds") 239 | } 240 | 241 | /// Reslices the underlying data, returning a copy of `self` with the indices 242 | /// further constrained. 243 | /// 244 | /// Note that indexing is local: 245 | /// ``` 246 | /// let xs = ArcSlice::new([1, 2, 3, 4, 5]); 247 | /// assert_eq!(xs.slice(2..5).slice(1..2).as_sliced(), &[4]); 248 | /// ``` 249 | pub fn slice(&self, range: Range) -> Option { 250 | let inner = self.inner.as_ref(); 251 | 252 | let start = range.start + self.start; 253 | let end = range.end + self.start; 254 | inner.slice(start..end)?; 255 | 256 | if range.start == range.end { 257 | return Some(Self::empty()); 258 | } 259 | 260 | Some(ArcSlice { 261 | start, 262 | end, 263 | ..self.clone() 264 | }) 265 | } 266 | 267 | /// Returns the subrange of the underlying data that this `ArcSlice` points 268 | /// to. 269 | pub fn range(&self) -> Range { 270 | self.start..self.end 271 | } 272 | 273 | /// Returns the length of the subrange that this `ArcSlice` points to. 274 | pub fn len(&self) -> usize { 275 | self.end - self.start 276 | } 277 | 278 | /// Returns whether this `ArcSlice` is empty. 279 | pub fn is_empty(&self) -> bool { 280 | self.len() == 0 281 | } 282 | 283 | /// Compares `self` and `other` for pointer equality, i.e., they point to the 284 | /// same subrange of the same block of data. 285 | pub fn ptr_eq(&self, other: &Self) -> bool { 286 | self.start == other.start 287 | && self.end == other.end 288 | && self.inner.as_ref().ptr_value() == other.inner.as_ref().ptr_value() 289 | } 290 | } 291 | 292 | impl Deref for ArcSlice<'_, S> { 293 | type Target = S; 294 | 295 | fn deref(&self) -> &Self::Target { 296 | self.as_sliced() 297 | } 298 | } 299 | 300 | impl Borrow for ArcSlice<'_, S> { 301 | fn borrow(&self) -> &S { 302 | self.as_sliced() 303 | } 304 | } 305 | 306 | impl>> From for ArcSlice<'_, S> { 307 | fn from(seq: T) -> Self { 308 | Self::new(seq) 309 | } 310 | } 311 | 312 | impl Hash for ArcSlice<'_, S> { 313 | #[inline] 314 | fn hash(&self, state: &mut H) { 315 | self.as_sliced().hash(state) 316 | } 317 | } 318 | 319 | impl PartialEq for ArcSlice<'_, S> { 320 | #[inline] 321 | fn eq(&self, other: &Self) -> bool { 322 | self.as_sliced().eq(other.as_sliced()) 323 | } 324 | } 325 | 326 | impl Eq for ArcSlice<'_, S> {} 327 | 328 | impl Clone for ArcSlice<'_, S> { 329 | fn clone(&self) -> Self { 330 | ArcSlice { 331 | inner: match &self.inner { 332 | Inner::Empty => Inner::Empty, 333 | Inner::Static(p) => Inner::Static(p), 334 | Inner::Dynamic(arc) => Inner::Dynamic(arc.clone()), 335 | }, 336 | start: self.start, 337 | end: self.end, 338 | } 339 | } 340 | } 341 | 342 | impl fmt::Display for Str<'_> { 343 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 344 | let mut buf = String::new(); 345 | escaping::escape_alkyne_string(&*self, false, &mut buf); 346 | write!(f, "{}", buf) 347 | } 348 | } 349 | 350 | impl fmt::Display for List<'_> { 351 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 352 | write!(f, "[")?; 353 | let mut first = false; 354 | for v in self.iter() { 355 | if first { 356 | write!(f, "{}", v)?; 357 | first = false; 358 | } else { 359 | write!(f, ", {}", v)?; 360 | } 361 | } 362 | write!(f, "]") 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /src/exec/fs.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Virtual file systems for executor file lookups. 16 | 17 | use std::cell::RefCell; 18 | use std::collections::HashMap; 19 | use std::env; 20 | use std::fs; 21 | use std::io; 22 | use std::path::Path; 23 | use std::path::PathBuf; 24 | use std::sync::Mutex; 25 | 26 | use toolshed::Arena; 27 | 28 | /// A virtual file system, allowing for access to access to files for execution. 29 | /// 30 | /// For example, this could be a collection of in-memory files, or it could 31 | /// be a thin wrapper around the local file system (or a subset of it). 32 | pub trait FileSys: Send + Sync { 33 | /// Looks up the file with the given name, 34 | fn read_file(&self, file_name: &Path) -> io::Result<&str>; 35 | } 36 | 37 | /// The local file system. 38 | /// 39 | /// A `Local` can specify a custom "working directory" (relative to which 40 | /// relative paths are resolved) and a "required prefix", such that 41 | /// canonicalized paths cannot escape a certain subset of the local file system. 42 | pub struct Local { 43 | inner: Mutex, 44 | } 45 | 46 | struct LocalInner { 47 | arena: Arena, 48 | // NOTE: the pointers in `files` live for as long as `arena` does. 49 | files: RefCell>, 50 | 51 | cwd: PathBuf, 52 | prefix: Option, 53 | } 54 | 55 | // It's actually Send, but we need to force the implementation because it has 56 | // raw self-pointers inside it. 57 | unsafe impl Send for LocalInner {} 58 | 59 | impl Local { 60 | /// Creates a new `Local` with the current process's working directory as both 61 | /// the working directory and the prefix. 62 | pub fn new() -> Self { 63 | let cwd = 64 | env::current_dir().expect("cannot read current working directory"); 65 | let cwd_clone = cwd.clone(); 66 | Self::with_options(Some(cwd_clone), Some(cwd)) 67 | } 68 | 69 | /// Creates a new `Local` with the given working directory and prefix. 70 | pub fn with_options(cwd: Option, prefix: Option) -> Self { 71 | Self { 72 | inner: Mutex::new(LocalInner { 73 | arena: Arena::new(), 74 | files: RefCell::new(HashMap::new()), 75 | 76 | cwd: cwd.unwrap_or_else(|| { 77 | env::current_dir().expect("cannot read current working directory") 78 | }), 79 | prefix, 80 | }), 81 | } 82 | } 83 | } 84 | 85 | impl FileSys for Local { 86 | fn read_file(&self, file_name: &Path) -> io::Result<&str> { 87 | let inner = self.inner.lock().unwrap(); 88 | 89 | let mut files = inner.files.borrow_mut(); 90 | if let Some(ptr) = files.get(file_name) { 91 | // SAFETY: This is safe because we only put arena pointers into the map. 92 | // Those pointers are always alive and valid when this function is called. 93 | unsafe { return Ok(&**ptr) } 94 | } 95 | 96 | let mut full_path = inner.cwd.clone(); 97 | full_path.push(file_name); 98 | if let Some(prefix) = &inner.prefix { 99 | full_path = fs::canonicalize(&full_path)?; 100 | if !full_path.starts_with(prefix) { 101 | return Err(io::Error::new( 102 | io::ErrorKind::PermissionDenied, 103 | "attempted to escape local filesystem prefix", 104 | )); 105 | } 106 | } 107 | 108 | let text = inner 109 | .arena 110 | .alloc_string(std::fs::read_to_string(full_path)?) 111 | as *const str; 112 | files.insert(file_name.to_path_buf(), text); 113 | // SAFETY: This is necessary to convince the compiler that the returned 114 | // reference can outlive the mutex lock. 115 | Ok(unsafe { &*text }) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/exec/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Executors for different modalities of Alkyne execution. 16 | 17 | pub mod fs; 18 | pub mod parallel; 19 | pub mod repl; 20 | -------------------------------------------------------------------------------- /src/exec/parallel.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Execution environment for evaluating many Alkyne files at once. 16 | 17 | #![allow(warnings)] 18 | 19 | use std::collections::HashMap; 20 | use std::io; 21 | use std::path::Path; 22 | use std::sync::atomic::AtomicUsize; 23 | use std::sync::atomic::Ordering; 24 | use std::sync::mpsc; 25 | use std::sync::Mutex; 26 | 27 | use chashmap::CHashMap; 28 | 29 | use thread_local::ThreadLocal; 30 | 31 | use crate::eval::value::Value; 32 | use crate::eval::Context; 33 | use crate::exec::fs::FileSys; 34 | use crate::syn; 35 | 36 | /// A parallel executor, which can execute a large set of Alkyne files 37 | /// simulataneously. 38 | pub struct Executor<'i, Fs> { 39 | evaluated_files: CHashMap<&'i Path, EvalState<'i>>, 40 | fs: &'i Fs, 41 | arenas: &'i ThreadLocal, 42 | log_output: Mutex>, 43 | errors: AtomicUsize, 44 | } 45 | 46 | enum EvalState<'i> { 47 | Complete(Option>), 48 | Waiting(Vec>>>), 49 | } 50 | 51 | impl<'i, Fs> Executor<'i, Fs> 52 | where 53 | Fs: FileSys, 54 | { 55 | /// Constructs a new `Executor`, using the given file system, arena source, 56 | /// and log sink. 57 | pub fn new( 58 | fs: &'i Fs, 59 | arenas: &'i ThreadLocal, 60 | log_output: impl io::Write + Send + 'i, 61 | ) -> Self { 62 | Executor { 63 | evaluated_files: CHashMap::new(), 64 | fs, 65 | arenas, 66 | log_output: Mutex::new(Box::new(log_output)), 67 | errors: AtomicUsize::new(0), 68 | } 69 | } 70 | 71 | /// Executes the given set of files with the given level of parallism. 72 | pub fn exec_files( 73 | &self, 74 | file_names: impl IntoIterator, 75 | parallelism: usize, 76 | ) -> Result>, usize> { 77 | let file_names = file_names.into_iter().collect::>(); 78 | let next_work_item = AtomicUsize::new(0); 79 | crossbeam::scope(|s| { 80 | for i in 0..parallelism { 81 | s.builder() 82 | .name(format!("alkyne-evaluator-{}", i)) 83 | .stack_size(1024 * 1024 * 8) // 8 MB. 84 | .spawn(|_| loop { 85 | let idx = next_work_item.fetch_add(1, Ordering::SeqCst); 86 | if idx >= file_names.len() { 87 | return; 88 | } 89 | let file_name = file_names[idx]; 90 | match self.lookup_or_exec(file_name) { 91 | Ok(v) => v, 92 | Err(chan) => chan.recv().unwrap(), 93 | }; 94 | }) 95 | .unwrap(); 96 | } 97 | }) 98 | .unwrap(); 99 | 100 | let errors = self.errors.load(Ordering::SeqCst); 101 | if errors > 0 { 102 | return Err(errors); 103 | } 104 | 105 | let mut values = HashMap::new(); 106 | for file_name in file_names.iter() { 107 | let val = match &*self.evaluated_files.get(file_name).unwrap() { 108 | EvalState::Complete(Some(v)) => v.clone(), 109 | _ => unreachable!(), 110 | }; 111 | values.insert(*file_name, val); 112 | } 113 | 114 | Ok(values) 115 | } 116 | 117 | /// Attempts to look up `file_name`'s computed result, or, if unavailable, it 118 | /// computes it itself. 119 | fn lookup_or_exec( 120 | &self, 121 | file_name: &'i Path, 122 | ) -> Result>, mpsc::Receiver>>> { 123 | let mut ret = None; 124 | self.evaluated_files.upsert( 125 | file_name, 126 | || EvalState::Waiting(Vec::new()), 127 | |v| match v { 128 | EvalState::Complete(v) => ret = Some(Ok(v.clone())), 129 | EvalState::Waiting(chans) => { 130 | let (tx, rx) = mpsc::sync_channel(1); 131 | chans.push(tx); 132 | ret = Some(Err(rx)); 133 | } 134 | }, 135 | ); 136 | 137 | if let Some(ret) = ret { 138 | return ret; 139 | } 140 | 141 | let result = self.exec(file_name); 142 | self.evaluated_files.alter(file_name, |v| match v { 143 | Some(EvalState::Waiting(chans)) => { 144 | for chan in chans { 145 | chan.send(result.clone()).unwrap(); 146 | } 147 | Some(EvalState::Complete(result.clone())) 148 | } 149 | Some(v) => Some(v), 150 | None => None, 151 | }); 152 | 153 | Ok(result) 154 | } 155 | 156 | /// Actually executes some Alkyne. 157 | fn exec(&self, file_name: &'i Path) -> Option> { 158 | writeln!( 159 | self.log_output.lock().unwrap(), 160 | "info: queueing {}...", 161 | file_name.display() 162 | ) 163 | .unwrap(); 164 | 165 | let text = match self.fs.read_file(file_name) { 166 | Ok(s) => s, 167 | Err(e) => { 168 | self.errors.fetch_add(1, Ordering::SeqCst); 169 | writeln!( 170 | self.log_output.lock().unwrap(), 171 | "error: cannot read file {}: {}", 172 | file_name.display(), 173 | e 174 | ) 175 | .unwrap(); 176 | return None; 177 | } 178 | }; 179 | 180 | let arena = self.arenas.get_or(|| syn::Arena::new()); 181 | let ast = match syn::parse(file_name.as_ref(), text, arena) { 182 | Ok(a) => a, 183 | Err(e) => { 184 | self.errors.fetch_add(1, Ordering::SeqCst); 185 | write!(self.log_output.lock().unwrap(), "{}", e).unwrap(); 186 | return None; 187 | } 188 | }; 189 | 190 | let mut ctx = Context::new(); 191 | let val = match ctx.eval(ast, |file_name| { 192 | match self.lookup_or_exec(file_name.as_ref()) { 193 | Ok(v) => v, 194 | Err(chan) => chan.recv().unwrap(), 195 | } 196 | }) { 197 | Ok(v) => v, 198 | Err(e) => { 199 | self.errors.fetch_add(1, Ordering::SeqCst); 200 | write!(self.log_output.lock().unwrap(), "{}", e).unwrap(); 201 | return None; 202 | } 203 | }; 204 | 205 | writeln!( 206 | self.log_output.lock().unwrap(), 207 | "info: finished {}", 208 | file_name.display() 209 | ) 210 | .unwrap(); 211 | Some(val) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/exec/repl.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Execution environment for the Alkyne Read-Eval-Print Loop. 16 | 17 | use std::convert::Infallible; 18 | use std::io; 19 | use std::time::Duration; 20 | use std::time::Instant; 21 | 22 | use rustyline::error::ReadlineError; 23 | use rustyline::Editor; 24 | 25 | use crate::eval::encode::alkyne::UclEncoding; 26 | use crate::eval::encode::alkyne::VerboseEncoding; 27 | use crate::eval::encode::json::JsonEncoding; 28 | use crate::eval::encode::EncodeError; 29 | use crate::eval::encode::Encoder; 30 | use crate::eval::value::Value; 31 | use crate::eval::Context; 32 | use crate::syn; 33 | 34 | const DOUBLE_CTRL_C_THRESHOLD: Duration = Duration::from_millis(300); 35 | 36 | /// All state for the REPL. 37 | pub struct Executor<'i> { 38 | arena: &'i syn::Arena, 39 | context: Context<'i>, 40 | editor: Editor<()>, 41 | last_ctrl_c: Instant, 42 | print_function: Box) -> Result>>, 43 | } 44 | 45 | impl<'i> Executor<'i> { 46 | /// Creates a new REPL; call `execute_loop()` to run it. 47 | pub fn new(arena: &'i syn::Arena) -> Self { 48 | Executor { 49 | arena, 50 | context: Context::new(), 51 | editor: Editor::new(), 52 | last_ctrl_c: Instant::now(), 53 | print_function: Box::new(|v| Encoder::::new().encode(v)), 54 | } 55 | } 56 | 57 | /// Execs into the REPL; does not return except in the case of errors. 58 | pub fn execute_loop(&mut self) -> io::Result { 59 | eprintln!( 60 | "Welcome to Alkyne v{version} (rustc v{rustc}, {arch} {os})", 61 | version = env!("CARGO_PKG_VERSION"), 62 | os = std::env::consts::OS, 63 | arch = std::env::consts::ARCH, 64 | rustc = rustc_version::version().unwrap_or_else(|_| (0, 0, 0).into()), 65 | ); 66 | eprintln!("Enter expressions to below and have them evaluated"); 67 | eprintln!("Run :quit, or double-press ^C, to escape."); 68 | eprintln!("Run :help for more information."); 69 | eprintln!(); 70 | loop { 71 | let buf = self.read_line()?; 72 | if buf.is_empty() { 73 | continue; 74 | } 75 | 76 | if buf.starts_with(':') { 77 | self.execute_command(&buf)?; 78 | continue; 79 | } 80 | 81 | let text = self.arena.alloc_string(buf); 82 | let unit = match syn::parse("".as_ref(), text, self.arena) { 83 | Ok(unit) => unit, 84 | Err(e) => { 85 | eprintln!("{}", e); 86 | continue; 87 | } 88 | }; 89 | 90 | match self.context.eval(unit, |_| None) { 91 | Ok(Value::Null) => continue, 92 | Ok(val) => { 93 | let text = match (self.print_function)(&val) { 94 | Ok(text) => text, 95 | Err(e) => { 96 | eprintln!("{}", e); 97 | continue; 98 | } 99 | }; 100 | println!("{}", text); 101 | } 102 | Err(e) => { 103 | eprintln!("{}", e); 104 | } 105 | } 106 | } 107 | } 108 | 109 | fn read_line(&mut self) -> io::Result { 110 | let mut buf = String::new(); 111 | let mut indent = 0; 112 | loop { 113 | let prompt = if buf.is_empty() { "alkyne> " } else { " | " }; 114 | let indent_whitespace = " ".repeat(indent); 115 | match self 116 | .editor 117 | .readline_with_initial(prompt, (&indent_whitespace, "")) 118 | { 119 | Ok(s) => { 120 | buf.push_str(s.as_str()); 121 | buf.push('\n'); 122 | } 123 | Err(ReadlineError::Io(e)) => return Err(e), 124 | Err(ReadlineError::Interrupted) => { 125 | if self.last_ctrl_c.elapsed() < DOUBLE_CTRL_C_THRESHOLD { 126 | std::process::exit(0) 127 | } 128 | self.last_ctrl_c = Instant::now(); 129 | 130 | return Ok(String::new()); 131 | } 132 | Err(e) => panic!("{}", e), 133 | } 134 | 135 | let (matches, i) = check_brackets_match(&buf); 136 | if matches || i < 0 { 137 | buf.pop(); 138 | self.editor.add_history_entry(&buf); 139 | return Ok(buf); 140 | } 141 | indent = i as usize; 142 | } 143 | } 144 | 145 | fn execute_command(&mut self, command: &str) -> io::Result<()> { 146 | let args = command.split_ascii_whitespace().collect::>(); 147 | match args[0] { 148 | ":help" | ":h" => println!( 149 | "\ 150 | available commands: 151 | :clear - clears the terminal 152 | :emit - set print mode: one of alkyne, debug, or json 153 | :help - shows this message 154 | :quit - exits the REPL\ 155 | " 156 | ), 157 | ":emit" | ":e" => match args.get(1).copied() { 158 | Some("alkyne") | Some("u") => { 159 | self.print_function = 160 | Box::new(|v| Encoder::::new().encode(v)) 161 | } 162 | Some("json") | Some("j") => { 163 | self.print_function = 164 | Box::new(|v| Encoder::::new().encode(v)) 165 | } 166 | Some("debug") | Some("d") => { 167 | self.print_function = 168 | Box::new(|v| Encoder::::new().encode(v)) 169 | } 170 | _ => eprintln!("expected 'alkyne', 'debug' or 'json'"), 171 | }, 172 | ":clear" | ":c" => { 173 | print!("{}{}", termion::clear::All, termion::cursor::Goto(1, 1)) 174 | } 175 | ":quit" | ":q" => std::process::exit(0), 176 | command => eprintln!("unknown command: {}", command), 177 | } 178 | Ok(()) 179 | } 180 | } 181 | 182 | /// Returns true if `s` has balanced brackets, strings, and comments; 183 | /// it also returns the expected indentation level. 184 | fn check_brackets_match(s: &str) -> (bool, i32) { 185 | let mut bracket_count: i32 = 0; 186 | let mut comment_count: i32 = 0; 187 | 188 | let mut chars = s.chars().peekable(); 189 | 'char_loop: while let Some(c) = chars.next() { 190 | match (c, chars.peek()) { 191 | ('/', Some('/')) if comment_count == 0 => loop { 192 | match chars.next() { 193 | Some('\n') | None => continue 'char_loop, 194 | _ => {} 195 | } 196 | }, 197 | 198 | ('/', Some('*')) => comment_count += 1, 199 | ('*', Some('/')) => comment_count -= 1, 200 | 201 | ('(', _) | ('[', _) | ('{', _) if comment_count == 0 => { 202 | bracket_count += 1 203 | } 204 | (')', _) | (']', _) | ('}', _) if comment_count == 0 => { 205 | bracket_count -= 1 206 | } 207 | 208 | ('"', _) | ('\'', _) if comment_count == 0 => loop { 209 | let next = chars.next(); 210 | let peek = chars.peek(); 211 | match (next, peek) { 212 | (Some('\\'), Some('\\')) => { 213 | let _ = chars.next(); 214 | } 215 | (Some('\\'), Some(q)) if *q == c => { 216 | let _ = chars.next(); 217 | } 218 | (Some(q), _) if q == c => continue 'char_loop, 219 | (None, _) => return (false, 0), 220 | _ => {} 221 | } 222 | }, 223 | _ => {} 224 | } 225 | } 226 | 227 | (bracket_count == 0 && comment_count == 0, bracket_count) 228 | } 229 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![deny(unused)] 16 | #![deny(warnings)] 17 | #![allow(clippy::new_without_default)] 18 | 19 | use crate::eval::encode::json::JsonEncoding; 20 | use crate::eval::encode::Encoder; 21 | use crate::exec::fs::Local; 22 | use crate::exec::parallel::Executor; 23 | 24 | pub mod eval; 25 | pub mod exec; 26 | pub mod syn; 27 | 28 | fn main() { 29 | if std::env::args().len() <= 1 { 30 | let arena = syn::Arena::new(); 31 | exec::repl::Executor::new(&arena).execute_loop().unwrap(); 32 | } 33 | 34 | let fs = Local::new(); 35 | let arenas = thread_local::ThreadLocal::new(); 36 | 37 | let file_names = std::env::args_os().skip(1).collect::>(); 38 | let file_names = file_names.iter().map(AsRef::as_ref).collect::>(); 39 | let exec = Executor::new(&fs, &arenas, std::io::stderr()); 40 | let vals = match exec.exec_files(file_names, 8) { 41 | Ok(v) => v, 42 | Err(n) => { 43 | eprintln!("error: got {} errors", n); 44 | std::process::exit(1) 45 | } 46 | }; 47 | 48 | for (file, value) in vals { 49 | match Encoder::::new().encode(&value) { 50 | Ok(j) => println!("# {}\n{}", file.display(), j), 51 | Err(e) => eprintln!("# {}\n{}", file.display(), e), 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/syn/alkyne.pest: -------------------------------------------------------------------------------- 1 | 2 | // Comments and whitespace. 3 | LineComment = _{ "//" ~ (!NEWLINE ~ ANY)* ~ NEWLINE } 4 | BlockComment = _{ "/*" ~ (BlockComment | (!"*/" ~ ANY))* ~ "*/" } 5 | COMMENT = _{ LineComment | BlockComment } 6 | WHITESPACE = _{ " " | "\t" | "\r" | "\n" } 7 | 8 | // Keywords. 9 | Keyword = _{ 10 | Break | Cont | Do | Else | False | Fn | For | Here | If | In | It | Let | Null | Oper | 11 | Ret | Zelf | Std | Super | Switch | Top | True | Use | Yield 12 | } 13 | Break = { "break" } 14 | Cont = { "continue" } 15 | Do = { "do" } 16 | Else = { "else" } 17 | False = { "false" } 18 | Fn = { "fn" } 19 | For = { "for" } 20 | Here = { "here" } 21 | If = { "if" } 22 | In = { "in" } 23 | It = { "it" } 24 | Let = { "let" } 25 | Null = { "null" } 26 | Oper = { "oper" } 27 | Ret = { "return" } 28 | Zelf = { "self" } 29 | Std = { "std" } 30 | Super = { "super" } 31 | Switch = { "switch" } 32 | Top = { "top" } 33 | True = { "true" } 34 | Use = { "use" } 35 | Yield = { "yield" } 36 | 37 | // Punctuation. 38 | At = _{ "@" } 39 | Eq = { "=" } 40 | Semi = _{ ";" } 41 | Col = _{ ":" } 42 | Comma = _{ "," } 43 | Dot = _{ "." } 44 | DotDot = { ".." } 45 | Arrow = { "->" } 46 | Question = { "?" } 47 | 48 | Plus = { "+" } 49 | Dash = { "-" } 50 | Star = { "*" } 51 | Slash = { "/" } 52 | Pct = { "%" } 53 | AndAnd = { "&&" } 54 | OrOr = { "||" } 55 | Or = { "|" } 56 | EqEq = { "==" } 57 | NeEq = { "!=" } 58 | Gt = { ">" } 59 | Lt = { "<" } 60 | GtEq = { ">=" } 61 | LtEq = { "<=" } 62 | Bang = { "!" } 63 | Elvis = { "??" } 64 | 65 | OverOp = _{ Plus | Dash | Star | Slash | Pct | EqEq | Lt | Bang } 66 | 67 | LParen = _{ "(" } 68 | RParen = _{ ")" } 69 | LBrack = _{ "[" } 70 | RBrack = _{ "]" } 71 | LBrace = _{ "{" } 72 | RBrace = _{ "}" } 73 | 74 | // Literals. 75 | Ident = { Word | Operator } 76 | Word = @{ 77 | (!Keyword ~ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")*) | 78 | (Keyword ~ (ASCII_ALPHANUMERIC | "_")+) 79 | } 80 | Operator = @{ Oper ~ OverOp } 81 | 82 | String = { DString | SString } 83 | DString = ${ "\"" ~ DStringInner ~ "\"" } 84 | DStringInner = @{ DStringChar* } 85 | DStringChar = { 86 | !("\"" | "\\") ~ ANY 87 | | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "x" | "u" | "U") 88 | | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) 89 | } 90 | SString = ${ "'" ~ SStringInner ~ "'" } 91 | SStringInner = @{ SStringChar* } 92 | SStringChar = { 93 | !("\'" | "\\") ~ ANY 94 | | "\\" ~ ("'" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "x" | "u" | "U") 95 | | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) 96 | } 97 | 98 | Number = @{ ASCII_DIGIT+ ~ (Dot ~ ASCII_DIGIT+)? } 99 | 100 | // Expressions. 101 | List = { LBrack ~ (Expr ~ Comma)* ~ Expr? ~ RBrack } 102 | // NOTE: Object takes priority over Block. 103 | Object = { LBrace ~ (Kv ~ Comma)* ~ Kv? ~ RBrace } 104 | Block = { LBrace ~ ((Stmt* ~ Expr) | Stmt+) ~ RBrace } 105 | FnLit = { Fn ~ LParen ~ (Pat ~ Comma)* ~ Pat? ~ RParen ~ Expr } 106 | IfExpr = { 107 | If ~ Expr ~ Block ~ 108 | (Else ~ If ~ Expr ~ Block)* ~ 109 | (Else ~ Block)? 110 | } 111 | SwitchExpr = { 112 | Switch ~ Expr ~ LBrace ~ 113 | ((Expr ~ Comma)* ~ Expr ~ Comma? ~ Arrow ~ Expr ~ Comma)* ~ 114 | (Else ~ Arrow ~ Expr ~ Comma)? ~ 115 | RBrace 116 | } 117 | ForExpr = { 118 | For ~ (Pat ~ Comma)* ~ Pat ~ Comma? ~ In ~ Question? ~ Expr ~ Block 119 | } 120 | ForYield = { 121 | For ~ (Pat ~ Comma)* ~ Pat ~ Comma? ~ In ~ Question? 122 | ~ Expr ~ (YieldKv | YieldVal) 123 | } 124 | YieldVal = { Yield ~ Expr } 125 | YieldKv = { Yield ~ Kv } 126 | BreakExpr = { Break ~ Expr? } 127 | RetExpr = { Ret ~ Expr } 128 | Parens = { LParen ~ Expr ~ RParen } 129 | 130 | FloatingExpr = _{ IfExpr | ForExpr | SwitchExpr | Block } 131 | ScalarExpr = _{ Null | True | False | Number | String } 132 | 133 | Key = { (Super ~ Question? ~ Dot)? ~ (Ident | String | (LBrack ~ Expr ~ RBrack)) } 134 | Kv = { Key ~ Question? ~ Col ~ Expr } 135 | 136 | BaseExpr = _{ 137 | ScalarExpr | Ident | Zelf | Super | Top | Here | It | Std | List | Object | 138 | Block | FnLit | IfExpr | SwitchExpr | ForExpr | ForYield | YieldKv | 139 | YieldVal | BreakExpr | Cont | RetExpr | Parens 140 | } 141 | 142 | SufExpr = { BaseExpr ~ (Member | Call | Index | Object | DoExpr)* } 143 | Member = { (Question? ~ Dot ~ (Ident | String)) } 144 | Call = { (LParen ~ (Expr ~ Comma)* ~ Expr? ~ RParen) } 145 | Index = { (Question? ~ LBrack ~ (Expr ~ Comma)* ~ Expr? ~ RBrack) } 146 | DoExpr = { Do ~ Question? ~ FloatingExpr } 147 | 148 | UnaryExpr = { (Dash | Bang)* ~ SufExpr } 149 | ProdExpr = { UnaryExpr ~ ((Star | Slash | Pct) ~ UnaryExpr)* } 150 | SumExpr = { ProdExpr ~ ((Plus | Dash) ~ ProdExpr)* } 151 | ElvisExpr = { SumExpr ~ (Elvis ~ SumExpr)* } 152 | OrdExpr = { ElvisExpr ~ ((GtEq | Gt | LtEq | Lt) ~ ElvisExpr)* } 153 | EqExpr = { OrdExpr ~ ((EqEq | NeEq) ~ OrdExpr)* } 154 | AndExpr = { EqExpr ~ (AndAnd ~ EqExpr)* } 155 | OrExpr = { AndExpr ~ (OrOr ~ AndExpr)* } 156 | 157 | Expr = { OrExpr } 158 | 159 | // Patterns. 160 | LiteralPat = { ScalarExpr } 161 | BindPat = { Ident } 162 | ExactListPat = { LBrack ~ (Pat ~ Comma)* ~ Pat? ~ RBrack } 163 | GlobListPat = { 164 | LBrack ~ 165 | (Pat ~ Comma)* ~ 166 | DotDot ~ Ident? ~ 167 | (Comma ~ Pat)* ~ Comma? ~ 168 | RBrack 169 | } 170 | KvPat = { (Ident | String) ~ Question? ~ (Col ~ Pat)? } 171 | ObjectPat = { 172 | LBrace ~ 173 | (KvPat ~ Comma)* ~ ((DotDot ~ Comma?) | KvPat)? ~ 174 | RBrace 175 | } 176 | ParenPat = { LParen ~ Pat ~ RParen } 177 | 178 | BasePat = _{ 179 | LiteralPat | BindPat | ExactListPat | GlobListPat | ObjectPat | ParenPat 180 | } 181 | OrPat = { BasePat ~ (Or ~ BasePat)* } 182 | AtPat = { (Ident ~ At)* ~ OrPat } 183 | Pat = { AtPat } 184 | 185 | // Statements. 186 | LetStmt = { Let ~ Pat ~ Eq ~ Expr ~ Semi } 187 | FnStmt = { 188 | Fn ~ Ident ~ LParen ~ 189 | (Pat ~ Comma)* ~ Pat? ~ 190 | RParen ~ ((Eq ~ Expr ~ Semi) | FloatingExpr) 191 | } 192 | ExprStmt = { Expr ~ Semi } 193 | Stmt = { LetStmt | FnStmt | ExprStmt | FloatingExpr | Semi } 194 | Stmts = _{ Stmt* ~ Expr? } 195 | 196 | ExprOrStmt = _{ Expr | Stmt } 197 | 198 | // Grammar root. 199 | Import = { Use ~ Ident ~ Eq ~ String ~ Semi } 200 | Unit = { 201 | SOI ~ 202 | Import* ~ 203 | Stmts ~ 204 | EOI 205 | } -------------------------------------------------------------------------------- /src/syn/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Syntax tree data structures for the Alkyne language. 16 | 17 | #![deny(missing_docs)] 18 | 19 | use std::fmt; 20 | use std::path::Path; 21 | 22 | mod parser; 23 | pub use parser::{parse, ParseError}; 24 | 25 | pub use toolshed::Arena; 26 | 27 | /// A source span. 28 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] 29 | pub struct Span<'i> { 30 | file_name: &'i Path, 31 | input: Option<&'i str>, 32 | start: usize, 33 | end: usize, 34 | } 35 | 36 | impl<'i> Span<'i> { 37 | /// Builds a new `Span` from raw parts. 38 | pub(crate) fn new_from_parts( 39 | file_name: &'i Path, 40 | input: &'i str, 41 | span: (usize, usize), 42 | ) -> Self { 43 | Self { 44 | file_name, 45 | input: Some(input), 46 | start: span.0, 47 | end: span.1, 48 | } 49 | } 50 | 51 | /// Builds a new "synthetic" span, which might point to a non-Alkyne file. 52 | pub fn synthetic(file_name: &'i Path) -> Self { 53 | Self { 54 | file_name, 55 | input: None, 56 | start: 0, 57 | end: 0, 58 | } 59 | } 60 | 61 | /// Returns the name of the file this `Span` refers to. 62 | pub fn file_name(&self) -> &'i Path { 63 | self.file_name 64 | } 65 | 66 | /// Returns whether this `Span` is synthetic, i.e., whether it does not 67 | /// actually point to a real file. 68 | pub fn is_synthetic(&self) -> bool { 69 | self.input.is_none() 70 | } 71 | 72 | /// Returns the offset at which this `Span` starts. 73 | pub fn start_byte(&self) -> usize { 74 | self.start 75 | } 76 | 77 | /// Returns the offset at which this `Span` ends. 78 | pub fn end_byte(&self) -> usize { 79 | self.end 80 | } 81 | 82 | /// Returns the line and column this `Span` starts at, both one-indexed. 83 | pub fn start_position(&self) -> Option<(usize, usize)> { 84 | self.input.map(|s| { 85 | pest::Position::new(s, self.start_byte()) 86 | .unwrap() 87 | .line_col() 88 | }) 89 | } 90 | 91 | /// Returns the line and column this `Span` ends at, both one-indexed. 92 | pub fn end_position(&self) -> Option<(usize, usize)> { 93 | self 94 | .input 95 | .map(|s| pest::Position::new(s, self.end_byte()).unwrap().line_col()) 96 | } 97 | 98 | /// Returns the input text that this `Span` refers to. 99 | pub fn input(&self) -> Option<&'i str> { 100 | self.input 101 | } 102 | 103 | /// Returns the text that this `Span` refers to. 104 | pub fn text(&self) -> Option<&'i str> { 105 | self.input.as_ref().map(|s| &s[self.start..self.end]) 106 | } 107 | 108 | /// Joins two spans into a single, contiguous span. 109 | /// 110 | /// # Panics 111 | /// 112 | /// Panics if `first` and `second` are not in sequence. 113 | pub fn join(first: Span<'i>, second: Span<'i>) -> Span<'i> { 114 | assert!(first.end <= second.start); 115 | Span { 116 | end: second.end, 117 | ..first 118 | } 119 | } 120 | 121 | /// Creates a copy of this `Span`, such that it contains all the lines that 122 | /// this this `Span` contained a part of. 123 | pub fn snap_to_lines(&self) -> Self { 124 | if self.is_synthetic() { 125 | return *self; 126 | } 127 | 128 | let input = self.input.unwrap(); 129 | let start = self.start_position().unwrap().0 - 1; 130 | let end = self.end_position().unwrap().0 - 1; 131 | 132 | let mut start_byte = 0; 133 | let mut end_byte = 0; 134 | for (i, line) in input.split('\n').enumerate() { 135 | if i < start { 136 | start_byte += line.len() + 1; 137 | } 138 | end_byte += line.len() + 1; 139 | if i == end { 140 | break; 141 | } 142 | } 143 | end_byte = end_byte.min(input.len()); 144 | 145 | Self { 146 | file_name: self.file_name, 147 | input: Some(input), 148 | start: start_byte, 149 | end: end_byte, 150 | } 151 | } 152 | } 153 | 154 | impl fmt::Debug for Span<'_> { 155 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 156 | if self.is_synthetic() { 157 | return write!(f, "<{}>[?..?]", self.file_name().display()); 158 | } 159 | write!( 160 | f, 161 | "<{}>[{}..{}]", 162 | self.file_name().display(), 163 | self.start_byte(), 164 | self.end_byte() 165 | ) 166 | } 167 | } 168 | 169 | impl fmt::Display for Span<'_> { 170 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 171 | if self.is_synthetic() { 172 | return write!(f, "{}:?:?", self.file_name().display()); 173 | } 174 | let (line, col) = self.start_position().unwrap(); 175 | write!(f, "{}:{}:{}", self.file_name().display(), line + 1, col + 1) 176 | } 177 | } 178 | 179 | /// Represents a type with a file span. 180 | pub trait Spanned<'i> { 181 | /// Returns the associated file span. 182 | fn span(&self) -> Span<'i>; 183 | } 184 | 185 | impl<'i> Spanned<'i> for Span<'i> { 186 | fn span(&self) -> Span<'i> { 187 | *self 188 | } 189 | } 190 | 191 | impl<'i, S> Spanned<'i> for &S 192 | where 193 | S: Spanned<'i>, 194 | { 195 | fn span(&self) -> Span<'i> { 196 | S::span(*self) 197 | } 198 | } 199 | 200 | /// A `Unit` represents a complete unit of evaluation, i.e., a single file. 201 | #[derive(Copy, Clone, Debug)] 202 | pub struct Unit<'i> { 203 | /// The imports this `Unit` brings in. 204 | pub imports: &'i [Use<'i>], 205 | /// The `let` statements at the top level of this file. 206 | pub stmts: &'i [Let<'i>], 207 | /// The value at the end of the file. 208 | pub value: Expr<'i>, 209 | } 210 | 211 | /// An `Ident` represents a Alkyne identifier, appearing as both an expression 212 | /// and in binding definitions. 213 | #[derive(Copy, Clone, Debug)] 214 | pub struct Ident<'i> { 215 | /// The name of this identifier. 216 | pub name: &'i str, 217 | /// This AST node's span. 218 | pub span: Span<'i>, 219 | } 220 | 221 | impl<'i> Spanned<'i> for Ident<'i> { 222 | fn span(&self) -> Span<'i> { 223 | self.span 224 | } 225 | } 226 | 227 | /// A `Use` represents an import statement in Alkyne, with syntax 228 | /// ```text 229 | /// use foo = 'my/config/foo.alkyne'; 230 | /// ``` 231 | /// 232 | /// Imports can only appear at the beginning of a file. 233 | #[derive(Copy, Clone, Debug)] 234 | pub struct Use<'i> { 235 | /// The variable this import introduces into file scope. 236 | pub var: Ident<'i>, 237 | /// The text of the string literal after the '=' sign, quoted and 238 | /// unescaped. 239 | pub import: Str<'i>, 240 | /// This AST node's span. 241 | pub span: Span<'i>, 242 | } 243 | 244 | impl<'i> Spanned<'i> for Use<'i> { 245 | fn span(&self) -> Span<'i> { 246 | self.span 247 | } 248 | } 249 | 250 | /// A `Let` represents a variable binding, the sole permitted statement 251 | /// in Alkyne. It computes the right hand side, and then attempts to match it 252 | /// against the pattern. 253 | #[derive(Copy, Clone, Debug)] 254 | pub struct Let<'i> { 255 | /// The pattern to match the rhs against. 256 | pub pat: Option<&'i Pat<'i>>, 257 | /// Whether this `Let` is really a floating statement. This information is 258 | /// mostly just used by the parser. 259 | pub is_floating: bool, 260 | /// The value to be evaluated, and then matched against left hand side. 261 | pub value: Expr<'i>, 262 | /// This AST node's span. 263 | pub span: Span<'i>, 264 | } 265 | 266 | impl<'i> Spanned<'i> for Let<'i> { 267 | fn span(&self) -> Span<'i> { 268 | self.span 269 | } 270 | } 271 | 272 | /// A `Pat` represents a Alkyne pattern, used in e.g. `let` bindings. 273 | #[derive(Copy, Clone, Debug)] 274 | #[allow(missing_docs)] 275 | pub enum Pat<'i> { 276 | Expr(Expr<'i>), 277 | Bind(Ident<'i>), 278 | ExactList(ExactListPat<'i>), 279 | GlobList(GlobListPat<'i>), 280 | Object(ObjectPat<'i>), 281 | Or(OrPat<'i>), 282 | At(AtPat<'i>), 283 | } 284 | 285 | impl<'i> Spanned<'i> for Pat<'i> { 286 | fn span(&self) -> Span<'i> { 287 | match self { 288 | Pat::Expr(s) => s.span(), 289 | Pat::Bind(s) => s.span(), 290 | Pat::ExactList(s) => s.span(), 291 | Pat::GlobList(s) => s.span(), 292 | Pat::Object(s) => s.span(), 293 | Pat::Or(s) => s.span(), 294 | Pat::At(s) => s.span(), 295 | } 296 | } 297 | } 298 | 299 | /// An `ExactListPat` represents a Alkyne pattern that matches a list of an exact 300 | /// size. 301 | #[derive(Copy, Clone, Debug)] 302 | pub struct ExactListPat<'i> { 303 | /// The exact patterns to match, in order. 304 | pub pats: &'i [Pat<'i>], 305 | /// This AST node's span. 306 | pub span: Span<'i>, 307 | } 308 | 309 | impl<'i> Spanned<'i> for ExactListPat<'i> { 310 | fn span(&self) -> Span<'i> { 311 | self.span 312 | } 313 | } 314 | 315 | /// A `GlobListPat` represents a Alkyne pattern that matches a list of unknown 316 | /// size, extracting elements from the front and back. 317 | #[derive(Copy, Clone, Debug)] 318 | pub struct GlobListPat<'i> { 319 | /// The patterns to match at the front end of the list. 320 | pub front: &'i [Pat<'i>], 321 | /// The name to bind the "middle" portion of the list to, if any. 322 | pub middle: Option>, 323 | /// The patterns to match at the back end of the list. 324 | pub back: &'i [Pat<'i>], 325 | /// This AST node's span. 326 | pub span: Span<'i>, 327 | } 328 | 329 | impl<'i> Spanned<'i> for GlobListPat<'i> { 330 | fn span(&self) -> Span<'i> { 331 | self.span 332 | } 333 | } 334 | 335 | /// A `ObjectPat` represents a Alkyne pattern that matches an object with various 336 | /// keys, possibly exactly. 337 | #[derive(Copy, Clone, Debug)] 338 | pub struct ObjectPat<'i> { 339 | /// The fields to match for. 340 | pub fields: &'i [Kv<'i, Option>>], 341 | /// Whether this pattern expects the pattern to match *exactly*, i.e., there 342 | /// are no superfluous keys. 343 | pub is_exact: bool, 344 | /// This AST node's span. 345 | pub span: Span<'i>, 346 | } 347 | 348 | impl<'i> Spanned<'i> for ObjectPat<'i> { 349 | fn span(&self) -> Span<'i> { 350 | self.span 351 | } 352 | } 353 | 354 | /// A `AtPat` represents a Alkyne pattern that delegates to another pattern, but 355 | /// binds the matchee to a given identifier. 356 | #[derive(Copy, Clone, Debug)] 357 | pub struct AtPat<'i> { 358 | /// The pattern to defer to. 359 | pub pat: &'i Pat<'i>, 360 | /// The identifier to bind the matchee to. 361 | pub ident: Ident<'i>, 362 | /// This AST node's span. 363 | pub span: Span<'i>, 364 | } 365 | 366 | impl<'i> Spanned<'i> for AtPat<'i> { 367 | fn span(&self) -> Span<'i> { 368 | self.span 369 | } 370 | } 371 | 372 | /// A `OrPat` represents a Alkyne pattern that tries one of several patterns until 373 | /// one succeeds. 374 | #[derive(Copy, Clone, Debug)] 375 | pub struct OrPat<'i> { 376 | /// The patterns to try. 377 | pub pats: &'i [Pat<'i>], 378 | /// This AST node's span. 379 | pub span: Span<'i>, 380 | } 381 | 382 | impl<'i> Spanned<'i> for OrPat<'i> { 383 | fn span(&self) -> Span<'i> { 384 | self.span 385 | } 386 | } 387 | 388 | /// An `Expr` represents a Alkyne expression, as a union of all of the potential 389 | /// Alkyne expression types. 390 | #[derive(Copy, Clone, Debug)] 391 | #[allow(missing_docs)] 392 | pub enum Expr<'i> { 393 | Null(Null<'i>), 394 | Bool(Bool<'i>), 395 | Num(Num<'i>), 396 | Str(Str<'i>), 397 | Ref(Ident<'i>), 398 | Builtin(Builtin<'i>), 399 | List(List<'i>), 400 | Object(Object<'i>), 401 | Block(Block<'i>), 402 | Fn(Fn<'i>), 403 | If(If<'i>), 404 | Switch(Switch<'i>), 405 | For(For<'i>), 406 | Yield(Yield<'i>), 407 | YieldKv(YieldKv<'i>), 408 | Break(Break<'i>), 409 | Cont(Cont<'i>), 410 | Ret(Ret<'i>), 411 | Member(Member<'i>), 412 | Call(Call<'i>), 413 | Index(Index<'i>), 414 | Do(Do<'i>), 415 | UnOp(UnOp<'i>), 416 | BinOp(BinOp<'i>), 417 | } 418 | 419 | impl<'i> Spanned<'i> for Expr<'i> { 420 | fn span(&self) -> Span<'i> { 421 | match self { 422 | Expr::Null(s) => s.span(), 423 | Expr::Bool(s) => s.span(), 424 | Expr::Num(s) => s.span(), 425 | Expr::Str(s) => s.span(), 426 | Expr::Ref(s) => s.span(), 427 | Expr::Builtin(s) => s.span(), 428 | Expr::List(s) => s.span(), 429 | Expr::Object(s) => s.span(), 430 | Expr::Block(s) => s.span(), 431 | Expr::Fn(s) => s.span(), 432 | Expr::If(s) => s.span(), 433 | Expr::Switch(s) => s.span(), 434 | Expr::For(s) => s.span(), 435 | Expr::Yield(s) => s.span(), 436 | Expr::YieldKv(s) => s.span(), 437 | Expr::Break(s) => s.span(), 438 | Expr::Cont(s) => s.span(), 439 | Expr::Ret(s) => s.span(), 440 | Expr::Member(s) => s.span(), 441 | Expr::Call(s) => s.span(), 442 | Expr::Index(s) => s.span(), 443 | Expr::Do(s) => s.span(), 444 | Expr::UnOp(s) => s.span(), 445 | Expr::BinOp(s) => s.span(), 446 | } 447 | } 448 | } 449 | 450 | /// A `Null` represents a literal `null`. 451 | #[derive(Copy, Clone, Debug)] 452 | pub struct Null<'i> { 453 | /// This AST node's span. 454 | pub span: Span<'i>, 455 | } 456 | 457 | impl<'i> Spanned<'i> for Null<'i> { 458 | fn span(&self) -> Span<'i> { 459 | self.span 460 | } 461 | } 462 | 463 | /// A `Bool` represents a literal number: one of `true` or `false`. 464 | #[derive(Copy, Clone, Debug)] 465 | pub struct Bool<'i> { 466 | /// The parsed value of this literal. 467 | pub value: bool, 468 | /// This AST node's span. 469 | pub span: Span<'i>, 470 | } 471 | 472 | impl<'i> Spanned<'i> for Bool<'i> { 473 | fn span(&self) -> Span<'i> { 474 | self.span 475 | } 476 | } 477 | 478 | /// A `Num` represents a literal number. 479 | /// 480 | /// Currently, all numbers are floating point, but this is subject to 481 | /// change. 482 | #[derive(Copy, Clone, Debug)] 483 | pub struct Num<'i> { 484 | /// The parsed value of this literal. 485 | pub value: f64, 486 | /// This AST node's span. 487 | pub span: Span<'i>, 488 | } 489 | 490 | impl<'i> Spanned<'i> for Num<'i> { 491 | fn span(&self) -> Span<'i> { 492 | self.span 493 | } 494 | } 495 | 496 | /// A `Str` represents a literal string, which can be double or single 497 | /// quoted, or neither. Literals with `is_quoted` set should be treated as 498 | /// being surrounded by quotes and needing unescaping 499 | #[derive(Copy, Clone, Debug)] 500 | pub struct Str<'i> { 501 | /// The possibly quoted, escaped value of this literal. 502 | pub value: &'i str, 503 | /// Whether this is a "quoted" string literal, as opposed to a bareword. 504 | pub is_quoted: bool, 505 | /// This AST node's span. 506 | pub span: Span<'i>, 507 | } 508 | 509 | impl<'i> Spanned<'i> for Str<'i> { 510 | fn span(&self) -> Span<'i> { 511 | self.span 512 | } 513 | } 514 | 515 | /// A `BuiltinTy` is a particular type of `Builtin`. 516 | /// 517 | /// Because both `Self` and `self` are reserved words in Rust, we spell that 518 | /// variant as `Zelf`. 519 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] 520 | pub enum BuiltinTy { 521 | /// `self`, the value of the nearest enclosing object literal, or the receiver 522 | /// of a method call: `foo.bar(..)`. 523 | Zelf, 524 | /// `super`, super object of `self` (or null, if it doesn't exist). 525 | Super, 526 | /// `top`, the file-level scope object. 527 | Top, 528 | /// `here`, the current scope object against which all variable lookups 529 | /// are made. 530 | Here, 531 | /// `std`, the special standard library object. 532 | Std, 533 | /// `it`, used in `do` expressions. 534 | It, 535 | } 536 | 537 | /// A `Builtin` represents a literal reserved name has special meaning as 538 | /// an expression and cannot be used for variable declarations. 539 | #[derive(Copy, Clone, Debug)] 540 | pub struct Builtin<'i> { 541 | /// The type of builtin this literal represents. 542 | pub ty: BuiltinTy, 543 | /// This AST node's span. 544 | pub span: Span<'i>, 545 | } 546 | 547 | impl<'i> Spanned<'i> for Builtin<'i> { 548 | fn span(&self) -> Span<'i> { 549 | self.span 550 | } 551 | } 552 | 553 | /// A `List` represents a list literal, with the syntax 554 | /// ```text 555 | /// [expr1, expr2, expr3] 556 | /// ``` 557 | #[derive(Copy, Clone, Debug)] 558 | pub struct List<'i> { 559 | /// The values that this list, when evaluated, should contain. 560 | pub values: &'i [Expr<'i>], 561 | /// This AST node's span. 562 | pub span: Span<'i>, 563 | } 564 | 565 | impl<'i> Spanned<'i> for List<'i> { 566 | fn span(&self) -> Span<'i> { 567 | self.span 568 | } 569 | } 570 | 571 | /// A `KvType` is a type of key-value binding. 572 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 573 | #[allow(missing_docs)] 574 | pub enum KvType { 575 | Normal, 576 | Super, 577 | MaybeSuper, 578 | } 579 | 580 | /// A `Kv` represents a key-value pair, for use as fields in objects; it is 581 | /// not an expression. It has the syntaxes 582 | /// ```text 583 | /// ident: expr 584 | /// "string literal": expr 585 | /// [key_expr]: expr 586 | /// ``` 587 | /// 588 | /// The first variant should be represented as a `Str` with `is_quoted` 589 | /// false, rather than an identifier. 590 | #[derive(Copy, Clone, Debug)] 591 | pub struct Kv<'i, V> { 592 | /// The key associated with this pair. 593 | pub key: Expr<'i>, 594 | /// The value associated with this pair. 595 | pub value: V, 596 | /// The type of key-value: normal, `super`, or `super?`. 597 | pub ty: KvType, 598 | /// Whether this pair ignores null values. 599 | pub nullable: bool, 600 | /// This AST node's span. 601 | pub span: Span<'i>, 602 | } 603 | 604 | impl<'i, V> Spanned<'i> for Kv<'i, V> { 605 | fn span(&self) -> Span<'i> { 606 | self.span 607 | } 608 | } 609 | 610 | /// A `Object` represents an object literal, possibly with a "super" object 611 | /// that it is extending. It has the syntaxes 612 | /// ```text 613 | /// { key: value, key: value } 614 | /// expr { key: value, key: value } 615 | /// ``` 616 | #[derive(Copy, Clone, Debug)] 617 | pub struct Object<'i> { 618 | /// The super expression for this object, if it has one. 619 | pub zuper: Option<&'i Expr<'i>>, 620 | /// The fields this object is being initialized with. 621 | pub fields: &'i [Kv<'i, Expr<'i>>], 622 | /// This AST node's span. 623 | pub span: Span<'i>, 624 | } 625 | 626 | impl<'i> Spanned<'i> for Object<'i> { 627 | fn span(&self) -> Span<'i> { 628 | self.span 629 | } 630 | } 631 | 632 | /// A `Block` represents a block expression, i.e., a sequence of `Let` bindings 633 | /// ending in an expression. It has the syntax 634 | /// ```text 635 | /// { 636 | /// let var = expr; 637 | /// let var = expr; 638 | /// expr 639 | /// } 640 | /// ``` 641 | #[derive(Copy, Clone, Debug)] 642 | pub struct Block<'i> { 643 | /// The bindings to execute in preparation for the final expression. 644 | pub lets: &'i [Let<'i>], 645 | /// The final expression to evaluate and return. 646 | pub value: &'i Expr<'i>, 647 | /// This AST node's span. 648 | pub span: Span<'i>, 649 | } 650 | 651 | impl<'i> Spanned<'i> for Block<'i> { 652 | fn span(&self) -> Span<'i> { 653 | self.span 654 | } 655 | } 656 | 657 | /// A `Fn` represents a function literal, which includes argument names and a 658 | /// body. It has syntaxes 659 | /// ```text 660 | /// fn foo(a, b, c) { .. } 661 | /// fn(a, b, c) expr 662 | /// ``` 663 | /// 664 | /// The former is actually a `Let` which has a `Fn` as its value. 665 | #[derive(Copy, Clone, Debug)] 666 | pub struct Fn<'i> { 667 | /// The name of this `Fn`, if it has one. 668 | pub name: Option>, 669 | /// The declared argument names. 670 | pub args: &'i [Pat<'i>], 671 | /// The body, to be evaluated after the argument names are bound. 672 | pub body: &'i Expr<'i>, 673 | /// This AST node's span. 674 | pub span: Span<'i>, 675 | } 676 | 677 | impl<'i> Spanned<'i> for Fn<'i> { 678 | fn span(&self) -> Span<'i> { 679 | self.span 680 | } 681 | } 682 | 683 | /// An `If` represents a `if` expression, consisting of a sequence of 684 | /// clause and expression pairs, and possibly an `else` block. In full 685 | /// generality, it has syntax 686 | /// ```text 687 | /// if expr { 688 | /// ... 689 | /// } else if expr { 690 | /// ... 691 | /// } else { 692 | /// ... 693 | /// } 694 | /// ``` 695 | #[derive(Copy, Clone, Debug)] 696 | pub struct If<'i> { 697 | /// The conditioned `if` clauses in this expression. 698 | pub clauses: &'i [(&'i Expr<'i>, Block<'i>)], 699 | /// The ending `else` block, if there is one. 700 | pub else_block: Option>, 701 | /// This AST node's span. 702 | pub span: Span<'i>, 703 | } 704 | 705 | impl<'i> Spanned<'i> for If<'i> { 706 | fn span(&self) -> Span<'i> { 707 | self.span 708 | } 709 | } 710 | 711 | /// A `Switch` represents a `switch` expression, which evaluates the first 712 | /// branch whose value equals that of the argument. In full generality, it 713 | /// has syntax 714 | /// ```text 715 | /// switch arg { 716 | /// expr -> expr, 717 | /// expr, expr -> expr, 718 | /// else -> expr, 719 | /// } 720 | /// ``` 721 | #[derive(Copy, Clone, Debug)] 722 | pub struct Switch<'i> { 723 | /// The value being switched on. 724 | pub arg: &'i Expr<'i>, 725 | /// The non-else clauses. 726 | pub clauses: &'i [SwitchClause<'i>], 727 | /// The optional `else` clause. 728 | pub else_clause: Option<&'i Expr<'i>>, 729 | /// This AST node's span. 730 | pub span: Span<'i>, 731 | } 732 | 733 | /// A `SwitchClause` is a non-`else` clause within a `Switch`, consisting 734 | /// of a sequence of potential cases and the value it should evaluate to. 735 | #[derive(Copy, Clone, Debug)] 736 | pub struct SwitchClause<'i> { 737 | /// The values to potentially match on the lhs. 738 | pub cases: &'i [Expr<'i>], 739 | /// The value to evaluate and return, on the rhs. 740 | pub value: Expr<'i>, 741 | } 742 | 743 | impl<'i> Spanned<'i> for Switch<'i> { 744 | fn span(&self) -> Span<'i> { 745 | self.span 746 | } 747 | } 748 | 749 | /// A `For` represents a `for` expression, i.e., a comprehension. It has 750 | /// the syntax 751 | /// ```text 752 | /// for x, y in iter { 753 | /// ... 754 | /// } 755 | /// ``` 756 | #[derive(Copy, Clone, Debug)] 757 | pub struct For<'i> { 758 | /// The patterns to bind during each iteration of the loop. 759 | pub pats: &'i [Pat<'i>], 760 | /// Whether failure of a pattern match should be interpreted as skipping that 761 | /// iteration step. 762 | pub is_match_required: bool, 763 | /// The value to iterate over. 764 | pub iter: &'i Expr<'i>, 765 | /// The body to execute, and potentially yield from. 766 | pub body: &'i Expr<'i>, 767 | /// This AST node's span. 768 | pub span: Span<'i>, 769 | } 770 | 771 | impl<'i> Spanned<'i> for For<'i> { 772 | fn span(&self) -> Span<'i> { 773 | self.span 774 | } 775 | } 776 | 777 | /// A `Yield` represents a normal `yield` expression, which builds up a 778 | /// list from inside a `for` loop. It has syntax 779 | /// ```text 780 | /// yield expr 781 | /// ``` 782 | #[derive(Copy, Clone, Debug)] 783 | pub struct Yield<'i> { 784 | /// The value to yield. 785 | pub value: &'i Expr<'i>, 786 | /// This AST node's span. 787 | pub span: Span<'i>, 788 | } 789 | 790 | impl<'i> Spanned<'i> for Yield<'i> { 791 | fn span(&self) -> Span<'i> { 792 | self.span 793 | } 794 | } 795 | 796 | /// A `YieldKv` represents a key-value `yield` expression, which builds up an 797 | /// object from inside a `for` loop. It has syntax 798 | /// ```text 799 | /// yield [key]: value 800 | /// ``` 801 | #[derive(Copy, Clone, Debug)] 802 | pub struct YieldKv<'i> { 803 | /// The key-value pair to yield. 804 | pub kv: &'i Kv<'i, Expr<'i>>, 805 | /// This AST node's span. 806 | pub span: Span<'i>, 807 | } 808 | 809 | impl<'i> Spanned<'i> for YieldKv<'i> { 810 | fn span(&self) -> Span<'i> { 811 | self.span 812 | } 813 | } 814 | 815 | /// A `Break` represents a `break` expression, which ends iteration in a loop. 816 | /// A `break` may have a value to indicate that that value should replace 817 | /// whatever the loop was in the process of yielding. It has syntaxes 818 | /// ```text 819 | /// break 820 | /// break expr 821 | /// ``` 822 | #[derive(Copy, Clone, Debug)] 823 | pub struct Break<'i> { 824 | /// The value to break with, if it exists. 825 | pub value: Option<&'i Expr<'i>>, 826 | /// This AST node's span. 827 | pub span: Span<'i>, 828 | } 829 | 830 | impl<'i> Spanned<'i> for Break<'i> { 831 | fn span(&self) -> Span<'i> { 832 | self.span 833 | } 834 | } 835 | 836 | /// A `Cont` represents a `continue` expression, which moves on to th next 837 | /// iteration in a loop. 838 | /// ```text 839 | /// continue 840 | /// ``` 841 | #[derive(Copy, Clone, Debug)] 842 | pub struct Cont<'i> { 843 | /// This AST node's span. 844 | pub span: Span<'i>, 845 | } 846 | 847 | impl<'i> Spanned<'i> for Cont<'i> { 848 | fn span(&self) -> Span<'i> { 849 | self.span 850 | } 851 | } 852 | 853 | /// A `Ret` represents a `return` expression, which returns a value early from 854 | /// a function. 855 | /// ```text 856 | /// return expr 857 | /// ``` 858 | #[derive(Copy, Clone, Debug)] 859 | pub struct Ret<'i> { 860 | /// The value to return. 861 | pub value: &'i Expr<'i>, 862 | /// This AST node's span. 863 | pub span: Span<'i>, 864 | } 865 | 866 | impl<'i> Spanned<'i> for Ret<'i> { 867 | fn span(&self) -> Span<'i> { 868 | self.span 869 | } 870 | } 871 | 872 | /// A `Member` represents a member access, which has syntax 873 | /// ```text 874 | /// expr.ident 875 | /// ``` 876 | #[derive(Copy, Clone, Debug)] 877 | pub struct Member<'i> { 878 | /// The value to perform an access on. 879 | pub value: &'i Expr<'i>, 880 | /// The name of the member to access. 881 | pub member: &'i Expr<'i>, 882 | /// Whether this access is null-safe. 883 | pub nullable: bool, 884 | /// This AST node's span. 885 | pub span: Span<'i>, 886 | } 887 | 888 | impl<'i> Spanned<'i> for Member<'i> { 889 | fn span(&self) -> Span<'i> { 890 | self.span 891 | } 892 | } 893 | 894 | /// A `Call` represents a function call, which evaluates a function, a list 895 | /// of arguments, and then calls the former with the latter. It has syntax 896 | /// ```text 897 | /// expr(expr, expr, expr) 898 | /// ``` 899 | #[derive(Copy, Clone, Debug)] 900 | pub struct Call<'i> { 901 | /// The function to call. 902 | pub fnc: &'i Expr<'i>, 903 | /// The arguments to evaluate and call the function with. 904 | pub args: &'i [Expr<'i>], 905 | /// This AST node's span. 906 | pub span: Span<'i>, 907 | } 908 | 909 | impl<'i> Spanned<'i> for Call<'i> { 910 | fn span(&self) -> Span<'i> { 911 | self.span 912 | } 913 | } 914 | 915 | /// An `Index` represents an indexing operation, which is syntactically 916 | /// identical to a function call. It has syntax 917 | /// ```text 918 | /// expr[expr, expr, expr] 919 | /// ``` 920 | #[derive(Copy, Clone, Debug)] 921 | pub struct Index<'i> { 922 | /// The value to be indexed. 923 | pub value: &'i Expr<'i>, 924 | /// The values to index it with. 925 | pub args: &'i [Expr<'i>], 926 | /// Whether this indexing is null-safe. 927 | pub nullable: bool, 928 | /// This AST node's span. 929 | pub span: Span<'i>, 930 | } 931 | 932 | impl<'i> Spanned<'i> for Index<'i> { 933 | fn span(&self) -> Span<'i> { 934 | self.span 935 | } 936 | } 937 | 938 | /// A `Do` represents a do expression. It has syntax 939 | /// ```text 940 | /// expr do { foo(it) } 941 | /// expr do? switch it { .. } 942 | /// ``` 943 | #[derive(Copy, Clone, Debug)] 944 | pub struct Do<'i> { 945 | /// The value of `it` in this block. 946 | pub it: &'i Expr<'i>, 947 | /// The calculation to perform on `it`. 948 | pub body: &'i Expr<'i>, 949 | /// Whether this `do`-expression should short-circuit on `null`. 950 | pub nullable: bool, 951 | /// This AST node's span. 952 | pub span: Span<'i>, 953 | } 954 | 955 | impl<'i> Spanned<'i> for Do<'i> { 956 | fn span(&self) -> Span<'i> { 957 | self.span 958 | } 959 | } 960 | 961 | /// An `UnOpTy` is a type of unary operation. 962 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 963 | #[allow(missing_docs)] 964 | pub enum UnOpTy { 965 | Neg, 966 | Not, 967 | } 968 | 969 | impl UnOpTy { 970 | /// Returns the symbol corresponding to this operator. 971 | pub fn name(&self) -> &'static str { 972 | match self { 973 | UnOpTy::Neg => "-", 974 | UnOpTy::Not => "!", 975 | } 976 | } 977 | 978 | /// Returns whether this operator is overloadable by user code. 979 | pub fn overloadable(&self) -> bool { 980 | true 981 | } 982 | } 983 | 984 | impl fmt::Display for UnOpTy { 985 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 986 | write!(f, "{}", self.name()) 987 | } 988 | } 989 | 990 | /// An `UnOp` represents an unary operation, with the usual syntax. 991 | #[derive(Copy, Clone, Debug)] 992 | pub struct UnOp<'i> { 993 | /// The argument to the operation. 994 | pub arg: &'i Expr<'i>, 995 | /// The operation type. 996 | pub ty: UnOpTy, 997 | /// This AST node's span. 998 | pub span: Span<'i>, 999 | } 1000 | 1001 | impl<'i> Spanned<'i> for UnOp<'i> { 1002 | fn span(&self) -> Span<'i> { 1003 | self.span 1004 | } 1005 | } 1006 | 1007 | /// An `UnOpTy` is a type of unary operation. 1008 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 1009 | #[allow(missing_docs)] 1010 | pub enum BinOpTy { 1011 | Add, 1012 | Sub, 1013 | Mul, 1014 | Div, 1015 | Rem, 1016 | Eq, 1017 | Ne, 1018 | Ge, 1019 | Le, 1020 | Gt, 1021 | Lt, 1022 | AndAnd, 1023 | OrOr, 1024 | Elvis, 1025 | } 1026 | 1027 | impl BinOpTy { 1028 | /// Returns the symbol corresponding to this operator. 1029 | pub fn name(&self) -> &'static str { 1030 | match self { 1031 | BinOpTy::Add => "+", 1032 | BinOpTy::Sub => "-", 1033 | BinOpTy::Mul => "*", 1034 | BinOpTy::Div => "/", 1035 | BinOpTy::Rem => "%", 1036 | BinOpTy::Eq => "==", 1037 | BinOpTy::Ne => "!=", 1038 | BinOpTy::Ge => ">=", 1039 | BinOpTy::Le => "<=", 1040 | BinOpTy::Gt => ">", 1041 | BinOpTy::Lt => "<", 1042 | BinOpTy::AndAnd => "&&", 1043 | BinOpTy::OrOr => "||", 1044 | BinOpTy::Elvis => "??", 1045 | } 1046 | } 1047 | 1048 | /// Returns whether this operator is overloadable by user code. 1049 | pub fn overloadable(&self) -> bool { 1050 | matches!( 1051 | self, 1052 | BinOpTy::Add 1053 | | BinOpTy::Sub 1054 | | BinOpTy::Mul 1055 | | BinOpTy::Div 1056 | | BinOpTy::Rem 1057 | | BinOpTy::Eq 1058 | ) 1059 | } 1060 | } 1061 | 1062 | impl fmt::Display for BinOpTy { 1063 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 1064 | write!(f, "{}", self.name()) 1065 | } 1066 | } 1067 | 1068 | /// An `BinOp` represents an unary operation, with the usual syntax. 1069 | #[derive(Copy, Clone, Debug)] 1070 | pub struct BinOp<'i> { 1071 | /// The left-hand-side of the operation. 1072 | pub lhs: &'i Expr<'i>, 1073 | /// The right-hand-side of the operation. 1074 | pub rhs: &'i Expr<'i>, 1075 | /// The operation type. 1076 | pub ty: BinOpTy, 1077 | /// This AST node's span. 1078 | pub span: Span<'i>, 1079 | } 1080 | 1081 | impl<'i> Spanned<'i> for BinOp<'i> { 1082 | fn span(&self) -> Span<'i> { 1083 | self.span 1084 | } 1085 | } 1086 | -------------------------------------------------------------------------------- /src/syn/parser.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! The nuts and bolts of the Alkyne parser. 16 | 17 | #![allow(clippy::upper_case_acronyms)] 18 | 19 | use std::fmt; 20 | use std::mem; 21 | use std::path::Path; 22 | 23 | use pest::error::ErrorVariant; 24 | use pest::error::InputLocation; 25 | use pest::iterators::Pair; 26 | use pest::Position; 27 | 28 | use pest_derive::Parser; 29 | 30 | use crate::syn; 31 | use crate::syn::Span; 32 | use crate::syn::Spanned as _; 33 | 34 | /// A `ParseError` represents a parse failure at some `Span`. 35 | #[derive(Clone, Debug)] 36 | pub struct ParseError<'i> { 37 | /// The `Span` at which the error occured. 38 | pub span: Span<'i>, 39 | /// An error message. 40 | pub message: String, 41 | } 42 | 43 | impl fmt::Display for ParseError<'_> { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | writeln!(f, "error: {}", self.message) 46 | } 47 | } 48 | 49 | /// Parse `src` into a `Unit`, returning an error on failure. 50 | pub fn parse<'i>( 51 | file_name: &'i Path, 52 | input: &'i str, 53 | arena: &'i syn::Arena, 54 | ) -> Result<&'i syn::Unit<'i>, ParseError<'i>> { 55 | use pest::Parser as _; 56 | let ctx = Context { 57 | file_name, 58 | input, 59 | arena, 60 | }; 61 | 62 | let mut pairs = match PegParser::parse(Rule::Unit, input) { 63 | Ok(pair) => pair, 64 | Err(err) => { 65 | let span = match err.location { 66 | InputLocation::Pos(pos) => { 67 | let pos = Position::new(input, pos).unwrap(); 68 | pos.span(&pos) 69 | } 70 | InputLocation::Span((start, end)) => { 71 | let start = Position::new(input, start).unwrap(); 72 | let end = Position::new(input, end).unwrap(); 73 | start.span(&end) 74 | } 75 | }; 76 | 77 | let message = match err.variant { 78 | ErrorVariant::ParsingError { 79 | positives, 80 | negatives, 81 | } => format!("positives: {:?}, negatives: {:?}", positives, negatives), 82 | _ => "parse error".to_string(), 83 | }; 84 | 85 | return Err(syn::ParseError { 86 | span: ctx.span(span), 87 | message, 88 | }); 89 | } 90 | }; 91 | 92 | Ok(ctx.arena.alloc(ctx.parse_unit(pairs.next().unwrap()))) 93 | } 94 | 95 | #[derive(Parser)] 96 | #[grammar = "syn/alkyne.pest"] 97 | struct PegParser; 98 | 99 | struct Context<'i> { 100 | file_name: &'i Path, 101 | input: &'i str, 102 | arena: &'i toolshed::Arena, 103 | } 104 | 105 | impl<'i> Context<'i> { 106 | fn span(&self, span: pest::Span<'i>) -> Span<'i> { 107 | syn::Span::new_from_parts( 108 | self.file_name, 109 | self.input, 110 | (span.start(), span.end()), 111 | ) 112 | } 113 | 114 | fn zero_span(&self) -> Span<'i> { 115 | syn::Span::new_from_parts(self.file_name, self.input, (0, 0)) 116 | } 117 | 118 | fn parse_unit(&self, pair: Pair<'i, Rule>) -> syn::Unit<'i> { 119 | let mut imports = Vec::new(); 120 | let mut stmts = Vec::new(); 121 | 122 | let mut value = None; 123 | for pair in pair.into_inner() { 124 | let span = self.span(pair.as_span()); 125 | match pair.as_rule() { 126 | Rule::Import => { 127 | let mut pairs = pair.into_inner(); 128 | let _use = pairs.next(); 129 | let name_pair = pairs.next().unwrap(); 130 | let name = syn::Ident { 131 | name: name_pair.as_str(), 132 | span: self.span(name_pair.as_span()), 133 | }; 134 | let _eq = pairs.next(); 135 | let str = match self.parse_expr(pairs.next().unwrap()) { 136 | syn::Expr::Str(s) => s, 137 | _ => unreachable!(), 138 | }; 139 | 140 | imports.push(syn::Use { 141 | var: name, 142 | import: str, 143 | span, 144 | }) 145 | } 146 | Rule::Stmt => { 147 | if let Some(stmt) = self.parse_stmt(pair) { 148 | stmts.push(stmt) 149 | } 150 | } 151 | Rule::Expr => { 152 | value = Some(self.parse_expr(pair)); 153 | break; 154 | } 155 | Rule::EOI => break, 156 | r => panic!("unexpected rule: {:?}", r), 157 | } 158 | } 159 | // We never got a final expression, so we make one up, either from a floating 160 | // statement or just a null. 161 | let value = match value { 162 | Some(value) => value, 163 | None => { 164 | if let Some(syn::Let { 165 | is_floating: true, .. 166 | }) = stmts.last() 167 | { 168 | stmts.pop().unwrap().value 169 | } else { 170 | syn::Expr::Null(syn::Null { 171 | // TODO(mcyoung): pick a better span. 172 | span: self.zero_span(), 173 | }) 174 | } 175 | } 176 | }; 177 | syn::Unit { 178 | imports: self.arena.alloc_vec(imports), 179 | stmts: self.arena.alloc_vec(stmts), 180 | value, 181 | } 182 | } 183 | 184 | fn parse_expr(&self, pair: Pair<'i, Rule>) -> syn::Expr<'i> { 185 | let span = self.span(pair.as_span()); 186 | match pair.as_rule() { 187 | Rule::Null => syn::Expr::Null(syn::Null { span }), 188 | Rule::True => syn::Expr::Bool(syn::Bool { value: true, span }), 189 | Rule::False => syn::Expr::Bool(syn::Bool { value: false, span }), 190 | Rule::Number => syn::Expr::Num(syn::Num { 191 | // TODO: don't just unwrap! 192 | value: pair.as_str().parse::().unwrap(), 193 | span, 194 | }), 195 | Rule::String => syn::Expr::Str(syn::Str { 196 | value: pair.as_str(), 197 | is_quoted: true, 198 | span, 199 | }), 200 | Rule::Ident => syn::Expr::Ref(syn::Ident { 201 | name: pair.as_str(), 202 | span, 203 | }), 204 | Rule::Zelf => syn::Expr::Builtin(syn::Builtin { 205 | ty: syn::BuiltinTy::Zelf, 206 | span, 207 | }), 208 | Rule::Super => syn::Expr::Builtin(syn::Builtin { 209 | ty: syn::BuiltinTy::Super, 210 | span, 211 | }), 212 | Rule::Top => syn::Expr::Builtin(syn::Builtin { 213 | ty: syn::BuiltinTy::Top, 214 | span, 215 | }), 216 | Rule::Here => syn::Expr::Builtin(syn::Builtin { 217 | ty: syn::BuiltinTy::Here, 218 | span, 219 | }), 220 | Rule::Std => syn::Expr::Builtin(syn::Builtin { 221 | ty: syn::BuiltinTy::Std, 222 | span, 223 | }), 224 | Rule::It => syn::Expr::Builtin(syn::Builtin { 225 | ty: syn::BuiltinTy::It, 226 | span, 227 | }), 228 | Rule::List => syn::Expr::List(syn::List { 229 | values: self 230 | .arena 231 | .alloc_vec(pair.into_inner().map(|p| self.parse_expr(p)).collect()), 232 | span, 233 | }), 234 | Rule::Object => syn::Expr::Object(syn::Object { 235 | zuper: None, 236 | fields: self 237 | .arena 238 | .alloc_vec(pair.into_inner().map(|p| self.parse_kv(p)).collect()), 239 | span, 240 | }), 241 | Rule::Block => { 242 | let mut stmts = Vec::new(); 243 | let mut value = None; 244 | for pair in pair.into_inner() { 245 | match pair.as_rule() { 246 | Rule::Stmt => { 247 | if let Some(stmt) = self.parse_stmt(pair) { 248 | stmts.push(stmt) 249 | } 250 | } 251 | Rule::Expr => { 252 | value = Some(self.parse_expr(pair)); 253 | break; 254 | } 255 | r => panic!("unexpected rule: {:?}", r), 256 | } 257 | } 258 | let value = match value { 259 | Some(value) => value, 260 | None => { 261 | if let Some(syn::Let { 262 | is_floating: true, .. 263 | }) = stmts.last() 264 | { 265 | stmts.pop().unwrap().value 266 | } else { 267 | syn::Expr::Null(syn::Null { 268 | // TODO(mcyoung): pick a better span. 269 | span, 270 | }) 271 | } 272 | } 273 | }; 274 | syn::Expr::Block(syn::Block { 275 | lets: self.arena.alloc_vec(stmts), 276 | value: self.arena.alloc(value), 277 | span, 278 | }) 279 | } 280 | Rule::FnLit => { 281 | let mut args = Vec::new(); 282 | for pair in pair.into_inner() { 283 | match pair.as_rule() { 284 | Rule::Fn => {} 285 | Rule::Pat => args.push(self.parse_pat(pair)), 286 | Rule::Expr => { 287 | return syn::Expr::Fn(syn::Fn { 288 | name: None, 289 | args: self.arena.alloc_vec(args), 290 | body: self.arena.alloc(self.parse_expr(pair)), 291 | span, 292 | }) 293 | } 294 | r => panic!("unexpected rule: {:?}", r), 295 | } 296 | } 297 | unreachable!() 298 | } 299 | Rule::IfExpr => { 300 | let mut clauses = Vec::new(); 301 | let mut else_block = None; 302 | let mut pairs = pair.into_inner(); 303 | while let Some(mut kw) = pairs.next() { 304 | if kw.as_rule() == Rule::Else { 305 | kw = pairs.next().unwrap(); 306 | } 307 | match kw.as_rule() { 308 | Rule::If => { 309 | let cond = self.parse_expr(pairs.next().unwrap()); 310 | let block = match self.parse_expr(pairs.next().unwrap()) { 311 | syn::Expr::Block(b) => b, 312 | _ => unreachable!(), 313 | }; 314 | clauses.push((&*self.arena.alloc(cond), block)) 315 | } 316 | Rule::Block => { 317 | let block = match self.parse_expr(kw) { 318 | syn::Expr::Block(b) => b, 319 | _ => unreachable!(), 320 | }; 321 | else_block = Some(block) 322 | } 323 | r => panic!("unexpected rule: {:?}", r), 324 | } 325 | } 326 | syn::Expr::If(syn::If { 327 | clauses: self.arena.alloc_vec(clauses), 328 | else_block, 329 | span, 330 | }) 331 | } 332 | Rule::SwitchExpr => { 333 | let mut clauses = Vec::new(); 334 | let mut else_clause = None; 335 | let mut cases = Vec::new(); 336 | 337 | let mut pairs = pair.into_inner(); 338 | let _switch = pairs.next(); 339 | let arg = self.parse_expr(pairs.next().unwrap()); 340 | while let Some(pair) = pairs.next() { 341 | match pair.as_rule() { 342 | Rule::Expr => cases.push(self.parse_expr(pair)), 343 | Rule::Arrow => { 344 | let clause = pairs.next().unwrap(); 345 | let value = self.parse_expr(clause); 346 | clauses.push(syn::SwitchClause { 347 | cases: self.arena.alloc_vec(mem::take(&mut cases)), 348 | value, 349 | }); 350 | } 351 | Rule::Else => { 352 | let _arrow = pairs.next(); 353 | else_clause = Some( 354 | &*self.arena.alloc(self.parse_expr(pairs.next().unwrap())), 355 | ); 356 | } 357 | r => panic!("unexpected rule: {:?}", r), 358 | } 359 | } 360 | syn::Expr::Switch(syn::Switch { 361 | arg: self.arena.alloc(arg), 362 | clauses: self.arena.alloc_vec(clauses), 363 | else_clause, 364 | span, 365 | }) 366 | } 367 | Rule::ForYield | Rule::ForExpr => { 368 | let mut pats = Vec::new(); 369 | let mut pairs = pair.into_inner(); 370 | let _for = pairs.next(); 371 | while let Some(pair) = pairs.next() { 372 | match pair.as_rule() { 373 | Rule::Pat => pats.push(self.parse_pat(pair)), 374 | Rule::In => break, 375 | r => panic!("unexpected rule: {:?}", r), 376 | } 377 | } 378 | let is_match_required = 379 | pairs.peek().unwrap().as_rule() != Rule::Question; 380 | if !is_match_required { 381 | let _ = pairs.next(); 382 | } 383 | let iter = self.parse_expr(pairs.next().unwrap()); 384 | let body = self.parse_expr(pairs.next().unwrap()); 385 | 386 | syn::Expr::For(syn::For { 387 | pats: self.arena.alloc_vec(pats), 388 | is_match_required, 389 | iter: self.arena.alloc(iter), 390 | body: self.arena.alloc(body), 391 | span, 392 | }) 393 | } 394 | Rule::YieldVal => { 395 | let mut pairs = pair.into_inner(); 396 | let _yield = pairs.next(); 397 | let value = self.parse_expr(pairs.next().unwrap()); 398 | 399 | syn::Expr::Yield(syn::Yield { 400 | value: self.arena.alloc(value), 401 | span, 402 | }) 403 | } 404 | Rule::YieldKv => { 405 | let mut pairs = pair.into_inner(); 406 | let _yield = pairs.next(); 407 | let kv = self.parse_kv(pairs.next().unwrap()); 408 | 409 | syn::Expr::YieldKv(syn::YieldKv { 410 | kv: self.arena.alloc(kv), 411 | span, 412 | }) 413 | } 414 | Rule::BreakExpr => { 415 | let mut pairs = pair.into_inner(); 416 | let _break = pairs.next(); 417 | let value = 418 | pairs.next().map(|p| &*self.arena.alloc(self.parse_expr(p))); 419 | 420 | syn::Expr::Break(syn::Break { value, span }) 421 | } 422 | Rule::Cont => syn::Expr::Cont(syn::Cont { span }), 423 | Rule::RetExpr => { 424 | let mut pairs = pair.into_inner(); 425 | let _return = pairs.next(); 426 | let value = self.parse_expr(pairs.next().unwrap()); 427 | 428 | syn::Expr::Ret(syn::Ret { 429 | value: self.arena.alloc(value), 430 | span, 431 | }) 432 | } 433 | Rule::SufExpr => { 434 | let mut pairs = pair.into_inner(); 435 | let mut expr = self.parse_expr(pairs.next().unwrap()); 436 | for suf in pairs { 437 | let span = Span::join(expr.span(), self.span(suf.as_span())); 438 | match suf.as_rule() { 439 | Rule::Member => { 440 | let mut suf = suf.into_inner(); 441 | let nullable = match suf.peek().map(|p| p.as_rule()) { 442 | Some(Rule::Question) => { 443 | let _ = suf.next(); 444 | true 445 | } 446 | _ => false, 447 | }; 448 | let field = suf.next().unwrap(); 449 | let field_expr = match field.as_rule() { 450 | Rule::Ident => syn::Str { 451 | value: field.as_str(), 452 | is_quoted: false, 453 | span: self.span(field.as_span()), 454 | }, 455 | Rule::String => syn::Str { 456 | value: field.as_str(), 457 | is_quoted: true, 458 | span: self.span(field.as_span()), 459 | }, 460 | r => panic!("unexpected rule: {:?}", r), 461 | }; 462 | expr = syn::Expr::Member(syn::Member { 463 | value: self.arena.alloc(expr), 464 | member: self.arena.alloc(syn::Expr::Str(field_expr)), 465 | nullable, 466 | span, 467 | }) 468 | } 469 | Rule::Call => { 470 | let args = suf.into_inner().map(|p| self.parse_expr(p)).collect(); 471 | expr = syn::Expr::Call(syn::Call { 472 | fnc: self.arena.alloc(expr), 473 | args: self.arena.alloc_vec(args), 474 | span, 475 | }) 476 | } 477 | Rule::Index => { 478 | let mut suf = suf.into_inner(); 479 | let nullable = match suf.peek().map(|p| p.as_rule()) { 480 | Some(Rule::Question) => { 481 | let _ = suf.next(); 482 | true 483 | } 484 | _ => false, 485 | }; 486 | let args = suf.map(|p| self.parse_expr(p)).collect(); 487 | expr = syn::Expr::Index(syn::Index { 488 | value: self.arena.alloc(expr), 489 | args: self.arena.alloc_vec(args), 490 | nullable, 491 | span, 492 | }) 493 | } 494 | Rule::DoExpr => { 495 | let mut suf = suf.into_inner(); 496 | let _do = suf.next(); 497 | let nullable = match suf.peek().map(|p| p.as_rule()) { 498 | Some(Rule::Question) => { 499 | let _ = suf.next(); 500 | true 501 | } 502 | _ => false, 503 | }; 504 | let body = self.parse_expr(suf.next().unwrap()); 505 | expr = syn::Expr::Do(syn::Do { 506 | it: self.arena.alloc(expr), 507 | body: self.arena.alloc(body), 508 | nullable, 509 | span, 510 | }) 511 | } 512 | Rule::Object => { 513 | let fields = suf.into_inner().map(|p| self.parse_kv(p)).collect(); 514 | expr = syn::Expr::Object(syn::Object { 515 | zuper: Some(self.arena.alloc(expr)), 516 | fields: self.arena.alloc_vec(fields), 517 | span, 518 | }) 519 | } 520 | r => panic!("unexpected rule: {:?}", r), 521 | } 522 | } 523 | expr 524 | } 525 | Rule::UnaryExpr => { 526 | let mut pairs = pair.into_inner().rev(); 527 | let mut expr = self.parse_expr(pairs.next().unwrap()); 528 | for op in pairs { 529 | let op_ty = match op.as_rule() { 530 | Rule::Dash => syn::UnOpTy::Neg, 531 | Rule::Bang => syn::UnOpTy::Not, 532 | r => panic!("unexpected rule: {:?}", r), 533 | }; 534 | let op_span = self.span(op.as_span()); 535 | let span = Span::join(op_span, expr.span()); 536 | expr = syn::Expr::UnOp(syn::UnOp { 537 | arg: self.arena.alloc(expr), 538 | ty: op_ty, 539 | span, 540 | }); 541 | } 542 | expr 543 | } 544 | Rule::ElvisExpr 545 | | Rule::AndExpr 546 | | Rule::OrExpr 547 | | Rule::EqExpr 548 | | Rule::OrdExpr 549 | | Rule::SumExpr 550 | | Rule::ProdExpr => { 551 | let mut pairs = pair.into_inner(); 552 | let mut expr = self.parse_expr(pairs.next().unwrap()); 553 | while let Some(op) = pairs.next() { 554 | let op_ty = match op.as_rule() { 555 | Rule::Plus => syn::BinOpTy::Add, 556 | Rule::Dash => syn::BinOpTy::Sub, 557 | Rule::Star => syn::BinOpTy::Mul, 558 | Rule::Slash => syn::BinOpTy::Div, 559 | Rule::Pct => syn::BinOpTy::Rem, 560 | Rule::AndAnd => syn::BinOpTy::AndAnd, 561 | Rule::OrOr => syn::BinOpTy::OrOr, 562 | Rule::EqEq => syn::BinOpTy::Eq, 563 | Rule::NeEq => syn::BinOpTy::Ne, 564 | Rule::Gt => syn::BinOpTy::Gt, 565 | Rule::Lt => syn::BinOpTy::Lt, 566 | Rule::GtEq => syn::BinOpTy::Ge, 567 | Rule::LtEq => syn::BinOpTy::Le, 568 | Rule::Elvis => syn::BinOpTy::Elvis, 569 | r => panic!("unknown rule: {:?}", r), 570 | }; 571 | let rhs = self.parse_expr(pairs.next().unwrap()); 572 | let span = Span::join(expr.span(), rhs.span()); 573 | expr = syn::Expr::BinOp(syn::BinOp { 574 | lhs: self.arena.alloc(expr), 575 | rhs: self.arena.alloc(rhs), 576 | ty: op_ty, 577 | span, 578 | }) 579 | } 580 | expr 581 | } 582 | Rule::Parens | Rule::Expr => { 583 | self.parse_expr(pair.into_inner().next().unwrap()) 584 | } 585 | r => panic!("unknown rule: {:?}", r), 586 | } 587 | } 588 | 589 | fn parse_stmt(&self, pair: Pair<'i, Rule>) -> Option> { 590 | let span = self.span(pair.as_span()); 591 | let stmt = match pair.as_rule() { 592 | Rule::LetStmt => { 593 | let mut pairs = pair.into_inner(); 594 | let _let = pairs.next(); 595 | let pat = self.parse_pat(pairs.next().unwrap()); 596 | let _eq = pairs.next(); 597 | let value = self.parse_expr(pairs.next().unwrap()); 598 | syn::Let { 599 | pat: Some(&*self.arena.alloc(pat)), 600 | is_floating: false, 601 | value, 602 | span, 603 | } 604 | } 605 | Rule::FnStmt => { 606 | let mut args = Vec::new(); 607 | let mut pairs = pair.into_inner(); 608 | let _fn = pairs.next(); 609 | let name_pair = pairs.next().unwrap(); 610 | let name = syn::Ident { 611 | name: name_pair.as_str(), 612 | span: self.span(name_pair.as_span()), 613 | }; 614 | for pair in pairs { 615 | match pair.as_rule() { 616 | Rule::Pat => args.push(self.parse_pat(pair)), 617 | Rule::Eq => {} 618 | _ => { 619 | let body = self.parse_expr(pair); 620 | let fnc = syn::Expr::Fn(syn::Fn { 621 | name: Some(name), 622 | args: self.arena.alloc_vec(args), 623 | body: self.arena.alloc(body), 624 | span, 625 | }); 626 | return Some(syn::Let { 627 | pat: Some(&*self.arena.alloc(syn::Pat::Bind(name))), 628 | is_floating: false, 629 | value: fnc, 630 | span, 631 | }); 632 | } 633 | } 634 | } 635 | unreachable!() 636 | } 637 | Rule::ExprStmt => syn::Let { 638 | pat: None, 639 | is_floating: false, 640 | value: self.parse_expr(pair.into_inner().next().unwrap()), 641 | span, 642 | }, 643 | Rule::Stmt => { 644 | if let Some(pair) = pair.into_inner().next() { 645 | return self.parse_stmt(pair); 646 | } else { 647 | return None; 648 | } 649 | } 650 | _ => syn::Let { 651 | pat: None, 652 | is_floating: true, 653 | value: self.parse_expr(pair), 654 | span, 655 | }, 656 | }; 657 | Some(stmt) 658 | } 659 | 660 | fn parse_pat(&self, pair: Pair<'i, Rule>) -> syn::Pat<'i> { 661 | let span = self.span(pair.as_span()); 662 | match pair.as_rule() { 663 | Rule::LiteralPat => { 664 | syn::Pat::Expr(self.parse_expr(pair.into_inner().next().unwrap())) 665 | } 666 | Rule::BindPat => { 667 | let mut pairs = pair.into_inner(); 668 | let name_pair = pairs.next().unwrap(); 669 | let name = syn::Ident { 670 | name: name_pair.as_str(), 671 | span, 672 | }; 673 | syn::Pat::Bind(name) 674 | } 675 | Rule::ExactListPat => syn::Pat::ExactList(syn::ExactListPat { 676 | pats: self 677 | .arena 678 | .alloc_vec(pair.into_inner().map(|p| self.parse_pat(p)).collect()), 679 | span, 680 | }), 681 | Rule::GlobListPat => { 682 | let mut front = Vec::new(); 683 | let mut back = Vec::new(); 684 | let mut middle = None; 685 | 686 | let mut has_seen_dotdot = false; 687 | let mut pairs = pair.into_inner(); 688 | while let Some(pair) = pairs.next() { 689 | if let Rule::DotDot = pair.as_rule() { 690 | has_seen_dotdot = true; 691 | if let Some(Rule::Ident) = pairs.peek().map(|p| p.as_rule()) { 692 | let name_pair = pairs.next().unwrap(); 693 | middle = Some(syn::Ident { 694 | name: name_pair.as_str(), 695 | span, 696 | }); 697 | } 698 | continue; 699 | } 700 | 701 | let pat = self.parse_pat(pair); 702 | if !has_seen_dotdot { 703 | front.push(pat); 704 | } else { 705 | back.push(pat); 706 | } 707 | } 708 | 709 | syn::Pat::GlobList(syn::GlobListPat { 710 | front: self.arena.alloc_vec(front), 711 | middle, 712 | back: self.arena.alloc_vec(back), 713 | span, 714 | }) 715 | } 716 | Rule::ObjectPat => { 717 | let mut fields = Vec::new(); 718 | let mut is_exact = true; 719 | for pair in pair.into_inner() { 720 | match pair.as_rule() { 721 | Rule::DotDot => is_exact = false, 722 | Rule::KvPat => { 723 | let span = self.span(pair.as_span()); 724 | 725 | let mut inner = pair.into_inner(); 726 | let key = inner.next().unwrap(); 727 | let nullable = match inner.peek().map(|p| p.as_rule()) { 728 | Some(Rule::Question) => { 729 | let _ = inner.next(); 730 | true 731 | } 732 | _ => false, 733 | }; 734 | let val = inner.next(); 735 | 736 | let key_expr = match key.as_rule() { 737 | Rule::Ident => syn::Expr::Str(syn::Str { 738 | value: key.as_str(), 739 | is_quoted: false, 740 | span: self.span(key.as_span()), 741 | }), 742 | Rule::String => syn::Expr::Str(syn::Str { 743 | value: key.as_str(), 744 | is_quoted: true, 745 | span: self.span(key.as_span()), 746 | }), 747 | r => panic!("unexpected rule: {:?}", r), 748 | }; 749 | 750 | let val_pat = val.map(|v| self.parse_pat(v)); 751 | 752 | fields.push(syn::Kv { 753 | key: key_expr, 754 | value: val_pat, 755 | ty: syn::KvType::Normal, 756 | nullable, 757 | span, 758 | }) 759 | } 760 | r => panic!("unexpected rule: {:?}", r), 761 | } 762 | } 763 | 764 | syn::Pat::Object(syn::ObjectPat { 765 | fields: self.arena.alloc_vec(fields), 766 | is_exact, 767 | span, 768 | }) 769 | } 770 | Rule::OrPat => { 771 | let pairs = pair.into_inner(); 772 | let mut pats = Vec::new(); 773 | 774 | for pair in pairs { 775 | if let Rule::Or = pair.as_rule() { 776 | continue; 777 | } 778 | 779 | pats.push(self.parse_pat(pair)); 780 | } 781 | if pats.len() == 1 { 782 | return pats.pop().unwrap(); 783 | } 784 | syn::Pat::Or(syn::OrPat { 785 | pats: self.arena.alloc_vec(pats), 786 | span, 787 | }) 788 | } 789 | Rule::AtPat => { 790 | let mut idents = Vec::new(); 791 | for pair in pair.into_inner() { 792 | match pair.as_rule() { 793 | Rule::Ident => { 794 | let name = syn::Ident { 795 | name: pair.as_str(), 796 | span, 797 | }; 798 | idents.push(name); 799 | } 800 | _ => { 801 | let mut pat = self.parse_pat(pair); 802 | for ident in idents.into_iter().rev() { 803 | let span = Span::join(ident.span(), pat.span()); 804 | pat = syn::Pat::At(syn::AtPat { 805 | pat: self.arena.alloc(pat), 806 | ident, 807 | span, 808 | }) 809 | } 810 | return pat; 811 | } 812 | } 813 | } 814 | unreachable!() 815 | } 816 | Rule::ParenPat | Rule::Pat => { 817 | self.parse_pat(pair.into_inner().next().unwrap()) 818 | } 819 | r => panic!("unexpected rule: {:?}", r), 820 | } 821 | } 822 | 823 | fn parse_kv(&self, pair: Pair<'i, Rule>) -> syn::Kv<'i, syn::Expr<'i>> { 824 | let span = self.span(pair.as_span()); 825 | 826 | let mut inner = pair.into_inner(); 827 | let key = inner.next().unwrap(); 828 | let nullable = match inner.peek().map(|p| p.as_rule()) { 829 | Some(Rule::Question) => { 830 | let _ = inner.next(); 831 | true 832 | } 833 | _ => false, 834 | }; 835 | let val = inner.next().unwrap(); 836 | 837 | let mut key_inner = key.into_inner(); 838 | let mut ty = syn::KvType::Normal; 839 | if let Some(Rule::Super) = key_inner.peek().map(|p| p.as_rule()) { 840 | ty = syn::KvType::Super; 841 | let _ = key_inner.next(); 842 | if let Some(Rule::Question) = key_inner.peek().map(|p| p.as_rule()) { 843 | ty = syn::KvType::MaybeSuper; 844 | let _ = key_inner.next(); 845 | } 846 | } 847 | 848 | let key_pair = key_inner.next().unwrap(); 849 | let key_expr = match key_pair.as_rule() { 850 | Rule::Ident => syn::Expr::Str(syn::Str { 851 | value: key_pair.as_str(), 852 | is_quoted: false, 853 | span: self.span(key_pair.as_span()), 854 | }), 855 | Rule::String => syn::Expr::Str(syn::Str { 856 | value: key_pair.as_str(), 857 | is_quoted: true, 858 | span: self.span(key_pair.as_span()), 859 | }), 860 | Rule::Expr => self.parse_expr(key_pair), 861 | _ => panic!("unexpected rule: {:?}", key_pair.as_rule()), 862 | }; 863 | 864 | let val_expr = self.parse_expr(val); 865 | 866 | syn::Kv { 867 | key: key_expr, 868 | value: val_expr, 869 | ty, 870 | nullable, 871 | span, 872 | } 873 | } 874 | } 875 | --------------------------------------------------------------------------------