Full Code of fogfish/datalog for AI

master 6e302ac4a7ba cached
24 files
114.4 KB
37.4k tokens
1 requests
Download .txt
Repository: fogfish/datalog
Branch: master
Commit: 6e302ac4a7ba
Files: 24
Total size: 114.4 KB

Directory structure:
gitextract_dy2df2ny/

├── .gitignore
├── .travis.yml
├── Emakefile
├── LICENSE
├── Makefile
├── README.md
├── doc/
│   └── syntax.md
├── erlang.mk
├── priv/
│   ├── imdb.config
│   └── imdb.nt
├── rebar.config
├── rebar.config.script
├── src/
│   ├── datalog.app.src
│   ├── datalog.erl
│   ├── datalog.hrl
│   ├── datalog_lang.erl
│   ├── datalog_leex.xrl
│   ├── datalog_list.erl
│   ├── datalog_q.erl
│   ├── datalog_vm.erl
│   └── datalog_yeec.yrl
└── test/
    ├── datalog_SUITE.erl
    ├── datalog_parser_SUITE.erl
    └── tests.config

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.*~
*.log
*.aux
*.beam
*.dump
*.tag.gz
*.tgz
_build/
tests/
rebar3
*_leex.erl
*_yeec.erl
*.sublime-*


================================================
FILE: .travis.yml
================================================
language: erlang
dist: trusty

script:
   - make
   - make test
   - ./rebar3 coveralls send

otp_release:
   - 21.2
   - 20.3
   - 19.3
   - 18.3



================================================
FILE: Emakefile
================================================
{"src/*", [
   report, 
   verbose, 
   {i, "include"}, 
   {outdir, "_build/default/lib/datalog/ebin"},
   debug_info 
]}.


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: Makefile
================================================
##
## @doc
##   an example Makefile to build and ship erlang software
##
##   APP - identity of the application
##   ORG - identity of the organization 
##   URI - identity of the docker repository with last /  

APP = datalog 
ORG = fogfish
URI = 

include erlang.mk


================================================
FILE: README.md
================================================
# datalog

Datalog is a query language based on the logic programming paradigm. The library is designed to formalize relation of n-ary streams. It implements an ad-hoc query engine using simplified version of general _logic programming_ paradigm. The library facilitates development of data integration, information exchange and semantic web applications.

