├── .github └── FUNDING.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── assets ├── help-application.txt ├── help-avatars.txt ├── help-equality.txt ├── help-eval.txt ├── help-graph.txt ├── help-hide.txt ├── help-inequality.txt ├── help-list.txt ├── help-pairs.txt ├── help-roles.txt ├── help-rules.txt ├── help.txt └── syntax.txt ├── examples └── avalog_repl.rs ├── source ├── amb.txt ├── amb_fail.txt ├── amb_success-2.txt ├── amb_success.txt ├── app_empty.txt ├── app_match.txt ├── associativity-0.txt ├── associativity.txt ├── avalog_state.txt ├── bool_alg-0.txt ├── bool_alg.txt ├── bool_alg2.txt ├── bool_alg3.txt ├── borrowchk.txt ├── capital-0.txt ├── capital.txt ├── category-0.txt ├── category.txt ├── chu_space.txt ├── closure_calculus.txt ├── commutativity-0.txt ├── commutativity.txt ├── convert_eq_into_has-0.txt ├── convert_eq_into_has.txt ├── convert_has_into_eq-0.txt ├── convert_has_into_eq.txt ├── convert_has_into_eq2-0.txt ├── convert_has_into_eq2.txt ├── convert_has_into_eq3-0.txt ├── convert_has_into_eq3.txt ├── convert_has_into_eq4-0.txt ├── convert_has_into_eq4.txt ├── convert_has_into_eq5-0.txt ├── convert_has_into_eq5.txt ├── convert_has_into_eq6-0.txt ├── convert_has_into_eq6-1.txt ├── convert_has_into_eq6.txt ├── copy-0.txt ├── copy.txt ├── grandparent-0.txt ├── grandparent.txt ├── inner-0.txt ├── inner.txt ├── inner2-0.txt ├── inner2.txt ├── mom_and_dad-0.txt ├── mom_and_dad-1.txt ├── mom_and_dad-2.txt ├── mom_and_dad.txt ├── nat.txt ├── no_amb.txt ├── parents-0.txt ├── parents-1.txt ├── parents-2.txt ├── parents.txt ├── parents2-0.txt ├── parents2.txt ├── parents3-0.txt ├── parents3.txt ├── parents4-0.txt ├── parents4.txt ├── parents5-0.txt ├── parents5.txt ├── parents6-0.txt ├── parents6.txt ├── parents7-0.txt ├── parents7.txt ├── parents8-0.txt ├── parents8.txt ├── path_sem.txt ├── psi.txt ├── role_block.txt ├── role_lift_app-0.txt ├── role_lift_app.txt ├── role_lift_ava-0.txt ├── role_lift_ava.txt ├── role_lift_eq-0.txt ├── role_lift_eq.txt ├── role_lift_inner-0.txt ├── role_lift_inner.txt ├── sibling-0.txt ├── sibling.txt ├── squares-0.txt ├── squares.txt ├── squares2-0.txt ├── squares2.txt ├── squares3-0.txt ├── squares3.txt ├── squares4-0.txt ├── squares4.txt ├── string.txt ├── sub_type-0.txt ├── sub_type.txt ├── transitivity-0.txt └── transitivity.txt ├── src ├── lib.rs └── parsing.rs └── tests └── lib.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: bvssvni 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | examples/test.rs 13 | tmp.dot 14 | tmp.png 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "avalog" 3 | version = "0.7.2" 4 | authors = ["Sven Nilsen "] 5 | edition = "2018" 6 | keywords = ["avatar", "logic", "prolog", "solver", "advancedresearch"] 7 | description = "An experimental implementation of Avatar Logic with a Prolog-like syntax" 8 | license = "MIT OR Apache-2.0" 9 | readme = "README.md" 10 | repository = "https://github.com/advancedresearch/avalog.git" 11 | homepage = "https://github.com/advancedresearch/avalog" 12 | exclude = ["source/*"] 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | monotonic_solver = "0.5.0" 18 | piston_meta = "2.0.1" 19 | 20 | [dev-dependencies] 21 | read_token = "1.0.0" 22 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 PistonDevelopers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Avalog 2 | 3 | An experimental implementation of [Avatar Logic](https://advancedresearch.github.io/avatar-extensions/summary.html) 4 | with a Prolog-like syntax. 5 | 6 | ```text 7 | === Avalog 0.7 === 8 | Type `help` for more information. 9 | > parent'(alice) : mom 10 | > (bob, parent'(alice)) 11 | > prove mom(bob) => parent'(alice) 12 | parent'(alice) : mom 13 | (bob, parent'(alice)) 14 | ---------------------------------- 15 | mom(bob) => parent'(alice) 16 | 17 | OK 18 | ``` 19 | 20 | To run Avalog from your Terminal, type: 21 | 22 | ```text 23 | cargo install --example avalog_repl avalog 24 | ``` 25 | 26 | Then, to run: 27 | 28 | ```text 29 | avalog_repl 30 | ``` 31 | 32 | Based on paper [Avatar Binary Relations](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/avatar-binary-relations.pdf). 33 | 34 | ### Motivation 35 | 36 | Avatar Logic is an attempt to create a formal language that satisfies "avatars", 37 | in the sense of [Avatar Extensions](https://github.com/advancedresearch/path_semantics/blob/master/sequences.md#avatar-extensions). 38 | Avatar Extensions is a technique for abstract generalization in [Path Semantics](https://github.com/advancedresearch/path_semantics), 39 | an extremely expressive language for mathematical programming. 40 | 41 | In higher dimensional programming, such as Path Semantics, one would like to generalize 42 | the abstractions across multiple dimensions at the same time, without having to write axioms 43 | for each dimension. 44 | 45 | If normal programming is 2D, then [normal paths](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/normal-paths.pdf) 46 | in Path Semantics is 3D. 47 | Avatar Extensions seeks to go beyond 3D programming to arbitrary higher dimensions. 48 | Avatar Logic is a deep result from discussing interpretations of [Avatar Graphs](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/avatar-graphs.pdf), 49 | which relates a variant of Cartesian combinatorics with graphs, group actions and topology. 50 | 51 | ### Example: Grandma Alice 52 | 53 | ```text 54 | uniq parent 55 | parent'(alice) : mom 56 | grandparent'(alice) : grandma 57 | parent'(bob) : dad 58 | (bob, parent'(alice)) 59 | (carl, parent'(bob)) 60 | (X, grandparent'(Z)) :- (X, parent'(Y)), (Y, parent'(Z)). 61 | ``` 62 | 63 | This can be used to prove the following goal: 64 | 65 | ```text 66 | grandma(carl) => grandparent'(alice) 67 | ``` 68 | 69 | The first statement `uniq parent` tells that the 1-avatar `parent` should behave uniquely. 70 | Basically, this means one person can have maximum one mom and one dad. 71 | 72 | When this is turned off, one can only say e.g. "Bob has a mom who's name is Alice", 73 | but not "Bob's mom is Alice", because Bob might have more than one mom. 74 | 75 | Formally, `mom(bob) => parent'(alice)` (has) vs `mom(bob) = parent'(alice)` (is). 76 | 77 | The statement `parent'(alice) : mom` says that Alice has a 1-avatar "parent" which 78 | is assigned the role "mom". 79 | Avatar Logic "knows" that Alice, and Alice as a parent, are one and the same, 80 | but the relations to Alice are universal while relations to Alice as a parent depends on context. 81 | 82 | The relation `(bob, parent'(alice))` does not specify how Bob and Alice as a parent are related, 83 | because it is inferred from the assigned role to Alice as a parent. 84 | This simplifies the logic a lot for higher dimensions of programming. 85 | 86 | The rule `(X, grandparent'(Z)) :- (X, parent'(Y)), (Y, parent'(Z)).` is similar 87 | to a [Horn clause](https://en.wikipedia.org/wiki/Horn_clause), which is used in 88 | [Prolog](https://en.wikipedia.org/wiki/Prolog). 89 | 90 | The grandparent rule works for any combination of moms and dads. 91 | It also works for other parental relationship that can be used for e.g. animals. 92 | You can use separate roles for separate kind of objects, but not mix e.g. humans and animals. 93 | This is because Avatar Logic is kind of like [dependent types](https://en.wikipedia.org/wiki/Dependent_type), but for logic. 94 | Relationships depends on the values, but enforces local consistency, kind of like types. 95 | 96 | ### Introduction to Avatar Logic 97 | 98 | In Prolog, you would write relations using predicates, e.g. `mom(alice, bob)`. 99 | The problem is that predicates are 1) unconstrained and 2) axioms doesn't carry over 100 | arbitrary Cartesian relations. 101 | 102 | In Avatar Logic, instead of predicates, you use "binary relations" with added axioms for roles and avatars. 103 | 104 | To explain how Avatar Logic works, one can start with binary relations. 105 | 106 | A binary relation is an ordered pair: 107 | 108 | ```text 109 | (a, b) 110 | ``` 111 | 112 | By adding axioms to binary relations, one can improve expressiveness and simplify 113 | modeling over abstract relations. This is used because unconstrained relations are 114 | too hard to use for formalizing advanced mathematical theories, like Path Semantics. 115 | 116 | Avatar Logic consists of two kinds of pairs: 117 | 118 | - [Unique Universal Binary Relations](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/unique-universal-binary-relations.pdf) 119 | - [Avatar Binary Relations](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/avatar-binary-relations.pdf) 120 | 121 | Axioms: 122 | 123 | ```text 124 | p(a, b) b : p p(a) = b 125 | p(a, q'(b)) q'(b) : p p(a) = {q'(_)} ∈ q'(b) 126 | ``` 127 | 128 | To make Avatar Binary Relations behave like Unique Universal Binary Relations, 129 | one can use the `uniq q` directive where `q` is a 1-avatar. 130 | This forces the following relation for `q`: 131 | 132 | ```text 133 | p(a) = q'(b) 134 | ``` 135 | 136 | ### Design 137 | 138 | Uses the [Monotonic-Solver](https://github.com/advancedresearch/monotonic_solver) library 139 | for generic automated theorem proving. 140 | 141 | Uses the [Piston-Meta](https://github.com/pistondevelopers/meta) library for meta parsing. 142 | 143 | The axioms for Avatar Logic can not be used directly, 144 | but instead inference rules are derived from the axioms. 145 | 146 | A part of this project is to experiment with inference rules, 147 | so no recommended set of inference is published yet. 148 | 149 | The inference rules are coded in the `infer` function. 150 | -------------------------------------------------------------------------------- /assets/help-application.txt: -------------------------------------------------------------------------------- 1 | === Application === 2 | When applying a role `p` to some argument `a`, 3 | it is written `p(a)`. 4 | 5 | Sometimes, application returns a 1-avatar. 6 | To get the inner value of a 1-avatar, one can use `.` in front: 7 | 8 | .p(a) 9 | 10 | Application is treated as unique by default. 11 | This means that when writing: 12 | 13 | p(b) : f 14 | (a, p(b)) 15 | 16 | This adds the following expressions: 17 | 18 | f(a) = p(b) 19 | f(a) => p(b) 20 | 21 | To make application non-unique, use the following trick: 22 | 23 | q'(b) : f 24 | p(b) = q'(b) 25 | (a, q'(.p(b))) 26 | 27 | This only adds the following expression: 28 | 29 | f(a) => q'(b) 30 | -------------------------------------------------------------------------------- /assets/help-avatars.txt: -------------------------------------------------------------------------------- 1 | === Avatars === 2 | The benefit of Avatar Logic is that it can infer 3 | a lot from the data without explicit constraints. 4 | 5 | In Avatar Logic, a 1-avatar is a simple extension of a term. 6 | 1-avatars are used when using the term directly is too constrained. 7 | 8 | Higher avatars than 1-avatars are an implicit result from 9 | Avatar Logic itself, so you only need to specify 1-avatars. 10 | 11 | For example: 12 | 13 | parent'(alice) : mom 14 | (bob, parent'(alice)) 15 | 16 | Avalog adds the following expression: 17 | 18 | mom(bob) => parent'(alice) 19 | 20 | Here, `parent` is the 1-avatar extending `alice`. 21 | 22 | One can think about this as `mom(bob)` being a set, 23 | which contains only `parent'(_)` 1-avatars. 24 | Among these parents, is `parent'(alice)`. 25 | Hence, this can be read as "Bob has a mom named Alice". 26 | 27 | To specify that Bob only has one mom, one can write: 28 | 29 | uniq parent 30 | 31 | This adds the following expression: 32 | 33 | mom(bob) = parent'(alice) 34 | 35 | Now, one can read this as "Bob's mom is Alice". 36 | -------------------------------------------------------------------------------- /assets/help-equality.txt: -------------------------------------------------------------------------------- 1 | === Equality === 2 | An equality is an expression of the following form: 3 | 4 | p(a) = b 5 | 6 | Equalities are used to substitute sub-expressions in application. 7 | 8 | For example, `p(a)` is substituted with `b` when `p(a) = b`. 9 | 10 | In Avatar Logic, equalities are inferred from, 11 | but stored as separated facts, from pairs, e.g. `(a, b)` where `b : p`. 12 | 13 | This allows one to control substitution behavior. 14 | 15 | For example, a 1-avatar `territory` might be used to describe 16 | countries that share the same capital. When asking 17 | what the capital is of the country, it is convenient to lift the equality: 18 | 19 | capital(X) = Y :- capital(territory'(X)) = Y. 20 | 21 | When a "has" relation `p(a) => q'(b)` where `uniq q`, 22 | it is lifted to `p(a) = q'(b)` automatically. 23 | 24 | Equalities are lifted into "has" relations automatically. 25 | -------------------------------------------------------------------------------- /assets/help-eval.txt: -------------------------------------------------------------------------------- 1 | === Evaluation === 2 | Avalog supports injection of evaluation role into expressions 3 | for easier evaluation in advanced modeling. 4 | 5 | For example: 6 | 7 | x : val 8 | (f(x), x) 9 | eval val 10 | (f(f(x)), x) 11 | 12 | The directive `eval val` expands `(f(f(x)), x)` into: 13 | 14 | (f(val(f(x))), x) 15 | 16 | Here, `val` is the role used for evaluation. 17 | Notice that the role is not injected for top expressions. 18 | 19 | Similarly `(f(f(f(x))), x)` would be expanded to: 20 | 21 | (f(val(f(val(f(x))))), x) 22 | 23 | The directive `no eval` turns off injection of evaluation role. 24 | 25 | The reason roles are injected like this, 26 | is because Avatar Logic can have multiple dimensions of evaluation. 27 | -------------------------------------------------------------------------------- /assets/help-graph.txt: -------------------------------------------------------------------------------- 1 | === GraphViz Export === 2 | Avalog supports exporting to the GraphViz format for visualization. 3 | 4 | graph expands all facts and exports data 5 | graphf "" sets output file 6 | no graphf uses ".dot" after last imported file, 7 | or "tmp.dot" if there is no imported file 8 | graphl "" sets layout GraphViz algorithm 9 | 10 | List of layout algorithm: 11 | 12 | dot hierarchial directed graphs 13 | neato spring model using energy 14 | fdp spring model using forces 15 | sfdp multiscale fdp 16 | twopi concentric circles 17 | circo circo 18 | -------------------------------------------------------------------------------- /assets/help-hide.txt: -------------------------------------------------------------------------------- 1 | === Hide and Show Facts and Rules === 2 | 3 | You can hide and show facts and rules in proof assumptions. 4 | 5 | Special commands: 6 | - hide facts 7 | - hide rules 8 | - hide all 9 | - show facts 10 | - show rules 11 | - show all 12 | -------------------------------------------------------------------------------- /assets/help-inequality.txt: -------------------------------------------------------------------------------- 1 | === Inequality === 2 | Avatar Logic supports inequality in rules. 3 | 4 | For example: 5 | 6 | (a, foo) 7 | (X, Y) :- (Y, X), X != foo. 8 | 9 | Since `foo != foo` is false, the pair `(foo, a)` will not be generated. 10 | 11 | Variables can be used as patterns: 12 | 13 | (X, Y) :- (Y, X), X != F'(Z). 14 | 15 | Inequality is often used to terminate rules, 16 | that otherwise would expand infinitely. 17 | -------------------------------------------------------------------------------- /assets/help-list.txt: -------------------------------------------------------------------------------- 1 | === List === 2 | Avalog supports variable number of arguments, 3 | which can be used to represent lists. 4 | 5 | For example: 6 | 7 | (do, copy(A)) :- (do, copy(A, ..)). 8 | (do, copy(B)) :- (do, copy(A, ..B)). 9 | (do, copy(a, b, c, d)) 10 | 11 | The pattern `..` ignores the tail. 12 | The pattern `..B` puts the tail in a new list. 13 | 14 | This generates the following pairs: 15 | 16 | (do, copy(a)) 17 | (do, copy(b)) 18 | (do, copy(c)) 19 | (do, copy(d)) 20 | -------------------------------------------------------------------------------- /assets/help-pairs.txt: -------------------------------------------------------------------------------- 1 | === Pairs === 2 | In Avatar Logic, data consists mainly of ordered pairs. 3 | 4 | A pair can also be thought of as a directed edge between two nodes. 5 | 6 | For example: 7 | 8 | (a, b) pair with two concrete elements `a` and `b` 9 | an edge from `a` to `b` 10 | (a, q'(b)) pair with a 1-avatar `q'(b)` 11 | an edge from `a` to `q'(b)` 12 | (a, p(b)) pair with application `p(b)` 13 | an edge from `a` to `p(b)` 14 | 15 | This is different from normal logic, where predicates are used instead. 16 | 17 | The benefit of using pairs is that can think about the underlying 18 | data as a matrix with columns and rows for each term. 19 | The data of pairs also can also be thought of as a graph, 20 | with nodes and edges, where each edge is a pair. 21 | 22 | The left argument `A` of the pair `(A, B)` can be thought of as 23 | the input, while the right argument `B` can be thought of as the output. 24 | 25 | What pairs mean depends on the interpretation of what you are modeling. 26 | -------------------------------------------------------------------------------- /assets/help-roles.txt: -------------------------------------------------------------------------------- 1 | === Roles === 2 | In Avatar Logic, a role is kind of like a type, 3 | but is also kind of like a function, or sometimes like a set. 4 | 5 | For example: 6 | 7 | london : city the city London 8 | 9 | When you have a pair: 10 | 11 | (a, london) 12 | 13 | Avalog adds the following expressions: 14 | 15 | city(a) = london the city of `a` "is" London 16 | city(a) => london the city of `a` "has" a London 17 | 18 | These expressions are used for further inference. 19 | The expresssion `city(a) = london` means that when you write 20 | `city(a)` anywhere, it can be substituted with `london`. 21 | -------------------------------------------------------------------------------- /assets/help-rules.txt: -------------------------------------------------------------------------------- 1 | === Rules === 2 | A rule in Avatar Logic is similar to rules in Prolog. 3 | 4 | For example: 5 | 6 | (X, Y) :- (Y, X), X : capital, Y : country. 7 | 8 | The left side `(X, Y)` is the Head and the right side is the Body of rule: 9 | 10 | Head :- Body. 11 | 12 | The Head is true if the Body is true. 13 | 14 | When adding facts, the rules can infer new facts: 15 | 16 | england : country 17 | london : capital 18 | (england, london) 19 | ----------------- 20 | (london, england) 21 | -------------------------------------------------------------------------------- /assets/help.txt: -------------------------------------------------------------------------------- 1 | === Avalog Help === 2 | Made by Sven Nilsen, 2020 3 | 4 | Avalog is based on the theory of avatar extensions in path semantics: 5 | https://github.com/advancedresearch/path_semantics/blob/master/sequences.md#avatar-extensions 6 | 7 | Special commands: 8 | - bye quits the program 9 | - ? search for a pattern 10 | - prove prove a goal 11 | - provenr prove a goal without reduction of the proof 12 | - import "" imports an Avalog file 13 | - reload reload last import while keeping added facts and rules 14 | - clear clears all facts and rules 15 | - maxsize proof search uses maximum number of facts and rules 16 | - no maxsize proof search uses unlimited number of facts and rules 17 | - graph exports expanded pairs and roles to GraphViz format 18 | - graphf "" set GraphViz file name 19 | - no graphf use ".dot" after last imported Avalog file or "tmp.dot" 20 | - echo prints out debug format of expression 21 | - help hide more help about hiding and showing facts and rules 22 | - help pairs more help about pairs 23 | - help roles more help about roles 24 | - help avatars more help about avatars 25 | - help rules more help about rules 26 | - help application more help about application 27 | - help eval more help about evaluation 28 | - help equality more help about equality 29 | - help inequality more help about inequality 30 | - help list more help about lists 31 | - help graph more help about GraphViz export 32 | 33 | Type in an expression in avatar logic, e.g. `(a, q'(b))` 34 | -------------------------------------------------------------------------------- /assets/syntax.txt: -------------------------------------------------------------------------------- 1 | _seps: "/(){},.:='-!" 2 | 3 | 20 neq = [term:"left" .w? "!=" .w? term:"right"] 4 | 19 role_block = [term:"role" .w? "{" .w? .s!.([.w? "," .w?] [ 5 | .r?([comment .w?]) term:"arg"]) .w? "}"] 6 | 18 amb = { 7 | ["no" .w? "amb":!"amb"] 8 | ["amb":"amb"] 9 | } 10 | 17 rule_term = { 11 | uniq:"uniq" 12 | role_of:"role_of" 13 | "amb":"amb" 14 | has:"has" 15 | eq:"eq" 16 | neq:"neq" 17 | rel:"rel" 18 | } 19 | 16 rule = [rule_term:"res" .w? ":-" .w? .s!([.w? "," .w?] rule_term:"arg") .w? "."] 20 | 15 inner = ["." term:"arg"] 21 | 14 app = [.._seps!:"f" "(" .w? .s?([.w? "," .w?] { 22 | [".." .._seps!:"tail_sym"] 23 | "..":"tail" 24 | term:"arg" 25 | }) .w? ")"] 26 | 13 amb_role = ["amb_role" .w? "(" .w? 27 | term:"a" .w? "," .w? term:"b1" .w? "," .w? term:"b2" .w? ")"] 28 | 12 comment = ["//" ..."\n"?] 29 | 11 amb_rel = ["amb_rel" .w? "(" .w? 30 | term:"a" .w? "," .w? term:"b1" .w? "," .w? term:"b2" .w? ")"] 31 | 10 has = [term_no_app:"f" .w? "(" .w? term:"arg" .w? ")" .w? "=>" .w? term:"res"] 32 | 9 ava = [.._seps!:"avatar" "'" .w? "(" .w? term:"core" .w? ")"] 33 | 8 uniq = ["uniq" .w! term:"arg"] 34 | 7 eq = [term_no_app:"f" .w? "(" .w? term:"arg" .w? ")" .w? "=" .w? term:"res"] 35 | 6 rel = ["(" .w? term:"a" .w? "," .w? term:"b" .w? ")"] 36 | 5 sym = {.t?:"str_val" .._seps!:"val"} 37 | 4 role_of = { 38 | ["role_of(" .w? term:"arg" .w? ")" .w? "=" .w? term:"role"] 39 | [term:"arg" .w? ":" .w? term:"role"] 40 | } 41 | 3 term = {inner:"inner" ava:"ava" app:"app" sym:"sym"} 42 | 2 term_no_app = {inner:"inner" ava:"ava" sym:"sym"} 43 | 2 expr = { 44 | rule:"rule" 45 | uniq:"uniq" 46 | role_of:"role_of" 47 | amb_role:"amb_role" 48 | amb_rel:"amb_rel" 49 | amb 50 | has:"has" 51 | eq:"eq" 52 | rel:"rel" 53 | } 54 | 1 data = .l({ 55 | ["import" .w! .t!:"import"] 56 | ["eval" .w! .._seps!:"eval"] 57 | ["no" .w! "eval":"no_eval"] 58 | role_block 59 | [expr:"expr" .w?] 60 | comment 61 | }) 62 | 0 document = [.w? data:"data" .w?] 63 | -------------------------------------------------------------------------------- /examples/avalog_repl.rs: -------------------------------------------------------------------------------- 1 | use std::time::SystemTime; 2 | use std::path::PathBuf; 3 | 4 | use avalog::*; 5 | 6 | fn main() { 7 | println!("=== Avalog 0.7 ==="); 8 | println!("Type `help` for more information."); 9 | 10 | let ref parent = match std::env::current_dir() { 11 | Ok(x) => x, 12 | Err(_) => { 13 | eprintln!("Could not get working directory"); 14 | return; 15 | } 16 | }; 17 | let mut facts = vec![]; 18 | let mut settings = ProveSettings { 19 | hide_facts: false, 20 | hide_rules: false, 21 | max_size: Some(600), 22 | reduce: true, 23 | last_import: None, 24 | graph_file: None, 25 | graph_layout: None, 26 | }; 27 | loop { 28 | use std::io::{self, Write}; 29 | 30 | print!("> "); 31 | let mut input = String::new(); 32 | io::stdout().flush().unwrap(); 33 | match io::stdin().read_line(&mut input) { 34 | Ok(_) => {} 35 | Err(_) => { 36 | println!("ERROR: Could not read input"); 37 | continue; 38 | } 39 | }; 40 | 41 | match input.trim() { 42 | "bye" => break, 43 | "clear" => {facts.clear(); continue} 44 | "hide facts" => { 45 | settings.hide_facts = true; 46 | continue 47 | } 48 | "hide rules" => { 49 | settings.hide_rules = true; 50 | continue 51 | } 52 | "hide all" => { 53 | settings.hide_facts = true; 54 | settings.hide_rules = true; 55 | continue 56 | } 57 | "show facts" => { 58 | settings.hide_facts = false; 59 | continue 60 | } 61 | "show rules" => { 62 | settings.hide_rules = false; 63 | continue 64 | } 65 | "show all" => { 66 | settings.hide_facts = false; 67 | settings.hide_rules = false; 68 | continue 69 | } 70 | "no maxsize" => { 71 | settings.max_size = None; 72 | continue 73 | } 74 | "reload" => { 75 | if let Some(v) = settings.last_import.as_ref() { 76 | input = v.into(); 77 | } else { 78 | println!("ERROR: No last import"); 79 | continue; 80 | } 81 | } 82 | "graph" => { 83 | export_graph(&facts, &settings, parent); 84 | continue; 85 | } 86 | "no graphf" => { 87 | settings.graph_file = None; 88 | continue; 89 | } 90 | "help" => {print_help(); continue} 91 | "help hide" => {print_help_hide(); continue} 92 | "help pairs" => {print_help_pairs(); continue} 93 | "help roles" => {print_help_roles(); continue} 94 | "help avatars" => {print_help_avatars(); continue} 95 | "help rules" => {print_help_rules(); continue} 96 | "help application" => {print_help_application(); continue} 97 | "help eval" => {print_help_eval(); continue} 98 | "help equality" => {print_help_equality(); continue} 99 | "help inequality" => {print_help_inequality(); continue} 100 | "help list" => {print_help_list(); continue} 101 | "help graph" => {print_help_graph(); continue} 102 | x => { 103 | if x.starts_with("?") { 104 | match parse_str(x[1..].trim(), &parent) { 105 | Ok(pat) => { 106 | search_pat(&pat, &facts, &settings); 107 | continue; 108 | } 109 | Err(err) => { 110 | println!("ERROR:\n{}", err); 111 | continue; 112 | } 113 | } 114 | } else if x.starts_with("prove ") { 115 | match parse_str(x[6..].trim(), &parent) { 116 | Ok(goals) => { 117 | settings.reduce = true; 118 | prove(&goals, &facts, &settings); 119 | continue; 120 | } 121 | Err(err) => { 122 | println!("ERROR:\n{}", err); 123 | continue; 124 | } 125 | } 126 | } else if x.starts_with("provenr ") { 127 | match parse_str(x[8..].trim(), &parent) { 128 | Ok(goals) => { 129 | settings.reduce = false; 130 | prove(&goals, &facts, &settings); 131 | continue; 132 | } 133 | Err(err) => { 134 | println!("ERROR:\n{}", err); 135 | continue; 136 | } 137 | } 138 | } else if x.starts_with("echo ") { 139 | let res: ParseResult = parse_str(x[5..].trim(), parent); 140 | match res { 141 | Ok(facts) => { 142 | println!("{:#?}", facts); 143 | continue; 144 | } 145 | Err(err) => { 146 | println!("ERROR:\n{}", err); 147 | continue; 148 | } 149 | } 150 | } else if x.starts_with("maxsize ") { 151 | match x[8..].trim().parse::() { 152 | Ok(n) => settings.max_size = Some(n), 153 | Err(_) => eprintln!("ERROR: Could not parse number"), 154 | }; 155 | continue; 156 | } else if x.starts_with("import ") { 157 | settings.last_import = Some(input.clone()); 158 | // Don't continue since import is intrinsic. 159 | } else if x.starts_with("graphf ") { 160 | // Set GraphViz file name. 161 | if let Some(txt) = json_str(x[7..].trim()) { 162 | settings.graph_file = Some(parent.join(&txt)); 163 | } 164 | continue; 165 | } else if x.starts_with("graphl ") { 166 | if let Some(txt) = json_str(x[7..].trim()) { 167 | settings.graph_layout = Some(txt); 168 | } 169 | continue; 170 | } 171 | } 172 | } 173 | 174 | match parse_str(&input, parent) { 175 | Ok(new_facts) => { 176 | facts.extend(new_facts); 177 | facts.sort(); 178 | facts.dedup(); 179 | } 180 | Err(err) => { 181 | println!("ERROR:\n{}", err); 182 | continue; 183 | } 184 | }; 185 | 186 | } 187 | } 188 | 189 | // Parses a JSON string. 190 | fn json_str(txt: &str) -> Option { 191 | use read_token::ReadToken; 192 | let r = ReadToken::new(txt, 0); 193 | if let Some(range) = r.string() { 194 | if let Ok(txt) = r.parse_string(range.length) { 195 | Some(txt) 196 | } else { 197 | println!("ERROR:\nCould not parse string"); 198 | None 199 | } 200 | } else { 201 | println!("ERROR:\nExpected string"); 202 | None 203 | } 204 | } 205 | 206 | pub struct ProveSettings { 207 | pub hide_facts: bool, 208 | pub hide_rules: bool, 209 | pub max_size: Option, 210 | pub reduce: bool, 211 | pub last_import: Option, 212 | pub graph_file: Option, 213 | pub graph_layout: Option, 214 | } 215 | 216 | fn conclusion( 217 | status: Result<(), Error>, 218 | start_time: SystemTime, 219 | end_time: SystemTime, 220 | settings: &ProveSettings, 221 | ) { 222 | println!(""); 223 | if status.is_ok() { 224 | println!("OK"); 225 | } else { 226 | println!("ERROR"); 227 | if let Some(m) = settings.max_size { 228 | if let Err(Error::MaxSize) = status { 229 | println!("Maximum limit reached."); 230 | println!("Use `maxsize ` or `no maxsize` to set limit."); 231 | println!("Current maximum number of facts and rules: {}", m); 232 | } 233 | } 234 | } 235 | match end_time.duration_since(start_time) { 236 | Ok(d) => println!("Proof search took {} milliseconds", d.as_millis()), 237 | Err(_) => {} 238 | } 239 | } 240 | 241 | fn export_graph(facts: &[Expr], settings: &ProveSettings, parent: &PathBuf) { 242 | use std::fmt::Write; 243 | use std::collections::{HashMap, HashSet}; 244 | use std::fs::File; 245 | 246 | let start_time = SystemTime::now(); 247 | let (res, status) = search_with_accelerator( 248 | &facts, 249 | |e| if let Expr::Rel(_, _) = e {Some(e.clone())} 250 | else if let Expr::RoleOf(_, _) = e {Some(e.clone())} 251 | else {None}, 252 | settings.max_size, 253 | &[], 254 | &[], 255 | infer, 256 | &mut Accelerator::new() 257 | ); 258 | let end_time = SystemTime::now(); 259 | let n: usize = res.len(); 260 | let mut nodes: HashSet = HashSet::new(); 261 | let mut roles: HashMap = HashMap::new(); 262 | for e in &res { 263 | if let Expr::Rel(a, b) = e { 264 | if !nodes.contains(a) {nodes.insert((**a).clone());} 265 | if !nodes.contains(b) {nodes.insert((**b).clone());} 266 | } else if let Expr::RoleOf(a, b) = e { 267 | if !roles.contains_key(a) { 268 | roles.insert((**a).clone(), (**b).clone()); 269 | } 270 | } 271 | } 272 | conclusion(status, start_time, end_time, settings); 273 | println!("{} results found", n); 274 | 275 | let file = if let Some(file) = &settings.graph_file { 276 | parent.join(file) 277 | } else if let Some(last_import) = &settings.last_import { 278 | if let Some(txt) = json_str(last_import[7..].trim()) { 279 | parent.join(txt + ".dot") 280 | } else { 281 | return 282 | } 283 | } else { 284 | parent.join("tmp.dot") 285 | }; 286 | println!("Exporting to `{}`", file.clone().into_os_string().into_string().unwrap()); 287 | 288 | let layout: &str = settings.graph_layout.as_ref().map(|s| &**s).unwrap_or("dot"); 289 | let node_color = "#ffffffa0"; 290 | let edge_color = "black"; 291 | let mut s = String::new(); 292 | writeln!(&mut s, "digraph G {{").unwrap(); 293 | writeln!(&mut s, " layout={}; edge[penwidth=1,color=\"{}\"]", layout, edge_color).unwrap(); 294 | writeln!(&mut s, " node[regular=false,style=filled,fillcolor=\"{}\"]", node_color).unwrap(); 295 | for e in &res { 296 | if let Expr::Rel(a, b) = e { 297 | if roles.contains_key(b) { 298 | writeln!(&mut s, " \"{}\" -> \"{}\"[label=\"{}\"];", a, b, 299 | roles.get(b).unwrap()).unwrap(); 300 | } else { 301 | writeln!(&mut s, " \"{}\" -> \"{}\";", a, b).unwrap(); 302 | } 303 | } 304 | } 305 | writeln!(&mut s, "}}").unwrap(); 306 | 307 | match File::create(file) { 308 | Ok(mut f) => { 309 | use std::io::Write; 310 | if write!(&mut f, "{}", s).is_err() { 311 | println!("ERROR:\nWhen exporting graph to file"); 312 | } 313 | } 314 | Err(_) => { 315 | println!("ERROR:\nCould not create file"); 316 | return; 317 | } 318 | }; 319 | } 320 | 321 | fn search_pat(pat: &[Expr], facts: &[Expr], settings: &ProveSettings) { 322 | if pat.len() == 0 {return}; 323 | 324 | let pat = &pat[0]; 325 | let start_time = SystemTime::now(); 326 | let (res, status) = search_with_accelerator( 327 | &facts, 328 | |e| { 329 | let mut vs = vec![]; 330 | let mut tail = vec![]; 331 | if bind(pat, e, &mut vs, &mut tail) { 332 | Some(e.clone()) 333 | } else { 334 | None 335 | } 336 | }, 337 | settings.max_size, 338 | &[], 339 | &[], 340 | infer, 341 | &mut Accelerator::new() 342 | ); 343 | let end_time = SystemTime::now(); 344 | let n: usize = res.len(); 345 | for r in res { 346 | println!("{}", r); 347 | } 348 | conclusion(status, start_time, end_time, settings); 349 | println!("{} results found", n); 350 | } 351 | 352 | fn prove(goals: &[Expr], facts: &[Expr], settings: &ProveSettings) { 353 | let start_time = SystemTime::now(); 354 | let (res, status) = if settings.reduce { 355 | solve_and_reduce_with_accelerator( 356 | &facts, 357 | &goals, 358 | settings.max_size, 359 | &[], 360 | &[], 361 | infer, 362 | Accelerator::constructor(), 363 | ) 364 | } else { 365 | solve_with_accelerator( 366 | &facts, 367 | &goals, 368 | settings.max_size, 369 | &[], 370 | &[], 371 | infer, 372 | &mut Accelerator::new(), 373 | ) 374 | }; 375 | let end_time = SystemTime::now(); 376 | let mut count_hidden_facts = 0; 377 | let mut count_hidden_rules = 0; 378 | let mut in_start = true; 379 | for r in &res { 380 | if in_start { 381 | let mut found = false; 382 | for q in facts { 383 | if q == r { 384 | found = true; 385 | break; 386 | } 387 | } 388 | if !found { 389 | in_start = false; 390 | if count_hidden_facts > 0 { 391 | println!("<---oo-o--< {} hidden facts >--o-oo--->", count_hidden_facts); 392 | } 393 | if count_hidden_rules > 0 { 394 | println!("<---oo-o--< {} hidden rules >--o-oo--->", count_hidden_rules); 395 | } 396 | print!("----------------------------------------"); 397 | println!("----------------------------------------"); 398 | } 399 | } 400 | let rule = if let Expr::Rule(_, _) = r {true} else {false}; 401 | let hide = in_start && (!rule && settings.hide_facts || 402 | rule && settings.hide_rules); 403 | if !hide { 404 | println!("{}", r); 405 | } else { 406 | if !rule {count_hidden_facts += 1}; 407 | if rule {count_hidden_rules += 1}; 408 | } 409 | } 410 | 411 | conclusion(status, start_time, end_time, settings); 412 | } 413 | 414 | fn print_help() {print!("{}", include_str!("../assets/help.txt"))} 415 | fn print_help_hide() {print!("{}", include_str!("../assets/help-hide.txt"))} 416 | fn print_help_pairs() {print!("{}", include_str!("../assets/help-pairs.txt"))} 417 | fn print_help_roles() {print!("{}", include_str!("../assets/help-roles.txt"))} 418 | fn print_help_avatars() {print!("{}", include_str!("../assets/help-avatars.txt"))} 419 | fn print_help_rules() {print!("{}", include_str!("../assets/help-rules.txt"))} 420 | fn print_help_application() {print!("{}", include_str!("../assets/help-application.txt"))} 421 | fn print_help_eval() {print!("{}", include_str!("../assets/help-eval.txt"))} 422 | fn print_help_equality() {print!("{}", include_str!("../assets/help-equality.txt"))} 423 | fn print_help_inequality() {print!("{}", include_str!("../assets/help-inequality.txt"))} 424 | fn print_help_list() {print!("{}", include_str!("../assets/help-list.txt"))} 425 | fn print_help_graph() {println!("{}", include_str!("../assets/help-graph.txt"))} 426 | -------------------------------------------------------------------------------- /source/amb.txt: -------------------------------------------------------------------------------- 1 | amb 2 | -------------------------------------------------------------------------------- /source/amb_fail.txt: -------------------------------------------------------------------------------- 1 | amb :- (X, X). 2 | (a, b) 3 | -------------------------------------------------------------------------------- /source/amb_success-2.txt: -------------------------------------------------------------------------------- 1 | a(b) => q'(c) 2 | amb :- A(B) => C. 3 | -------------------------------------------------------------------------------- /source/amb_success.txt: -------------------------------------------------------------------------------- 1 | // This should prove ambiguity because 2 | // `person : table` and `address : table` 3 | // which gives `table(address_book) = person` 4 | // and `table(address_book) = address`. 5 | 6 | // Type hierarchy. 7 | Y : U :- X : T, (X, Y), (T, U). 8 | 9 | (schema, table) 10 | 11 | address_book : schema 12 | (address_book, person) 13 | (address_book, address) 14 | -------------------------------------------------------------------------------- /source/app_empty.txt: -------------------------------------------------------------------------------- 1 | (foo(), test) 2 | 3 | -------------------------------------------------------------------------------- /source/app_match.txt: -------------------------------------------------------------------------------- 1 | (a, fun(b, c)) 2 | 3 | (A, B) :- (A, C), C != fun(D, E). 4 | -------------------------------------------------------------------------------- /source/associativity-0.txt: -------------------------------------------------------------------------------- 1 | eval value 2 | 3 | (add(3, 3), 6) 4 | -------------------------------------------------------------------------------- /source/associativity.txt: -------------------------------------------------------------------------------- 1 | eval value 2 | 3 | (F(F(X, Y), Z), W) :- (F(X, F(Y, Z)), W), assoc'(F) : op. 4 | (F(X, F(Y, Z)), W) :- (F(F(X, Y), Z), W), assoc'(F) : op. 5 | 6 | assoc'(add) : op 7 | 8 | 1 : value 9 | 2 : value 10 | 3 : value 11 | 12 | (add(1, add(add(1, 1), 3)), 6) 13 | 14 | (add(1, 1), 2) 15 | (add(1, 2), 3) 16 | -------------------------------------------------------------------------------- /source/avalog_state.txt: -------------------------------------------------------------------------------- 1 | // 2 | // This is an attempt model Avalog's inference rules 3 | // as a graph inside Avalog. 4 | // 5 | (start, fact_iter) 6 | (fact_iter, detect_ambiguous_role) 7 | (detect_ambiguous_role, emit_ambiguous_role) 8 | (detect_ambiguous_role, no_ambiguous_role_detected) 9 | emit_ambiguous_role : yes 10 | no_ambiguous_role_detected : no 11 | (emit_ambiguous_role, ambiguous_role) 12 | (ambiguous_role, ambiguous_role'(ambiguity)) 13 | (ambiguous_role'(ambiguity), detect_convert_unique_has_into_eq) 14 | (no_ambiguous_role_detected, detect_convert_unique_has_into_eq) 15 | (detect_convert_unique_has_into_eq, has_one_avatar) 16 | has_one_avatar : yes 17 | (detect_convert_unique_has_into_eq, else) 18 | else : no 19 | -------------------------------------------------------------------------------- /source/bool_alg-0.txt: -------------------------------------------------------------------------------- 1 | no amb 2 | 3 | eval value 4 | 5 | (not(not(true)), true) 6 | 7 | bool : type 8 | -------------------------------------------------------------------------------- /source/bool_alg.txt: -------------------------------------------------------------------------------- 1 | univ : type 2 | 3 | // type -> type 4 | type2 : type 5 | // type -> type -> type 6 | type3 : type 7 | 8 | bool : type 9 | // bool -> bool 10 | bool2 : type 11 | // bool -> bool -> bool 12 | bool3 : type 13 | 14 | (type, univ) 15 | (type2, type) 16 | (bool2, type2) 17 | (bool3, type3) 18 | 19 | false : value 20 | true : value 21 | 22 | (false, bool) 23 | (true, bool) 24 | 25 | (not, bool2) 26 | (not(false), true) 27 | (not(true), false) 28 | 29 | (id, bool2) 30 | (id(false), false) 31 | (id(true), true) 32 | 33 | (and, bool3) 34 | (and(false, false), false) 35 | (and(false, true), false) 36 | (and(true, false), false) 37 | (and(true, true), true) 38 | 39 | (or, bool3) 40 | (or(false, false), false) 41 | (or(false, true), true) 42 | (or(true, false), true) 43 | (or(true, true), true) 44 | 45 | eval value 46 | 47 | (not(not(not(false))), true) 48 | 49 | (and(true, not(true)), false) 50 | (and(not(false), not(false)), true) 51 | (and(or(false, true), true), true) 52 | -------------------------------------------------------------------------------- /source/bool_alg2.txt: -------------------------------------------------------------------------------- 1 | true : bool 2 | false : bool 3 | 4 | amb :- (false, true). 5 | amb :- (true, false). 6 | 7 | (not(false), true) 8 | (not(true), false) 9 | (X, Y) :- (not(X), not(Y)), X : bool, Y : bool. 10 | 11 | (and(true, true), true) 12 | (and(X, false), false) :- X : bool. 13 | (and(false, X), false) :- X : bool. 14 | 15 | (or(false, false), false) 16 | (or(X, true), true) :- X : bool. 17 | (or(true, X), true) :- X : bool. 18 | -------------------------------------------------------------------------------- /source/bool_alg3.txt: -------------------------------------------------------------------------------- 1 | import "bool_alg2.txt" 2 | 3 | eval bool 4 | (not(and(not(X), not(Y))), or(X, Y)) :- X : bool, Y : bool. 5 | (not(or(not(X), not(Y))), and(X, Y)) :- X : bool, Y : bool. 6 | 7 | no eval 8 | -------------------------------------------------------------------------------- /source/borrowchk.txt: -------------------------------------------------------------------------------- 1 | // # Borrow Checker 2 | // 3 | // To use this file, save it in your working folder and type: 4 | // `import "borrowchk.txt"` 5 | // 6 | // This formalises a borrow checker with allocation/deallocation on tree structures 7 | // with move and borrow semantics. 8 | // 9 | // Technique is based on paper "Restricted Dual Composition": 10 | // https://github.com/advancedresearch/path_semantics/blob/master/papers-wip2/restricted-dual-composition.pdf 11 | // 12 | // Restricted Dual Composition uses sink/sources semantics over dual objects to make composition more efficient: 13 | // 14 | // - `(c'(X), X)` source: the object `X` needs to be deallocated (generated by allocation) 15 | // - `(X, c'(X))` sink: the object `X` is deallocated 16 | // 17 | // To add types that require cleanup, use e.g. `(c'(X), X) :- X : P, P : string.` 18 | // 19 | // Here is an example of a person `a` named "Carl" that is 18 years old: 20 | // ``` 21 | // name : string 22 | // age : nat 23 | // 24 | // carl : name 25 | // a : person 26 | // 18 : age 27 | // 28 | // (a, carl) 29 | // (a, 18) 30 | // ``` 31 | // 32 | // To move `a`, use e.g. `(a, mov'(b))`. 33 | // You can also move a moved object, e.g. `(b, mov'(c))`. 34 | // Moved objects can be destroyed, e.g. `(c, c'(c))`. 35 | // 36 | // To borrow `a`, use e.g. `(a, ref'(b))`. 37 | // To borrow a mutable `a`, use e.g. `(a, ref_mut'(b))`. 38 | // 39 | // This formalisation only works in a single context, 40 | // and does not check for aliased moves (due to limitations of monotonic solvers). 41 | // To check for aliased moves, a linear solver might be used on the generated output. 42 | // 43 | // Conditional branches, e.g. if-else expressions, must match sink/sources. 44 | // 45 | // Loops must not introduce new sink/sources that are not covered by isomorphisms. 46 | // This means every `(c'(X), X)` must be covered by an `(X, c'(X))` and vice versa. 47 | 48 | // Needing only one side of restricted dual composition. 49 | (X, Z) :- (X, Y), (Y, Z), (c'(X), X), X != c'(T). 50 | 51 | // Lift `mov`, `ref` and `ref_mut` to preserve composition. 52 | (N'(Y), M'(Z)) :- (Y, M'(Z)), (X, N'(Y)), Y != A'(B), M != c, N != c. 53 | 54 | // Seek to clean up everything that has some 55 | // property to be cleaned up. 56 | (c'(X), X) :- X : T, (c'(Y), Y), U(X) = Y. 57 | 58 | // If something has been cleaned up and has some 59 | // property to be cleaned up, then the property is cleaned up. 60 | (Y, c'(Y)) :- X : T, (X, c'(X)), (c'(Y), Y), U(X) = Y. 61 | 62 | // When cleaning up a moved object, call its destructor. 63 | (mov'(Y), c'(X)) :- (Y, c'(Y)), (X, mov'(Y)). 64 | 65 | // Do not allow destroying objects that are borrowed. 66 | amb :- (X, c'(X)), (X, ref'(Y)). 67 | amb :- (X, c'(X)), (X, ref_mut'(Y)). 68 | 69 | // Borrow aliasing rules. 70 | amb :- (X, ref_mut'(Y)), (X, ref'(Z)), Y != Z. 71 | amb :- (X, ref_mut'(Y)), (X, ref_mut'(Z)), Y != Z. 72 | -------------------------------------------------------------------------------- /source/capital-0.txt: -------------------------------------------------------------------------------- 1 | capital(norway) = oslo 2 | capital(sweden) = stockholm 3 | 4 | country(oslo) = norway 5 | country(stockholm) = sweden 6 | -------------------------------------------------------------------------------- /source/capital.txt: -------------------------------------------------------------------------------- 1 | uniq capital 2 | 3 | oslo : capital 4 | stockholm : capital 5 | 6 | norway : country 7 | sweden : country 8 | 9 | (norway, oslo) 10 | (sweden, stockholm) 11 | 12 | (X, Y) :- (Y, X), Y : country, X : capital. 13 | -------------------------------------------------------------------------------- /source/category-0.txt: -------------------------------------------------------------------------------- 1 | no amb 2 | -------------------------------------------------------------------------------- /source/category.txt: -------------------------------------------------------------------------------- 1 | // 2 | // Formalization of Category Theory 3 | // 4 | 5 | // Composition. 6 | (X, F'(G'(Z))) :- 7 | (X, F'(Y)), F != id, Y != F1'(X1), 8 | (Y, G'(Z)), G != id, Z != F2'(X2), 9 | F'(Y) : T1, G'(Z) : T2. 10 | 11 | // Identity morphism. 12 | (X, id'(X)) :- (X, obj). 13 | 14 | // Make relations between objects, 15 | // that are not identical, ambiguous. 16 | amb :- (X, Y), X != Y, (X, obj), (Y, obj). 17 | 18 | // Role identity. 19 | F'(X) : F :- (Y, F'(X)), X != G'(Z). 20 | 21 | // Role uniqueness. 22 | uniq F :- (X, F'(Y)). 23 | 24 | // Create objects from relations. 25 | (A, obj) :- (A, F'(B)), B != G'(C). 26 | (B, obj) :- (A, F'(B)), B != G'(C). 27 | 28 | //////////////////////////////////////////////////////////////////// 29 | // Examples (remove this section when reusing the definition) 30 | 31 | (a, f'(b)) 32 | (b, g'(a)) 33 | 34 | // Prove an isomorphism from `a` to `b`. 35 | (.g(.f(a)), a) 36 | (.f(.g(b)), b) 37 | 38 | // Prove identity map from every object to itself. 39 | (.id(X), X) :- (X, obj). 40 | 41 | (a2, f'(b2)) 42 | (b2, g'(c2)) 43 | (a2, h'(c2)) 44 | 45 | // Prove composition. 46 | (.g(.f(a2)), .h(a2)) 47 | -------------------------------------------------------------------------------- /source/chu_space.txt: -------------------------------------------------------------------------------- 1 | // 2 | // A Chu space based on introduction example for Cartesian frames: 3 | // https://www.lesswrong.com/posts/BSpdshJWGAW6TuNzZ/introduction-to-cartesian-frames 4 | // 5 | 6 | // Do not allow multiple `not`s. 7 | uniq not 8 | 9 | // Generate negative roles (this makes them right-exclusive). 10 | not'(X) : Y :- X : Y, X != not'(Z). 11 | 12 | // Create negative agents. 13 | (not'(X), agent) :- (X, agent), X != not'(Y). 14 | 15 | // Create negative environments. 16 | (not'(X), env) :- (X, env), X != not'(Y). 17 | 18 | // Commute agent and environment to make roles 19 | // both right-exclusive and left-exclusive. 20 | (Y, X) :- (X, Y), (X, agent), (Y, env). 21 | 22 | // Make agent vs agent relations ambiguous. 23 | amb :- (X, Y), (X, agent), (Y, agent). 24 | 25 | // Make environment vs environment relations ambiguous. 26 | amb :- (X, Y), (X, env), (Y, env). 27 | 28 | // Generate all simple products of agents and environments. 29 | (X, Y) :- (X, agent), (Y, env), (X, prod), (Y, prod). 30 | 31 | // Generate the negative agent for destructive environments. 32 | (not'(X), Y) :- (X, agent), (Y, env), (Y, destr), X != not'(Z). 33 | 34 | u : umbrella 35 | n : no_umbrella 36 | 37 | r : rain 38 | s : sun 39 | m : meteorite 40 | 41 | (u, agent) 42 | (n, agent) 43 | 44 | (u, prod) 45 | (n, prod) 46 | 47 | (r, env) 48 | (s, env) 49 | (m, env) 50 | 51 | (r, prod) 52 | (s, prod) 53 | (m, destr) 54 | -------------------------------------------------------------------------------- /source/closure_calculus.txt: -------------------------------------------------------------------------------- 1 | // Closure Calculus 2 | // See paper: https://dl.acm.org/doi/pdf/10.1145/3294032.3294085 3 | // 4 | // To check up to 3, you type (after putting this file in your working folder): 5 | // 6 | // > import "closure_calculus.txt" 7 | // > see'(r(r(r(j)))) : goal 8 | // > maxsize 4000 9 | // > provenr no amb 10 | 11 | j : term 12 | 13 | r(X) : term :- see'(r(X)) : goal, X : term. 14 | see'(X) : goal :- see'(r(X)) : goal. 15 | 16 | f(R, T) : term :- see'(f(R, T)) : goal, R : term, T : term. 17 | see'(R) : goal :- see'(f(R, T)) : goal. 18 | see'(T) : goal :- see'(f(R, T)) : goal. 19 | 20 | lam(S, T) : term :- see'(lam(S, T)) : goal, S : term, T : term. 21 | see'(S) : goal :- see'(lam(S, T)) : goal. 22 | see'(T) : goal :- see'(lam(S, T)) : goal. 23 | 24 | i : term 25 | 26 | p(U, S) : term :- see'(p(U, S)) : goal, U : term, S : term. 27 | see'(U) : goal :- see'(p(U, S)) : goal. 28 | see'(S) : goal :- see'(p(U, S)) : goal. 29 | 30 | T(U) : term :- see'(T(U)) : goal, T : term, U : term. 31 | see'(T) : goal :- see'(T(U)) : goal. 32 | see'(U) : goal :- see'(T(U)) : goal. 33 | 34 | // J(t) => J’(t) 35 | f(j, T) : term :- see'(f(j, T)) : goal, T : term. 36 | (T, f(j, T)) :- T : term. 37 | 38 | // R(t)(u) => R(t)’(u) 39 | f(r(T), U) : term :- see'(f(r(T), U)) : goal, T : term, U : term. 40 | (app(r(T), U), f(r(T), U)) :- T : term, U : term. 41 | 42 | // r’(t)(u) => r’(t)’(u) 43 | f(f(R, T), U) : term :- 44 | see'(f(f(R, T), U)) : goal, 45 | R : term, T : term, U : term. 46 | (app(f(R, T), U), f(f(R, T), U)) :- R : term, T : term, U : term. 47 | 48 | // (\(J) ~ s = t)(u) => (u, s)(t) 49 | app(p(U, S), T) : term :- 50 | see'(app(p(U, S), T)) : goal, 51 | U : term, S : term, T : term. 52 | (app(lam(S, T), U), app(p(U, S), T)) :- U : term, S : term, T : term. 53 | 54 | // id(t) => t 55 | (i(T), T) :- T : term. 56 | 57 | // (u, s)(J) => u 58 | (app(p(U, S), j), U) :- U : term, S : term. 59 | 60 | // (u, s)(R(t)) => s(t) 61 | app(S, T) : term :- see'(app(S, T)) : goal, S : term, T : term. 62 | (app(p(U, S), r(T)), app(S, T)) :- U : term, S : term, T : term. 63 | 64 | // (u, s)(r’(t)) => (u, s)(r)((u, s)(t)) 65 | app(app(p(U, S), R), app(p(U, S), T)) : term :- 66 | see'(app(app(p(U, S), R), app(p(U, S), T))) : goal, 67 | U : term, S : term, T : term, R : term. 68 | (app(p(U, S), f(R, T)), app(app(p(U, S), R), app(p(U, S), T))) :- 69 | U : term, S : term, T : term, R : term. 70 | 71 | // (u, s)(\(J) ~ r = t) => \(J) ~ (u, s)(r) = t 72 | lam(app(p(U, S), R), T) : term :- 73 | see'(lam(app(p(U, S), R), T)) : goal, U : term, S : term, R : term, T : term. 74 | (app(p(U, S), lam(R, T)), lam(app(p(U, S), R), T)) :- 75 | U : term, S : term, R : term, T : term. 76 | 77 | // (u, s)(id) => id 78 | (app(p(U, S), i), i) :- U : term, S : term. 79 | 80 | // (u, s)((r, t)) => ((u, s)(r), (u, s)(t)) 81 | p(app(p(U, S), R), app(p(U, S), T)) : term :- 82 | see'(p(app(p(U, S), R), app(p(U, S), T))) : goal, 83 | U : term, S : term, R : term, T : term. 84 | (app(p(U, S), p(R, T)), p(app(p(U, S), R), app(p(U, S), T))) :- 85 | U : term, S : term, R : term, T : term. 86 | -------------------------------------------------------------------------------- /source/commutativity-0.txt: -------------------------------------------------------------------------------- 1 | (add(3, 2), 5) 2 | -------------------------------------------------------------------------------- /source/commutativity.txt: -------------------------------------------------------------------------------- 1 | eval value 2 | 3 | (F(Y, X), W) :- (F(X, Y), W), commut'(F) : op. 4 | 5 | commut'(add) : op 6 | 7 | 1 : value 8 | 2 : value 9 | 3 : value 10 | 11 | (add(2, 3), 5) 12 | -------------------------------------------------------------------------------- /source/convert_eq_into_has-0.txt: -------------------------------------------------------------------------------- 1 | p(a) => b 2 | -------------------------------------------------------------------------------- /source/convert_eq_into_has.txt: -------------------------------------------------------------------------------- 1 | p(a) = b 2 | -------------------------------------------------------------------------------- /source/convert_has_into_eq-0.txt: -------------------------------------------------------------------------------- 1 | p(a) = b 2 | -------------------------------------------------------------------------------- /source/convert_has_into_eq.txt: -------------------------------------------------------------------------------- 1 | p(a) => b 2 | -------------------------------------------------------------------------------- /source/convert_has_into_eq2-0.txt: -------------------------------------------------------------------------------- 1 | // This should fail. 2 | p(a) = q'(b) 3 | -------------------------------------------------------------------------------- /source/convert_has_into_eq2.txt: -------------------------------------------------------------------------------- 1 | p(a) => q'(b) 2 | -------------------------------------------------------------------------------- /source/convert_has_into_eq3-0.txt: -------------------------------------------------------------------------------- 1 | p(a) = q'(b) 2 | -------------------------------------------------------------------------------- /source/convert_has_into_eq3.txt: -------------------------------------------------------------------------------- 1 | uniq q 2 | p(a) => q'(b) 3 | -------------------------------------------------------------------------------- /source/convert_has_into_eq4-0.txt: -------------------------------------------------------------------------------- 1 | p(a) = b 2 | -------------------------------------------------------------------------------- /source/convert_has_into_eq4.txt: -------------------------------------------------------------------------------- 1 | p(a) => .q'(b) 2 | -------------------------------------------------------------------------------- /source/convert_has_into_eq5-0.txt: -------------------------------------------------------------------------------- 1 | // This should succeed, because `.q1` was used. 2 | p(a) = q2'(b) 3 | -------------------------------------------------------------------------------- /source/convert_has_into_eq5.txt: -------------------------------------------------------------------------------- 1 | p(a) => .q1'(q2'(b)) 2 | -------------------------------------------------------------------------------- /source/convert_has_into_eq6-0.txt: -------------------------------------------------------------------------------- 1 | // This should fail. 2 | p(a) = q2'(b) 3 | -------------------------------------------------------------------------------- /source/convert_has_into_eq6-1.txt: -------------------------------------------------------------------------------- 1 | // This should succeed. 2 | p(a) => q2'(b) 3 | -------------------------------------------------------------------------------- /source/convert_has_into_eq6.txt: -------------------------------------------------------------------------------- 1 | p(a) => q2'(.q1'(.q2'(b))) 2 | -------------------------------------------------------------------------------- /source/copy-0.txt: -------------------------------------------------------------------------------- 1 | (do, copy(a)) 2 | (do, copy(b)) 3 | (do, copy(c)) 4 | -------------------------------------------------------------------------------- /source/copy.txt: -------------------------------------------------------------------------------- 1 | (do, copy(A)) :- (do, copy(A, ..)). 2 | (do, copy(B)) :- (do, copy(A, ..B)). 3 | 4 | (do, copy(a, b, c, d)) 5 | -------------------------------------------------------------------------------- /source/grandparent-0.txt: -------------------------------------------------------------------------------- 1 | grandma(carl) => grandparent'(alice) 2 | -------------------------------------------------------------------------------- /source/grandparent.txt: -------------------------------------------------------------------------------- 1 | uniq parent 2 | parent'(alice) : mom 3 | grandparent'(alice) : grandma 4 | parent'(bob) : dad 5 | (bob, parent'(alice)) 6 | (carl, parent'(bob)) 7 | (X, grandparent'(Z)) :- (X, parent'(Y)), (Y, parent'(Z)). 8 | -------------------------------------------------------------------------------- /source/inner-0.txt: -------------------------------------------------------------------------------- 1 | (a, b) 2 | -------------------------------------------------------------------------------- /source/inner.txt: -------------------------------------------------------------------------------- 1 | role_of(q'(b)) = r 2 | (a, .q'(b)) 3 | -------------------------------------------------------------------------------- /source/inner2-0.txt: -------------------------------------------------------------------------------- 1 | (a, b) 2 | -------------------------------------------------------------------------------- /source/inner2.txt: -------------------------------------------------------------------------------- 1 | role_of(b) = r 2 | (.p'(a), b) 3 | -------------------------------------------------------------------------------- /source/mom_and_dad-0.txt: -------------------------------------------------------------------------------- 1 | mom(bob) = alice 2 | -------------------------------------------------------------------------------- /source/mom_and_dad-1.txt: -------------------------------------------------------------------------------- 1 | dad(bob) = carl 2 | -------------------------------------------------------------------------------- /source/mom_and_dad-2.txt: -------------------------------------------------------------------------------- 1 | mom(bob) = alice 2 | dad(bob) = carl 3 | -------------------------------------------------------------------------------- /source/mom_and_dad.txt: -------------------------------------------------------------------------------- 1 | role_of(alice) = mom 2 | role_of(carl) = dad 3 | (bob, alice) 4 | (bob, carl) 5 | -------------------------------------------------------------------------------- /source/nat.txt: -------------------------------------------------------------------------------- 1 | z : nat 2 | s(X) : nat :- X : nat. 3 | 4 | (add(z, z), z) 5 | (add(X, z), X) :- X : nat. 6 | (add(z, X), X) :- X : nat. 7 | (add(s(X), s(Y)), nat(add(s(s(X)), Y))) :- X : nat, Y : nat. 8 | -------------------------------------------------------------------------------- /source/no_amb.txt: -------------------------------------------------------------------------------- 1 | no amb 2 | -------------------------------------------------------------------------------- /source/parents-0.txt: -------------------------------------------------------------------------------- 1 | mom(bob) = parent'(alice) 2 | -------------------------------------------------------------------------------- /source/parents-1.txt: -------------------------------------------------------------------------------- 1 | mom(bob) => parent'(alice) 2 | -------------------------------------------------------------------------------- /source/parents-2.txt: -------------------------------------------------------------------------------- 1 | mom(bob) = parent'(alice) 2 | dad(bob) = parent'(carl) 3 | -------------------------------------------------------------------------------- /source/parents.txt: -------------------------------------------------------------------------------- 1 | uniq parent 2 | role_of(parent'(alice)) = mom 3 | role_of(parent'(carl)) = dad 4 | (bob, parent'(alice)) 5 | (bob, parent'(carl)) 6 | -------------------------------------------------------------------------------- /source/parents2-0.txt: -------------------------------------------------------------------------------- 1 | amb_rel(bob, parent'(alice), foster_parent'(diana)) 2 | -------------------------------------------------------------------------------- /source/parents2.txt: -------------------------------------------------------------------------------- 1 | uniq parent 2 | role_of(parent'(alice)) = mom 3 | role_of(parent'(carl)) = dad 4 | role_of(foster_parent'(diana)) = mom 5 | (bob, parent'(alice)) 6 | (bob, parent'(carl)) 7 | (bob, foster_parent'(diana)) 8 | -------------------------------------------------------------------------------- /source/parents3-0.txt: -------------------------------------------------------------------------------- 1 | amb_rel(bob, parent'(alice), parent'(diana)) 2 | -------------------------------------------------------------------------------- /source/parents3.txt: -------------------------------------------------------------------------------- 1 | uniq parent 2 | role_of(parent'(alice)) = mom 3 | role_of(parent'(carl)) = dad 4 | role_of(parent'(diana)) = mom 5 | (bob, parent'(alice)) 6 | (bob, parent'(carl)) 7 | (bob, parent'(diana)) 8 | -------------------------------------------------------------------------------- /source/parents4-0.txt: -------------------------------------------------------------------------------- 1 | amb_role(parent'(alice), mom, mother) 2 | -------------------------------------------------------------------------------- /source/parents4.txt: -------------------------------------------------------------------------------- 1 | uniq parent 2 | role_of(parent'(alice)) = mom 3 | role_of(parent'(alice)) = mother 4 | (bob, parent'(alice)) 5 | -------------------------------------------------------------------------------- /source/parents5-0.txt: -------------------------------------------------------------------------------- 1 | (carl, parent'(alice)) 2 | -------------------------------------------------------------------------------- /source/parents5.txt: -------------------------------------------------------------------------------- 1 | uniq parent 2 | role_of(parent'(alice)) = mom 3 | (bob, parent'(alice)) 4 | (carl, mom(bob)) 5 | -------------------------------------------------------------------------------- /source/parents6-0.txt: -------------------------------------------------------------------------------- 1 | (parent'(alice), parent'(carl)) 2 | -------------------------------------------------------------------------------- /source/parents6.txt: -------------------------------------------------------------------------------- 1 | uniq parent 2 | role_of(parent'(alice)) = mom 3 | role_of(parent'(carl)) = dad 4 | (bob, parent'(alice)) 5 | (mom(bob), parent'(carl)) 6 | -------------------------------------------------------------------------------- /source/parents7-0.txt: -------------------------------------------------------------------------------- 1 | (alice, parent'(carl)) 2 | -------------------------------------------------------------------------------- /source/parents7.txt: -------------------------------------------------------------------------------- 1 | uniq parent 2 | role_of(parent'(alice)) = mom 3 | role_of(parent'(carl)) = dad 4 | (bob, parent'(alice)) 5 | (.mom(bob), parent'(carl)) 6 | -------------------------------------------------------------------------------- /source/parents8-0.txt: -------------------------------------------------------------------------------- 1 | (carl, alice) 2 | -------------------------------------------------------------------------------- /source/parents8.txt: -------------------------------------------------------------------------------- 1 | uniq parent 2 | role_of(parent'(alice)) = mom 3 | role_of(parent'(carl)) = dad 4 | (bob, parent'(alice)) 5 | (carl, .mom(bob)) 6 | -------------------------------------------------------------------------------- /source/path_sem.txt: -------------------------------------------------------------------------------- 1 | (C, D) :- (A, B), (B, A), (A, C), (B, D). 2 | -------------------------------------------------------------------------------- /source/psi.txt: -------------------------------------------------------------------------------- 1 | // # Path Semantical Intuitionistic Logic (PSI) 2 | // 3 | // Instructions: 4 | // 5 | // - Add `(tr, a)` to express that `a` is true. 6 | // - Add `(a, fa)` to express that `a` is false. 7 | // - Add `(tr, inf)` to enable non-terminating rules. 8 | 9 | // Contradiction if true implies false. 10 | amb :- (tr, fa). 11 | 12 | // Self-implication. 13 | (X, X) :- (X, Y). 14 | 15 | // Other-self-implication. 16 | (Y, Y) :- (X, Y). 17 | 18 | // Transitivity of implication. 19 | (X, Z) :- (X, Y), (Y, Z), Z != T'(U). 20 | 21 | // Lowering of quality. 22 | (X, Y) :- (X, q'(Y)). 23 | 24 | // Quality (like equality but non-reflexive). 25 | (X, q'(Y)) :- (X, Y), (Y, X), X != Y. 26 | 27 | // Transitivity of quality. 28 | (X, q'(Z)) :- (X, q'(Y)), (Y, q'(Z)). 29 | 30 | // Symmetry of quality. 31 | (Y, q'(X)) :- (X, q'(Y)). 32 | 33 | // ### Intuitionistic logic 34 | 35 | // Modus ponens is covered by transitivity of implication `(tr, X)`. 36 | 37 | // Then-1. 38 | (Y, X) :- (tr, X), (Y, Z). 39 | 40 | // Then-2. 41 | (imply(X, Y), imply(X, Z)) :- (X, imply(Y, Z)). 42 | 43 | // And-1. 44 | (tr, X) :- (tr, and(X, Y)). 45 | 46 | // And-2. 47 | (tr, Y) :- (tr, and(X, Y)). 48 | 49 | // And-3. 50 | (Y, and(X, Y)) :- (tr, inf), (tr, X), (Y, Z). 51 | 52 | // Or-1. 53 | (tr, or(X, Y)) :- (tr, inf), (tr, X), (Y, Z). 54 | 55 | // False. 56 | // This also holds for Y due to transitivity of implication. 57 | (fa, X) :- (X, Y), X != T'(U). 58 | 59 | // ### Other axioms 60 | 61 | // Lift implication to expression. 62 | (tr, imply(X, Y)) :- (tr, inf), (X, Y). 63 | 64 | // Lowering of implication. 65 | (X, Y) :- (tr, imply(X, Y)). 66 | 67 | // Lift pairs of implications to expressions. 68 | (tr, and(imply(X, Y), imply(Z, W))) :- (tr, inf), (X, Y), (Z, W). 69 | 70 | // Double negation lift (constructive). 71 | (imply(X, fa), fa) :- (tr, inf), (tr, X). 72 | 73 | // Core axiom of Path Semantics 74 | (X0, q'(X1)) :- (F0, q'(F1)), (F0, X0), (F1, X1), (F0, gt'(X0)), (F1, gt'(X1)). 75 | 76 | // Other axioms for greater-than relation. 77 | // These are used to support path semantical levels. 78 | (X, gt'(Z)) :- (X, gt'(Y)), (Y, gt'(Z)). 79 | (X, gt'(Y)) :- (X, z), (Y, s'(z)). 80 | (X, gt'(Y)) :- (X, s'(N)), (Y, s'(M)), (N, gt'(M)). 81 | 82 | // Natural numbers. 83 | uniq s 84 | z : nat 85 | s'(X) : nat :- X : nat, (tr, inf). 86 | X : nat :- s'(X) : nat. 87 | (X, gt'(s'(X))) :- s'(X) : nat. 88 | 89 | // Set maximum number of path semantical levels for terminating rules. 90 | // s'(s'(z)) : nat 91 | s'(z) : nat 92 | -------------------------------------------------------------------------------- /source/role_block.txt: -------------------------------------------------------------------------------- 1 | person { 2 | bob, 3 | // carl, 4 | alice, 5 | } 6 | -------------------------------------------------------------------------------- /source/role_lift_app-0.txt: -------------------------------------------------------------------------------- 1 | val(f(val(f(a)))) : p 2 | a : val(g(val(g(p)))) 3 | -------------------------------------------------------------------------------- /source/role_lift_app.txt: -------------------------------------------------------------------------------- 1 | (a, b) 2 | b : val 3 | 4 | eval val 5 | f(f(a)) : p 6 | a : g(g(p)) 7 | -------------------------------------------------------------------------------- /source/role_lift_ava-0.txt: -------------------------------------------------------------------------------- 1 | (c, q'(val(f(a)))) 2 | -------------------------------------------------------------------------------- /source/role_lift_ava.txt: -------------------------------------------------------------------------------- 1 | (a, b) 2 | b : val 3 | 4 | eval val 5 | (c, q'(f(a))) 6 | -------------------------------------------------------------------------------- /source/role_lift_eq-0.txt: -------------------------------------------------------------------------------- 1 | val(f(val(f(a)))) = b 2 | val(a) = val(g(val(g(b)))) 3 | -------------------------------------------------------------------------------- /source/role_lift_eq.txt: -------------------------------------------------------------------------------- 1 | (a, b) 2 | b : val 3 | 4 | eval val 5 | val(f(f(a))) = b 6 | val(a) = g(g(b)) 7 | -------------------------------------------------------------------------------- /source/role_lift_inner-0.txt: -------------------------------------------------------------------------------- 1 | (c, .val(f(a))) 2 | -------------------------------------------------------------------------------- /source/role_lift_inner.txt: -------------------------------------------------------------------------------- 1 | (a, b) 2 | b : val 3 | 4 | eval val 5 | (c, .f(a)) 6 | -------------------------------------------------------------------------------- /source/sibling-0.txt: -------------------------------------------------------------------------------- 1 | no amb 2 | 3 | brother(bob) => sibling'(carl) 4 | brother(carl) => sibling'(bob) 5 | sister(carl) => sibling'(siri) 6 | step_sister(bob) => step_sibling'(siri) 7 | step_brother(siri) => step_sibling'(bob) 8 | -------------------------------------------------------------------------------- /source/sibling.txt: -------------------------------------------------------------------------------- 1 | parent'(alice) : mom 2 | parent'(sigmund) : dad 3 | sibling'(bob) : brother 4 | sibling'(carl) : brother 5 | sibling'(siri) : sister 6 | step_sibling'(bob) : step_brother 7 | step_sibling'(carl) : step_brother 8 | step_sibling'(siri) : step_sister 9 | 10 | (bob, parent'(alice)) 11 | (carl, parent'(alice)) 12 | (carl, parent'(sigmund)) 13 | (siri, parent'(sigmund)) 14 | 15 | (X, sibling'(Y)) :- (X, parent'(Z)), (Y, parent'(Z)). 16 | (X, step_sibling'(Y)) :- (X, sibling'(Z)), (Z, sibling'(Y)). 17 | -------------------------------------------------------------------------------- /source/squares-0.txt: -------------------------------------------------------------------------------- 1 | // 2 | // a ----> c 3 | // | \ | 4 | // | \ | 5 | // v J v 6 | // b ----> d 7 | // 8 | (a, d) 9 | -------------------------------------------------------------------------------- /source/squares.txt: -------------------------------------------------------------------------------- 1 | // Composition. 2 | (X, Z) :- (X, Y), (Y, Z). 3 | 4 | // 5 | // A commutative square: 6 | // 7 | // a ----> c 8 | // | | 9 | // | | 10 | // v v 11 | // b ----> d 12 | // 13 | (a, b) 14 | (c, d) 15 | (a, c) 16 | (b, d) 17 | -------------------------------------------------------------------------------- /source/squares2-0.txt: -------------------------------------------------------------------------------- 1 | // 2 | // a ----> b 3 | // | | 4 | // | | 5 | // v v 6 | // c ----> d 7 | // 8 | (a, b) 9 | (a, c) 10 | (c, d) 11 | (b, d) 12 | -------------------------------------------------------------------------------- /source/squares2.txt: -------------------------------------------------------------------------------- 1 | // Composition. 2 | (X, Z) :- (X, Y), (Y, Z). 3 | 4 | // 5 | // A zig zag pattern: 6 | // 7 | // a ----> b 8 | // / 9 | // / 10 | // L 11 | // c ----> d 12 | // 13 | (a, b) 14 | (b, c) 15 | (c, d) 16 | -------------------------------------------------------------------------------- /source/squares3-0.txt: -------------------------------------------------------------------------------- 1 | (a, c) 2 | (b, d) 3 | (c, e) 4 | 5 | (a, d) 6 | (b, e) 7 | 8 | (a, e) 9 | -------------------------------------------------------------------------------- /source/squares3.txt: -------------------------------------------------------------------------------- 1 | // Composition. 2 | (X, Z) :- (X, Y), (Y, Z). 3 | 4 | (a, b) 5 | (b, c) 6 | (c, d) 7 | (d, e) 8 | -------------------------------------------------------------------------------- /source/squares4-0.txt: -------------------------------------------------------------------------------- 1 | (square, one) 2 | -------------------------------------------------------------------------------- /source/squares4.txt: -------------------------------------------------------------------------------- 1 | (square, one) :- (a, b), (a, c), (b, d), (c, d). 2 | 3 | (a, b) 4 | (a, c) 5 | (b, d) 6 | (c, d) 7 | -------------------------------------------------------------------------------- /source/string.txt: -------------------------------------------------------------------------------- 1 | ("hello world!", test) 2 | 3 | -------------------------------------------------------------------------------- /source/sub_type-0.txt: -------------------------------------------------------------------------------- 1 | no amb 2 | 3 | sub_type(x) = sub_type(y) 4 | 5 | sub_type(y) = f'(a) 6 | -------------------------------------------------------------------------------- /source/sub_type.txt: -------------------------------------------------------------------------------- 1 | uniq f 2 | 3 | f'(a) : sub_type 4 | 5 | (x, f'(a)) 6 | (y, f'(b)) 7 | 8 | sub_type(X) = sub_type(Y) :- (X, f'(A)), (Y, f'(Z)). 9 | -------------------------------------------------------------------------------- /source/transitivity-0.txt: -------------------------------------------------------------------------------- 1 | (a, c) 2 | -------------------------------------------------------------------------------- /source/transitivity.txt: -------------------------------------------------------------------------------- 1 | (a, b) 2 | (b, c) 3 | 4 | (A, C) :- (A, B), (B, C). 5 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | 3 | //! # Avalog 4 | //! 5 | //! An experimental implementation of [Avatar Logic](https://advancedresearch.github.io/avatar-extensions/summary.html) 6 | //! with a Prolog-like syntax. 7 | //! 8 | //! ```text 9 | //! === Avalog 0.7 === 10 | //! Type `help` for more information. 11 | //! > parent'(alice) : mom 12 | //! > (bob, parent'(alice)) 13 | //! > prove mom(bob) => parent'(alice) 14 | //! parent'(alice) : mom 15 | //! (bob, parent'(alice)) 16 | //! ---------------------------------- 17 | //! mom(bob) => parent'(alice) 18 | //! 19 | //! OK 20 | //! ``` 21 | //! 22 | //! To run Avalog from your Terminal, type: 23 | //! 24 | //! ```text 25 | //! cargo install --example avalog_repl avalog 26 | //! ``` 27 | //! 28 | //! Then, to run: 29 | //! 30 | //! ```text 31 | //! avalog_repl 32 | //! ``` 33 | //! 34 | //! Based on paper [Avatar Binary Relations](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/avatar-binary-relations.pdf). 35 | //! 36 | //! ### Motivation 37 | //! 38 | //! Avatar Logic is an attempt to create a formal language that satisfies "avatars", 39 | //! in the sense of [Avatar Extensions](https://github.com/advancedresearch/path_semantics/blob/master/sequences.md#avatar-extensions). 40 | //! Avatar Extensions is a technique for abstract generalization in [Path Semantics](https://github.com/advancedresearch/path_semantics), 41 | //! an extremely expressive language for mathematical programming. 42 | //! 43 | //! In higher dimensional programming, such as Path Semantics, one would like to generalize 44 | //! the abstractions across multiple dimensions at the same time, without having to write axioms 45 | //! for each dimension. 46 | //! 47 | //! If normal programming is 2D, then [normal paths](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/normal-paths.pdf) 48 | //! in Path Semantics is 3D. 49 | //! Avatar Extensions seeks to go beyond 3D programming to arbitrary higher dimensions. 50 | //! Avatar Logic is a deep result from discussing interpretations of [Avatar Graphs](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/avatar-graphs.pdf), 51 | //! which relates a variant of Cartesian combinatorics with graphs, group actions and topology. 52 | //! 53 | //! ### Example: Grandma Alice 54 | //! 55 | //! ```text 56 | //! uniq parent 57 | //! parent'(alice) : mom 58 | //! grandparent'(alice) : grandma 59 | //! parent'(bob) : dad 60 | //! (bob, parent'(alice)) 61 | //! (carl, parent'(bob)) 62 | //! (X, grandparent'(Z)) :- (X, parent'(Y)), (Y, parent'(Z)). 63 | //! ``` 64 | //! 65 | //! This can be used to prove the following goal: 66 | //! 67 | //! ```text 68 | //! grandma(carl) => grandparent'(alice) 69 | //! ``` 70 | //! 71 | //! The first statement `uniq parent` tells that the 1-avatar `parent` should behave uniquely. 72 | //! Basically, this means one person can have maximum one mom and one dad. 73 | //! 74 | //! When this is turned off, one can only say e.g. "Bob has a mom who's name is Alice", 75 | //! but not "Bob's mom is Alice", because Bob might have more than one mom. 76 | //! 77 | //! Formally, `mom(bob) => parent'(alice)` (has) vs `mom(bob) = parent'(alice)` (is). 78 | //! 79 | //! The statement `parent'(alice) : mom` says that Alice has a 1-avatar "parent" which 80 | //! is assigned the role "mom". 81 | //! Avatar Logic "knows" that Alice, and Alice as a parent, are one and the same, 82 | //! but the relations to Alice are universal while relations to Alice as a parent depends on context. 83 | //! 84 | //! The relation `(bob, parent'(alice))` does not specify how Bob and Alice as a parent are related, 85 | //! because it is inferred from the assigned role to Alice as a parent. 86 | //! This simplifies the logic a lot for higher dimensions of programming. 87 | //! 88 | //! The rule `(X, grandparent'(Z)) :- (X, parent'(Y)), (Y, parent'(Z)).` is similar 89 | //! to a [Horn clause](https://en.wikipedia.org/wiki/Horn_clause), which is used in 90 | //! [Prolog](https://en.wikipedia.org/wiki/Prolog). 91 | //! 92 | //! The grandparent rule works for any combination of moms and dads. 93 | //! It also works for other parental relationship that can be used for e.g. animals. 94 | //! You can use separate roles for separate kind of objects, but not mix e.g. humans and animals. 95 | //! This is because Avatar Logic is kind of like [dependent types](https://en.wikipedia.org/wiki/Dependent_type), but for logic. 96 | //! Relationships depends on the values, but enforces local consistency, kind of like types. 97 | //! 98 | //! ### Introduction to Avatar Logic 99 | //! 100 | //! In Prolog, you would write relations using predicates, e.g. `mom(alice, bob)`. 101 | //! The problem is that predicates are 1) unconstrained and 2) axioms doesn't carry over 102 | //! arbitrary Cartesian relations. 103 | //! 104 | //! In Avatar Logic, instead of predicates, you use "binary relations" with added axioms for roles and avatars. 105 | //! 106 | //! To explain how Avatar Logic works, one can start with binary relations. 107 | //! 108 | //! A binary relation is an ordered pair: 109 | //! 110 | //! ```text 111 | //! (a, b) 112 | //! ``` 113 | //! 114 | //! By adding axioms to binary relations, one can improve expressiveness and simplify 115 | //! modeling over abstract relations. This is used because unconstrained relations are 116 | //! too hard to use for formalizing advanced mathematical theories, like Path Semantics. 117 | //! 118 | //! Avatar Logic consists of two kinds of pairs: 119 | //! 120 | //! - [Unique Universal Binary Relations](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/unique-universal-binary-relations.pdf) 121 | //! - [Avatar Binary Relations](https://github.com/advancedresearch/path_semantics/blob/master/papers-wip/avatar-binary-relations.pdf) 122 | //! 123 | //! Axioms: 124 | //! 125 | //! ```text 126 | //! p(a, b) b : p p(a) = b 127 | //! p(a, q'(b)) q'(b) : p p(a) = {q'(_)} ∈ q'(b) 128 | //! ``` 129 | //! 130 | //! To make Avatar Binary Relations behave like Unique Universal Binary Relations, 131 | //! one can use the `uniq q` directive where `q` is a 1-avatar. 132 | //! This forces the following relation for `q`: 133 | //! 134 | //! ```text 135 | //! p(a) = q'(b) 136 | //! ``` 137 | //! 138 | //! ### Design 139 | //! 140 | //! Uses the [Monotonic-Solver](https://github.com/advancedresearch/monotonic_solver) library 141 | //! for generic automated theorem proving. 142 | //! 143 | //! Uses the [Piston-Meta](https://github.com/pistondevelopers/meta) library for meta parsing. 144 | //! 145 | //! The axioms for Avatar Logic can not be used directly, 146 | //! but instead inference rules are derived from the axioms. 147 | //! 148 | //! A part of this project is to experiment with inference rules, 149 | //! so no recommended set of inference is published yet. 150 | //! 151 | //! The inference rules are coded in the `infer` function. 152 | 153 | use std::sync::Arc; 154 | use std::fmt; 155 | use std::collections::HashMap; 156 | use std::cmp; 157 | use std::hash::Hash; 158 | 159 | use Expr::*; 160 | 161 | pub use monotonic_solver::*; 162 | pub use parsing::*; 163 | 164 | mod parsing; 165 | 166 | /// Detect a variable when parsing. 167 | pub trait IsVar { 168 | /// Returns `true` if string is a variable. 169 | fn is_var(val: &str) -> bool { 170 | if let Some(c) = val.chars().next() {c.is_uppercase()} else {false} 171 | } 172 | } 173 | 174 | /// Implemented by symbol types. 175 | pub trait Symbol: 176 | IsVar + 177 | From> + 178 | Clone + 179 | fmt::Debug + 180 | fmt::Display + 181 | cmp::PartialEq + 182 | cmp::Eq + 183 | cmp::PartialOrd + 184 | Hash { 185 | } 186 | 187 | impl IsVar for Arc {} 188 | 189 | impl Symbol for T 190 | where T: IsVar + 191 | From> + 192 | Clone + 193 | fmt::Debug + 194 | fmt::Display + 195 | cmp::PartialEq + 196 | cmp::Eq + 197 | cmp::PartialOrd + 198 | Hash 199 | {} 200 | 201 | /// Represents an expression. 202 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 203 | pub enum Expr> { 204 | /// A symbol. 205 | Sym(T), 206 | /// A variable. 207 | Var(Arc), 208 | /// A relation between two symbols. 209 | Rel(Box>, Box>), 210 | /// A 1-avatar of some expression. 211 | Ava(Box>, Box>), 212 | /// Unwraps 1-avatar. 213 | Inner(Box>), 214 | /// A 1-avatar `q'(b)` such that `p(a) = q'(b)`. 215 | UniqAva(Box>), 216 | /// A role of expression. 217 | RoleOf(Box>, Box>), 218 | /// An equality, e.g. `p(a) = b`. 219 | Eq(Box>, Box>), 220 | /// An inequality, e.g. `X != a`. 221 | Neq(Box>, Box>), 222 | /// A set membership relation, e.g. `p(a) ∋ b`. 223 | Has(Box>, Box>), 224 | /// Apply an argument to a role, e.g. `p(a)`. 225 | App(Box>, Box>), 226 | /// There is an ambiguous role. 227 | AmbiguousRole(Box>, Box>, Box>), 228 | /// There is an ambiguous relation. 229 | AmbiguousRel(Box>, Box>, Box>), 230 | /// Defines a rule. 231 | Rule(Box>, Vec>), 232 | /// Ambiguity summary. 233 | /// 234 | /// This is `true` when some ambiguity is found, 235 | /// and `false` when no ambiguity is found. 236 | Ambiguity(bool), 237 | /// Represents the tail of an argument list `..`. 238 | Tail, 239 | /// Represents the tail of an argument list bound a symbol `..x`. 240 | TailVar(Arc), 241 | /// Represents a list. 242 | List(Vec>), 243 | } 244 | 245 | impl fmt::Display for Expr { 246 | fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { 247 | match self { 248 | Sym(a) => write!(w, "{}", a)?, 249 | Var(a) => write!(w, "{}", a)?, 250 | Rel(a, b) => write!(w, "({}, {})", a, b)?, 251 | Ava(a, b) => write!(w, "{}'({})", a, b)?, 252 | Inner(a) => write!(w, ".{}", a)?, 253 | UniqAva(a) => write!(w, "uniq {}", a)?, 254 | RoleOf(a, b) => write!(w, "{} : {}", a, b)?, 255 | Eq(a, b) => write!(w, "{} = {}", a, b)?, 256 | Neq(a, b) => write!(w, "{} != {}", a, b)?, 257 | Has(a, b) => write!(w, "{} => {}", a, b)?, 258 | App(a, b) => { 259 | let mut expr = a; 260 | let mut args = vec![]; 261 | let mut found_f = false; 262 | while let App(a1, a2) = &**expr { 263 | if let App(_, _) = &**a1 {} else { 264 | found_f = true; 265 | write!(w, "{}(", a1)?; 266 | } 267 | args.push(a2); 268 | expr = a1; 269 | } 270 | 271 | if !found_f { 272 | write!(w, "{}(", a)?; 273 | } 274 | let mut first = true; 275 | for arg in args.iter().rev() { 276 | if !first { 277 | write!(w, ", ")?; 278 | } 279 | first = false; 280 | write!(w, "{}", arg)?; 281 | } 282 | if !first { 283 | write!(w, ", ")?; 284 | } 285 | write!(w, "{})", b)?; 286 | } 287 | AmbiguousRole(a, b, c) => write!(w, "amb_role({}, {}, {})", a, b, c)?, 288 | AmbiguousRel(a, b, c) => write!(w, "amb_rel({}, {}, {})", a, b, c)?, 289 | Ambiguity(v) => if *v {write!(w, "amb")?} else {write!(w, "no amb")?}, 290 | Rule(a, b) => { 291 | write!(w, "{} :- ", a)?; 292 | let mut first = true; 293 | for arg in b { 294 | if !first { 295 | write!(w, ", ")?; 296 | } 297 | first = false; 298 | write!(w, "{}", arg)?; 299 | } 300 | write!(w, ".")?; 301 | } 302 | Tail => write!(w, "..")?, 303 | TailVar(a) => write!(w, "..{}", a)?, 304 | List(a) => { 305 | write!(w, "[")?; 306 | let mut first = true; 307 | for item in a { 308 | if !first { 309 | write!(w, ", ")?; 310 | } 311 | first = false; 312 | write!(w, "{}", item)?; 313 | } 314 | write!(w, "]")?; 315 | } 316 | } 317 | Ok(()) 318 | } 319 | } 320 | 321 | /// Constructs a unique directive expression, e.g. `uniq a`. 322 | pub fn uniq_ava(a: T) -> Expr 323 | where T: Into>, S: Symbol 324 | { 325 | UniqAva(Box::new(a.into())) 326 | } 327 | 328 | /// Constructs a role-of expression, e.g. `a : b`. 329 | pub fn role_of(a: T, b: U) -> Expr 330 | where T: Into>, U: Into>, S: Symbol 331 | { 332 | RoleOf(Box::new(a.into()), Box::new(b.into())) 333 | } 334 | 335 | /// Constructs a relation expression, e.g. `(a, b)`. 336 | pub fn rel(a: T, b: U) -> Expr 337 | where T: Into>, U: Into>, S: Symbol 338 | { 339 | Rel(Box::new(a.into()), Box::new(b.into())) 340 | } 341 | 342 | /// Constructs a 1-avatar expression, e.g. `p'(a)`. 343 | pub fn ava(a: T, b: U) -> Expr 344 | where T: Into>, U: Into>, S: Symbol 345 | { 346 | Ava(Box::new(a.into()), Box::new(b.into())) 347 | } 348 | 349 | /// Constructs an equality expression. 350 | pub fn eq(a: T, b: U) -> Expr 351 | where T: Into>, U: Into>, S: Symbol 352 | { 353 | Eq(Box::new(a.into()), Box::new(b.into())) 354 | } 355 | 356 | /// Constructs an inequality expression. 357 | pub fn neq(a: T, b: U) -> Expr 358 | where T: Into>, U: Into>, S: Symbol 359 | { 360 | Neq(Box::new(a.into()), Box::new(b.into())) 361 | } 362 | 363 | /// Constructs a "has" expression e.g. `p(a) => b`. 364 | pub fn has(a: T, b: U) -> Expr 365 | where T: Into>, U: Into>, S: Symbol 366 | { 367 | Has(Box::new(a.into()), Box::new(b.into())) 368 | } 369 | 370 | /// Constructs an apply expression. 371 | pub fn app(a: T, b: U) -> Expr 372 | where T: Into>, U: Into>, S: Symbol 373 | { 374 | App(Box::new(a.into()), Box::new(b.into())) 375 | } 376 | 377 | /// Constructs an inner expression e.g. `.p'(a) = a`. 378 | pub fn inner>, S: Symbol>(a: T) -> Expr { 379 | Inner(Box::new(a.into())) 380 | } 381 | 382 | /// Constructs an ambiguous role expression. 383 | pub fn ambiguous_role(a: T, b: U, c: V) -> Expr 384 | where T: Into>, U: Into>, V: Into>, S: Symbol 385 | { 386 | let b = b.into(); 387 | let c = c.into(); 388 | let (b, c) = if b < c {(b, c)} else {(c, b)}; 389 | AmbiguousRole(Box::new(a.into()), Box::new(b), Box::new(c)) 390 | } 391 | 392 | /// Constructs an ambiguous relation expression. 393 | pub fn ambiguous_rel(a: T, b: U, c: V) -> Expr 394 | where T: Into>, U: Into>, V: Into>, S: Symbol 395 | { 396 | let b = b.into(); 397 | let c = c.into(); 398 | let (b, c) = if b < c {(b, c)} else {(c, b)}; 399 | AmbiguousRel(Box::new(a.into()), Box::new(b), Box::new(c)) 400 | } 401 | 402 | impl Expr { 403 | /// Lifts apply with an eval role. 404 | pub fn eval_lift(&self, eval: &T, top: bool) -> Expr { 405 | match self { 406 | Rel(a, b) => rel(a.eval_lift(eval, true), b.eval_lift(eval, true)), 407 | App(a, b) => { 408 | if top { 409 | app(a.eval_lift(eval, true), b.eval_lift(eval, false)) 410 | } else { 411 | app(Sym(eval.clone()), 412 | app(a.eval_lift(eval, true), b.eval_lift(eval, false)) 413 | ) 414 | } 415 | } 416 | Rule(res, arg) => { 417 | let new_res = res.eval_lift(eval, true); 418 | let new_arg: Vec> = arg.iter().map(|a| a.eval_lift(eval, true)).collect(); 419 | Rule(Box::new(new_res), new_arg) 420 | } 421 | Sym(_) | Var(_) => self.clone(), 422 | UniqAva(_) => self.clone(), 423 | Ambiguity(_) => self.clone(), 424 | Tail => self.clone(), 425 | TailVar(_) => self.clone(), 426 | RoleOf(a, b) => { 427 | role_of(a.eval_lift(eval, false), b.eval_lift(eval, false)) 428 | } 429 | Ava(a, b) => ava((**a).clone(), b.eval_lift(eval, false)), 430 | Inner(a) => inner(a.eval_lift(eval, false)), 431 | Eq(a, b) => { 432 | if let App(a1, a2) = &**a { 433 | eq(app((**a1).clone(), a2.eval_lift(eval, true)), b.eval_lift(eval, false)) 434 | } else { 435 | self.clone() 436 | } 437 | } 438 | // TODO: Handle these cases. 439 | Neq(_, _) => self.clone(), 440 | Has(_, _) => self.clone(), 441 | AmbiguousRole(_, _, _) => self.clone(), 442 | AmbiguousRel(_, _, _) => self.clone(), 443 | List(_) => self.clone(), 444 | // _ => unimplemented!("{:?}", self) 445 | } 446 | } 447 | 448 | /// Returns `true` if expression contains no variables. 449 | pub fn is_const(&self) -> bool { 450 | match self { 451 | Var(_) => false, 452 | Sym(_) => true, 453 | App(ref a, ref b) => a.is_const() && b.is_const(), 454 | Ava(ref a, ref b) => a.is_const() && b.is_const(), 455 | _ => false 456 | } 457 | } 458 | 459 | /// Returns `true` if expression is a tail pattern. 460 | pub fn is_tail(&self) -> bool { 461 | match self { 462 | Tail | TailVar(_) => true, 463 | _ => false 464 | } 465 | } 466 | 467 | /// Returns the number of arguments in apply expression. 468 | pub fn arity(&self) -> usize { 469 | if let App(a, _) = self {a.arity() + 1} else {0} 470 | } 471 | } 472 | 473 | /// Bind `a` to pattern `e`. 474 | pub fn bind( 475 | e: &Expr, 476 | a: &Expr, 477 | vs: &mut Vec<(Arc, Expr)>, 478 | tail: &mut Vec> 479 | ) -> bool { 480 | match (e, a) { 481 | (&Rel(ref a1, ref b1), &Rel(ref a2, ref b2)) => { 482 | bind(a1, a2, vs, tail) && 483 | bind(b1, b2, vs, tail) 484 | } 485 | (&RoleOf(ref a1, ref b1), &RoleOf(ref a2, ref b2)) => { 486 | bind(a1, a2, vs, tail) && 487 | bind(b1, b2, vs, tail) 488 | } 489 | (&Eq(ref a1, ref b1), &Eq(ref a2, ref b2)) => { 490 | bind(a1, a2, vs, tail) && 491 | bind(b1, b2, vs, tail) 492 | } 493 | (&Has(ref a1, ref b1), &Has(ref a2, ref b2)) => { 494 | bind(a1, a2, vs, tail) && 495 | bind(b1, b2, vs, tail) 496 | } 497 | (&App(ref a1, ref b1), &App(ref a2, ref b2)) if b1.is_tail() && 498 | a2.arity() >= a1.arity() && b2.is_const() => { 499 | tail.push((**b2).clone()); 500 | if a2.arity() > a1.arity() { 501 | bind(e, a2, vs, tail) 502 | } else { 503 | bind(a1, a2, vs, tail) && 504 | if let TailVar(b1_sym) = &**b1 { 505 | if tail.len() > 0 { 506 | tail.reverse(); 507 | vs.push((b1_sym.clone(), List(tail.clone()))); 508 | tail.clear(); 509 | true 510 | } else { 511 | tail.clear(); 512 | false 513 | } 514 | } else { 515 | true 516 | } 517 | } 518 | } 519 | (&App(ref a1, ref b1), &App(ref a2, ref b2)) => { 520 | bind(a1, a2, vs, tail) && 521 | bind(b1, b2, vs, tail) 522 | } 523 | (&Sym(ref a1), &Sym(ref a2)) => a1 == a2, 524 | (&Var(ref a1), &Sym(_)) => { 525 | // Look for previous occurences of bound variable. 526 | let mut found = false; 527 | for &(ref b, ref b_expr) in &*vs { 528 | if b == a1 { 529 | if let Some(true) = equal(b_expr, a) { 530 | found = true; 531 | break; 532 | } else { 533 | return false; 534 | } 535 | } 536 | } 537 | if !found { 538 | vs.push((a1.clone(), a.clone())); 539 | } 540 | true 541 | } 542 | (&Var(ref a1), _) if a.is_const() => { 543 | vs.push((a1.clone(), a.clone())); 544 | true 545 | } 546 | (&Ava(ref a1, ref b1), &Ava(ref a2, ref b2)) => { 547 | bind(a1, a2, vs, tail) && 548 | bind(b1, b2, vs, tail) 549 | } 550 | _ => false 551 | } 552 | } 553 | 554 | fn substitute(r: &Expr, vs: &Vec<(Arc, Expr)>) -> Expr { 555 | match r { 556 | Rel(a1, b1) => { 557 | rel(substitute(a1, vs), substitute(b1, vs)) 558 | } 559 | Var(a1) => { 560 | for v in vs { 561 | if &v.0 == a1 { 562 | return v.1.clone(); 563 | } 564 | } 565 | r.clone() 566 | } 567 | Sym(_) => r.clone(), 568 | Ava(a1, b1) => { 569 | ava(substitute(a1, vs), substitute(b1, vs)) 570 | } 571 | RoleOf(a, b) => { 572 | role_of(substitute(a, vs), substitute(b, vs)) 573 | } 574 | Eq(a, b) => { 575 | eq(substitute(a, vs), substitute(b, vs)) 576 | } 577 | Neq(a, b) => { 578 | neq(substitute(a, vs), substitute(b, vs)) 579 | } 580 | Has(a, b) => { 581 | has(substitute(a, vs), substitute(b, vs)) 582 | } 583 | App(a, b) => { 584 | let a_expr = substitute(a, vs); 585 | if let Var(a1) = &**b { 586 | for v in vs { 587 | if &v.0 == a1 { 588 | if let List(args) = &v.1 { 589 | let mut expr = a_expr; 590 | for arg in args { 591 | expr = app(expr, arg.clone()); 592 | } 593 | return expr; 594 | } 595 | } 596 | } 597 | } 598 | app(a_expr, substitute(b, vs)) 599 | } 600 | Ambiguity(_) => r.clone(), 601 | UniqAva(a) => { 602 | uniq_ava(substitute(a, vs)) 603 | } 604 | Inner(a) => { 605 | inner(substitute(a, vs)) 606 | } 607 | x => unimplemented!("{:?}", x) 608 | } 609 | } 610 | 611 | // Returns `Some(true)` if two expressions are proven to be equal, 612 | // `Some(false)` when proven to be inequal, and `None` when unknown. 613 | fn equal(a: &Expr, b: &Expr) -> Option { 614 | fn false_or_none(val: Option) -> bool { 615 | if let Some(val) = val {!val} else {true} 616 | } 617 | 618 | if a.is_const() && b.is_const() {Some(a == b)} 619 | else { 620 | match (a, b) { 621 | (&Sym(_), &Sym(_)) | 622 | (&Sym(_), &Var(_)) | (&Var(_), &Sym(_)) | 623 | (&Var(_), &Var(_)) | 624 | (&Var(_), &Ava(_, _)) | (&Ava(_, _), &Var(_)) | 625 | (&App(_, _), &Sym(_)) | (&Sym(_), &App(_, _)) | 626 | (&App(_, _), &Var(_)) | (&Var(_), &App(_, _)) | 627 | (&App(_, _), &Ava(_, _)) | (&Ava(_, _), &App(_, _)) => None, 628 | (&Sym(_), &Ava(_, _)) | (&Ava(_, _), &Sym(_)) => Some(false), 629 | (&Ava(ref a1, ref b1), &Ava(ref a2, ref b2)) | 630 | (&App(ref a1, ref b1), &App(ref a2, ref b2)) => { 631 | let cmp_a = equal(a1, a2); 632 | if false_or_none(cmp_a) {return cmp_a}; 633 | equal(b1, b2) 634 | } 635 | // TODO: Handle other cases. 636 | x => unimplemented!("{:?}", x) 637 | } 638 | } 639 | } 640 | 641 | fn match_rule(r: &Expr, rel: &Expr) -> Option> { 642 | if let Rule(res, args) = r { 643 | let mut vs = vec![]; 644 | let mut tail: Vec> = vec![]; 645 | if bind(&args[0], rel, &mut vs, &mut tail) { 646 | if args.len() > 1 { 647 | let mut new_args = vec![]; 648 | for e in &args[1..] { 649 | let new_e = substitute(e, &vs); 650 | if let Neq(a, b) = &new_e { 651 | match equal(a, b) { 652 | Some(true) => return None, 653 | Some(false) => continue, 654 | None => {} 655 | } 656 | } 657 | new_args.push(new_e); 658 | } 659 | let new_res = substitute(res, &vs); 660 | if new_args.len() > 0 { 661 | return Some(Rule(Box::new(new_res), new_args)); 662 | } else { 663 | return Some(new_res); 664 | } 665 | } else { 666 | let new_res = substitute(res, &vs); 667 | return Some(new_res); 668 | } 669 | } 670 | None 671 | } else { 672 | None 673 | } 674 | } 675 | 676 | fn apply(e: &Expr, facts: &[Expr]) -> Option> { 677 | match e { 678 | App(a, b) => { 679 | for e2 in facts { 680 | if let Eq(b2, b3) = e2 { 681 | if &**b2 == e { 682 | return Some((**b3).clone()); 683 | } 684 | } 685 | } 686 | 687 | match (apply(a, facts), apply(b, facts)) { 688 | (Some(a), Some(b)) => return Some(app(a, b)), 689 | (None, Some(b)) => return Some(app((**a).clone(), b)), 690 | (Some(a), None) => return Some(app(a, (**b).clone())), 691 | (None, None) => {} 692 | } 693 | } 694 | Rel(a, b) => { 695 | match (apply(a, facts), apply(b, facts)) { 696 | (Some(a), Some(b)) => return Some(rel(a, b)), 697 | (None, Some(b)) => return Some(rel((**a).clone(), b)), 698 | (Some(a), None) => return Some(rel(a, (**b).clone())), 699 | (None, None) => {} 700 | } 701 | } 702 | Ava(a, b) => { 703 | match (apply(a, facts), apply(b, facts)) { 704 | (Some(a), Some(b)) => return Some(ava(a, b)), 705 | (None, Some(b)) => return Some(ava((**a).clone(), b)), 706 | (Some(a), None) => return Some(ava(a, (**b).clone())), 707 | (None, None) => {} 708 | } 709 | } 710 | Eq(a, b) => { 711 | let new_b = apply(b, facts)?; 712 | return Some(eq((**a).clone(), new_b)) 713 | } 714 | Has(a, b) => { 715 | let new_b = apply(b, facts)?; 716 | return Some(has((**a).clone(), new_b)) 717 | } 718 | Inner(a) => { 719 | let new_a = apply(a, facts); 720 | if new_a.is_some() {return new_a.map(|n| inner(n))}; 721 | if let Ava(_, b) = &**a { 722 | if let Some(new_b) = apply(b, facts) { 723 | return Some(new_b); 724 | } else { 725 | return Some((**b).clone()); 726 | } 727 | } else { 728 | return Some(inner(apply(a, facts)?)); 729 | } 730 | } 731 | _ => {} 732 | } 733 | None 734 | } 735 | 736 | /// Index of sub-expressions. 737 | pub struct Accelerator { 738 | /// Index for each sub-expression. 739 | pub index: HashMap, Vec>, 740 | /// The number of facts that has been indexed. 741 | pub len: usize, 742 | /// The index of the last rule. 743 | pub last_rule: Option, 744 | } 745 | 746 | impl Accelerator { 747 | /// Creates a new accelerator. 748 | pub fn new() -> Accelerator { 749 | Accelerator {index: HashMap::new(), len: 0, last_rule: None} 750 | } 751 | 752 | /// Returns a constructor for solve-and-reduce. 753 | pub fn constructor() -> fn(&[Expr], &[Expr]) -> Accelerator { 754 | |_, _| Accelerator::new() 755 | } 756 | 757 | /// Updates the accelerator with list of facts. 758 | pub fn update(&mut self, facts: &[Expr]) 759 | where T: Clone + std::hash::Hash + std::cmp::Eq 760 | { 761 | let accelerator_len = self.len; 762 | let ref mut index = self.index; 763 | let mut insert = |i: usize, e: &Expr| { 764 | index.entry(e.clone()).or_insert(vec![]).push(i); 765 | }; 766 | for (i, e) in facts[accelerator_len..].iter().enumerate() { 767 | let i = i + accelerator_len; 768 | match e { 769 | RoleOf(a, b) | Rel(a, b) | Ava(a, b) | Eq(a, b) | 770 | Neq(a, b) | Has(a, b) | App(a, b) => { 771 | insert(i, a); 772 | insert(i, b); 773 | } 774 | Sym(_) | Var(_) | Inner(_) | UniqAva(_) => { 775 | insert(i, e); 776 | } 777 | AmbiguousRel(_, _, _) | 778 | AmbiguousRole(_, _, _) | 779 | Rule(_, _) | 780 | Ambiguity(_) | 781 | Tail | TailVar(_) | List(_) => {} 782 | } 783 | } 784 | self.len = facts.len(); 785 | } 786 | } 787 | 788 | /// Specifies inference rules for monotonic solver. 789 | pub fn infer( 790 | solver: Solver, Accelerator>, 791 | facts: &[Expr] 792 | ) -> Option> { 793 | // Build an index to improve worst-case performance. 794 | solver.accelerator.update(facts); 795 | let find = |e: &Expr| { 796 | solver.accelerator.index.get(e).unwrap().iter().map(|&i| &facts[i]) 797 | }; 798 | 799 | for e in facts.iter().rev() { 800 | // Detect ambiguous roles. 801 | if let RoleOf(a, b) = e { 802 | for e2 in find(a) { 803 | if let RoleOf(a2, b2) = e2 { 804 | if a2 == a && b2 != b { 805 | let new_expr = ambiguous_role((**a).clone(), (**b).clone(), (**b2).clone()); 806 | if solver.can_add(&new_expr) {return Some(new_expr)}; 807 | 808 | let new_expr = Ambiguity(true); 809 | if solver.can_add(&new_expr) {return Some(new_expr)}; 810 | } 811 | } 812 | } 813 | } 814 | 815 | // Convert 'has' into 'eq' using uniqueness. 816 | if let Has(a, b) = e { 817 | if let Ava(b1, _) = &**b { 818 | // `p(a) => q'(b), uniq q => p(a) = q'(b)` 819 | let uniq_expr = uniq_ava((**b1).clone()); 820 | if solver.cache.contains(&uniq_expr) { 821 | let new_expr = eq((**a).clone(), (**b).clone()); 822 | if solver.can_add(&new_expr) {return Some(new_expr)}; 823 | } 824 | } else { 825 | // `p(a) => b => p(a) = b` 826 | let new_expr = eq((**a).clone(), (**b).clone()); 827 | if solver.can_add(&new_expr) {return Some(new_expr)}; 828 | } 829 | } 830 | 831 | // Convert 'eq' into 'has'. 832 | if let Eq(a, b) = e { 833 | let new_expr = has((**a).clone(), (**b).clone()); 834 | if solver.can_add(&new_expr) {return Some(new_expr)}; 835 | } 836 | 837 | if let Rel(a, b) = e { 838 | if let Ava(av, _) = &**b { 839 | // Avatar Binary Relation. 840 | let mut b_role: Option> = None; 841 | let mut uniq = false; 842 | for e2 in find(b) { 843 | if let RoleOf(b2, r) = e2 { 844 | if b2 == b { 845 | if solver.cache.contains(&uniq_ava((**av).clone())) { 846 | uniq = true; 847 | let new_expr = eq(app((**r).clone(), (**a).clone()), (**b).clone()); 848 | if solver.can_add(&new_expr) {return Some(new_expr)}; 849 | } 850 | 851 | let new_expr = has(app((**r).clone(), (**a).clone()), (**b).clone()); 852 | if solver.can_add(&new_expr) {return Some(new_expr)}; 853 | b_role = Some((**r).clone()); 854 | } 855 | } 856 | } 857 | 858 | // Look for another avatar relation with same role. 859 | if let Some(b_role) = &b_role { 860 | for e1 in find(a) { 861 | if let Rel(a2, b2) = e1 { 862 | if a2 == a && b2 != b { 863 | if let Ava(av2, _) = &**b2 { 864 | if uniq || av2 != av { 865 | for e2 in find(b2) { 866 | if let RoleOf(a3, b3) = e2 { 867 | if a3 == b2 && &**b3 == b_role { 868 | let new_expr = ambiguous_rel((**a).clone(), 869 | (**b).clone(), (**b2).clone()); 870 | if solver.can_add(&new_expr) { 871 | return Some(new_expr) 872 | } 873 | 874 | let new_expr = Ambiguity(true); 875 | if solver.can_add(&new_expr) { 876 | return Some(new_expr) 877 | } 878 | } 879 | } 880 | } 881 | } 882 | } 883 | } 884 | } 885 | } 886 | } 887 | } else { 888 | // Unique Universal Binary Relation. 889 | let mut b_role: Option> = None; 890 | for e2 in find(b) { 891 | if let RoleOf(b2, r) = e2 { 892 | if b2 == b { 893 | let new_expr = eq(app((**r).clone(), (**a).clone()), (**b).clone()); 894 | if solver.can_add(&new_expr) {return Some(new_expr)}; 895 | let new_expr = has(app((**r).clone(), (**a).clone()), (**b).clone()); 896 | if solver.can_add(&new_expr) {return Some(new_expr)}; 897 | b_role = Some((**r).clone()); 898 | } 899 | } 900 | } 901 | 902 | // Look for another relation with same role. 903 | if let Some(b_role) = &b_role { 904 | for e1 in find(a) { 905 | if let Rel(a2, b2) = e1 { 906 | if a2 == a && b2 != b { 907 | if solver.cache.contains(&role_of((**b2).clone(), b_role.clone())) { 908 | let new_expr = ambiguous_rel((**a).clone(), 909 | (**b).clone(), (**b2).clone()); 910 | if solver.can_add(&new_expr) {return Some(new_expr)}; 911 | 912 | let new_expr = Ambiguity(true); 913 | if solver.can_add(&new_expr) {return Some(new_expr)}; 914 | } 915 | } 916 | } 917 | } 918 | } 919 | } 920 | } 921 | } 922 | 923 | for e in facts { 924 | if let Some(new_expr) = apply(e, facts) { 925 | if solver.can_add(&new_expr) {return Some(new_expr)}; 926 | } 927 | } 928 | 929 | match solver.accelerator.last_rule { 930 | None => { 931 | // Normal rule order. 932 | for (i, e) in facts.iter().enumerate() { 933 | if let Rule(_, _) = e { 934 | for e2 in facts { 935 | if let Some(new_expr) = match_rule(e, e2) { 936 | solver.accelerator.last_rule = Some(i); 937 | if solver.can_add(&new_expr) {return Some(new_expr)}; 938 | } 939 | } 940 | } 941 | } 942 | } 943 | Some(i) => { 944 | // Diagonalize rules. 945 | // Start with the next rule. 946 | for (j, e) in facts[i + 1..].iter().enumerate() { 947 | if let Rule(_, _) = e { 948 | for e2 in facts { 949 | if let Some(new_expr) = match_rule(e, e2) { 950 | solver.accelerator.last_rule = Some(i + 1 + j); 951 | if solver.can_add(&new_expr) {return Some(new_expr)}; 952 | } 953 | } 954 | } 955 | } 956 | // Try previous used rules. 957 | for (j, e) in facts[..i + 1].iter().enumerate() { 958 | if let Rule(_, _) = e { 959 | for e2 in facts { 960 | if let Some(new_expr) = match_rule(e, e2) { 961 | solver.accelerator.last_rule = Some(j); 962 | if solver.can_add(&new_expr) {return Some(new_expr)}; 963 | } 964 | } 965 | } 966 | } 967 | } 968 | } 969 | 970 | if !solver.cache.contains(&Ambiguity(true)) { 971 | let mut amb = false; 972 | for e in facts { 973 | if let AmbiguousRel(_, _, _) = e { 974 | amb = true; 975 | break; 976 | } else if let AmbiguousRole(_, _, _) = e { 977 | amb = true; 978 | break; 979 | } 980 | } 981 | let new_expr = Ambiguity(amb); 982 | if solver.can_add(&new_expr) {return Some(new_expr)}; 983 | } 984 | None 985 | } 986 | -------------------------------------------------------------------------------- /src/parsing.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | use piston_meta::{Convert, Range}; 4 | use std::path::Path; 5 | 6 | fn parse_rule( 7 | node: &str, 8 | mut convert: Convert, 9 | ignored: &mut Vec 10 | ) -> Result<(Range, Expr), ()> { 11 | let start = convert; 12 | let start_range = convert.start_node(node)?; 13 | convert.update(start_range); 14 | 15 | let mut res: Option> = None; 16 | let mut args: Vec> = vec![]; 17 | loop { 18 | if let Ok(range) = convert.end_node(node) { 19 | convert.update(range); 20 | break; 21 | } else if let Ok((range, v)) = parse_expr("res", convert, ignored) { 22 | convert.update(range); 23 | res = Some(v); 24 | } else if let Ok((range, v)) = parse_expr("arg", convert, ignored) { 25 | convert.update(range); 26 | args.push(v); 27 | } else { 28 | let range = convert.ignore(); 29 | convert.update(range); 30 | ignored.push(range); 31 | } 32 | } 33 | 34 | let res = res.ok_or(())?; 35 | Ok((convert.subtract(start), Expr::Rule(Box::new(res), args))) 36 | } 37 | 38 | fn parse_inner( 39 | node: &str, 40 | mut convert: Convert, 41 | ignored: &mut Vec 42 | ) -> Result<(Range, Expr), ()> { 43 | let start = convert; 44 | let start_range = convert.start_node(node)?; 45 | convert.update(start_range); 46 | 47 | let mut arg: Option> = None; 48 | loop { 49 | if let Ok(range) = convert.end_node(node) { 50 | convert.update(range); 51 | break; 52 | } else if let Ok((range, v)) = parse_expr("arg", convert, ignored) { 53 | convert.update(range); 54 | arg = Some(v); 55 | } else { 56 | let range = convert.ignore(); 57 | convert.update(range); 58 | ignored.push(range); 59 | } 60 | } 61 | 62 | let arg = arg.ok_or(())?; 63 | Ok((convert.subtract(start), Expr::Inner(Box::new(arg)))) 64 | } 65 | 66 | fn parse_app( 67 | node: &str, 68 | mut convert: Convert, 69 | ignored: &mut Vec 70 | ) -> Result<(Range, Expr), ()> { 71 | let start = convert; 72 | let start_range = convert.start_node(node)?; 73 | convert.update(start_range); 74 | 75 | let mut f: Option> = None; 76 | let mut arg: Vec> = vec![]; 77 | loop { 78 | if let Ok(range) = convert.end_node(node) { 79 | convert.update(range); 80 | break; 81 | } else if let Ok((range, v)) = convert.meta_string("f") { 82 | convert.update(range); 83 | if T::is_var(&v) { 84 | f = Some(Expr::Var(v)); 85 | } else { 86 | f = Some(Expr::Sym(v.into())); 87 | } 88 | } else if let Ok((range, v)) = parse_expr("arg", convert, ignored) { 89 | convert.update(range); 90 | arg.push(v); 91 | } else if let Ok((range, v)) = convert.meta_bool("tail") { 92 | convert.update(range); 93 | if v {arg.push(Tail)}; 94 | } else if let Ok((range, v)) = convert.meta_string("tail_sym") { 95 | convert.update(range); 96 | arg.push(TailVar(v.into())); 97 | } else { 98 | let range = convert.ignore(); 99 | convert.update(range); 100 | ignored.push(range); 101 | } 102 | } 103 | 104 | let mut expr = f.ok_or(())?; 105 | for a in &arg { 106 | expr = app(expr, a.clone()); 107 | } 108 | 109 | Ok((convert.subtract(start), expr)) 110 | } 111 | 112 | fn parse_ava( 113 | node: &str, 114 | mut convert: Convert, 115 | ignored: &mut Vec 116 | ) -> Result<(Range, Expr), ()> { 117 | let start = convert; 118 | let start_range = convert.start_node(node)?; 119 | convert.update(start_range); 120 | 121 | let mut avatar: Option> = None; 122 | let mut core: Option> = None; 123 | loop { 124 | if let Ok(range) = convert.end_node(node) { 125 | convert.update(range); 126 | break; 127 | } else if let Ok((range, v)) = convert.meta_string("avatar") { 128 | convert.update(range); 129 | if T::is_var(&v) { 130 | avatar = Some(Expr::Var(v)); 131 | } else { 132 | avatar = Some(Expr::Sym(v.into())); 133 | } 134 | } else if let Ok((range, v)) = parse_expr("core", convert, ignored) { 135 | convert.update(range); 136 | core = Some(v); 137 | } else { 138 | let range = convert.ignore(); 139 | convert.update(range); 140 | ignored.push(range); 141 | } 142 | } 143 | 144 | let avatar = avatar.ok_or(())?; 145 | let core = core.ok_or(())?; 146 | Ok((convert.subtract(start), Expr::Ava( 147 | Box::new(avatar), 148 | Box::new(core) 149 | ))) 150 | } 151 | 152 | fn parse_uniq( 153 | node: &str, 154 | mut convert: Convert, 155 | ignored: &mut Vec 156 | ) -> Result<(Range, Expr), ()> { 157 | let start = convert; 158 | let start_range = convert.start_node(node)?; 159 | convert.update(start_range); 160 | 161 | let mut arg: Option> = None; 162 | loop { 163 | if let Ok(range) = convert.end_node(node) { 164 | convert.update(range); 165 | break; 166 | } else if let Ok((range, v)) = parse_expr("arg", convert, ignored) { 167 | convert.update(range); 168 | arg = Some(Expr::UniqAva(Box::new(v))); 169 | } else { 170 | let range = convert.ignore(); 171 | convert.update(range); 172 | ignored.push(range); 173 | } 174 | } 175 | 176 | let arg = arg.ok_or(())?; 177 | Ok((convert.subtract(start), arg)) 178 | } 179 | 180 | /// Parses symbol or variable. 181 | /// 182 | /// Converts to variable automatically when starting with upper case. 183 | fn parse_sym_or_var( 184 | node: &str, 185 | mut convert: Convert, 186 | ignored: &mut Vec 187 | ) -> Result<(Range, Expr), ()> { 188 | let start = convert; 189 | let start_range = convert.start_node(node)?; 190 | convert.update(start_range); 191 | 192 | let mut val: Option> = None; 193 | loop { 194 | if let Ok(range) = convert.end_node(node) { 195 | convert.update(range); 196 | break; 197 | } else if let Ok((range, v)) = convert.meta_string("val") { 198 | convert.update(range); 199 | if T::is_var(&v) { 200 | val = Some(Expr::Var(v)); 201 | } else { 202 | val = Some(Expr::Sym(v.into())); 203 | } 204 | } else if let Ok((range, v)) = convert.meta_string("str_val") { 205 | convert.update(range); 206 | let v = Arc::new(format!("{:?}", v)); 207 | if T::is_var(&v) { 208 | val = Some(Expr::Var(v)); 209 | } else { 210 | val = Some(Expr::Sym(v.into())); 211 | } 212 | } else { 213 | let range = convert.ignore(); 214 | convert.update(range); 215 | ignored.push(range); 216 | } 217 | } 218 | 219 | let val = val.ok_or(())?; 220 | Ok((convert.subtract(start), val)) 221 | } 222 | 223 | fn parse_has( 224 | node: &str, 225 | mut convert: Convert, 226 | ignored: &mut Vec 227 | ) -> Result<(Range, Expr), ()> { 228 | let start = convert; 229 | let start_range = convert.start_node(node)?; 230 | convert.update(start_range); 231 | 232 | let mut f: Option> = None; 233 | let mut arg: Option> = None; 234 | let mut res: Option> = None; 235 | loop { 236 | if let Ok(range) = convert.end_node(node) { 237 | convert.update(range); 238 | break; 239 | } else if let Ok((range, val)) = parse_expr("f", convert, ignored) { 240 | convert.update(range); 241 | f = Some(val); 242 | } else if let Ok((range, val)) = parse_expr("arg", convert, ignored) { 243 | convert.update(range); 244 | arg = Some(val); 245 | } else if let Ok((range, v)) = parse_expr("res", convert, ignored) { 246 | convert.update(range); 247 | res = Some(v); 248 | } else { 249 | let range = convert.ignore(); 250 | convert.update(range); 251 | ignored.push(range); 252 | } 253 | } 254 | 255 | let f = f.ok_or(())?; 256 | let arg = arg.ok_or(())?; 257 | let res = res.ok_or(())?; 258 | Ok((convert.subtract(start), Expr::Has( 259 | Box::new(Expr::App(Box::new(f), Box::new(arg))), 260 | Box::new(res) 261 | ))) 262 | } 263 | 264 | fn parse_eq( 265 | node: &str, 266 | mut convert: Convert, 267 | ignored: &mut Vec 268 | ) -> Result<(Range, Expr), ()> { 269 | let start = convert; 270 | let start_range = convert.start_node(node)?; 271 | convert.update(start_range); 272 | 273 | let mut f: Option> = None; 274 | let mut arg: Option> = None; 275 | let mut res: Option> = None; 276 | loop { 277 | if let Ok(range) = convert.end_node(node) { 278 | convert.update(range); 279 | break; 280 | } else if let Ok((range, val)) = parse_expr("f", convert, ignored) { 281 | convert.update(range); 282 | f = Some(val); 283 | } else if let Ok((range, val)) = parse_expr("arg", convert, ignored) { 284 | convert.update(range); 285 | arg = Some(val); 286 | } else if let Ok((range, v)) = parse_expr("res", convert, ignored) { 287 | convert.update(range); 288 | res = Some(v); 289 | } else { 290 | let range = convert.ignore(); 291 | convert.update(range); 292 | ignored.push(range); 293 | } 294 | } 295 | 296 | let f = f.ok_or(())?; 297 | let arg = arg.ok_or(())?; 298 | let res = res.ok_or(())?; 299 | Ok((convert.subtract(start), Expr::Eq( 300 | Box::new(Expr::App(Box::new(f), Box::new(arg))), 301 | Box::new(res) 302 | ))) 303 | } 304 | 305 | fn parse_neq( 306 | node: &str, 307 | mut convert: Convert, 308 | ignored: &mut Vec 309 | ) -> Result<(Range, Expr), ()> { 310 | let start = convert; 311 | let start_range = convert.start_node(node)?; 312 | convert.update(start_range); 313 | 314 | let mut left: Option> = None; 315 | let mut right: Option> = None; 316 | loop { 317 | if let Ok(range) = convert.end_node(node) { 318 | convert.update(range); 319 | break; 320 | } else if let Ok((range, val)) = parse_expr("left", convert, ignored) { 321 | convert.update(range); 322 | left = Some(val); 323 | } else if let Ok((range, val)) = parse_expr("right", convert, ignored) { 324 | convert.update(range); 325 | right = Some(val); 326 | } else { 327 | let range = convert.ignore(); 328 | convert.update(range); 329 | ignored.push(range); 330 | } 331 | } 332 | 333 | let left = left.ok_or(())?; 334 | let right = right.ok_or(())?; 335 | Ok((convert.subtract(start), Expr::Neq( 336 | Box::new(left), 337 | Box::new(right) 338 | ))) 339 | } 340 | 341 | fn parse_role_of( 342 | node: &str, 343 | mut convert: Convert, 344 | ignored: &mut Vec 345 | ) -> Result<(Range, Expr), ()> { 346 | let start = convert; 347 | let start_range = convert.start_node(node)?; 348 | convert.update(start_range); 349 | 350 | let mut arg: Option> = None; 351 | let mut role: Option> = None; 352 | loop { 353 | if let Ok(range) = convert.end_node(node) { 354 | convert.update(range); 355 | break; 356 | } else if let Ok((range, val)) = parse_expr("arg", convert, ignored) { 357 | convert.update(range); 358 | arg = Some(val); 359 | } else if let Ok((range, v)) = parse_expr("role", convert, ignored) { 360 | convert.update(range); 361 | role = Some(v); 362 | } else { 363 | let range = convert.ignore(); 364 | convert.update(range); 365 | ignored.push(range); 366 | } 367 | } 368 | 369 | let arg = arg.ok_or(())?; 370 | let role = role.ok_or(())?; 371 | Ok((convert.subtract(start), Expr::RoleOf( 372 | Box::new(arg), 373 | Box::new(role) 374 | ))) 375 | } 376 | 377 | fn parse_amb_role( 378 | node: &str, 379 | mut convert: Convert, 380 | ignored: &mut Vec 381 | ) -> Result<(Range, Expr), ()> { 382 | let start = convert; 383 | let start_range = convert.start_node(node)?; 384 | convert.update(start_range); 385 | 386 | let mut a: Option> = None; 387 | let mut b1: Option> = None; 388 | let mut b2: Option> = None; 389 | loop { 390 | if let Ok(range) = convert.end_node(node) { 391 | convert.update(range); 392 | break; 393 | } else if let Ok((range, val)) = parse_expr("a", convert, ignored) { 394 | convert.update(range); 395 | a = Some(val); 396 | } else if let Ok((range, val)) = parse_expr("b1", convert, ignored) { 397 | convert.update(range); 398 | b1 = Some(val); 399 | } else if let Ok((range, val)) = parse_expr("b2", convert, ignored) { 400 | convert.update(range); 401 | b2 = Some(val); 402 | } else { 403 | let range = convert.ignore(); 404 | convert.update(range); 405 | ignored.push(range); 406 | } 407 | } 408 | 409 | let a = a.ok_or(())?; 410 | let b1 = b1.ok_or(())?; 411 | let b2 = b2.ok_or(())?; 412 | Ok((convert.subtract(start), ambiguous_role(a, b1, b2))) 413 | } 414 | 415 | fn parse_amb_rel( 416 | node: &str, 417 | mut convert: Convert, 418 | ignored: &mut Vec 419 | ) -> Result<(Range, Expr), ()> { 420 | let start = convert; 421 | let start_range = convert.start_node(node)?; 422 | convert.update(start_range); 423 | 424 | let mut a: Option> = None; 425 | let mut b1: Option> = None; 426 | let mut b2: Option> = None; 427 | loop { 428 | if let Ok(range) = convert.end_node(node) { 429 | convert.update(range); 430 | break; 431 | } else if let Ok((range, val)) = parse_expr("a", convert, ignored) { 432 | convert.update(range); 433 | a = Some(val); 434 | } else if let Ok((range, val)) = parse_expr("b1", convert, ignored) { 435 | convert.update(range); 436 | b1 = Some(val); 437 | } else if let Ok((range, val)) = parse_expr("b2", convert, ignored) { 438 | convert.update(range); 439 | b2 = Some(val); 440 | } else { 441 | let range = convert.ignore(); 442 | convert.update(range); 443 | ignored.push(range); 444 | } 445 | } 446 | 447 | let a = a.ok_or(())?; 448 | let b1 = b1.ok_or(())?; 449 | let b2 = b2.ok_or(())?; 450 | Ok((convert.subtract(start), ambiguous_rel(a, b1, b2))) 451 | } 452 | 453 | fn parse_rel( 454 | node: &str, 455 | mut convert: Convert, 456 | ignored: &mut Vec 457 | ) -> Result<(Range, Expr), ()> { 458 | let start = convert; 459 | let start_range = convert.start_node(node)?; 460 | convert.update(start_range); 461 | 462 | let mut a: Option> = None; 463 | let mut b: Option> = None; 464 | loop { 465 | if let Ok(range) = convert.end_node(node) { 466 | convert.update(range); 467 | break; 468 | } else if let Ok((range, val)) = parse_expr("a", convert, ignored) { 469 | convert.update(range); 470 | a = Some(val); 471 | } else if let Ok((range, val)) = parse_expr("b", convert, ignored) { 472 | convert.update(range); 473 | b = Some(val); 474 | } else { 475 | let range = convert.ignore(); 476 | convert.update(range); 477 | ignored.push(range); 478 | } 479 | } 480 | 481 | let a = a.ok_or(())?; 482 | let b = b.ok_or(())?; 483 | Ok((convert.subtract(start), Expr::Rel( 484 | Box::new(a), 485 | Box::new(b) 486 | ))) 487 | } 488 | 489 | fn parse_expr( 490 | node: &str, 491 | mut convert: Convert, 492 | ignored: &mut Vec 493 | ) -> Result<(Range, Expr), ()> { 494 | let start = convert; 495 | let start_range = convert.start_node(node)?; 496 | convert.update(start_range); 497 | 498 | let mut expr: Option> = None; 499 | loop { 500 | if let Ok(range) = convert.end_node(node) { 501 | convert.update(range); 502 | break; 503 | } else if let Ok((range, val)) = parse_role_of("role_of", convert, ignored) { 504 | convert.update(range); 505 | expr = Some(val); 506 | } else if let Ok((range, val)) = parse_rel("rel", convert, ignored) { 507 | convert.update(range); 508 | expr = Some(val); 509 | } else if let Ok((range, val)) = parse_sym_or_var("sym", convert, ignored) { 510 | convert.update(range); 511 | expr = Some(val); 512 | } else if let Ok((range, val)) = parse_uniq("uniq", convert, ignored) { 513 | convert.update(range); 514 | expr = Some(val); 515 | } else if let Ok((range, val)) = parse_ava("ava", convert, ignored) { 516 | convert.update(range); 517 | expr = Some(val); 518 | } else if let Ok((range, val)) = parse_inner("inner", convert, ignored) { 519 | convert.update(range); 520 | expr = Some(val); 521 | } else if let Ok((range, val)) = parse_app("app", convert, ignored) { 522 | convert.update(range); 523 | expr = Some(val); 524 | } else if let Ok((range, val)) = parse_eq("eq", convert, ignored) { 525 | convert.update(range); 526 | expr = Some(val); 527 | } else if let Ok((range, val)) = parse_neq("neq", convert, ignored) { 528 | convert.update(range); 529 | expr = Some(val); 530 | } else if let Ok((range, val)) = parse_has("has", convert, ignored) { 531 | convert.update(range); 532 | expr = Some(val); 533 | } else if let Ok((range, val)) = parse_amb_rel("amb_rel", convert, ignored) { 534 | convert.update(range); 535 | expr = Some(val); 536 | } else if let Ok((range, val)) = parse_amb_role("amb_role", convert, ignored) { 537 | convert.update(range); 538 | expr = Some(val); 539 | } else if let Ok((range, val)) = parse_rule("rule", convert, ignored) { 540 | convert.update(range); 541 | expr = Some(val); 542 | } else if let Ok((range, val)) = convert.meta_bool("amb") { 543 | convert.update(range); 544 | expr = Some(Ambiguity(val)); 545 | } else { 546 | let range = convert.ignore(); 547 | convert.update(range); 548 | ignored.push(range); 549 | } 550 | } 551 | 552 | let expr = expr.ok_or(())?; 553 | Ok((convert.subtract(start), expr)) 554 | } 555 | 556 | fn parse_data( 557 | node: &str, 558 | mut convert: Convert, 559 | ignored: &mut Vec, 560 | parent: &Path 561 | ) -> Result<(Range, Vec>), ()> { 562 | let start = convert; 563 | let start_range = convert.start_node(node)?; 564 | convert.update(start_range); 565 | 566 | let mut res: Vec> = vec![]; 567 | let mut eval: Option = None; 568 | let mut role: Option> = None; 569 | loop { 570 | if let Ok(range) = convert.end_node(node) { 571 | convert.update(range); 572 | break; 573 | } else if let Ok((range, val)) = parse_expr("expr", convert, ignored) { 574 | convert.update(range); 575 | let val = if let Some(eval) = eval.as_ref() { 576 | let top = true; 577 | val.eval_lift(eval, top) 578 | } else { 579 | val 580 | }; 581 | res.push(val); 582 | } else if let Ok((range, val)) = convert.meta_string("eval") { 583 | convert.update(range); 584 | eval = Some(val.into()); 585 | } else if let Ok((range, val)) = convert.meta_bool("no_eval") { 586 | convert.update(range); 587 | if val {eval = None}; 588 | } else if let Ok((range, val)) = convert.meta_string("import") { 589 | convert.update(range); 590 | match parse(parent.join(&**val)) { 591 | Ok(facts) => res.extend(facts), 592 | Err(err) => println!("ERROR:\n{}", err), 593 | } 594 | } else if let Ok((range, val)) = parse_expr("role", convert, ignored) { 595 | convert.update(range); 596 | role = Some(val); 597 | } else if let Ok((range, val)) = parse_expr("arg", convert, ignored) { 598 | convert.update(range); 599 | if let Some(role) = role.as_ref() { 600 | res.push(RoleOf(Box::new(val), Box::new(role.clone().into()))); 601 | } 602 | } else { 603 | let range = convert.ignore(); 604 | convert.update(range); 605 | ignored.push(range); 606 | } 607 | } 608 | 609 | Ok((convert.subtract(start), res)) 610 | } 611 | 612 | /// Parses a string. 613 | pub fn parse_str( 614 | data: &str, 615 | parent: &Path 616 | ) -> Result>, String> { 617 | use piston_meta::{parse_errstr, syntax_errstr}; 618 | 619 | let syntax_src = include_str!("../assets/syntax.txt"); 620 | let syntax = syntax_errstr(syntax_src)?; 621 | 622 | let mut meta_data = vec![]; 623 | parse_errstr(&syntax, &data, &mut meta_data)?; 624 | 625 | // piston_meta::json::print(&meta_data); 626 | 627 | let convert = Convert::new(&meta_data); 628 | let mut ignored = vec![]; 629 | match parse_data("data", convert, &mut ignored, parent) { 630 | Err(()) => Err("Could not convert meta data".into()), 631 | Ok((_, expr)) => Ok(expr), 632 | } 633 | } 634 | 635 | /// Helps specifying the parse data type. 636 | pub type ParseData> = Vec>; 637 | /// Helps specifying the type of the parsing result. 638 | pub type ParseResult> = Result, String>; 639 | 640 | /// Parses a source file. 641 | pub fn parse(source: P) -> ParseResult 642 | where P: AsRef, T: Symbol 643 | { 644 | use std::fs::File; 645 | use std::io::Read; 646 | 647 | let source = source.as_ref(); 648 | let mut data_file = File::open(source).map_err(|err| 649 | format!("Could not open `{:?}`, {}", source, err))?; 650 | let mut data = String::new(); 651 | let parent = if let Some(dir) = source.parent() { 652 | dir 653 | } else { 654 | return Err("Could not get parent directory of file".into()); 655 | }; 656 | data_file.read_to_string(&mut data).unwrap(); 657 | parse_str(&data, &parent) 658 | } 659 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate avalog; 2 | 3 | fn check(facts: &'static str, goals: &'static str) { 4 | use avalog::{solve_with_accelerator, parse, infer, Accelerator, ParseData}; 5 | 6 | let facts: ParseData = parse(facts).unwrap(); 7 | let goals = parse(goals).unwrap(); 8 | assert!(solve_with_accelerator( 9 | &facts, 10 | &goals, 11 | None, 12 | &[], 13 | &[], 14 | infer, 15 | &mut Accelerator::new() 16 | ).1.is_ok()); 17 | } 18 | 19 | fn fail(facts: &'static str, goals: &'static str) { 20 | use avalog::{solve_with_accelerator, parse, infer, Accelerator, ParseData}; 21 | 22 | let facts: ParseData = parse(facts).unwrap(); 23 | let goals = parse(goals).unwrap(); 24 | assert!(solve_with_accelerator( 25 | &facts, 26 | &goals, 27 | None, 28 | &[], 29 | &[], 30 | infer, 31 | &mut Accelerator::new() 32 | ).1.is_err()); 33 | } 34 | 35 | #[test] 36 | fn capital() { 37 | check("source/capital.txt", "source/capital-0.txt"); 38 | } 39 | 40 | #[test] 41 | fn category() { 42 | check("source/category.txt", "source/category-0.txt"); 43 | } 44 | 45 | #[test] 46 | fn chu_space() { 47 | check("source/chu_space.txt", "source/no_amb.txt"); 48 | } 49 | 50 | #[test] 51 | fn assocativity() { 52 | check("source/associativity.txt", "source/associativity-0.txt"); 53 | } 54 | 55 | #[test] 56 | fn bool_alg() { 57 | check("source/bool_alg.txt", "source/bool_alg-0.txt"); 58 | } 59 | 60 | #[test] 61 | fn squares() { 62 | check("source/squares.txt", "source/squares-0.txt"); 63 | check("source/squares2.txt", "source/squares2-0.txt"); 64 | check("source/squares3.txt", "source/squares3-0.txt"); 65 | check("source/squares4.txt", "source/squares4-0.txt"); 66 | } 67 | 68 | #[test] 69 | fn convert_unique_has_into_eq() { 70 | check("source/convert_has_into_eq.txt", "source/convert_has_into_eq-0.txt"); 71 | fail("source/convert_has_into_eq2.txt", "source/convert_has_into_eq2-0.txt"); 72 | check("source/convert_has_into_eq3.txt", "source/convert_has_into_eq3-0.txt"); 73 | check("source/convert_has_into_eq4.txt", "source/convert_has_into_eq4-0.txt"); 74 | check("source/convert_has_into_eq5.txt", "source/convert_has_into_eq5-0.txt"); 75 | fail("source/convert_has_into_eq6.txt", "source/convert_has_into_eq6-0.txt"); 76 | check("source/convert_has_into_eq6.txt", "source/convert_has_into_eq6-1.txt"); 77 | } 78 | 79 | #[test] 80 | fn convert_eq_into_has() { 81 | check("source/convert_eq_into_has.txt", "source/convert_eq_into_has-0.txt"); 82 | } 83 | 84 | #[test] 85 | fn copy() { 86 | check("source/copy.txt", "source/copy-0.txt"); 87 | } 88 | 89 | #[test] 90 | fn role_lift() { 91 | check("source/role_lift_app.txt", "source/role_lift_app-0.txt"); 92 | check("source/role_lift_ava.txt", "source/role_lift_ava-0.txt"); 93 | check("source/role_lift_inner.txt", "source/role_lift_inner-0.txt"); 94 | check("source/role_lift_eq.txt", "source/role_lift_eq-0.txt"); 95 | } 96 | 97 | #[test] 98 | fn amb_fail() { 99 | fail("source/amb_fail.txt", "source/amb.txt"); 100 | } 101 | 102 | #[test] 103 | fn amb_success() { 104 | check("source/amb_success.txt", "source/amb.txt"); 105 | check("source/amb_success-2.txt", "source/amb.txt"); 106 | } 107 | 108 | #[test] 109 | fn app_match() { 110 | fail("source/app_match.txt", "source/amb.txt"); 111 | } 112 | 113 | #[test] 114 | fn string() { 115 | check("source/string.txt", "source/string.txt"); 116 | } 117 | 118 | #[test] 119 | fn app_empty() { 120 | check("source/app_empty.txt", "source/app_empty.txt"); 121 | } 122 | 123 | --------------------------------------------------------------------------------