Repository: fogfish/datalog Branch: master Commit: 6e302ac4a7ba Files: 24 Total size: 114.4 KB Directory structure: gitextract_dy2df2ny/ ├── .gitignore ├── .travis.yml ├── Emakefile ├── LICENSE ├── Makefile ├── README.md ├── doc/ │ └── syntax.md ├── erlang.mk ├── priv/ │ ├── imdb.config │ └── imdb.nt ├── rebar.config ├── rebar.config.script ├── src/ │ ├── datalog.app.src │ ├── datalog.erl │ ├── datalog.hrl │ ├── datalog_lang.erl │ ├── datalog_leex.xrl │ ├── datalog_list.erl │ ├── datalog_q.erl │ ├── datalog_vm.erl │ └── datalog_yeec.yrl └── test/ ├── datalog_SUITE.erl ├── datalog_parser_SUITE.erl └── tests.config ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.*~ *.log *.aux *.beam *.dump *.tag.gz *.tgz _build/ tests/ rebar3 *_leex.erl *_yeec.erl *.sublime-* ================================================ FILE: .travis.yml ================================================ language: erlang dist: trusty script: - make - make test - ./rebar3 coveralls send otp_release: - 21.2 - 20.3 - 19.3 - 18.3 ================================================ FILE: Emakefile ================================================ {"src/*", [ report, verbose, {i, "include"}, {outdir, "_build/default/lib/datalog/ebin"}, debug_info ]}. ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: Makefile ================================================ ## ## @doc ## an example Makefile to build and ship erlang software ## ## APP - identity of the application ## ORG - identity of the organization ## URI - identity of the docker repository with last / APP = datalog ORG = fogfish URI = include erlang.mk ================================================ FILE: README.md ================================================ # datalog Datalog is a query language based on the logic programming paradigm. The library is designed to formalize relation of n-ary streams. It implements an ad-hoc query engine using simplified version of general _logic programming_ paradigm. The library facilitates development of data integration, information exchange and semantic web applications. [![Build Status](https://secure.travis-ci.org/fogfish/datalog.svg?branch=master)](http://travis-ci.org/fogfish/datalog) [![Coverage Status](https://coveralls.io/repos/github/fogfish/datalog/badge.svg?branch=master)](https://coveralls.io/github/fogfish/datalog?branch=master) [![Hex.pm](https://img.shields.io/hexpm/v/datalog.svg)](https://hex.pm/packages/datalog) [![Hex Downloads](https://img.shields.io/hexpm/dt/datalog.svg)](https://hex.pm/packages/datalog) ## Key features * Top-down, sub-query, breath-first evaluation algorithm of logical program * Erlang native interface to describe relation of streams * Formalizes streams relation using human readable language * Supports conjunctions, unions and recursion ### Background The logic program consists of finite set of rules and large volume of ground facts -- knowledge. The rules are used to deduce new facts from other facts (built new relations). The [Horn clauses](https://en.wikipedia.org/wiki/Horn_clause) formally defines rules (first-order formula) ``` p₀(ẋ₀) :- p₁(ẋ₁) ^ ... ^ pₙ(ẋₙ). ``` `p₀` is a rule head, it is a producer of new relation (facts). The body is a conjunction of predicates. Each `pᵢ` is a predicate expression consist of predicate symbol and terms such as `p(t₁ , ... , tₙ)`, terms are either a literal constant or a variable. The predicate expression refers to relation of arbitrary arity - stream of tuples; terms range over this stream of tuples. Body predicates refers either to derived relations or ground facts. The predicates with common variables give rise to join. Ground facts are physically stored in external memory and accesses using streams abstractions. A head is a new derived relation, deducted through the _logical program_ (body of horn clause) and ground facts. It is not explicitly persisted anywhere and corresponds the relation view (projection). The materialization of these view is the main task of this library. A naive example, a new relation `about` is deducted from two relations `category` and `article`. ``` about(title, subject) :- category(x, subject), article(title, x). ``` ### σ function The library uses a "functional" interpretation of predicates, any predicate is a function -- sigma expression. It associates some of its bound terms to the remaining ones, returning the lazy set of tuples corresponding to materialized predicate. For example if p is binary predicate, its σ function is denoted as ``` σ(S) -> { ẋ ∈ S ^ p(ẋ) } ``` The library translate goals of rules into algebraic queries with an objective to access the minimum of ground facts needed in order to determine the answer. Rules are compiled to composition of σ functions (sub-queries). They are recursively expanded and the evaluation of the current sub-query is postponed until the new sub-query has been completely solved. The defined sigma expression formalism translates purely declarative semantic into operational semantic, i.e. specify of query must be executed. The lazy set ensures simplicity of one-tuple-at-a-time evaluation strategy while preserving efficiency of set-oriented methods used by high-level query languages. The sigma function is the formalism to relate logic program to ground facts persisted by external storage (most common query languages, access methodologies, i/o interfaces). The library uses sigma algebra to evaluate logic program but it requires developers to implement corresponding access protocols supported by external storage. This is an abstraction interface to retrieve ground facts _matching_ predicate. The library hides the concerns of logical program evaluation but provides hooks to implement access protocols. ## Getting started The latest version of the library is available at its `master` branch. All development, including new features and bug fixes, take place on the `master` branch using forking and pull requests as described in contribution guidelines. ### Installation The stable library release is available via hex packages, add the library as dependency to `rebar.config` ```erlang {deps, [{datalog}]}. ``` ### Usage The library requires implementation of streaming interface to fetch a ground facts from external storage. It provides a [reference implementation](src/datalog_lists.erl) to deal with lists. Let's consider usage example of library using [movies dataset](priv/imdb.config) and human readable [datalog](doc/syntax.md). Build library and run the development console ```bash make && make run ``` The typical usage scenario **parse**, **compile** and **evaluate**. ```erlang %% parse query Q = datalog:p("?- h(_, _). f(x,y). h(x,y) :- f(x,y), y > 1."). %% compile query E = datalog:c(datalog_list, Q). %% evaluate query S = datalog:q(E, [{a, 1}, {b, 2}, {c, 3}]). %% [ %% [b,2], [c,3] %% ] stream:list(S). ``` Let's consider a complex scenarios with a reference dataset about movies. ```erlang {ok, Imdb} = file:consult("./priv/imdb.config"). ``` #### Basic queries Match a person from dataset using query goals ```erlang %% %% define a query goal to match a person with `name` equal to `Ridley Scott`. %% An identity rule is used to produce stream of tuples Q = "?- h(_, \"name\", \"Ridley Scott\"). h(s, p, o) :- f(s, p, o).". %% %% parse and compile a query into executable function F = datalog:c(datalog_list, datalog:p(Q)). %% %% apply the function to dataset and materialize a stream of tuple, it returns %% [ %% [<<"urn:person:137">>,<<"name">>,<<"Ridley Scott">>] %% ] stream:list(F(Imdb)). ``` #### Data patterns Match a person from dataset using patterns: literals and guards ```erlang Q = " ?- h(_, _). f(s, p, o). h(s, o) :- f(s, \"name\", o), o = \"Ridley Scott\". ". %% %% [ %% [<<"urn:person:137">>,<<"Ridley Scott">>] %% ] F = datalog:c(datalog_list, datalog:p(Q)). stream:list(F(Imdb)). ``` Discover all movies produces in 1987 ```erlang Q = " ?- h(_, _). f(s, p, o). h(s, title) :- f(s, \"year\", 1987), f(s, \"title\", title). ". %% %% [ %% [<<"urn:movie:202">>,<<"Predator">>], %% [<<"urn:movie:203">>,<<"Lethal Weapon">>], %% [<<"urn:movie:204">>,<<"RoboCop">>] %% ] F = datalog:c(datalog_list, datalog:p(Q)). stream:list(F(Imdb)). ``` Discover all actors of "Lethal Weapon" movie. ```erlang Q = " ?- h(_). f(s, p, o). h(name) :- f(m, \"title\", \"Lethal Weapon\"), f(m, \"cast\", p), f(p, \"name\", name). ". %% %% [ %% [<<"Mel Gibson">>], %% [<<"Danny Glover">>], %% [<<"Gary Busey">>] %% ] F = datalog:c(datalog_list, datalog:p(Q)). stream:list(F(Imdb)). ``` #### Predicates Discover all movies produced before 1984 ```erlang Q = " ?- h(_, _). f(s, p, o). h(title, year) :- f(s, \"year\", year), f(s, \"title\", title), year < 1984. ". %% %% [ %% [<<"First Blood">>,1982], %% [<<"Alien">>,1979], %% [<<"Mad Max">>,1979], %% [<<"Mad Max 2">>,1981] %% ] F = datalog:c(datalog_list, datalog:p(Q)). stream:list(F(Imdb)). ``` ### Design custom σ function σ function is a partial application takes terms and side-effect environment, it returns a stream on tuples matching the terms pattern. As an example, the following sigma function takes two terms (2-arity) and returns corresponding stream of tuples. The library uses list `[_]` as data structure for tuples. It allows efficiently bind deducted values to term variable. Thus, each sigma function return stream (lazy list) of lists. ```erlang f(_, _, [X, Y]) -> fun(Env) -> stream:build(...) end. ``` Each term takes one of the following types `undefined | [filter()] | literal()`: * `undefined` terms position corresponds to free variable, streams output at this position will be bound to corresponding variable at datalog expression. * `[filter()]` defines acceptable range of term values * `literal()` defines exact matching of term at stream ## Reference 1. [What You Always Wanted to Know About Datalog (And Never Dared to Ask)](https://pdfs.semanticscholar.org/9374/f0da312f3ba77fa840071d68935a28cba364.pdf) 1. [Theory of Relational Databases](http://www.cs.nott.ac.uk/~psznza/G53RDB07/rdb14.pdf) 1. [Foundation of Database](https://wiki.epfl.ch/provenance2011/documents/foundations%20of%20databases-abiteboul-1995.pdf) 1. [Recursive Programming in Datalog](http://infolab.stanford.edu/~ullman/dscb/dl-old.pdf) 1. [Datalog and Recursive Query Processing](http://blogs.evergreen.edu/sosw/files/2014/04/Green-Vol5-DBS-017.pdf) 1. http://ion.uwinnipeg.ca/~ychen2/journalpapers/StratifiedDB.pdf 1. http://www.cs.toronto.edu/~drosu/csc343-l7-handout6.pdf ## License Copyright 2014 Dmitry Kolesnikov Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: doc/syntax.md ================================================ # datalog syntax > Datalog is a declarative logic programming language that syntactically is a subset of Prolog. It is often used as a query language for deductive databases... Datalog is not Turing complete, and is thus used as a domain-specific language. Unlike in Prolog, Datalog queries on finite sets are guaranteed to terminate. > https://en.wikipedia.org/wiki/Datalog This document depicts a datalog syntax supported by the library and it extensions that implements enhancement for Semantic Web and bindings with Erlang runtime. The datalog program consist of finite set of rules and references to ground facts, which are stored in external memory. The rules are used to deduce new facts from other facts. It is important to understand: * there are no functions symbols in datalog, each predicate symbol refers to relation of arbitrary arity. * datalog has a purely declarative semantics - the order of clauses is irrelevant. ## Rules The [Horn clauses](https://en.wikipedia.org/wiki/Horn_clause) formally defines datalog rules (first-order formula) ``` p₀(ẋ₀) :- p₁(ẋ₁), ... , pₙ(ẋₙ). ``` `p₀` is a rule head, which represents a newly derived relation, deducted through horn clauses evaluation. The body is a conjunction of predicates `p₁ , ... , pₙ`. Each predicate refers to either derived or ground relation of arity equals to `|ẋ|`. The language support primitive predicates using infix notation, e.g. equality predicate `x = 1`. `ẋ₀ , ... , ẋₙ` are vector of variables and constants. Every variable of `ẋ₀` must occur in `ẋ₁ , ... , ẋₙ` so that rule is range restricted. The predicates with common variables give rise to join of relations. Vector `|ẋ|` facilitates predicate to map a relation from domain Dⁿ to boolean values so that program is able to holds in each relation that obeys rules. A datalog program is a finite set of rules. Example of rules ``` h(x, z) :- a(x, y), b(y, z). ``` ## Constants Constants are predicate terms that do not change its value during the evaluation of program. Constants facilitate in pattern matching of ground relations while evaluating a programs. The library uses [semantic types](https://github.com/fogfish/semantic/blob/master/doc/datatype.md) to express constants. The usage of semantic types improves ambiguous translations of string literals to type system used at external memory. Data type | Syntax | Example --- | --- | --- xsd:anyURI | `<.+>` | `` || `[a-zA-Z_@]+:[a-zA-Z_@]+` | `example:a` xsd:string | `".*"` | `"example text"` || `".*"@en` | `"example text"@en` xsd:integer | `-?\d+` | `123` || `"-?\d+"^^xsd:integer` | `"123"^^xsd:integer` xsd:decimal | `-?\d+.\d+` | `12.3` || `"-?\d+.\d+"^^xsd:decimal` | `"12.3"^^xsd:decimal` xsd:boolean | `true\|false` | `true` xsd:datetime | ISO8601 | `2007-04-05T14:30:00Z` xsd:date | ISO8601 | `2007-04-05` xsd:time | ISO8601 | `14:30:00Z` xsd:yearmonth | ISO8601 | `2007-04` xsd:monthdate | ISO8601 | `--04-05` The library also implements container data types (tuples, maps, lists) usable as constants Data type | Syntax | Example --- | --- | --- rdf:seq | `( ... )` | `(154, 7623, 23)` rdf:map | `{ ... }` | `{type:"Point", coordinates:[21.3, 60.2]}` rdf:list| `[ ... ]` | `[1, "a", 3.2]` Example of constants ``` h(x) :- a("example", x), b(x, 10.0). h(x) :- a(, x), b(x, y), x = example:a. ``` ## Variables Variable consists of all finite alphanumeric characters and digits beginning with an upper case letter, says datalog syntax. The library relaxes an upper case requirement for variables due to internal AST representation where variable becomes an atom. Variable are used to lift positional values of relation to a new one, and so on. The library support JSON-LD flavored syntax for variable name e.g. `@id`, `@type`. Sometimes, you need to skip or ignore value of relation, use a black symbol (\_) to mark unused positions. Example of variables ``` h(z) :- a(_, y), b(y, z). h(@id,z) :- a(@id, @type), b(@type, z). ``` ## Predicates > In a logic programming view, the term “predicate” is used as synonym for “relation (name)”. Let's take an example `p₁(x₁ , ... , xₙ)`. `p₁` the name of relation, which is either correspond to ground truths one maintained by external storage or logical one derived while evaluating a program. `x₁ , ... , xₙ` terms either variable or constants. ``` actors(id, name). movies(id, title, year, cast). casting(title, name) :- movies(_, title, year, cast), actors(cast, name), year < 1984. ``` Ground truths relation `actors` consists of tuples `(id, name)`, another ground truth relation `movies` is `(id, title, year, cast)`. The derived relation `casting` joins `movies` and `actors` relations and restricts `movies` to instance where `year` less then 1984. Values of `title` and `name` is lifted to derived relation. **Ground-truths** predicate provides reference to external memory and mapping of external tuples. ``` p( ... ). ``` **Derived relation** ``` p( ... ) :- ... . ``` **Semantic Web** predicates are compact IRIs, its just annotates relations as an instance of a class (rdf:type), nothing more the syntax sugar. ``` schema:thing( ... ) :- foaf:person( ... foaf:name ), foaf:name > "A", foaf:name < "B". ``` **Native** predicates refers to ground truths relations implemented by Erlang functions. The syntax uses dot (.) to separate references to native modules and functions. ``` module.function( ... ). .function( ... ). ``` **Built-in** are native predicates where module definition is by passed, they are implemented by the evaluator. ``` .unique( ... ) // a predicate ensures unique term(s) within the stream .flat( ... ) // a predicate flatmap identity of term(s) over stream .eq( ... ) // a predicate is a boolean predicates over stream terms .ne( ... ) .lt( ... ) .gt( ... ) .le( ... ) .ge( ... ) ``` **Infix** predicates, also know as guards, restricts terms of other relations. They always takes left argument a variable and right argument as constant. ``` x = 10 x > 10 x < 10 x >= 10 x =< 10 x != 10 x in (10, 20, 30) ``` ## Union datalog supports union `a(X,Y) ⋁ b(X,Y)` by ``` h(X,Y) :- a(X,Y) h(X,Y) :- b(X,Y) ``` ## Recursion The following example computes the transitive closure of a graph. ``` t(x,y) :- g(x,y) t(x,y) :- g(x,z), t(z,y). ``` Consider the graph ``` g : (1, 2), (2, 3), (3, 4), (4, 5). ``` Then we have ``` 0 -> t : (1, 2), (2, 3), (3, 4), (4, 5) 1 -> t : (1, 3), (2, 4), (3, 5) 2 -> t : (1, 4), (2, 5) 3 -> t : (1, 5) ``` An other example ``` g : (1, 2), (2, 3), (3, 2). 0 -> t : (1, 2), (2, 3), (3, 2) 1 -> t : (1, 3), (2, 2), (3, 3) ``` ## Aggregations Many applications require the computation of various kinds of summary information over the data. Formally, an aggregate function is a mapping `ƒ` from bags (multi-sets) of domain values to domain values. The library offloads implementation of aggregations to sigma functions (external storage) but it provides a syntax to declare aggregation intent at ground truth predicates: ``` p(id, count "links", category 10 "subject"). ``` ## Goal Goal defines a first rule. ``` ?- h(_, _). ?- h("example", _). ?- h/2. ``` ## Semantic Web compatibility Semantic Web heavily uses IRIs as identifiers. IRIs defines subjects, predicates and sometimes objects. This library implements datalog syntax enhancement that supports absolute and compact IRIs inside queries either as constants or variables. IRIs as constants uses traditional syntax `` or `prefix:suffix` ``` h(x) :- p(x, ). h(x) :- p(x, foaf:name). ``` Only compact IRIs are allowed as variable, they have to be prefixed by `'` character ``` h('rdf:id, 'foaf:name) :- p('rdf:id, 'foaf:name). ``` ================================================ FILE: erlang.mk ================================================ ## ## Copyright (C) 2012 Dmitry Kolesnikov ## ## This Makefile may be modified and distributed under the terms ## of the MIT license. See the LICENSE file for details. ## https://github.com/fogfish/makefile ## ## @doc ## This makefile is the wrapper of rebar to build and ship erlang software ## ## @version 1.0.12 .PHONY: all compile test unit clean distclean run console mock-up mock-rm benchmark release dist APP := $(strip $(APP)) ORG := $(strip $(ORG)) URI := $(strip $(URI)) ## ## config PREFIX ?= /usr/local APP ?= $(notdir $(CURDIR)) ARCH = $(shell uname -m) PLAT ?= $(shell uname -s) VSN ?= $(shell test -z "`git status --porcelain`" && git describe --tags --long | sed -e 's/-g[0-9a-f]*//' | sed -e 's/-0//' || echo "`git describe --abbrev=0 --tags`-dev") LATEST ?= latest REL = ${APP}-${VSN} PKG = ${REL}+${ARCH}.${PLAT} TEST ?= tests COOKIE ?= nocookie DOCKER ?= fogfish/erlang-alpine IID = ${URI}${ORG}/${APP} ## required tools ## - rebar version (no spaces at end) ## - path to basho benchmark REBAR ?= 3.9.0 BB = ../basho_bench ## erlang runtime configration flags ROOT = $(shell pwd) ADDR = localhost.localdomain EFLAGS = \ -name ${APP}@${ADDR} \ -setcookie ${COOKIE} \ -pa ${ROOT}/_build/default/lib/*/ebin \ -pa ${ROOT}/_build/default/lib/*/priv \ -pa ${ROOT}/rel \ -kernel inet_dist_listen_min 32100 \ -kernel inet_dist_listen_max 32199 \ +P 1000000 \ +K true +A 160 -sbt ts ## erlang common test bootstrap BOOT_CT = \ -module(test). \ -export([run/1]). \ run(Spec) -> \ {ok, Test} = file:consult(Spec), \ case lists:keymember(node, 1, Test) of \ false -> \ erlang:halt(element(2, ct:run_test([{spec, Spec}]))); \ true -> \ ct_master:run(Spec), \ erlang:halt(0) \ end. ## BUILDER = FROM ${DOCKER}\nARG VERSION=\nRUN mkdir ${APP}\nCOPY . ${APP}/\nRUN cd ${APP} && make VSN=\x24{VERSION} && make release VSN=\x24{VERSION}\n SPAWNER = FROM ${DOCKER}\nENV VERSION=${VSN}\nRUN mkdir ${APP}\nCOPY . ${APP}/\nRUN cd ${APP} && make VSN=\x24{VERSION} && make release VSN=\x24{VERSION}\nCMD sh -c 'cd ${APP} && make console VSN=\x24{VERSION} RELX_REPLACE_OS_VARS=true ERL_NODE=${APP}'\n ## self extracting bundle archive BUNDLE_INIT = PREFIX=${PREFIX}\nREL=${PREFIX}/${REL}\nAPP=${APP}\nVSN=${VSN}\nLINE=`grep -a -n "BUNDLE:$$" $$0`\nmkdir -p $${REL}\ntail -n +$$(( $${LINE%%%%:*} + 1)) $$0 | gzip -vdc - | tar -C $${REL} -xvf - > /dev/null\n BUNDLE_FREE = exit\nBUNDLE:\n ##################################################################### ## ## build ## ##################################################################### all: rebar3 compile test compile: rebar3 @./rebar3 compile ## ## execute common test and terminate node test: @./rebar3 ct --config=test/${TEST}.config --cover --verbose @./rebar3 cover # test: _build/test.beam # @mkdir -p /tmp/test/${APP} # @erl ${EFLAGS} -noshell -pa _build/ -pa test/ -run test run test/${TEST}.config # @F=`ls /tmp/test/${APP}/ct_run*/all.coverdata | tail -n 1` ;\ # cp $$F /tmp/test/${APP}/ct.coverdata # # _build/test.beam: _build/test.erl # @erlc -o _build $< # # _build/test.erl: # @mkdir -p _build && echo "${BOOT_CT}" > $@ # testclean: @rm -f _build/test.beam @rm -f _build/test.erl @rm -f test/*.beam @rm -rf test.*-temp-data @rm -rf tests ## ## execute unit test unit: all @./rebar3 skip_deps=true eunit ## ## clean clean: testclean dockerclean -@./rebar3 clean @rm -Rf _build/builder @rm -Rf _build/default/rel @rm -rf log @rm -f relx.config @rm -f *.tar.gz @rm -f *.bundle distclean: clean -@make mock-rm -@make dist-rm -@rm -Rf _build -@rm rebar3 ##################################################################### ## ## debug ## ##################################################################### run: @erl ${EFLAGS} console: ${PKG}.tar.gz @_build/default/rel/${APP}/bin/${APP} foreground mock-up: test/mock/docker-compose.yml @docker-compose -f $< up mock-rm: test/mock/docker-compose.yml -@docker-compose -f $< down --rmi all -v --remove-orphans dist-up: docker-compose.yml _build/spawner @docker-compose build @docker-compose -f $< up dist-rm: docker-compose.yml -@rm -f _build/spawner -@docker-compose -f $< down --rmi all -v --remove-orphans benchmark: @echo "==> benchmark: ${TEST}" ;\ $(BB)/basho_bench -N bb@127.0.0.1 -C nocookie priv/${TEST}.benchmark ;\ $(BB)/priv/summary.r -i tests/current ;\ open tests/current/summary.png ##################################################################### ## ## release ## ##################################################################### release: ${PKG}.tar.gz ## assemble VM release ifeq (${PLAT},$(shell uname -s)) ${PKG}.tar.gz: relx.config @./rebar3 tar -n ${APP} -v ${VSN} ;\ mv _build/default/rel/${APP}/${APP}-${VSN}.tar.gz $@ ;\ echo "==> tarball: $@" relx.config: rel/relx.config.src @cat $< | sed "s/release/release, {'${APP}', \"${VSN}\"}/" > $@ else ${PKG}.tar.gz: _build/builder @docker build --file=$< --force-rm=true --build-arg="VERSION=${VSN}" --tag=build/${APP}:latest . ;\ I=`docker create build/${APP}:latest` ;\ docker cp $$I:/${APP}/$@ $@ ;\ docker rm -f $$I ;\ docker rmi build/${APP}:latest ;\ test -f $@ && echo "==> tarball: $@" _build/builder: @mkdir -p _build && echo "${BUILDER}" > $@ endif ## build docker image docker: Dockerfile git status --porcelain test -z "`git status --porcelain`" || exit -1 docker build \ --build-arg APP=${APP} \ --build-arg VSN=${VSN} \ -t ${IID}:${VSN} -f $< . docker tag ${IID}:${VSN} ${IID}:${LATEST} dockerclean: -@docker rmi -f ${IID}:${LATEST} -@docker rmi -f ${IID}:${VSN} _build/spawner: @mkdir -p _build && echo "${SPAWNER}" > $@ dist: ${PKG}.tar.gz ${PKG}.bundle ${PKG}.bundle: rel/bootstrap.sh @printf '${BUNDLE_INIT}' > $@ ;\ cat $< >> $@ ;\ printf '${BUNDLE_FREE}' >> $@ ;\ cat ${PKG}.tar.gz >> $@ ;\ chmod ugo+x $@ ;\ echo "==> bundle: $@" ##################################################################### ## ## dependencies ## ##################################################################### rebar3: @echo "==> install rebar (${REBAR})" ;\ curl -L -O -s https://github.com/erlang/rebar3/releases/download/${REBAR}/rebar3 ;\ chmod +x $@ ================================================ FILE: priv/imdb.config ================================================ {<<"urn:person:100">>, <<"name">>, <<"James Cameron">>}. {<<"urn:person:100">>, <<"born">>, <<"1954-08-16">>}. {<<"urn:person:101">>, <<"name">>, <<"Arnold Schwarzenegger">>}. {<<"urn:person:101">>, <<"born">>, <<"1947-07-30">>}. {<<"urn:person:102">>, <<"name">>, <<"Linda Hamilton">>}. {<<"urn:person:102">>, <<"born">>, <<"1956-09-26">>}. {<<"urn:person:103">>, <<"name">>, <<"Michael Biehn">>}. {<<"urn:person:103">>, <<"born">>, <<"1956-07-31">>}. {<<"urn:person:104">>, <<"name">>, <<"Ted Kotcheff">>}. {<<"urn:person:104">>, <<"born">>, <<"1931-04-07">>}. {<<"urn:person:105">>, <<"name">>, <<"Sylvester Stallone">>}. {<<"urn:person:105">>, <<"born">>, <<"1946-07-06">>}. {<<"urn:person:106">>, <<"name">>, <<"Richard Crenna">>}. {<<"urn:person:106">>, <<"born">>, <<"1926-11-30">>}. {<<"urn:person:106">>, <<"death">>, <<"2003-01-17">>}. {<<"urn:person:107">>, <<"name">>, <<"Brian Dennehy">>}. {<<"urn:person:107">>, <<"born">>, <<"1938-07-09">>}. {<<"urn:person:108">>, <<"name">>, <<"John McTiernan">>}. {<<"urn:person:108">>, <<"born">>, <<"1951-01-08">>}. {<<"urn:person:109">>, <<"name">>, <<"Elpidia Carrillo">>}. {<<"urn:person:109">>, <<"born">>, <<"1961-08-16">>}. {<<"urn:person:110">>, <<"name">>, <<"Carl Weathers">>}. {<<"urn:person:110">>, <<"born">>, <<"1948-01-14">>}. {<<"urn:person:111">>, <<"name">>, <<"Richard Donner">>}. {<<"urn:person:111">>, <<"born">>, <<"1930-04-24">>}. {<<"urn:person:112">>, <<"name">>, <<"Mel Gibson">>}. {<<"urn:person:112">>, <<"born">>, <<"1956-01-03">>}. {<<"urn:person:113">>, <<"name">>, <<"Danny Glover">>}. {<<"urn:person:113">>, <<"born">>, <<"1946-07-22">>}. {<<"urn:person:114">>, <<"name">>, <<"Gary Busey">>}. {<<"urn:person:114">>, <<"born">>, <<"1944-07-29">>}. {<<"urn:person:115">>, <<"name">>, <<"Paul Verhoeven">>}. {<<"urn:person:115">>, <<"born">>, <<"1938-07-18">>}. {<<"urn:person:116">>, <<"name">>, <<"Peter Weller">>}. {<<"urn:person:116">>, <<"born">>, <<"1947-06-24">>}. {<<"urn:person:117">>, <<"name">>, <<"Nancy Allen">>}. {<<"urn:person:117">>, <<"born">>, <<"1950-06-24">>}. {<<"urn:person:118">>, <<"name">>, <<"Ronny Cox">>}. {<<"urn:person:118">>, <<"born">>, <<"1938-07-23">>}. {<<"urn:person:119">>, <<"name">>, <<"Mark L. Lester">>}. {<<"urn:person:119">>, <<"born">>, <<"1946-11-26">>}. {<<"urn:person:120">>, <<"name">>, <<"Rae Dawn Chong">>}. {<<"urn:person:120">>, <<"born">>, <<"1961-02-28">>}. {<<"urn:person:121">>, <<"name">>, <<"Alyssa Milano">>}. {<<"urn:person:121">>, <<"born">>, <<"1972-12-19">>}. {<<"urn:person:122">>, <<"name">>, <<"Bruce Willis">>}. {<<"urn:person:122">>, <<"born">>, <<"1955-03-19">>}. {<<"urn:person:123">>, <<"name">>, <<"Alan Rickman">>}. {<<"urn:person:123">>, <<"born">>, <<"1946-02-21">>}. {<<"urn:person:124">>, <<"name">>, <<"Alexander Godunov">>}. {<<"urn:person:124">>, <<"born">>, <<"1949-11-28">>}. {<<"urn:person:124">>, <<"death">>, <<"1995-05-18">>}. {<<"urn:person:125">>, <<"name">>, <<"Robert Patrick">>}. {<<"urn:person:125">>, <<"born">>, <<"1958-11-05">>}. {<<"urn:person:126">>, <<"name">>, <<"Edward Furlong">>}. {<<"urn:person:126">>, <<"born">>, <<"1977-08-02">>}. {<<"urn:person:127">>, <<"name">>, <<"Jonathan Mostow">>}. {<<"urn:person:127">>, <<"born">>, <<"1961-11-28">>}. {<<"urn:person:128">>, <<"name">>, <<"Nick Stahl">>}. {<<"urn:person:128">>, <<"born">>, <<"1979-12-05">>}. {<<"urn:person:129">>, <<"name">>, <<"Claire Danes">>}. {<<"urn:person:129">>, <<"born">>, <<"1979-04-12">>}. {<<"urn:person:130">>, <<"name">>, <<"George P. Cosmatos">>}. {<<"urn:person:130">>, <<"born">>, <<"1941-01-04">>}. {<<"urn:person:130">>, <<"death">>, <<"2005-04-19">>}. {<<"urn:person:131">>, <<"name">>, <<"Charles Napier">>}. {<<"urn:person:131">>, <<"born">>, <<"1936-04-12">>}. {<<"urn:person:131">>, <<"death">>, <<"2011-10-05">>}. {<<"urn:person:132">>, <<"name">>, <<"Peter MacDonald">>}. {<<"urn:person:133">>, <<"name">>, <<"Marc de Jonge">>}. {<<"urn:person:133">>, <<"born">>, <<"1949-02-16">>}. {<<"urn:person:133">>, <<"death">>, <<"1996-06-06">>}. {<<"urn:person:134">>, <<"name">>, <<"Stephen Hopkins">>}. {<<"urn:person:135">>, <<"name">>, <<"Ruben Blades">>}. {<<"urn:person:135">>, <<"born">>, <<"1948-07-16">>}. {<<"urn:person:136">>, <<"name">>, <<"Joe Pesci">>}. {<<"urn:person:136">>, <<"born">>, <<"1943-02-09">>}. {<<"urn:person:137">>, <<"name">>, <<"Ridley Scott">>}. {<<"urn:person:137">>, <<"born">>, <<"1937-11-30">>}. {<<"urn:person:138">>, <<"name">>, <<"Tom Skerritt">>}. {<<"urn:person:138">>, <<"born">>, <<"1933-08-25">>}. {<<"urn:person:139">>, <<"name">>, <<"Sigourney Weaver">>}. {<<"urn:person:139">>, <<"born">>, <<"1949-10-08">>}. {<<"urn:person:140">>, <<"name">>, <<"Veronica Cartwright">>}. {<<"urn:person:140">>, <<"born">>, <<"1949-04-20">>}. {<<"urn:person:141">>, <<"name">>, <<"Carrie Henn">>}. {<<"urn:person:142">>, <<"name">>, <<"George Miller">>}. {<<"urn:person:142">>, <<"born">>, <<"1945-03-03">>}. {<<"urn:person:143">>, <<"name">>, <<"Steve Bisley">>}. {<<"urn:person:143">>, <<"born">>, <<"1951-12-26">>}. {<<"urn:person:144">>, <<"name">>, <<"Joanne Samuel">>}. {<<"urn:person:145">>, <<"name">>, <<"Michael Preston">>}. {<<"urn:person:145">>, <<"born">>, <<"1938-05-14">>}. {<<"urn:person:146">>, <<"name">>, <<"Bruce Spence">>}. {<<"urn:person:146">>, <<"born">>, <<"1945-09-17">>}. {<<"urn:person:147">>, <<"name">>, <<"George Ogilvie">>}. {<<"urn:person:147">>, <<"born">>, <<"1931-03-05">>}. {<<"urn:person:148">>, <<"name">>, <<"Tina Turner">>}. {<<"urn:person:148">>, <<"born">>, <<"1939-11-26">>}. {<<"urn:person:149">>, <<"name">>, <<"Sophie Marceau">>}. {<<"urn:person:149">>, <<"born">>, <<"1966-11-17">>}. {<<"urn:movie:200">>, <<"title">>, <<"The Terminator">>}. {<<"urn:movie:200">>, <<"year">>, 1984}. {<<"urn:movie:200">>, <<"director">>,<<"urn:person:100">>}. {<<"urn:movie:200">>, <<"cast">>, <<"urn:person:101">>}. {<<"urn:movie:200">>, <<"cast">>, <<"urn:person:102">>}. {<<"urn:movie:200">>, <<"cast">>, <<"urn:person:103">>}. {<<"urn:movie:200">>, <<"sequel">>, <<"urn:movie:207">>}. {<<"urn:movie:201">>, <<"title">>, <<"First Blood">>}. {<<"urn:movie:201">>, <<"year">>, 1982}. {<<"urn:movie:201">>, <<"director">>, <<"urn:person:104">>}. {<<"urn:movie:201">>, <<"cast">>, <<"urn:person:105">>}. {<<"urn:movie:201">>, <<"cast">>, <<"urn:person:106">>}. {<<"urn:movie:201">>, <<"cast">>, <<"urn:person:107">>}. {<<"urn:movie:201">>, <<"sequel">>, <<"urn:movie:209">>}. {<<"urn:movie:202">>, <<"title">>, <<"Predator">>}. {<<"urn:movie:202">>, <<"year">>, 1987}. {<<"urn:movie:202">>, <<"director">>, <<"urn:person:108">>}. {<<"urn:movie:202">>, <<"cast">>, <<"urn:person:101">>}. {<<"urn:movie:202">>, <<"cast">>, <<"urn:person:109">>}. {<<"urn:movie:202">>, <<"cast">>, <<"urn:person:110">>}. {<<"urn:movie:202">>, <<"sequel">>, <<"urn:movie:211">>}. {<<"urn:movie:203">>, <<"title">>, <<"Lethal Weapon">>}. {<<"urn:movie:203">>, <<"year">>, 1987}. {<<"urn:movie:203">>, <<"director">>, <<"urn:person:111">>}. {<<"urn:movie:203">>, <<"cast">>, <<"urn:person:112">>}. {<<"urn:movie:203">>, <<"cast">>, <<"urn:person:113">>}. {<<"urn:movie:203">>, <<"cast">>, <<"urn:person:114">>}. {<<"urn:movie:203">>, <<"sequel">>, <<"urn:movie:212">>}. {<<"urn:movie:204">>, <<"title">>, <<"RoboCop">>}. {<<"urn:movie:204">>, <<"year">>, 1987}. {<<"urn:movie:204">>, <<"director">>, <<"urn:person:115">>}. {<<"urn:movie:204">>, <<"cast">>, <<"urn:person:116">>}. {<<"urn:movie:204">>, <<"cast">>, <<"urn:person:117">>}. {<<"urn:movie:204">>, <<"cast">>, <<"urn:person:118">>}. {<<"urn:movie:205">>, <<"title">>, <<"Commando">>}. {<<"urn:movie:205">>, <<"year">>, 1985}. {<<"urn:movie:205">>, <<"director">>, <<"urn:person:119">>}. {<<"urn:movie:205">>, <<"cast">>, <<"urn:person:101">>}. {<<"urn:movie:205">>, <<"cast">>, <<"urn:person:120">>}. {<<"urn:movie:205">>, <<"cast">>, <<"urn:person:121">>}. {<<"urn:movie:206">>, <<"title">>, <<"Die Hard">>}. {<<"urn:movie:206">>, <<"year">>, 1988}. {<<"urn:movie:206">>, <<"director">>, <<"urn:person:108">>}. {<<"urn:movie:206">>, <<"cast">>, <<"urn:person:122">>}. {<<"urn:movie:206">>, <<"cast">>, <<"urn:person:123">>}. {<<"urn:movie:206">>, <<"cast">>, <<"urn:person:124">>}. {<<"urn:movie:207">>, <<"title">>, <<"Terminator 2: Judgment Day">>}. {<<"urn:movie:207">>, <<"year">>, 1991}. {<<"urn:movie:207">>, <<"director">>, <<"urn:person:100">>}. {<<"urn:movie:207">>, <<"cast">>, <<"urn:person:101">>}. {<<"urn:movie:207">>, <<"cast">>, <<"urn:person:102">>}. {<<"urn:movie:207">>, <<"cast">>, <<"urn:person:125">>}. {<<"urn:movie:207">>, <<"cast">>, <<"urn:person:126">>}. {<<"urn:movie:207">>, <<"sequel">>, <<"urn:movie:208">>}. {<<"urn:movie:208">>, <<"title">>, <<"Terminator 3: Rise of the Machines">>}. {<<"urn:movie:208">>, <<"year">>, 2003}. {<<"urn:movie:208">>, <<"director">>, <<"urn:person:127">>}. {<<"urn:movie:208">>, <<"cast">>, <<"urn:person:101">>}. {<<"urn:movie:208">>, <<"cast">>, <<"urn:person:128">>}. {<<"urn:movie:208">>, <<"cast">>, <<"urn:person:129">>}. {<<"urn:movie:209">>, <<"title">>, <<"Rambo: First Blood Part II">>}. {<<"urn:movie:209">>, <<"year">>, 1985}. {<<"urn:movie:209">>, <<"director">>, <<"urn:person:130">>}. {<<"urn:movie:209">>, <<"cast">>, <<"urn:person:105">>}. {<<"urn:movie:209">>, <<"cast">>, <<"urn:person:106">>}. {<<"urn:movie:209">>, <<"cast">>, <<"urn:person:131">>}. {<<"urn:movie:209">>, <<"sequel">>, <<"urn:movie:210">>}. {<<"urn:movie:210">>, <<"title">>, <<"Rambo III">>}. {<<"urn:movie:210">>, <<"year">>, 1988}. {<<"urn:movie:210">>, <<"director">>, <<"urn:person:132">>}. {<<"urn:movie:210">>, <<"cast">>, <<"urn:person:105">>}. {<<"urn:movie:210">>, <<"cast">>, <<"urn:person:106">>}. {<<"urn:movie:210">>, <<"cast">>, <<"urn:person:133">>}. {<<"urn:movie:211">>, <<"title">>, <<"Predator 2">>}. {<<"urn:movie:211">>, <<"year">>, 1990}. {<<"urn:movie:211">>, <<"director">>, <<"urn:person:134">>}. {<<"urn:movie:211">>, <<"cast">>, <<"urn:person:113">>}. {<<"urn:movie:211">>, <<"cast">>, <<"urn:person:114">>}. {<<"urn:movie:211">>, <<"cast">>, <<"urn:person:135">>}. {<<"urn:movie:212">>, <<"title">>, <<"Lethal Weapon 2">>}. {<<"urn:movie:212">>, <<"year">>, 1989}. {<<"urn:movie:212">>, <<"director">>, <<"urn:person:111">>}. {<<"urn:movie:212">>, <<"cast">>, <<"urn:person:112">>}. {<<"urn:movie:212">>, <<"cast">>, <<"urn:person:113">>}. {<<"urn:movie:212">>, <<"cast">>, <<"urn:person:136">>}. {<<"urn:movie:212">>, <<"sequel">>, <<"urn:movie:213">>}. {<<"urn:movie:213">>, <<"title">>, <<"Lethal Weapon 3">>}. {<<"urn:movie:213">>, <<"year">>, 1992}. {<<"urn:movie:213">>, <<"director">>, <<"urn:person:111">>}. {<<"urn:movie:213">>, <<"cast">>, <<"urn:person:112">>}. {<<"urn:movie:213">>, <<"cast">>, <<"urn:person:113">>}. {<<"urn:movie:213">>, <<"cast">>, <<"urn:person:136">>}. {<<"urn:movie:214">>, <<"title">>, <<"Alien">>}. {<<"urn:movie:214">>, <<"year">>, 1979}. {<<"urn:movie:214">>, <<"director">>, <<"urn:person:137">>}. {<<"urn:movie:214">>, <<"cast">>, <<"urn:person:138">>}. {<<"urn:movie:214">>, <<"cast">>, <<"urn:person:139">>}. {<<"urn:movie:214">>, <<"cast">>, <<"urn:person:140">>}. {<<"urn:movie:214">>, <<"sequel">>, <<"urn:movie:215">>}. {<<"urn:movie:215">>, <<"title">>, <<"Aliens">>}. {<<"urn:movie:215">>, <<"year">>, 1986}. {<<"urn:movie:215">>, <<"director">>, <<"urn:person:100">>}. {<<"urn:movie:215">>, <<"cast">>, <<"urn:person:139">>}. {<<"urn:movie:215">>, <<"cast">>, <<"urn:person:141">>}. {<<"urn:movie:215">>, <<"cast">>, <<"urn:person:103">>}. {<<"urn:movie:216">>, <<"title">>, <<"Mad Max">>}. {<<"urn:movie:216">>, <<"year">>, 1979}. {<<"urn:movie:216">>, <<"director">>, <<"urn:person:142">>}. {<<"urn:movie:216">>, <<"cast">>, <<"urn:person:112">>}. {<<"urn:movie:216">>, <<"cast">>, <<"urn:person:143">>}. {<<"urn:movie:216">>, <<"cast">>, <<"urn:person:144">>}. {<<"urn:movie:216">>, <<"sequel">>, <<"urn:movie:217">>}. {<<"urn:movie:217">>, <<"title">>, <<"Mad Max 2">>}. {<<"urn:movie:217">>, <<"year">>, 1981}. {<<"urn:movie:217">>, <<"director">>, <<"urn:person:142">>}. {<<"urn:movie:217">>, <<"cast">>, <<"urn:person:112">>}. {<<"urn:movie:217">>, <<"cast">>, <<"urn:person:145">>}. {<<"urn:movie:217">>, <<"cast">>, <<"urn:person:146">>}. {<<"urn:movie:217">>, <<"sequel">>, <<"urn:movie:218">>}. {<<"urn:movie:218">>, <<"title">>, <<"Mad Max Beyond Thunderdome">>}. {<<"urn:movie:218">>, <<"year">>, 1985}. {<<"urn:movie:218">>, <<"director">>, <<"urn:person:142">>}. {<<"urn:movie:218">>, <<"director">>, <<"urn:person:147">>}. {<<"urn:movie:218">>, <<"cast">>, <<"urn:person:112">>}. {<<"urn:movie:218">>, <<"cast">>, <<"urn:person:148">>}. {<<"urn:movie:219">>, <<"title">>, <<"Braveheart">>}. {<<"urn:movie:219">>, <<"year">>, 1995}. {<<"urn:movie:219">>, <<"director">>, <<"urn:person:112">>}. {<<"urn:movie:219">>, <<"cast">>, <<"urn:person:112">>}. {<<"urn:movie:219">>, <<"cast">>, <<"urn:person:149">>}. ================================================ FILE: priv/imdb.nt ================================================ "James Cameron"^^ . "1954-08-16"^^ . "Arnold Schwarzenegger"^^ . "1947-07-30"^^ . "Linda Hamilton"^^ . "1956-09-26"^^ . "Michael Biehn"^^ . "1956-07-31"^^ . "Ted Kotcheff"^^ . "1931-04-07"^^ . "Sylvester Stallone"^^ . "1946-07-06"^^ . "Richard Crenna"^^ . "1926-11-30"^^ . "2003-01-17"^^ . "Brian Dennehy"^^ . "1938-07-09"^^ . "John McTiernan"^^ . "1951-01-08"^^ . "Elpidia Carrillo"^^ . "1961-08-16"^^ . "Carl Weathers"^^ . "1948-01-14"^^ . "Richard Donner"^^ . "1930-04-24"^^ . "Mel Gibson"^^ . "1956-01-03"^^ . "Danny Glover"^^ . "1946-07-22"^^ . "Gary Busey"^^ . "1944-07-29"^^ . "Paul Verhoeven"^^ . "1938-07-18"^^ . "Peter Weller"^^ . "1947-06-24"^^ . "Nancy Allen"^^ . "1950-06-24"^^ . "Ronny Cox"^^ . "1938-07-23"^^ . "Mark L. Lester"^^ . "1946-11-26"^^ . "Rae Dawn Chong"^^ . "1961-02-28"^^ . "Alyssa Milano"^^ . "1972-12-19"^^ . "Bruce Willis"^^ . "1955-03-19"^^ . "Alan Rickman"^^ . "1946-02-21"^^ . "Alexander Godunov"^^ . "1949-11-28"^^ . "1995-05-18"^^ . "Robert Patrick"^^ . "1958-11-05"^^ . "Edward Furlong"^^ . "1977-08-02"^^ . "Jonathan Mostow"^^ . "1961-11-28"^^ . "Nick Stahl"^^ . "1979-12-05"^^ . "Claire Danes"^^ . "1979-04-12"^^ . "George P. Cosmatos"^^ . "1941-01-04"^^ . "2005-04-19"^^ . "Charles Napier"^^ . "1936-04-12"^^ . "2011-10-05"^^ . "Peter MacDonald"^^ . "Marc de Jonge"^^ . "1949-02-16"^^ . "1996-06-06"^^ . "Stephen Hopkins"^^ . "Ruben Blades"^^ . "1948-07-16"^^ . "Joe Pesci"^^ . "1943-02-09"^^ . "Ridley Scott"^^ . "1937-11-30"^^ . "Tom Skerritt"^^ . "1933-08-25"^^ . "Sigourney Weaver"^^ . "1949-10-08"^^ . "Veronica Cartwright"^^ . "1949-04-20"^^ . "Carrie Henn"^^ . "George Miller"^^ . "1945-03-03"^^ . "Steve Bisley"^^ . "1951-12-26"^^ . "Joanne Samuel"^^ . "Michael Preston"^^ . "1938-05-14"^^ . "Bruce Spence"^^ . "1945-09-17"^^ . "George Ogilvie"^^ . "1931-03-05"^^ . "Tina Turner"^^ . "1939-11-26"^^ . "Sophie Marceau"^^ . "1966-11-17"^^ . "The Terminator"^^ . "1984"^^ . . . . . . "First Blood"^^ . "1982"^^ . . . . . . "Predator"^^ . "1987"^^ . . . . . . "Lethal Weapon"^^ . "1987"^^ . . . . . . "RoboCop"^^ . "1987"^^ . . . . . "Commando"^^ . "1985"^^ . . . . . "Die Hard"^^ . "1988"^^ . . . . . "Terminator 2: Judgment Day"^^ . "1991"^^ . . . . . . . "Terminator 3: Rise of the Machines"^^ . "2003"^^ . . . . . "Rambo: First Blood Part II"^^ . "1985"^^ . . . . . . "Rambo III"^^ . "1988"^^ . . . . . "Predator 2"^^ . "1990"^^ . . . . . "Lethal Weapon 2"^^ . "1989"^^ . . . . . . "Lethal Weapon 3"^^ . "1992"^^ . . . . . "Alien"^^ . "1979"^^ . . . . . . "Aliens"^^ . "1986"^^ . . . . . "Mad Max"^^ . "1979"^^ . . . . . . "Mad Max 2"^^ . "1981"^^ . . . . . . "Mad Max Beyond Thunderdome"^^ . "1985"^^ . . . . . "Braveheart"^^ . "1995"^^ . . . . ================================================ FILE: rebar.config ================================================ {erl_opts, [ ]}. {deps, [ datum ]}. %% %% {plugins , [coveralls]}. {cover_enabled , true}. {cover_export_enabled , true}. {coveralls_coverdata , "_build/test/cover/ct.coverdata"}. {coveralls_service_name , "travis-ci"}. ================================================ FILE: rebar.config.script ================================================ case os:getenv("TRAVIS") of "true" -> JobId = os:getenv("TRAVIS_JOB_ID"), lists:keystore(coveralls_service_job_id, 1, CONFIG, {coveralls_service_job_id, JobId}); _ -> CONFIG end. ================================================ FILE: src/datalog.app.src ================================================ {application, datalog, [ {description, "datalog is a query language based on the logic programming paradigm"}, {vsn, "git"}, {modules, []}, {registered, []}, {applications,[ kernel ,stdlib ]}, {env, []}, {licenses, ["Apache"]}, {maintainers, ["Dmitry Kolesnikov"]}, {links, [ {"GitHub", "https://github.com/fogfish/datalog"} ]} ] }. ================================================ FILE: src/datalog.erl ================================================ %% %% Copyright 2014 - 2016 Dmitry Kolesnikov, All Rights Reserved %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% @doc %% datalog evaluator -module(datalog). -include("datalog.hrl"). %% %% datalog interface -export([ t/0, q/2, p/1, c/2, c/3, % schema/1, filter/1, takewhile/2 ]). -export_type([q/0, eval/0, heap/0, predicate/0]). %%%---------------------------------------------------------------------------- %%% %%% data types %%% %%%---------------------------------------------------------------------------- %% datalog query is a set of horn clauses -type q() :: #{horn() => [head() | body()]}. % -type head() :: [atom()]. % -type body() :: [predicate()]. -type horn() :: atom(). %% pattern is unit of work to access ground facts persisted in external storage. %% sigma function uses pattern as abstract sub-query definition towards the storage -type predicate() :: #{'@' => name(), '_' => head(), _ => pattern()}. -type name() :: f() | {module(), f()}. -type pattern() :: _ | [filter()]. -type filter() :: {'>' | '<' | '>=' | '=<' | '=/=', _}. -type f() :: atom(). %% sigma function (@todo: define types) -type eval() :: fun( (_) -> datum:stream() ). -type heap() :: fun( (map()) -> eval() ). %% %% stream based recursion t() -> stream:unfold(fun tt/1, [stream:build([1,2,3])]). tt([undefined]) -> undefined; tt([undefined | Stack]) -> tt(Stack); tt([H | T]) -> spin(stream:head(H), [stream:tail(H) | T]). spin(X, Stack) when X < 100 -> {X, [rec(X) | Stack]}; spin(X, Stack) -> {X, Stack}. rec(X) -> stream:build([X * 10, X * 10, X * 10]). %% %% evaluate compiled datalog expression with environment -spec q(_, _) -> eval(). q(Datalog, Env) -> Datalog(Env). %%%---------------------------------------------------------------------------- %%% %%% sigma function helpers %%% %%%---------------------------------------------------------------------------- %% %% in-line stream filter(s) using predicate term and pattern -spec filter(pattern()) -> fun( (_, datum:stream()) -> datum:stream() ). filter(Pattern) -> datalog_lang:filter(fun stream:filter/2, Pattern). %% %% in-line stream filter(s) using predicate term and pattern -spec takewhile(_, pattern()) -> fun( (_, datum:stream()) -> datum:stream() ). takewhile(X, Pattern) -> datalog_lang:filter(fun stream:takewhile/2, X, Pattern). %%%---------------------------------------------------------------------------- %%% %%% compiler %%% %%%---------------------------------------------------------------------------- %% %% parse datalog to native format -spec p(string()) -> datalog:q(). p(Datalog) -> p(Datalog, fun datalog_q:native/1). p(Datalog, PreProcessor) -> try {ok, Lex, _} = datalog_leex:string(Datalog), {ok, Req} = datalog_yeec:parse(Lex), PreProcessor(Req) catch _:{badmatch, {error, {_, rds_parser, Reason}}} -> {error, Reason}; _:{badmatch, {error, {_, rds_lexer, Reason},_}} -> {error, Reason}; _:{badmatch, Error} -> Error end. %% %% compile native datalog to evaluator function c(Source, Datalog) -> c(Source, Datalog, []). c(Source, Datalog, Opts) -> case lists:keyfind(return, 1, Opts) of {_, maps} -> c_map(Source, Datalog); {_, tuples} -> c_tuple(Source, Datalog); _ -> c_list(Source, Datalog) end. c_list(Source, [#goal{id = Goal, head = Head} | Datalog]) -> Lprogram = lists:foldl(fun(Horn, Acc) -> compile(Source, Horn, Acc) end, #{}, Datalog), Fun = maps:get(Goal, Lprogram), fun(Env) -> (Fun(Env))( head_lits(Head) ) end. c_tuple(Source, [#goal{id = Goal, head = Head} | Datalog]) -> Lprogram = lists:foldl(fun(Horn, Acc) -> compile(Source, Horn, Acc) end, #{}, Datalog), Fun = maps:get(Goal, Lprogram), fun(Env) -> stream:map( fun(Tuple) -> erlang:list_to_tuple(Tuple) end, (Fun(Env))( head_lits(Head) ) ) end. c_map(Source, [#goal{id = Goal, head = Head} | Datalog]) -> Lprogram = lists:foldl(fun(Horn, Acc) -> compile(Source, Horn, Acc) end, #{}, Datalog), Vars = case lists:keyfind(Goal, 2, Datalog) of {_, _, X, _} -> X; #source{} -> Head end, Fun = maps:get(Goal, Lprogram), fun(Env) -> stream:map( fun(Tuple) -> maps:from_list( lists:zip(Vars, Tuple) ) end, (Fun(Env))( head_lits(Head) ) ) end. compile(Source, #source{id = Id, head = Head}, Datalog) -> Datalog#{Id => datalog_vm:stream(fun Source:stream/3, Id, Head)}; compile(_, #horn{id = Id} = Horn, Datalog) -> Datalog#{Id => compile(Horn, Datalog)}; compile(_, #join{id = Id} = Join, Datalog) -> Datalog#{Id => compile(Join, Datalog)}; compile(_, #recc{id = Id} = Recc, Datalog) -> Datalog#{Id => compile(Recc, Datalog)}. compile(#{'@' := {datalog, Fun}} = Sigma, _Datalog) -> Sigma#{'@' => datalog_lang:Fun(Sigma), '.' => pipe}; compile(#{'@' := Gen} = Sigma, Datalog) -> Sigma#{'@' => maps:get(Gen, Datalog, Gen)}; compile(#horn{head = Head, body = Body}, Datalog) -> datalog_vm:horn(Head, [compile(Sigma, Datalog) || Sigma <- Body]); compile(#join{horn = Body}, Datalog) -> datalog_vm:union([compile(Sigma, Datalog) || Sigma <- Body]); compile(#recc{horn = [I, #horn{body = Body} = Horn]}, Datalog) -> datalog_vm:recursion( compile(I, Datalog), Horn#horn{body = [compile(Sigma, Datalog) || Sigma <- Body]} ). head_lits(Head) -> lists:map( fun(X) -> case is_atom(X) of true -> '_'; false -> X end end, Head ). ================================================ FILE: src/datalog.hrl ================================================ %% @doc %% abstract syntax tree %% %% Erlang Native Format -type id() :: atom() | {atom(), atom()} | {iri, binary(), binary()}. -type head() :: [_]. -type body() :: [#{}]. -record(goal, { id = undefined :: id() , head = undefined :: head() }). -record(horn, { id = undefined :: id() , head = undefined :: head() , body = undefined :: body() }). -record(join, { id = undefined :: id() , head = undefined :: head() , horn = undefined :: [#horn{}] }). -record(recc, { id = undefined :: id() , head = undefined :: head() , horn = undefined :: [#horn{}] }). -record(source, { id = undefined :: id() , head = undefined :: head() }). ================================================ FILE: src/datalog_lang.erl ================================================ %% %% Copyright 2014 - 2016 Dmitry Kolesnikov, All Rights Reserved %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% @doc %% build-in datalog primitives -module(datalog_lang). -include_lib("datum/include/datum.hrl"). -export([ filter/2, unique/1, flat/1, eq/1, ne/1, lt/1, gt/1, le/1, ge/1 ]). %% %% scalable bloom filter definition -define(SBF, sbf:new(128, 0.0001)). %% %% in-line stream filter(s), %% helper function to apply predicate terms and patterns on stream of tuples -spec filter(_, datalog:pattern()) -> _. filter(_With, '_') -> fun(_Lens, Stream) -> Stream end; filter(_With, undefined) -> fun(_Lens, Stream) -> Stream end; filter(With, Pattern) when is_list(Pattern) -> % pattern is guard condition: p(..., X, ...), X > 5, X < 10 fun(Lens, Stream) -> lists:foldl(fun(Filter, Acc) -> filter(With, Lens, Filter, Acc) end, Stream, Pattern) end; filter(With, Pattern) -> filter(With, [{'=:=', Pattern}]). filter(With, Lens, '_', Stream) -> Stream; filter(With, Lens, Filter, Stream) -> With(fun(X) -> filter_check(Lens(X), Filter) end, Stream). filter_check(B, {'>', A}) -> B > A; filter_check(B, {'>=', A}) -> B >= A; filter_check(B, {'<', A}) -> B < A; filter_check(B, {'=<', A}) -> B =< A; filter_check(B, {'=:=', A}) -> B =:= A; filter_check(B, {'=/=', A}) -> B =/= A. %% %% a predicate ensures unique terms within the stream %% ``` %% h(x,z) :- a(x,y), .unique(y), b(y,z) . %% ``` -spec unique(datalog:predicate()) -> _. unique(#{'_' := Head}) -> fun(_) -> fun(Stream) -> stream:unfold(fun uniq/1, {?SBF, Head, Stream}) end end. uniq({_, _, ?stream()}) -> stream:new(); uniq({Sbf0, Head, Stream}) -> Item = maps:with(Head, stream:head(Stream)), Sbf1 = sbf:add(Item, Sbf0), Tail = stream:dropwhile(fun(X) -> sbf:has(maps:with(Head, X), Sbf1) end, Stream), {stream:head(Stream), {Sbf1, Head, Tail}}. %% %% a predicate flatmap identity over stream %% ``` %% h(x,z) :- a(x,y), .flat(y), b(y,z) . %% ``` -spec flat(datalog:predicate()) -> _. flat(#{'_' := [Term]}) -> fun(_) -> fun(Stream) -> stream:unfold(fun flatten/1, {[], Term, Stream}) end end. flatten({_, _, ?stream()}) -> stream:new(); flatten({[], Term, Stream}) -> case stream:head(Stream) of #{Term := X} when is_list(X) -> flatten({X, Term, Stream}); Head -> {Head, {[], Term, stream:tail(Stream)}} end; flatten({[H], Term, Stream}) -> Head = stream:head(Stream), {Head#{Term => H}, {[], Term, stream:tail(Stream)}}; flatten({[H|T], Term, Stream}) -> Head = stream:head(Stream), {Head#{Term => H}, {T, Term, Stream}}. %% %% comparison predicates %% ``` %% h(x,z) :- a(x,y), b(y,z), .eq(x,z). %% ``` -spec eq(datalog:predicate()) -> _. eq(#{'_' := [A, B]}) -> fun(_) -> fun(Stream) -> stream:filter(fun(#{A := Ax, B := Bx}) -> Ax =:= Bx end, Stream) end end. -spec ne(datalog:predicate()) -> _. ne(#{'_' := [A, B]}) -> fun(_) -> fun(Stream) -> stream:filter(fun(#{A := Ax, B := Bx}) -> Ax =/= Bx end, Stream) end end. -spec lt(datalog:predicate()) -> _. lt(#{'_' := [A, B]}) -> fun(_) -> fun(Stream) -> stream:filter(fun(#{A := Ax, B := Bx}) -> Ax < Bx end, Stream) end end. -spec gt(datalog:predicate()) -> _. gt(#{'_' := [A, B]}) -> fun(_) -> fun(Stream) -> stream:filter(fun(#{A := Ax, B := Bx}) -> Ax > Bx end, Stream) end end. -spec le(datalog:predicate()) -> _. le(#{'_' := [A, B]}) -> fun(_) -> fun(Stream) -> stream:filter(fun(#{A := Ax, B := Bx}) -> Ax =< Bx end, Stream) end end. -spec ge(datalog:predicate()) -> _. ge(#{'_' := [A, B]}) -> fun(_) -> fun(Stream) -> stream:filter(fun(#{A := Ax, B := Bx}) -> Ax >= Bx end, Stream) end end. ================================================ FILE: src/datalog_leex.xrl ================================================ %% %% Copyright 2014 - 2015 Dmitry Kolesnikov, All Rights Reserved %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% @doc %% datalog Definitions. CHAR = [a-zA-Z_@] DIGIT = [0-9] WS = ([\000-\s]|%.*) %% %% Rules. %% %% symbol %% {CHAR}+ : {token, {symbol, TokenLine, TokenChars}}. '[a-zA-Z_:]* : {token, {symbol, TokenLine, lists:sublist(TokenChars, 2, TokenLen - 1)}}. %% %% xsd:anyURI %% - absolute IRI %% a:b - compact IRI %% <[^>]*> : {token, {iri, TokenLine, strip(TokenChars,TokenLen)}}. %% %% xsd:string %% \"[^\"]*\" : {token, {binary, TokenLine, strip(TokenChars,TokenLen)}}. %% %% xsd:integer %% {DIGIT}+ : {token, {integer, TokenLine, TokenChars}}. %% %% xsd:decimal %% {DIGIT}+\.{DIGIT}+ : {token, {decimal, TokenLine, TokenChars}}. %% %% syntax %% \?\- : {token,{list_to_atom(TokenChars),TokenLine}}. \:\- : {token,{list_to_atom(TokenChars),TokenLine}}. [()\[\]{}_<=>!.,^\-\:@/] : {token,{list_to_atom(TokenChars),TokenLine}}. {WS}+ : skip_token. %% %% Erlang code. strip(TokenChars,TokenLen) -> lists:sublist(TokenChars, 2, TokenLen - 2). ================================================ FILE: src/datalog_list.erl ================================================ %% %% Copyright 2014 - 2015 Dmitry Kolesnikov, All Rights Reserved %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% @doc %% a reference implementation of stream generators -module(datalog_list). -compile({parse_transform, category}). -export([stream/3]). %% %% example of ground facts generator, it produces a stream of tuple from input list %% %% a(x,y). stream(_, _, [X1]) -> fun(List) -> [identity || stream:build(List), nary(1, _), filter(1, X1, _), stream:map(fun erlang:tuple_to_list/1, _) ] end; stream(_, _, [X1, X2]) -> fun(List) -> [identity || stream:build(List), nary(2, _), filter(1, X1, _), filter(2, X2, _), stream:map(fun erlang:tuple_to_list/1, _) ] end; stream(_, H, [X1, X2, X3] = K) -> io:format("==< ~p ~p~n", [H, K]), fun(List) -> [identity || stream:build(List), nary(3, _), filter(1, X1, _), filter(2, X2, _), filter(3, X3, _), stream:map(fun erlang:tuple_to_list/1, _) ] end; stream(_, _, [X1, X2, X3, X4]) -> fun(List) -> [identity || stream:build(List), nary(4, _), filter(1, X1, _), filter(2, X2, _), filter(3, X3, _), filter(4, X4, _), stream:map(fun erlang:tuple_to_list/1, _) ] end. %% %% nary(N, Stream) -> stream:filter(fun(X) -> is_tuple(X) andalso size(X) =:= N end, Stream). filter(I, Filter, Stream) -> Fun = datalog:filter(Filter), Fun(fun(X) -> erlang:element(I, X) end, Stream). ================================================ FILE: src/datalog_q.erl ================================================ %% %% Copyright 2014 - 2018 Dmitry Kolesnikov, All Rights Reserved %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% @doc %% the library parser uses leex / yeec to convert query (datalog syntax) to %% abstract syntax tree (set of horn classes). The abstract syntax is not optimal %% for evaluation. It has to be adapted to `-type datalog:q()`. -module(datalog_q). -include("datalog.hrl"). -export([ native/1 ]). %% %% -spec native([{_, _, _}]) -> datalog:q(). native(Datalog) -> join(lists:map(fun optimise/1, Datalog)). %% optimise(#horn{body = Body} = Horn) -> % horn clause body consists of predicates and infix predicates. % infix predicates are converted to stream range filters {Pattern, Infix} = lists:partition(fun(X) -> size(X) =:= 2 end, Body), Horn#horn{body = ast_to_body(Pattern, Infix)}; optimise(Clause) -> Clause. %% join([#horn{id = Id, head = Head} | _] = Datalog) -> case lists:partition(fun(#horn{id = X}) -> X =:= Id; (_) -> false end, Datalog) of {[Horn], Other} -> [Horn | join(Other)]; {Joints, Other} -> case is_recc(Id, Joints) of false -> [#join{id = Id, head = Head, horn = Joints} | join(Other)]; true -> [#recc{id = Id, head = Head, horn = Joints} | join(Other)] end end; join([Head | Tail]) -> [Head | join(Tail)]; join([]) -> []. %% is_recc(_, []) -> false; is_recc(Horn, [#horn{body = Body} | T]) -> case lists:dropwhile(fun(#{'@' := X}) -> X /= Horn end, Body) of [] -> is_recc(Horn, T); _ -> true end. %% %% ast_to_body(Pattern, Infix) -> [ast_to_pattern(X, Infix) || X <- Pattern]. %% %% ast_to_pattern({Id, Pattern}, Filter) -> %% check each pattern variable for BIF constrains lists:foldl( fun(X, Acc) -> ast_to_filter(X, Filter, Acc) end, #{'@' => Id, '_' => Pattern}, Pattern ). %% %% interleave pattern variable with filters ast_to_filter(Variable, Filter, Pattern) -> case lists:filter( fun(X) -> erlang:element(2, X) =:= Variable end, Filter ) of %% no BIF constrain [] -> Pattern; %% BIF constrain variable Fs -> case lists:keyfind('=:=', 1, Fs) of false -> maps:put(Variable, [{P, F} || {P, _, F} <- Fs], Pattern); {'=:=', _, F} -> maps:put(Variable, F, Pattern) end end. ================================================ FILE: src/datalog_vm.erl ================================================ %% %% Copyright 2014 - 2018 Dmitry Kolesnikov, All Rights Reserved %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% @doc %% evaluates a logical program -module(datalog_vm). -compile({parse_transform, partial}). -compile({parse_transform, category}). -include_lib("datum/include/datum.hrl"). -include("datalog.hrl"). -export([ union/1, recursion/2, horn/2, stream/3 ]). %% %% union(Horns) -> fun(Env) -> HHorns = [F(Env) || F <- Horns], fun(SubQ) -> stream:unfold(fun funion/1, {?stream(), SubQ, HHorns}) end end. funion({?stream(), _, []}) -> ?stream(); funion({?stream(), SubQ, [HHorn | THorn]}) -> funion({HHorn(SubQ), SubQ, THorn}); funion({Stream, SubQ, HHorns}) -> {stream:head(Stream), {stream:tail(Stream), SubQ, HHorns}}. %% %% recursion(I, #horn{head = Head, body = Body}) -> fun(Env) -> IEnv = I(Env), {Horns, [#{'_' := XHead}]} = lists:partition(fun(#{'@' := F}) -> is_function(F) end, Body), THorns = [Spec#{'@' => F(Env)} || #{'@' := F} = Spec <- Horns], fun(SubQ) -> stream:unfold(fun frecc/1, {[IEnv(SubQ)], XHead, THorns, Head, sbf:new(128, 0.0001)}) end end. frecc({[undefined], _, _, _, _}) -> undefined; frecc({[undefined | Stack], XHead, THorn, Head, Sbf}) -> frecc({Stack, XHead, THorn, Head, Sbf}); frecc({[H | T], XHead, THorn, Head, Sbf}) -> Tuple = stream:head(H), case not sbf:has(Tuple, Sbf) of true -> spinoff(Tuple, {[stream:tail(H) | T], XHead, THorn, Head, sbf:add(Tuple, Sbf)}); false -> frecc({[stream:tail(H) | T], XHead, THorn, Head, Sbf}) end. spinoff(Cell, {Stack, XHead, [HHorn | THorn] = Horns, Head, Sbf}) -> Heap = maps:from_list(lists:zip(XHead, Cell)), Stream = join(eval(Heap, HHorn), THorn), New = stream:map( fun(X) -> [maps:get(K, X) || K <- Head] end, Stream ), {Cell, {[New | Stack], XHead, Horns, Head, Sbf}}. %% %% horn(Head, Horn) -> fun(Env) -> [HHorn | THorn] = [Spec#{'@' => F(Env)} || #{'@' := F} = Spec <- Horn, is_function(F)], fun(SubQ) -> Heap = maps:from_list(lists:zip(Head, SubQ)), Stream = join(eval(Heap, HHorn), THorn), stream:map( fun(X) -> [maps:get(K, X) || K <- Head] end, Stream ) end end. join(Stream, [#{'.' := pipe, '@' := Pipe} | THorn]) -> join(Pipe(Stream), THorn); join(Stream, [HHorn | THorn]) -> join( stream:flat( stream:map(fun(Heap) -> eval(Heap, HHorn) end, Stream) ), THorn ); join(Stream, []) -> Stream. eval(Heap, #{'_' := Head, '@' := Fun} = Spec) -> SubQ = [term(T, Spec, Heap) || T <- Head], stream:map( fun(Tuple) -> %% Note: we need to give a priority to existed heap values, unless '_' %% maps:merge(Heap, maps:from_list( lists:zip(Head, Tuple) )) Prev = maps:filter(fun(_, X) -> X /= '_' end, Heap), This = maps:from_list( lists:zip(Head, Tuple) ), maps:merge(Heap, maps:merge(This, Prev)) end, Fun(SubQ) ). %% %% term(T, Spec, Heap) when is_atom(T) -> case Spec of #{T := [{Op, Var}]} when is_atom(Var) -> [{Op, maps:get(Var, Heap)}]; _ -> case term(T, Spec) of '_' -> term(T, Heap); Val -> Val end end; term(T, _, _) -> T. term(T, Predicate) -> case Predicate of #{T := Value} -> Value; _ -> '_' end; term(T, _) -> T. %% %% stream(Gen, Id, Head) -> fun(Env) -> fun(SubQ) -> (Gen(Id, Head, SubQ))(Env) end end. ================================================ FILE: src/datalog_yeec.yrl ================================================ %% %% Copyright 2014 - 2015 Dmitry Kolesnikov, All Rights Reserved %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% @doc %% datalog Nonterminals CLAUSES GOAL HORN BODY ITEM PREDICATE TERMS TERM PAIRS LIT INFIX. Terminals '?-' ':-' '(' ')' '.' ',' '<' '=' '>' '!' '-' ':' '[' ']' '{' '}' '/' symbol iri binary integer decimal. Rootsymbol CLAUSES. %% %% CLAUSES -> GOAL CLAUSES : ['$1' | '$2']. CLAUSES -> HORN CLAUSES : ['$1' | '$2']. CLAUSES -> '$empty' : []. %% %% GOAL -> '?-' PREDICATE '(' TERMS ')' '.' : {goal, '$2', '$4'}. GOAL -> '?-' PREDICATE '/' integer '.' : {goal, '$2', head('$4')}. %% %% HORN -> PREDICATE '(' TERMS ')' ':-' BODY '.' : {horn, '$1', '$3', '$6'}. HORN -> PREDICATE '(' TERMS ')' '.' : {source, '$1', '$3'}. HORN -> PREDICATE '(' ')' '.' : {source, '$1', []}. %% %% BODY -> ITEM ',' BODY : ['$1' | '$3']. BODY -> ITEM : ['$1']. %% %% ITEM -> PREDICATE '(' ')' : {'$1', []}. ITEM -> PREDICATE '(' TERMS ')' : {'$1', '$3'}. ITEM -> symbol INFIX TERM : {'$2', atom('$1'), '$3'}. ITEM -> symbol ':' symbol INFIX TERM : {'$4', {iri, binary('$1'), binary('$3')}, '$5'}. %% %% PREDICATE -> symbol : atom('$1'). PREDICATE -> symbol ':' symbol : {iri, binary('$1'), binary('$3')}. PREDICATE -> symbol '.' symbol : {atom('$1'), atom('$3')}. PREDICATE -> '.' symbol : {datalog, atom('$2')}. %% %% TERMS -> TERM ',' TERMS : ['$1' | '$3']. TERMS -> TERM : ['$1']. %% %% TERM -> '{' PAIRS '}' : maps:from_list('$2'). TERM -> '(' TERMS ')' : erlang:list_to_tuple('$2'). TERM -> '[' TERMS ']' : '$2'. TERM -> symbol LIT : {atom('$1'), '$2'}. TERM -> symbol LIT LIT : {atom('$1'), '$2', '$3'}. TERM -> symbol LIT LIT LIT : {atom('$1'), '$2', '$3', '$4'}. TERM -> LIT : '$1'. %% %% PAIRS -> symbol ':' TERM ',' PAIRS : [{binary('$1'), '$3'} | '$5']. PAIRS -> symbol ':' TERM : [{binary('$1'), '$3'}]. %% %% xsd:anyURI LIT -> iri : {iri, binary('$1')}. LIT -> symbol ':' symbol : {iri, binary('$1'), binary('$3')}. %% %% xsd:string LIT -> binary : binary('$1'). LIT -> binary symbol : binary('$1'). %% %% xsd:integer LIT -> integer : integer('$1'). LIT -> '-' integer : -integer('$2'). %% %% xsd:decimal LIT -> decimal : decimal('$1'). LIT -> '-' decimal : -decimal('$2'). %% %% xsd:dateTime LIT -> integer '-' integer '-' integer symbol integer ':' integer ':' integer symbol : timestamp({{integer('$1'), integer('$3'), integer('$5')}, {integer('$7'), integer('$9'), integer('$11')}}). LIT -> integer '-' integer '-' integer : {{integer('$1'), integer('$3'), integer('$5')}, {0, 0, 0}}. LIT -> integer '-' integer : {{integer('$1'), integer('$3'), 0}, {0, 0, 0}}. LIT -> '-' '-' integer '-' integer : {{0, integer('$3'), integer('$5')}, {0, 0, 0}}. LIT -> integer ':' integer ':' integer symbol : {{0, 0, 0}, {integer('$1'), integer('$3'), integer('$5')}}. %% %% symbols LIT -> symbol : atom('$1'). %% %% INFIX -> '=' : '=:='. INFIX -> '>' : '>'. INFIX -> '<' : '<'. INFIX -> '>' '=' : '>='. INFIX -> '=' '>' : '>='. INFIX -> '=' '<' : '=<'. INFIX -> '<' '=' : '=<'. INFIX -> '!' '=' : '=/='. INFIX -> symbol : atom('$1'). %% %% Erlang code. unwrap({_, _, X}) -> X. atom({_,_,X}) -> erlang:list_to_atom(X). binary({_,_,X}) -> erlang:list_to_binary(X). integer({_,_,X}) -> erlang:list_to_integer(X). decimal({_,_,X}) -> erlang:list_to_float(X). timestamp({{_, _, _}, {_, _, _}} = T) -> Sec = calendar:datetime_to_gregorian_seconds(T) - 62167219200, {Sec div 1000000, Sec rem 1000000, 0}. head({_,_,X}) -> ['_' || _ <- lists:seq(1, erlang:list_to_integer(X))]. ================================================ FILE: test/datalog_SUITE.erl ================================================ -module(datalog_SUITE). -include_lib("common_test/include/ct.hrl"). -compile({parse_transform, category}). -export([all/0]). -export([ predicate_stream_arity_1/1 , predicate_stream_arity_2/1 , predicate_stream_arity_3/1 , predicate_single_horn_1/1 , predicate_single_horn_2/1 , predicate_single_horn_3/1 , predicate_horn_2/1 , predicate_horn_3/1 , cartesian_product/1 , infix_eq/1 , infix_lt/1 , infix_le/1 , infix_gt/1 , infix_ge/1 , infix_ne/1 , union_2/1 , union_3/1 , recursion_1/1 , recursion_2/1 , recursion_3/1 , source_semantic_web/1 , source_native/1 , lang_eq/1 , lang_ne/1 , lang_lt/1 , lang_gt/1 , lang_le/1 , lang_ge/1 , lang_unique/1 , lang_flat/1 ]). all() -> [Test || {Test, NAry} <- ?MODULE:module_info(exports), Test =/= module_info, NAry =:= 1 ]. datalog(Datalog, Input, Expect) -> Expect = [identity || datalog:p(Datalog), datalog:c(datalog_list, _, [{return, tuples}]), datalog:q(_, Input), stream:list(_) ]. %% %% predicate_stream_arity_1(_) -> datalog( "?- p(_). p(x).", [{1}, {2}, {3}], [{1}, {2}, {3}] ). predicate_stream_arity_2(_) -> datalog( "?- p(_, _). p(x, y).", [{1, 2}, {2, 3}, {3, 4}], [{1, 2}, {2, 3}, {3, 4}] ). predicate_stream_arity_3(_) -> datalog( "?- p(_, _, _). p(x, y, z).", [{1, 2, 3}, {2, 3, 4}, {3, 4, 5}], [{1, 2, 3}, {2, 3, 4}, {3, 4, 5}] ). %% %% predicate_single_horn_1(_) -> datalog( "?- a(_, _). p(x, y). a(x, y) :- p(x, y).", [{1, 2}, {2, 3}, {3, 4}, {4, 5}], [{1, 2}, {2, 3}, {3, 4}, {4, 5}] ). predicate_single_horn_2(_) -> datalog( "?- a(_, _). p(x, y). a(x, y) :- p(x, z), p(z, y).", [{1, 2}, {2, 3}, {3, 4}, {4, 5}], [{1, 3}, {2, 4}, {3, 5}] ). predicate_single_horn_3(_) -> datalog( "?- a(_, _). p(x, y). a(x, y) :- p(x, z), p(z, f), p(f, y).", [{1, 2}, {2, 3}, {3, 4}, {4, 5}], [{1, 4}, {2, 5}] ). predicate_horn_2(_) -> datalog( "?- b(_, _). p(x, y). a(x, y) :- p(x, y). b(x, y) :- a(x, z), p(z, y).", [{1, 2}, {2, 3}, {3, 4}, {4, 5}], [{1, 3}, {2, 4}, {3, 5}] ). predicate_horn_3(_) -> datalog( "?- c(_, _). p(x, y). a(x, y) :- p(x, y). b(x, y) :- a(x, z), p(z, y). c(x, y) :- b(x, z), p(z, y).", [{1, 2}, {2, 3}, {3, 4}, {4, 5}], [{1, 4}, {2, 5}] ). cartesian_product(_) -> datalog( "?- h(_,_). p(x). h(x,y) :- p(x), p(y).", [{1}, {2}, {3}], [{1,1}, {1,2}, {1,3}, {2,1}, {2,2}, {2,3}, {3,1}, {3,2}, {3,3}] ). %% %% infix_eq(_) -> datalog( "?- h(_). p(x). h(x) :- p(x), x = 2.", [{1}, {2}, {3}, {4}], [{2}] ). infix_lt(_) -> datalog( "?- h(_). p(x). h(x) :- p(x), x < 3.", [{1}, {2}, {3}, {4}], [{1}, {2}] ). infix_le(_) -> datalog( "?- h(_). p(x). h(x) :- p(x), x =< 2.", [{1}, {2}, {3}, {4}], [{1}, {2}] ). infix_gt(_) -> datalog( "?- h(_). p(x). h(x) :- p(x), x > 2.", [{1}, {2}, {3}, {4}], [{3}, {4}] ). infix_ge(_) -> datalog( "?- h(_). p(x). h(x) :- p(x), x >= 3.", [{1}, {2}, {3}, {4}], [{3}, {4}] ). infix_ne(_) -> datalog( "?- h(_). p(x). h(x) :- p(x), x != 2.", [{1}, {2}, {3}, {4}], [{1}, {3}, {4}] ). %% %% union_2(_) -> datalog( "?- a(_, _). p(x,y). a(x,y) :- p(x,y), x > 2. a(x,y) :- p(x,y).", [{1,2}, {2,3}, {3,4}, {4,5}], [{3,4}, {4,5}, {1,2}, {2,3}, {3,4}, {4,5}] ). union_3(_) -> datalog( "?- a(_, _). p(x,y). a(x,y) :- p(x,y), x > 2. a(x,y) :- p(x,y), x < 3. a(x,y) :- p(x,y).", [{1,2}, {2,3}, {3,4}, {4,5}], [{3,4}, {4,5}, {1,2}, {2,3}, {1,2}, {2,3}, {3,4}, {4,5}] ). %% %% recursion_1(_) -> datalog( "?- a(_, _). p(x,y). a(x, y) :- p(x, y). a(x, y) :- p(x, z), a(z, y).", [{1,2}, {2,3}, {3,2}], [{1,2}, {2,3}, {1,3}, {3,3}, {3,2}, {2,2}] ). recursion_2(_) -> datalog( "?- a(_, _). p(x,y). a(x, y) :- p(x, y). a(x, y) :- p(x, z), a(z, y).", [{1,2}, {2,3}, {3,4}, {4,5}], [{1,2}, {2,3}, {1,3}, {3,4}, {2,4}, {1,4}, {4,5}, {3,5}, {2,5}, {1,5}] ). recursion_3(_) -> datalog( "?- a(_, _). p(x,y). a(x, y) :- p(x, y). a(x, y) :- p(x, z), a(z, y).", [{1,2}, {2,3}, {2,5}, {3,4}, {4,2}, {5,4}], [{1,2}, {2,3}, {1,3}, {4,3}, {3,3}, {5,3}, {2,5}, {1,5}, {4,5}, {3,5}, {5,5}, {3,4}, {2,4}, {1,4}, {4,4}, {5,4}, {4,2}, {3,2}, {2,2}, {5,2}] ). %% %% source_semantic_web(_) -> datalog( "?- schema:person(_, _). foaf:person('rdf:id, 'foaf:name). schema:person('rdf:id, 'foaf:name) :- foaf:person('rdf:id, 'foaf:name).", [{1,2}, {2,3}, {3,4}], [{1,2}, {2,3}, {3,4}] ). source_native(_) -> datalog( "?- a(_, _). foo.p(x, y). bar.p(x, y). a(x, y) :- foo.p(x, z), bar.p(z, y).", [{1,2}, {2,3}, {3,4}, {4,5}], [{1,3}, {2,4}, {3,5}] ). %% %% lang_eq(_) -> datalog( "?- a(_, _). p(x). a(x, y) :- p(x), p(y), .eq(x, y).", [{1}, {2}, {3}, {4}], [{1,1}, {2,2}, {3,3}, {4,4}] ). lang_ne(_) -> datalog( "?- a(_, _). p(x). a(x, y) :- p(x), p(y), .ne(x, y).", [{1}, {2}], [{1,2}, {2,1}] ). lang_lt(_) -> datalog( "?- a(_, _). p(x). a(x, y) :- p(x), p(y), .lt(x, y).", [{1}, {2}, {3}, {4}], [{1,2}, {1,3}, {1,4}, {2,3}, {2,4}, {3,4}] ). lang_gt(_) -> datalog( "?- a(_, _). p(x). a(x, y) :- p(x), p(y), .gt(x, y).", [{1}, {2}, {3}, {4}], [{2,1}, {3,1}, {3,2}, {4,1}, {4,2}, {4,3}] ). lang_le(_) -> datalog( "?- a(_, _). p(x). a(x, y) :- p(x), p(y), .le(x, y).", [{1}, {2}, {3}, {4}], [{1,1}, {1,2}, {1,3}, {1,4}, {2,2}, {2,3}, {2,4}, {3,3}, {3,4}, {4,4}] ). lang_ge(_) -> datalog( "?- a(_, _). p(x). a(x, y) :- p(x), p(y), .ge(x, y).", [{1}, {2}, {3}, {4}], [{1,1}, {2,1}, {2,2}, {3,1}, {3,2}, {3,3}, {4,1}, {4,2}, {4,3}, {4,4}] ). lang_unique(_) -> datalog( "?- a(_). p(x). a(x) :- p(x), .unique(x).", [{1}, {2}, {1}, {3}, {1}, {2}, {4}, {1}, {2}, {3}], [{1}, {2}, {3}, {4}] ). lang_flat(_) -> datalog( "?- a(_). p(x). a(x) :- p(x), .flat(x).", [{[1, 2]}, {[2, 3]}, {[3, 4]}], [{1}, {2}, {2}, {3}, {3}, {4}] ). ================================================ FILE: test/datalog_parser_SUITE.erl ================================================ %% %% Copyright 2014 - 2015 Dmitry Kolesnikov, All Rights Reserved %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% @doc %% datalog parser test suite -module(datalog_parser_SUITE). -include_lib("common_test/include/ct.hrl"). %% common test -export([all/0]). -export([ goal/1, stream/1, stream_native/1, stream_semantic/1, horn_1/1, horn_n/1, horn_0/1, inline_string/1, inline_int/1, inline_float/1, anything/1, guard_eq_string/1, guard_eq_tuple_of_string/1, guard_eq_int/1, guard_eq_float/1, guard_gt_string/1, guard_gt_int/1, guard_gt_float/1, guard_string/1, guard_number/1, bif_datalog/1, bif_external/1, semantic_web/1, jsonld/1, constant_xsd_absolute_uri/1, constant_xsd_compact_uri/1, constant_xsd_string/1, constant_xsd_lang_string/1, constant_xsd_integer/1, constant_xsd_decimal/1, constant_xsd_boolean/1, constant_xsd_datetime/1, constant_xsd_date/1, constant_xsd_yearmonth/1, constant_xsd_monthdate/1, constant_xsd_time/1, constant_xsd_seq/1, constant_xsd_map/1, constant_xsd_list/1, aggregation/1 ]). %%%---------------------------------------------------------------------------- %%% %%% factory %%% %%%---------------------------------------------------------------------------- all() -> [Test || {Test, NAry} <- ?MODULE:module_info(exports), Test =/= module_info, NAry =:= 1 ]. %%%---------------------------------------------------------------------------- %%% %%% unit test %%% %%%---------------------------------------------------------------------------- %% %% goal(_) -> [ {goal, h, [x,y]} ] = datalog:p("?- h(x, y)."). %% %% stream(_) -> [ {source, p, [x, y]}, {source, p, []} ] = datalog:p("p(x, y). p()."). stream_native(_) -> [ {source, {datalog, a}, []}, {source, {mod, a}, [<<"x">>, <<"y">>]} ] = datalog:p(".a(). mod.a(\"x\", \"y\")."). stream_semantic(_) -> [ {source, {iri, <<"foaf">>, <<"person">>}, [x, y]} ] = datalog:p("foaf:person(x, y)."). %% %% horn_1(_Config) -> [ {horn, h, [x,y], [ #{'@' := a, '_' := [x,y]} ]} ] = datalog:p("h(x,y) :- a(x, y)."). horn_n(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y]}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- a(x, y), b(y, z)."). horn_0(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y]}, #{'@' := b, '_' := []} ]} ] = datalog:p("h(x,z) :- a(x, y), b()."). %% %% inline_string(_Config) -> [ {horn, h, [z], [ #{'@' := a, '_' := [<<"x">>,y]}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(z) :- a(\"x\", y), b(y, z)."). inline_int(_Config) -> [ {horn, h, [z], [ #{'@' := a, '_' := [100,y]}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(z) :- a(100, y), b(y, z)."). inline_float(_Config) -> [ {horn, h, [z], [ #{'@' := a, '_' := [1.0,y]}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(z) :- a(1.0, y), b(y, z)."). %% %% anything(_Config) -> [ {horn, h, [z], [ #{'@' := a, '_' := ['_',y]}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(z) :- a(_, y), b(y, z)."). %% %% guard_eq_string(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y], x := <<"x">>}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- a(x, y), b(y, z), x = \"x\"."). guard_eq_tuple_of_string(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y], x := {<<"x">>, <<"y">>, <<"z">>}}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- a(x, y), b(y, z), x = (\"x\", \"y\", \"z\")."). guard_eq_int(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y], x := 100}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- a(x, y), b(y, z), x = 100."). guard_eq_float(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y], x := 1.0}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- a(x, y), b(y, z), x = 1.0."). %% %% guard_gt_string(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y], x := [{'>', <<"x">>}]}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- a(x, y), b(y, z), x > \"x\"."). guard_gt_int(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y], x := [{'>', 100}]}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- a(x, y), b(y, z), x > 100."). guard_gt_float(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y], x := [{'>', 1.0}]}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- a(x, y), b(y, z), x > 1.0."). %% %% guard_string(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y], x := [{'>=', <<"x">>}, {'=<', <<"z">>}]}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- a(x, y), b(y, z), x >= \"x\", x =< \"z\" ."). guard_number(_Config) -> [ {horn, h, [x,z], [ #{'@' := a, '_' := [x,y], x := [{'>=', 1.0}, {'=<', 100}]}, #{'@' := b, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- a(x, y), b(y, z), x >= 1.0, x =< 100."). %% %% bif_datalog(_Config) -> [ {horn, h, [x,z], [ #{'@' := {datalog, a}, '_' := [x,y]}, #{'@' := {datalog, b}, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- .a(x, y), .b(y, z)."). bif_external(_Config) -> [ {horn, h, [x,z], [ #{'@' := {mod, a}, '_' := [x,y]}, #{'@' := {mod, b}, '_' := [y,z]} ]} ] = datalog:p("h(x,z) :- mod.a(x, y), mod.b(y, z)."). %% %% semantic_web(_Config) -> [ {horn, {iri, <<"foaf">>, <<"person">>}, [{iri, <<"rdf">>, <<"id">>}, {iri, <<"foaf">>, <<"name">>}], [ #{ '@' := {iri, <<"foaf">>, <<"person">>}, '_' := [{iri, <<"rdf">>, <<"id">>}, {iri, <<"foaf">>, <<"name">>}], {iri, <<"foaf">>, <<"name">>} := [{'>', <<"A">>}, {'<', <<"B">>}] } ] } ] = datalog:p("foaf:person(rdf:id, foaf:name) :- foaf:person(rdf:id, foaf:name), foaf:name > \"A\", foaf:name < \"B\"."). %% %% jsonld(_Config) -> [ {horn, h, ['@id',z], [ #{'@' := a, '_' := ['@id', '@type']}, #{'@' := b, '_' := ['@type', z]} ]} ] = datalog:p("h(@id,z) :- a(@id, @type), b(@type, z)."). %% %% constant_xsd_absolute_uri(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := {iri, <<"http://example.com/a">>}} ]} ] = datalog:p("h(x) :- a(x), x = ."). constant_xsd_compact_uri(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := {iri, <<"example">>, <<"a">>}} ]} ] = datalog:p("h(x) :- a(x), x = example:a."). constant_xsd_string(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := <<"example">>} ]} ] = datalog:p("h(x) :- a(x), x = \"example\"."). constant_xsd_lang_string(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := <<"example">>} ]} ] = datalog:p("h(x) :- a(x), x = \"example\"@en."). constant_xsd_integer(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := 123} ]} ] = datalog:p("h(x) :- a(x), x = 123."). constant_xsd_decimal(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := 12.3} ]} ] = datalog:p("h(x) :- a(x), x = 12.3."). constant_xsd_boolean(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x, y], x := true, y := false} ]} ] = datalog:p("h(x) :- a(x, y), x = true, y = false."). constant_xsd_datetime(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := {1175,783410,0}} ]} ] = datalog:p("h(x) :- a(x), x = 2007-04-05T14:30:10Z."). constant_xsd_date(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := {{2007,4,5},{0,0,0}}} ]} ] = datalog:p("h(x) :- a(x), x = 2007-04-05."). constant_xsd_yearmonth(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := {{2007,4,0},{0,0,0}}} ]} ] = datalog:p("h(x) :- a(x), x = 2007-04."). constant_xsd_monthdate(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := {{0,4,5},{0,0,0}}} ]} ] = datalog:p("h(x) :- a(x), x = --04-05."). constant_xsd_time(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := {{0,0,0},{14,30,10}}} ]} ] = datalog:p("h(x) :- a(x), x = 14:30:10Z."). constant_xsd_seq(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := {154, 7623, 23} } ]} ] = datalog:p("h(x) :- a(x), x = (154, 7623, 23)."). constant_xsd_map(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := #{<<"type">> := <<"Point">>, <<"coordinates">> := [21.3, 60.2]} } ]} ] = datalog:p("h(x) :- a(x), x = {type:\"Point\", coordinates:[21.3, 60.2]}."). constant_xsd_list(_) -> [ {horn, h, [x], [ #{'@' := a, '_' := [x], x := [1, <<"a">>, 3.2] } ]} ] = datalog:p("h(x) :- a(x), x = [1, \"a\", 3.2]."). %% %% aggregation(_) -> [ {source, p, [ id, {count, <<"x">>}, {stats, <<"y">>}, {category, 10, <<"z">>} ]} ] = datalog:p("p(id, count \"x\", stats \"y\", category 10 \"z\")."). ================================================ FILE: test/tests.config ================================================ %% %% suites {suites, ".", all}.