Repository: afiniate/seresye Branch: master Commit: fd4a0713402a Files: 63 Total size: 238.8 KB Directory structure: gitextract_cfewqrrs/ ├── .gitignore ├── LICENSE.md ├── README.md ├── do-gh-pages ├── doc/ │ ├── CNAME │ ├── _layouts/ │ │ └── default.html │ ├── assets/ │ │ ├── css/ │ │ │ ├── code.css │ │ │ ├── prettify.css │ │ │ ├── skeleton/ │ │ │ │ ├── 404.html │ │ │ │ ├── index.html │ │ │ │ ├── javascripts/ │ │ │ │ │ └── tabs.js │ │ │ │ ├── robots.txt │ │ │ │ └── stylesheets/ │ │ │ │ ├── base.css │ │ │ │ ├── layout.css │ │ │ │ └── skeleton.css │ │ │ └── style.css │ │ └── javascript/ │ │ ├── html5.js │ │ ├── lang-apollo.js │ │ ├── lang-clj.js │ │ ├── lang-css.js │ │ ├── lang-go.js │ │ ├── lang-hs.js │ │ ├── lang-lisp.js │ │ ├── lang-lua.js │ │ ├── lang-ml.js │ │ ├── lang-n.js │ │ ├── lang-proto.js │ │ ├── lang-scala.js │ │ ├── lang-sql.js │ │ ├── lang-tex.js │ │ ├── lang-vb.js │ │ ├── lang-vhdl.js │ │ ├── lang-wiki.js │ │ ├── lang-xq.js │ │ ├── lang-yaml.js │ │ └── prettify.js │ └── index.md ├── examples/ │ ├── seresye_phil.erl │ ├── seresye_prodcons.erl │ └── seresyee_auto.erl ├── features/ │ ├── seresyet_12.feature │ └── seresyet_13.feature ├── include/ │ └── seresye.hrl ├── rebar.config ├── sinan.config ├── src/ │ ├── internal.hrl │ ├── seresye.app.src │ ├── seresye.erl │ ├── seresye_agenda.erl │ ├── seresye_app.erl │ ├── seresye_autoneg.erl │ ├── seresye_engine.erl │ ├── seresye_speedtest.erl │ ├── seresye_sup.erl │ ├── seresye_transform.erl │ └── seresye_tree_list.erl └── test/ ├── seresyet_12.erl ├── seresyet_13.erl ├── seresyet_cannibals.erl ├── seresyet_relatives.erl ├── seresyet_sample.erl ├── seresyet_sieve.erl └── seresyet_simple_relatives.erl ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ _build erl_crash.dump doc/_site deps ebin ================================================ FILE: LICENSE.md ================================================ BSD License =========== Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Francesca Gangemi, Corrado Santoro may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY Francesca Gangemi AND Corrado Santoro ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ SERESYE - Swarm oriented ERlang Expert SYstem Engine ==================================================== Introduction ------------ SERESYE is a Rete based rules engine written in Erlang, descended directly from the Eresye project by Francesca Gangemi and Corrado Santoro. In the following article we will describe how to use the system. As it is widely known, a rule-based system is composed by a **knowledge base**, which stores a set of *facts* representing the 'universe of discourse' of a given application, and a set of **production rules**, which are used to infer knowledge and/or reason about the knowledge. A rule is activated when one or more facts match the template(s) given in the rule declaration: in such a case, the body of the rule contains a code that is thus executed In SERESYE, *facts* are expressed by means of Erlang tuples or records, while rules are written using standard Erlang function clauses, whose declaration reports, in the clause head, the facts or fact templates that have to be matched for the rule to be activated and executed. For more information about SERESYE please refer to the paper docs directory. For more information about rule-based inference engines and expert systems, you can refer to the book: *S. Russell and P. Norvig. **Artificial Intelligence: A Modern Approach/2E.** Prentice Hall, 2003.* To write an AI application with SERESYE the following steps have to be performed: 1. Indentify your universe of discourse and determine the facts that have to be used to represent such a world; 2. Indentify the rules that you need and write them by using, e.g. first-order-logic predicates or even natural language; 3. Implement the system by writing your rules as Erlang function clauses, according to the modality required by SERESYE. The Application: the Domain of Relatives ---------------------------------------- We will design a system able to derive new knowledge using some inference rules and starting from a small set; as a sample application, we chose the domain of relatives: we will start from some base concepts, such as *parent*, *male* and *female*, and then, by means of a proper set of rules, we will derive the concepts of *mother*, *father*, *sister*, *brother*, *grandmother* and *grandfather*. According to the list above, we will first derive the facts that will be used to represent our concepts. Given the set of relationships above, they will be represented by means of the following facts:
# Concept Fact / Erlang tuple
1 X is male {male, X}
2 X is female {female, X}
3 X is Y's parent {parent, X, Y}
4 X is Y's mother {mother, X, Y}
5 X is Y's father {father, X, Y}
6 X is Y's sister {sister, X, Y}
7 X is Y's brother {brother, X, Y}
8 X is Y's grandmother {grandmother, X, Y}
9 X is Y's grandfather {grandfather, X, Y}
Concepts 1, 2 and 3 will be used as a base to derive the other ones. Deriving new concepts by means of rules --------------------------------------- #### Concept: mother The rule to derive the concept of mother is quite straightforward: if X is female and X is Y's parent then X is Y's mother. From the point of view of SERESYE, since knowledge is stored in the *knowledge base* of the engine, the rule above is translated into the following one: *if the facts {female, X} and {parent, X, Y} are *asserted* in the knowledge base, then we assert the fact {mother, X, Y}. The rule *mother* can be thus written as follows: %% %% if (X is female) and (X is Y's parent) then (X is Y's mother) %% mother (Engine, {female, X}, {parent, X, Y}) -> seresye:assert (Engine, {mother, X, Y}). #### Concept: father This concept can be easily derived by means of the following rule: %% %% if (X is male) and (X is Y's parent) then (X is Y's father) %% father (Engine, {male, X}, {parent, X, Y}) -> seresye:assert (Engine, {father, X, Y}). #### Concept: sister This concept can be expressed by the following rule: if Y and Z have the same parent and Z is female, then Z is the Y's sister. The SERESYE rule used to map this concept is: %% %% if (Y and Z have the same parent X) and (Z is female) %% then (Z is Y's sister) %% sister (Engine, {parent, X, Y}, {parent, X, Z}, {female, Z}) when Y =/= Z -> seresye:assert (Engine, {sister, Z, Y}). Please note the guard, which is needed to ensure that when Y and Z are bound to the same value, the rule is not activated (indeed this is possible since the same fact can match both the first and second 'parent' pattern). #### Concept: brother Given the previous one, this concept is now quite simple to implement: %% %% if (Y and Z have the same parent X) and (Z is male) %% then (Z is Y's brother) %% brother (Engine, {parent, X, Y}, {parent, X, Z}, {male, Z}) when Y =/= Z -> seresye:assert (Engine, {brother, Z, Y}). #### Concepts: grandmother and grandfather The former concept can be expressed by means of the rule: if X is Y's mother and Y is Z's parent, then X is Z's grandmother.* The latter concept is now obvious. Both can be implemented using the following SERESYE rules: %% %% if (X is Y's mother) and (Y is Z's parent) %% then (X is Z's grandmother) %% grandmother (Engine, {mother, X, Y}, {parent, Y, Z}) -> seresye:assert (Engine, {grandmother, X, Z}). %% %% if (X is Y's father) and (Y is Z's parent) %% then (X is Z's grandfather) %% grandfather (Engine, {father, X, Y}, {parent, Y, Z}) -> seresye:assert (Engine, {grandfather, X, Z}). Instantiating the Engine and Populating the Knowledge Base ---------------------------------------------------------- After writing the rules, we need to: - define the rules to seresye - instantiate the engine; - populate the knowledge base with a set of initial facts. We define the rules to SERESYE by defined a 'rules' attribute at the start of the module. %%% %%% relatives.erl %%% -module (relatives). -export([father/3, grandfather/3, grandmother/3, mother/3, brother/4, sister/4, start/0]). -rules([mother, father, brother, sister, grandfather, grandmother]). We continue on to instantiate the engine and popoulate the knowledge base in the function *start* below: start () -> application:start(seresye) % Only if it is not already started seresye:start(relatives), seresye:add_rules(relatives, ?MODULE) seresye:assert(relatives, [{male, bob}, {male, corrado}, {male, mark}, {male, caesar}, {female, alice}, {female, sara}, {female, jane}, {female, anna}, {parent, jane, bob}, {parent, corrado, bob}, {parent, jane, mark}, {parent, corrado, mark}, {parent, jane, alice}, {parent, corrado, alice}, {parent, bob, caesar}, {parent, bob, anna}, {parent, sara, casear}, {parent, sara, anna}]), ok. As the listing reports, creating a new SERESYE engine implies to call the function *seresye:start/1*, giving the name of the engine to be created Then, we have to add the rules to the engine by using the function *seresye:add_rule/2*: it takes two arguments, the name of the engine and a tuple representing the function in the form *{Module, FuncName}*; obviously the function *Module:FuncName* must be exported. Function *add_rule* has to be called for each rule that has to be added; for this reason, the code above has an iteration over the list of rules written before. Finally, we populate the inference engine with a set of sample facts by giving them, in a list, to the function *seresye:assert/2*. To test our rules, we considered the relationships in the Figure below and assert only the facts for *male*, *female* and *parent*. Testing the application ----------------------- The final complete code of our AI application is thus the following: %%% %%% relatives.erl %%% -module (relatives). -export([father/3, grandfather/3, grandmother/3, mother/3, brother/4, sister/4, start/0]). %% %% if (X is female) and (X is Y's parent) then (X is Y's mother) %% mother(Engine, {female, X}, {parent, X, Y}) -> seresye:assert(Engine, {mother, X, Y}). %% %% if (X is male) and (X is Y's parent) then (X is Y's father) %% father(Engine, {male, X}, {parent, X, Y}) -> seresye:assert(Engine, {father, X, Y}). %% %% if (Y and Z have the same parent X) and (Z is female) %% then (Z is Y's sister) %% sister(Engine, {parent, X, Y}, {parent, X, Z}, {female, Z}) when Y =/= Z -> seresye:assert(Engine, {sister, Z, Y}). %% %% if (Y and Z have the same parent X) and (Z is male) %% then (Z is Y's brother) %% brother(Engine, {parent, X, Y}, {parent, X, Z}, {male, Z}) when Y =/= Z -> seresye:assert(Engine, {brother, Z, Y}). %% %% if (X is Y's father) and (Y is Z's parent) %% then (X is Z's grandfather) %% grandfather (Engine, {father, X, Y}, {parent, Y, Z}) -> seresye:assert (Engine, {grandfather, X, Z}). %% %% if (X is Y's mother) and (Y is Z's parent) %% then (X is Z's grandmother) %% grandmother(Engine, {mother, X, Y}, {parent, Y, Z}) -> seresye:assert(Engine, {grandmother, X, Z}). start () -> application:start(seresye), seresye:start (relatives), seresye:add_rules(relatives, ?MODULE) seresye:assert (relatives, [{male, bob}, {male, corrado}, {male, mark}, {male, caesar}, {female, alice}, {female, sara}, {female, jane}, {female, anna}, {parent, jane, bob}, {parent, corrado, bob}, {parent, jane, mark}, {parent, corrado, mark}, {parent, jane, alice}, {parent, corrado, alice}, {parent, bob, caesar}, {parent, bob, anna}, {parent, sara, casear}, {parent, sara, anna}]), ok. Now it's time to test our application: Erlang (BEAM) emulator version 5.5 [source] [async-threads:0] [hipe] Eshell V5.5 (abort with ^G) 1> c(relatives). {ok,relatives} 2> relatives:start(). ok 3> Following the call to function *relatives:start/0*, the engine is created and populated; if no errors occurred, the rules should have been processed and the new facts derived. To check this, we can use the function *seresye:get_kb/1*, which returns the list of facts asserted into the knowledge base of a given engine: 4> seresye:get_kb(relatives). [{brother,bob,mark}, {sister,alice,bob}, {sister,alice,mark}, {brother,bob,alice}, {brother,mark,alice}, {grandmother,jane,caesar}, {grandfather,corrado,caesar}, {grandmother,jane,anna}, {grandfather,corrado,anna}, {sister,anna,caesar}, {brother,caesar,anna}, {sister,anna,casear}, {mother,sara,anna}, {mother,sara,casear}, {parent,sara,anna}, {father,bob,anna}, {parent,sara,casear}, {father,bob,caesar}, {parent,bob,anna}, {father,corrado,alice}, {parent,bob,caesar}, {mother,jane,alice}, {parent,corrado,alice}, {father,corrado,mark}, {parent,jane,alice}, {mother,jane,mark}, {parent,corrado|...}, {brother|...}, {...}|...] 5> The presence of facts representing concepts like *father*, *sister*, etc., proves that the rules seems to be working as expected. We can however query the knowledge base using specific fact templates. For example, if we want to know who are Alice's brothers, we can use the function *seresye:query_kb/2* as follows: 6> seresye:query_kb(relatives, {brother, '_', alice}). [{brother,bob,alice},{brother,mark,alice}] 7> The facts returned conform to the relationships depicted in the figure above, thus proving that the rules written are really working. As the example shows, function *seresye:query_kb/2* takes the engine name as the first argument, while, for the second parameter, a tuple has to be specified, representing the fact template to be matched; in such a tuple, the atom *'_'* plays the role of a wildcard. However, to specify a more complex matching, a *fun* can be used as a tuple element; this *fun* has to return a boolean value which indicates if the element matches the template. For example, to select both Alice's and Anna's brothers, we can use the following function call: 7> seresye:query_kb(relatives, {brother, '_', fun (X) -> (X == alice) or (X == anna) end}). [{brother,bob,alice},{brother,mark,alice},{brother,caesar,anna}] 8> Conclusions ----------- This HowTo not only shows how to use the SERESYE engine to write an AI application, but also highlights the versatility of the Erlang language: the characteristics of functional and symbolic programming, together with the possibility of performing *introspection* of function declaration, can be successfully exploited for application domains which are completely new for Erlang but can surely be very interesting. ================================================ FILE: do-gh-pages ================================================ #!/bin/sh PARENT_SHA=$(git show-ref -s refs/heads/gh-pages) DOC_SHA=$(git ls-tree -d HEAD doc | awk '{print $3}') NEW_COMMIT=$(echo "Auto-update docs." | git commit-tree $DOC_SHA -p $PARENT_SHA) git update-ref refs/heads/gh-pages $NEW_COMMIT ================================================ FILE: doc/CNAME ================================================ seresye.org ================================================ FILE: doc/_layouts/default.html ================================================ {{ page.title }} x

SERESYE - {{ page.title }}

{{content}}
================================================ FILE: doc/assets/css/code.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight { background: #ffffff; } .highlight .c { color: #999988; font-style: italic } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { font-weight: bold } /* Keyword */ .highlight .o { font-weight: bold } /* Operator */ .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #999999 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { font-weight: bold } /* Keyword.Constant */ .highlight .kd { font-weight: bold } /* Keyword.Declaration */ .highlight .kn { font-weight: bold } /* Keyword.Namespace */ .highlight .kp { font-weight: bold } /* Keyword.Pseudo */ .highlight .kr { font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #009999 } /* Literal.Number */ .highlight .s { color: #bb8844 } /* Literal.String */ .highlight .na { color: #008080 } /* Name.Attribute */ .highlight .nb { color: #999999 } /* Name.Builtin */ .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ .highlight .no { color: #008080 } /* Name.Constant */ .highlight .ni { color: #800080 } /* Name.Entity */ .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ .highlight .nn { color: #555555 } /* Name.Namespace */ .highlight .nt { color: #000080 } /* Name.Tag */ .highlight .nv { color: #008080 } /* Name.Variable */ .highlight .ow { font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #009999 } /* Literal.Number.Float */ .highlight .mh { color: #009999 } /* Literal.Number.Hex */ .highlight .mi { color: #009999 } /* Literal.Number.Integer */ .highlight .mo { color: #009999 } /* Literal.Number.Oct */ .highlight .sb { color: #bb8844 } /* Literal.String.Backtick */ .highlight .sc { color: #bb8844 } /* Literal.String.Char */ .highlight .sd { color: #bb8844 } /* Literal.String.Doc */ .highlight .s2 { color: #bb8844 } /* Literal.String.Double */ .highlight .se { color: #bb8844 } /* Literal.String.Escape */ .highlight .sh { color: #bb8844 } /* Literal.String.Heredoc */ .highlight .si { color: #bb8844 } /* Literal.String.Interpol */ .highlight .sx { color: #bb8844 } /* Literal.String.Other */ .highlight .sr { color: #808000 } /* Literal.String.Regex */ .highlight .s1 { color: #bb8844 } /* Literal.String.Single */ .highlight .ss { color: #bb8844 } /* Literal.String.Symbol */ .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #008080 } /* Name.Variable.Class */ .highlight .vg { color: #008080 } /* Name.Variable.Global */ .highlight .vi { color: #008080 } /* Name.Variable.Instance */ .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ ================================================ FILE: doc/assets/css/prettify.css ================================================ .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} ================================================ FILE: doc/assets/css/skeleton/404.html ================================================ Your Page Title Here :)

Sorry. Couldn't Find That Page!

================================================ FILE: doc/assets/css/skeleton/index.html ================================================ Your Page Title Here :)

Skeleton

Version 1.1

