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.
[](http://travis-ci.org/fogfish/datalog)
[](https://coveralls.io/github/fogfish/datalog?branch=master)
[](https://hex.pm/packages/datalog)
[](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 | `<.+>` | `<http://example.com/a>`
|| `[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(<http://example.com/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 `<absolute IRI>` or `prefix:suffix`
```
h(x) :- p(x, <http://example.com/1>).
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
================================================
<http://example.org/person/100> <http://schema.org/name> "James Cameron"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/100> <http://schema.org/born> "1954-08-16"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/101> <http://schema.org/name> "Arnold Schwarzenegger"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/101> <http://schema.org/born> "1947-07-30"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/102> <http://schema.org/name> "Linda Hamilton"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/102> <http://schema.org/born> "1956-09-26"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/103> <http://schema.org/name> "Michael Biehn"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/103> <http://schema.org/born> "1956-07-31"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/104> <http://schema.org/name> "Ted Kotcheff"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/104> <http://schema.org/born> "1931-04-07"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/105> <http://schema.org/name> "Sylvester Stallone"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/105> <http://schema.org/born> "1946-07-06"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/106> <http://schema.org/name> "Richard Crenna"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/106> <http://schema.org/born> "1926-11-30"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/106> <http://schema.org/death> "2003-01-17"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/107> <http://schema.org/name> "Brian Dennehy"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/107> <http://schema.org/born> "1938-07-09"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/108> <http://schema.org/name> "John McTiernan"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/108> <http://schema.org/born> "1951-01-08"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/109> <http://schema.org/name> "Elpidia Carrillo"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/109> <http://schema.org/born> "1961-08-16"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/110> <http://schema.org/name> "Carl Weathers"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/110> <http://schema.org/born> "1948-01-14"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/111> <http://schema.org/name> "Richard Donner"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/111> <http://schema.org/born> "1930-04-24"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/112> <http://schema.org/name> "Mel Gibson"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/112> <http://schema.org/born> "1956-01-03"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/113> <http://schema.org/name> "Danny Glover"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/113> <http://schema.org/born> "1946-07-22"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/114> <http://schema.org/name> "Gary Busey"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/114> <http://schema.org/born> "1944-07-29"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/115> <http://schema.org/name> "Paul Verhoeven"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/115> <http://schema.org/born> "1938-07-18"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/116> <http://schema.org/name> "Peter Weller"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/116> <http://schema.org/born> "1947-06-24"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/117> <http://schema.org/name> "Nancy Allen"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/117> <http://schema.org/born> "1950-06-24"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/118> <http://schema.org/name> "Ronny Cox"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/118> <http://schema.org/born> "1938-07-23"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/119> <http://schema.org/name> "Mark L. Lester"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/119> <http://schema.org/born> "1946-11-26"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/120> <http://schema.org/name> "Rae Dawn Chong"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/120> <http://schema.org/born> "1961-02-28"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/121> <http://schema.org/name> "Alyssa Milano"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/121> <http://schema.org/born> "1972-12-19"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/122> <http://schema.org/name> "Bruce Willis"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/122> <http://schema.org/born> "1955-03-19"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/123> <http://schema.org/name> "Alan Rickman"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/123> <http://schema.org/born> "1946-02-21"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/124> <http://schema.org/name> "Alexander Godunov"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/124> <http://schema.org/born> "1949-11-28"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/124> <http://schema.org/death> "1995-05-18"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/125> <http://schema.org/name> "Robert Patrick"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/125> <http://schema.org/born> "1958-11-05"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/126> <http://schema.org/name> "Edward Furlong"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/126> <http://schema.org/born> "1977-08-02"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/127> <http://schema.org/name> "Jonathan Mostow"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/127> <http://schema.org/born> "1961-11-28"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/128> <http://schema.org/name> "Nick Stahl"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/128> <http://schema.org/born> "1979-12-05"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/129> <http://schema.org/name> "Claire Danes"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/129> <http://schema.org/born> "1979-04-12"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/130> <http://schema.org/name> "George P. Cosmatos"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/130> <http://schema.org/born> "1941-01-04"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/130> <http://schema.org/death> "2005-04-19"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/131> <http://schema.org/name> "Charles Napier"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/131> <http://schema.org/born> "1936-04-12"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/131> <http://schema.org/death> "2011-10-05"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/132> <http://schema.org/name> "Peter MacDonald"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/133> <http://schema.org/name> "Marc de Jonge"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/133> <http://schema.org/born> "1949-02-16"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/133> <http://schema.org/death> "1996-06-06"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/134> <http://schema.org/name> "Stephen Hopkins"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/135> <http://schema.org/name> "Ruben Blades"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/135> <http://schema.org/born> "1948-07-16"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/136> <http://schema.org/name> "Joe Pesci"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/136> <http://schema.org/born> "1943-02-09"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/137> <http://schema.org/name> "Ridley Scott"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/137> <http://schema.org/born> "1937-11-30"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/138> <http://schema.org/name> "Tom Skerritt"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/138> <http://schema.org/born> "1933-08-25"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/139> <http://schema.org/name> "Sigourney Weaver"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/139> <http://schema.org/born> "1949-10-08"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/140> <http://schema.org/name> "Veronica Cartwright"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/140> <http://schema.org/born> "1949-04-20"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/141> <http://schema.org/name> "Carrie Henn"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/142> <http://schema.org/name> "George Miller"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/142> <http://schema.org/born> "1945-03-03"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/143> <http://schema.org/name> "Steve Bisley"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/143> <http://schema.org/born> "1951-12-26"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/144> <http://schema.org/name> "Joanne Samuel"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/145> <http://schema.org/name> "Michael Preston"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/145> <http://schema.org/born> "1938-05-14"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/146> <http://schema.org/name> "Bruce Spence"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/146> <http://schema.org/born> "1945-09-17"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/147> <http://schema.org/name> "George Ogilvie"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/147> <http://schema.org/born> "1931-03-05"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/148> <http://schema.org/name> "Tina Turner"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/148> <http://schema.org/born> "1939-11-26"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/149> <http://schema.org/name> "Sophie Marceau"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/person/149> <http://schema.org/born> "1966-11-17"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/200> <http://schema.org/title> "The Terminator"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/200> <http://schema.org/year> "1984"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/200> <http://schema.org/director> <http://example.org/person/100> .
<http://example.org/movie/200> <http://schema.org/cast> <http://example.org/person/101> .
<http://example.org/movie/200> <http://schema.org/cast> <http://example.org/person/102> .
<http://example.org/movie/200> <http://schema.org/cast> <http://example.org/person/103> .
<http://example.org/movie/200> <http://schema.org/sequel> <http://example.org/movie/207> .
<http://example.org/movie/201> <http://schema.org/title> "First Blood"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/201> <http://schema.org/year> "1982"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/201> <http://schema.org/director> <http://example.org/person/104> .
<http://example.org/movie/201> <http://schema.org/cast> <http://example.org/person/105> .
<http://example.org/movie/201> <http://schema.org/cast> <http://example.org/person/106> .
<http://example.org/movie/201> <http://schema.org/cast> <http://example.org/person/107> .
<http://example.org/movie/201> <http://schema.org/sequel> <http://example.org/movie/209> .
<http://example.org/movie/202> <http://schema.org/title> "Predator"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/202> <http://schema.org/year> "1987"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/202> <http://schema.org/director> <http://example.org/person/108> .
<http://example.org/movie/202> <http://schema.org/cast> <http://example.org/person/101> .
<http://example.org/movie/202> <http://schema.org/cast> <http://example.org/person/109> .
<http://example.org/movie/202> <http://schema.org/cast> <http://example.org/person/110> .
<http://example.org/movie/202> <http://schema.org/sequel> <http://example.org/movie/211> .
<http://example.org/movie/203> <http://schema.org/title> "Lethal Weapon"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/203> <http://schema.org/year> "1987"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/203> <http://schema.org/director> <http://example.org/person/111> .
<http://example.org/movie/203> <http://schema.org/cast> <http://example.org/person/112> .
<http://example.org/movie/203> <http://schema.org/cast> <http://example.org/person/113> .
<http://example.org/movie/203> <http://schema.org/cast> <http://example.org/person/114> .
<http://example.org/movie/203> <http://schema.org/sequel> <http://example.org/movie/212> .
<http://example.org/movie/204> <http://schema.org/title> "RoboCop"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/204> <http://schema.org/year> "1987"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/204> <http://schema.org/director> <http://example.org/person/115> .
<http://example.org/movie/204> <http://schema.org/cast> <http://example.org/person/116> .
<http://example.org/movie/204> <http://schema.org/cast> <http://example.org/person/117> .
<http://example.org/movie/204> <http://schema.org/cast> <http://example.org/person/118> .
<http://example.org/movie/205> <http://schema.org/title> "Commando"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/205> <http://schema.org/year> "1985"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/205> <http://schema.org/director> <http://example.org/person/119> .
<http://example.org/movie/205> <http://schema.org/cast> <http://example.org/person/101> .
<http://example.org/movie/205> <http://schema.org/cast> <http://example.org/person/120> .
<http://example.org/movie/205> <http://schema.org/cast> <http://example.org/person/121> .
<http://example.org/movie/206> <http://schema.org/title> "Die Hard"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/206> <http://schema.org/year> "1988"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/206> <http://schema.org/director> <http://example.org/person/108> .
<http://example.org/movie/206> <http://schema.org/cast> <http://example.org/person/122> .
<http://example.org/movie/206> <http://schema.org/cast> <http://example.org/person/123> .
<http://example.org/movie/206> <http://schema.org/cast> <http://example.org/person/124> .
<http://example.org/movie/207> <http://schema.org/title> "Terminator 2: Judgment Day"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/207> <http://schema.org/year> "1991"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/207> <http://schema.org/director> <http://example.org/person/100> .
<http://example.org/movie/207> <http://schema.org/cast> <http://example.org/person/101> .
<http://example.org/movie/207> <http://schema.org/cast> <http://example.org/person/102> .
<http://example.org/movie/207> <http://schema.org/cast> <http://example.org/person/125> .
<http://example.org/movie/207> <http://schema.org/cast> <http://example.org/person/126> .
<http://example.org/movie/207> <http://schema.org/sequel> <http://example.org/movie/208> .
<http://example.org/movie/208> <http://schema.org/title> "Terminator 3: Rise of the Machines"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/208> <http://schema.org/year> "2003"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/208> <http://schema.org/director> <http://example.org/person/127> .
<http://example.org/movie/208> <http://schema.org/cast> <http://example.org/person/101> .
<http://example.org/movie/208> <http://schema.org/cast> <http://example.org/person/128> .
<http://example.org/movie/208> <http://schema.org/cast> <http://example.org/person/129> .
<http://example.org/movie/209> <http://schema.org/title> "Rambo: First Blood Part II"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/209> <http://schema.org/year> "1985"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/209> <http://schema.org/director> <http://example.org/person/130> .
<http://example.org/movie/209> <http://schema.org/cast> <http://example.org/person/105> .
<http://example.org/movie/209> <http://schema.org/cast> <http://example.org/person/106> .
<http://example.org/movie/209> <http://schema.org/cast> <http://example.org/person/131> .
<http://example.org/movie/209> <http://schema.org/sequel> <http://example.org/movie/210> .
<http://example.org/movie/210> <http://schema.org/title> "Rambo III"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/210> <http://schema.org/year> "1988"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/210> <http://schema.org/director> <http://example.org/person/132> .
<http://example.org/movie/210> <http://schema.org/cast> <http://example.org/person/105> .
<http://example.org/movie/210> <http://schema.org/cast> <http://example.org/person/106> .
<http://example.org/movie/210> <http://schema.org/cast> <http://example.org/person/133> .
<http://example.org/movie/211> <http://schema.org/title> "Predator 2"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/211> <http://schema.org/year> "1990"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/211> <http://schema.org/director> <http://example.org/person/134> .
<http://example.org/movie/211> <http://schema.org/cast> <http://example.org/person/113> .
<http://example.org/movie/211> <http://schema.org/cast> <http://example.org/person/114> .
<http://example.org/movie/211> <http://schema.org/cast> <http://example.org/person/135> .
<http://example.org/movie/212> <http://schema.org/title> "Lethal Weapon 2"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/212> <http://schema.org/year> "1989"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/212> <http://schema.org/director> <http://example.org/person/111> .
<http://example.org/movie/212> <http://schema.org/cast> <http://example.org/person/112> .
<http://example.org/movie/212> <http://schema.org/cast> <http://example.org/person/113> .
<http://example.org/movie/212> <http://schema.org/cast> <http://example.org/person/136> .
<http://example.org/movie/212> <http://schema.org/sequel> <http://example.org/movie/213> .
<http://example.org/movie/213> <http://schema.org/title> "Lethal Weapon 3"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/213> <http://schema.org/year> "1992"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/213> <http://schema.org/director> <http://example.org/person/111> .
<http://example.org/movie/213> <http://schema.org/cast> <http://example.org/person/112> .
<http://example.org/movie/213> <http://schema.org/cast> <http://example.org/person/113> .
<http://example.org/movie/213> <http://schema.org/cast> <http://example.org/person/136> .
<http://example.org/movie/214> <http://schema.org/title> "Alien"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/214> <http://schema.org/year> "1979"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/214> <http://schema.org/director> <http://example.org/person/137> .
<http://example.org/movie/214> <http://schema.org/cast> <http://example.org/person/138> .
<http://example.org/movie/214> <http://schema.org/cast> <http://example.org/person/139> .
<http://example.org/movie/214> <http://schema.org/cast> <http://example.org/person/140> .
<http://example.org/movie/214> <http://schema.org/sequel> <http://example.org/movie/215> .
<http://example.org/movie/215> <http://schema.org/title> "Aliens"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/215> <http://schema.org/year> "1986"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/215> <http://schema.org/director> <http://example.org/person/100> .
<http://example.org/movie/215> <http://schema.org/cast> <http://example.org/person/139> .
<http://example.org/movie/215> <http://schema.org/cast> <http://example.org/person/141> .
<http://example.org/movie/215> <http://schema.org/cast> <http://example.org/person/103> .
<http://example.org/movie/216> <http://schema.org/title> "Mad Max"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/216> <http://schema.org/year> "1979"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/216> <http://schema.org/director> <http://example.org/person/142> .
<http://example.org/movie/216> <http://schema.org/cast> <http://example.org/person/112> .
<http://example.org/movie/216> <http://schema.org/cast> <http://example.org/person/143> .
<http://example.org/movie/216> <http://schema.org/cast> <http://example.org/person/144> .
<http://example.org/movie/216> <http://schema.org/sequel> <http://example.org/movie/217> .
<http://example.org/movie/217> <http://schema.org/title> "Mad Max 2"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/217> <http://schema.org/year> "1981"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/217> <http://schema.org/director> <http://example.org/person/142> .
<http://example.org/movie/217> <http://schema.org/cast> <http://example.org/person/112> .
<http://example.org/movie/217> <http://schema.org/cast> <http://example.org/person/145> .
<http://example.org/movie/217> <http://schema.org/cast> <http://example.org/person/146> .
<http://example.org/movie/217> <http://schema.org/sequel> <http://example.org/movie/218> .
<http://example.org/movie/218> <http://schema.org/title> "Mad Max Beyond Thunderdome"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/218> <http://schema.org/year> "1985"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/218> <http://schema.org/director> <http://example.org/person/142> .
<http://example.org/movie/218> <http://schema.org/director> <http://example.org/person/147> .
<http://example.org/movie/218> <http://schema.org/cast> <http://example.org/person/112> .
<http://example.org/movie/218> <http://schema.org/cast> <http://example.org/person/148> .
<http://example.org/movie/219> <http://schema.org/title> "Braveheart"^^<http://www.w3.org/2001/XMLSchema#string> .
<http://example.org/movie/219> <http://schema.org/year> "1995"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.org/movie/219> <http://schema.org/director> <http://example.org/person/112> .
<http://example.org/movie/219> <http://schema.org/cast> <http://example.org/person/112> .
<http://example.org/movie/219> <http://schema.org/cast> <http://example.org/person/149> .
================================================
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
%% <http://a/b> - 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 = <http://example.com/a>.").
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}.
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
Condensed preview — 24 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (124K chars).
[
{
"path": ".gitignore",
"chars": 102,
"preview": "*.*~\n*.log\n*.aux\n*.beam\n*.dump\n*.tag.gz\n*.tgz\n_build/\ntests/\nrebar3\n*_leex.erl\n*_yeec.erl\n*.sublime-*\n"
},
{
"path": ".travis.yml",
"chars": 148,
"preview": "language: erlang\ndist: trusty\n\nscript:\n - make\n - make test\n - ./rebar3 coveralls send\n\notp_release:\n - 21.2\n "
},
{
"path": "Emakefile",
"chars": 124,
"preview": "{\"src/*\", [\n report, \n verbose, \n {i, \"include\"}, \n {outdir, \"_build/default/lib/datalog/ebin\"},\n debug_info \n"
},
{
"path": "LICENSE",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 268,
"preview": "##\n## @doc\n## an example Makefile to build and ship erlang software\n##\n## APP - identity of the application\n## ORG"
},
{
"path": "README.md",
"chars": 9547,
"preview": "# datalog\n\nDatalog is a query language based on the logic programming paradigm. The library is designed to formalize rel"
},
{
"path": "doc/syntax.md",
"chars": 7763,
"preview": "# datalog syntax\n\n> Datalog is a declarative logic programming language that syntactically is a subset of Prolog. It is "
},
{
"path": "erlang.mk",
"chars": 6300,
"preview": "##\n## Copyright (C) 2012 Dmitry Kolesnikov\n##\n## This Makefile may be modified and distributed under the terms\n## of the"
},
{
"path": "priv/imdb.config",
"chars": 12888,
"preview": "{<<\"urn:person:100\">>, <<\"name\">>, <<\"James Cameron\">>}.\n{<<\"urn:person:100\">>, <<\"born\">>, <<\"1954-08-16\">>}.\n\n{<<\"urn:"
},
{
"path": "priv/imdb.nt",
"chars": 24600,
"preview": "<http://example.org/person/100> <http://schema.org/name> \"James Cameron\"^^<http://www.w3.org/2001/XMLSchema#string> .\n<h"
},
{
"path": "rebar.config",
"chars": 253,
"preview": "{erl_opts, [\n]}.\n\n{deps, [\n datum\n]}.\n\n%%\n%%\n{plugins , [coveralls]}.\n{cover_enabled , true}.\n"
},
{
"path": "rebar.config.script",
"chars": 198,
"preview": "case os:getenv(\"TRAVIS\") of\n \"true\" ->\n JobId = os:getenv(\"TRAVIS_JOB_ID\"),\n lists:keystore(coveralls_service_j"
},
{
"path": "src/datalog.app.src",
"chars": 447,
"preview": "{application, datalog,\n [\n {description, \"datalog is a query language based on the logic programming paradigm\"},\n"
},
{
"path": "src/datalog.erl",
"chars": 6220,
"preview": "%%\n%% Copyright 2014 - 2016 Dmitry Kolesnikov, All Rights Reserved\n%%\n%% Licensed under the Apache License, Version "
},
{
"path": "src/datalog.hrl",
"chars": 770,
"preview": "%% @doc\n%% abstract syntax tree \n\n%%\n%% Erlang Native Format\n\n\n\n-type id() :: atom() | {atom(), atom()} | {iri, bi"
},
{
"path": "src/datalog_lang.erl",
"chars": 4439,
"preview": "%%\n%% Copyright 2014 - 2016 Dmitry Kolesnikov, All Rights Reserved\n%%\n%% Licensed under the Apache License, Version "
},
{
"path": "src/datalog_leex.xrl",
"chars": 1655,
"preview": "%%\n%% Copyright 2014 - 2015 Dmitry Kolesnikov, All Rights Reserved\n%%\n%% Licensed under the Apache License, Version "
},
{
"path": "src/datalog_list.erl",
"chars": 2146,
"preview": "%%\n%% Copyright 2014 - 2015 Dmitry Kolesnikov, All Rights Reserved\n%%\n%% Licensed under the Apache License, Version "
},
{
"path": "src/datalog_q.erl",
"chars": 3058,
"preview": "%%\n%% Copyright 2014 - 2018 Dmitry Kolesnikov, All Rights Reserved\n%%\n%% Licensed under the Apache License, Version "
},
{
"path": "src/datalog_vm.erl",
"chars": 4196,
"preview": "%%\n%% Copyright 2014 - 2018 Dmitry Kolesnikov, All Rights Reserved\n%%\n%% Licensed under the Apache License, Version "
},
{
"path": "src/datalog_yeec.yrl",
"chars": 4274,
"preview": "%%\n%% Copyright 2014 - 2015 Dmitry Kolesnikov, All Rights Reserved\n%%\n%% Licensed under the Apache License, Version "
},
{
"path": "test/datalog_SUITE.erl",
"chars": 6300,
"preview": "-module(datalog_SUITE).\n-include_lib(\"common_test/include/ct.hrl\").\n-compile({parse_transform, category}).\n\n-export([all"
},
{
"path": "test/datalog_parser_SUITE.erl",
"chars": 10099,
"preview": "%%\n%% Copyright 2014 - 2015 Dmitry Kolesnikov, All Rights Reserved\n%%\n%% Licensed under the Apache License, Version "
},
{
"path": "test/tests.config",
"chars": 33,
"preview": "%%\n%% suites\n{suites, \".\", all}.\n"
}
]
About this extraction
This page contains the full source code of the fogfish/datalog GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 24 files (114.4 KB), approximately 37.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.