[![Build Status](https://secure.travis-ci.org/fogfish/datalog.svg?branch=master)](http://travis-ci.org/fogfish/datalog)
[![Coverage Status](https://coveralls.io/repos/github/fogfish/datalog/badge.svg?branch=master)](https://coveralls.io/github/fogfish/datalog?branch=master)
[![Hex.pm](https://img.shields.io/hexpm/v/datalog.svg)](https://hex.pm/packages/datalog) 
[![Hex Downloads](https://img.shields.io/hexpm/dt/datalog.svg)](https://hex.pm/packages/datalog)

## Key features

* Top-down, sub-query, breath-first evaluation algorithm of logical program
* Erlang native interface to describe relation of streams
* Formalizes streams relation using human readable language
* Supports conjunctions, unions and recursion

### Background

The logic program consists of finite set of rules and large volume of ground facts -- knowledge. The rules are used to deduce new facts from other facts (built new relations). The [Horn clauses](https://en.wikipedia.org/wiki/Horn_clause) formally defines rules (first-order formula)

```
p₀(ẋ₀) :- p₁(ẋ₁) ^ ... ^ pₙ(ẋₙ).
```

`p₀` is a rule head, it is a producer of new relation (facts). The body is a conjunction of predicates. Each `pᵢ` is a predicate expression consist of predicate symbol and terms such as `p(t₁ , ... , tₙ)`, terms are either a literal constant or a variable. The predicate expression refers to relation of arbitrary arity - stream of tuples; terms range over this stream of tuples. Body predicates refers either to derived relations or ground facts. The predicates with common variables give rise to join. Ground facts are physically stored in external memory and accesses using streams abstractions.

A head is a new derived relation, deducted through the _logical program_ (body of horn clause) and ground facts. It is not explicitly persisted anywhere and corresponds the relation view (projection). The materialization of these view is the main task of this library.

A naive example, a new relation `about` is deducted from two relations `category` and `article`.
```
about(title, subject) :- category(x, subject), article(title, x).
```

### σ function

The library uses a "functional" interpretation of predicates, any predicate is a function -- sigma expression. It associates some of its bound terms to the remaining ones, returning the lazy set of tuples corresponding to materialized predicate. For example if p is binary predicate, its σ function is denoted as

```
σ(S) -> { ẋ ∈ S ^ p(ẋ) }
```

The library translate goals of rules into algebraic queries with an objective to access the minimum of ground facts needed in order to determine the answer. Rules are compiled to composition of σ functions (sub-queries). They are recursively expanded and the evaluation of the current sub-query is postponed until the new sub-query has been completely solved. 

The defined sigma expression formalism translates purely declarative semantic into operational semantic, i.e. specify of query must be executed. The lazy set ensures simplicity of one-tuple-at-a-time evaluation strategy while preserving efficiency of set-oriented methods used by high-level query languages. 

The sigma function is the formalism to relate logic program to ground facts persisted by external storage (most common query languages, access methodologies, i/o interfaces). The library uses sigma algebra to evaluate logic program but it requires developers to implement corresponding access protocols supported by external storage. This is an abstraction interface to retrieve ground facts _matching_ predicate. The library hides the concerns of logical program evaluation but provides hooks to implement access protocols.


## Getting started

The latest version of the library is available at its `master` branch. All development, including new features and bug fixes, take place on the `master` branch using forking and pull requests as described in contribution guidelines.

### Installation

The stable library release is available via hex packages, add the library as dependency to `rebar.config`

```erlang
{deps, [{datalog}]}.
``` 

### Usage

The library requires implementation of streaming interface to fetch a ground facts from external storage. It provides a [reference implementation](src/datalog_lists.erl) to deal with lists.

Let's consider usage example of library using [movies dataset](priv/imdb.config) and human readable [datalog](doc/syntax.md).

Build library and run the development console

```bash
make && make run
```

The typical usage scenario **parse**, **compile** and **evaluate**.

```erlang
%% parse query
Q = datalog:p("?- h(_, _). f(x,y). h(x,y) :- f(x,y), y > 1.").

%% compile query
E = datalog:c(datalog_list, Q).

%% evaluate query
S = datalog:q(E, [{a, 1}, {b, 2}, {c, 3}]).

%% [ 
%%   [b,2], [c,3] 
%% ]
stream:list(S).
```

Let's consider a complex scenarios with a reference dataset about movies.

```erlang
{ok, Imdb} = file:consult("./priv/imdb.config").
```

#### Basic queries

Match a person from dataset using query goals

```erlang
%%
%% define a query goal to match a person with `name` equal to `Ridley Scott`.
%% An identity rule is used to produce stream of tuples 
Q = "?- h(_, \"name\", \"Ridley Scott\"). h(s, p, o) :- f(s, p, o).".

%%
%% parse and compile a query into executable function
F = datalog:c(datalog_list, datalog:p(Q)).

%%
%% apply the function to dataset and materialize a stream of tuple, it returns
%% [
%%    [<<"urn:person:137">>,<<"name">>,<<"Ridley Scott">>]
%% ]
stream:list(F(Imdb)).
```

#### Data patterns

Match a person from dataset using patterns: literals and guards

```erlang
Q = "
   ?- h(_, _). 

   f(s, p, o). 

   h(s, o) :- 
      f(s, \"name\", o), o = \"Ridley Scott\".
".

%%
%% [
%%    [<<"urn:person:137">>,<<"Ridley Scott">>]
%% ]
F = datalog:c(datalog_list, datalog:p(Q)).
stream:list(F(Imdb)).
```

Discover all movies produces in 1987

```erlang
Q = "
   ?- h(_, _). 

   f(s, p, o). 

   h(s, title) :- 
      f(s, \"year\", 1987), 
      f(s, \"title\", title).
".

%%
%% [
%%    [<<"urn:movie:202">>,<<"Predator">>],
%%    [<<"urn:movie:203">>,<<"Lethal Weapon">>],
%%    [<<"urn:movie:204">>,<<"RoboCop">>]
%% ]
F = datalog:c(datalog_list, datalog:p(Q)).
stream:list(F(Imdb)).
```

Discover all actors of "Lethal Weapon" movie.

```erlang
Q = "
   ?- h(_). 

   f(s, p, o). 

   h(name) :- 
      f(m, \"title\", \"Lethal Weapon\"), 
      f(m, \"cast\", p), 
      f(p, \"name\", name).
".

%%
%% [
%%    [<<"Mel Gibson">>],
%%    [<<"Danny Glover">>],
%%    [<<"Gary Busey">>]
%% ]
F = datalog:c(datalog_list, datalog:p(Q)).
stream:list(F(Imdb)).
```

#### Predicates

Discover all movies produced before 1984

```erlang
Q = "
   ?- h(_, _). 

   f(s, p, o).

   h(title, year) :- 
      f(s, \"year\", year), 
      f(s, \"title\", title), 
      year < 1984.
".

%%
%% [
%%    [<<"First Blood">>,1982],
%%    [<<"Alien">>,1979],
%%    [<<"Mad Max">>,1979],
%%    [<<"Mad Max 2">>,1981]
%% ]
F = datalog:c(datalog_list, datalog:p(Q)).
stream:list(F(Imdb)).
```


### Design custom σ function

σ function is a partial application takes terms and side-effect environment, it returns a stream on tuples matching the terms pattern.

As an example, the following sigma function takes two terms (2-arity) and returns corresponding stream of tuples. The library uses list `[_]` as data structure for tuples. It allows efficiently bind deducted values to term variable. Thus, each sigma function return stream (lazy list) of lists.

```erlang
f(_, _, [X, Y]) ->
   fun(Env) ->
      stream:build(...)
   end.
```

Each term takes one of the following types `undefined | [filter()] | literal()`:
* `undefined` terms position corresponds to free variable, streams output at this 
position will be bound to corresponding variable at datalog expression.
* `[filter()]` defines acceptable range of term values
* `literal()` defines exact matching of term at stream


## Reference

1. [What You Always Wanted to Know About Datalog (And Never Dared to Ask)](https://pdfs.semanticscholar.org/9374/f0da312f3ba77fa840071d68935a28cba364.pdf)
1. [Theory of Relational Databases](http://www.cs.nott.ac.uk/~psznza/G53RDB07/rdb14.pdf)
1. [Foundation of Database](https://wiki.epfl.ch/provenance2011/documents/foundations%20of%20databases-abiteboul-1995.pdf)
1. [Recursive Programming in Datalog](http://infolab.stanford.edu/~ullman/dscb/dl-old.pdf)
1. [Datalog and Recursive Query Processing](http://blogs.evergreen.edu/sosw/files/2014/04/Green-Vol5-DBS-017.pdf)
1. http://ion.uwinnipeg.ca/~ychen2/journalpapers/StratifiedDB.pdf
1. http://www.cs.toronto.edu/~drosu/csc343-l7-handout6.pdf


## License

Copyright 2014 Dmitry Kolesnikov

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.


================================================
FILE: doc/syntax.md
================================================
# datalog syntax

> Datalog is a declarative logic programming language that syntactically is a subset of Prolog. It is often used as a query language for deductive databases... Datalog is not Turing complete, and is thus used as a domain-specific language. Unlike in Prolog, Datalog queries on finite sets are guaranteed to terminate.
> https://en.wikipedia.org/wiki/Datalog

This document depicts a datalog syntax supported by the library and it extensions that implements enhancement for Semantic Web and bindings with Erlang runtime.

The datalog program consist of finite set of rules and references to ground facts, which are stored in external memory. The rules are used to deduce new facts from other facts. It is important to understand: 
* there are no functions symbols in datalog, each predicate symbol refers to relation of arbitrary arity.
* datalog has a purely declarative semantics - the order of clauses is irrelevant.

## Rules

The [Horn clauses](https://en.wikipedia.org/wiki/Horn_clause) formally defines datalog rules (first-order formula)

```
p₀(ẋ₀) :- p₁(ẋ₁), ... , pₙ(ẋₙ).
```

`p₀` is a rule head, which represents a newly derived relation, deducted through horn clauses evaluation. The body is a conjunction of predicates `p₁ , ... , pₙ`. Each predicate refers to either derived or ground relation of arity equals to `|ẋ|`. The language support primitive predicates using infix notation, e.g. equality predicate `x = 1`. 

`ẋ₀ , ... , ẋₙ` are vector of variables and constants. Every variable of `ẋ₀` must occur in `ẋ₁ , ... , ẋₙ` so that rule is range restricted. The predicates with common variables give rise to join of relations. Vector `|ẋ|` facilitates predicate to map a relation from domain Dⁿ to boolean values so that program is able to holds in each relation that obeys rules.

A datalog program is a finite set of rules.

Example of rules

```
h(x, z) :- a(x, y), b(y, z).
```

## Constants

Constants are predicate terms that do not change its value during the evaluation of program. Constants facilitate in pattern matching of ground relations while evaluating a programs. The library uses [semantic types](https://github.com/fogfish/semantic/blob/master/doc/datatype.md) to express constants. The usage of semantic types improves ambiguous translations of string literals to type system used at external memory. 

Data type | Syntax | Example
--- | --- | ---
xsd:anyURI |  `<.+>` | `<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}.
Download .txt
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.

Copied to clipboard!