================================================ FILE: doc/assets/css/skeleton/javascripts/tabs.js ================================================ /* * Skeleton V1.1 * Copyright 2011, Dave Gamache * www.getskeleton.com * Free to use under the MIT license. * http://www.opensource.org/licenses/mit-license.php * 8/17/2011 */ $(document).ready(function() { /* Tabs Activiation ================================================== */ var tabs = $('ul.tabs'); tabs.each(function(i) { //Get all tabs var tab = $(this).find('> li > a'); tab.click(function(e) { //Get Location of tab's content var contentLocation = $(this).attr('href'); //Let go if not a hashed one if(contentLocation.charAt(0)=="#") { e.preventDefault(); //Make Tab Active tab.removeClass('active'); $(this).addClass('active'); //Show Tab Content & add active class $(contentLocation).show().addClass('active').siblings().hide().removeClass('active'); } }); }); }); ================================================ FILE: doc/assets/css/skeleton/robots.txt ================================================ # www.robotstxt.org/ # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 User-agent: * ================================================ FILE: doc/assets/css/skeleton/stylesheets/base.css ================================================ /* * Skeleton V1.1 * Copyright 2011, Dave Gamache * www.getskeleton.com * Free to use under the MIT license. * http://www.opensource.org/licenses/mit-license.php * 8/17/2011 */ /* Table of Content ================================================== #Reset & Basics #Basic Styles #Site Styles #Typography #Links #Lists #Images #Buttons #Tabs #Forms #Misc */ /* #Reset & Basics (Inspired by E. Meyers) ================================================== */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } table { border-collapse: collapse; border-spacing: 0; } /* #Basic Styles ================================================== */ body { background: #fff; font: 14px/21px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; color: #444; -webkit-font-smoothing: antialiased; /* Fix for webkit rendering */ -webkit-text-size-adjust: 100%; } /* #Typography ================================================== */ h1, h2, h3, h4, h5, h6 { color: #181818; font-family: "Georgia", "Times New Roman", Helvetica, Arial, sans-serif; font-weight: normal; } h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; } h1 { font-size: 46px; line-height: 50px; margin-bottom: 14px;} h2 { font-size: 35px; line-height: 40px; margin-bottom: 10px; } h3 { font-size: 28px; line-height: 34px; margin-bottom: 8px; } h4 { font-size: 21px; line-height: 30px; margin-bottom: 4px; } h5 { font-size: 17px; line-height: 24px; } h6 { font-size: 14px; line-height: 21px; } .subheader { color: #777; } p { margin: 0 0 20px 0; } p img { margin: 0; } p.lead { font-size: 21px; line-height: 27px; color: #777; } em { font-style: italic; } strong { font-weight: bold; color: #333; } small { font-size: 80%; } /* Blockquotes */ blockquote, blockquote p { font-size: 17px; line-height: 24px; color: #777; font-style: italic; } blockquote { margin: 0 0 20px; padding: 9px 20px 0 19px; border-left: 1px solid #ddd; } blockquote cite { display: block; font-size: 12px; color: #555; } blockquote cite:before { content: "\2014 \0020"; } blockquote cite a, blockquote cite a:visited, blockquote cite a:visited { color: #555; } hr { border: solid #ddd; border-width: 1px 0 0; clear: both; margin: 10px 0 30px; height: 0; } /* #Links ================================================== */ a, a:visited { color: #333; text-decoration: underline; outline: 0; } a:hover, a:focus { color: #000; } p a, p a:visited { line-height: inherit; } /* #Lists ================================================== */ ul, ol { margin-bottom: 20px; } ul { list-style: none outside; } ol { list-style: decimal; } ol, ul.square, ul.circle, ul.disc { margin-left: 30px; } ul.square { list-style: square outside; } ul.circle { list-style: circle outside; } ul.disc { list-style: disc outside; } ul ul, ul ol, ol ol, ol ul { margin: 4px 0 5px 30px; font-size: 90%; } ul ul li, ul ol li, ol ol li, ol ul li { margin-bottom: 6px; } li { line-height: 18px; margin-bottom: 12px; } ul.large li { line-height: 21px; } li p { line-height: 21px; } /* #Images ================================================== */ img.scale-with-grid { max-width: 100%; height: auto; } /* #Buttons ================================================== */ a.button, button, input[type="submit"], input[type="reset"], input[type="button"] { background: #eee; /* Old browsers */ background: -moz-linear-gradient(top, rgba(255,255,255,.2) 0%, rgba(0,0,0,.2) 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.2)), color-stop(100%,rgba(0,0,0,.2))); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Chrome10+,Safari5.1+ */ background: -o-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Opera11.10+ */ background: -ms-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* IE10+ */ background: linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* W3C */ border: 1px solid #aaa; border-top: 1px solid #ccc; border-left: 1px solid #ccc; padding: 4px 12px; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; color: #444; display: inline-block; font-size: 11px; font-weight: bold; text-decoration: none; text-shadow: 0 1px rgba(255, 255, 255, .75); cursor: pointer; margin-bottom: 20px; line-height: 21px; font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; } a.button:hover, button:hover, input[type="submit"]:hover, input[type="reset"]:hover, input[type="button"]:hover { color: #222; background: #ddd; /* Old browsers */ background: -moz-linear-gradient(top, rgba(255,255,255,.3) 0%, rgba(0,0,0,.3) 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.3)), color-stop(100%,rgba(0,0,0,.3))); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Chrome10+,Safari5.1+ */ background: -o-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Opera11.10+ */ background: -ms-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* IE10+ */ background: linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* W3C */ border: 1px solid #888; border-top: 1px solid #aaa; border-left: 1px solid #aaa; } a.button:active, button:active, input[type="submit"]:active, input[type="reset"]:active, input[type="button"]:active { border: 1px solid #666; background: #ccc; /* Old browsers */ background: -moz-linear-gradient(top, rgba(255,255,255,.35) 0%, rgba(10,10,10,.4) 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.35)), color-stop(100%,rgba(10,10,10,.4))); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Chrome10+,Safari5.1+ */ background: -o-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Opera11.10+ */ background: -ms-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* IE10+ */ background: linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* W3C */ } .button.full-width, button.full-width, input[type="submit"].full-width, input[type="reset"].full-width, input[type="button"].full-width { width: 100%; padding-left: 0 !important; padding-right: 0 !important; text-align: center; } /* #Tabs (activate in tabs.js) ================================================== */ ul.tabs { display: block; margin: 0 0 20px 0; padding: 0; border-bottom: solid 1px #ddd; } ul.tabs li { display: block; width: auto; height: 30px; padding: 0; float: left; margin-bottom: 0; } ul.tabs li a { display: block; text-decoration: none; width: auto; height: 29px; padding: 0px 20px; line-height: 30px; border: solid 1px #ddd; border-width: 1px 1px 0 0; margin: 0; background: #f5f5f5; font-size: 13px; } ul.tabs li a.active { background: #fff; height: 30px; position: relative; top: -4px; padding-top: 4px; border-left-width: 1px; margin: 0 0 0 -1px; color: #111; -moz-border-radius-topleft: 2px; -webkit-border-top-left-radius: 2px; border-top-left-radius: 2px; -moz-border-radius-topright: 2px; -webkit-border-top-right-radius: 2px; border-top-right-radius: 2px; } ul.tabs li:first-child a.active { margin-left: 0; } ul.tabs li:first-child a { border-width: 1px 1px 0 1px; -moz-border-radius-topleft: 2px; -webkit-border-top-left-radius: 2px; border-top-left-radius: 2px; } ul.tabs li:last-child a { -moz-border-radius-topright: 2px; -webkit-border-top-right-radius: 2px; border-top-right-radius: 2px; } ul.tabs-content { margin: 0; display: block; } ul.tabs-content > li { display:none; } ul.tabs-content > li.active { display: block; } /* Clearfixing tabs for beautiful stacking */ ul.tabs:before, ul.tabs:after { content: '\0020'; display: block; overflow: hidden; visibility: hidden; width: 0; height: 0; } ul.tabs:after { clear: both; } ul.tabs { zoom: 1; } /* #Forms ================================================== */ form { margin-bottom: 20px; } fieldset { margin-bottom: 20px; } input[type="text"], input[type="password"], input[type="email"], textarea, select { border: 1px solid #ccc; padding: 6px 4px; outline: none; -moz-border-radius: 2px; -webkit-border-radius: 2px; border-radius: 2px; font: 13px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; color: #777; margin: 0; width: 210px; max-width: 100%; display: block; margin-bottom: 20px; background: #fff; } select { padding: 0; } input[type="text"]:focus, input[type="password"]:focus, input[type="email"]:focus, textarea:focus { border: 1px solid #aaa; color: #444; -moz-box-shadow: 0 0 3px rgba(0,0,0,.2); -webkit-box-shadow: 0 0 3px rgba(0,0,0,.2); box-shadow: 0 0 3px rgba(0,0,0,.2); } textarea { min-height: 60px; } label, legend { display: block; font-weight: bold; font-size: 13px; } select { width: 220px; } input[type="checkbox"] { display: inline; } label span, legend span { font-weight: normal; font-size: 13px; color: #444; } /* #Misc ================================================== */ .remove-bottom { margin-bottom: 0 !important; } .half-bottom { margin-bottom: 10px !important; } .add-bottom { margin-bottom: 20px !important; } ================================================ FILE: doc/assets/css/skeleton/stylesheets/layout.css ================================================ /* * Skeleton V1.1 * Copyright 2011, Dave Gamache * www.getskeleton.com * Free to use under the MIT license. * http://www.opensource.org/licenses/mit-license.php * 8/17/2011 */ /* Table of Content ================================================== #Site Styles #Page Styles #Media Queries #Font-Face */ /* #Site Styles ================================================== */ /* #Page Styles ================================================== */ /* #Media Queries ================================================== */ /* Smaller than standard 960 (devices and browsers) */ @media only screen and (max-width: 959px) {} /* Tablet Portrait size to standard 960 (devices and browsers) */ @media only screen and (min-width: 768px) and (max-width: 959px) {} /* All Mobile Sizes (devices and browser) */ @media only screen and (max-width: 767px) {} /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ @media only screen and (min-width: 480px) and (max-width: 767px) {} /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ @media only screen and (max-width: 479px) {} /* #Font-Face ================================================== */ /* This is the proper syntax for an @font-face file Just create a "fonts" folder at the root, copy your FontName into code below and remove comment brackets */ /* @font-face { font-family: 'FontName'; src: url('../fonts/FontName.eot'); src: url('../fonts/FontName.eot?iefix') format('eot'), url('../fonts/FontName.woff') format('woff'), url('../fonts/FontName.ttf') format('truetype'), url('../fonts/FontName.svg#webfontZam02nTh') format('svg'); font-weight: normal; font-style: normal; } */ ================================================ FILE: doc/assets/css/skeleton/stylesheets/skeleton.css ================================================ /* * Skeleton V1.1 * Copyright 2011, Dave Gamache * www.getskeleton.com * Free to use under the MIT license. * http://www.opensource.org/licenses/mit-license.php * 8/17/2011 */ /* Table of Contents ================================================== #Base 960 Grid #Tablet (Portrait) #Mobile (Portrait) #Mobile (Landscape) #Clearing */ /* #Base 960 Grid ================================================== */ .container { position: relative; width: 960px; margin: 0 auto; padding: 0; } .column, .columns { float: left; display: inline; margin-left: 10px; margin-right: 10px; } .row { margin-bottom: 20px; } /* Nested Column Classes */ .column.alpha, .columns.alpha { margin-left: 0; } .column.omega, .columns.omega { margin-right: 0; } /* Base Grid */ .container .one.column { width: 40px; } .container .two.columns { width: 100px; } .container .three.columns { width: 160px; } .container .four.columns { width: 220px; } .container .five.columns { width: 280px; } .container .six.columns { width: 340px; } .container .seven.columns { width: 400px; } .container .eight.columns { width: 460px; } .container .nine.columns { width: 520px; } .container .ten.columns { width: 580px; } .container .eleven.columns { width: 640px; } .container .twelve.columns { width: 700px; } .container .thirteen.columns { width: 760px; } .container .fourteen.columns { width: 820px; } .container .fifteen.columns { width: 880px; } .container .sixteen.columns { width: 940px; } .container .one-third.column { width: 300px; } .container .two-thirds.column { width: 620px; } /* Offsets */ .container .offset-by-one { padding-left: 60px; } .container .offset-by-two { padding-left: 120px; } .container .offset-by-three { padding-left: 180px; } .container .offset-by-four { padding-left: 240px; } .container .offset-by-five { padding-left: 300px; } .container .offset-by-six { padding-left: 360px; } .container .offset-by-seven { padding-left: 420px; } .container .offset-by-eight { padding-left: 480px; } .container .offset-by-nine { padding-left: 540px; } .container .offset-by-ten { padding-left: 600px; } .container .offset-by-eleven { padding-left: 660px; } .container .offset-by-twelve { padding-left: 720px; } .container .offset-by-thirteen { padding-left: 780px; } .container .offset-by-fourteen { padding-left: 840px; } .container .offset-by-fifteen { padding-left: 900px; } /* #Tablet (Portrait) ================================================== */ /* Note: Design for a width of 768px */ @media only screen and (min-width: 768px) and (max-width: 959px) { .container { width: 768px; } .container .column, .container .columns { margin-left: 10px; margin-right: 10px; } .column.alpha, .columns.alpha { margin-left: 0; margin-right: 10px; } .column.omega, .columns.omega { margin-right: 0; margin-left: 10px; } .container .one.column { width: 28px; } .container .two.columns { width: 76px; } .container .three.columns { width: 124px; } .container .four.columns { width: 172px; } .container .five.columns { width: 220px; } .container .six.columns { width: 268px; } .container .seven.columns { width: 316px; } .container .eight.columns { width: 364px; } .container .nine.columns { width: 412px; } .container .ten.columns { width: 460px; } .container .eleven.columns { width: 508px; } .container .twelve.columns { width: 556px; } .container .thirteen.columns { width: 604px; } .container .fourteen.columns { width: 652px; } .container .fifteen.columns { width: 700px; } .container .sixteen.columns { width: 748px; } .container .one-third.column { width: 236px; } .container .two-thirds.column { width: 492px; } /* Offsets */ .container .offset-by-one { padding-left: 48px; } .container .offset-by-two { padding-left: 96px; } .container .offset-by-three { padding-left: 144px; } .container .offset-by-four { padding-left: 192px; } .container .offset-by-five { padding-left: 240px; } .container .offset-by-six { padding-left: 288px; } .container .offset-by-seven { padding-left: 336px; } .container .offset-by-eight { padding-left: 348px; } .container .offset-by-nine { padding-left: 432px; } .container .offset-by-ten { padding-left: 480px; } .container .offset-by-eleven { padding-left: 528px; } .container .offset-by-twelve { padding-left: 576px; } .container .offset-by-thirteen { padding-left: 624px; } .container .offset-by-fourteen { padding-left: 672px; } .container .offset-by-fifteen { padding-left: 720px; } } /* #Mobile (Portrait) ================================================== */ /* Note: Design for a width of 320px */ @media only screen and (max-width: 767px) { .container { width: 300px; } .columns, .column { margin: 0; } .container .one.column, .container .two.columns, .container .three.columns, .container .four.columns, .container .five.columns, .container .six.columns, .container .seven.columns, .container .eight.columns, .container .nine.columns, .container .ten.columns, .container .eleven.columns, .container .twelve.columns, .container .thirteen.columns, .container .fourteen.columns, .container .fifteen.columns, .container .sixteen.columns, .container .one-third.column, .container .two-thirds.column { width: 300px; } /* Offsets */ .container .offset-by-one, .container .offset-by-two, .container .offset-by-three, .container .offset-by-four, .container .offset-by-five, .container .offset-by-six, .container .offset-by-seven, .container .offset-by-eight, .container .offset-by-nine, .container .offset-by-ten, .container .offset-by-eleven, .container .offset-by-twelve, .container .offset-by-thirteen, .container .offset-by-fourteen, .container .offset-by-fifteen { padding-left: 0; } } /* #Mobile (Landscape) ================================================== */ /* Note: Design for a width of 480px */ @media only screen and (min-width: 480px) and (max-width: 767px) { .container { width: 420px; } .columns, .column { margin: 0; } .container .one.column, .container .two.columns, .container .three.columns, .container .four.columns, .container .five.columns, .container .six.columns, .container .seven.columns, .container .eight.columns, .container .nine.columns, .container .ten.columns, .container .eleven.columns, .container .twelve.columns, .container .thirteen.columns, .container .fourteen.columns, .container .fifteen.columns, .container .sixteen.columns, .container .one-third.column, .container .two-thirds.column { width: 420px; } } /* #Clearing ================================================== */ /* Self Clearing Goodness */ .container:after { content: "\0020"; display: block; height: 0; clear: both; visibility: hidden; } /* Use clearfix class on parent to clear nested columns, or wrap each row of columns in a
*/ .clearfix:before, .clearfix:after, .row:before, .row:after { content: '\0020'; display: block; overflow: hidden; visibility: hidden; width: 0; height: 0; } .row:after, .clearfix:after { clear: both; } .row, .clearfix { zoom: 1; } /* You can also use a
to clear columns */ .clear { clear: both; display: block; overflow: hidden; visibility: hidden; width: 0; height: 0; } ================================================ FILE: doc/assets/css/style.css ================================================ /** * A clean concise theme for your GitHub projects * * Licenced under GPL v3 * http://www.gnu.org/licenses/gpl.html **/ /* Page */ body { background: #333333 url('../images/page-background.png') repeat top left; font-family: arial,sans-serif; } /* Header */ header { padding-top: 50px; padding-bottom: 28px; } /* Footer */ footer { background-color: #FFFFFF; margin-top: 50px; -webkit-box-shadow: -10px 0 15px #000000; -moz-box-shadow: -10px 0 15px #000000; box-shadow: -10px 0 15px #000000; } div.credits { padding-top: 9px; margin-top: 30px; margin-bottom: 25px; border-top: 1px solid #DDD2B2; text-align: center; } footer p table { color: #333333; } div.repo-author { padding-top: 30px; } /* Text */ p, strong, li { color: #CCCCCC; font-size: 14px; } strong { font-weight: bold; color: #EEEEEE; } a { color: #0075B6; text-decoration: none; } a:visited { color: #0075B6; } a:hover { text-decoration: underline; } h1, h2 { font-family: georgia,serif; } h1 { font-style: italic; color: #FFFFFF; font-size: 50px; margin: 0; } h2 { color: #CCCCCC; font-size: 25px; line-height: 23px; padding-top: 15px; } h3, h4, h5 { color: #FFFFFF; font-weight: bold; font-family: inherit; } /* Useful classes and styles */ a.github-ribbon { position: absolute; top: 0; left: 0; border: 0; } a.download-button { display: block; padding: 15px 20px 10px 20px; color: #FFFFFF; text-decoration: none; font-size: 28px; font-weight: bold; background: #33A700 url('../images/github-logo.png') no-repeat 92% 50%; border: 2px solid #339410; -webkit-box-shadow: 3px 3px 5px #000000; -moz-box-shadow: 3px 3px 5px #000000; box-shadow: 3px 3px 5px #000000; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; -webkit-transition: 350ms; -moz-transition: 350ms; -o-transition: 350ms; transition: 350ms; } a.download-button:hover { background-color: #267C00; background-position: 90% 50%; } a.download-button span { font-size: 14px; display: block; margin-top: 2px; } div.highlight { margin-top: 15px; min-height: 220px; border: 3px solid #FFFFFF; background-color: #CCCC99; display: block; padding: 20px; font-family: monospace; -webkit-box-shadow: 3px 3px 5px #000000; -moz-box-shadow: 3px 3px 5px #000000; box-shadow: 3px 3px 5px #000000; overflow-x: auto; } dl { margin-top: 15px; min-height: 220px; border: 3px solid #FFFFFF; background-color: #CCCC99; display: block; padding: 20px; font-family: monospace; -webkit-box-shadow: 3px 3px 5px #000000; -moz-box-shadow: 3px 3px 5px #000000; box-shadow: 3px 3px 5px #000000; overflow-x: auto; } dt { font-weight: bold } code { margin-top: 15px; min-height: 50px; border: 3px solid #FFFFFF; background-color: #CCCC99; display: block; padding: 20px; font-family: monospace; -webkit-box-shadow: 3px 3px 5px #000000; -moz-box-shadow: 3px 3px 5px #000000; box-shadow: 3px 3px 5px #000000; overflow-x: auto; } pre.prettyprint { border: 0; padding: 0; margin: 0; } img.repo-author-logo { float: left; margin-right: 15px; } .menu { width: 100%; border-bottom: 2px solid #77746C; border-top: 2px solid #77746C; margin-bottom: 28px; } .menu ul { margin: 0; padding: 0; float: left; } .menu ul li { display: inline; } .menu ul li a { float: left; text-decoration: none; color: white; padding: 10.5px 11px; } .menu ul li a:visited { color: white; } .menu ul li a:hover, .menu ul li .current { color: #fff; background-color:#0b75b2; } /* Media queries */ /* Hide the ribbon when we are on a phone, screen is too small */ @media only screen and (max-width: 479px) { a.github-ribbon { display: none; } } ================================================ FILE: doc/assets/javascript/html5.js ================================================ // html5shiv @rem remysharp.com/html5-enabling-script // iepp v1.6.2 @jon_neal iecss.com/print-protector // Dual licensed under the MIT or GPL Version 2 licenses /*@cc_on(function(a,b){function r(a){var b=-1;while(++b";return a.childNodes.length!==1}())){a.iepp=a.iepp||{};var c=a.iepp,d=c.html5elements||"abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",e=d.split("|"),f=e.length,g=new RegExp("(^|\\s)("+d+")","gi"),h=new RegExp("<(/*)("+d+")","gi"),i=/^\s*[\{\}]\s*$/,j=new RegExp("(^|[^\\n]*?\\s)("+d+")([^\\n]*)({[\\n\\w\\W]*?})","gi"),k=b.createDocumentFragment(),l=b.documentElement,m=l.firstChild,n=b.createElement("body"),o=b.createElement("style"),p=/print|all/,q;c.getCSS=function(a,b){if(a+""===undefined)return"";var d=-1,e=a.length,f,g=[];while(++d)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); ================================================ FILE: doc/assets/javascript/lang-go.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \xa0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]); ================================================ FILE: doc/assets/javascript/lang-hs.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n \r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/, null],["pln",/^(?:[A-Z][\w']*\.)*[A-Za-z][\w']*/],["pun",/^[^\d\t-\r "'A-Za-z]+/]]),["hs"]); ================================================ FILE: doc/assets/javascript/lang-lisp.js ================================================ var a=null; PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,a,"("],["clo",/^\)+/,a,")"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,a], ["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["cl","el","lisp","scm"]); ================================================ FILE: doc/assets/javascript/lang-lua.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \xa0"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\S\s]*?(?:]\1]|$)|[^\n\r]*)/],["str",/^\[(=*)\[[\S\s]*?(?:]\1]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i], ["pln",/^[_a-z]\w*/i],["pun",/^[^\w\t\n\r \xa0][^\w\t\n\r "'+=\xa0-]*/]]),["lua"]); ================================================ FILE: doc/assets/javascript/lang-ml.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \xa0"],["com",/^#(?:if[\t\n\r \xa0]+(?:[$_a-z][\w']*|``[^\t\n\r`]*(?:``|$))|else|endif|light)/i,null,"#"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])(?:'|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\(\*[\S\s]*?\*\))/],["kwd",/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/], ["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^(?:[_a-z][\w']*[!#?]?|``[^\t\n\r`]*(?:``|$))/i],["pun",/^[^\w\t\n\r "'\xa0]+/]]),["fs","ml"]); ================================================ FILE: doc/assets/javascript/lang-n.js ================================================ var a=null; PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:'(?:[^\n\r'\\]|\\.)*'|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,a,'"'],["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,a,"#"],["pln",/^\s+/,a," \r\n\t\xa0"]],[["str",/^@"(?:[^"]|"")*(?:"|$)/,a],["str",/^<#[^#>]*(?:#>|$)/,a],["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,a],["com",/^\/\/[^\n\r]*/,a],["com",/^\/\*[\S\s]*?(?:\*\/|$)/, a],["kwd",/^(?:abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield)\b/, a],["typ",/^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\b/,a],["lit",/^@[$_a-z][\w$@]*/i,a],["typ",/^@[A-Z]+[a-z][\w$@]*/,a],["pln",/^'?[$_a-z][\w$@]*/i,a],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,a,"0123456789"],["pun",/^.[^\s\w"-$'./@`]*/,a]]),["n","nemerle"]); ================================================ FILE: doc/assets/javascript/lang-proto.js ================================================ PR.registerLangHandler(PR.sourceDecorator({keywords:"bytes,default,double,enum,extend,extensions,false,group,import,max,message,option,optional,package,repeated,required,returns,rpc,service,syntax,to,true",types:/^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\b/,cStyleComments:!0}),["proto"]); ================================================ FILE: doc/assets/javascript/lang-scala.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \xa0"],["str",/^"(?:""(?:""?(?!")|[^"\\]|\\.)*"{0,3}|(?:[^\n\r"\\]|\\.)*"?)/,null,'"'],["lit",/^`(?:[^\n\r\\`]|\\.)*`?/,null,"`"],["pun",/^[!#%&(--:-@[-^{-~]+/,null,"!#%&()*+,-:;<=>?@[\\]^{|}~"]],[["str",/^'(?:[^\n\r'\\]|\\(?:'|[^\n\r']+))'/],["lit",/^'[$A-Z_a-z][\w$]*(?![\w$'])/],["kwd",/^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/], ["lit",/^(?:true|false|null|this)\b/],["lit",/^(?:0(?:[0-7]+|x[\da-f]+)l?|(?:0|[1-9]\d*)(?:(?:\.\d+)?(?:e[+-]?\d+)?f?|l?)|\\.\d+(?:e[+-]?\d+)?f?)/i],["typ",/^[$_]*[A-Z][\d$A-Z_]*[a-z][\w$]*/],["pln",/^[$A-Z_a-z][\w$]*/],["com",/^\/(?:\/.*|\*(?:\/|\**[^*/])*(?:\*+\/?)?)/],["pun",/^(?:\.+|\/)/]]),["scala"]); ================================================ FILE: doc/assets/javascript/lang-sql.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \xa0"],["str",/^(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\n\r]*|\/\*[\S\s]*?(?:\*\/|$))/],["kwd",/^(?:add|all|alter|and|any|as|asc|authorization|backup|begin|between|break|browse|bulk|by|cascade|case|check|checkpoint|close|clustered|coalesce|collate|column|commit|compute|constraint|contains|containstable|continue|convert|create|cross|current|current_date|current_time|current_timestamp|current_user|cursor|database|dbcc|deallocate|declare|default|delete|deny|desc|disk|distinct|distributed|double|drop|dummy|dump|else|end|errlvl|escape|except|exec|execute|exists|exit|fetch|file|fillfactor|for|foreign|freetext|freetexttable|from|full|function|goto|grant|group|having|holdlock|identity|identitycol|identity_insert|if|in|index|inner|insert|intersect|into|is|join|key|kill|left|like|lineno|load|match|merge|national|nocheck|nonclustered|not|null|nullif|of|off|offsets|on|open|opendatasource|openquery|openrowset|openxml|option|or|order|outer|over|percent|plan|precision|primary|print|proc|procedure|public|raiserror|read|readtext|reconfigure|references|replication|restore|restrict|return|revoke|right|rollback|rowcount|rowguidcol|rule|save|schema|select|session_user|set|setuser|shutdown|some|statistics|system_user|table|textsize|then|to|top|tran|transaction|trigger|truncate|tsequal|union|unique|update|updatetext|use|user|using|values|varying|view|waitfor|when|where|while|with|writetext)(?=[^\w-]|$)/i, null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^[_a-z][\w-]*/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'+\xa0-]*/]]),["sql"]); ================================================ FILE: doc/assets/javascript/lang-tex.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \xa0"],["com",/^%[^\n\r]*/,null,"%"]],[["kwd",/^\\[@-Za-z]+/],["kwd",/^\\./],["typ",/^[$&]/],["lit",/[+-]?(?:\.\d+|\d+(?:\.\d*)?)(cm|em|ex|in|pc|pt|bp|mm)/i],["pun",/^[()=[\]{}]+/]]),["latex","tex"]); ================================================ FILE: doc/assets/javascript/lang-vb.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0\u2028\u2029]+/,null,"\t\n\r \xa0

"],["str",/^(?:["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})(?:["\u201c\u201d]c|$)|["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})*(?:["\u201c\u201d]|$))/i,null,'"“”'],["com",/^['\u2018\u2019].*/,null,"'‘’"]],[["kwd",/^(?:addhandler|addressof|alias|and|andalso|ansi|as|assembly|auto|boolean|byref|byte|byval|call|case|catch|cbool|cbyte|cchar|cdate|cdbl|cdec|char|cint|class|clng|cobj|const|cshort|csng|cstr|ctype|date|decimal|declare|default|delegate|dim|directcast|do|double|each|else|elseif|end|endif|enum|erase|error|event|exit|finally|for|friend|function|get|gettype|gosub|goto|handles|if|implements|imports|in|inherits|integer|interface|is|let|lib|like|long|loop|me|mod|module|mustinherit|mustoverride|mybase|myclass|namespace|new|next|not|notinheritable|notoverridable|object|on|option|optional|or|orelse|overloads|overridable|overrides|paramarray|preserve|private|property|protected|public|raiseevent|readonly|redim|removehandler|resume|return|select|set|shadows|shared|short|single|static|step|stop|string|structure|sub|synclock|then|throw|to|try|typeof|unicode|until|variant|wend|when|while|with|withevents|writeonly|xor|endif|gosub|let|variant|wend)\b/i, null],["com",/^rem.*/i],["lit",/^(?:true\b|false\b|nothing\b|\d+(?:e[+-]?\d+[dfr]?|[dfilrs])?|(?:&h[\da-f]+|&o[0-7]+)[ils]?|\d*\.\d+(?:e[+-]?\d+)?[dfr]?|#\s+(?:\d+[/-]\d+[/-]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:am|pm))?)?|\d+:\d+(?::\d+)?(\s*(?:am|pm))?)\s+#)/i],["pln",/^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*])/i],["pun",/^[^\w\t\n\r "'[\]\xa0\u2018\u2019\u201c\u201d\u2028\u2029]+/],["pun",/^(?:\[|])/]]),["vb","vbs"]); ================================================ FILE: doc/assets/javascript/lang-vhdl.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \xa0"]],[["str",/^(?:[box]?"(?:[^"]|"")*"|'.')/i],["com",/^--[^\n\r]*/],["kwd",/^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i, null],["typ",/^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\w-]|$)/i,null],["typ",/^'(?:active|ascending|base|delayed|driving|driving_value|event|high|image|instance_name|last_active|last_event|last_value|left|leftof|length|low|path_name|pos|pred|quiet|range|reverse_range|right|rightof|simple_name|stable|succ|transaction|val|value)(?=[^\w-]|$)/i,null],["lit",/^\d+(?:_\d+)*(?:#[\w.\\]+#(?:[+-]?\d+(?:_\d+)*)?|(?:\.\d+(?:_\d+)*)?(?:e[+-]?\d+(?:_\d+)*)?)/i], ["pln",/^(?:[a-z]\w*|\\[^\\]*\\)/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'\xa0-]*/]]),["vhdl","vhd"]); ================================================ FILE: doc/assets/javascript/lang-wiki.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\d\t a-gi-z\xa0]+/,null,"\t \xa0abcdefgijklmnopqrstuvwxyz0123456789"],["pun",/^[*=[\]^~]+/,null,"=*~^[]"]],[["lang-wiki.meta",/(?:^^|\r\n?|\n)(#[a-z]+)\b/],["lit",/^[A-Z][a-z][\da-z]+[A-Z][a-z][^\W_]+\b/],["lang-",/^{{{([\S\s]+?)}}}/],["lang-",/^`([^\n\r`]+)`/],["str",/^https?:\/\/[^\s#/?]*(?:\/[^\s#?]*)?(?:\?[^\s#]*)?(?:#\S*)?/i],["pln",/^(?:\r\n|[\S\s])[^\n\r#*=A-[^`h{~]*/]]),["wiki"]); PR.registerLangHandler(PR.createSimpleLexer([["kwd",/^#[a-z]+/i,null,"#"]],[]),["wiki.meta"]); ================================================ FILE: doc/assets/javascript/lang-xq.js ================================================ PR.registerLangHandler(PR.createSimpleLexer([["var pln",/^\$[\w-]+/,null,"$"]],[["pln",/^[\s=][<>][\s=]/],["lit",/^@[\w-]+/],["tag",/^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["com",/^\(:[\S\s]*?:\)/],["pln",/^[(),/;[\]{}]$/],["str",/^(?:"(?:[^"\\{]|\\[\S\s])*(?:"|$)|'(?:[^'\\{]|\\[\S\s])*(?:'|$))/,null,"\"'"],["kwd",/^(?:xquery|where|version|variable|union|typeswitch|treat|to|then|text|stable|sortby|some|self|schema|satisfies|returns|return|ref|processing-instruction|preceding-sibling|preceding|precedes|parent|only|of|node|namespace|module|let|item|intersect|instance|in|import|if|function|for|follows|following-sibling|following|external|except|every|else|element|descending|descendant-or-self|descendant|define|default|declare|comment|child|cast|case|before|attribute|assert|ascending|as|ancestor-or-self|ancestor|after|eq|order|by|or|and|schema-element|document-node|node|at)\b/], ["typ",/^(?:xs:yearMonthDuration|xs:unsignedLong|xs:time|xs:string|xs:short|xs:QName|xs:Name|xs:long|xs:integer|xs:int|xs:gYearMonth|xs:gYear|xs:gMonthDay|xs:gDay|xs:float|xs:duration|xs:double|xs:decimal|xs:dayTimeDuration|xs:dateTime|xs:date|xs:byte|xs:boolean|xs:anyURI|xf:yearMonthDuration)\b/,null],["fun pln",/^(?:xp:dereference|xinc:node-expand|xinc:link-references|xinc:link-expand|xhtml:restructure|xhtml:clean|xhtml:add-lists|xdmp:zip-manifest|xdmp:zip-get|xdmp:zip-create|xdmp:xquery-version|xdmp:word-convert|xdmp:with-namespaces|xdmp:version|xdmp:value|xdmp:user-roles|xdmp:user-last-login|xdmp:user|xdmp:url-encode|xdmp:url-decode|xdmp:uri-is-file|xdmp:uri-format|xdmp:uri-content-type|xdmp:unquote|xdmp:unpath|xdmp:triggers-database|xdmp:trace|xdmp:to-json|xdmp:tidy|xdmp:subbinary|xdmp:strftime|xdmp:spawn-in|xdmp:spawn|xdmp:sleep|xdmp:shutdown|xdmp:set-session-field|xdmp:set-response-encoding|xdmp:set-response-content-type|xdmp:set-response-code|xdmp:set-request-time-limit|xdmp:set|xdmp:servers|xdmp:server-status|xdmp:server-name|xdmp:server|xdmp:security-database|xdmp:security-assert|xdmp:schema-database|xdmp:save|xdmp:role-roles|xdmp:role|xdmp:rethrow|xdmp:restart|xdmp:request-timestamp|xdmp:request-status|xdmp:request-cancel|xdmp:request|xdmp:redirect-response|xdmp:random|xdmp:quote|xdmp:query-trace|xdmp:query-meters|xdmp:product-edition|xdmp:privilege-roles|xdmp:privilege|xdmp:pretty-print|xdmp:powerpoint-convert|xdmp:platform|xdmp:permission|xdmp:pdf-convert|xdmp:path|xdmp:octal-to-integer|xdmp:node-uri|xdmp:node-replace|xdmp:node-kind|xdmp:node-insert-child|xdmp:node-insert-before|xdmp:node-insert-after|xdmp:node-delete|xdmp:node-database|xdmp:mul64|xdmp:modules-root|xdmp:modules-database|xdmp:merging|xdmp:merge-cancel|xdmp:merge|xdmp:md5|xdmp:logout|xdmp:login|xdmp:log-level|xdmp:log|xdmp:lock-release|xdmp:lock-acquire|xdmp:load|xdmp:invoke-in|xdmp:invoke|xdmp:integer-to-octal|xdmp:integer-to-hex|xdmp:http-put|xdmp:http-post|xdmp:http-options|xdmp:http-head|xdmp:http-get|xdmp:http-delete|xdmp:hosts|xdmp:host-status|xdmp:host-name|xdmp:host|xdmp:hex-to-integer|xdmp:hash64|xdmp:hash32|xdmp:has-privilege|xdmp:groups|xdmp:group-serves|xdmp:group-servers|xdmp:group-name|xdmp:group-hosts|xdmp:group|xdmp:get-session-field-names|xdmp:get-session-field|xdmp:get-response-encoding|xdmp:get-response-code|xdmp:get-request-username|xdmp:get-request-user|xdmp:get-request-url|xdmp:get-request-protocol|xdmp:get-request-path|xdmp:get-request-method|xdmp:get-request-header-names|xdmp:get-request-header|xdmp:get-request-field-names|xdmp:get-request-field-filename|xdmp:get-request-field-content-type|xdmp:get-request-field|xdmp:get-request-client-certificate|xdmp:get-request-client-address|xdmp:get-request-body|xdmp:get-current-user|xdmp:get-current-roles|xdmp:get|xdmp:function-name|xdmp:function-module|xdmp:function|xdmp:from-json|xdmp:forests|xdmp:forest-status|xdmp:forest-restore|xdmp:forest-restart|xdmp:forest-name|xdmp:forest-delete|xdmp:forest-databases|xdmp:forest-counts|xdmp:forest-clear|xdmp:forest-backup|xdmp:forest|xdmp:filesystem-file|xdmp:filesystem-directory|xdmp:exists|xdmp:excel-convert|xdmp:eval-in|xdmp:eval|xdmp:estimate|xdmp:email|xdmp:element-content-type|xdmp:elapsed-time|xdmp:document-set-quality|xdmp:document-set-property|xdmp:document-set-properties|xdmp:document-set-permissions|xdmp:document-set-collections|xdmp:document-remove-properties|xdmp:document-remove-permissions|xdmp:document-remove-collections|xdmp:document-properties|xdmp:document-locks|xdmp:document-load|xdmp:document-insert|xdmp:document-get-quality|xdmp:document-get-properties|xdmp:document-get-permissions|xdmp:document-get-collections|xdmp:document-get|xdmp:document-forest|xdmp:document-delete|xdmp:document-add-properties|xdmp:document-add-permissions|xdmp:document-add-collections|xdmp:directory-properties|xdmp:directory-locks|xdmp:directory-delete|xdmp:directory-create|xdmp:directory|xdmp:diacritic-less|xdmp:describe|xdmp:default-permissions|xdmp:default-collections|xdmp:databases|xdmp:database-restore-validate|xdmp:database-restore-status|xdmp:database-restore-cancel|xdmp:database-restore|xdmp:database-name|xdmp:database-forests|xdmp:database-backup-validate|xdmp:database-backup-status|xdmp:database-backup-purge|xdmp:database-backup-cancel|xdmp:database-backup|xdmp:database|xdmp:collection-properties|xdmp:collection-locks|xdmp:collection-delete|xdmp:collation-canonical-uri|xdmp:castable-as|xdmp:can-grant-roles|xdmp:base64-encode|xdmp:base64-decode|xdmp:architecture|xdmp:apply|xdmp:amp-roles|xdmp:amp|xdmp:add64|xdmp:add-response-header|xdmp:access|trgr:trigger-set-recursive|trgr:trigger-set-permissions|trgr:trigger-set-name|trgr:trigger-set-module|trgr:trigger-set-event|trgr:trigger-set-description|trgr:trigger-remove-permissions|trgr:trigger-module|trgr:trigger-get-permissions|trgr:trigger-enable|trgr:trigger-disable|trgr:trigger-database-online-event|trgr:trigger-data-event|trgr:trigger-add-permissions|trgr:remove-trigger|trgr:property-content|trgr:pre-commit|trgr:post-commit|trgr:get-trigger-by-id|trgr:get-trigger|trgr:document-scope|trgr:document-content|trgr:directory-scope|trgr:create-trigger|trgr:collection-scope|trgr:any-property-content|thsr:set-entry|thsr:remove-term|thsr:remove-synonym|thsr:remove-entry|thsr:query-lookup|thsr:lookup|thsr:load|thsr:insert|thsr:expand|thsr:add-synonym|spell:suggest-detailed|spell:suggest|spell:remove-word|spell:make-dictionary|spell:load|spell:levenshtein-distance|spell:is-correct|spell:insert|spell:double-metaphone|spell:add-word|sec:users-collection|sec:user-set-roles|sec:user-set-password|sec:user-set-name|sec:user-set-description|sec:user-set-default-permissions|sec:user-set-default-collections|sec:user-remove-roles|sec:user-privileges|sec:user-get-roles|sec:user-get-description|sec:user-get-default-permissions|sec:user-get-default-collections|sec:user-doc-permissions|sec:user-doc-collections|sec:user-add-roles|sec:unprotect-collection|sec:uid-for-name|sec:set-realm|sec:security-version|sec:security-namespace|sec:security-installed|sec:security-collection|sec:roles-collection|sec:role-set-roles|sec:role-set-name|sec:role-set-description|sec:role-set-default-permissions|sec:role-set-default-collections|sec:role-remove-roles|sec:role-privileges|sec:role-get-roles|sec:role-get-description|sec:role-get-default-permissions|sec:role-get-default-collections|sec:role-doc-permissions|sec:role-doc-collections|sec:role-add-roles|sec:remove-user|sec:remove-role-from-users|sec:remove-role-from-role|sec:remove-role-from-privileges|sec:remove-role-from-amps|sec:remove-role|sec:remove-privilege|sec:remove-amp|sec:protect-collection|sec:privileges-collection|sec:privilege-set-roles|sec:privilege-set-name|sec:privilege-remove-roles|sec:privilege-get-roles|sec:privilege-add-roles|sec:priv-doc-permissions|sec:priv-doc-collections|sec:get-user-names|sec:get-unique-elem-id|sec:get-role-names|sec:get-role-ids|sec:get-privilege|sec:get-distinct-permissions|sec:get-collection|sec:get-amp|sec:create-user-with-role|sec:create-user|sec:create-role|sec:create-privilege|sec:create-amp|sec:collections-collection|sec:collection-set-permissions|sec:collection-remove-permissions|sec:collection-get-permissions|sec:collection-add-permissions|sec:check-admin|sec:amps-collection|sec:amp-set-roles|sec:amp-remove-roles|sec:amp-get-roles|sec:amp-doc-permissions|sec:amp-doc-collections|sec:amp-add-roles|search:unparse|search:suggest|search:snippet|search:search|search:resolve-nodes|search:resolve|search:remove-constraint|search:parse|search:get-default-options|search:estimate|search:check-options|prof:value|prof:reset|prof:report|prof:invoke|prof:eval|prof:enable|prof:disable|prof:allowed|ppt:clean|pki:template-set-request|pki:template-set-name|pki:template-set-key-type|pki:template-set-key-options|pki:template-set-description|pki:template-in-use|pki:template-get-version|pki:template-get-request|pki:template-get-name|pki:template-get-key-type|pki:template-get-key-options|pki:template-get-id|pki:template-get-description|pki:need-certificate|pki:is-temporary|pki:insert-trusted-certificates|pki:insert-template|pki:insert-signed-certificates|pki:insert-certificate-revocation-list|pki:get-trusted-certificate-ids|pki:get-template-ids|pki:get-template-certificate-authority|pki:get-template-by-name|pki:get-template|pki:get-pending-certificate-requests-xml|pki:get-pending-certificate-requests-pem|pki:get-pending-certificate-request|pki:get-certificates-for-template-xml|pki:get-certificates-for-template|pki:get-certificates|pki:get-certificate-xml|pki:get-certificate-pem|pki:get-certificate|pki:generate-temporary-certificate-if-necessary|pki:generate-temporary-certificate|pki:generate-template-certificate-authority|pki:generate-certificate-request|pki:delete-template|pki:delete-certificate|pki:create-template|pdf:make-toc|pdf:insert-toc-headers|pdf:get-toc|pdf:clean|p:status-transition|p:state-transition|p:remove|p:pipelines|p:insert|p:get-by-id|p:get|p:execute|p:create|p:condition|p:collection|p:action|ooxml:runs-merge|ooxml:package-uris|ooxml:package-parts-insert|ooxml:package-parts|msword:clean|mcgm:polygon|mcgm:point|mcgm:geospatial-query-from-elements|mcgm:geospatial-query|mcgm:circle|math:tanh|math:tan|math:sqrt|math:sinh|math:sin|math:pow|math:modf|math:log10|math:log|math:ldexp|math:frexp|math:fmod|math:floor|math:fabs|math:exp|math:cosh|math:cos|math:ceil|math:atan2|math:atan|math:asin|math:acos|map:put|map:map|map:keys|map:get|map:delete|map:count|map:clear|lnk:to|lnk:remove|lnk:insert|lnk:get|lnk:from|lnk:create|kml:polygon|kml:point|kml:interior-polygon|kml:geospatial-query-from-elements|kml:geospatial-query|kml:circle|kml:box|gml:polygon|gml:point|gml:interior-polygon|gml:geospatial-query-from-elements|gml:geospatial-query|gml:circle|gml:box|georss:point|georss:geospatial-query|georss:circle|geo:polygon|geo:point|geo:interior-polygon|geo:geospatial-query-from-elements|geo:geospatial-query|geo:circle|geo:box|fn:zero-or-one|fn:years-from-duration|fn:year-from-dateTime|fn:year-from-date|fn:upper-case|fn:unordered|fn:true|fn:translate|fn:trace|fn:tokenize|fn:timezone-from-time|fn:timezone-from-dateTime|fn:timezone-from-date|fn:sum|fn:subtract-dateTimes-yielding-yearMonthDuration|fn:subtract-dateTimes-yielding-dayTimeDuration|fn:substring-before|fn:substring-after|fn:substring|fn:subsequence|fn:string-to-codepoints|fn:string-pad|fn:string-length|fn:string-join|fn:string|fn:static-base-uri|fn:starts-with|fn:seconds-from-time|fn:seconds-from-duration|fn:seconds-from-dateTime|fn:round-half-to-even|fn:round|fn:root|fn:reverse|fn:resolve-uri|fn:resolve-QName|fn:replace|fn:remove|fn:QName|fn:prefix-from-QName|fn:position|fn:one-or-more|fn:number|fn:not|fn:normalize-unicode|fn:normalize-space|fn:node-name|fn:node-kind|fn:nilled|fn:namespace-uri-from-QName|fn:namespace-uri-for-prefix|fn:namespace-uri|fn:name|fn:months-from-duration|fn:month-from-dateTime|fn:month-from-date|fn:minutes-from-time|fn:minutes-from-duration|fn:minutes-from-dateTime|fn:min|fn:max|fn:matches|fn:lower-case|fn:local-name-from-QName|fn:local-name|fn:last|fn:lang|fn:iri-to-uri|fn:insert-before|fn:index-of|fn:in-scope-prefixes|fn:implicit-timezone|fn:idref|fn:id|fn:hours-from-time|fn:hours-from-duration|fn:hours-from-dateTime|fn:floor|fn:false|fn:expanded-QName|fn:exists|fn:exactly-one|fn:escape-uri|fn:escape-html-uri|fn:error|fn:ends-with|fn:encode-for-uri|fn:empty|fn:document-uri|fn:doc-available|fn:doc|fn:distinct-values|fn:distinct-nodes|fn:default-collation|fn:deep-equal|fn:days-from-duration|fn:day-from-dateTime|fn:day-from-date|fn:data|fn:current-time|fn:current-dateTime|fn:current-date|fn:count|fn:contains|fn:concat|fn:compare|fn:collection|fn:codepoints-to-string|fn:codepoint-equal|fn:ceiling|fn:boolean|fn:base-uri|fn:avg|fn:adjust-time-to-timezone|fn:adjust-dateTime-to-timezone|fn:adjust-date-to-timezone|fn:abs|feed:unsubscribe|feed:subscription|feed:subscribe|feed:request|feed:item|feed:description|excel:clean|entity:enrich|dom:set-pipelines|dom:set-permissions|dom:set-name|dom:set-evaluation-context|dom:set-domain-scope|dom:set-description|dom:remove-pipeline|dom:remove-permissions|dom:remove|dom:get|dom:evaluation-context|dom:domains|dom:domain-scope|dom:create|dom:configuration-set-restart-user|dom:configuration-set-permissions|dom:configuration-set-evaluation-context|dom:configuration-set-default-domain|dom:configuration-get|dom:configuration-create|dom:collection|dom:add-pipeline|dom:add-permissions|dls:retention-rules|dls:retention-rule-remove|dls:retention-rule-insert|dls:retention-rule|dls:purge|dls:node-expand|dls:link-references|dls:link-expand|dls:documents-query|dls:document-versions-query|dls:document-version-uri|dls:document-version-query|dls:document-version-delete|dls:document-version-as-of|dls:document-version|dls:document-update|dls:document-unmanage|dls:document-set-quality|dls:document-set-property|dls:document-set-properties|dls:document-set-permissions|dls:document-set-collections|dls:document-retention-rules|dls:document-remove-properties|dls:document-remove-permissions|dls:document-remove-collections|dls:document-purge|dls:document-manage|dls:document-is-managed|dls:document-insert-and-manage|dls:document-include-query|dls:document-history|dls:document-get-permissions|dls:document-extract-part|dls:document-delete|dls:document-checkout-status|dls:document-checkout|dls:document-checkin|dls:document-add-properties|dls:document-add-permissions|dls:document-add-collections|dls:break-checkout|dls:author-query|dls:as-of-query|dbk:convert|dbg:wait|dbg:value|dbg:stopped|dbg:stop|dbg:step|dbg:status|dbg:stack|dbg:out|dbg:next|dbg:line|dbg:invoke|dbg:function|dbg:finish|dbg:expr|dbg:eval|dbg:disconnect|dbg:detach|dbg:continue|dbg:connect|dbg:clear|dbg:breakpoints|dbg:break|dbg:attached|dbg:attach|cvt:save-converted-documents|cvt:part-uri|cvt:destination-uri|cvt:basepath|cvt:basename|cts:words|cts:word-query-weight|cts:word-query-text|cts:word-query-options|cts:word-query|cts:word-match|cts:walk|cts:uris|cts:uri-match|cts:train|cts:tokenize|cts:thresholds|cts:stem|cts:similar-query-weight|cts:similar-query-nodes|cts:similar-query|cts:shortest-distance|cts:search|cts:score|cts:reverse-query-weight|cts:reverse-query-nodes|cts:reverse-query|cts:remainder|cts:registered-query-weight|cts:registered-query-options|cts:registered-query-ids|cts:registered-query|cts:register|cts:query|cts:quality|cts:properties-query-query|cts:properties-query|cts:polygon-vertices|cts:polygon|cts:point-longitude|cts:point-latitude|cts:point|cts:or-query-queries|cts:or-query|cts:not-query-weight|cts:not-query-query|cts:not-query|cts:near-query-weight|cts:near-query-queries|cts:near-query-options|cts:near-query-distance|cts:near-query|cts:highlight|cts:geospatial-co-occurrences|cts:frequency|cts:fitness|cts:field-words|cts:field-word-query-weight|cts:field-word-query-text|cts:field-word-query-options|cts:field-word-query-field-name|cts:field-word-query|cts:field-word-match|cts:entity-highlight|cts:element-words|cts:element-word-query-weight|cts:element-word-query-text|cts:element-word-query-options|cts:element-word-query-element-name|cts:element-word-query|cts:element-word-match|cts:element-values|cts:element-value-ranges|cts:element-value-query-weight|cts:element-value-query-text|cts:element-value-query-options|cts:element-value-query-element-name|cts:element-value-query|cts:element-value-match|cts:element-value-geospatial-co-occurrences|cts:element-value-co-occurrences|cts:element-range-query-weight|cts:element-range-query-value|cts:element-range-query-options|cts:element-range-query-operator|cts:element-range-query-element-name|cts:element-range-query|cts:element-query-query|cts:element-query-element-name|cts:element-query|cts:element-pair-geospatial-values|cts:element-pair-geospatial-value-match|cts:element-pair-geospatial-query-weight|cts:element-pair-geospatial-query-region|cts:element-pair-geospatial-query-options|cts:element-pair-geospatial-query-longitude-name|cts:element-pair-geospatial-query-latitude-name|cts:element-pair-geospatial-query-element-name|cts:element-pair-geospatial-query|cts:element-pair-geospatial-boxes|cts:element-geospatial-values|cts:element-geospatial-value-match|cts:element-geospatial-query-weight|cts:element-geospatial-query-region|cts:element-geospatial-query-options|cts:element-geospatial-query-element-name|cts:element-geospatial-query|cts:element-geospatial-boxes|cts:element-child-geospatial-values|cts:element-child-geospatial-value-match|cts:element-child-geospatial-query-weight|cts:element-child-geospatial-query-region|cts:element-child-geospatial-query-options|cts:element-child-geospatial-query-element-name|cts:element-child-geospatial-query-child-name|cts:element-child-geospatial-query|cts:element-child-geospatial-boxes|cts:element-attribute-words|cts:element-attribute-word-query-weight|cts:element-attribute-word-query-text|cts:element-attribute-word-query-options|cts:element-attribute-word-query-element-name|cts:element-attribute-word-query-attribute-name|cts:element-attribute-word-query|cts:element-attribute-word-match|cts:element-attribute-values|cts:element-attribute-value-ranges|cts:element-attribute-value-query-weight|cts:element-attribute-value-query-text|cts:element-attribute-value-query-options|cts:element-attribute-value-query-element-name|cts:element-attribute-value-query-attribute-name|cts:element-attribute-value-query|cts:element-attribute-value-match|cts:element-attribute-value-geospatial-co-occurrences|cts:element-attribute-value-co-occurrences|cts:element-attribute-range-query-weight|cts:element-attribute-range-query-value|cts:element-attribute-range-query-options|cts:element-attribute-range-query-operator|cts:element-attribute-range-query-element-name|cts:element-attribute-range-query-attribute-name|cts:element-attribute-range-query|cts:element-attribute-pair-geospatial-values|cts:element-attribute-pair-geospatial-value-match|cts:element-attribute-pair-geospatial-query-weight|cts:element-attribute-pair-geospatial-query-region|cts:element-attribute-pair-geospatial-query-options|cts:element-attribute-pair-geospatial-query-longitude-name|cts:element-attribute-pair-geospatial-query-latitude-name|cts:element-attribute-pair-geospatial-query-element-name|cts:element-attribute-pair-geospatial-query|cts:element-attribute-pair-geospatial-boxes|cts:document-query-uris|cts:document-query|cts:distance|cts:directory-query-uris|cts:directory-query-depth|cts:directory-query|cts:destination|cts:deregister|cts:contains|cts:confidence|cts:collections|cts:collection-query-uris|cts:collection-query|cts:collection-match|cts:classify|cts:circle-radius|cts:circle-center|cts:circle|cts:box-west|cts:box-south|cts:box-north|cts:box-east|cts:box|cts:bearing|cts:arc-intersection|cts:and-query-queries|cts:and-query-options|cts:and-query|cts:and-not-query-positive-query|cts:and-not-query-negative-query|cts:and-not-query|css:get|css:convert|cpf:success|cpf:failure|cpf:document-set-state|cpf:document-set-processing-status|cpf:document-set-last-updated|cpf:document-set-error|cpf:document-get-state|cpf:document-get-processing-status|cpf:document-get-last-updated|cpf:document-get-error|cpf:check-transition|alert:spawn-matching-actions|alert:rule-user-id-query|alert:rule-set-user-id|alert:rule-set-query|alert:rule-set-options|alert:rule-set-name|alert:rule-set-description|alert:rule-set-action|alert:rule-remove|alert:rule-name-query|alert:rule-insert|alert:rule-id-query|alert:rule-get-user-id|alert:rule-get-query|alert:rule-get-options|alert:rule-get-name|alert:rule-get-id|alert:rule-get-description|alert:rule-get-action|alert:rule-action-query|alert:remove-triggers|alert:make-rule|alert:make-log-action|alert:make-config|alert:make-action|alert:invoke-matching-actions|alert:get-my-rules|alert:get-all-rules|alert:get-actions|alert:find-matching-rules|alert:create-triggers|alert:config-set-uri|alert:config-set-trigger-ids|alert:config-set-options|alert:config-set-name|alert:config-set-description|alert:config-set-cpf-domain-names|alert:config-set-cpf-domain-ids|alert:config-insert|alert:config-get-uri|alert:config-get-trigger-ids|alert:config-get-options|alert:config-get-name|alert:config-get-id|alert:config-get-description|alert:config-get-cpf-domain-names|alert:config-get-cpf-domain-ids|alert:config-get|alert:config-delete|alert:action-set-options|alert:action-set-name|alert:action-set-module-root|alert:action-set-module-db|alert:action-set-module|alert:action-set-description|alert:action-remove|alert:action-insert|alert:action-get-options|alert:action-get-name|alert:action-get-module-root|alert:action-get-module-db|alert:action-get-module|alert:action-get-description|zero-or-one|years-from-duration|year-from-dateTime|year-from-date|upper-case|unordered|true|translate|trace|tokenize|timezone-from-time|timezone-from-dateTime|timezone-from-date|sum|subtract-dateTimes-yielding-yearMonthDuration|subtract-dateTimes-yielding-dayTimeDuration|substring-before|substring-after|substring|subsequence|string-to-codepoints|string-pad|string-length|string-join|string|static-base-uri|starts-with|seconds-from-time|seconds-from-duration|seconds-from-dateTime|round-half-to-even|round|root|reverse|resolve-uri|resolve-QName|replace|remove|QName|prefix-from-QName|position|one-or-more|number|not|normalize-unicode|normalize-space|node-name|node-kind|nilled|namespace-uri-from-QName|namespace-uri-for-prefix|namespace-uri|name|months-from-duration|month-from-dateTime|month-from-date|minutes-from-time|minutes-from-duration|minutes-from-dateTime|min|max|matches|lower-case|local-name-from-QName|local-name|last|lang|iri-to-uri|insert-before|index-of|in-scope-prefixes|implicit-timezone|idref|id|hours-from-time|hours-from-duration|hours-from-dateTime|floor|false|expanded-QName|exists|exactly-one|escape-uri|escape-html-uri|error|ends-with|encode-for-uri|empty|document-uri|doc-available|doc|distinct-values|distinct-nodes|default-collation|deep-equal|days-from-duration|day-from-dateTime|day-from-date|data|current-time|current-dateTime|current-date|count|contains|concat|compare|collection|codepoints-to-string|codepoint-equal|ceiling|boolean|base-uri|avg|adjust-time-to-timezone|adjust-dateTime-to-timezone|adjust-date-to-timezone|abs)\b/], ["pln",/^[\w:-]+/],["pln",/^[\t\n\r \xa0]+/]]),["xq","xquery"]); ================================================ FILE: doc/assets/javascript/lang-yaml.js ================================================ var a=null; PR.registerLangHandler(PR.createSimpleLexer([["pun",/^[:>?|]+/,a,":|>?"],["dec",/^%(?:YAML|TAG)[^\n\r#]+/,a,"%"],["typ",/^&\S+/,a,"&"],["typ",/^!\S*/,a,"!"],["str",/^"(?:[^"\\]|\\.)*(?:"|$)/,a,'"'],["str",/^'(?:[^']|'')*(?:'|$)/,a,"'"],["com",/^#[^\n\r]*/,a,"#"],["pln",/^\s+/,a," \t\r\n"]],[["dec",/^(?:---|\.\.\.)(?:[\n\r]|$)/],["pun",/^-/],["kwd",/^\w+:[\n\r ]/],["pln",/^\w+/]]),["yaml","yml"]); ================================================ FILE: doc/assets/javascript/prettify.js ================================================ var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p

#

Concept

Fact / Erlang tuple

1

X is male

{male, X}

2

X is female

{female, X}

3

X is Y's parent

{parent, X, Y}

4

X is Y's mother

{mother, X, Y}

5

X is Y's father

{father, X, Y}

6

X is Y's sister

{sister, X, Y}

7

X is Y's brother

{brother, X, Y}

8

X is Y's grandmother

{grandmother, X, Y}

9

X is Y's grandfather

{grandfather, X, Y}

Concepts 1, 2 and 3 will be used as a base to derive the other ones. Deriving new concepts by means of rules --------------------------------------- #### Concept: mother The rule to derive the concept of mother is quite straightforward: if X is female and X is Y's parent then X is Y's mother. From the point of view of SERESYE, since knowledge is stored in the *knowledge base* of the engine, the rule above is translated into the following one: *if the facts {female, X} and {parent, X, Y} are *asserted* in the knowledge base, then we assert the fact {mother, X, Y}. The rule *mother* can be thus written as follows: %% %% if (X is female) and (X is Y's parent) then (X is Y's mother) %% mother (Engine, {female, X}, {parent, X, Y}) -> seresye:assert (Engine, {mother, X, Y}). #### Concept: father This concept can be easily derived by means of the following rule: %% %% if (X is male) and (X is Y's parent) then (X is Y's father) %% father (Engine, {male, X}, {parent, X, Y}) -> seresye:assert (Engine, {father, X, Y}). #### Concept: sister This concept can be expressed by the following rule: if Y and Z have the same parent and Z is female, then Z is the Y's sister. The SERESYE rule used to map this concept is: %% %% if (Y and Z have the same parent X) and (Z is female) %% then (Z is Y's sister) %% sister (Engine, {parent, X, Y}, {parent, X, Z}, {female, Z}) when Y =/= Z -> seresye:assert (Engine, {sister, Z, Y}). Please note the guard, which is needed to ensure that when Y and Z are bound to the same value, the rule is not activated (indeed this is possible since the same fact can match both the first and second 'parent' pattern). #### Concept: brother Given the previous one, this concept is now quite simple to implement: %% %% if (Y and Z have the same parent X) and (Z is male) %% then (Z is Y's brother) %% brother (Engine, {parent, X, Y}, {parent, X, Z}, {male, Z}) when Y =/= Z -> seresye:assert (Engine, {brother, Z, Y}). #### Concepts: grandmother and grandfather The former concept can be expressed by means of the rule: if X is Y's mother and Y is Z's parent, then X is Z's grandmother.* The latter concept is now obvious. Both can be implemented using the following SERESYE rules: %% %% if (X is Y's mother) and (Y is Z's parent) %% then (X is Z's grandmother) %% grandmother (Engine, {mother, X, Y}, {parent, Y, Z}) -> seresye:assert (Engine, {grandmother, X, Z}). %% %% if (X is Y's father) and (Y is Z's parent) %% then (X is Z's grandfather) %% grandfather (Engine, {father, X, Y}, {parent, Y, Z}) -> seresye:assert (Engine, {grandfather, X, Z}). Instantiating the Engine and Populating the Knowledge Base ---------------------------------------------------------- After writing the rules, we need to: - define the rules to seresye - instantiate the engine; - populate the knowledge base with a set of initial facts. We define the rules to SERESYE by defined a 'rules' attribute at the start of the module. %%% %%% relatives.erl %%% -module (relatives). -export([father/3, grandfather/3, grandmother/3, mother/3, brother/4, sister/4, start/0]). -rules([mother, father, brother, sister, grandfather, grandmother]). We continue on to instantiate the engine and popoulate the knowledge base in the function *start* below: start () -> application:start(seresye) % Only if it is not already started seresye:start(relatives), seresye:add_rules(relatives, ?MODULE) seresye:assert(relatives, [{male, bob}, {male, corrado}, {male, mark}, {male, caesar}, {female, alice}, {female, sara}, {female, jane}, {female, anna}, {parent, jane, bob}, {parent, corrado, bob}, {parent, jane, mark}, {parent, corrado, mark}, {parent, jane, alice}, {parent, corrado, alice}, {parent, bob, caesar}, {parent, bob, anna}, {parent, sara, casear}, {parent, sara, anna}]), ok. As the listing reports, creating a new SERESYE engine implies to call the function *seresye:start/1*, giving the name of the engine to be created Then, we have to add the rules to the engine by using the function *seresye:add_rule/2*: it takes two arguments, the name of the engine and a tuple representing the function in the form *{Module, FuncName}*; obviously the function *Module:FuncName* must be exported. Function *add_rule* has to be called for each rule that has to be added; for this reason, the code above has an iteration over the list of rules written before. Finally, we populate the inference engine with a set of sample facts by giving them, in a list, to the function *seresye:assert/2*. To test our rules, we considered the relationships in the Figure below and assert only the facts for *male*, *female* and *parent*. Testing the application ----------------------- The final complete code of our AI application is thus the following: %%% %%% relatives.erl %%% -module (relatives). -export([father/3, grandfather/3, grandmother/3, mother/3, brother/4, sister/4, start/0]). %% %% if (X is female) and (X is Y's parent) then (X is Y's mother) %% mother(Engine, {female, X}, {parent, X, Y}) -> seresye:assert(Engine, {mother, X, Y}). %% %% if (X is male) and (X is Y's parent) then (X is Y's father) %% father(Engine, {male, X}, {parent, X, Y}) -> seresye:assert(Engine, {father, X, Y}). %% %% if (Y and Z have the same parent X) and (Z is female) %% then (Z is Y's sister) %% sister(Engine, {parent, X, Y}, {parent, X, Z}, {female, Z}) when Y =/= Z -> seresye:assert(Engine, {sister, Z, Y}). %% %% if (Y and Z have the same parent X) and (Z is male) %% then (Z is Y's brother) %% brother(Engine, {parent, X, Y}, {parent, X, Z}, {male, Z}) when Y =/= Z -> seresye:assert(Engine, {brother, Z, Y}). %% %% if (X is Y's father) and (Y is Z's parent) %% then (X is Z's grandfather) %% grandfather (Engine, {father, X, Y}, {parent, Y, Z}) -> seresye:assert (Engine, {grandfather, X, Z}). %% %% if (X is Y's mother) and (Y is Z's parent) %% then (X is Z's grandmother) %% grandmother(Engine, {mother, X, Y}, {parent, Y, Z}) -> seresye:assert(Engine, {grandmother, X, Z}). start () -> application:start(seresye), seresye:start (relatives), seresye:add_rules(relatives, ?MODULE) seresye:assert (relatives, [{male, bob}, {male, corrado}, {male, mark}, {male, caesar}, {female, alice}, {female, sara}, {female, jane}, {female, anna}, {parent, jane, bob}, {parent, corrado, bob}, {parent, jane, mark}, {parent, corrado, mark}, {parent, jane, alice}, {parent, corrado, alice}, {parent, bob, caesar}, {parent, bob, anna}, {parent, sara, casear}, {parent, sara, anna}]), ok. Now it's time to test our application: Erlang (BEAM) emulator version 5.5 [source] [async-threads:0] [hipe] Eshell V5.5 (abort with ^G) 1> c(relatives). {ok,relatives} 2> relatives:start(). ok 3> Following the call to function *relatives:start/0*, the engine is created and populated; if no errors occurred, the rules should have been processed and the new facts derived. To check this, we can use the function *seresye:get_kb/1*, which returns the list of facts asserted into the knowledge base of a given engine: 4> seresye:get_kb(relatives). [{brother,bob,mark}, {sister,alice,bob}, {sister,alice,mark}, {brother,bob,alice}, {brother,mark,alice}, {grandmother,jane,caesar}, {grandfather,corrado,caesar}, {grandmother,jane,anna}, {grandfather,corrado,anna}, {sister,anna,caesar}, {brother,caesar,anna}, {sister,anna,casear}, {mother,sara,anna}, {mother,sara,casear}, {parent,sara,anna}, {father,bob,anna}, {parent,sara,casear}, {father,bob,caesar}, {parent,bob,anna}, {father,corrado,alice}, {parent,bob,caesar}, {mother,jane,alice}, {parent,corrado,alice}, {father,corrado,mark}, {parent,jane,alice}, {mother,jane,mark}, {parent,corrado|...}, {brother|...}, {...}|...] 5> The presence of facts representing concepts like *father*, *sister*, etc., proves that the rules seems to be working as expected. We can however query the knowledge base using specific fact templates. For example, if we want to know who are Alice's brothers, we can use the function *seresye:query_kb/2* as follows: 6> seresye:query_kb(relatives, {brother, '_', alice}). [{brother,bob,alice},{brother,mark,alice}] 7> The facts returned conform to the relationships depicted in the figure above, thus proving that the rules written are really working. As the example shows, function *seresye:query_kb/2* takes the engine name as the first argument, while, for the second parameter, a tuple has to be specified, representing the fact template to be matched; in such a tuple, the atom *'_'* plays the role of a wildcard. However, to specify a more complex matching, a *fun* can be used as a tuple element; this *fun* has to return a boolean value which indicates if the element matches the template. For example, to select both Alice's and Anna's brothers, we can use the following function call: 7> seresye:query_kb(relatives, {brother, '_', fun (X) -> (X == alice) or (X == anna) end}). [{brother,bob,alice},{brother,mark,alice},{brother,caesar,anna}] 8> Conclusions ----------- This HowTo not only shows how to use the SERESYE engine to write an AI application, but also highlights the versatility of the Erlang language: the characteristics of functional and symbolic programming, together with the possibility of performing *introspection* of function declaration, can be successfully exploited for application domains which are completely new for Erlang but can surely be very interesting. ================================================ FILE: examples/seresye_phil.erl ================================================ %%% SERESYE, an ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php% -module(seresye_phil). -export([start/0, phil_spawn/1, philosopher/2, think/1, eat/1]). -define(N_PHIL, 5). start() -> application:start(seresye), seresye:start(restaurant), phil_spawn(0). phil_spawn(?N_PHIL) -> ok; phil_spawn(N) -> seresye:assert(restaurant, {fork, N}), spawn(phil, philosopher, [N, init]), if N < (?N_PHIL) - 1 -> seresye:assert(restaurant, {room_ticket, N}); true -> ok end, phil_spawn(N + 1). philosopher(N, init) -> new_seed(), philosopher(N, ok); philosopher(N, X) -> think(N), Ticket = seresye:wait_and_retract(restaurant, {room_ticket, '_'}), seresye:wait_and_retract(restaurant, {fork, N}), seresye:wait_and_retract(restaurant, {fork, (N + 1) rem (?N_PHIL)}), eat(N), seresye:assert(restaurant, {fork, N}), seresye:assert(restaurant, {fork, (N + 1) rem (?N_PHIL)}), seresye:assert(restaurant, Ticket), philosopher(N, X). think(N) -> io:format("~w: thinking ...~n", [N]), timer:sleep(random:uniform(10) * 1000). eat(N) -> io:format("~w: eating ...~n", [N]), timer:sleep(random:uniform(10) * 1000). new_seed() -> {_, _, X} = erlang:now(), {H, M, S} = time(), H1 = H * X rem 32767, M1 = M * X rem 32767, S1 = S * X rem 32767, put(random_seed, {H1, M1, S1}). ================================================ FILE: examples/seresye_prodcons.erl ================================================ %%% ERESYE, an ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module(seresye_prodcons). -export([start/0, prod/1, cons/1, cons_1/1]). start() -> application:start(seresye), eresye:start(pc), spawn(prodcons, cons_1, [1]), spawn(prodcons, cons_1, [2]), spawn(prodcons, cons_1, [3]), spawn(prodcons, prod, [0]), ok. prod(20) -> ok; prod(Index) -> eresye:assert(pc, {item, Index}), prod(Index + 1). cons(20) -> ok; cons(Index) -> Fact = eresye:retract(pc, {item, fun (X) -> X == Index end}), io:format("Consumer ~p~n", [Fact]), cons(Index + 1). cons_1(N) -> Fact = eresye:wait_and_retract(pc, {item, '_'}), io:format("~w: Consumer ~p~n", [N, Fact]), timer:sleep(random:uniform(500)), cons_1(N). ================================================ FILE: examples/seresyee_auto.erl ================================================ %%% SERESYE, an ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php %%% ====================================================== %%% Automotive Expert System %%% %%% This expert system diagnoses some simple %%% problems with a car. %%% %%% It is a bare translation of the same example %%% provided in CLIPS Version 6.0 %%% %%% To execute, type 'auto:start().' %%% ====================================================== -module(seresyee_auto). -export([determine_battery_state/2, determine_conductivity_test/4, determine_engine_state/2, determine_gas_level/3, determine_knocking/2, determine_low_output/2, determine_misfiring/2, determine_point_surface_state_1/3, determine_point_surface_state_2/2, determine_rotation_state/2, determine_sluggishness/2, no_repairs/2, normal_engine_state_conclusions/2, print_repair/3, start/0, unsatisfactory_engine_state_conclusions/2]). -neg_rule({determine_engine_state, [{'working-state', engine, '__IGNORE_UNDERSCORE__'}, {repair, '__IGNORE_UNDERSCORE__'}]}). -include_lib("seresye/include/seresye.hrl"). %% ********************** %% * ENGINE STATE RULES * %% ********************** normal_engine_state_conclusions(Engine, {'working-state', engine, normal}) -> seresye_engine:assert(Engine, [{repair, "No repair needed."}, {'spark-state', engine, normal}, {'charge-state', battery, charged}, {'rotation-state', engine, rotates}]). unsatisfactory_engine_state_conclusions(Engine, {'working-state', engine, unsatisfactory}) -> seresye_engine:assert(Engine, [{'charge-state', battery, charged}, {'rotation-state', engine, rotates}]). %% *************** %% * QUERY RULES * %% *************** determine_engine_state(Engine, {start, _}) when not {rule, [{'working-state', engine, _}, {repair, _}]} -> case ask_yn('Does the engine start (yes/no)? ') of true -> case ask_yn('Does the engine run normally (yes/no)? ') of true -> seresye_engine:assert(Engine, {'working-state', engine, normal}); _ -> seresye_engine:assert(Engine, {'working-state', engine, unsatisfactory}) end; _ -> seresye_engine:assert(Engine, {'working-state', engine, 'does-not-start'}) end. determine_rotation_state(Engine, {'working-state', engine, 'does-not-start'}) when not {rule, [{'rotation-state', engine, _}, {repair, _}]} -> case ask_yn('Does the engine rotate (yes/no)? ') of true -> seresye_engine:assert(Engine, [{'rotation-state', engine, rotates}, {'spark-state', engine, 'irregular-spark'}]); _ -> seresye_engine:assert(Engine, [{'rotation-state', engine, 'does-not-rotate'}, {'spark-state', engine, 'does-not-spark'}]) end. determine_sluggishness(Engine, {'working-state', engine, unsatisfactory}) when not {rule, [{repair, _}]} -> case ask_yn('Is the engine sluggish (yes/no)? ') of true -> seresye_engine:assert(Engine, {repair, "Clean the fuel line."}); _ -> Engine end. determine_misfiring(Engine, {'working-state', engine, unsatisfactory}) when not {rule, [{repair, _}]} -> case ask_yn('Does the engine misfire (yes/no)? ') of true -> seresye_engine:assert(Engine, [{repair, "Point gap adjustment."}, {'spark-state', engine, 'irregular-spark'}]); _ -> Engine end. determine_knocking(E, {'working-state', engine, unsatisfactory}) when not {rule, [{repair, _}]} -> case ask_yn('Does the engine knock (yes/no)? ') of true -> seresye_engine:assert(E, {repair, "Timing adjustment."}); _ -> E end. determine_low_output(E, {'working-state', engine, unsatisfactory}) when not {rule, [{symptom, engine, _}, {repair, _}]} -> case ask_yn('Is the output of the engine low (yes/no)? ') of true -> seresye_engine:assert(E, {symptom, engine, 'low-output'}); _ -> seresye_engine:assert(E, {symptom, engine, 'not-low-output'}) end. determine_gas_level(E, {'working-state', engine, 'does-not-start'}, {'rotation-state', engine, rotates}) when not {rule, [{repair, _}]} -> case ask_yn('Does the tank have any gas in it (yes/no)? ') of false -> seresye_engine:assert(E, {repair, "Add gas."}); _ -> E end. determine_battery_state(E, {'rotation-state', engine, 'does-not-rotate'}) when not {rule, [{'charge-state', battery, _}, {repair, _}]} -> case ask_yn('Is the battery charged (yes/no)? ') of true -> seresye_engine:assert(E, {'charge-state', battery, charged}); _ -> seresye_engine:assert(E, [{repair, "Charge the battery."}, {'charge-state', battery, dead}]) end. determine_point_surface_state_1(E, {'working-state', engine, 'does-not-start'}, {'spark-state', engine, 'irregular-spark'}) when not {rule, [{repair, _}]} -> dpss(E). determine_point_surface_state_2(E, {symptom, engine, 'low-output'}) when not {rule, [{repair, _}]} -> dpss(E). dpss(E) -> case ask_question('What is the surface state of the points (normal/burned/contaminated)? ') of [$b, $u, $r, $n, $e, $d | _] -> seresye_engine:assert(E, {repair, "Replace the points."}); [$c, $o, $n, $t, $a, $m, $i, $n, $a, $t, $e, $d | _] -> seresye_engine:assert(E, {repair, "Clean the points."}); _ -> E end. determine_conductivity_test(E, {'working-state', engine, 'does-not-start'}, {'spark-state', engine, 'does-not-spark'}, {'charge-state', battery, charged}) when not {rule, [{repair, _}]}; true -> case ask_yn('Is the conductivity test for the ignition coil positive (yes/no)? ') of true -> seresye_engine:assert(E, {repair, "Repair the distributor lead wire."}); _ -> seresye_engine:assert(E, {repair, "Replace the ignition coil."}) end. no_repairs(E, {start, _}) when not {rule, [{repair, _}]}, true -> seresye_engine:assert(E, {repair, "Take your car to a mechanic."}). print_repair(E, {repair, X}, {start, _}) -> io:format("Suggested Repair: ~p~n", [X]), E. ask_yn(Prompt) -> [Response | _] = io:get_line(Prompt), case Response of $y -> true; _ -> false end. ask_question(Prompt) -> io:get_line(Prompt). start() -> Engine0 = seresye_engine:new(), %% Rules with high priority (10) Engine2 = lists:foldl(fun (Rule, Engine1) -> seresye_engine:add_rule(Engine1, {?MODULE, Rule}, 10) end, Engine0, [normal_engine_state_conclusions, unsatisfactory_engine_state_conclusions, print_repair]), %% Rules with normal priority (0) Engine3 = lists:foldl(fun (Rule, Engine1) -> seresye_engine:add_rule(Engine1, {?MODULE, Rule}) end, Engine2, [determine_engine_state, determine_rotation_state, determine_sluggishness, determine_misfiring, determine_knocking, determine_low_output, determine_gas_level, determine_battery_state, determine_point_surface_state_1, determine_point_surface_state_2, determine_conductivity_test]), %% Rules with low priority (-10) Engine4 = seresye_engine:add_rule(Engine3, {?MODULE, no_repairs}, -10), seresye_engine:assert(Engine4, {start, ok}). ================================================ FILE: features/seresyet_12.feature ================================================ Feature: Support explicit state passing in an seresye system In order to make callbacks more easily accessible and less prone to requiring side effects As an Erlang Developer I want to be able to have my own per engine state and have that state be passed to my rules when they execute Scenario: Retrievable state gets passed to rules Given a seresye engine that is initialized with state And initialized with data When seresye propagation is complete Then the per engine state is retrievable And contains the data populated by the rules ================================================ FILE: features/seresyet_13.feature ================================================ Feature: Support defining rules via module attributes In order to seresye more usable As an Erlang Developer I want to be able define my rules via rule attributes in the module file itself Scenario: Rules are defined in the attribute itself Given a seresye engine that is initialized with data And rules from a module with rules defined via attributes When when seresye propagation is complete Then the engine runs normally And contains the data populated by the rules ================================================ FILE: include/seresye.hrl ================================================ %% Parse transforms for automatic creating negative specs for rules in %% a rules file. though the user can write them in a spec it is much %% nicer to write them in a when clause and let the transform do its %% work. -compile({parse_transform, seresye_autoneg}). -compile({parse_transform, seresye_transform}). ================================================ FILE: rebar.config ================================================ {deps, [ {edown, ".*", {git, "https://github.com/uwiger/edown.git", {branch, "master"}}}, {erlware_commons, ".*", {git, "https://github.com/erlware/erlware_commons.git", {branch, "master"}}}, {parse_trans, ".*", {git, "https://github.com/esl/parse_trans.git", {branch, "master"}}} ]}. ================================================ FILE: sinan.config ================================================ {project_name, seresye}. {project_vsn, "0.0.4"}. {compile_args, [debug_info, warnings_as_errors, {warn_format, 2}, warn_export_all, warn_export_vars, warn_shadow_vars, warn_obsolete_guard, warn_unused_import]}. {ignore_dirs, ["_", ".", "features"]}. ================================================ FILE: src/internal.hrl ================================================ -record(seresye, {kb, alfa, join, agenda, pending_actions, client_state, fired_rule, hooks = []}). ================================================ FILE: src/seresye.app.src ================================================ %% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*- {application, seresye, [{description, "SERESYE means Swarm oriented ERlang Expert SYstem Engine. It is a library " "to write expert systems and rule processing engines using the Erlang " "programming language"}, {vsn, "0.0.4"}, {modules, []}, {registered, []}, {applications, [kernel, stdlib, erlware_commons]}, {mod, {seresye_app, []}}]}. ================================================ FILE: src/seresye.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module(seresye). %%==================================================================== %% External exports %%==================================================================== -export([start/0, start/1, start/2, stop/1, get_engine/1, add_rules/2, add_rule/2, add_rule/3, assert/2, get_kb/1, get_rules_fired/1, get_client_state/1, set_hooks/2, get_fired_rule/1, set_client_state/2, query_kb/2, serialize/1, remove_rule/2, retract/2]). %% gen_server callbacks -export([start_link/0, start_link/1, start_link/2, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %%==================================================================== %% External functions %%==================================================================== start() -> seresye_sup:start_engine(). start(Name) -> seresye_sup:start_engine(Name). start(Name, ClientState) -> seresye_sup:start_engine(Name, ClientState). set_hooks(Name, Hooks) when is_list(Hooks) -> gen_server:cast(Name, {set_hooks, Hooks}). set_client_state(Name, NewState) -> gen_server:cast(Name, {set_client_state, NewState}). get_client_state(Name) -> gen_server:call(Name, get_client_state). stop(EngineName) -> (catch gen_server:call(EngineName, stop)), ok. get_engine(EngineName) -> gen_server:call(EngineName, get_engine). %% @doc Insert a fact in the KB. %% It also checks if the fact verifies any condition, %% if this is the case the fact is also inserted in the alpha-memory assert(Name, Facts) -> gen_server:call(Name, {assert, Facts}, infinity). %% @doc removes a 'fact' in the Knowledge Base and if something occurs %% Condition is also deleted from the corresponding alpha-memory retract(Name, Facts) -> gen_server:call(Name, {retract, Facts}, infinity). add_rules(Name, RuleList) when is_list(RuleList) orelse is_atom(RuleList) -> gen_server:call(Name, {add_rules, RuleList}). add_rule(Name, Fun) -> gen_server:call(Name, {add_rule, Fun}). add_rule(Name, Rule, Salience) -> gen_server:call(Name, {add_rule, Rule, Salience}). remove_rule(Name, Rule) -> gen_server:call(Name, {remove_rule, Rule}). get_rules_fired(Name) -> gen_server:call(Name, get_rules_fired). get_fired_rule(Name) -> gen_server:call(Name, get_fired_rule). get_kb(Name) -> gen_server:call(Name, get_kb). query_kb(Name, Pattern) -> gen_server:call(Name, {query_kb, Pattern}). serialize(Name) -> gen_server:call(Name, serialize). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== start_link() -> gen_server:start_link(?MODULE, [], []). start_link(Name) when is_atom(Name) -> gen_server:start_link({local, Name}, ?MODULE, [], []); start_link(ClientState) when not is_atom(ClientState) -> gen_server:start_link(?MODULE, [ClientState], []). start_link(ClientState, Name) when is_atom(Name) -> gen_server:start_link({local, Name}, ?MODULE, [ClientState], []). init([]) -> {ok, seresye_engine:new()}; init([Engine]) when element(1, Engine) == seresye -> {ok, seresye_engine:restore(Engine)}; init([ClientState]) -> {ok, seresye_engine:new(ClientState)}. handle_call(get_client_state, _From, State) -> Reply = try {ok, seresye_engine:get_client_state(State)} catch Type:Reason -> {error, {Type, Reason}} end, {reply, Reply, State}; handle_call(stop, _From, State) -> {stop, normal, State}; handle_call({assert, Facts}, _From, State0) -> {Reply, State1} = try {ok, seresye_engine:assert(State0, Facts)} catch Type:Reason -> {{error, {Type, Reason}}, State0} end, {reply, Reply, State1}; handle_call({retract, Facts}, _From, State0) -> {Reply, State1} = try {ok, seresye_engine:retract(State0, Facts)} catch Type:Reason -> {{error, {Type, Reason}}, State0} end, {reply, Reply, State1}; handle_call({add_rules, Rules}, _From, State0) -> {Reply, State1} = try {ok, seresye_engine:add_rules(State0, Rules)} catch Type:Reason -> {{error, {Type, Reason}}, State0} end, {reply, Reply, State1}; handle_call({add_rule, Rule}, _From, State0) -> {Reply, State1} = try {ok, seresye_engine:add_rule(State0, Rule)} catch Type:Reason -> {{error, {Type, Reason}}, State0} end, {reply, Reply, State1}; handle_call({add_rule, Rule, Salience}, _From, State0) -> {Reply, State1} = try {ok, seresye_engine:add_rule(State0, Rule, Salience)} catch Type:Reason -> {{error, {Type, Reason}}, State0} end, {reply, Reply, State1}; handle_call({remove_rule, Rule}, _From, State0) -> {Reply, State1} = try {ok, seresye_engine:remove_rule(State0, Rule)} catch Type:Reason -> {{error, {Type, Reason}}, State0} end, {reply, Reply, State1}; handle_call(get_rules_fired, _From, State0) -> Reply = try seresye_engine:get_rules_fired(State0) catch Type:Reason -> {error, {Type, Reason}} end, {reply, Reply, State0}; handle_call(get_fired_rule, _From, State0) -> Reply = try seresye_engine:get_fired_rule(State0) catch Type:Reason -> {error, {Type, Reason}} end, {reply, Reply, State0}; handle_call(get_engine, _From, State0) -> {reply, State0, State0}; handle_call(get_kb, _From, State0) -> Reply = try seresye_engine:get_kb(State0) catch Type:Reason -> {error, {Type, Reason}} end, {reply, Reply, State0}; handle_call({query_kb, Pattern}, _From, State0) -> Reply = try seresye_engine:query_kb(State0, Pattern) catch Type:Reason -> {error, {Type, Reason}} end, {reply, Reply, State0}; handle_call(serialize, _From, State) -> Reply = seresye_engine:serialize(State), {reply, Reply, State}. handle_cast({set_hooks, Hooks}, State) -> {noreply, seresye_engine:set_hooks(State, Hooks)}; handle_cast({set_client_state, CS}, State) -> {noreply, seresye_engine:set_client_state(State, CS)}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. ================================================ FILE: src/seresye_agenda.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% Copyright (c) 2011 Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module(seresye_agenda). -export([new/1, add_activation/3, add_activation/4, breadth_order/2, clear_agenda/1, delete_activation/2, delete_rule/2, depth_order/2, fifo_order/2, get_activation/2, get_activation_from_name/2, get_activation_salience/2, get_first_activation/1, get_number_of_activations/1, get_rules_fired/1, get_strategy/1, pop_rule/1, set_activation_salience/3, set_rule_salience/3, set_strategy/2]). -include("internal.hrl"). -record(agenda, {rules_fired, strategy, rule_list, exec_state, id, pending_actions}). %%==================================================================== %% External functions %%==================================================================== new(Seresye) -> Seresye#seresye{agenda=#agenda{rules_fired=0, strategy=depth, rule_list=[], exec_state=nil, pending_actions=[], id=0}}. add_activation(Agenda, Rule, Args) -> add_activation(Agenda, Rule, Args, 0). add_activation(EngineState0 = #seresye{agenda= Agenda0 = #agenda{strategy=Strategy, rule_list=RuleList0, id=Id}}, Rule, Args, Salience) -> RuleList1 = case Strategy of depth -> depth_add(RuleList0, Rule, Args, Salience, Id); breadth -> breadth_add(RuleList0, Rule, Args, Salience, Id); fifo -> fifo_add(RuleList0, Rule, Args, Salience, Id) end, Agenda1 = Agenda0#agenda{rule_list=RuleList1, id=Id + 1}, execute_pending(after_activation_schedule(EngineState0#seresye{agenda=Agenda1})). %% @doc Remove all activation from Agenda, %% returns an empty agenda with same past strategy clear_agenda(EngineState = #seresye{agenda=Agenda0}) -> EngineState#seresye{agenda=Agenda0#agenda{rule_list=[], id=0}}. get_strategy(#seresye{agenda=#agenda{strategy=Strategy}}) -> Strategy. set_strategy(EngineState = #seresye{agenda=Agenda0 = #agenda{rule_list=RuleList0}}, NewStrategy) -> RuleList1 = case NewStrategy of depth -> lists:sort(fun depth_order/2, RuleList0); breadth -> lists:sort(fun breadth_order/2, RuleList0); fifo -> lists:sort(fun fifo_order/2, RuleList0); _ -> erlang:throw({seresye, {invalid_strategy, NewStrategy}}) end, EngineState#seresye{agenda=Agenda0#agenda{strategy=NewStrategy, rule_list=RuleList1}}. get_rules_fired(#seresye{agenda=#agenda{rules_fired=Fired}}) -> Fired. %% @doc Remove activation with id='Id' or %% all activation whose id is in the list passed as argument delete_activation(EngineState, []) -> EngineState; delete_activation(EngineState = #seresye{agenda=Agenda0 = #agenda{rule_list=RuleList0}}, Id) when not is_list(Id) -> EngineState#seresye{agenda=Agenda0#agenda{rule_list=lists:keydelete(Id, 4, RuleList0)}}; delete_activation(EngineState, [Id | OtherId]) -> EngineState1 = delete_activation(EngineState, Id), delete_activation(EngineState1, OtherId). %% @doc Remove all activation associated with rule 'Rule' from the agenda, %% returns the modified agenda delete_rule(EngineState = #seresye{agenda=Agenda0 = #agenda{rule_list=RuleList0}}, Rule) -> ActList = proplists:lookup_all(Rule, RuleList0), RuleList1 = lists:foldl(fun (X, R1) -> lists:delete(X, R1) end, RuleList0, ActList), EngineState#seresye{agenda=Agenda0#agenda{rule_list=RuleList1}}. %% @doc Returns the Id of activation associated to rule 'Rule' and %% arguments 'Args', false if it not present get_activation(#seresye{agenda=#agenda{rule_list=RuleList0}}, {Rule, Args}) -> ActList = proplists:lookup_all(Rule, RuleList0), case lists:keysearch(Args, 2, ActList) of {value, {_, _, _, Id}} -> Id; false -> false end. %% @doc Returns the Id of first activation associated to rule 'Rule', %% false if 'Rule' is not present in the agenda get_activation_from_name(#seresye{agenda=#agenda{rule_list=RuleList0}}, Rule) -> case lists:keysearch(Rule, 1, RuleList0) of {value, {_, _, _, Id}} -> Id; false -> false end. %% @doc Return the Id associated to first activation in the agenda %% false if agenda is empty get_first_activation(#seresye{agenda=#agenda{rule_list=[First | _]}}) -> element(4, First). %% @doc Return the salience value of activation with id='Id' %% false if Id is not present in the agenda get_activation_salience(#seresye{agenda=#agenda{rule_list=RuleList0, id=NextId}}, Id) -> case NextId < Id of true -> false; false -> case lists:keysearch(Id, 4, RuleList0) of {value, {_, _, Salience, _}} -> Salience; false -> false end end. %% @doc Sets the salience value of activation with id='Id', %% returns the modified agenda set_activation_salience(EngineState = #seresye{agenda=Agenda0 = #agenda{rule_list=RuleList0, strategy=Strategy}}, Id, NewSalience) when is_number(NewSalience) and not is_list(Id) -> RuleList2 = case lists:keysearch(Id, 4, RuleList0) of {value, {Rule, Args, _Salience, Id}} -> RuleList1 = lists:keyreplace(Id, 4, RuleList0, {Rule, Args, NewSalience, Id}), order(RuleList1, Strategy); false -> RuleList0 end, EngineState#seresye{agenda=Agenda0#agenda{rule_list=RuleList2}}; set_activation_salience(_EngineState, Id, NewSalience) when not is_list(Id) -> erlang:throw({seresye, {invalid_salience, NewSalience}}); set_activation_salience(EngineState0, [Id | OtherId], NewSalience) -> EngineState1 = set_activation_salience(EngineState0, Id, NewSalience), set_activation_salience(EngineState1, OtherId, NewSalience). %% @doc Sets the salience value of all activations associated to rule 'Rule', %% returns the modified agenda set_rule_salience(EngineState0 = #seresye{agenda=#agenda{rule_list=RuleList0}}, Rule, NewSalience) -> ActList = proplists:lookup_all(Rule, RuleList0), IdList = [Id || {_, _, _, Id} <- ActList], set_activation_salience(EngineState0, IdList, NewSalience). %%==================================================================== %% Internal functions %%==================================================================== execute_pending(EngineState0 = #seresye{agenda = Agenda0 = #agenda{pending_actions=[PA | Rest]}}) -> EngineState1 = PA(EngineState0#seresye{agenda=Agenda0#agenda{pending_actions=Rest}}), execute_pending(EngineState1); execute_pending(EngineState0 = #seresye{agenda = #agenda{pending_actions=[]}}) -> EngineState0. after_activation_schedule(EngineState0 = #seresye{agenda=Agenda0 = #agenda{rule_list=RuleList0, exec_state=ExecState0, pending_actions=PA0, rules_fired=RF0}}) -> {ExecState1, RuleList1, PA1, RF1} = if ExecState0 == active -> {ExecState0, RuleList0, PA0, RF0}; true -> {RuleToExecute, RL} = pop_rule(RuleList0), if RuleToExecute == false -> {nil, RL, PA0, RF0}; true -> {active, RL, PA0 ++ [fun(EngineState) -> exec(EngineState, RuleToExecute) end], RF0 + 1} end end, EngineState0#seresye{agenda=Agenda0#agenda{exec_state=ExecState1, rule_list=RuleList1, pending_actions=PA1, rules_fired=RF1}}. after_execution_schedule(EngineState0 = #seresye{agenda=Agenda0 = #agenda{rule_list=RuleList0, pending_actions=PA0, rules_fired=RF0}}) -> {RuleToExecute, RL} = pop_rule(RuleList0), {ExecState1, RuleList1, PA1, RF1} = if RuleToExecute == false -> {nil, RL, PA0, RF0}; true -> {active, RL, PA0 ++ [fun(EngineState) -> exec(EngineState, RuleToExecute) end], RF0 + 1} end, EngineState0#seresye{agenda=Agenda0#agenda{exec_state=ExecState1, rule_list=RuleList1, pending_actions=PA1, rules_fired=RF1}}. %% @doc Remove the first activation, returns the rule, %% function arguments and the modified agenda pop_rule([]) -> {false, []}; pop_rule([Activation | NewRuleList]) -> {Activation, NewRuleList}. depth_add(RuleList, Rule, Args, Salience, Id) -> {L1, L2} = lists:splitwith(fun ({_, _, S, _}) -> S > Salience end, RuleList), L1 ++ [{Rule, Args, Salience, Id} | L2]. breadth_add(RuleList, Rule, Args, Salience, Id) -> {L1, L2} = lists:splitwith(fun ({_, _, S, _}) -> S >= Salience end, RuleList), L1 ++ [{Rule, Args, Salience, Id} | L2]. fifo_add(RuleList, Rule, Args, Salience, Id) -> RuleList ++ [{Rule, Args, Salience, Id}]. get_number_of_activations({_Strategy, RuleList, _NextId}) -> length(RuleList). order(ActionList, Strategy) -> case Strategy of breadth -> lists:sort(fun breadth_order/2, ActionList); depth -> lists:sort(fun depth_order/2, ActionList); fifo -> lists:sort(fun fifo_order/2, ActionList); _Other -> ActionList end. %% @doc define when an Act1 comes before Act2 in breadth strategy breadth_order(Act1, Act2) when element(3, Act1) > element(3, Act2) -> true; breadth_order(Act1, Act2) when element(3, Act1) < element(3, Act2) -> false; breadth_order(Act1, Act2) when element(4, Act1) > element(4, Act2) -> false; breadth_order(_Act1, _Act2) -> true. %% @doc define when an Act1 comes before Act2 in depth strategy depth_order(Act1, Act2) when element(3, Act1) > element(3, Act2) -> true; depth_order(Act1, Act2) when element(3, Act1) < element(3, Act2) -> false; depth_order(Act1, Act2) when element(4, Act1) < element(4, Act2) -> false; depth_order(_Act1, _Act2) -> true. %% @doc define when an Act1 comes before Act2 in fifo strategy fifo_order(Act1, Act2) when element(4, Act1) > element(4, Act2) -> false; fifo_order(_Act1, _Act2) -> true. %%==================================================================== %% Executor functions %%==================================================================== exec(EngineState0, R) -> {Mod, Fun} = case R of {{M, F}, _, _, _} -> {M, F}; _ -> {'_', '_'} end, EngineState2 = case catch execute_rule(EngineState0, R) of {'EXIT', {function_clause, [{Mod, Fun, _} | _]}} -> EngineState0; {'EXIT', {function_clause, [{Mod, Fun, _, _Location} | _]}} -> EngineState0; {'EXIT', Reason} -> erlang:throw({seresye, {rule_execution, [R, Reason]}}); EngineState1 -> EngineState1 end, after_execution_schedule(EngineState2). %% Fun = {Module, Function} or %% Fun = fun (...) execute_rule(EngineState, {{M,F}, Args, X1, X2}) -> L = length(Args) + 1, execute_rule(EngineState, {fun M:F/L, Args, X1, X2}); execute_rule(EngineState, {Fun, Args, _, _}) when is_function(Fun) -> case proplists:get_value(before_rule, EngineState#seresye.hooks) of BF when is_function(BF) -> BF(EngineState, Fun, Args); _ -> ignore end, Result = apply(Fun, [EngineState#seresye { fired_rule = {Fun, Args} } | Args]), case proplists:get_value(after_rule, EngineState#seresye.hooks) of AF when is_function(AF) -> AF(Result, Fun, Args); _ -> ignore end, Result#seresye{ fired_rule = undefined }. ================================================ FILE: src/seresye_app.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% Copyright (c) 2011 Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module(seresye_app). -behaviour(application). %% Application callbacks -export([start/2, stop/1]). %%%=================================================================== %%% Application callbacks %%%=================================================================== start(_StartType, _StartArgs) -> case seresye_sup:start_link() of {ok, Pid} -> {ok, Pid}; Error -> Error end. stop(_State) -> ok. ================================================ FILE: src/seresye_autoneg.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2011, Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module(seresye_autoneg). -export([parse_transform/2]). parse_transform(Forms, _Options) -> {Head, NewAttributes, Body} = lists:foldl(fun parse_forms/2, {[], [], []}, Forms), lists:reverse(Head) ++ NewAttributes ++ lists:reverse(Body). parse_forms({function,Line,FunName, Arity, Clauses0}, {Head, Attrs0, Body}) -> {_, Clauses2, Attrs2} = lists:foldl(fun(Clause0, {ClauseCount, Clauses1, Attrs1}) -> case parse_clause(Clause0) of {false, Clause1} -> {ClauseCount + 1, [Clause1 | Clauses1], Attrs1}; {NewDetail, Clause1} -> {ClauseCount + 1, [Clause1 | Clauses1], [{ClauseCount, NewDetail} | Attrs1]} end end, {0, [], []}, Clauses0), {Head, lists:map(fun({CC, Detail0}) -> {attribute, Line, rule_neg, {FunName, CC, Detail0}} end, Attrs2) ++ Attrs0, [{function, Line, FunName, Arity, lists:reverse(Clauses2)} | Body]}; parse_forms(F = {attribute,_,file,_}, {Head, Attrs0, Body}) -> {[F | Head], Attrs0, Body}; parse_forms(F = {attribute,_,module,_}, {Head, Attrs, Body}) -> {[F | Head], Attrs, Body}; parse_forms(El, {Head, Attrs, Body}) -> {Head, Attrs, [El |Body]}. parse_clause({clause,Line, Args, [[{op,_,'not', {tuple, _, [{atom,_,rule}, Neg]}} | AR] | OR], Body}) -> NewGuards = case AR of [] -> OR; _ -> [AR | OR] end, {rewrite_negs(Neg), {clause, Line, Args, NewGuards, Body}}; parse_clause(Clause) -> {false, Clause}. rewrite_negs({tuple, L, Elements}) -> {tuple, L, lists:map(fun rewrite_negs/1, Elements)}; rewrite_negs(C = {cons, _, _, _}) -> rewrite_cons(C); rewrite_negs({var, Line, '_'}) -> {atom, Line, '___IGNORE___'}; rewrite_negs(Else) -> Else. rewrite_cons({cons, Line, Element, nil}) -> {cons, Line, rewrite_negs(Element), nil}; rewrite_cons({cons, Line, E1, Rest}) -> {cons, Line, rewrite_negs(E1), rewrite_negs(Rest)}. ================================================ FILE: src/seresye_engine.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% Copyright (c) 2011 Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module(seresye_engine). -ifdef(debug). -define(LOG(F, X), io:format(F, X)). -else. -define(LOG(F, X), true). -endif. %%==================================================================== %% Include files %%==================================================================== -include("internal.hrl"). %%==================================================================== %% External exports %%==================================================================== -export([new/0, new/1, serialize/1, restore/1, cleanup/1, set_hooks/2, get_fired_rule/1, add_rules/2, add_rule/2, add_rule/3, assert/2, get_kb/1, get_rules_fired/1, get_client_state/1, set_client_state/2, query_kb/2, remove_rule/2, retract/2]). %%==================================================================== %% External functions %%==================================================================== new() -> new([]). new(ClientState) -> seresye_agenda:new(#seresye{kb=[], alfa=[], join=seresye_tree_list:new(), pending_actions=[], client_state=ClientState}). set_hooks(EngineState, Hooks) when is_list(Hooks) -> EngineState#seresye{ hooks = Hooks }. set_client_state(EngineState, NewState) -> EngineState#seresye{client_state=NewState}. get_client_state(#seresye{client_state=State}) -> State. cleanup(#seresye{ alfa = Alfa0, join = Join0 }) -> [ begin (catch ets:delete(Tab)), Tab end || {_, Tab, _} <- Alfa0 ] ++ [ begin (catch ets:delete(Tab)), Tab end || {{Tab, _}, _, _, _, _} <- Join0, is_integer(Tab) ]. restore(#seresye{ alfa = Alfa0, join = Join0 } = Engine) -> TabCache = ets:new(tab_cache, []), Alfa = [ {Cond, restore_tab(TabCache, Tab), Alfa_fun} || {Cond, Tab, Alfa_fun} <- Alfa0 ], Join = [ {case Key of {Tab,V} when element(1,Tab) == ets_table -> {restore_tab(TabCache, Tab), V}; _ -> Key end, Beta, Children, Parent, Pos} || {Key, Beta, Children, Parent, Pos} <- Join0 ], ets:delete(TabCache), Engine#seresye{ alfa = Alfa, join = Join }. serialize(#seresye{ alfa = Alfa0, join = Join0 } = Engine) -> Alfa = [ {Cond, serialize_tab(Tab), Alfa_fun} || {Cond, Tab, Alfa_fun} <- Alfa0 ], Join = [ {case Key of {Tab,V} when is_integer(Tab) -> {serialize_tab(Tab), V}; _ -> Key end, Beta, Children, Parent, Pos} || {Key, Beta, Children, Parent, Pos} <- Join0 ], Engine#seresye{ alfa = Alfa, join = Join }. % where serialize_tab(Tab) -> case ets:info(Tab) of undefined -> Tab; Info -> {ets_table, Tab, Info, ets:tab2list(Tab)} end. restore_tab(Cache, {ets_table, Tab, Info, Content}) -> case ets:lookup(Cache, Tab) of [{Tab, NewTab}] -> NewTab; [] -> NewTab = ets:new(proplists:get_value(name, Info), [ proplists:get_value(Opt, Info) || Opt <- [type,protection] ] ++ [ {Opt, proplists:get_value(Opt, Info)} || Opt <- [keypos] ]), ets:insert(NewTab, Content), ets:insert(Cache, {Tab, NewTab}), NewTab end; restore_tab(_Cache, Tab) -> Tab. %% @doc Insert a fact in the KB. %% It also checks if the fact verifies any condition, %% if this is the case the fact is also inserted in the alpha-memory assert(EngineState0, Facts) when is_list(Facts) -> lists:foldl(fun(Fact, EngineState1) -> assert(EngineState1, Fact) end, EngineState0, Facts); assert(EngineState = #seresye{kb=Kb, alfa=Alfa}, Fact) when is_tuple(Fact) -> execute_pending(case lists:member(Fact, Kb) of false -> Kb1 = [Fact | Kb], check_cond(EngineState#seresye{kb=Kb1}, Alfa, {Fact, plus}); true -> EngineState end). %% @doc removes a 'fact' in the Knowledge Base and if something occurs %% Condition is also deleted from the corresponding alpha-memory retract(EngineState0, Facts) when is_list(Facts) -> lists:foldl(fun(Fact, EngineState1) -> retract(EngineState1, Fact) end, EngineState0, Facts); retract(EngineState = #seresye{kb=Kb, alfa=Alfa}, Fact) when is_tuple(Fact) -> execute_pending(case lists:member(Fact, Kb) of true -> Kb1 = Kb -- [Fact], check_cond(EngineState#seresye{kb=Kb1}, Alfa, {Fact, minus}); false -> EngineState end). add_rules(EngineState0, RulesList) when is_list(RulesList) -> lists:foldl(fun(Rule, EngineState1) -> add_rule(EngineState1, Rule) end, EngineState0, RulesList); add_rules(EngineState0, Module) when is_atom(Module) -> AST = get_abstract_code(Module), case get_rules(Module, AST) of [] -> erlang:throw({seresye, {no_rules_specified, Module}}); RulesList -> lists:foldl(fun(Rule, EngineState1) -> add_rule(EngineState1, Rule) end, EngineState0, RulesList) end. add_rule(EngineState, {Module, Fun, ClauseID, Salience}) -> add_rule(EngineState, {Module, Fun}, ClauseID, Salience); add_rule(EngineState, Fun) -> add_rule(EngineState, Fun, 0). add_rule(EngineState, {Module, Fun, ClauseID}, Salience) -> add_rule(EngineState, {Module, Fun}, ClauseID, Salience); add_rule(EngineState, {Module, Fun}, Salience) -> add_rule(EngineState, {Module, Fun}, 0, Salience). add_rule(EngineState0, {Module, Fun}, ClauseID, Salience) -> AST = get_abstract_code(Module), case get_conds(Fun, ClauseID, AST) of error -> erlang:throw({seresye, {error_extracting_conditions, Fun}}); CondsList -> execute_pending( lists:foldl(fun (X, EngineState1) -> case X of {error, Msg} -> erlang:throw({seresye, {error_adding_rule, [Module, Fun, Msg]}}); {PConds, NConds} -> ?LOG(">> PConds=~p~n", [PConds]), ?LOG(">> NConds=~p~n", [NConds]), add_rule__(EngineState1, {{Module, Fun}, Salience}, {PConds, NConds}) end end, EngineState0, CondsList)) end. remove_rule(EngineState0, Rule) -> execute_pending(remove_prod(seresye_agenda:delete_rule(EngineState0, Rule), Rule)). get_fired_rule(#seresye{ fired_rule = Rule }) -> Rule. get_rules_fired(EngineState) -> seresye_agenda:get_rules_fired(EngineState). get_kb(#seresye{kb=Kb}) -> Kb. query_kb(EngineState, Pattern) when is_tuple(Pattern) -> PList = tuple_to_list(Pattern), PatternSize = length(PList), FunList = [if is_function(X) -> X; X == '_' -> fun (_) -> true end; true -> fun (Z) -> Z == X end end || X <- PList], MatchFunction = fun (P) -> FunPatPairs = lists:zip(FunList, tuple_to_list(P)), FunEval = [F(X) || {F, X} <- FunPatPairs], MatchResult = lists:foldr(fun (X, Y) -> X and Y end, true, FunEval), MatchResult end, KB = [X || X <- get_kb(EngineState), size(X) == PatternSize], lists:filter(MatchFunction, KB); query_kb(Name, F) when is_function(F) -> lists:filter(F, get_kb(Name)). %%==================================================================== %% Internal functions %%==================================================================== execute_pending(EngineState0 = #seresye{pending_actions=[PA | Rest]}) -> EngineState1 = PA(EngineState0#seresye{pending_actions=Rest}), execute_pending(EngineState1); execute_pending(EngineState0 = #seresye{pending_actions=[]}) -> EngineState0. add_rule__(EngineState0 = #seresye{join=Join}, Rule, {PConds, NConds}) when is_tuple(Rule)-> Root = seresye_tree_list:get_root(Join), case NConds of [] -> make_struct(EngineState0, Rule, PConds, [], Root, nil); _ -> {EngineState1, Np_node} = make_struct(EngineState0, Rule, NConds, [], Root, ng), Root1 = seresye_tree_list:refresh(Root, EngineState1#seresye.join), make_struct(EngineState1, Rule, PConds, [], Root1, {Np_node, NConds}) end. %% @doc initializes the memory with a new alpha-present the facts %% In the Knowledge Base that satisfy the condition Cond initialize_alfa(_, _, []) -> nil; initialize_alfa(Cond, Tab, [Fact | Other_fact]) -> Fun = prepare_match_alpha_fun(Cond), case Fun(Fact) of Fact -> ets:insert(Tab, Fact), initialize_alfa(Cond, Tab, Other_fact); false -> initialize_alfa(Cond, Tab, Other_fact) end. prepare_match_alpha_fun(Cond) -> Expr = [{'fun',1, {clauses,[{clause,1, [{match,1,Cond,{var,1,'X__x__X'}}], [], [{var,1,'X__x__X'}]}, {clause,1,[{var,1,'_'}],[],[{atom,1,false}]}]}}], eval(Expr). get_abstract_code(Module) -> case beam_lib:chunks(code:which(Module), [abstract_code]) of {error, beam_lib, {file_error, File, enoent}} -> erlang:throw({seresye, {unable_to_find_file, Module, File}}); {ok, {Module, [{abstract_code,no_abstract_code}]}} -> erlang:throw({seresye, {no_abstract_code, Module, "module must be compiled with +debug_info"}}); {ok, {Module, [{abstract_code, {raw_abstract_v1, Forms}}]}} -> Forms end. get_conds(FunName, ClauseID, AST) -> Records = get_records(AST, []), case search_fun(FunName, AST) of {error, Reason} -> erlang:throw({seresye, {error_parsing_forms, FunName, Reason}}); {ok, CL} -> if ClauseID > 0 -> [read_clause(FunName, ClauseID, lists:nth(ClauseID, CL), Records, AST)]; true -> [read_clause(FunName, ClauseID, Clause, Records, AST) || Clause <- CL] end end. get_records([], Acc) -> Acc; get_records([{attribute, _, record, {RecordName, RecordFields}} | Tail], Acc) -> NewAcc = [{RecordName, get_record_fields(RecordFields, [])} | Acc], get_records(Tail, NewAcc); get_records([_ | Tail], Acc) -> get_records(Tail, Acc). get_record_fields([], Acc) -> lists:reverse(Acc); get_record_fields([{typed_record_field, RecordField, _T} | Tail], Acc) -> get_record_fields([RecordField | Tail], Acc); get_record_fields([{record_field, _, {atom, _, FieldName}, {nil, _}} | Tail], Acc) -> NewAcc = [{FieldName, {nil, []}} | Acc], get_record_fields(Tail, NewAcc); get_record_fields([{record_field, _, {atom, _, FieldName}, {Type, _, DefaultValue}} | Tail], Acc) -> NewAcc = [{FieldName, {Type, DefaultValue}} | Acc], get_record_fields(Tail, NewAcc); get_record_fields([{record_field, _, {atom, _, FieldName}} | Tail], Acc) -> NewAcc = [{FieldName, {atom, undefined}} | Acc], get_record_fields(Tail, NewAcc). get_rules(Module, AST) -> get_rules(Module, AST, []). get_rules(_Module, [], Acc) -> lists:reverse(Acc); get_rules(Module, [{attribute, _, rule, Rule} | Rest], Acc) -> get_rules(Module, Rest, [resolve_rule(Module, Rule) | Acc]); get_rules(Module, [{attribute, _, rules, RuleList0} | Rest], Acc) when is_list(RuleList0) -> RuleList1 = [resolve_rule(Module, Rule) || Rule <- RuleList0], get_rules(Module, Rest, RuleList1 ++ Acc); get_rules(Module, [_ | Rest], Acc) -> get_rules(Module, Rest, Acc). resolve_rule(Module, Rule) when is_atom(Rule) -> {Module, Rule, 0, 0}; resolve_rule(Module, {Rule, Salience}) -> {Module, Rule, 0, Salience}; resolve_rule(Module, {Rule, Salience, ClauseID}) -> {Module, Rule, ClauseID, Salience}; resolve_rule(Module, Rule) -> erlang:throw({seresye, {invalid_rule_definition, Module, Rule}}). search_fun([], _) -> {error, "function not found"}; search_fun(Func, [{function, _, Func, _, ClauseList} | _Rest]) -> {ok, ClauseList}; search_fun( Func, [_Tuple | Rest]) -> search_fun(Func, Rest). read_clause(FunName, ClauseID, Clause, RecordList, AST) -> {clause, _, ParamList, _, _} = Clause, PosConds = read_parameters(ParamList, RecordList), {PosConds, get_neg_conds(FunName, ClauseID, AST)}. get_neg_conds(FunName0, ClauseID0, AST) -> lists:foldl(fun({attribute,_,rule_neg, {FunName1, ClauseID1, Detail}}, _) when ClauseID0 == ClauseID1, FunName0 == FunName1 -> neg_to_list(rewrite_negs(Detail), []); (_, Acc) -> Acc end, [], AST). neg_to_list({cons, _, C1, Rest}, Acc) -> neg_to_list(Rest, [C1 | Acc]); neg_to_list({nil, _}, Acc) -> lists:reverse(Acc). rewrite_negs({tuple, _, Elements}) -> {tuple, 0, lists:map(fun rewrite_negs/1, Elements)}; rewrite_negs(C = {cons, _, _, _}) -> rewrite_cons(C); rewrite_negs({atom, _, '___IGNORE___'}) -> {var, 0, '_'}; rewrite_negs(Else) -> remove_line_numbers(Else). rewrite_cons({cons, _, Element, nil}) -> {cons, 0, rewrite_negs(Element), nil}; rewrite_cons({cons, _, E1, Rest}) -> {cons, 0, rewrite_negs(E1), rewrite_negs(Rest)}. read_parameters([{var, _, _} | Tail], RecordList) -> extract_parameters(Tail, RecordList, []). extract_parameters([], _, Acc) -> lists:reverse(Acc); extract_parameters([{match, _, {record, _, _, _} = R, {var, _, _}} | Tail], RecordList, Acc) -> extract_parameters([R | Tail], RecordList, Acc); extract_parameters([{match, _, Condition, {var, _, _}} | Tail], RecordList, Acc) -> extract_parameters(Tail, RecordList, [remove_line_numbers(Condition) | Acc]); extract_parameters([{match, _, {var, _, _}, {record, _, _, _} = R} | Tail], RecordList, Acc) -> extract_parameters([R | Tail], RecordList, Acc); extract_parameters([{match, _, {var, _, _}, Condition} | Tail], RecordList, Acc) -> extract_parameters(Tail, RecordList, [remove_line_numbers(Condition) | Acc]); extract_parameters([{record, _, RecordName, Condition} | Tail], RecordList, Acc) -> RecordDefinition = get_record_def(RecordName, RecordList), RecordDefaults = make_record_default(RecordDefinition, []), Pattern = {tuple, 0, [{atom, 0, RecordName} | make_record_pattern(Condition, RecordDefaults, RecordDefinition)]}, extract_parameters(Tail, RecordList, [Pattern | Acc]); extract_parameters([{tuple, L, Elements}| Tail], RecordList, Acc) -> Pattern = {tuple, L, extract_parameters(Elements, RecordList, [])}, extract_parameters(Tail, RecordList, [Pattern | Acc]); extract_parameters([Condition | Tail], RecordList, Acc) -> extract_parameters(Tail, RecordList, [remove_line_numbers(Condition) | Acc]). %% @doc removing the line numbers make it easier to compare conditions %% for equality %% Note since OTP 24 (https://github.com/erlang/otp/pull/3006) %% abstract code contains location conforming to %% {Line :: integer(), Column :: integer()} format, %% therefore additional clauses have been added remove_line_numbers(Values) when is_list(Values) -> lists:map(fun remove_line_numbers/1, Values); remove_line_numbers({op, _, Op, T1, T2}) -> {op, 0, Op, remove_line_numbers(T1), remove_line_numbers(T2)}; remove_line_numbers({Type, {L,C}}) % >= OTP24 when is_integer(L), is_integer(C), is_atom(Type) -> remove_line_numbers({Type, L}); remove_line_numbers({Type, L}) when is_integer(L), is_atom(Type) -> {Type, 0}; remove_line_numbers({string, _, String}) -> {string, 0, String}; remove_line_numbers({Type, {L, C}, Values}) % >= OTP24 when is_integer(L), is_integer(C), is_atom(Type), is_list(Values) -> remove_line_numbers({Type, L, Values}); remove_line_numbers({Type, L, Values}) when is_integer(L), is_atom(Type), is_list(Values) -> {Type, 0, lists:map(fun remove_line_numbers/1, Values)}; remove_line_numbers({Type, {L, C}, Value}) % >= OTP24 when is_integer(L), is_integer(C), is_atom(Type) -> remove_line_numbers({Type, L, Value}); remove_line_numbers({Type, L, Value}) when is_integer(L), is_atom(Type) -> {Type, 0, Value}; remove_line_numbers({Type, {L, C}, C1, C2}) when is_integer(L), is_integer(C), is_atom(Type) -> remove_line_numbers({Type, L, C1, C2}); remove_line_numbers({Type, L, C1, C2}) % >= OTP24 when is_integer(L), is_atom(Type) -> {Type, 0, remove_line_numbers(C1), remove_line_numbers(C2)}. get_record_def(_, []) -> nil; get_record_def(Name, [{Name, Definition} | _Rest]) -> Definition; get_record_def(Name, [_ | Rest]) -> get_record_def(Name, Rest). make_record_default([], Acc) -> lists:reverse(Acc); make_record_default([{_,_} | Tail], Acc) -> make_record_default(Tail, [{var, 0, '_'} | Acc]). make_record_pattern([], Pattern, _RecordDefinition) -> Pattern; make_record_pattern([{record_field, _, {atom, _, FieldName}, MatchValue} | Tail], Pattern, RecordDefinition) -> FieldPosition = get_record_field_pos(FieldName, RecordDefinition, 1), NewPattern = lists:sublist(Pattern, FieldPosition - 1) ++ [MatchValue] ++ lists:sublist(Pattern, FieldPosition + 1, length(Pattern) - FieldPosition), make_record_pattern(Tail, NewPattern, RecordDefinition). get_record_field_pos(Name, [], _) -> exit({bad_record_field, Name}); get_record_field_pos(Name, [{Name, _} | _], Index) -> Index; get_record_field_pos(Name, [{Name} | _], Index) -> Index; get_record_field_pos(Name, [_ | Tail], Index) -> get_record_field_pos(Name, Tail, Index + 1). %% @doc P and a list containing the conditions prior to Cond Of the %% same production make_struct(EngineState0 = #seresye{join=Join}, _Rule, [], _P, Cur_node, ng) -> %% create production node Key = {np_node, nil}, {Node, {Join2, EngineState1}} = case seresye_tree_list:child(Key, Cur_node, Join) of false -> Value = [], {Node0, Join1} = seresye_tree_list:insert(Key, Value, Cur_node, Join), {Node0, update_new_node(EngineState0, Node0, Cur_node, Join1)}; Node0 -> {Node0, {Join, EngineState0}} end, {EngineState1#seresye{join=Join2}, Node}; make_struct(EngineState0 = #seresye{join=Join}, Rule, [], _P, Cur_node, nil) -> %% create production node Key = {p_node, Rule}, {Join2, EngineState1} = case seresye_tree_list:child(Key, Cur_node, Join) of false -> Value = [], {Node, Join1} = seresye_tree_list:insert(Key, Value, Cur_node, Join), update_new_node(EngineState0, Node, Cur_node, Join1); Node -> {Fun, _Salience} = Rule, Key1 = element(1, Node), Sal = element(2, element(2, Key1)), io:format(">> Rule (~w) already present ~n", [Fun]), io:format(">> with salience = ~w~n", [Sal]), io:format(">> To change salience use 'set_salience()'.~n"), {Join, EngineState0} end, EngineState1#seresye{join=Join2}; make_struct(EngineState0 = #seresye{join=Join}, Rule, [], P, Cur_node, {Nod, NConds}) -> Id = seresye_tree_list:get_id(Nod), {Cur_node1, Join1, EngineState1} = make_join_node(EngineState0, Join, {old, {n_node, Id}}, NConds, P, Cur_node), Nod1 = seresye_tree_list:refresh(Nod, Join1), Join2 = seresye_tree_list:set_child(Cur_node1, Nod1, Join1), Key = {p_node, Rule}, {Join4, EngineState2} = case seresye_tree_list:child(Key, Cur_node1, Join2) of false -> Value = [], {Node, Join3} = seresye_tree_list:insert(Key, Value, Cur_node1, Join2), Cur_node2 = seresye_tree_list:refresh(Cur_node1, Join3), update_new_node(EngineState1, Node, Cur_node2, Join3); Node -> {Fun, _Salience} = Rule, Key1 = element(1, Node), Sal = element(2, element(2, Key1)), io:format(">> Rule (~w) already present ~n", [Fun]), io:format(">> with salience = ~w~n", [Sal]), io:format(">> To change salience use 'set_salience()'.~n"), {Join2, EngineState1} end, EngineState2#seresye{join=Join4}; make_struct(EngineState0 = #seresye{join=Join}, Rule, [Cond | T], P, Cur_node, Nod) -> {Alfa1, Tab} = add_alfa(EngineState0, Cond), {Cur_node1, Join1, EngineState1} = make_join_node(EngineState0, Join, Tab, Cond, P, Cur_node), P1 = P ++ [Cond], make_struct(EngineState1#seresye{alfa=Alfa1, join=Join1}, Rule, T, P1, Cur_node1, Nod). add_alfa(#seresye{kb=Kb, alfa=Alfa}, Cond) -> case is_present(Cond, Alfa) of false -> Tab = ets:new(alfa, [bag]), Fun = [{'fun',1, {clauses, [{clause,1, [Cond],[],[{atom,1,true}]}]}}], Alfa_fun = eval(Fun), Alfa1 = [{Cond, Tab, Alfa_fun} | Alfa], initialize_alfa(Cond, Tab, Kb), {Alfa1, {new, Tab}}; {true, Tab} -> {Alfa, {old, Tab}} end. remove_prod(EngineState0 = #seresye{join=Join}, Fun) -> case seresye_tree_list:keysearch({p_node, Fun}, Join) of false -> EngineState0; Node -> Parent_node = seresye_tree_list:get_parent(Node, Join), Join1 = seresye_tree_list:remove_node(Node, Join), Parent_node1 = seresye_tree_list:refresh(Parent_node, Join1), EngineState1 = remove_nodes(Parent_node1, EngineState0#seresye{join=Join1}), remove_prod(EngineState1, Fun) end. remove_nodes(Node, EngineState0 = #seresye{join=Join, alfa=Alfa}) -> case seresye_tree_list:have_child(Node) of false -> case seresye_tree_list:is_root(Node) of false -> Parent_node = seresye_tree_list:get_parent(Node, Join), Join1 = seresye_tree_list:remove_node(Node, Join), Parent_node1 = seresye_tree_list:refresh(Parent_node, Join1), {First, _} = seresye_tree_list:get_key(Node), case First of {n_node, IdNp_node} -> ParentKey = seresye_tree_list:get_key(Parent_node1), %% delete all nodes of the conditions negated Np_node = seresye_tree_list:get_node(IdNp_node, Join1), Join2 = seresye_tree_list:remove_child(Node, Np_node, Join1), Np_node1 = seresye_tree_list:refresh(Np_node, Join2), EngineState1 = remove_nodes(Np_node1, EngineState0#seresye{join=Join2}), %% Recovery of the parent node of the node passed as an argument n_node %% The parent can now have a different id, but has the same key Join3 = EngineState1#seresye.join, Parent_node2 = seresye_tree_list:keysearch(ParentKey, Join3), remove_nodes(Parent_node2, EngineState1); np_node -> remove_nodes(Parent_node1, EngineState0#seresye{join=Join1}); Tab -> Alfa1 = case seresye_tree_list:is_present(Tab, Join1) of false -> ets:delete(Tab), lists:keydelete(Tab, 2, Alfa); true -> Alfa end, remove_nodes(Parent_node1, EngineState0#seresye{alfa=Alfa1, join=Join1}) end; true -> EngineState0 end; true -> EngineState0 end. %% @doc occurs and if the condition Cond is present is_present(_Cond, []) -> false; is_present(Cond, [{C1, Tab, _Alfa_fun} | Other_cond]) -> case same_cond(Cond, C1) of true -> {true, Tab}; false -> is_present(Cond, Other_cond) end. same_cond(Cond, Cond) -> true; same_cond(_Cond1, _Cond2) -> false. eval(Expr) -> ?LOG("FUN = ~p~n", [String]), case catch erl_eval:exprs(Expr, erl_eval:new_bindings()) of {'EXIT', _} -> false; {value, Value, _Bindings} -> Value; _ -> false end. prepare_fun(_Cond, []) -> [{atom, 1, nil}]; prepare_fun(Cond0, Cond1) when not is_list(Cond0), is_list(Cond1) -> [{'fun',1, {clauses, [{clause,1, [Cond0, prepare_rest(Cond1)], [], [{atom,1,true}]}]}}]; prepare_fun(Cond0, Cond1) when is_list(Cond0), is_list(Cond1) -> [{'fun',1, {clauses, [{clause,1, [prepare_rest(Cond0), prepare_rest(Cond1)], [], [{atom,1,true}]}]}}]. prepare_rest([Element | Rest]) -> {cons, 1, Element, prepare_rest(Rest)}; prepare_rest([]) -> {nil, 1}. %% @doc join_node entering any new updates in the beta-token memory update_new_node(EngineState0, Node, Parent_node, Join) -> case seresye_tree_list:is_root(Parent_node) of false -> Children = seresye_tree_list:children(Parent_node, Join), case Children -- [Node] of [] -> case seresye_tree_list:get_key(Parent_node) of {{n_node, _IdNp_node}, _Join_fun} -> Beta = seresye_tree_list:get_beta(Parent_node), update_from_n_node(EngineState0, Parent_node, Beta, Join); {Tab, _} -> Fact_list = ets:tab2list(Tab), update_new_node(EngineState0, Node, Parent_node, Join, Fact_list) end; [Child | _Other_child] -> Beta = seresye_tree_list:get_beta(Child), Join1 = seresye_tree_list:update_beta(Beta, Node, Join), EngineState1 = case seresye_tree_list:get_key(Node) of {p_node, Rule} -> update_agenda(EngineState0, Beta, Rule); _Key -> EngineState0 end, {Join1, EngineState1} end; true -> {Join, EngineState0} end. update_agenda(EngineState, [], _Rule) -> EngineState; update_agenda(EngineState, [Tok | OtherTok], Rule) -> EngineState1 = signal(EngineState, Tok, plus, Rule), update_agenda(EngineState1, OtherTok, Rule). update_new_node(EngineState0, _Node, _Parent_node, Join, []) -> {Join, EngineState0}; update_new_node(EngineState0, Node, Parent_node, Join, [Fact | Other_fact]) -> Tok_list = right_act({Fact, plus}, Parent_node), {Join1, EngineState1} = pass_tok(EngineState0, Tok_list, [Node], Join), Node1 = seresye_tree_list:refresh(Node, Join1), update_new_node(EngineState1, Node1, Parent_node, Join1, Other_fact). update_from_n_node(EngineState0, Parent_node, [], Join) -> Join1 = seresye_tree_list:update_node(Parent_node, Join), {Join1, EngineState0}; update_from_n_node(EngineState0, Parent_node, [Tok | OtherTok], Join) -> {Join1, EngineState1} = left_act(EngineState0, {Tok, plus}, [Parent_node], Join), update_from_n_node(Parent_node, OtherTok, Join1, EngineState1). signal(EngineState0 = #seresye{pending_actions=PAList}, Token, Sign, {Fun, Salience}) -> NewPA = case Sign of plus -> fun(EngineState1) -> seresye_agenda:add_activation(EngineState1, Fun, Token, Salience) end; minus -> ActivationId = seresye_agenda:get_activation(EngineState0, {Fun, Token}), fun(EngineState1) -> seresye_agenda:delete_activation(EngineState1, ActivationId) end end, EngineState0#seresye{pending_actions=PAList ++ [NewPA]}. %%==================================================================== %% Fact Assertion/Retraction Functions %%==================================================================== check_cond(EngineState, [], {_Fact, _Sign}) -> EngineState; check_cond(EngineState0, [{_C1, Tab, Alfa_fun} | T], {Fact, Sign}) -> case catch Alfa_fun(Fact) of true -> case Sign of plus -> ets:insert(Tab, Fact); minus -> ets:delete_object(Tab, Fact) end, EngineState1 = pass_fact(EngineState0, Tab, {Fact, Sign}), check_cond(EngineState1, T, {Fact, Sign}); {'EXIT', {function_clause, _}} -> check_cond(EngineState0, T, {Fact, Sign}); _Other -> check_cond(EngineState0, T, {Fact, Sign}) end. %% @doc propagates the 'done' to all the nodes that follow the alpha-memory %% With an index tab pass_fact(EngineState0 = #seresye{join=Join}, Tab, {Fact, Sign}) -> Succ_node_list = seresye_tree_list:lookup_all(Tab, Join), {Join1, EngineState1} = propagate(EngineState0, Succ_node_list, {Fact, Sign}, Join), EngineState1#seresye{join=Join1}. propagate(EngineState0, [], {_Fact, _Sign}, Join) -> {Join, EngineState0}; propagate(EngineState0, [Join_node | T], {Fact, Sign}, Join) -> Join_node1 = seresye_tree_list:refresh(Join_node, Join), Tok_list = right_act({Fact, Sign}, Join_node1), {Join1, EngineState1} = case seresye_tree_list:get_key(Join_node1) of {{n_node, _}, _} -> propagate_nnode(EngineState0, Join_node1, Tok_list, Sign, Join); {_Tab, _Fun} -> Children_list = seresye_tree_list:children(Join_node1, Join), pass_tok(EngineState0, Tok_list, Children_list, Join) end, propagate(EngineState1, T, {Fact, Sign}, Join1). propagate_nnode(EngineState0, _Join_node, [], _, Join) -> {Join, EngineState0}; propagate_nnode(EngineState0, Join_node, Tok_list, Sign, Join) -> Children_list = seresye_tree_list:children(Join_node, Join), Toks = [make_toks(Tok, Sign) || Tok <- Tok_list], case Sign of plus -> pass_tok(EngineState0, Toks, Children_list, Join); minus -> {{n_node, IdNp_node}, Join_fun} = seresye_tree_list:get_key(Join_node), test_nnode(EngineState0, Toks, IdNp_node, Join_fun, Join_node, Join) end. make_toks(Tk, Sign) -> L = element(1, Tk), T1 = lists:sublist(L, length(L) - 1), case Sign of plus -> {T1, minus}; minus -> {T1, plus} end. %% @doc Token propagates all the nodes in Children_list pass_tok(EngineState0, [], _Children_list, Join) -> {Join, EngineState0}; pass_tok(EngineState0, [Tok | T], Children_list, Join) -> {Join1, EngineState1} = left_act(EngineState0, Tok, Children_list, Join), Children_list1 = refresh(Children_list, Join1), pass_tok(EngineState1, T, Children_list1, Join1). %% @doc Insert the token in Tok Beta_memory of Join_node, Token Tok %% compares the present with all the facts nell'alfa memory Associated %% with Join_node and if you have a successful propagates Token to the %% child nodes left_act(EngineState0, {_Token, _Sign}, [], Join) -> {Join, EngineState0}; left_act(EngineState0, {Token, Sign}, [Join_node | T], Join) -> Beta = seresye_tree_list:get_beta(Join_node), Beta1 = case Sign of plus -> Beta ++ [Token]; minus -> Beta -- [Token] end, Join1 = seresye_tree_list:update_beta(Beta1, Join_node, Join), case seresye_tree_list:get_key(Join_node) of {p_node, Rule} -> left_act(signal(EngineState0, Token, Sign, Rule), {Token, Sign}, T, Join1); {{n_node, IdNp_node}, Join_fun} -> left_act_nnode(EngineState0, {Token, Sign}, IdNp_node, Join_fun, [Join_node | T], Join1); {np_node, nil} -> Children_list = seresye_tree_list:children(Join_node, Join1), {Join2, EngineState1} = propagate(EngineState0, Children_list, {Token, Sign}, Join1), left_act(EngineState1, {Token, Sign}, T, Join2); {Tab, Join_fun} -> Alfa_mem = ets:tab2list(Tab), Tok_list = join_left({Token, Sign}, Alfa_mem, Join_fun), Children_list = seresye_tree_list:children(Join_node, Join1), {Join2, EngineState1} = pass_tok(EngineState0, Tok_list, Children_list, Join1), left_act(EngineState1, {Token, Sign}, T, Join2) end. join_left({Tok, Sign}, Mem, Join_fun) -> lists:foldl(fun (WmeOrTok, Tok_list) -> Tok1 = match(WmeOrTok, Tok, Join_fun), case Tok1 of [] -> Tok_list; _Other -> Tok_list ++ [{Tok1, Sign}] end end, [], Mem). left_act_nnode(EngineState0, {Token, Sign}, IdNp_node, Join_fun, [Join_node | T], Join) -> Np_node = seresye_tree_list:get_node(IdNp_node, Join), BetaN = seresye_tree_list:get_beta(Np_node), Tok_list = join_left({Token, Sign}, BetaN, Join_fun), case Tok_list of [] -> Children_list = seresye_tree_list:children(Join_node, Join), {Join1, EngineState1} = pass_tok(EngineState0, [{Token, Sign}], Children_list, Join), left_act(EngineState1, {Token, Sign}, T, Join1); Tok_list -> left_act(EngineState0, {Token, Sign}, T, Join) end. test_nnode(EngineState0, [], _, _, _, Join) -> {Join, EngineState0}; test_nnode(EngineState0, [Tok | OtherTok], IdNpNode, Join_fun, Join_node, Join) -> {Join1, EngineState1} = left_act_nnode(EngineState0, Tok, IdNpNode, Join_fun, [Join_node], Join), test_nnode(EngineState1, OtherTok, IdNpNode, Join_fun, Join_node, Join1). %% @doc WME compares with the new all tokens in the beta-memory Parent %% and returns a list of new tokens ([+ Tok1 WME, WME + Tok2,...]) Or %% an empty list if the WME not 'match' with tokens of Beta-memory right_act({Fact, Sign}, Join_node) -> Beta = element(2, Join_node), Join_fun = element(2, element(1, Join_node)), join_right({Fact, Sign}, Beta, Join_fun). join_right({Fact, Sign}, Beta, nil) -> append(Beta, {Fact, Sign}); join_right({_Fact, _Sign}, [], _Join_fun) -> []; join_right({Fact, Sign}, Beta, Join_fun) -> join_right({Fact, Sign}, Beta, Join_fun, []). join_right({_Fact, _Sign}, [], _Join_fun, PassedTok) -> PassedTok; join_right({Fact, Sign}, [Tok | T], Join_fun, PassedTok) -> Pass = match(Fact, Tok, Join_fun), case Pass of [] -> join_right({Fact, Sign}, T, Join_fun, PassedTok); _New_tok -> L = PassedTok ++ [{Pass, Sign}], join_right({Fact, Sign}, T, Join_fun, L) end. refresh([], _Join, L) -> lists:reverse(L); refresh([Node | OtherNode], Join, L) -> Node1 = seresye_tree_list:refresh(Node, Join), L1 = [Node1 | L], refresh(OtherNode, Join, L1). refresh(N, Join) -> refresh(N, Join, []). %% @doc WME is the match between the token and Tok and returns an %% empty list in case Negative or a new token (Tok + WME) if so match(Wme, Tok, Join_fun) -> case catch Join_fun(Wme, Tok) of true -> Tok ++ [Wme]; {'EXIT', {function_clause, _}} -> []; _Other -> [] end. append([], {Fact, Sign}) -> [{[Fact], Sign}]; append(Beta, {Fact, Sign}) -> lists:foldl(fun (Tok, New_Beta) -> Tok1 = Tok ++ [Fact], New_Beta ++ {Tok1, Sign} end, [], Beta). %% @doc shares or create a join-node make_join_node(EngineState0, J, {new, Tab}, Cond, P, Parent_node) -> Join_fun = eval(prepare_fun(Cond, P)), new_join(EngineState0, J, Tab, Join_fun, Parent_node); make_join_node(EngineState0, J, {old, Tab}, Cond, P, Parent_node) -> Result = seresye_tree_list:child(Tab, Parent_node, J), case Result of false -> Join_fun = eval(prepare_fun(Cond, P)), new_join(EngineState0, J, Tab, Join_fun, Parent_node); Node -> {Node, J, EngineState0} end. new_join(EngineState0, J, Tab, Join_fun, Parent_node) -> Key = {Tab, Join_fun}, Value = [], {Node, J1} = seresye_tree_list:insert(Key, Value, Parent_node, J), {J2, EngineState1} = update_new_node(EngineState0, Node, Parent_node, J1), Node1 = seresye_tree_list:refresh(Node, J2), {Node1, J2, EngineState1}. ================================================ FILE: src/seresye_speedtest.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% Copyright (c) 2011 Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module (seresye_speedtest). -export([remove_multiple/3, final_rule/2, run_sieve/0]). remove_multiple(Engine, {X}, {Y}) when ((X rem Y) == 0) and (X =/= Y)-> seresye_engine:retract (Engine, {X}). final_rule(Engine0, {is, started} = X) when not ["{is, finished}"];true -> Engine1 = seresye_engine:retract (Engine0, X), seresye_engine:assert (Engine1, {is, finished}). run_sieve() -> Start = now(), Engine1 = seresye_engine:assert (seresye_engine:new(), [{X} || X <- lists:seq (2, 200)]), Engine2 = seresye_engine:add_rule (Engine1, {?MODULE, remove_multiple}, 2), Engine3 = seresye_engine:add_rule (Engine2, {?MODULE, final_rule}, 1), Engine4 = seresye_engine:assert (Engine3, {is, started}), End = now(), io:format("Kb ~p~n", [seresye_engine:get_kb (Engine4)]), R = seresye_engine:get_rules_fired (Engine4), io:format ("Rules fired: ~p~n", [R]), D = timer:now_diff(End, Start), io:format ("Time = ~p sec, ~p rules/sec, rule execution time ~p msec~n", [D / 1000000.0, R / (D / 1000000.0), (D / 1000.0) / R]). ================================================ FILE: src/seresye_sup.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% All rights reserved. %%% Copyright (c) 2011, Afiniate, Inc. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module(seresye_sup). -behaviour(supervisor). %% API -export([start_link/0, start_engine/0, start_engine/1, start_engine/2]). %% Supervisor callbacks -export([init/1]). -define(SERVER, ?MODULE). %%%=================================================================== %%% API functions %%%=================================================================== start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). start_engine() -> supervisor:start_child(?SERVER, []). start_engine(Name) -> supervisor:start_child(?SERVER, [Name]). start_engine(Name, ClientState) -> supervisor:start_child(?SERVER, [Name, ClientState]). %%%=================================================================== %%% Supervisor callbacks %%%=================================================================== init([]) -> RestartStrategy = simple_one_for_one, MaxRestarts = 1000, MaxSecondsBetweenRestarts = 3600, SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, Restart = transient, Shutdown = 2000, Type = worker, AChild = {seresye, {seresye, start_link, []}, Restart, Shutdown, Type, [seresye]}, {ok, {SupFlags, [AChild]}}. ================================================ FILE: src/seresye_transform.erl ================================================ -module(seresye_transform). -export([parse_transform/2]). -record(state,{ rules = [], rule_functions = [], exports = [], options }). -record(context, {module, function, arity, file, options}). parse_transform(Forms, Options) -> #context{ file = File } = parse_trans:initial_context(Forms, Options), case erl_lint:module(Forms, File, [nowarn_unused_function,nowarn_unused_vars,nowarn_unused_record]) of {error, _Errors, _Warnings} -> Forms; _ -> {Forms1, State} = parse_trans:transform(fun do_transform/4, #state{ options = Options }, Forms, Options), Result0 = parse_trans:revert(Forms1), lists:foldl(fun ({Fun, Arity}, Acc) -> parse_trans:export_function(Fun, Arity, Acc) end, Result0, State#state.rule_functions) end. do_transform(attribute,{attribute, _, export, Exports} = Attr, _Context, #state{} = State) -> {Attr, true, State#state{ exports = State#state.exports ++ Exports }}; do_transform(attribute,{attribute, _, rule, Rule} = Attr, _Context, #state{ rules = Rs } = State) -> {Attr, false, State#state{ rules = [rule_name(Rule)|Rs] }}; do_transform(attribute,{attribute, _, rules, Rules0} = Attr, _Context, #state{ rules = Rs } = State) -> Rules = [ rule_name(R) || R <- Rules0 ], {Attr, false, State#state{ rules = Rules ++ Rs }}; do_transform(function, {function, _, Fun, Arity, _Cs} = Form, _Context, #state{ rules = Rules, rule_functions = RFuns } = State) -> case lists:member(Fun, Rules) of false -> {Form, true, State}; true -> {Form, true, State#state{ rule_functions = [{Fun, Arity}|RFuns] -- State#state.exports }} end; do_transform(_Type, Form, _Context, State) -> {Form, true, State}. rule_name(A) when is_atom(A) -> A; rule_name({A, _}) -> A. ================================================ FILE: src/seresye_tree_list.erl ================================================ %%% SERESYE, an Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% Copyright (c) 2011 Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module(seresye_tree_list). -export([child/3, children/2, get_beta/1, get_id/1, get_key/1, get_last_insert/1, get_node/2, get_parent/2, get_root/1, have_child/1, insert/4, is_present/2, is_root/1, keysearch/2, lookup_all/2, new/0, refresh/2, remove_child/3, remove_node/2, set_child/3, update_beta/3, update_key/3, update_node/2]). new() -> [{root, nil, [], 0, 1}]. insert(Key, Value, ParentNode, List) -> {ParentKey, ParentValue, Children, Parent, Pos} = ParentNode, Next = length(List) + 1, Node = {Key, Value, [], Pos, Next}, List1 = List ++ [Node], Children1 = Children ++ [Next], ParentNode1 = {ParentKey, ParentValue, Children1, Parent, Pos}, L1 = lists:keyreplace(Pos, 5, List1, ParentNode1), {Node, L1}. %% @doc Parent_node search for children of a node whose key (tuple) is equal to Key %% if Key is not 'look for the node a tuple whose first element' {Key, _} %% Returns the node itself or false if there is no tuple child(Key, ParentNode, List) -> Children = element(3, ParentNode), search(Key, List, Children). search(_Key, _List, []) -> false; search(Key, List, [NthElement | T]) -> Elem = lists:nth(NthElement, List), Res = search(Key, Elem), case Res of true -> Elem; false -> search(Key, List, T) end. search({p_node, Fun}, {{p_node, {Fun, _}}, _, _, _, _}) -> true; search({p_node, Fun, _}, {{p_node, {Fun, _}}, _, _, _, _}) -> true; search({Tab, Fun}, {{Tab, Fun}, _, _, _, _}) -> true; search(Tab, {{Tab, _}, _, _, _, _}) -> true; search(_K, _E) -> false. test(X, X) -> true; test(_X, _Y) -> false. get_root([]) -> nil; get_root([Root | _T]) -> case is_root(Root) of true -> Root; false -> nil end. is_root({root, _, _, _, _}) -> true; is_root(_Node) -> false. %% @doc Check if there is an alpha-memory join_node %% true if there is at least one, otherwise false is_present(_Tab, []) -> false; is_present(Tab, [Node | T]) -> case catch element(1, element(1, Node)) % The first element of the list of {'EXIT', {badarg, _}} -> % throws an exception is_present(Tab, T); TabElem -> case test(TabElem, Tab) of true -> true; false -> is_present(Tab, T) end end. %% @doc returns a list of all the elements associated with the List %% Key Key. If there is no element with the requested key %% The result is' an empty list = Tab Key %% Key =% {Tab} Join_fun %% Key =% {p_node, Fun} %% Key =% {p_node, Fun {, _}} lookup_all(Key, [_Root | L]) -> lists:foldl(fun (Elem, ResultList) -> case search(Key, Elem) of true -> [Elem | ResultList]; false -> ResultList end end, [], L). %% @doc Search for an item with key Key and returns the first found or false keysearch(_Key, []) -> false; keysearch(Key, [Node | OtherNode]) -> case search(Key, Node) of false -> keysearch(Key, OtherNode); true -> Node end. %% @doc returns all child nodes of Join_node %% Or an empty list if no successors Join_node children(Join_node, List) -> Pos_list = element(3, Join_node), lists:foldl(fun (Pos, Children) -> Elem = lists:nth(Pos, List), Children ++ [Elem] end, [], Pos_list). update_node(JoinNode, List) -> Pos = element(5, JoinNode), lists:keyreplace(Pos, 5, List, JoinNode). get_node(Id, List) -> lists:nth(Id, List). get_last_insert(List) -> N = length(List), lists:nth(N, List). get_id(Node) -> element(5, Node). refresh(Node, List) -> Pos = element(5, Node), lists:nth(Pos, List). get_beta(Node) -> element(2, Node). update_beta(BetaNew, {Key, _BetaOld, Children, Parent, Pos}, List) -> NewNode = {Key, BetaNew, Children, Parent, Pos}, lists:keyreplace(Pos, 5, List, NewNode). get_key(Node) -> element(1, Node). update_key(NewKey, {_Key, Beta, Children, Parent, Pos}, List) -> NewNode = {NewKey, Beta, Children, Parent, Pos}, lists:keyreplace(Pos, 5, List, NewNode). get_parent(Node, List) -> Parent = element(4, Node), lists:nth(Parent, List). have_child(Node) -> Children = element(3, Node), case Children of [] -> false; _Other -> true end. remove_child(Child, ParentNode, List) -> ChildId = element(5, Child), {Key, Value, Children, Parent, Pos} = ParentNode, NewNode = {Key, Value, Children -- [ChildId], Parent, Pos}, lists:keyreplace(Pos, 5, List, NewNode). set_child(Child, ParentNode, List) -> {Key, Value, Children, Parent, Pos} = ParentNode, ChildId = element(5, Child), NewNode = {Key, Value, Children ++ [ChildId], Parent, Pos}, lists:keyreplace(Pos, 5, List, NewNode). remove_node(Node, List) -> Pos = element(5, Node), ParentNode = get_parent(Node, List), List1 = remove_child(Node, ParentNode, List), Head = lists:sublist(List1, Pos - 1), Tail = lists:nthtail(Pos, List1), update_list(Head, Tail, Pos). update_list(List, [], _N) -> List; update_list(List, [Node | T], N) -> NewPos = length(List) + 1, {Key, Value, Children, Parent, Pos} = Node, {NewParent, List1} = case Parent > N of true -> {Parent - 1, List}; false -> NewParent0 = Parent, {Key1, Value1, Children1, Parent1, Pos1} = lists:nth(NewParent0, List), ParentNode = {Key1, Value1, (Children1 -- [Pos]) ++ [NewPos], Parent1, Pos1}, {NewParent0, lists:keyreplace(Parent, 5, List, ParentNode)} end, NewChildren = [update_list_1(V1, N) || V1 <- Children], NewNode = {Key, Value, NewChildren, NewParent, NewPos}, List2 = List1 ++ [NewNode], update_list(List2, T, N). update_list_1(X, N) -> case X > N of true -> X - 1; false -> X end. ================================================ FILE: test/seresyet_12.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2011, Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module(seresyet_12). -export([given/3, then/3, 'when'/3]). -include_lib("eunit/include/eunit.hrl"). given([initialized, with, data], Engine, _) -> {ok, seresye_engine:assert(Engine, [{male, bob}, {mail, joe}, {male, corrado}, {female, sara}, {parent, bob, joe}, {parent, sara, bob}, {parent, corrado, bob}])}; given([a, seresye, engine, that, is, initialized, with, state], _, _) -> Engine0 = seresye_engine:new([]), Engine1 = lists:foldl(fun (X, Engine1) -> seresye_engine:add_rule(Engine1, {seresyet_simple_relatives, X}) end, Engine0, [mother, father, grandfather, grandmother]), {ok, Engine1}. 'when'([seresye, propagation, is, complete], Engine, _) -> %% Propagation happens immediately, so nothing to do here. Its %% just a placeholder {ok, Engine}. then([the, per, engine, state, is, retrievable], Engine, _) -> {ok, {Engine, seresye_engine:get_client_state(Engine)}}; then([contains, the, data, populated, by, the, rules], State = {_, InternalState}, _) -> ?assertMatch(true, (lists:member({grandmother, sara, joe}, InternalState))), ?assertMatch(true, (lists:member({grandfather, corrado, joe}, InternalState))), ?assertMatch(true, (lists:member({mother, sara, bob}, InternalState))), ?assertMatch(true, (lists:member({mother, sara, bob}, InternalState))), ?assertMatch(true, (lists:member({father, corrado, bob}, InternalState))), ?assertMatch(true, (lists:member({father, bob, joe}, InternalState))), {ok, State}. ================================================ FILE: test/seresyet_13.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2011 Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php-module(eresyet_13). -module(seresyet_13). -export([given/3, 'when'/3, then/3]). -include_lib("eunit/include/eunit.hrl"). given([a, seresye, engine, that, is, initialized, with, data], _State, _) -> Engine0 = seresye_engine:new([]), {ok, seresye_engine:assert(Engine0, [{male, bob}, {mail, joe}, {male, corrado}, {female, sara}, {parent, bob, joe}, {parent, sara, bob}, {parent, corrado, bob}])}; given([rules,from,a,module,with,rules,defined,via,attributes], Engine, _) -> {ok, seresye_engine:add_rules(Engine, seresyet_simple_relatives)}. 'when'(['when',seresye,propagation,is,complete], Engine, _) -> %% Propagation happens immediately, so nothing to do here. Its %% just a placeholder {ok, Engine}. then([contains,the,data,populated,by,the,rules], State = {_, InternalState}, _) -> ?assertMatch(true, (lists:member({grandmother, sara, joe}, InternalState))), ?assertMatch(true, (lists:member({grandfather, corrado, joe}, InternalState))), ?assertMatch(true, (lists:member({mother, sara, bob}, InternalState))), ?assertMatch(true, (lists:member({mother, sara, bob}, InternalState))), ?assertMatch(true, (lists:member({father, corrado, bob}, InternalState))), ?assertMatch(true, (lists:member({father, bob, joe}, InternalState))), {ok, State}; then([the, engine, runs, normally], Engine, _) -> {ok, {Engine, seresye_engine:get_client_state(Engine)}}. ================================================ FILE: test/seresyet_cannibals.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php %%% ====================================================== %%% Cannibals and Missionaries Problem %%% %%% Another classic AI problem. The point is %%% to get three cannibals and three missionaries %%% across a stream with a boat that can only %%% hold two people. If the cannibals outnumber %%% the missionaries on either side of the stream, %%% then the cannibals will eat the missionaries. %%% %%% It is a bare translation of the same example %%% provided in CLIPS Version 6.0 %%% %%% ====================================================== -module (seresyet_cannibals). -export([shore_1_move/3, shore_2_move/3, cannibals_eat_missionaries/2, circular_path/3, recognize_solution/2]). -record (status, {'search-depth', 'parent', 'shore-1-missionaries', 'shore-1-cannibals', 'shore-2-missionaries', 'shore-2-cannibals', 'boat-location'}). -define (INITIAL_MISSIONARIES, 3). -define (INITIAL_CANNIBALS, 3). -include_lib("eunit/include/eunit.hrl"). -rules([shore_1_move, shore_2_move, {cannibals_eat_missionaries, 10}, {circular_path, 10}, {recognize_solution, 10}]). for (Min, Max) when Min > Max -> []; for (Min, Max) -> lists:seq (Min, Max). shore_1_move (Engine0, #status {'boat-location' = 'shore-1'} = Node, {'boat-can-host', Limit}) -> Num = Node#status.'search-depth', S1M = Node#status.'shore-1-missionaries', S1C = Node#status.'shore-1-cannibals', S2M = Node#status.'shore-2-missionaries', S2C = Node#status.'shore-2-cannibals', MaxMissionaries = lists:min ([S1M, Limit]), lists:foldl ( fun (Missionaries, Engine1) -> MinCannibals = lists:max ([0, 1 - Missionaries]), MaxCannibals = lists:min ([S1C, Limit - Missionaries]), %%io:format ("Shore 1 ~p,~p~n", [MinCannibals, MaxCannibals]), lists:foldl ( fun (Cannibals, Engine2) -> NewNode = #status { 'search-depth' = Num + 1, 'parent' = Node, 'shore-1-missionaries' = S1M - Missionaries, 'shore-1-cannibals' = S1C - Cannibals, 'shore-2-missionaries' = Missionaries + S2M, 'shore-2-cannibals' = Cannibals + S2C, 'boat-location' = 'shore-2'}, seresye_engine:assert(Engine2, NewNode) end, Engine1, for (MinCannibals, MaxCannibals)) end, Engine0, for (0, MaxMissionaries)). shore_2_move (Engine0, #status {'boat-location' = 'shore-2'} = Node, {'boat-can-host', Limit}) -> Num = Node#status.'search-depth', S1M = Node#status.'shore-1-missionaries', S1C = Node#status.'shore-1-cannibals', S2M = Node#status.'shore-2-missionaries', S2C = Node#status.'shore-2-cannibals', MaxMissionaries = lists:min ([S2M, Limit]), lists:foldl( fun (Missionaries, Engine1) -> MinCannibals = lists:max ([0, 1 - Missionaries]), MaxCannibals = lists:min ([S2C, Limit - Missionaries]), lists:foldl ( fun (Cannibals, Engine2) -> NewNode = #status { 'search-depth' = Num + 1, 'parent' = Node, 'shore-1-missionaries' = S1M + Missionaries, 'shore-1-cannibals' = S1C + Cannibals, 'shore-2-missionaries' = S2M - Missionaries, 'shore-2-cannibals' = S2C - Cannibals, 'boat-location' = 'shore-1'}, seresye_engine:assert (Engine2, NewNode) end, Engine1, for (MinCannibals, MaxCannibals)) end, Engine0, for (0, MaxMissionaries)). cannibals_eat_missionaries (Engine0, #status {'shore-1-missionaries' = S1M, 'shore-1-cannibals' = S1C, 'shore-2-missionaries' = S2M, 'shore-2-cannibals' = S2C} = Node) -> if ((S2C > S2M) and (S2M =/= 0)) or ((S1C > S1M) and (S1M =/= 0)) -> %%io:format ("Invalid ~p~n", [Node]), seresye_engine:retract (Engine0, Node); true -> Engine0 end. circular_path (Engine0, #status {'search-depth' = SD1, 'boat-location' = BL, 'shore-1-missionaries' = S1M, 'shore-1-cannibals' = S1C, 'shore-2-missionaries' = S2M, 'shore-2-cannibals' = S2C}, #status {'search-depth' = SD2, 'boat-location' = BL, 'shore-1-missionaries' = S1M, 'shore-1-cannibals' = S1C, 'shore-2-missionaries' = S2M, 'shore-2-cannibals' = S2C} = Node) -> if SD1 < SD2 -> seresye_engine:retract (Engine0, Node); true -> Engine0 end. recognize_solution (Engine0, #status {'shore-2-missionaries' = M, 'shore-2-cannibals' = C} = Node) -> if (M == ?INITIAL_MISSIONARIES) and (C == ?INITIAL_CANNIBALS) -> Engine1 = seresye_engine:retract (Engine0, Node), seresye_engine:set_client_state(Engine1, Node); true -> Engine0 end. rules_test() -> Engine0 = seresye_engine:new(), Engine2 = seresye_engine:add_rules(Engine0, ?MODULE), Engine3 = seresye_engine:assert (Engine2, #status {'search-depth' = 1, 'parent' = 'no-parent', 'shore-1-missionaries' = ?INITIAL_MISSIONARIES, 'shore-2-missionaries' = 0, 'shore-1-cannibals' = ?INITIAL_CANNIBALS, 'shore-2-cannibals' = 0, 'boat-location' = 'shore-1'}), Engine4 = seresye_engine:assert (Engine3, {'boat-can-host', 2}), P = seresye_engine:get_client_state(Engine4), verify_result(P). verify_result(Node = #status{'shore-2-missionaries'=S2M, 'shore-2-cannibals'=S2C}) -> ?assertMatch(?INITIAL_MISSIONARIES, S2M), ?assertMatch(?INITIAL_CANNIBALS, S2C), verify_result_(Node). verify_result_(#status{'parent' = 'no-parent'}) -> ok; verify_result_(#status{'parent' = Parent, 'shore-1-missionaries'=S1M, 'shore-1-cannibals'=S1C, 'shore-2-missionaries'=S2M, 'shore-2-cannibals'=S2C}) -> ?assertMatch(true, (S1M >= S1C orelse S1M == 0)), ?assertMatch(true, (S2M >= S2C orelse S2M == 0)), verify_result_(Parent). ================================================ FILE: test/seresyet_relatives.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% Copyright (c) 2011, Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module (seresyet_relatives). -export([father/3, grandfather/3, grandmother/3, mother/3, brother/4, sister/4]). -include_lib("eunit/include/eunit.hrl"). -rules([mother, father, brother, sister, grandfather, grandmother]). %% %% if (X is female) and (X is Y's parent) then (X is Y's mother) %% mother (Engine, {female, X}, {parent, X, Y}) -> seresye_engine:set_client_state(seresye_engine:assert(Engine, {mother, X, Y}), [{mother, X, Y} | seresye_engine:get_client_state(Engine)]). %% %% if (X is male) and (X is Y's parent) then (X is Y's father) %% father (Engine, {male, X}, {parent, X, Y}) -> seresye_engine:set_client_state(seresye_engine:assert(Engine, {father, X, Y}), [{father, X, Y} | seresye_engine:get_client_state(Engine)]). %% %% if (Y and Z have the same parent X) and (Z is female) %% then (Z is Y's sister) %% sister (Engine, {parent, X, Y}, {parent, X, Z}, {female, Z}) when Y =/= Z -> seresye_engine:set_client_state(seresye_engine:assert(Engine, {sister, Z, Y}), [{sister, Z, Y} | seresye_engine:get_client_state(Engine)]). %% %% if (Y and Z have the same parent X) and (Z is male) %% then (Z is Y's brother) %% brother (Engine, {parent, X, Y}, {parent, X, Z}, {male, Z}) when Y =/= Z -> seresye_engine:set_client_state(seresye_engine:assert(Engine, {brother, Z, Y}), [{brother, Z, Y} | seresye_engine:get_client_state(Engine)]). %% %% if (X is Y's father) and (Y is Z's parent) %% then (X is Z's grandfather) %% grandfather (Engine, {father, X, Y}, {parent, Y, Z}) -> seresye_engine:set_client_state(seresye_engine:assert(Engine, {grandfather, X, Z}), [{grandfather, X, Z} | seresye_engine:get_client_state(Engine)]). %% %% if (X is Y's mother) and (Y is Z's parent) %% then (X is Z's grandmother) %% grandmother (Engine, {mother, X, Y}, {parent, Y, Z}) -> seresye_engine:set_client_state(seresye_engine:assert(Engine, {grandmother, X, Z}), [{grandmother, X, Z} | seresye_engine:get_client_state(Engine)]). rules_test() -> Engine0 = seresye_engine:new([]), Engine2 = seresye_engine:add_rules(Engine0, ?MODULE), Engine3 = seresye_engine:assert (Engine2, [{male, bob}, {male, corrado}, {male, mark}, {male, caesar}, {female, alice}, {female, sara}, {female, jane}, {female, anna}, {parent, jane, bob}, {parent, corrado, bob}, {parent, jane, mark}, {parent, corrado, mark}, {parent, jane, alice}, {parent, corrado, alice}, {parent, bob, caesar}, {parent, bob, anna}, {parent, sara, casear}, {parent, sara, anna}]), InternalState = seresye_engine:get_client_state(Engine3), ?assertMatch(true, lists:member({mother,sara,anna}, InternalState)), ?assertMatch(true, lists:member({sister,anna,casear}, InternalState)), ?assertMatch(true, lists:member({mother,sara,casear}, InternalState)), ?assertMatch(true, lists:member({grandfather,corrado,anna}, InternalState)), ?assertMatch(true, lists:member({sister,anna,caesar}, InternalState)), ?assertMatch(true, lists:member({brother,caesar,anna}, InternalState)), ?assertMatch(true, lists:member({father,bob,anna}, InternalState)), ?assertMatch(true, lists:member({grandmother,jane,anna}, InternalState)), ?assertMatch(true, lists:member({grandfather,corrado,caesar}, InternalState)), ?assertMatch(true, lists:member({father,bob,caesar}, InternalState)), ?assertMatch(true, lists:member({grandmother,jane,caesar}, InternalState)), ?assertMatch(true, lists:member({sister,alice,mark}, InternalState)), ?assertMatch(true, lists:member({brother,bob,alice}, InternalState)), ?assertMatch(true, lists:member({brother,mark,alice}, InternalState)), ?assertMatch(true, lists:member({father,corrado,alice}, InternalState)), ?assertMatch(true, lists:member({sister,alice,bob}, InternalState)), ?assertMatch(true, lists:member({sister,alice,mark}, InternalState)), ?assertMatch(true, lists:member({brother,bob,alice}, InternalState)), ?assertMatch(true, lists:member({brother,mark,alice}, InternalState)), ?assertMatch(true, lists:member({mother,jane,alice}, InternalState)), ?assertMatch(true, lists:member({sister,alice,bob}, InternalState)), ?assertMatch(true, lists:member({brother,bob,mark}, InternalState)), ?assertMatch(true, lists:member({father,corrado,mark}, InternalState)), ?assertMatch(true, lists:member({brother,mark,bob}, InternalState)), ?assertMatch(true, lists:member({brother,bob,mark}, InternalState)), ?assertMatch(true, lists:member({mother,jane,mark}, InternalState)), ?assertMatch(true, lists:member({brother,mark,bob}, InternalState)), ?assertMatch(true, lists:member({father,corrado,bob}, InternalState)), ?assertMatch(true, lists:member({mother,jane,bob}, InternalState)), ?assertMatch(29, erlang:length(InternalState)). ================================================ FILE: test/seresyet_sample.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% Copyright (c) 2011 Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module (seresyet_sample). -export ([rule/4, rule1/3, rule2/4, rule3/3, start/0]). -record (sample_record, { a = nil, b}). -include_lib("eunit/include/eunit.hrl"). rule (Engine, {hello, world}, {ciao, X}, {X, 10}) -> seresye_engine:set_client_state(Engine, [rule | seresye_engine:get_client_state(Engine)]). rule1 (Engine0, {hello, world}, {ciao, _X} = F) -> Engine1 = seresye_engine:assert (Engine0, {test}), Engine2 = seresye_engine:retract (Engine1, F), seresye_engine:set_client_state(Engine2, [rule1a | seresye_engine:get_client_state(Engine2)]); rule1 (Engine, {hello, world}, {test}) -> seresye_engine:set_client_state(Engine, [rule1b | seresye_engine:get_client_state(Engine)]). rule2 (Engine, {hello, world}, #sample_record { a = Z }, {mondo, Z}) -> seresye_engine:set_client_state(Engine, [rule2 | seresye_engine:get_client_state(Engine)]). rule3 (Engine, {hello, [_H|T]}, {test, T}) -> seresye_engine:set_client_state(Engine, [rule3 | seresye_engine:get_client_state(Engine)]). start () -> Engine0 = seresye_engine:add_rule (seresye_engine:new([]), {?MODULE, rule2}), Engine1 = seresye_engine:assert (Engine0, [[{ciao, mondo}, {mondo, 20}], {hello, world}, {ok, world}, #sample_record { a = 10, b = 50}]), Engine2 = seresye_engine:add_rule (Engine1, {?MODULE, rule3}), Engine3 = seresye_engine:assert (Engine2, [{hello, [ciao, mondo]}, {test, ciao}, {test, [ciao]}, {test, [mondo]}, {hello, [ciao, mondo, world]}, {test, [mondo, world]}]), Engine4 = seresye_engine:add_rules (Engine3, [{?MODULE, rule}, {?MODULE, rule1}]), Engine5 = seresye_engine:retract (Engine4, {test}), State = seresye_engine:get_client_state(seresye_engine:assert (Engine5, {test})), ?assertMatch([rule1b,rule1b,rule1a,rule3,rule3], State). ================================================ FILE: test/seresyet_sieve.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% Copyright (c) Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module (seresyet_sieve). -export([remove_multiple/3, final_rule/2, run_sieve/0]). -include_lib("eunit/include/eunit.hrl"). -include_lib("seresye/include/seresye.hrl"). -rules([{remove_multiple, 2}, {final_rule, 1}]). remove_multiple (Engine, {X}, {Y}) when ((X rem Y) == 0) and (X =/= Y)-> seresye_engine:retract (Engine, {X}). final_rule (Engine0, {is, started} = X) when not {rule, [{is, finished}]} -> Engine1 = seresye_engine:retract (Engine0, X), seresye_engine:assert (Engine1, {is, finished}). run_sieve() -> Start = now(), Engine1 = seresye_engine:assert (seresye_engine:new(), [{X} || X <- lists:seq (2, 100)]), Engine2 = seresye_engine:add_rules(Engine1, ?MODULE), Engine3 = seresye_engine:assert (Engine2, {is, started}), End = now(), ?assertMatch([{is,finished}, {97}, {89}, {83}, {79}, {73}, {71}, {67}, {61}, {59}, {53}, {47}, {43}, {41}, {37}, {31}, {29}, {23}, {19}, {17}, {13}, {11}, {7}, {5}, {3}, {2}], seresye_engine:get_kb (Engine3)), R = seresye_engine:get_rules_fired (Engine3), io:format ("Rules fired: ~p~n", [R]), D = timer:now_diff(End, Start), io:format ("Time = ~p sec, ~p rules/sec, rule execution time ~p msec~n", [D / 1000000.0, R / (D / 1000000.0), (D / 1000.0) / R]). rules_test_() -> {timeout, 160, fun() -> run_sieve() end}. ================================================ FILE: test/seresyet_simple_relatives.erl ================================================ %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine %%% %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro %%% Copyright (c) 2011 Afiniate, Inc. %%% All rights reserved. %%% %%% You may use this file under the terms of the BSD License. See the %%% license distributed with this project or %%% http://www.opensource.org/licenses/bsd-license.php -module(seresyet_simple_relatives). -export([father/3, grandfather/3, grandmother/3, mother/3]). -include_lib("eunit/include/eunit.hrl"). -rule(mother). -rule(father). -rules([{grandfather, 10}, grandmother]). %% %% if (X is female) and (X is Y's parent) then (X is Y's mother) %% mother(Engine, {female, X}, {parent, X, Y}) -> seresye_engine:set_client_state(seresye_engine:assert(Engine, {mother, X, Y}), [{mother, X, Y} | seresye_engine:get_client_state(Engine)]). %% %% if (X is male) and (X is Y's parent) then (X is Y's father) %% father(Engine, {male, X}, {parent, X, Y}) -> seresye_engine:set_client_state(seresye_engine:assert(Engine, {father, X, Y}), [{father, X, Y} | seresye_engine:get_client_state(Engine)]). %% %% if (X is Y's father) and (Y is Z's parent) %% then (X is Z's grandfather) %% grandfather(Engine, {father, X, Y}, {parent, Y, Z}) -> seresye_engine:set_client_state(seresye_engine:assert(Engine, {grandfather, X, Z}), [{grandfather, X, Z} | seresye_engine:get_client_state(Engine)]). %% %% if (X is Y's mother) and (Y is Z's parent) %% then (X is Z's grandmother) %% grandmother(Engine, {mother, X, Y}, {parent, Y, Z}) -> seresye_engine:set_client_state(seresye_engine:assert(Engine, {grandmother, X, Z}), [{grandmother, X, Z} | seresye_engine:get_client_state(Engine)]). rules_test() -> Engine0 = seresye_engine:new([]), Engine2 = lists:foldl(fun (X, Engine1) -> seresye_engine:add_rule (Engine1, {?MODULE, X}) end, Engine0, [mother, father, grandfather, grandmother]), Engine3 = seresye_engine:assert(Engine2, [{male, bob}, {mail, joe}, {male, corrado}, {female, sara}, {parent, bob, joe}, {parent, sara, bob}, {parent, corrado, bob}]), InternalState = seresye_engine:get_client_state(Engine3), ?assertMatch(true, lists:member({grandmother, sara, joe}, InternalState)), ?assertMatch(true, lists:member({grandfather, corrado, joe}, InternalState)), ?assertMatch(true, lists:member({mother, sara, bob}, InternalState)), ?assertMatch(true, lists:member({father, corrado, bob}, InternalState)), ?assertMatch(true, lists:member({father, bob, joe}, InternalState)), ?assertMatch(5, erlang:length(InternalState)).