├── doc ├── CNAME ├── ERESYE_Paper.pdf ├── assets │ ├── images │ │ ├── github-logo.png │ │ ├── page-background.png │ │ └── github-ribbons │ │ │ ├── red.png │ │ │ ├── black.png │ │ │ ├── green.png │ │ │ ├── grey.png │ │ │ ├── orange.png │ │ │ └── white.png │ ├── javascript │ │ ├── lang-go.js │ │ ├── lang-lua.js │ │ ├── lang-ml.js │ │ ├── lang-sql.js │ │ ├── lang-tex.js │ │ ├── lang-vb.js │ │ ├── lang-scala.js │ │ ├── lang-vhdl.js │ │ ├── lang-wiki.js │ │ ├── lang-apollo.js │ │ ├── lang-proto.js │ │ ├── lang-yaml.js │ │ ├── lang-hs.js │ │ ├── lang-lisp.js │ │ ├── lang-css.js │ │ ├── lang-n.js │ │ ├── lang-clj.js │ │ ├── html5.js │ │ ├── prettify.js │ │ └── lang-xq.js │ └── css │ │ ├── skeleton │ │ ├── images │ │ │ ├── favicon.ico │ │ │ ├── apple-touch-icon.png │ │ │ ├── apple-touch-icon-72x72.png │ │ │ └── apple-touch-icon-114x114.png │ │ ├── robots.txt │ │ ├── javascripts │ │ │ └── tabs.js │ │ ├── 404.html │ │ ├── stylesheets │ │ │ ├── layout.css │ │ │ ├── skeleton.css │ │ │ └── base.css │ │ └── index.html │ │ ├── prettify.css │ │ ├── code.css │ │ └── style.css ├── Domain_Of_Relatives_Example.pdf ├── _layouts │ └── default.html └── index.md ├── .gitignore ├── src ├── internal.hrl ├── seresye.app.src ├── seresye_app.erl ├── seresye_speedtest.erl ├── seresye_sup.erl ├── seresye_transform.erl ├── seresye_autoneg.erl ├── seresye_tree_list.erl ├── seresye.erl └── seresye_agenda.erl ├── do-gh-pages ├── rebar.config ├── include └── seresye.hrl ├── sinan.config ├── features ├── seresyet_13.feature └── seresyet_12.feature ├── examples ├── seresye_prodcons.erl ├── seresye_phil.erl └── seresyee_auto.erl ├── LICENSE.md ├── test ├── seresyet_13.erl ├── seresyet_12.erl ├── seresyet_sieve.erl ├── seresyet_sample.erl ├── seresyet_simple_relatives.erl ├── seresyet_cannibals.erl └── seresyet_relatives.erl └── README.md /doc/CNAME: -------------------------------------------------------------------------------- 1 | seresye.org -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | erl_crash.dump 3 | doc/_site 4 | deps 5 | ebin 6 | -------------------------------------------------------------------------------- /doc/ERESYE_Paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/ERESYE_Paper.pdf -------------------------------------------------------------------------------- /doc/assets/images/github-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/images/github-logo.png -------------------------------------------------------------------------------- /doc/assets/javascript/lang-go.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/javascript/lang-go.js -------------------------------------------------------------------------------- /doc/assets/javascript/lang-lua.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/javascript/lang-lua.js -------------------------------------------------------------------------------- /doc/assets/javascript/lang-ml.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/javascript/lang-ml.js -------------------------------------------------------------------------------- /doc/assets/javascript/lang-sql.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/javascript/lang-sql.js -------------------------------------------------------------------------------- /doc/assets/javascript/lang-tex.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/javascript/lang-tex.js -------------------------------------------------------------------------------- /doc/assets/javascript/lang-vb.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/javascript/lang-vb.js -------------------------------------------------------------------------------- /doc/Domain_Of_Relatives_Example.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/Domain_Of_Relatives_Example.pdf -------------------------------------------------------------------------------- /doc/assets/javascript/lang-scala.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/javascript/lang-scala.js -------------------------------------------------------------------------------- /doc/assets/javascript/lang-vhdl.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/javascript/lang-vhdl.js -------------------------------------------------------------------------------- /doc/assets/javascript/lang-wiki.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/javascript/lang-wiki.js -------------------------------------------------------------------------------- /src/internal.hrl: -------------------------------------------------------------------------------- 1 | -record(seresye, {kb, alfa, join, agenda, pending_actions, client_state, fired_rule, hooks = []}). 2 | -------------------------------------------------------------------------------- /doc/assets/images/page-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/images/page-background.png -------------------------------------------------------------------------------- /doc/assets/javascript/lang-apollo.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/javascript/lang-apollo.js -------------------------------------------------------------------------------- /doc/assets/images/github-ribbons/red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/images/github-ribbons/red.png -------------------------------------------------------------------------------- /doc/assets/css/skeleton/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/css/skeleton/images/favicon.ico -------------------------------------------------------------------------------- /doc/assets/images/github-ribbons/black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/images/github-ribbons/black.png -------------------------------------------------------------------------------- /doc/assets/images/github-ribbons/green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/images/github-ribbons/green.png -------------------------------------------------------------------------------- /doc/assets/images/github-ribbons/grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/images/github-ribbons/grey.png -------------------------------------------------------------------------------- /doc/assets/images/github-ribbons/orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/images/github-ribbons/orange.png -------------------------------------------------------------------------------- /doc/assets/images/github-ribbons/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/images/github-ribbons/white.png -------------------------------------------------------------------------------- /doc/assets/css/skeleton/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/css/skeleton/images/apple-touch-icon.png -------------------------------------------------------------------------------- /doc/assets/css/skeleton/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 3 | 4 | User-agent: * 5 | 6 | -------------------------------------------------------------------------------- /doc/assets/css/skeleton/images/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/css/skeleton/images/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /doc/assets/css/skeleton/images/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afiniate/seresye/HEAD/doc/assets/css/skeleton/images/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /do-gh-pages: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PARENT_SHA=$(git show-ref -s refs/heads/gh-pages) 4 | DOC_SHA=$(git ls-tree -d HEAD doc | awk '{print $3}') 5 | NEW_COMMIT=$(echo "Auto-update docs." | git commit-tree $DOC_SHA -p $PARENT_SHA) 6 | git update-ref refs/heads/gh-pages $NEW_COMMIT 7 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {deps, 2 | [ 3 | {edown, ".*", {git, "https://github.com/uwiger/edown.git", {branch, "master"}}}, 4 | {erlware_commons, ".*", {git, "https://github.com/erlware/erlware_commons.git", {branch, "master"}}}, 5 | {parse_trans, ".*", {git, "https://github.com/esl/parse_trans.git", {branch, "master"}}} 6 | ]}. 7 | -------------------------------------------------------------------------------- /doc/assets/javascript/lang-proto.js: -------------------------------------------------------------------------------- 1 | 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"]); 2 | -------------------------------------------------------------------------------- /include/seresye.hrl: -------------------------------------------------------------------------------- 1 | %% Parse transforms for automatic creating negative specs for rules in 2 | %% a rules file. though the user can write them in a spec it is much 3 | %% nicer to write them in a when clause and let the transform do its 4 | %% work. 5 | -compile({parse_transform, seresye_autoneg}). 6 | -compile({parse_transform, seresye_transform}). 7 | -------------------------------------------------------------------------------- /sinan.config: -------------------------------------------------------------------------------- 1 | {project_name, seresye}. 2 | {project_vsn, "0.0.4"}. 3 | 4 | {compile_args, [debug_info, warnings_as_errors, 5 | {warn_format, 2}, warn_export_all, 6 | warn_export_vars, warn_shadow_vars, warn_obsolete_guard, 7 | warn_unused_import]}. 8 | 9 | {ignore_dirs, ["_", 10 | ".", 11 | "features"]}. 12 | -------------------------------------------------------------------------------- /doc/assets/javascript/lang-yaml.js: -------------------------------------------------------------------------------- 1 | var a=null; 2 | 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"]); 3 | -------------------------------------------------------------------------------- /src/seresye.app.src: -------------------------------------------------------------------------------- 1 | %% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*- 2 | 3 | {application, seresye, 4 | [{description, "SERESYE means Swarm oriented ERlang Expert SYstem Engine. It is a library " 5 | "to write expert systems and rule processing engines using the Erlang " 6 | "programming language"}, 7 | {vsn, "0.0.4"}, 8 | {modules, []}, 9 | {registered, []}, 10 | {applications, [kernel, stdlib, erlware_commons]}, 11 | {mod, {seresye_app, []}}]}. 12 | -------------------------------------------------------------------------------- /features/seresyet_13.feature: -------------------------------------------------------------------------------- 1 | Feature: Support defining rules via module attributes 2 | In order to seresye more usable 3 | As an Erlang Developer 4 | I want to be able define my rules via rule attributes in the module file itself 5 | 6 | Scenario: Rules are defined in the attribute itself 7 | Given a seresye engine that is initialized with data 8 | And rules from a module with rules defined via attributes 9 | When when seresye propagation is complete 10 | Then the engine runs normally 11 | And contains the data populated by the rules 12 | -------------------------------------------------------------------------------- /doc/assets/javascript/lang-hs.js: -------------------------------------------------------------------------------- 1 | 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]|$)/, 2 | null],["pln",/^(?:[A-Z][\w']*\.)*[A-Za-z][\w']*/],["pun",/^[^\d\t-\r "'A-Za-z]+/]]),["hs"]); 3 | -------------------------------------------------------------------------------- /features/seresyet_12.feature: -------------------------------------------------------------------------------- 1 | Feature: Support explicit state passing in an seresye system 2 | In order to make callbacks more easily accessible and less prone to requiring side effects 3 | As an Erlang Developer 4 | I want to be able to have my own per engine state 5 | and have that state be passed to my rules when they execute 6 | 7 | Scenario: Retrievable state gets passed to rules 8 | Given a seresye engine that is initialized with state 9 | And initialized with data 10 | When seresye propagation is complete 11 | Then the per engine state is retrievable 12 | And contains the data populated by the rules 13 | -------------------------------------------------------------------------------- /doc/assets/css/prettify.css: -------------------------------------------------------------------------------- 1 | .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} -------------------------------------------------------------------------------- /doc/assets/javascript/lang-lisp.js: -------------------------------------------------------------------------------- 1 | var a=null; 2 | 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], 3 | ["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"]); 4 | -------------------------------------------------------------------------------- /doc/assets/javascript/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["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"]); 3 | -------------------------------------------------------------------------------- /src/seresye_app.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% Copyright (c) 2011 Afiniate, Inc. 5 | %%% All rights reserved. 6 | %%% 7 | %%% You may use this file under the terms of the BSD License. See the 8 | %%% license distributed with this project or 9 | %%% http://www.opensource.org/licenses/bsd-license.php 10 | -module(seresye_app). 11 | 12 | -behaviour(application). 13 | 14 | %% Application callbacks 15 | -export([start/2, stop/1]). 16 | 17 | %%%=================================================================== 18 | %%% Application callbacks 19 | %%%=================================================================== 20 | 21 | start(_StartType, _StartArgs) -> 22 | case seresye_sup:start_link() of 23 | {ok, Pid} -> 24 | {ok, Pid}; 25 | Error -> 26 | Error 27 | end. 28 | 29 | stop(_State) -> 30 | ok. 31 | -------------------------------------------------------------------------------- /doc/assets/css/skeleton/javascripts/tabs.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V1.1 3 | * Copyright 2011, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 8/17/2011 8 | */ 9 | 10 | 11 | $(document).ready(function() { 12 | 13 | /* Tabs Activiation 14 | ================================================== */ 15 | 16 | var tabs = $('ul.tabs'); 17 | 18 | tabs.each(function(i) { 19 | 20 | //Get all tabs 21 | var tab = $(this).find('> li > a'); 22 | tab.click(function(e) { 23 | 24 | //Get Location of tab's content 25 | var contentLocation = $(this).attr('href'); 26 | 27 | //Let go if not a hashed one 28 | if(contentLocation.charAt(0)=="#") { 29 | 30 | e.preventDefault(); 31 | 32 | //Make Tab Active 33 | tab.removeClass('active'); 34 | $(this).addClass('active'); 35 | 36 | //Show Tab Content & add active class 37 | $(contentLocation).show().addClass('active').siblings().hide().removeClass('active'); 38 | 39 | } 40 | }); 41 | }); 42 | }); -------------------------------------------------------------------------------- /examples/seresye_prodcons.erl: -------------------------------------------------------------------------------- 1 | %%% ERESYE, an ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% All rights reserved. 5 | %%% 6 | %%% You may use this file under the terms of the BSD License. See the 7 | %%% license distributed with this project or 8 | %%% http://www.opensource.org/licenses/bsd-license.php 9 | -module(seresye_prodcons). 10 | 11 | -export([start/0, prod/1, cons/1, cons_1/1]). 12 | 13 | start() -> 14 | application:start(seresye), 15 | eresye:start(pc), 16 | spawn(prodcons, cons_1, [1]), 17 | spawn(prodcons, cons_1, [2]), 18 | spawn(prodcons, cons_1, [3]), 19 | spawn(prodcons, prod, [0]), 20 | ok. 21 | 22 | prod(20) -> ok; 23 | prod(Index) -> 24 | eresye:assert(pc, {item, Index}), prod(Index + 1). 25 | 26 | cons(20) -> ok; 27 | cons(Index) -> 28 | Fact = eresye:retract(pc, 29 | {item, fun (X) -> X == Index end}), 30 | io:format("Consumer ~p~n", [Fact]), 31 | cons(Index + 1). 32 | 33 | cons_1(N) -> 34 | Fact = eresye:wait_and_retract(pc, {item, '_'}), 35 | io:format("~w: Consumer ~p~n", [N, Fact]), 36 | timer:sleep(random:uniform(500)), 37 | cons_1(N). 38 | -------------------------------------------------------------------------------- /doc/assets/javascript/lang-n.js: -------------------------------------------------------------------------------- 1 | var a=null; 2 | 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]*?(?:\*\/|$)/, 3 | 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/, 4 | 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"]); 5 | -------------------------------------------------------------------------------- /doc/assets/javascript/lang-clj.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | var a=null; 17 | 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",/^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\b/,a], 18 | ["typ",/^:[\dA-Za-z-]+/]]),["clj"]); 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD License 2 | =========== 3 | 4 | Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | * Neither the name of Francesca Gangemi, Corrado Santoro may be used 16 | to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | 20 | THIS SOFTWARE IS PROVIDED BY Francesca Gangemi AND Corrado Santoro ``AS 21 | IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /src/seresye_speedtest.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% Copyright (c) 2011 Afiniate, Inc. 5 | %%% All rights reserved. 6 | %%% 7 | %%% You may use this file under the terms of the BSD License. See the 8 | %%% license distributed with this project or 9 | %%% http://www.opensource.org/licenses/bsd-license.php 10 | -module (seresye_speedtest). 11 | -export([remove_multiple/3, final_rule/2, run_sieve/0]). 12 | 13 | 14 | remove_multiple(Engine, {X}, {Y}) when ((X rem Y) == 0) and (X =/= Y)-> 15 | seresye_engine:retract (Engine, {X}). 16 | 17 | final_rule(Engine0, {is, started} = X) 18 | when not ["{is, finished}"];true -> 19 | Engine1 = seresye_engine:retract (Engine0, X), 20 | seresye_engine:assert (Engine1, {is, finished}). 21 | 22 | run_sieve() -> 23 | Start = now(), 24 | Engine1 = seresye_engine:assert (seresye_engine:new(), 25 | [{X} || X <- lists:seq (2, 200)]), 26 | Engine2 = seresye_engine:add_rule (Engine1, {?MODULE, remove_multiple}, 2), 27 | Engine3 = seresye_engine:add_rule (Engine2, {?MODULE, final_rule}, 1), 28 | Engine4 = seresye_engine:assert (Engine3, {is, started}), 29 | End = now(), 30 | io:format("Kb ~p~n", [seresye_engine:get_kb (Engine4)]), 31 | R = seresye_engine:get_rules_fired (Engine4), 32 | io:format ("Rules fired: ~p~n", [R]), 33 | D = timer:now_diff(End, Start), 34 | io:format ("Time = ~p sec, ~p rules/sec, rule execution time ~p msec~n", 35 | [D / 1000000.0, 36 | R / (D / 1000000.0), 37 | (D / 1000.0) / R]). 38 | -------------------------------------------------------------------------------- /src/seresye_sup.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% All rights reserved. 5 | %%% Copyright (c) 2011, Afiniate, Inc. 6 | %%% 7 | %%% You may use this file under the terms of the BSD License. See the 8 | %%% license distributed with this project or 9 | %%% http://www.opensource.org/licenses/bsd-license.php 10 | -module(seresye_sup). 11 | 12 | -behaviour(supervisor). 13 | 14 | %% API 15 | -export([start_link/0, start_engine/0, start_engine/1, start_engine/2]). 16 | 17 | %% Supervisor callbacks 18 | -export([init/1]). 19 | 20 | -define(SERVER, ?MODULE). 21 | 22 | %%%=================================================================== 23 | %%% API functions 24 | %%%=================================================================== 25 | 26 | start_link() -> 27 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 28 | 29 | start_engine() -> 30 | supervisor:start_child(?SERVER, []). 31 | 32 | start_engine(Name) -> 33 | supervisor:start_child(?SERVER, [Name]). 34 | 35 | start_engine(Name, ClientState) -> 36 | supervisor:start_child(?SERVER, [Name, ClientState]). 37 | 38 | %%%=================================================================== 39 | %%% Supervisor callbacks 40 | %%%=================================================================== 41 | init([]) -> 42 | RestartStrategy = simple_one_for_one, 43 | MaxRestarts = 1000, 44 | MaxSecondsBetweenRestarts = 3600, 45 | 46 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, 47 | 48 | Restart = transient, 49 | Shutdown = 2000, 50 | Type = worker, 51 | 52 | AChild = {seresye, {seresye, start_link, []}, 53 | Restart, Shutdown, Type, [seresye]}, 54 | 55 | {ok, {SupFlags, [AChild]}}. 56 | -------------------------------------------------------------------------------- /doc/assets/css/skeleton/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | Your Page Title Here :) 12 | 13 | 14 | 17 | 18 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 |
41 |

Sorry. Couldn't Find That Page!

42 |
43 | 44 | 46 | 47 | -------------------------------------------------------------------------------- /examples/seresye_phil.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, an ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% All rights reserved. 5 | %%% 6 | %%% You may use this file under the terms of the BSD License. See the 7 | %%% license distributed with this project or 8 | %%% http://www.opensource.org/licenses/bsd-license.php% 9 | -module(seresye_phil). 10 | 11 | -export([start/0, phil_spawn/1, philosopher/2, think/1, eat/1]). 12 | 13 | -define(N_PHIL, 5). 14 | 15 | start() -> 16 | application:start(seresye), 17 | seresye:start(restaurant), 18 | phil_spawn(0). 19 | 20 | phil_spawn(?N_PHIL) -> ok; 21 | phil_spawn(N) -> 22 | seresye:assert(restaurant, {fork, N}), 23 | spawn(phil, philosopher, [N, init]), 24 | if N < (?N_PHIL) - 1 -> 25 | seresye:assert(restaurant, {room_ticket, N}); 26 | true -> ok 27 | end, 28 | phil_spawn(N + 1). 29 | 30 | philosopher(N, init) -> new_seed(), philosopher(N, ok); 31 | philosopher(N, X) -> 32 | think(N), 33 | Ticket = seresye:wait_and_retract(restaurant, 34 | {room_ticket, '_'}), 35 | seresye:wait_and_retract(restaurant, {fork, N}), 36 | seresye:wait_and_retract(restaurant, 37 | {fork, (N + 1) rem (?N_PHIL)}), 38 | eat(N), 39 | seresye:assert(restaurant, {fork, N}), 40 | seresye:assert(restaurant, 41 | {fork, (N + 1) rem (?N_PHIL)}), 42 | seresye:assert(restaurant, Ticket), 43 | philosopher(N, X). 44 | 45 | think(N) -> 46 | io:format("~w: thinking ...~n", [N]), 47 | timer:sleep(random:uniform(10) * 1000). 48 | 49 | eat(N) -> 50 | io:format("~w: eating ...~n", [N]), 51 | timer:sleep(random:uniform(10) * 1000). 52 | 53 | new_seed() -> 54 | {_, _, X} = erlang:now(), 55 | {H, M, S} = time(), 56 | H1 = H * X rem 32767, 57 | M1 = M * X rem 32767, 58 | S1 = S * X rem 32767, 59 | put(random_seed, {H1, M1, S1}). 60 | -------------------------------------------------------------------------------- /doc/assets/css/skeleton/stylesheets/layout.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V1.1 3 | * Copyright 2011, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 8/17/2011 8 | */ 9 | 10 | /* Table of Content 11 | ================================================== 12 | #Site Styles 13 | #Page Styles 14 | #Media Queries 15 | #Font-Face */ 16 | 17 | /* #Site Styles 18 | ================================================== */ 19 | 20 | /* #Page Styles 21 | ================================================== */ 22 | 23 | /* #Media Queries 24 | ================================================== */ 25 | 26 | /* Smaller than standard 960 (devices and browsers) */ 27 | @media only screen and (max-width: 959px) {} 28 | 29 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 30 | @media only screen and (min-width: 768px) and (max-width: 959px) {} 31 | 32 | /* All Mobile Sizes (devices and browser) */ 33 | @media only screen and (max-width: 767px) {} 34 | 35 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 36 | @media only screen and (min-width: 480px) and (max-width: 767px) {} 37 | 38 | /* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ 39 | @media only screen and (max-width: 479px) {} 40 | 41 | 42 | /* #Font-Face 43 | ================================================== */ 44 | /* This is the proper syntax for an @font-face file 45 | Just create a "fonts" folder at the root, 46 | copy your FontName into code below and remove 47 | comment brackets */ 48 | 49 | /* @font-face { 50 | font-family: 'FontName'; 51 | src: url('../fonts/FontName.eot'); 52 | src: url('../fonts/FontName.eot?iefix') format('eot'), 53 | url('../fonts/FontName.woff') format('woff'), 54 | url('../fonts/FontName.ttf') format('truetype'), 55 | url('../fonts/FontName.svg#webfontZam02nTh') format('svg'); 56 | font-weight: normal; 57 | font-style: normal; } 58 | */ -------------------------------------------------------------------------------- /doc/assets/javascript/html5.js: -------------------------------------------------------------------------------- 1 | // html5shiv @rem remysharp.com/html5-enabling-script 2 | // iepp v1.6.2 @jon_neal iecss.com/print-protector 3 | // Dual licensed under the MIT or GPL Version 2 licenses 4 | /*@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 16 | Engine0 = seresye_engine:new([]), 17 | {ok, 18 | seresye_engine:assert(Engine0, 19 | [{male, bob}, {mail, joe}, {male, corrado}, 20 | {female, sara}, {parent, bob, joe}, {parent, sara, bob}, 21 | {parent, corrado, bob}])}; 22 | given([rules,from,a,module,with,rules,defined,via,attributes], Engine, _) -> 23 | {ok, seresye_engine:add_rules(Engine, seresyet_simple_relatives)}. 24 | 25 | 'when'(['when',seresye,propagation,is,complete], Engine, _) -> 26 | %% Propagation happens immediately, so nothing to do here. Its 27 | %% just a placeholder 28 | {ok, Engine}. 29 | 30 | then([contains,the,data,populated,by,the,rules], 31 | State = {_, InternalState}, _) -> 32 | ?assertMatch(true, 33 | (lists:member({grandmother, sara, joe}, 34 | InternalState))), 35 | ?assertMatch(true, 36 | (lists:member({grandfather, corrado, joe}, 37 | InternalState))), 38 | ?assertMatch(true, 39 | (lists:member({mother, sara, bob}, InternalState))), 40 | ?assertMatch(true, 41 | (lists:member({mother, sara, bob}, InternalState))), 42 | ?assertMatch(true, 43 | (lists:member({father, corrado, bob}, InternalState))), 44 | ?assertMatch(true, 45 | (lists:member({father, bob, joe}, InternalState))), 46 | {ok, State}; 47 | then([the, engine, runs, normally], Engine, _) -> 48 | {ok, {Engine, seresye_engine:get_client_state(Engine)}}. 49 | 50 | -------------------------------------------------------------------------------- /src/seresye_transform.erl: -------------------------------------------------------------------------------- 1 | -module(seresye_transform). 2 | -export([parse_transform/2]). 3 | 4 | -record(state,{ 5 | rules = [], 6 | rule_functions = [], 7 | exports = [], 8 | options 9 | }). 10 | 11 | -record(context, {module, 12 | function, 13 | arity, 14 | file, 15 | options}). 16 | 17 | 18 | parse_transform(Forms, Options) -> 19 | #context{ file = File } = parse_trans:initial_context(Forms, Options), 20 | case erl_lint:module(Forms, File, [nowarn_unused_function,nowarn_unused_vars,nowarn_unused_record]) of 21 | {error, _Errors, _Warnings} -> 22 | Forms; 23 | _ -> 24 | {Forms1, State} = parse_trans:transform(fun do_transform/4, 25 | #state{ options = Options }, 26 | Forms, Options), 27 | Result0 = parse_trans:revert(Forms1), 28 | lists:foldl(fun ({Fun, Arity}, Acc) -> 29 | parse_trans:export_function(Fun, Arity, Acc) 30 | end, Result0, State#state.rule_functions) 31 | end. 32 | 33 | do_transform(attribute,{attribute, _, export, Exports} = Attr, _Context, #state{} = State) -> 34 | {Attr, true, State#state{ exports = State#state.exports ++ Exports }}; 35 | 36 | do_transform(attribute,{attribute, _, rule, Rule} = Attr, _Context, #state{ rules = Rs } = State) -> 37 | {Attr, false, State#state{ rules = [rule_name(Rule)|Rs] }}; 38 | 39 | do_transform(attribute,{attribute, _, rules, Rules0} = Attr, _Context, #state{ rules = Rs } = State) -> 40 | Rules = [ rule_name(R) || R <- Rules0 ], 41 | {Attr, false, State#state{ rules = Rules ++ Rs }}; 42 | 43 | do_transform(function, {function, _, Fun, Arity, _Cs} = Form, _Context, #state{ rules = Rules, rule_functions = RFuns } = State) -> 44 | case lists:member(Fun, Rules) of 45 | false -> 46 | {Form, true, State}; 47 | true -> 48 | {Form, true, State#state{ rule_functions = [{Fun, Arity}|RFuns] -- State#state.exports }} 49 | end; 50 | 51 | 52 | do_transform(_Type, Form, _Context, State) -> 53 | {Form, true, State}. 54 | 55 | rule_name(A) when is_atom(A) -> 56 | A; 57 | rule_name({A, _}) -> 58 | A. 59 | -------------------------------------------------------------------------------- /test/seresyet_12.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2011, Afiniate, Inc. 4 | %%% All rights reserved. 5 | %%% 6 | %%% You may use this file under the terms of the BSD License. See the 7 | %%% license distributed with this project or 8 | %%% http://www.opensource.org/licenses/bsd-license.php 9 | -module(seresyet_12). 10 | 11 | -export([given/3, then/3, 'when'/3]). 12 | 13 | -include_lib("eunit/include/eunit.hrl"). 14 | 15 | given([initialized, with, data], Engine, _) -> 16 | {ok, 17 | seresye_engine:assert(Engine, 18 | [{male, bob}, {mail, joe}, {male, corrado}, 19 | {female, sara}, {parent, bob, joe}, {parent, sara, bob}, 20 | {parent, corrado, bob}])}; 21 | given([a, seresye, engine, that, is, initialized, with, 22 | state], 23 | _, _) -> 24 | Engine0 = seresye_engine:new([]), 25 | Engine1 = lists:foldl(fun (X, Engine1) -> 26 | seresye_engine:add_rule(Engine1, 27 | {seresyet_simple_relatives, X}) 28 | end, 29 | Engine0, [mother, father, grandfather, grandmother]), 30 | {ok, Engine1}. 31 | 32 | 'when'([seresye, propagation, is, complete], 33 | Engine, _) -> 34 | %% Propagation happens immediately, so nothing to do here. Its 35 | %% just a placeholder 36 | {ok, Engine}. 37 | 38 | then([the, per, engine, state, is, retrievable], 39 | Engine, _) -> 40 | {ok, {Engine, seresye_engine:get_client_state(Engine)}}; 41 | then([contains, the, data, populated, by, the, rules], 42 | State = {_, InternalState}, _) -> 43 | ?assertMatch(true, 44 | (lists:member({grandmother, sara, joe}, 45 | InternalState))), 46 | ?assertMatch(true, 47 | (lists:member({grandfather, corrado, joe}, 48 | InternalState))), 49 | ?assertMatch(true, 50 | (lists:member({mother, sara, bob}, InternalState))), 51 | ?assertMatch(true, 52 | (lists:member({mother, sara, bob}, InternalState))), 53 | ?assertMatch(true, 54 | (lists:member({father, corrado, bob}, InternalState))), 55 | ?assertMatch(true, 56 | (lists:member({father, bob, joe}, InternalState))), 57 | {ok, State}. 58 | -------------------------------------------------------------------------------- /test/seresyet_sieve.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% Copyright (c) Afiniate, Inc. 5 | %%% All rights reserved. 6 | %%% 7 | %%% You may use this file under the terms of the BSD License. See the 8 | %%% license distributed with this project or 9 | %%% http://www.opensource.org/licenses/bsd-license.php 10 | -module (seresyet_sieve). 11 | -export([remove_multiple/3, final_rule/2, run_sieve/0]). 12 | 13 | -include_lib("eunit/include/eunit.hrl"). 14 | -include_lib("seresye/include/seresye.hrl"). 15 | 16 | -rules([{remove_multiple, 2}, 17 | {final_rule, 1}]). 18 | 19 | remove_multiple (Engine, {X}, {Y}) when ((X rem Y) == 0) and (X =/= Y)-> 20 | seresye_engine:retract (Engine, {X}). 21 | 22 | final_rule (Engine0, {is, started} = X) 23 | when not {rule, [{is, finished}]} -> 24 | Engine1 = seresye_engine:retract (Engine0, X), 25 | seresye_engine:assert (Engine1, {is, finished}). 26 | 27 | run_sieve() -> 28 | Start = now(), 29 | Engine1 = seresye_engine:assert (seresye_engine:new(), 30 | [{X} || X <- lists:seq (2, 100)]), 31 | Engine2 = seresye_engine:add_rules(Engine1, ?MODULE), 32 | Engine3 = seresye_engine:assert (Engine2, {is, started}), 33 | End = now(), 34 | ?assertMatch([{is,finished}, 35 | {97}, 36 | {89}, 37 | {83}, 38 | {79}, 39 | {73}, 40 | {71}, 41 | {67}, 42 | {61}, 43 | {59}, 44 | {53}, 45 | {47}, 46 | {43}, 47 | {41}, 48 | {37}, 49 | {31}, 50 | {29}, 51 | {23}, 52 | {19}, 53 | {17}, 54 | {13}, 55 | {11}, 56 | {7}, 57 | {5}, 58 | {3}, 59 | {2}], seresye_engine:get_kb (Engine3)), 60 | R = seresye_engine:get_rules_fired (Engine3), 61 | io:format ("Rules fired: ~p~n", [R]), 62 | D = timer:now_diff(End, Start), 63 | io:format ("Time = ~p sec, ~p rules/sec, rule execution time ~p msec~n", 64 | [D / 1000000.0, 65 | R / (D / 1000000.0), 66 | (D / 1000.0) / R]). 67 | 68 | rules_test_() -> 69 | {timeout, 160, 70 | fun() -> 71 | run_sieve() 72 | end}. 73 | -------------------------------------------------------------------------------- /src/seresye_autoneg.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2011, Afiniate, Inc. 4 | %%% All rights reserved. 5 | %%% 6 | %%% You may use this file under the terms of the BSD License. See the 7 | %%% license distributed with this project or 8 | %%% http://www.opensource.org/licenses/bsd-license.php 9 | -module(seresye_autoneg). 10 | 11 | -export([parse_transform/2]). 12 | 13 | parse_transform(Forms, _Options) -> 14 | {Head, NewAttributes, Body} = 15 | lists:foldl(fun parse_forms/2, {[], [], []}, Forms), 16 | lists:reverse(Head) ++ NewAttributes ++ lists:reverse(Body). 17 | 18 | parse_forms({function,Line,FunName, Arity, Clauses0}, {Head, Attrs0, Body}) -> 19 | {_, Clauses2, Attrs2} = 20 | lists:foldl(fun(Clause0, {ClauseCount, Clauses1, Attrs1}) -> 21 | case parse_clause(Clause0) of 22 | {false, Clause1} -> 23 | {ClauseCount + 1, 24 | [Clause1 | Clauses1], 25 | Attrs1}; 26 | {NewDetail, Clause1} -> 27 | {ClauseCount + 1, 28 | [Clause1 | Clauses1], 29 | [{ClauseCount, NewDetail} | Attrs1]} 30 | end 31 | end, {0, [], []}, Clauses0), 32 | 33 | {Head, lists:map(fun({CC, Detail0}) -> 34 | {attribute, Line, rule_neg, {FunName, CC, Detail0}} 35 | end, Attrs2) ++ Attrs0, 36 | [{function, Line, FunName, Arity, lists:reverse(Clauses2)} | Body]}; 37 | parse_forms(F = {attribute,_,file,_}, {Head, Attrs0, Body}) -> 38 | {[F | Head], Attrs0, Body}; 39 | parse_forms(F = {attribute,_,module,_}, {Head, Attrs, Body}) -> 40 | {[F | Head], Attrs, Body}; 41 | parse_forms(El, {Head, Attrs, Body}) -> 42 | {Head, Attrs, [El |Body]}. 43 | 44 | parse_clause({clause,Line, Args, 45 | [[{op,_,'not', 46 | {tuple, _, 47 | [{atom,_,rule}, Neg]}} | AR] | OR], 48 | Body}) -> 49 | NewGuards = case AR of 50 | [] -> 51 | OR; 52 | _ -> 53 | [AR | OR] 54 | end, 55 | {rewrite_negs(Neg), {clause, Line, Args, NewGuards, Body}}; 56 | parse_clause(Clause) -> 57 | {false, Clause}. 58 | 59 | rewrite_negs({tuple, L, Elements}) -> 60 | {tuple, L, lists:map(fun rewrite_negs/1, Elements)}; 61 | rewrite_negs(C = {cons, _, _, _}) -> 62 | rewrite_cons(C); 63 | rewrite_negs({var, Line, '_'}) -> 64 | {atom, Line, '___IGNORE___'}; 65 | rewrite_negs(Else) -> 66 | Else. 67 | 68 | rewrite_cons({cons, Line, Element, nil}) -> 69 | {cons, Line, rewrite_negs(Element), nil}; 70 | rewrite_cons({cons, Line, E1, Rest}) -> 71 | {cons, Line, rewrite_negs(E1), rewrite_negs(Rest)}. 72 | -------------------------------------------------------------------------------- /test/seresyet_sample.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% Copyright (c) 2011 Afiniate, Inc. 5 | %%% All rights reserved. 6 | %%% 7 | %%% You may use this file under the terms of the BSD License. See the 8 | %%% license distributed with this project or 9 | %%% http://www.opensource.org/licenses/bsd-license.php 10 | -module (seresyet_sample). 11 | -export ([rule/4, rule1/3, rule2/4, rule3/3, start/0]). 12 | 13 | -record (sample_record, { a = nil, b}). 14 | 15 | -include_lib("eunit/include/eunit.hrl"). 16 | 17 | 18 | rule (Engine, {hello, world}, {ciao, X}, {X, 10}) -> 19 | seresye_engine:set_client_state(Engine, 20 | [rule | seresye_engine:get_client_state(Engine)]). 21 | 22 | rule1 (Engine0, {hello, world}, {ciao, _X} = F) -> 23 | Engine1 = seresye_engine:assert (Engine0, {test}), 24 | Engine2 = seresye_engine:retract (Engine1, F), 25 | seresye_engine:set_client_state(Engine2, 26 | [rule1a | seresye_engine:get_client_state(Engine2)]); 27 | rule1 (Engine, {hello, world}, {test}) -> 28 | seresye_engine:set_client_state(Engine, 29 | [rule1b | seresye_engine:get_client_state(Engine)]). 30 | 31 | 32 | rule2 (Engine, {hello, world}, #sample_record { a = Z }, {mondo, Z}) -> 33 | seresye_engine:set_client_state(Engine, 34 | [rule2 | seresye_engine:get_client_state(Engine)]). 35 | 36 | rule3 (Engine, {hello, [_H|T]}, {test, T}) -> 37 | seresye_engine:set_client_state(Engine, 38 | [rule3 | seresye_engine:get_client_state(Engine)]). 39 | 40 | start () -> 41 | 42 | Engine0 = seresye_engine:add_rule (seresye_engine:new([]), {?MODULE, rule2}), 43 | Engine1 = seresye_engine:assert (Engine0, [[{ciao, mondo}, {mondo, 20}], 44 | {hello, world}, 45 | {ok, world}, 46 | #sample_record { a = 10, b = 50}]), 47 | Engine2 = seresye_engine:add_rule (Engine1, {?MODULE, rule3}), 48 | Engine3 = seresye_engine:assert (Engine2, [{hello, [ciao, mondo]}, 49 | {test, ciao}, 50 | {test, [ciao]}, 51 | {test, [mondo]}, 52 | {hello, [ciao, mondo, world]}, 53 | {test, [mondo, world]}]), 54 | 55 | Engine4 = seresye_engine:add_rules (Engine3, [{?MODULE, rule}, 56 | {?MODULE, rule1}]), 57 | 58 | Engine5 = seresye_engine:retract (Engine4, {test}), 59 | State = seresye_engine:get_client_state(seresye_engine:assert (Engine5, {test})), 60 | ?assertMatch([rule1b,rule1b,rule1a,rule3,rule3], 61 | State). 62 | -------------------------------------------------------------------------------- /doc/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page.title }} 5 | 6 | 7 | 8 | x 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |

SERESYE - {{ page.title }}

20 |
21 | 22 | 29 |
30 | 31 | 41 | 42 |
43 | 44 | {{content}} 45 | 46 |
47 | 48 |
49 | 50 | 51 |
52 |
53 | 54 |
55 |
56 |
57 | 59 | 60 | Follow Afiniate on Twitter 62 | 63 |

64 | Afiniate has proven expertise in delivering innovative, 65 | mission-critical enterprise solutions, including 66 | large-scale distributed systems to Fortune 500 companies 67 | like Amazon.com and leading edge high-frequency trading 68 | systems to private brokerages like PEAK6 Investments. 69 | 70 | We are focused on applying that expertise in developing 71 | intelligence and analytic systems for the banking 72 | industry, and using that technology to help already 73 | successful financial institutions to fully leverage their 74 | valuable, but underutilized, data in ways that drive 75 | increased revenue, improved operational efficiencies, 76 | insight-based decision making, and enhanced customer 77 | satisfaction. 78 |

79 |
80 | 81 |
82 |

GitHub release template built with HTML5, CSS3 and JS 83 | by Fublo

84 |
85 |
86 |
87 | 88 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /test/seresyet_simple_relatives.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% Copyright (c) 2011 Afiniate, Inc. 5 | %%% All rights reserved. 6 | %%% 7 | %%% You may use this file under the terms of the BSD License. See the 8 | %%% license distributed with this project or 9 | %%% http://www.opensource.org/licenses/bsd-license.php 10 | -module(seresyet_simple_relatives). 11 | 12 | -export([father/3, grandfather/3, grandmother/3, 13 | mother/3]). 14 | 15 | -include_lib("eunit/include/eunit.hrl"). 16 | 17 | -rule(mother). 18 | -rule(father). 19 | -rules([{grandfather, 10}, 20 | grandmother]). 21 | %% 22 | %% if (X is female) and (X is Y's parent) then (X is Y's mother) 23 | %% 24 | mother(Engine, {female, X}, {parent, X, Y}) -> 25 | seresye_engine:set_client_state(seresye_engine:assert(Engine, 26 | {mother, X, Y}), 27 | [{mother, X, Y} | seresye_engine:get_client_state(Engine)]). 28 | 29 | %% 30 | %% if (X is male) and (X is Y's parent) then (X is Y's father) 31 | %% 32 | father(Engine, {male, X}, {parent, X, Y}) -> 33 | seresye_engine:set_client_state(seresye_engine:assert(Engine, 34 | {father, X, Y}), 35 | [{father, X, Y} | seresye_engine:get_client_state(Engine)]). 36 | 37 | %% 38 | %% if (X is Y's father) and (Y is Z's parent) 39 | %% then (X is Z's grandfather) 40 | %% 41 | grandfather(Engine, {father, X, Y}, {parent, Y, Z}) -> 42 | seresye_engine:set_client_state(seresye_engine:assert(Engine, 43 | {grandfather, X, Z}), 44 | [{grandfather, X, Z} 45 | | seresye_engine:get_client_state(Engine)]). 46 | 47 | %% 48 | %% if (X is Y's mother) and (Y is Z's parent) 49 | %% then (X is Z's grandmother) 50 | %% 51 | grandmother(Engine, {mother, X, Y}, {parent, Y, Z}) -> 52 | seresye_engine:set_client_state(seresye_engine:assert(Engine, 53 | {grandmother, X, Z}), 54 | [{grandmother, X, Z} 55 | | seresye_engine:get_client_state(Engine)]). 56 | 57 | rules_test() -> 58 | Engine0 = seresye_engine:new([]), 59 | Engine2 = 60 | lists:foldl(fun (X, Engine1) -> 61 | seresye_engine:add_rule (Engine1, {?MODULE, X}) 62 | end, 63 | Engine0, 64 | [mother, father, grandfather, grandmother]), 65 | 66 | Engine3 = seresye_engine:assert(Engine2, [{male, bob}, 67 | {mail, joe}, 68 | {male, corrado}, 69 | {female, sara}, 70 | {parent, bob, joe}, 71 | {parent, sara, bob}, 72 | {parent, corrado, bob}]), 73 | InternalState = seresye_engine:get_client_state(Engine3), 74 | 75 | 76 | ?assertMatch(true, 77 | lists:member({grandmother, sara, joe}, InternalState)), 78 | ?assertMatch(true, 79 | lists:member({grandfather, corrado, joe}, InternalState)), 80 | ?assertMatch(true, 81 | lists:member({mother, sara, bob}, InternalState)), 82 | ?assertMatch(true, 83 | lists:member({father, corrado, bob}, InternalState)), 84 | ?assertMatch(true, 85 | lists:member({father, bob, joe}, InternalState)), 86 | ?assertMatch(5, erlang:length(InternalState)). 87 | -------------------------------------------------------------------------------- /doc/assets/css/skeleton/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | Your Page Title Here :) 12 | 13 | 14 | 17 | 18 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 45 |
46 |
47 |

Skeleton

48 |
Version 1.1
49 |
50 |
51 | 55 | 64 | 68 | 69 |
70 | 71 | 72 | 73 | 75 | 76 | 77 | 78 | 80 | 81 | -------------------------------------------------------------------------------- /doc/assets/css/code.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #ffffff; } 3 | .highlight .c { color: #999988; font-style: italic } /* Comment */ 4 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ 5 | .highlight .k { font-weight: bold } /* Keyword */ 6 | .highlight .o { font-weight: bold } /* Operator */ 7 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ 9 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ 11 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #aa0000 } /* Generic.Error */ 14 | .highlight .gh { color: #999999 } /* Generic.Heading */ 15 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ 16 | .highlight .go { color: #888888 } /* Generic.Output */ 17 | .highlight .gp { color: #555555 } /* Generic.Prompt */ 18 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 19 | .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ 20 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */ 21 | .highlight .kc { font-weight: bold } /* Keyword.Constant */ 22 | .highlight .kd { font-weight: bold } /* Keyword.Declaration */ 23 | .highlight .kn { font-weight: bold } /* Keyword.Namespace */ 24 | .highlight .kp { font-weight: bold } /* Keyword.Pseudo */ 25 | .highlight .kr { font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ 27 | .highlight .m { color: #009999 } /* Literal.Number */ 28 | .highlight .s { color: #bb8844 } /* Literal.String */ 29 | .highlight .na { color: #008080 } /* Name.Attribute */ 30 | .highlight .nb { color: #999999 } /* Name.Builtin */ 31 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #008080 } /* Name.Constant */ 33 | .highlight .ni { color: #800080 } /* Name.Entity */ 34 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ 35 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ 36 | .highlight .nn { color: #555555 } /* Name.Namespace */ 37 | .highlight .nt { color: #000080 } /* Name.Tag */ 38 | .highlight .nv { color: #008080 } /* Name.Variable */ 39 | .highlight .ow { font-weight: bold } /* Operator.Word */ 40 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 41 | .highlight .mf { color: #009999 } /* Literal.Number.Float */ 42 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */ 43 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */ 44 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */ 45 | .highlight .sb { color: #bb8844 } /* Literal.String.Backtick */ 46 | .highlight .sc { color: #bb8844 } /* Literal.String.Char */ 47 | .highlight .sd { color: #bb8844 } /* Literal.String.Doc */ 48 | .highlight .s2 { color: #bb8844 } /* Literal.String.Double */ 49 | .highlight .se { color: #bb8844 } /* Literal.String.Escape */ 50 | .highlight .sh { color: #bb8844 } /* Literal.String.Heredoc */ 51 | .highlight .si { color: #bb8844 } /* Literal.String.Interpol */ 52 | .highlight .sx { color: #bb8844 } /* Literal.String.Other */ 53 | .highlight .sr { color: #808000 } /* Literal.String.Regex */ 54 | .highlight .s1 { color: #bb8844 } /* Literal.String.Single */ 55 | .highlight .ss { color: #bb8844 } /* Literal.String.Symbol */ 56 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ 57 | .highlight .vc { color: #008080 } /* Name.Variable.Class */ 58 | .highlight .vg { color: #008080 } /* Name.Variable.Global */ 59 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */ 60 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ 61 | -------------------------------------------------------------------------------- /doc/assets/css/style.css: -------------------------------------------------------------------------------- 1 | /** 2 | * A clean concise theme for your GitHub projects 3 | * 4 | * Licenced under GPL v3 5 | * http://www.gnu.org/licenses/gpl.html 6 | **/ 7 | 8 | /* Page */ 9 | body { 10 | background: #333333 url('../images/page-background.png') repeat top left; 11 | font-family: arial,sans-serif; 12 | } 13 | 14 | /* Header */ 15 | header { 16 | padding-top: 50px; 17 | padding-bottom: 28px; 18 | } 19 | 20 | /* Footer */ 21 | footer { 22 | background-color: #FFFFFF; 23 | margin-top: 50px; 24 | -webkit-box-shadow: -10px 0 15px #000000; 25 | -moz-box-shadow: -10px 0 15px #000000; 26 | box-shadow: -10px 0 15px #000000; 27 | } 28 | 29 | div.credits { 30 | padding-top: 9px; 31 | margin-top: 30px; 32 | margin-bottom: 25px; 33 | border-top: 1px solid #DDD2B2; 34 | text-align: center; 35 | } 36 | 37 | footer p table { 38 | color: #333333; 39 | } 40 | 41 | div.repo-author { 42 | padding-top: 30px; 43 | } 44 | 45 | /* Text */ 46 | p, strong, li { 47 | color: #CCCCCC; 48 | font-size: 14px; 49 | } 50 | 51 | strong { 52 | font-weight: bold; 53 | color: #EEEEEE; 54 | } 55 | 56 | a { 57 | color: #0075B6; 58 | text-decoration: none; 59 | } 60 | 61 | a:visited { 62 | color: #0075B6; 63 | } 64 | 65 | a:hover { 66 | text-decoration: underline; 67 | } 68 | 69 | h1, h2 { 70 | font-family: georgia,serif; 71 | } 72 | 73 | h1 { 74 | font-style: italic; 75 | color: #FFFFFF; 76 | font-size: 50px; 77 | margin: 0; 78 | } 79 | 80 | h2 { 81 | color: #CCCCCC; 82 | font-size: 25px; 83 | line-height: 23px; 84 | padding-top: 15px; 85 | } 86 | 87 | h3, h4, h5 { 88 | color: #FFFFFF; 89 | font-weight: bold; 90 | font-family: inherit; 91 | } 92 | 93 | 94 | /* Useful classes and styles */ 95 | a.github-ribbon { 96 | position: absolute; 97 | top: 0; 98 | left: 0; 99 | border: 0; 100 | } 101 | 102 | a.download-button { 103 | display: block; 104 | padding: 15px 20px 10px 20px; 105 | color: #FFFFFF; 106 | text-decoration: none; 107 | font-size: 28px; 108 | font-weight: bold; 109 | background: #33A700 url('../images/github-logo.png') no-repeat 92% 50%; 110 | border: 2px solid #339410; 111 | -webkit-box-shadow: 3px 3px 5px #000000; 112 | -moz-box-shadow: 3px 3px 5px #000000; 113 | box-shadow: 3px 3px 5px #000000; 114 | -webkit-border-radius: 5px; 115 | -moz-border-radius: 5px; 116 | border-radius: 5px; 117 | -webkit-transition: 350ms; 118 | -moz-transition: 350ms; 119 | -o-transition: 350ms; 120 | transition: 350ms; 121 | } 122 | 123 | a.download-button:hover { 124 | background-color: #267C00; 125 | background-position: 90% 50%; 126 | } 127 | 128 | a.download-button span { 129 | font-size: 14px; 130 | display: block; 131 | margin-top: 2px; 132 | } 133 | 134 | div.highlight { 135 | margin-top: 15px; 136 | min-height: 220px; 137 | border: 3px solid #FFFFFF; 138 | background-color: #CCCC99; 139 | display: block; 140 | padding: 20px; 141 | font-family: monospace; 142 | -webkit-box-shadow: 3px 3px 5px #000000; 143 | -moz-box-shadow: 3px 3px 5px #000000; 144 | box-shadow: 3px 3px 5px #000000; 145 | overflow-x: auto; 146 | } 147 | 148 | dl { 149 | margin-top: 15px; 150 | min-height: 220px; 151 | border: 3px solid #FFFFFF; 152 | background-color: #CCCC99; 153 | display: block; 154 | padding: 20px; 155 | font-family: monospace; 156 | -webkit-box-shadow: 3px 3px 5px #000000; 157 | -moz-box-shadow: 3px 3px 5px #000000; 158 | box-shadow: 3px 3px 5px #000000; 159 | overflow-x: auto; 160 | } 161 | dt { 162 | font-weight: bold 163 | } 164 | 165 | 166 | code { 167 | margin-top: 15px; 168 | min-height: 50px; 169 | border: 3px solid #FFFFFF; 170 | background-color: #CCCC99; 171 | display: block; 172 | padding: 20px; 173 | font-family: monospace; 174 | -webkit-box-shadow: 3px 3px 5px #000000; 175 | -moz-box-shadow: 3px 3px 5px #000000; 176 | box-shadow: 3px 3px 5px #000000; 177 | overflow-x: auto; 178 | } 179 | 180 | pre.prettyprint { 181 | border: 0; 182 | padding: 0; 183 | margin: 0; 184 | } 185 | 186 | img.repo-author-logo { 187 | float: left; 188 | margin-right: 15px; 189 | } 190 | 191 | .menu { 192 | width: 100%; 193 | border-bottom: 2px solid #77746C; 194 | border-top: 2px solid #77746C; 195 | margin-bottom: 28px; 196 | } 197 | 198 | .menu ul { 199 | margin: 0; 200 | padding: 0; 201 | float: left; 202 | } 203 | 204 | .menu ul li { 205 | display: inline; 206 | } 207 | 208 | .menu ul li a { 209 | float: left; 210 | text-decoration: none; 211 | color: white; 212 | padding: 10.5px 11px; 213 | } 214 | 215 | .menu ul li a:visited { 216 | color: white; 217 | } 218 | 219 | .menu ul li a:hover, .menu ul li .current { 220 | color: #fff; 221 | background-color:#0b75b2; 222 | } 223 | 224 | /* Media queries */ 225 | /* Hide the ribbon when we are on a phone, screen is too small */ 226 | @media only screen and (max-width: 479px) { 227 | a.github-ribbon { 228 | display: none; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/seresye_tree_list.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, an Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% Copyright (c) 2011 Afiniate, Inc. 5 | %%% All rights reserved. 6 | %%% 7 | %%% You may use this file under the terms of the BSD License. See the 8 | %%% license distributed with this project or 9 | %%% http://www.opensource.org/licenses/bsd-license.php 10 | -module(seresye_tree_list). 11 | 12 | -export([child/3, children/2, get_beta/1, get_id/1, 13 | get_key/1, get_last_insert/1, get_node/2, get_parent/2, 14 | get_root/1, have_child/1, insert/4, is_present/2, 15 | is_root/1, keysearch/2, lookup_all/2, new/0, refresh/2, 16 | remove_child/3, remove_node/2, set_child/3, 17 | update_beta/3, update_key/3, update_node/2]). 18 | 19 | new() -> [{root, nil, [], 0, 1}]. 20 | 21 | insert(Key, Value, ParentNode, List) -> 22 | {ParentKey, ParentValue, Children, Parent, Pos} = 23 | ParentNode, 24 | Next = length(List) + 1, 25 | Node = {Key, Value, [], Pos, Next}, 26 | List1 = List ++ [Node], 27 | Children1 = Children ++ [Next], 28 | ParentNode1 = {ParentKey, ParentValue, Children1, 29 | Parent, Pos}, 30 | L1 = lists:keyreplace(Pos, 5, List1, ParentNode1), 31 | {Node, L1}. 32 | 33 | %% @doc Parent_node search for children of a node whose key (tuple) is equal to Key 34 | %% if Key is not 'look for the node a tuple whose first element' {Key, _} 35 | %% Returns the node itself or false if there is no tuple 36 | child(Key, ParentNode, List) -> 37 | Children = element(3, ParentNode), 38 | search(Key, List, Children). 39 | 40 | search(_Key, _List, []) -> false; 41 | search(Key, List, [NthElement | T]) -> 42 | Elem = lists:nth(NthElement, List), 43 | Res = search(Key, Elem), 44 | case Res of 45 | true -> Elem; 46 | false -> search(Key, List, T) 47 | end. 48 | 49 | search({p_node, Fun}, 50 | {{p_node, {Fun, _}}, _, _, _, _}) -> 51 | true; 52 | search({p_node, Fun, _}, 53 | {{p_node, {Fun, _}}, _, _, _, _}) -> 54 | true; 55 | search({Tab, Fun}, {{Tab, Fun}, _, _, _, _}) -> true; 56 | search(Tab, {{Tab, _}, _, _, _, _}) -> true; 57 | search(_K, _E) -> false. 58 | 59 | test(X, X) -> true; 60 | test(_X, _Y) -> false. 61 | 62 | get_root([]) -> nil; 63 | get_root([Root | _T]) -> 64 | case is_root(Root) of 65 | true -> Root; 66 | false -> nil 67 | end. 68 | 69 | is_root({root, _, _, _, _}) -> true; 70 | is_root(_Node) -> false. 71 | 72 | %% @doc Check if there is an alpha-memory join_node 73 | %% true if there is at least one, otherwise false 74 | is_present(_Tab, []) -> false; 75 | is_present(Tab, [Node | T]) -> 76 | case catch element(1, 77 | element(1, Node)) % The first element of the list 78 | of 79 | {'EXIT', 80 | {badarg, _}} -> % throws an exception 81 | is_present(Tab, T); 82 | TabElem -> 83 | case test(TabElem, Tab) of 84 | true -> true; 85 | false -> is_present(Tab, T) 86 | end 87 | end. 88 | 89 | %% @doc returns a list of all the elements associated with the List 90 | %% Key Key. If there is no element with the requested key 91 | %% The result is' an empty list = Tab Key 92 | %% Key =% {Tab} Join_fun 93 | %% Key =% {p_node, Fun} 94 | %% Key =% {p_node, Fun {, _}} 95 | lookup_all(Key, [_Root | L]) -> 96 | lists:foldl(fun (Elem, ResultList) -> 97 | case search(Key, Elem) of 98 | true -> 99 | [Elem | ResultList]; 100 | false -> ResultList 101 | end 102 | end, 103 | [], L). 104 | 105 | %% @doc Search for an item with key Key and returns the first found or false 106 | keysearch(_Key, []) -> false; 107 | keysearch(Key, [Node | OtherNode]) -> 108 | case search(Key, Node) of 109 | false -> keysearch(Key, OtherNode); 110 | true -> Node 111 | end. 112 | 113 | %% @doc returns all child nodes of Join_node 114 | %% Or an empty list if no successors Join_node 115 | children(Join_node, List) -> 116 | Pos_list = element(3, Join_node), 117 | lists:foldl(fun (Pos, Children) -> 118 | Elem = lists:nth(Pos, List), Children ++ [Elem] 119 | end, 120 | [], Pos_list). 121 | 122 | update_node(JoinNode, List) -> 123 | Pos = element(5, JoinNode), 124 | lists:keyreplace(Pos, 5, List, JoinNode). 125 | 126 | get_node(Id, List) -> lists:nth(Id, List). 127 | 128 | get_last_insert(List) -> 129 | N = length(List), lists:nth(N, List). 130 | 131 | get_id(Node) -> element(5, Node). 132 | 133 | refresh(Node, List) -> 134 | Pos = element(5, Node), lists:nth(Pos, List). 135 | 136 | get_beta(Node) -> element(2, Node). 137 | 138 | update_beta(BetaNew, {Key, _BetaOld, Children, Parent, Pos}, List) -> 139 | NewNode = {Key, BetaNew, Children, Parent, Pos}, 140 | lists:keyreplace(Pos, 5, List, NewNode). 141 | 142 | get_key(Node) -> element(1, Node). 143 | 144 | update_key(NewKey, {_Key, Beta, Children, Parent, Pos}, List) -> 145 | NewNode = {NewKey, Beta, Children, Parent, Pos}, 146 | lists:keyreplace(Pos, 5, List, NewNode). 147 | 148 | get_parent(Node, List) -> 149 | Parent = element(4, Node), lists:nth(Parent, List). 150 | 151 | have_child(Node) -> 152 | Children = element(3, Node), 153 | case Children of 154 | [] -> false; 155 | _Other -> true 156 | end. 157 | 158 | remove_child(Child, ParentNode, List) -> 159 | ChildId = element(5, Child), 160 | {Key, Value, Children, Parent, Pos} = ParentNode, 161 | NewNode = {Key, Value, Children -- [ChildId], Parent, 162 | Pos}, 163 | lists:keyreplace(Pos, 5, List, NewNode). 164 | 165 | set_child(Child, ParentNode, List) -> 166 | {Key, Value, Children, Parent, Pos} = ParentNode, 167 | ChildId = element(5, Child), 168 | NewNode = {Key, Value, Children ++ [ChildId], Parent, 169 | Pos}, 170 | lists:keyreplace(Pos, 5, List, NewNode). 171 | 172 | remove_node(Node, List) -> 173 | Pos = element(5, Node), 174 | ParentNode = get_parent(Node, List), 175 | List1 = remove_child(Node, ParentNode, List), 176 | Head = lists:sublist(List1, Pos - 1), 177 | Tail = lists:nthtail(Pos, List1), 178 | update_list(Head, Tail, Pos). 179 | 180 | update_list(List, [], _N) -> List; 181 | update_list(List, [Node | T], N) -> 182 | NewPos = length(List) + 1, 183 | {Key, Value, Children, Parent, Pos} = Node, 184 | {NewParent, List1} = 185 | case Parent > N of 186 | true -> 187 | {Parent - 1, List}; 188 | false -> 189 | NewParent0 = Parent, 190 | {Key1, Value1, Children1, Parent1, Pos1} = 191 | lists:nth(NewParent0, List), 192 | ParentNode = {Key1, Value1, 193 | (Children1 -- [Pos]) ++ [NewPos], Parent1, Pos1}, 194 | {NewParent0, lists:keyreplace(Parent, 5, List, ParentNode)} 195 | end, 196 | NewChildren = [update_list_1(V1, N) || V1 <- Children], 197 | NewNode = {Key, Value, NewChildren, NewParent, 198 | NewPos}, 199 | List2 = List1 ++ [NewNode], 200 | update_list(List2, T, N). 201 | 202 | update_list_1(X, N) -> 203 | case X > N of 204 | true -> X - 1; 205 | false -> X 206 | end. 207 | -------------------------------------------------------------------------------- /src/seresye.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% All rights reserved. 5 | %%% 6 | %%% You may use this file under the terms of the BSD License. See the 7 | %%% license distributed with this project or 8 | %%% http://www.opensource.org/licenses/bsd-license.php 9 | -module(seresye). 10 | 11 | %%==================================================================== 12 | %% External exports 13 | %%==================================================================== 14 | 15 | -export([start/0, start/1, start/2, stop/1, get_engine/1, 16 | add_rules/2, add_rule/2, add_rule/3, assert/2, get_kb/1, 17 | get_rules_fired/1, get_client_state/1, 18 | set_hooks/2, get_fired_rule/1, 19 | set_client_state/2, query_kb/2, serialize/1, 20 | remove_rule/2, retract/2]). 21 | 22 | %% gen_server callbacks 23 | -export([start_link/0, start_link/1, start_link/2, init/1, handle_call/3, 24 | handle_cast/2, handle_info/2, terminate/2, code_change/3]). 25 | 26 | %%==================================================================== 27 | %% External functions 28 | %%==================================================================== 29 | start() -> 30 | seresye_sup:start_engine(). 31 | 32 | start(Name) -> 33 | seresye_sup:start_engine(Name). 34 | 35 | start(Name, ClientState) -> 36 | seresye_sup:start_engine(Name, ClientState). 37 | 38 | set_hooks(Name, Hooks) when is_list(Hooks) -> 39 | gen_server:cast(Name, {set_hooks, Hooks}). 40 | 41 | set_client_state(Name, NewState) -> 42 | gen_server:cast(Name, {set_client_state, NewState}). 43 | 44 | get_client_state(Name) -> 45 | gen_server:call(Name, get_client_state). 46 | 47 | stop(EngineName) -> 48 | (catch gen_server:call(EngineName, stop)), 49 | ok. 50 | 51 | get_engine(EngineName) -> 52 | gen_server:call(EngineName, get_engine). 53 | 54 | %% @doc Insert a fact in the KB. 55 | %% It also checks if the fact verifies any condition, 56 | %% if this is the case the fact is also inserted in the alpha-memory 57 | assert(Name, Facts) -> 58 | gen_server:call(Name, {assert, Facts}, infinity). 59 | 60 | %% @doc removes a 'fact' in the Knowledge Base and if something occurs 61 | %% Condition is also deleted from the corresponding alpha-memory 62 | retract(Name, Facts) -> 63 | gen_server:call(Name, {retract, Facts}, infinity). 64 | 65 | add_rules(Name, RuleList) 66 | when is_list(RuleList) orelse is_atom(RuleList) -> 67 | gen_server:call(Name, {add_rules, RuleList}). 68 | 69 | add_rule(Name, Fun) -> 70 | gen_server:call(Name, {add_rule, Fun}). 71 | 72 | add_rule(Name, Rule, Salience) -> 73 | gen_server:call(Name, {add_rule, Rule, Salience}). 74 | 75 | remove_rule(Name, Rule) -> 76 | gen_server:call(Name, {remove_rule, Rule}). 77 | 78 | get_rules_fired(Name) -> 79 | gen_server:call(Name, get_rules_fired). 80 | 81 | get_fired_rule(Name) -> 82 | gen_server:call(Name, get_fired_rule). 83 | 84 | get_kb(Name) -> 85 | gen_server:call(Name, get_kb). 86 | 87 | query_kb(Name, Pattern) -> 88 | gen_server:call(Name, {query_kb, Pattern}). 89 | 90 | serialize(Name) -> 91 | gen_server:call(Name, serialize). 92 | 93 | %%%=================================================================== 94 | %%% gen_server callbacks 95 | %%%=================================================================== 96 | start_link() -> 97 | gen_server:start_link(?MODULE, [], []). 98 | 99 | start_link(Name) when is_atom(Name) -> 100 | gen_server:start_link({local, Name}, ?MODULE, [], []); 101 | 102 | start_link(ClientState) when not is_atom(ClientState) -> 103 | gen_server:start_link(?MODULE, [ClientState], []). 104 | 105 | start_link(ClientState, Name) when is_atom(Name) -> 106 | gen_server:start_link({local, Name}, ?MODULE, [ClientState], []). 107 | 108 | 109 | init([]) -> 110 | {ok, seresye_engine:new()}; 111 | init([Engine]) when element(1, Engine) == seresye -> 112 | {ok, seresye_engine:restore(Engine)}; 113 | init([ClientState]) -> 114 | {ok, seresye_engine:new(ClientState)}. 115 | 116 | 117 | handle_call(get_client_state, _From, State) -> 118 | Reply = 119 | try 120 | {ok, seresye_engine:get_client_state(State)} 121 | catch 122 | Type:Reason -> 123 | {error, {Type, Reason}} 124 | end, 125 | {reply, Reply, State}; 126 | handle_call(stop, _From, State) -> 127 | {stop, normal, State}; 128 | handle_call({assert, Facts}, _From, State0) -> 129 | {Reply, State1} = 130 | try 131 | {ok, seresye_engine:assert(State0, Facts)} 132 | catch 133 | Type:Reason -> 134 | {{error, {Type, Reason}}, State0} 135 | end, 136 | {reply, Reply, State1}; 137 | handle_call({retract, Facts}, _From, State0) -> 138 | {Reply, State1} = 139 | try 140 | {ok, seresye_engine:retract(State0, Facts)} 141 | catch 142 | Type:Reason -> 143 | {{error, {Type, Reason}}, State0} 144 | end, 145 | {reply, Reply, State1}; 146 | handle_call({add_rules, Rules}, _From, State0) -> 147 | {Reply, State1} = 148 | try 149 | {ok, seresye_engine:add_rules(State0, Rules)} 150 | catch 151 | Type:Reason -> 152 | {{error, {Type, Reason}}, State0} 153 | end, 154 | {reply, Reply, State1}; 155 | handle_call({add_rule, Rule}, _From, State0) -> 156 | {Reply, State1} = 157 | try 158 | {ok, seresye_engine:add_rule(State0, Rule)} 159 | catch 160 | Type:Reason -> 161 | {{error, {Type, Reason}}, State0} 162 | end, 163 | {reply, Reply, State1}; 164 | handle_call({add_rule, Rule, Salience}, _From, State0) -> 165 | {Reply, State1} = 166 | try 167 | {ok, seresye_engine:add_rule(State0, Rule, Salience)} 168 | catch 169 | Type:Reason -> 170 | {{error, {Type, Reason}}, State0} 171 | end, 172 | {reply, Reply, State1}; 173 | handle_call({remove_rule, Rule}, _From, State0) -> 174 | {Reply, State1} = 175 | try 176 | {ok, seresye_engine:remove_rule(State0, Rule)} 177 | catch 178 | Type:Reason -> 179 | {{error, {Type, Reason}}, State0} 180 | end, 181 | {reply, Reply, State1}; 182 | handle_call(get_rules_fired, _From, State0) -> 183 | Reply = 184 | try 185 | seresye_engine:get_rules_fired(State0) 186 | catch 187 | Type:Reason -> 188 | {error, {Type, Reason}} 189 | end, 190 | {reply, Reply, State0}; 191 | handle_call(get_fired_rule, _From, State0) -> 192 | Reply = 193 | try 194 | seresye_engine:get_fired_rule(State0) 195 | catch 196 | Type:Reason -> 197 | {error, {Type, Reason}} 198 | end, 199 | {reply, Reply, State0}; 200 | handle_call(get_engine, _From, State0) -> 201 | {reply, State0, State0}; 202 | handle_call(get_kb, _From, State0) -> 203 | Reply = 204 | try 205 | seresye_engine:get_kb(State0) 206 | catch 207 | Type:Reason -> 208 | {error, {Type, Reason}} 209 | end, 210 | {reply, Reply, State0}; 211 | handle_call({query_kb, Pattern}, _From, State0) -> 212 | Reply = 213 | try 214 | seresye_engine:query_kb(State0, Pattern) 215 | catch 216 | Type:Reason -> 217 | {error, {Type, Reason}} 218 | end, 219 | {reply, Reply, State0}; 220 | 221 | handle_call(serialize, _From, State) -> 222 | Reply = seresye_engine:serialize(State), 223 | {reply, Reply, State}. 224 | 225 | handle_cast({set_hooks, Hooks}, State) -> 226 | {noreply, seresye_engine:set_hooks(State, Hooks)}; 227 | 228 | handle_cast({set_client_state, CS}, State) -> 229 | {noreply, seresye_engine:set_client_state(State, CS)}. 230 | 231 | handle_info(_Info, State) -> 232 | {noreply, State}. 233 | 234 | terminate(_Reason, _State) -> 235 | ok. 236 | 237 | code_change(_OldVsn, State, _Extra) -> 238 | {ok, State}. 239 | -------------------------------------------------------------------------------- /test/seresyet_cannibals.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% All rights reserved. 5 | %%% 6 | %%% You may use this file under the terms of the BSD License. See the 7 | %%% license distributed with this project or 8 | %%% http://www.opensource.org/licenses/bsd-license.php 9 | %%% ====================================================== 10 | %%% Cannibals and Missionaries Problem 11 | %%% 12 | %%% Another classic AI problem. The point is 13 | %%% to get three cannibals and three missionaries 14 | %%% across a stream with a boat that can only 15 | %%% hold two people. If the cannibals outnumber 16 | %%% the missionaries on either side of the stream, 17 | %%% then the cannibals will eat the missionaries. 18 | %%% 19 | %%% It is a bare translation of the same example 20 | %%% provided in CLIPS Version 6.0 21 | %%% 22 | %%% ====================================================== 23 | -module (seresyet_cannibals). 24 | 25 | -export([shore_1_move/3, 26 | shore_2_move/3, 27 | cannibals_eat_missionaries/2, 28 | circular_path/3, 29 | recognize_solution/2]). 30 | 31 | -record (status, {'search-depth', 32 | 'parent', 33 | 'shore-1-missionaries', 34 | 'shore-1-cannibals', 35 | 'shore-2-missionaries', 36 | 'shore-2-cannibals', 37 | 'boat-location'}). 38 | 39 | 40 | -define (INITIAL_MISSIONARIES, 3). 41 | -define (INITIAL_CANNIBALS, 3). 42 | 43 | -include_lib("eunit/include/eunit.hrl"). 44 | 45 | -rules([shore_1_move, 46 | shore_2_move, 47 | {cannibals_eat_missionaries, 10}, 48 | {circular_path, 10}, 49 | {recognize_solution, 10}]). 50 | 51 | for (Min, Max) when Min > Max -> []; 52 | for (Min, Max) -> lists:seq (Min, Max). 53 | 54 | shore_1_move (Engine0, 55 | #status {'boat-location' = 'shore-1'} = Node, 56 | {'boat-can-host', Limit}) -> 57 | Num = Node#status.'search-depth', 58 | S1M = Node#status.'shore-1-missionaries', 59 | S1C = Node#status.'shore-1-cannibals', 60 | S2M = Node#status.'shore-2-missionaries', 61 | S2C = Node#status.'shore-2-cannibals', 62 | 63 | MaxMissionaries = lists:min ([S1M, Limit]), 64 | 65 | lists:foldl ( 66 | fun (Missionaries, Engine1) -> 67 | MinCannibals = lists:max ([0, 1 - Missionaries]), 68 | MaxCannibals = lists:min ([S1C, Limit - Missionaries]), 69 | 70 | %%io:format ("Shore 1 ~p,~p~n", [MinCannibals, MaxCannibals]), 71 | 72 | lists:foldl ( 73 | fun (Cannibals, Engine2) -> 74 | NewNode = 75 | #status { 'search-depth' = Num + 1, 76 | 'parent' = Node, 77 | 'shore-1-missionaries' = S1M - Missionaries, 78 | 'shore-1-cannibals' = S1C - Cannibals, 79 | 'shore-2-missionaries' = Missionaries + S2M, 80 | 'shore-2-cannibals' = Cannibals + S2C, 81 | 'boat-location' = 'shore-2'}, 82 | 83 | seresye_engine:assert(Engine2, NewNode) 84 | end, 85 | Engine1, 86 | for (MinCannibals, MaxCannibals)) 87 | end, 88 | Engine0, 89 | for (0, MaxMissionaries)). 90 | 91 | 92 | shore_2_move (Engine0, 93 | #status {'boat-location' = 'shore-2'} = Node, 94 | {'boat-can-host', Limit}) -> 95 | Num = Node#status.'search-depth', 96 | S1M = Node#status.'shore-1-missionaries', 97 | S1C = Node#status.'shore-1-cannibals', 98 | S2M = Node#status.'shore-2-missionaries', 99 | S2C = Node#status.'shore-2-cannibals', 100 | 101 | MaxMissionaries = lists:min ([S2M, Limit]), 102 | 103 | lists:foldl( 104 | fun (Missionaries, Engine1) -> 105 | MinCannibals = lists:max ([0, 1 - Missionaries]), 106 | MaxCannibals = lists:min ([S2C, Limit - Missionaries]), 107 | 108 | lists:foldl ( 109 | fun (Cannibals, Engine2) -> 110 | NewNode = 111 | #status { 'search-depth' = Num + 1, 112 | 'parent' = Node, 113 | 'shore-1-missionaries' = S1M + Missionaries, 114 | 'shore-1-cannibals' = S1C + Cannibals, 115 | 'shore-2-missionaries' = S2M - Missionaries, 116 | 'shore-2-cannibals' = S2C - Cannibals, 117 | 'boat-location' = 'shore-1'}, 118 | seresye_engine:assert (Engine2, NewNode) 119 | end, 120 | Engine1, 121 | for (MinCannibals, MaxCannibals)) 122 | end, 123 | Engine0, 124 | for (0, MaxMissionaries)). 125 | 126 | 127 | 128 | cannibals_eat_missionaries (Engine0, 129 | #status {'shore-1-missionaries' = S1M, 130 | 'shore-1-cannibals' = S1C, 131 | 'shore-2-missionaries' = S2M, 132 | 'shore-2-cannibals' = S2C} = Node) -> 133 | if 134 | ((S2C > S2M) and (S2M =/= 0)) or ((S1C > S1M) and (S1M =/= 0)) -> 135 | %%io:format ("Invalid ~p~n", [Node]), 136 | seresye_engine:retract (Engine0, Node); 137 | true -> Engine0 138 | end. 139 | 140 | 141 | circular_path (Engine0, 142 | #status {'search-depth' = SD1, 143 | 'boat-location' = BL, 144 | 'shore-1-missionaries' = S1M, 145 | 'shore-1-cannibals' = S1C, 146 | 'shore-2-missionaries' = S2M, 147 | 'shore-2-cannibals' = S2C}, 148 | #status {'search-depth' = SD2, 149 | 'boat-location' = BL, 150 | 'shore-1-missionaries' = S1M, 151 | 'shore-1-cannibals' = S1C, 152 | 'shore-2-missionaries' = S2M, 153 | 'shore-2-cannibals' = S2C} = Node) -> 154 | if 155 | SD1 < SD2 -> seresye_engine:retract (Engine0, Node); 156 | true -> Engine0 157 | end. 158 | 159 | 160 | recognize_solution (Engine0, 161 | #status {'shore-2-missionaries' = M, 162 | 'shore-2-cannibals' = C} = Node) -> 163 | if 164 | (M == ?INITIAL_MISSIONARIES) and (C == ?INITIAL_CANNIBALS) -> 165 | Engine1 = seresye_engine:retract (Engine0, Node), 166 | seresye_engine:set_client_state(Engine1, Node); 167 | true -> Engine0 168 | end. 169 | 170 | rules_test() -> 171 | Engine0 = seresye_engine:new(), 172 | Engine2 = seresye_engine:add_rules(Engine0, ?MODULE), 173 | 174 | Engine3 = 175 | seresye_engine:assert (Engine2, 176 | #status {'search-depth' = 1, 177 | 'parent' = 'no-parent', 178 | 'shore-1-missionaries' = ?INITIAL_MISSIONARIES, 179 | 'shore-2-missionaries' = 0, 180 | 'shore-1-cannibals' = ?INITIAL_CANNIBALS, 181 | 'shore-2-cannibals' = 0, 182 | 'boat-location' = 'shore-1'}), 183 | Engine4 = seresye_engine:assert (Engine3, {'boat-can-host', 2}), 184 | P = seresye_engine:get_client_state(Engine4), 185 | verify_result(P). 186 | 187 | verify_result(Node = #status{'shore-2-missionaries'=S2M, 188 | 'shore-2-cannibals'=S2C}) -> 189 | ?assertMatch(?INITIAL_MISSIONARIES, S2M), 190 | ?assertMatch(?INITIAL_CANNIBALS, S2C), 191 | verify_result_(Node). 192 | 193 | verify_result_(#status{'parent' = 'no-parent'}) -> 194 | ok; 195 | verify_result_(#status{'parent' = Parent, 196 | 'shore-1-missionaries'=S1M, 197 | 'shore-1-cannibals'=S1C, 198 | 'shore-2-missionaries'=S2M, 199 | 'shore-2-cannibals'=S2C}) -> 200 | ?assertMatch(true, (S1M >= S1C orelse S1M == 0)), 201 | ?assertMatch(true, (S2M >= S2C orelse S2M == 0)), 202 | verify_result_(Parent). 203 | -------------------------------------------------------------------------------- /test/seresyet_relatives.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, a Swarm oriented ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% Copyright (c) 2011, Afiniate, Inc. 5 | %%% All rights reserved. 6 | %%% 7 | %%% You may use this file under the terms of the BSD License. See the 8 | %%% license distributed with this project or 9 | %%% http://www.opensource.org/licenses/bsd-license.php 10 | -module (seresyet_relatives). 11 | 12 | -export([father/3, grandfather/3, grandmother/3, 13 | mother/3, brother/4, sister/4]). 14 | 15 | -include_lib("eunit/include/eunit.hrl"). 16 | 17 | -rules([mother, father, brother, sister, grandfather, 18 | grandmother]). 19 | 20 | %% 21 | %% if (X is female) and (X is Y's parent) then (X is Y's mother) 22 | %% 23 | mother (Engine, {female, X}, {parent, X, Y}) -> 24 | seresye_engine:set_client_state(seresye_engine:assert(Engine, 25 | {mother, X, Y}), 26 | [{mother, X, Y} | seresye_engine:get_client_state(Engine)]). 27 | 28 | 29 | %% 30 | %% if (X is male) and (X is Y's parent) then (X is Y's father) 31 | %% 32 | father (Engine, {male, X}, {parent, X, Y}) -> 33 | seresye_engine:set_client_state(seresye_engine:assert(Engine, 34 | {father, X, Y}), 35 | [{father, X, Y} | seresye_engine:get_client_state(Engine)]). 36 | 37 | 38 | %% 39 | %% if (Y and Z have the same parent X) and (Z is female) 40 | %% then (Z is Y's sister) 41 | %% 42 | sister (Engine, {parent, X, Y}, {parent, X, Z}, {female, Z}) when Y =/= Z -> 43 | seresye_engine:set_client_state(seresye_engine:assert(Engine, 44 | {sister, Z, Y}), 45 | [{sister, Z, Y} | seresye_engine:get_client_state(Engine)]). 46 | 47 | 48 | %% 49 | %% if (Y and Z have the same parent X) and (Z is male) 50 | %% then (Z is Y's brother) 51 | %% 52 | brother (Engine, {parent, X, Y}, {parent, X, Z}, {male, Z}) when Y =/= Z -> 53 | seresye_engine:set_client_state(seresye_engine:assert(Engine, 54 | {brother, Z, Y}), 55 | [{brother, Z, Y} | seresye_engine:get_client_state(Engine)]). 56 | 57 | 58 | %% 59 | %% if (X is Y's father) and (Y is Z's parent) 60 | %% then (X is Z's grandfather) 61 | %% 62 | grandfather (Engine, {father, X, Y}, {parent, Y, Z}) -> 63 | seresye_engine:set_client_state(seresye_engine:assert(Engine, 64 | {grandfather, X, Z}), 65 | [{grandfather, X, Z} 66 | | seresye_engine:get_client_state(Engine)]). 67 | 68 | %% 69 | %% if (X is Y's mother) and (Y is Z's parent) 70 | %% then (X is Z's grandmother) 71 | %% 72 | grandmother (Engine, {mother, X, Y}, {parent, Y, Z}) -> 73 | seresye_engine:set_client_state(seresye_engine:assert(Engine, 74 | {grandmother, X, Z}), 75 | [{grandmother, X, Z} 76 | | seresye_engine:get_client_state(Engine)]). 77 | 78 | 79 | rules_test() -> 80 | Engine0 = seresye_engine:new([]), 81 | Engine2 = seresye_engine:add_rules(Engine0, ?MODULE), 82 | 83 | Engine3 = seresye_engine:assert (Engine2, 84 | [{male, bob}, 85 | {male, corrado}, 86 | {male, mark}, 87 | {male, caesar}, 88 | {female, alice}, 89 | {female, sara}, 90 | {female, jane}, 91 | {female, anna}, 92 | {parent, jane, bob}, 93 | {parent, corrado, bob}, 94 | {parent, jane, mark}, 95 | {parent, corrado, mark}, 96 | {parent, jane, alice}, 97 | {parent, corrado, alice}, 98 | {parent, bob, caesar}, 99 | {parent, bob, anna}, 100 | {parent, sara, casear}, 101 | {parent, sara, anna}]), 102 | 103 | InternalState = seresye_engine:get_client_state(Engine3), 104 | 105 | ?assertMatch(true, 106 | lists:member({mother,sara,anna}, InternalState)), 107 | 108 | ?assertMatch(true, 109 | lists:member({sister,anna,casear}, 110 | InternalState)), 111 | 112 | ?assertMatch(true, 113 | lists:member({mother,sara,casear}, 114 | InternalState)), 115 | 116 | ?assertMatch(true, 117 | lists:member({grandfather,corrado,anna}, 118 | InternalState)), 119 | 120 | ?assertMatch(true, 121 | lists:member({sister,anna,caesar}, 122 | InternalState)), 123 | 124 | ?assertMatch(true, 125 | lists:member({brother,caesar,anna}, 126 | InternalState)), 127 | 128 | ?assertMatch(true, 129 | lists:member({father,bob,anna}, 130 | InternalState)), 131 | 132 | ?assertMatch(true, 133 | lists:member({grandmother,jane,anna}, 134 | InternalState)), 135 | 136 | ?assertMatch(true, 137 | lists:member({grandfather,corrado,caesar}, 138 | InternalState)), 139 | 140 | ?assertMatch(true, 141 | lists:member({father,bob,caesar}, 142 | InternalState)), 143 | 144 | ?assertMatch(true, 145 | lists:member({grandmother,jane,caesar}, 146 | InternalState)), 147 | 148 | ?assertMatch(true, 149 | lists:member({sister,alice,mark}, 150 | InternalState)), 151 | 152 | ?assertMatch(true, 153 | lists:member({brother,bob,alice}, 154 | InternalState)), 155 | 156 | ?assertMatch(true, 157 | lists:member({brother,mark,alice}, 158 | InternalState)), 159 | 160 | ?assertMatch(true, 161 | lists:member({father,corrado,alice}, 162 | InternalState)), 163 | 164 | ?assertMatch(true, 165 | lists:member({sister,alice,bob}, 166 | InternalState)), 167 | 168 | ?assertMatch(true, 169 | lists:member({sister,alice,mark}, 170 | InternalState)), 171 | 172 | ?assertMatch(true, 173 | lists:member({brother,bob,alice}, 174 | InternalState)), 175 | 176 | ?assertMatch(true, 177 | lists:member({brother,mark,alice}, 178 | InternalState)), 179 | 180 | ?assertMatch(true, 181 | lists:member({mother,jane,alice}, 182 | InternalState)), 183 | 184 | ?assertMatch(true, 185 | lists:member({sister,alice,bob}, 186 | InternalState)), 187 | 188 | ?assertMatch(true, 189 | lists:member({brother,bob,mark}, 190 | InternalState)), 191 | 192 | ?assertMatch(true, 193 | lists:member({father,corrado,mark}, 194 | InternalState)), 195 | 196 | ?assertMatch(true, 197 | lists:member({brother,mark,bob}, 198 | InternalState)), 199 | 200 | ?assertMatch(true, 201 | lists:member({brother,bob,mark}, 202 | InternalState)), 203 | 204 | ?assertMatch(true, 205 | lists:member({mother,jane,mark}, 206 | InternalState)), 207 | 208 | ?assertMatch(true, 209 | lists:member({brother,mark,bob}, 210 | InternalState)), 211 | 212 | ?assertMatch(true, 213 | lists:member({father,corrado,bob}, 214 | InternalState)), 215 | 216 | ?assertMatch(true, 217 | lists:member({mother,jane,bob}, 218 | InternalState)), 219 | 220 | ?assertMatch(29, erlang:length(InternalState)). 221 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /examples/seresyee_auto.erl: -------------------------------------------------------------------------------- 1 | %%% SERESYE, an ERlang Expert SYstem Engine 2 | %%% 3 | %%% Copyright (c) 2005-2010, Francesca Gangemi, Corrado Santoro 4 | %%% All rights reserved. 5 | %%% 6 | %%% You may use this file under the terms of the BSD License. See the 7 | %%% license distributed with this project or 8 | %%% http://www.opensource.org/licenses/bsd-license.php 9 | %%% ====================================================== 10 | %%% Automotive Expert System 11 | %%% 12 | %%% This expert system diagnoses some simple 13 | %%% problems with a car. 14 | %%% 15 | %%% It is a bare translation of the same example 16 | %%% provided in CLIPS Version 6.0 17 | %%% 18 | %%% To execute, type 'auto:start().' 19 | %%% ====================================================== 20 | -module(seresyee_auto). 21 | 22 | -export([determine_battery_state/2, 23 | determine_conductivity_test/4, determine_engine_state/2, 24 | determine_gas_level/3, determine_knocking/2, 25 | determine_low_output/2, determine_misfiring/2, 26 | determine_point_surface_state_1/3, 27 | determine_point_surface_state_2/2, 28 | determine_rotation_state/2, determine_sluggishness/2, 29 | no_repairs/2, normal_engine_state_conclusions/2, 30 | print_repair/3, start/0, 31 | unsatisfactory_engine_state_conclusions/2]). 32 | 33 | -neg_rule({determine_engine_state, [{'working-state', engine, '__IGNORE_UNDERSCORE__'}, 34 | {repair, '__IGNORE_UNDERSCORE__'}]}). 35 | 36 | -include_lib("seresye/include/seresye.hrl"). 37 | 38 | %% ********************** 39 | %% * ENGINE STATE RULES * 40 | %% ********************** 41 | normal_engine_state_conclusions(Engine, 42 | {'working-state', engine, normal}) -> 43 | seresye_engine:assert(Engine, 44 | [{repair, "No repair needed."}, 45 | {'spark-state', engine, normal}, 46 | {'charge-state', battery, charged}, 47 | {'rotation-state', engine, rotates}]). 48 | 49 | unsatisfactory_engine_state_conclusions(Engine, 50 | {'working-state', engine, 51 | unsatisfactory}) -> 52 | seresye_engine:assert(Engine, 53 | [{'charge-state', battery, charged}, 54 | {'rotation-state', engine, rotates}]). 55 | 56 | %% *************** 57 | %% * QUERY RULES * 58 | %% *************** 59 | determine_engine_state(Engine, {start, _}) 60 | when not {rule, [{'working-state', engine, _}, {repair, _}]} -> 61 | case ask_yn('Does the engine start (yes/no)? ') of 62 | true -> 63 | case ask_yn('Does the engine run normally (yes/no)? ') 64 | of 65 | true -> 66 | seresye_engine:assert(Engine, 67 | {'working-state', engine, normal}); 68 | _ -> 69 | seresye_engine:assert(Engine, 70 | {'working-state', engine, unsatisfactory}) 71 | end; 72 | _ -> 73 | seresye_engine:assert(Engine, 74 | {'working-state', engine, 'does-not-start'}) 75 | end. 76 | 77 | determine_rotation_state(Engine, 78 | {'working-state', engine, 'does-not-start'}) 79 | when not 80 | {rule, [{'rotation-state', engine, _}, {repair, _}]} -> 81 | case ask_yn('Does the engine rotate (yes/no)? ') of 82 | true -> 83 | seresye_engine:assert(Engine, 84 | [{'rotation-state', engine, rotates}, 85 | {'spark-state', engine, 'irregular-spark'}]); 86 | _ -> 87 | seresye_engine:assert(Engine, 88 | [{'rotation-state', engine, 'does-not-rotate'}, 89 | {'spark-state', engine, 'does-not-spark'}]) 90 | end. 91 | 92 | determine_sluggishness(Engine, 93 | {'working-state', engine, unsatisfactory}) 94 | when not {rule, [{repair, _}]} -> 95 | case ask_yn('Is the engine sluggish (yes/no)? ') of 96 | true -> 97 | seresye_engine:assert(Engine, {repair, "Clean the fuel line."}); 98 | _ -> Engine 99 | end. 100 | 101 | determine_misfiring(Engine, 102 | {'working-state', engine, unsatisfactory}) 103 | when not {rule, [{repair, _}]} -> 104 | case ask_yn('Does the engine misfire (yes/no)? ') of 105 | true -> 106 | seresye_engine:assert(Engine, 107 | [{repair, "Point gap adjustment."}, 108 | {'spark-state', engine, 'irregular-spark'}]); 109 | _ -> Engine 110 | end. 111 | 112 | determine_knocking(E, 113 | {'working-state', engine, unsatisfactory}) 114 | when not {rule, [{repair, _}]} -> 115 | case ask_yn('Does the engine knock (yes/no)? ') of 116 | true -> 117 | seresye_engine:assert(E, {repair, "Timing adjustment."}); 118 | _ -> E 119 | end. 120 | 121 | determine_low_output(E, 122 | {'working-state', engine, unsatisfactory}) 123 | when not {rule, [{symptom, engine, _}, {repair, _}]} -> 124 | case 125 | ask_yn('Is the output of the engine low (yes/no)? ') 126 | of 127 | true -> 128 | seresye_engine:assert(E, {symptom, engine, 'low-output'}); 129 | _ -> 130 | seresye_engine:assert(E, {symptom, engine, 'not-low-output'}) 131 | end. 132 | 133 | determine_gas_level(E, 134 | {'working-state', engine, 'does-not-start'}, 135 | {'rotation-state', engine, rotates}) 136 | when not {rule, [{repair, _}]} -> 137 | case 138 | ask_yn('Does the tank have any gas in it (yes/no)? ') 139 | of 140 | false -> seresye_engine:assert(E, {repair, "Add gas."}); 141 | _ -> E 142 | end. 143 | 144 | determine_battery_state(E, 145 | {'rotation-state', engine, 'does-not-rotate'}) 146 | when not 147 | {rule, [{'charge-state', battery, _}, {repair, _}]} -> 148 | case ask_yn('Is the battery charged (yes/no)? ') of 149 | true -> 150 | seresye_engine:assert(E, {'charge-state', battery, charged}); 151 | _ -> 152 | seresye_engine:assert(E, 153 | [{repair, "Charge the battery."}, 154 | {'charge-state', battery, dead}]) 155 | end. 156 | 157 | determine_point_surface_state_1(E, 158 | {'working-state', engine, 'does-not-start'}, 159 | {'spark-state', engine, 'irregular-spark'}) 160 | when not {rule, [{repair, _}]} -> 161 | dpss(E). 162 | 163 | determine_point_surface_state_2(E, 164 | {symptom, engine, 'low-output'}) 165 | when not {rule, [{repair, _}]} -> 166 | dpss(E). 167 | 168 | dpss(E) -> 169 | case 170 | ask_question('What is the surface state of the points (normal/burned/contaminated)? ') 171 | of 172 | [$b, $u, $r, $n, $e, $d | _] -> 173 | seresye_engine:assert(E, {repair, "Replace the points."}); 174 | [$c, $o, $n, $t, $a, $m, $i, $n, $a, $t, $e, $d | _] -> 175 | seresye_engine:assert(E, {repair, "Clean the points."}); 176 | _ -> E 177 | end. 178 | 179 | determine_conductivity_test(E, 180 | {'working-state', engine, 'does-not-start'}, 181 | {'spark-state', engine, 'does-not-spark'}, 182 | {'charge-state', battery, charged}) 183 | when not {rule, [{repair, _}]}; true -> 184 | case 185 | ask_yn('Is the conductivity test for the ignition coil positive (yes/no)? ') 186 | of 187 | true -> 188 | seresye_engine:assert(E, 189 | {repair, "Repair the distributor lead wire."}); 190 | _ -> 191 | seresye_engine:assert(E, {repair, "Replace the ignition coil."}) 192 | end. 193 | 194 | no_repairs(E, {start, _}) 195 | when not {rule, [{repair, _}]}, true -> 196 | seresye_engine:assert(E, 197 | {repair, "Take your car to a mechanic."}). 198 | 199 | print_repair(E, {repair, X}, {start, _}) -> 200 | io:format("Suggested Repair: ~p~n", [X]), E. 201 | 202 | ask_yn(Prompt) -> 203 | [Response | _] = io:get_line(Prompt), 204 | case Response of 205 | $y -> true; 206 | _ -> false 207 | end. 208 | 209 | ask_question(Prompt) -> io:get_line(Prompt). 210 | 211 | start() -> 212 | Engine0 = seresye_engine:new(), 213 | %% Rules with high priority (10) 214 | Engine2 = lists:foldl(fun (Rule, Engine1) -> 215 | seresye_engine:add_rule(Engine1, {?MODULE, Rule}, 10) 216 | end, 217 | Engine0, 218 | [normal_engine_state_conclusions, 219 | unsatisfactory_engine_state_conclusions, 220 | print_repair]), 221 | %% Rules with normal priority (0) 222 | Engine3 = lists:foldl(fun (Rule, Engine1) -> 223 | seresye_engine:add_rule(Engine1, {?MODULE, Rule}) 224 | end, 225 | Engine2, 226 | [determine_engine_state, determine_rotation_state, 227 | determine_sluggishness, determine_misfiring, 228 | determine_knocking, determine_low_output, 229 | determine_gas_level, determine_battery_state, 230 | determine_point_surface_state_1, 231 | determine_point_surface_state_2, 232 | determine_conductivity_test]), 233 | %% Rules with low priority (-10) 234 | Engine4 = seresye_engine:add_rule(Engine3, 235 | {?MODULE, no_repairs}, -10), 236 | seresye_engine:assert(Engine4, {start, ok}). 237 | -------------------------------------------------------------------------------- /doc/assets/css/skeleton/stylesheets/skeleton.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V1.1 3 | * Copyright 2011, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 8/17/2011 8 | */ 9 | 10 | 11 | /* Table of Contents 12 | ================================================== 13 | #Base 960 Grid 14 | #Tablet (Portrait) 15 | #Mobile (Portrait) 16 | #Mobile (Landscape) 17 | #Clearing */ 18 | 19 | 20 | 21 | /* #Base 960 Grid 22 | ================================================== */ 23 | 24 | .container { position: relative; width: 960px; margin: 0 auto; padding: 0; } 25 | .column, .columns { float: left; display: inline; margin-left: 10px; margin-right: 10px; } 26 | .row { margin-bottom: 20px; } 27 | 28 | /* Nested Column Classes */ 29 | .column.alpha, .columns.alpha { margin-left: 0; } 30 | .column.omega, .columns.omega { margin-right: 0; } 31 | 32 | /* Base Grid */ 33 | .container .one.column { width: 40px; } 34 | .container .two.columns { width: 100px; } 35 | .container .three.columns { width: 160px; } 36 | .container .four.columns { width: 220px; } 37 | .container .five.columns { width: 280px; } 38 | .container .six.columns { width: 340px; } 39 | .container .seven.columns { width: 400px; } 40 | .container .eight.columns { width: 460px; } 41 | .container .nine.columns { width: 520px; } 42 | .container .ten.columns { width: 580px; } 43 | .container .eleven.columns { width: 640px; } 44 | .container .twelve.columns { width: 700px; } 45 | .container .thirteen.columns { width: 760px; } 46 | .container .fourteen.columns { width: 820px; } 47 | .container .fifteen.columns { width: 880px; } 48 | .container .sixteen.columns { width: 940px; } 49 | 50 | .container .one-third.column { width: 300px; } 51 | .container .two-thirds.column { width: 620px; } 52 | 53 | /* Offsets */ 54 | .container .offset-by-one { padding-left: 60px; } 55 | .container .offset-by-two { padding-left: 120px; } 56 | .container .offset-by-three { padding-left: 180px; } 57 | .container .offset-by-four { padding-left: 240px; } 58 | .container .offset-by-five { padding-left: 300px; } 59 | .container .offset-by-six { padding-left: 360px; } 60 | .container .offset-by-seven { padding-left: 420px; } 61 | .container .offset-by-eight { padding-left: 480px; } 62 | .container .offset-by-nine { padding-left: 540px; } 63 | .container .offset-by-ten { padding-left: 600px; } 64 | .container .offset-by-eleven { padding-left: 660px; } 65 | .container .offset-by-twelve { padding-left: 720px; } 66 | .container .offset-by-thirteen { padding-left: 780px; } 67 | .container .offset-by-fourteen { padding-left: 840px; } 68 | .container .offset-by-fifteen { padding-left: 900px; } 69 | 70 | 71 | 72 | /* #Tablet (Portrait) 73 | ================================================== */ 74 | 75 | /* Note: Design for a width of 768px */ 76 | 77 | @media only screen and (min-width: 768px) and (max-width: 959px) { 78 | .container { width: 768px; } 79 | .container .column, 80 | .container .columns { margin-left: 10px; margin-right: 10px; } 81 | .column.alpha, .columns.alpha { margin-left: 0; margin-right: 10px; } 82 | .column.omega, .columns.omega { margin-right: 0; margin-left: 10px; } 83 | 84 | .container .one.column { width: 28px; } 85 | .container .two.columns { width: 76px; } 86 | .container .three.columns { width: 124px; } 87 | .container .four.columns { width: 172px; } 88 | .container .five.columns { width: 220px; } 89 | .container .six.columns { width: 268px; } 90 | .container .seven.columns { width: 316px; } 91 | .container .eight.columns { width: 364px; } 92 | .container .nine.columns { width: 412px; } 93 | .container .ten.columns { width: 460px; } 94 | .container .eleven.columns { width: 508px; } 95 | .container .twelve.columns { width: 556px; } 96 | .container .thirteen.columns { width: 604px; } 97 | .container .fourteen.columns { width: 652px; } 98 | .container .fifteen.columns { width: 700px; } 99 | .container .sixteen.columns { width: 748px; } 100 | 101 | .container .one-third.column { width: 236px; } 102 | .container .two-thirds.column { width: 492px; } 103 | 104 | /* Offsets */ 105 | .container .offset-by-one { padding-left: 48px; } 106 | .container .offset-by-two { padding-left: 96px; } 107 | .container .offset-by-three { padding-left: 144px; } 108 | .container .offset-by-four { padding-left: 192px; } 109 | .container .offset-by-five { padding-left: 240px; } 110 | .container .offset-by-six { padding-left: 288px; } 111 | .container .offset-by-seven { padding-left: 336px; } 112 | .container .offset-by-eight { padding-left: 348px; } 113 | .container .offset-by-nine { padding-left: 432px; } 114 | .container .offset-by-ten { padding-left: 480px; } 115 | .container .offset-by-eleven { padding-left: 528px; } 116 | .container .offset-by-twelve { padding-left: 576px; } 117 | .container .offset-by-thirteen { padding-left: 624px; } 118 | .container .offset-by-fourteen { padding-left: 672px; } 119 | .container .offset-by-fifteen { padding-left: 720px; } 120 | } 121 | 122 | 123 | /* #Mobile (Portrait) 124 | ================================================== */ 125 | 126 | /* Note: Design for a width of 320px */ 127 | 128 | @media only screen and (max-width: 767px) { 129 | .container { width: 300px; } 130 | .columns, .column { margin: 0; } 131 | 132 | .container .one.column, 133 | .container .two.columns, 134 | .container .three.columns, 135 | .container .four.columns, 136 | .container .five.columns, 137 | .container .six.columns, 138 | .container .seven.columns, 139 | .container .eight.columns, 140 | .container .nine.columns, 141 | .container .ten.columns, 142 | .container .eleven.columns, 143 | .container .twelve.columns, 144 | .container .thirteen.columns, 145 | .container .fourteen.columns, 146 | .container .fifteen.columns, 147 | .container .sixteen.columns, 148 | .container .one-third.column, 149 | .container .two-thirds.column { width: 300px; } 150 | 151 | /* Offsets */ 152 | .container .offset-by-one, 153 | .container .offset-by-two, 154 | .container .offset-by-three, 155 | .container .offset-by-four, 156 | .container .offset-by-five, 157 | .container .offset-by-six, 158 | .container .offset-by-seven, 159 | .container .offset-by-eight, 160 | .container .offset-by-nine, 161 | .container .offset-by-ten, 162 | .container .offset-by-eleven, 163 | .container .offset-by-twelve, 164 | .container .offset-by-thirteen, 165 | .container .offset-by-fourteen, 166 | .container .offset-by-fifteen { padding-left: 0; } 167 | 168 | } 169 | 170 | 171 | /* #Mobile (Landscape) 172 | ================================================== */ 173 | 174 | /* Note: Design for a width of 480px */ 175 | 176 | @media only screen and (min-width: 480px) and (max-width: 767px) { 177 | .container { width: 420px; } 178 | .columns, .column { margin: 0; } 179 | 180 | .container .one.column, 181 | .container .two.columns, 182 | .container .three.columns, 183 | .container .four.columns, 184 | .container .five.columns, 185 | .container .six.columns, 186 | .container .seven.columns, 187 | .container .eight.columns, 188 | .container .nine.columns, 189 | .container .ten.columns, 190 | .container .eleven.columns, 191 | .container .twelve.columns, 192 | .container .thirteen.columns, 193 | .container .fourteen.columns, 194 | .container .fifteen.columns, 195 | .container .sixteen.columns, 196 | .container .one-third.column, 197 | .container .two-thirds.column { width: 420px; } 198 | } 199 | 200 | 201 | /* #Clearing 202 | ================================================== */ 203 | 204 | /* Self Clearing Goodness */ 205 | .container:after { content: "\0020"; display: block; height: 0; clear: both; visibility: hidden; } 206 | 207 | /* Use clearfix class on parent to clear nested columns, 208 | or wrap each row of columns in a
*/ 209 | .clearfix:before, 210 | .clearfix:after, 211 | .row:before, 212 | .row:after { 213 | content: '\0020'; 214 | display: block; 215 | overflow: hidden; 216 | visibility: hidden; 217 | width: 0; 218 | height: 0; } 219 | .row:after, 220 | .clearfix:after { 221 | clear: both; } 222 | .row, 223 | .clearfix { 224 | zoom: 1; } 225 | 226 | /* You can also use a
to clear columns */ 227 | .clear { 228 | clear: both; 229 | display: block; 230 | overflow: hidden; 231 | visibility: hidden; 232 | width: 0; 233 | height: 0; 234 | } 235 | 236 | 237 | -------------------------------------------------------------------------------- /doc/assets/css/skeleton/stylesheets/base.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V1.1 3 | * Copyright 2011, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 8/17/2011 8 | */ 9 | 10 | 11 | /* Table of Content 12 | ================================================== 13 | #Reset & Basics 14 | #Basic Styles 15 | #Site Styles 16 | #Typography 17 | #Links 18 | #Lists 19 | #Images 20 | #Buttons 21 | #Tabs 22 | #Forms 23 | #Misc */ 24 | 25 | 26 | /* #Reset & Basics (Inspired by E. Meyers) 27 | ================================================== */ 28 | 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 { 29 | margin: 0; 30 | padding: 0; 31 | border: 0; 32 | font-size: 100%; 33 | font: inherit; 34 | vertical-align: baseline; } 35 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { 36 | display: block; } 37 | body { 38 | line-height: 1; } 39 | ol, ul { 40 | list-style: none; } 41 | blockquote, q { 42 | quotes: none; } 43 | blockquote:before, blockquote:after, 44 | q:before, q:after { 45 | content: ''; 46 | content: none; } 47 | table { 48 | border-collapse: collapse; 49 | border-spacing: 0; } 50 | 51 | 52 | /* #Basic Styles 53 | ================================================== */ 54 | body { 55 | background: #fff; 56 | font: 14px/21px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 57 | color: #444; 58 | -webkit-font-smoothing: antialiased; /* Fix for webkit rendering */ 59 | -webkit-text-size-adjust: 100%; 60 | } 61 | 62 | 63 | /* #Typography 64 | ================================================== */ 65 | h1, h2, h3, h4, h5, h6 { 66 | color: #181818; 67 | font-family: "Georgia", "Times New Roman", Helvetica, Arial, sans-serif; 68 | font-weight: normal; } 69 | h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; } 70 | h1 { font-size: 46px; line-height: 50px; margin-bottom: 14px;} 71 | h2 { font-size: 35px; line-height: 40px; margin-bottom: 10px; } 72 | h3 { font-size: 28px; line-height: 34px; margin-bottom: 8px; } 73 | h4 { font-size: 21px; line-height: 30px; margin-bottom: 4px; } 74 | h5 { font-size: 17px; line-height: 24px; } 75 | h6 { font-size: 14px; line-height: 21px; } 76 | .subheader { color: #777; } 77 | 78 | p { margin: 0 0 20px 0; } 79 | p img { margin: 0; } 80 | p.lead { font-size: 21px; line-height: 27px; color: #777; } 81 | 82 | em { font-style: italic; } 83 | strong { font-weight: bold; color: #333; } 84 | small { font-size: 80%; } 85 | 86 | /* Blockquotes */ 87 | blockquote, blockquote p { font-size: 17px; line-height: 24px; color: #777; font-style: italic; } 88 | blockquote { margin: 0 0 20px; padding: 9px 20px 0 19px; border-left: 1px solid #ddd; } 89 | blockquote cite { display: block; font-size: 12px; color: #555; } 90 | blockquote cite:before { content: "\2014 \0020"; } 91 | blockquote cite a, blockquote cite a:visited, blockquote cite a:visited { color: #555; } 92 | 93 | hr { border: solid #ddd; border-width: 1px 0 0; clear: both; margin: 10px 0 30px; height: 0; } 94 | 95 | 96 | /* #Links 97 | ================================================== */ 98 | a, a:visited { color: #333; text-decoration: underline; outline: 0; } 99 | a:hover, a:focus { color: #000; } 100 | p a, p a:visited { line-height: inherit; } 101 | 102 | 103 | /* #Lists 104 | ================================================== */ 105 | ul, ol { margin-bottom: 20px; } 106 | ul { list-style: none outside; } 107 | ol { list-style: decimal; } 108 | ol, ul.square, ul.circle, ul.disc { margin-left: 30px; } 109 | ul.square { list-style: square outside; } 110 | ul.circle { list-style: circle outside; } 111 | ul.disc { list-style: disc outside; } 112 | ul ul, ul ol, 113 | ol ol, ol ul { margin: 4px 0 5px 30px; font-size: 90%; } 114 | ul ul li, ul ol li, 115 | ol ol li, ol ul li { margin-bottom: 6px; } 116 | li { line-height: 18px; margin-bottom: 12px; } 117 | ul.large li { line-height: 21px; } 118 | li p { line-height: 21px; } 119 | 120 | /* #Images 121 | ================================================== */ 122 | 123 | img.scale-with-grid { 124 | max-width: 100%; 125 | height: auto; } 126 | 127 | 128 | /* #Buttons 129 | ================================================== */ 130 | 131 | a.button, 132 | button, 133 | input[type="submit"], 134 | input[type="reset"], 135 | input[type="button"] { 136 | background: #eee; /* Old browsers */ 137 | background: -moz-linear-gradient(top, rgba(255,255,255,.2) 0%, rgba(0,0,0,.2) 100%); /* FF3.6+ */ 138 | 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+ */ 139 | background: -webkit-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Chrome10+,Safari5.1+ */ 140 | background: -o-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Opera11.10+ */ 141 | background: -ms-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* IE10+ */ 142 | background: linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* W3C */ 143 | border: 1px solid #aaa; 144 | border-top: 1px solid #ccc; 145 | border-left: 1px solid #ccc; 146 | padding: 4px 12px; 147 | -moz-border-radius: 3px; 148 | -webkit-border-radius: 3px; 149 | border-radius: 3px; 150 | color: #444; 151 | display: inline-block; 152 | font-size: 11px; 153 | font-weight: bold; 154 | text-decoration: none; 155 | text-shadow: 0 1px rgba(255, 255, 255, .75); 156 | cursor: pointer; 157 | margin-bottom: 20px; 158 | line-height: 21px; 159 | font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; } 160 | 161 | a.button:hover, 162 | button:hover, 163 | input[type="submit"]:hover, 164 | input[type="reset"]:hover, 165 | input[type="button"]:hover { 166 | color: #222; 167 | background: #ddd; /* Old browsers */ 168 | background: -moz-linear-gradient(top, rgba(255,255,255,.3) 0%, rgba(0,0,0,.3) 100%); /* FF3.6+ */ 169 | 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+ */ 170 | background: -webkit-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Chrome10+,Safari5.1+ */ 171 | background: -o-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Opera11.10+ */ 172 | background: -ms-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* IE10+ */ 173 | background: linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* W3C */ 174 | border: 1px solid #888; 175 | border-top: 1px solid #aaa; 176 | border-left: 1px solid #aaa; } 177 | 178 | a.button:active, 179 | button:active, 180 | input[type="submit"]:active, 181 | input[type="reset"]:active, 182 | input[type="button"]:active { 183 | border: 1px solid #666; 184 | background: #ccc; /* Old browsers */ 185 | background: -moz-linear-gradient(top, rgba(255,255,255,.35) 0%, rgba(10,10,10,.4) 100%); /* FF3.6+ */ 186 | 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+ */ 187 | background: -webkit-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Chrome10+,Safari5.1+ */ 188 | background: -o-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Opera11.10+ */ 189 | background: -ms-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* IE10+ */ 190 | background: linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* W3C */ } 191 | 192 | .button.full-width, 193 | button.full-width, 194 | input[type="submit"].full-width, 195 | input[type="reset"].full-width, 196 | input[type="button"].full-width { 197 | width: 100%; 198 | padding-left: 0 !important; 199 | padding-right: 0 !important; 200 | text-align: center; } 201 | 202 | 203 | /* #Tabs (activate in tabs.js) 204 | ================================================== */ 205 | ul.tabs { 206 | display: block; 207 | margin: 0 0 20px 0; 208 | padding: 0; 209 | border-bottom: solid 1px #ddd; } 210 | ul.tabs li { 211 | display: block; 212 | width: auto; 213 | height: 30px; 214 | padding: 0; 215 | float: left; 216 | margin-bottom: 0; } 217 | ul.tabs li a { 218 | display: block; 219 | text-decoration: none; 220 | width: auto; 221 | height: 29px; 222 | padding: 0px 20px; 223 | line-height: 30px; 224 | border: solid 1px #ddd; 225 | border-width: 1px 1px 0 0; 226 | margin: 0; 227 | background: #f5f5f5; 228 | font-size: 13px; } 229 | ul.tabs li a.active { 230 | background: #fff; 231 | height: 30px; 232 | position: relative; 233 | top: -4px; 234 | padding-top: 4px; 235 | border-left-width: 1px; 236 | margin: 0 0 0 -1px; 237 | color: #111; 238 | -moz-border-radius-topleft: 2px; 239 | -webkit-border-top-left-radius: 2px; 240 | border-top-left-radius: 2px; 241 | -moz-border-radius-topright: 2px; 242 | -webkit-border-top-right-radius: 2px; 243 | border-top-right-radius: 2px; } 244 | ul.tabs li:first-child a.active { 245 | margin-left: 0; } 246 | ul.tabs li:first-child a { 247 | border-width: 1px 1px 0 1px; 248 | -moz-border-radius-topleft: 2px; 249 | -webkit-border-top-left-radius: 2px; 250 | border-top-left-radius: 2px; } 251 | ul.tabs li:last-child a { 252 | -moz-border-radius-topright: 2px; 253 | -webkit-border-top-right-radius: 2px; 254 | border-top-right-radius: 2px; } 255 | 256 | ul.tabs-content { margin: 0; display: block; } 257 | ul.tabs-content > li { display:none; } 258 | ul.tabs-content > li.active { display: block; } 259 | 260 | /* Clearfixing tabs for beautiful stacking */ 261 | ul.tabs:before, 262 | ul.tabs:after { 263 | content: '\0020'; 264 | display: block; 265 | overflow: hidden; 266 | visibility: hidden; 267 | width: 0; 268 | height: 0; } 269 | ul.tabs:after { 270 | clear: both; } 271 | ul.tabs { 272 | zoom: 1; } 273 | 274 | 275 | /* #Forms 276 | ================================================== */ 277 | 278 | form { 279 | margin-bottom: 20px; } 280 | fieldset { 281 | margin-bottom: 20px; } 282 | input[type="text"], 283 | input[type="password"], 284 | input[type="email"], 285 | textarea, 286 | select { 287 | border: 1px solid #ccc; 288 | padding: 6px 4px; 289 | outline: none; 290 | -moz-border-radius: 2px; 291 | -webkit-border-radius: 2px; 292 | border-radius: 2px; 293 | font: 13px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 294 | color: #777; 295 | margin: 0; 296 | width: 210px; 297 | max-width: 100%; 298 | display: block; 299 | margin-bottom: 20px; 300 | background: #fff; } 301 | select { 302 | padding: 0; } 303 | input[type="text"]:focus, 304 | input[type="password"]:focus, 305 | input[type="email"]:focus, 306 | textarea:focus { 307 | border: 1px solid #aaa; 308 | color: #444; 309 | -moz-box-shadow: 0 0 3px rgba(0,0,0,.2); 310 | -webkit-box-shadow: 0 0 3px rgba(0,0,0,.2); 311 | box-shadow: 0 0 3px rgba(0,0,0,.2); } 312 | textarea { 313 | min-height: 60px; } 314 | label, 315 | legend { 316 | display: block; 317 | font-weight: bold; 318 | font-size: 13px; } 319 | select { 320 | width: 220px; } 321 | input[type="checkbox"] { 322 | display: inline; } 323 | label span, 324 | legend span { 325 | font-weight: normal; 326 | font-size: 13px; 327 | color: #444; } 328 | 329 | /* #Misc 330 | ================================================== */ 331 | .remove-bottom { margin-bottom: 0 !important; } 332 | .half-bottom { margin-bottom: 10px !important; } 333 | .add-bottom { margin-bottom: 20px !important; } 334 | 335 | 336 | -------------------------------------------------------------------------------- /doc/assets/javascript/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (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= 3 | [],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), 9 | 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])*(?:`|$))/, 10 | 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]*/, 11 | 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, 12 | "");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), 13 | 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} 14 | 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"], 18 | "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"], 19 | 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"], 20 | 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"+ 21 | 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]]), 22 | ["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", 23 | /^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}), 24 | ["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", 25 | 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= 26 | !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 31 | Seresye#seresye{agenda=#agenda{rules_fired=0, 32 | strategy=depth, 33 | rule_list=[], 34 | exec_state=nil, 35 | pending_actions=[], 36 | id=0}}. 37 | 38 | add_activation(Agenda, Rule, Args) -> 39 | add_activation(Agenda, Rule, Args, 0). 40 | 41 | add_activation(EngineState0 = #seresye{agenda= 42 | Agenda0 = #agenda{strategy=Strategy, 43 | rule_list=RuleList0, 44 | id=Id}}, 45 | Rule, Args, Salience) -> 46 | RuleList1 = case Strategy of 47 | depth -> 48 | depth_add(RuleList0, Rule, Args, Salience, Id); 49 | breadth -> 50 | breadth_add(RuleList0, Rule, Args, Salience, Id); 51 | fifo -> 52 | fifo_add(RuleList0, Rule, Args, Salience, Id) 53 | end, 54 | Agenda1 = Agenda0#agenda{rule_list=RuleList1, id=Id + 1}, 55 | 56 | execute_pending(after_activation_schedule(EngineState0#seresye{agenda=Agenda1})). 57 | 58 | 59 | %% @doc Remove all activation from Agenda, 60 | %% returns an empty agenda with same past strategy 61 | clear_agenda(EngineState = #seresye{agenda=Agenda0}) -> 62 | EngineState#seresye{agenda=Agenda0#agenda{rule_list=[], id=0}}. 63 | 64 | get_strategy(#seresye{agenda=#agenda{strategy=Strategy}}) -> 65 | Strategy. 66 | 67 | set_strategy(EngineState = #seresye{agenda=Agenda0 = #agenda{rule_list=RuleList0}}, NewStrategy) -> 68 | RuleList1 = case NewStrategy of 69 | depth -> 70 | lists:sort(fun depth_order/2, RuleList0); 71 | breadth -> 72 | lists:sort(fun breadth_order/2, 73 | RuleList0); 74 | fifo -> 75 | lists:sort(fun fifo_order/2, RuleList0); 76 | _ -> 77 | erlang:throw({seresye, {invalid_strategy, NewStrategy}}) 78 | 79 | end, 80 | EngineState#seresye{agenda=Agenda0#agenda{strategy=NewStrategy, rule_list=RuleList1}}. 81 | 82 | get_rules_fired(#seresye{agenda=#agenda{rules_fired=Fired}}) -> 83 | Fired. 84 | 85 | %% @doc Remove activation with id='Id' or 86 | %% all activation whose id is in the list passed as argument 87 | delete_activation(EngineState, []) -> EngineState; 88 | delete_activation(EngineState = #seresye{agenda=Agenda0 = #agenda{rule_list=RuleList0}}, 89 | Id) 90 | when not is_list(Id) -> 91 | EngineState#seresye{agenda=Agenda0#agenda{rule_list=lists:keydelete(Id, 4, RuleList0)}}; 92 | delete_activation(EngineState, [Id | OtherId]) -> 93 | EngineState1 = delete_activation(EngineState, Id), 94 | delete_activation(EngineState1, OtherId). 95 | 96 | %% @doc Remove all activation associated with rule 'Rule' from the agenda, 97 | %% returns the modified agenda 98 | delete_rule(EngineState = #seresye{agenda=Agenda0 = #agenda{rule_list=RuleList0}}, Rule) -> 99 | ActList = proplists:lookup_all(Rule, RuleList0), 100 | RuleList1 = lists:foldl(fun (X, R1) -> 101 | lists:delete(X, R1) 102 | end, 103 | RuleList0, ActList), 104 | EngineState#seresye{agenda=Agenda0#agenda{rule_list=RuleList1}}. 105 | 106 | %% @doc Returns the Id of activation associated to rule 'Rule' and 107 | %% arguments 'Args', false if it not present 108 | get_activation(#seresye{agenda=#agenda{rule_list=RuleList0}}, {Rule, Args}) -> 109 | ActList = proplists:lookup_all(Rule, RuleList0), 110 | case lists:keysearch(Args, 2, ActList) of 111 | {value, {_, _, _, Id}} -> Id; 112 | false -> false 113 | end. 114 | 115 | %% @doc Returns the Id of first activation associated to rule 'Rule', 116 | %% false if 'Rule' is not present in the agenda 117 | get_activation_from_name(#seresye{agenda=#agenda{rule_list=RuleList0}}, Rule) -> 118 | case lists:keysearch(Rule, 1, RuleList0) of 119 | {value, {_, _, _, Id}} -> Id; 120 | false -> false 121 | end. 122 | 123 | %% @doc Return the Id associated to first activation in the agenda 124 | %% false if agenda is empty 125 | get_first_activation(#seresye{agenda=#agenda{rule_list=[First | _]}}) -> 126 | element(4, First). 127 | 128 | %% @doc Return the salience value of activation with id='Id' 129 | %% false if Id is not present in the agenda 130 | get_activation_salience(#seresye{agenda=#agenda{rule_list=RuleList0, id=NextId}}, Id) -> 131 | case NextId < Id of 132 | true -> false; 133 | false -> 134 | case lists:keysearch(Id, 4, RuleList0) of 135 | {value, {_, _, Salience, _}} -> Salience; 136 | false -> false 137 | end 138 | end. 139 | 140 | %% @doc Sets the salience value of activation with id='Id', 141 | %% returns the modified agenda 142 | set_activation_salience(EngineState = 143 | #seresye{agenda=Agenda0 = 144 | #agenda{rule_list=RuleList0, strategy=Strategy}}, Id, NewSalience) 145 | when is_number(NewSalience) and not is_list(Id) -> 146 | RuleList2 = 147 | case lists:keysearch(Id, 4, RuleList0) of 148 | {value, {Rule, Args, _Salience, Id}} -> 149 | RuleList1 = lists:keyreplace(Id, 4, RuleList0, 150 | {Rule, Args, NewSalience, Id}), 151 | order(RuleList1, Strategy); 152 | false -> 153 | RuleList0 154 | end, 155 | EngineState#seresye{agenda=Agenda0#agenda{rule_list=RuleList2}}; 156 | set_activation_salience(_EngineState, Id, NewSalience) 157 | when not is_list(Id) -> 158 | erlang:throw({seresye, {invalid_salience, NewSalience}}); 159 | set_activation_salience(EngineState0, [Id | OtherId], 160 | NewSalience) -> 161 | EngineState1 = set_activation_salience(EngineState0, Id, NewSalience), 162 | set_activation_salience(EngineState1, OtherId, NewSalience). 163 | 164 | %% @doc Sets the salience value of all activations associated to rule 'Rule', 165 | %% returns the modified agenda 166 | set_rule_salience(EngineState0 = 167 | #seresye{agenda=#agenda{rule_list=RuleList0}}, Rule, NewSalience) -> 168 | ActList = proplists:lookup_all(Rule, RuleList0), 169 | IdList = [Id || {_, _, _, Id} <- ActList], 170 | set_activation_salience(EngineState0, IdList, 171 | NewSalience). 172 | 173 | 174 | %%==================================================================== 175 | %% Internal functions 176 | %%==================================================================== 177 | execute_pending(EngineState0 = #seresye{agenda = Agenda0 = #agenda{pending_actions=[PA | Rest]}}) -> 178 | EngineState1 = PA(EngineState0#seresye{agenda=Agenda0#agenda{pending_actions=Rest}}), 179 | execute_pending(EngineState1); 180 | execute_pending(EngineState0 = #seresye{agenda = #agenda{pending_actions=[]}}) -> 181 | EngineState0. 182 | 183 | after_activation_schedule(EngineState0 = 184 | #seresye{agenda=Agenda0 = 185 | #agenda{rule_list=RuleList0, 186 | exec_state=ExecState0, 187 | pending_actions=PA0, 188 | rules_fired=RF0}}) -> 189 | {ExecState1, RuleList1, PA1, RF1} = 190 | if 191 | ExecState0 == active -> {ExecState0, RuleList0, PA0, RF0}; 192 | true -> 193 | {RuleToExecute, RL} = pop_rule(RuleList0), 194 | if 195 | RuleToExecute == false -> 196 | {nil, RL, PA0, RF0}; 197 | true -> 198 | {active, RL, PA0 ++ [fun(EngineState) -> 199 | exec(EngineState, RuleToExecute) 200 | end], RF0 + 1} 201 | end 202 | end, 203 | EngineState0#seresye{agenda=Agenda0#agenda{exec_state=ExecState1, 204 | rule_list=RuleList1, 205 | pending_actions=PA1, 206 | rules_fired=RF1}}. 207 | 208 | after_execution_schedule(EngineState0 = 209 | #seresye{agenda=Agenda0 = 210 | #agenda{rule_list=RuleList0, 211 | pending_actions=PA0, 212 | rules_fired=RF0}}) -> 213 | {RuleToExecute, RL} = pop_rule(RuleList0), 214 | {ExecState1, RuleList1, PA1, RF1} = 215 | if 216 | RuleToExecute == false -> {nil, RL, PA0, RF0}; 217 | true -> 218 | {active, RL, PA0 ++ [fun(EngineState) -> 219 | exec(EngineState, RuleToExecute) 220 | end], RF0 + 1} 221 | end, 222 | EngineState0#seresye{agenda=Agenda0#agenda{exec_state=ExecState1, 223 | rule_list=RuleList1, 224 | pending_actions=PA1, 225 | rules_fired=RF1}}. 226 | 227 | %% @doc Remove the first activation, returns the rule, 228 | %% function arguments and the modified agenda 229 | pop_rule([]) -> {false, []}; 230 | pop_rule([Activation | NewRuleList]) -> 231 | {Activation, NewRuleList}. 232 | 233 | depth_add(RuleList, Rule, Args, Salience, Id) -> 234 | {L1, L2} = lists:splitwith(fun ({_, _, S, _}) -> 235 | S > Salience 236 | end, 237 | RuleList), 238 | L1 ++ [{Rule, Args, Salience, Id} | L2]. 239 | 240 | breadth_add(RuleList, Rule, Args, Salience, Id) -> 241 | {L1, L2} = lists:splitwith(fun ({_, _, S, _}) -> 242 | S >= Salience 243 | end, 244 | RuleList), 245 | L1 ++ [{Rule, Args, Salience, Id} | L2]. 246 | 247 | fifo_add(RuleList, Rule, Args, Salience, Id) -> 248 | RuleList ++ [{Rule, Args, Salience, Id}]. 249 | 250 | get_number_of_activations({_Strategy, RuleList, _NextId}) -> 251 | length(RuleList). 252 | 253 | order(ActionList, Strategy) -> 254 | case Strategy of 255 | breadth -> 256 | lists:sort(fun breadth_order/2, ActionList); 257 | depth -> lists:sort(fun depth_order/2, ActionList); 258 | fifo -> lists:sort(fun fifo_order/2, ActionList); 259 | _Other -> ActionList 260 | end. 261 | 262 | %% @doc define when an Act1 comes before Act2 in breadth strategy 263 | breadth_order(Act1, Act2) 264 | when element(3, Act1) > element(3, Act2) -> 265 | true; 266 | breadth_order(Act1, Act2) 267 | when element(3, Act1) < element(3, Act2) -> 268 | false; 269 | breadth_order(Act1, Act2) 270 | when element(4, Act1) > element(4, Act2) -> 271 | false; 272 | breadth_order(_Act1, _Act2) -> true. 273 | 274 | %% @doc define when an Act1 comes before Act2 in depth strategy 275 | depth_order(Act1, Act2) 276 | when element(3, Act1) > element(3, Act2) -> 277 | true; 278 | depth_order(Act1, Act2) 279 | when element(3, Act1) < element(3, Act2) -> 280 | false; 281 | depth_order(Act1, Act2) 282 | when element(4, Act1) < element(4, Act2) -> 283 | false; 284 | depth_order(_Act1, _Act2) -> true. 285 | 286 | %% @doc define when an Act1 comes before Act2 in fifo strategy 287 | fifo_order(Act1, Act2) 288 | when element(4, Act1) > element(4, Act2) -> 289 | false; 290 | fifo_order(_Act1, _Act2) -> true. 291 | 292 | %%==================================================================== 293 | %% Executor functions 294 | %%==================================================================== 295 | exec(EngineState0, R) -> 296 | {Mod, Fun} = case R of 297 | {{M, F}, _, _, _} -> {M, F}; 298 | _ -> {'_', '_'} 299 | end, 300 | EngineState2 = 301 | case catch execute_rule(EngineState0, R) of 302 | {'EXIT', {function_clause, [{Mod, Fun, _} | _]}} -> EngineState0; 303 | {'EXIT', {function_clause, [{Mod, Fun, _, _Location} | _]}} -> EngineState0; 304 | {'EXIT', Reason} -> 305 | erlang:throw({seresye, {rule_execution, 306 | [R, Reason]}}); 307 | EngineState1 -> EngineState1 308 | end, 309 | after_execution_schedule(EngineState2). 310 | 311 | 312 | %% Fun = {Module, Function} or 313 | %% Fun = fun (...) 314 | execute_rule(EngineState, {{M,F}, Args, X1, X2}) -> 315 | L = length(Args) + 1, 316 | execute_rule(EngineState, {fun M:F/L, Args, X1, X2}); 317 | execute_rule(EngineState, {Fun, Args, _, _}) when is_function(Fun) -> 318 | case proplists:get_value(before_rule, EngineState#seresye.hooks) of 319 | BF when is_function(BF) -> 320 | BF(EngineState, Fun, Args); 321 | _ -> 322 | ignore 323 | end, 324 | Result = apply(Fun, [EngineState#seresye { fired_rule = {Fun, Args} } | Args]), 325 | case proplists:get_value(after_rule, EngineState#seresye.hooks) of 326 | AF when is_function(AF) -> 327 | AF(Result, Fun, Args); 328 | _ -> 329 | ignore 330 | end, 331 | Result#seresye{ fired_rule = undefined }. 332 | 333 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SERESYE - Swarm oriented ERlang Expert SYstem Engine 2 | ==================================================== 3 | 4 | Introduction 5 | ------------ 6 | 7 | SERESYE is a Rete based rules engine written in Erlang, descended 8 | directly from the Eresye project by Francesca Gangemi and Corrado 9 | Santoro. In the following article we will describe how to use the 10 | system. 11 | 12 | As it is widely known, a rule-based system is composed by a 13 | **knowledge base**, which stores a set of *facts* representing the 14 | 'universe of discourse' of a given application, and a set of 15 | **production rules**, which are used to infer knowledge and/or reason 16 | about the knowledge. A rule is activated when one or more facts match 17 | the template(s) given in the rule declaration: in such a case, the 18 | body of the rule contains a code that is thus executed 19 | 20 | In SERESYE, *facts* are expressed by means of Erlang tuples or records, 21 | while rules are written using standard Erlang function clauses, whose 22 | declaration reports, in the clause head, the facts or fact templates 23 | that have to be matched for the rule to be activated and executed. 24 | 25 | For more information about SERESYE please refer to the paper docs directory. 26 | 27 | For more information about rule-based inference engines and expert 28 | systems, you can refer to the book: *S. Russell and 29 | P. Norvig. **Artificial Intelligence: A Modern Approach/2E.** Prentice 30 | Hall, 2003.* 31 | 32 | To write an AI application with SERESYE the following steps have to be 33 | performed: 34 | 35 | 1. Indentify your universe of discourse and determine the facts that 36 | have to be used to represent such a world; 37 | 38 | 2. Indentify the rules that you need and write them by using, e.g. 39 | first-order-logic predicates or even natural language; 40 | 41 | 3. Implement the system by writing your rules as Erlang function 42 | clauses, according to the modality required by SERESYE. 43 | 44 | 45 | The Application: the Domain of Relatives 46 | ---------------------------------------- 47 | 48 | We will design a system able to derive new knowledge using some 49 | inference rules and starting from a small set; as a sample 50 | application, we chose the domain of relatives: we will start from some 51 | base concepts, such as *parent*, *male* and *female*, and then, by 52 | means of a proper set of rules, we will derive the concepts of 53 | *mother*, *father*, *sister*, *brother*, *grandmother* and 54 | *grandfather*. 55 | 56 | According to the list above, we will first derive the facts that will be 57 | used to represent our concepts. Given the set of relationships above, they 58 | will be represented by means of the following facts: 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 |
#ConceptFact / Erlang tuple
1X is male{male, X}
2X is female{female, X}
3X is Y's parent{parent, X, Y}
4X is Y's mother{mother, X, Y}
5X is Y's father{father, X, Y}
6X is Y's sister{sister, X, Y}
7X is Y's brother{brother, X, Y}
8X is Y's grandmother{grandmother, X, Y}
9X is Y's grandfather{grandfather, X, Y}
116 | 117 | Concepts 1, 2 and 3 will be used as a base to derive the other ones. 118 | 119 | Deriving new concepts by means of rules 120 | --------------------------------------- 121 | 122 | #### Concept: mother 123 | 124 | The rule to derive the concept of mother is quite 125 | straightforward: 126 | 127 | if X is female and X is Y's parent then X is Y's mother. 128 | 129 | From the point of view of SERESYE, since knowledge is stored in the 130 | *knowledge base* of the engine, the rule above is translated into the 131 | following one: *if the facts {female, X} and {parent, X, Y} are 132 | *asserted* in the knowledge base, then we assert the fact {mother, X, 133 | Y}. 134 | 135 | The rule *mother* can be thus written as follows: 136 | 137 | %% 138 | %% if (X is female) and (X is Y's parent) then (X is Y's mother) 139 | %% 140 | mother (Engine, {female, X}, {parent, X, Y}) -> 141 | seresye:assert (Engine, {mother, X, Y}). 142 | 143 | 144 | #### Concept: father 145 | 146 | This concept can be easily derived by means of the following rule: 147 | 148 | %% 149 | %% if (X is male) and (X is Y's parent) then (X is Y's father) 150 | %% 151 | father (Engine, {male, X}, {parent, X, Y}) -> 152 | seresye:assert (Engine, {father, X, Y}). 153 | 154 | 155 | #### Concept: sister 156 | 157 | This concept can be expressed by the following rule: 158 | 159 | if Y and Z have the same parent and Z is female, then Z 160 | is the Y's sister. 161 | 162 | The SERESYE rule used to map this concept is: 163 | 164 | %% 165 | %% if (Y and Z have the same parent X) and (Z is female) 166 | %% then (Z is Y's sister) 167 | %% 168 | sister (Engine, {parent, X, Y}, {parent, X, Z}, {female, Z}) when Y =/= Z -> 169 | seresye:assert (Engine, {sister, Z, Y}). 170 | 171 | 172 | Please note the guard, which is needed to ensure that when Y and Z are 173 | bound to the same value, the rule is not activated (indeed this is 174 | possible since the same fact can match both the first and second 175 | 'parent' pattern). 176 | 177 | #### Concept: brother 178 | 179 | Given the previous one, this concept is now quite simple to 180 | implement: 181 | 182 | 183 | %% 184 | %% if (Y and Z have the same parent X) and (Z is male) 185 | %% then (Z is Y's brother) 186 | %% 187 | brother (Engine, {parent, X, Y}, {parent, X, Z}, {male, Z}) when Y =/= Z -> 188 | seresye:assert (Engine, {brother, Z, Y}). 189 | 190 | 191 | #### Concepts: grandmother and grandfather 192 | 193 | The former concept can be expressed by means of the rule: 194 | 195 | if X is Y's mother and Y is Z's parent, then X is Z's 196 | grandmother.* The latter concept is now obvious. 197 | 198 | Both can be implemented using the following SERESYE rules: 199 | 200 | %% 201 | %% if (X is Y's mother) and (Y is Z's parent) 202 | %% then (X is Z's grandmother) 203 | %% 204 | grandmother (Engine, {mother, X, Y}, {parent, Y, Z}) -> 205 | seresye:assert (Engine, {grandmother, X, Z}). 206 | 207 | %% 208 | %% if (X is Y's father) and (Y is Z's parent) 209 | %% then (X is Z's grandfather) 210 | %% 211 | grandfather (Engine, {father, X, Y}, {parent, Y, Z}) -> 212 | seresye:assert (Engine, {grandfather, X, Z}). 213 | 214 | 215 | Instantiating the Engine and Populating the Knowledge Base 216 | ---------------------------------------------------------- 217 | 218 | After writing the rules, we need to: 219 | 220 | - define the rules to seresye 221 | - instantiate the engine; 222 | - populate the knowledge base with a set of initial facts. 223 | 224 | We define the rules to SERESYE by defined a 'rules' attribute at the 225 | start of the module. 226 | 227 | %%% 228 | %%% relatives.erl 229 | %%% 230 | -module (relatives). 231 | 232 | -export([father/3, grandfather/3, grandmother/3, 233 | mother/3, brother/4, sister/4, start/0]). 234 | 235 | -rules([mother, father, brother, sister, grandfather, 236 | grandmother]). 237 | 238 | We continue on to instantiate the engine and popoulate the knowledge 239 | base in the function *start* below: 240 | 241 | start () -> 242 | application:start(seresye) % Only if it is not already started 243 | seresye:start(relatives), 244 | seresye:add_rules(relatives, ?MODULE) 245 | 246 | seresye:assert(relatives, 247 | [{male, bob}, {male, corrado}, {male, mark}, {male, caesar}, 248 | {female, alice}, {female, sara}, {female, jane}, {female, anna}, 249 | {parent, jane, bob}, {parent, corrado, bob}, 250 | {parent, jane, mark}, {parent, corrado, mark}, 251 | {parent, jane, alice}, {parent, corrado, alice}, 252 | {parent, bob, caesar}, {parent, bob, anna}, 253 | {parent, sara, casear}, {parent, sara, anna}]), 254 | ok. 255 | 256 | As the listing reports, creating a new SERESYE engine implies to call 257 | the function *seresye:start/1*, giving the name of the engine to be 258 | created 259 | 260 | Then, we have to add the rules to the engine by using the function 261 | *seresye:add_rule/2*: it takes two arguments, the name of the engine 262 | and a tuple representing the function in the form *{Module, 263 | FuncName}*; obviously the function *Module:FuncName* must be 264 | exported. Function *add_rule* has to be called for each rule that has 265 | to be added; for this reason, the code above has an iteration over the 266 | list of rules written before. 267 | 268 | Finally, we populate the inference engine with a set of sample facts 269 | by giving them, in a list, to the function *seresye:assert/2*. To test 270 | our rules, we considered the relationships in the Figure below and 271 | assert only the facts for *male*, *female* and *parent*. 272 | 273 | Testing the application 274 | ----------------------- 275 | 276 | The final complete code of our AI application is thus the following: 277 | 278 | 279 | %%% 280 | %%% relatives.erl 281 | %%% 282 | -module (relatives). 283 | -export([father/3, grandfather/3, grandmother/3, 284 | mother/3, brother/4, sister/4, start/0]). 285 | 286 | %% 287 | %% if (X is female) and (X is Y's parent) then (X is Y's mother) 288 | %% 289 | mother(Engine, {female, X}, {parent, X, Y}) -> 290 | seresye:assert(Engine, {mother, X, Y}). 291 | 292 | %% 293 | %% if (X is male) and (X is Y's parent) then (X is Y's father) 294 | %% 295 | father(Engine, {male, X}, {parent, X, Y}) -> 296 | seresye:assert(Engine, {father, X, Y}). 297 | 298 | %% 299 | %% if (Y and Z have the same parent X) and (Z is female) 300 | %% then (Z is Y's sister) 301 | %% 302 | sister(Engine, {parent, X, Y}, {parent, X, Z}, {female, Z}) when Y =/= Z -> 303 | seresye:assert(Engine, {sister, Z, Y}). 304 | 305 | %% 306 | %% if (Y and Z have the same parent X) and (Z is male) 307 | %% then (Z is Y's brother) 308 | %% 309 | brother(Engine, {parent, X, Y}, {parent, X, Z}, {male, Z}) when Y =/= Z -> 310 | seresye:assert(Engine, {brother, Z, Y}). 311 | 312 | %% 313 | %% if (X is Y's father) and (Y is Z's parent) 314 | %% then (X is Z's grandfather) 315 | %% 316 | grandfather (Engine, {father, X, Y}, {parent, Y, Z}) -> 317 | seresye:assert (Engine, {grandfather, X, Z}). 318 | 319 | %% 320 | %% if (X is Y's mother) and (Y is Z's parent) 321 | %% then (X is Z's grandmother) 322 | %% 323 | grandmother(Engine, {mother, X, Y}, {parent, Y, Z}) -> 324 | seresye:assert(Engine, {grandmother, X, Z}). 325 | 326 | start () -> 327 | application:start(seresye), 328 | seresye:start (relatives), 329 | seresye:add_rules(relatives, ?MODULE) 330 | 331 | seresye:assert (relatives, 332 | [{male, bob}, 333 | {male, corrado}, 334 | {male, mark}, 335 | {male, caesar}, 336 | {female, alice}, 337 | {female, sara}, 338 | {female, jane}, 339 | {female, anna}, 340 | {parent, jane, bob}, 341 | {parent, corrado, bob}, 342 | {parent, jane, mark}, 343 | {parent, corrado, mark}, 344 | {parent, jane, alice}, 345 | {parent, corrado, alice}, 346 | {parent, bob, caesar}, 347 | {parent, bob, anna}, 348 | {parent, sara, casear}, 349 | {parent, sara, anna}]), 350 | ok. 351 | 352 | Now it's time to test our application: 353 | 354 | 355 | Erlang (BEAM) emulator version 5.5 [source] [async-threads:0] [hipe] 356 | 357 | Eshell V5.5 (abort with ^G) 358 | 1> c(relatives). 359 | {ok,relatives} 360 | 2> relatives:start(). 361 | ok 362 | 3> 363 | 364 | Following the call to function *relatives:start/0*, the engine is 365 | created and populated; if no errors occurred, the rules should have 366 | been processed and the new facts derived. To check this, we can use 367 | the function *seresye:get_kb/1*, which returns the list of facts 368 | asserted into the knowledge base of a given engine: 369 | 370 | 371 | 4> seresye:get_kb(relatives). 372 | [{brother,bob,mark}, 373 | {sister,alice,bob}, 374 | {sister,alice,mark}, 375 | {brother,bob,alice}, 376 | {brother,mark,alice}, 377 | {grandmother,jane,caesar}, 378 | {grandfather,corrado,caesar}, 379 | {grandmother,jane,anna}, 380 | {grandfather,corrado,anna}, 381 | {sister,anna,caesar}, 382 | {brother,caesar,anna}, 383 | {sister,anna,casear}, 384 | {mother,sara,anna}, 385 | {mother,sara,casear}, 386 | {parent,sara,anna}, 387 | {father,bob,anna}, 388 | {parent,sara,casear}, 389 | {father,bob,caesar}, 390 | {parent,bob,anna}, 391 | {father,corrado,alice}, 392 | {parent,bob,caesar}, 393 | {mother,jane,alice}, 394 | {parent,corrado,alice}, 395 | {father,corrado,mark}, 396 | {parent,jane,alice}, 397 | {mother,jane,mark}, 398 | {parent,corrado|...}, 399 | {brother|...}, 400 | {...}|...] 401 | 5> 402 | 403 | The presence of facts representing concepts like *father*, *sister*, 404 | etc., proves that the rules seems to be working as expected. 405 | 406 | We can however query the knowledge base using specific fact templates. 407 | For example, if we want to know who are Alice's brothers, we can use 408 | the function *seresye:query_kb/2* as follows: 409 | 410 | 411 | 6> seresye:query_kb(relatives, {brother, '_', alice}). 412 | [{brother,bob,alice},{brother,mark,alice}] 413 | 7> 414 | 415 | The facts returned conform to the relationships depicted in the figure 416 | above, thus proving that the rules written are really working. 417 | 418 | As the example shows, function *seresye:query_kb/2* takes the engine 419 | name as the first argument, while, for the second parameter, a tuple 420 | has to be specified, representing the fact template to be matched; in 421 | such a tuple, the atom *'_'* plays the role of a wildcard. However, to 422 | specify a more complex matching, a *fun* can be used as a tuple 423 | element; this *fun* has to return a boolean value which indicates if 424 | the element matches the template. For example, to select both Alice's 425 | and Anna's brothers, we can use the following function call: 426 | 427 | 428 | 7> seresye:query_kb(relatives, {brother, '_', fun (X) -> (X == alice) or (X == anna) end}). 429 | [{brother,bob,alice},{brother,mark,alice},{brother,caesar,anna}] 430 | 8> 431 | 432 | 433 | Conclusions 434 | ----------- 435 | 436 | This HowTo not only shows how to use the SERESYE engine to write an AI 437 | application, but also highlights the versatility of the Erlang language: 438 | the characteristics of functional and symbolic programming, together with 439 | the possibility of performing *introspection* of function declaration, 440 | can be successfully exploited for application domains which are completely 441 | new for Erlang but can surely be very interesting. 442 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Swarm oriented ERlang Expert SYstem Engine 4 | --- 5 | 6 | Introduction 7 | ------------ 8 | 9 | SERESYE is a Rete based rules engine written in Erlang, descended 10 | directly from the Eresye project by Francesca Gangemi and Corrado 11 | Santoro. In the following article we will describe how to use the 12 | system. 13 | 14 | A rule-based system is composed of a **knowledge base**, which stores 15 | a set of *facts* representing the 'universe of discourse' of a given 16 | application, and a set of **production rules**, which are used to 17 | infer knowledge and/or reason about the knowledge. A rule is activated 18 | when one or more facts match the template(s) given in the rule 19 | declaration: in such a case, the body of the rule contains a code that 20 | is thus executed 21 | 22 | In SERESYE, *facts* are expressed by means of Erlang tuples or records, 23 | while rules are written using standard Erlang function clauses, whose 24 | declaration reports, in the clause head, the facts or fact templates 25 | that have to be matched for the rule to be activated and executed. 26 | 27 | For more information about SERESYE please refer to the paper docs directory. 28 | 29 | For more information about rule-based inference engines and expert 30 | systems, you can refer to the book: *S. Russell and 31 | P. Norvig. **Artificial Intelligence: A Modern Approach/2E.** Prentice 32 | Hall, 2003.* 33 | 34 | To write an AI application with SERESYE the following steps have to be 35 | performed: 36 | 37 | 1. Identify your universe of discourse and determine the facts that 38 | have to be used to represent such a world; 39 | 40 | 2. Identify the rules that you need and write them by using, e.g. 41 | first-order-logic predicates or even natural language; 42 | 43 | 3. Implement the system by writing your rules as Erlang function 44 | clauses, according to the modality required by SERESYE. 45 | 46 | 47 | The Application: the Domain of Relatives 48 | ---------------------------------------- 49 | 50 | We will design a system able to derive new knowledge using some 51 | inference rules and starting from a small set; as a sample 52 | application, we chose the domain of relatives: we will start from some 53 | base concepts, such as *parent*, *male* and *female*, and then, by 54 | means of a proper set of rules, we will derive the concepts of 55 | *mother*, *father*, *sister*, *brother*, *grandmother* and 56 | *grandfather*. 57 | 58 | According to the list above, we will first derive the facts that will be 59 | used to represent our concepts. Given the set of relationships above, they 60 | will be represented by means of the following facts: 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 |

#

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}

118 | 119 | Concepts 1, 2 and 3 will be used as a base to derive the other ones. 120 | 121 | Deriving new concepts by means of rules 122 | --------------------------------------- 123 | 124 | #### Concept: mother 125 | 126 | The rule to derive the concept of mother is quite 127 | straightforward: 128 | 129 | if X is female and X is Y's parent then X is Y's mother. 130 | 131 | From the point of view of SERESYE, since knowledge is stored in the 132 | *knowledge base* of the engine, the rule above is translated into the 133 | following one: *if the facts {female, X} and {parent, X, Y} are 134 | *asserted* in the knowledge base, then we assert the fact {mother, X, 135 | Y}. 136 | 137 | The rule *mother* can be thus written as follows: 138 | 139 | %% 140 | %% if (X is female) and (X is Y's parent) then (X is Y's mother) 141 | %% 142 | mother (Engine, {female, X}, {parent, X, Y}) -> 143 | seresye:assert (Engine, {mother, X, Y}). 144 | 145 | 146 | #### Concept: father 147 | 148 | This concept can be easily derived by means of the following rule: 149 | 150 | %% 151 | %% if (X is male) and (X is Y's parent) then (X is Y's father) 152 | %% 153 | father (Engine, {male, X}, {parent, X, Y}) -> 154 | seresye:assert (Engine, {father, X, Y}). 155 | 156 | 157 | #### Concept: sister 158 | 159 | This concept can be expressed by the following rule: 160 | 161 | if Y and Z have the same parent and Z is female, then Z 162 | is the Y's sister. 163 | 164 | The SERESYE rule used to map this concept is: 165 | 166 | %% 167 | %% if (Y and Z have the same parent X) and (Z is female) 168 | %% then (Z is Y's sister) 169 | %% 170 | sister (Engine, {parent, X, Y}, {parent, X, Z}, {female, Z}) when Y =/= Z -> 171 | seresye:assert (Engine, {sister, Z, Y}). 172 | 173 | 174 | Please note the guard, which is needed to ensure that when Y and Z are 175 | bound to the same value, the rule is not activated (indeed this is 176 | possible since the same fact can match both the first and second 177 | 'parent' pattern). 178 | 179 | #### Concept: brother 180 | 181 | Given the previous one, this concept is now quite simple to 182 | implement: 183 | 184 | 185 | %% 186 | %% if (Y and Z have the same parent X) and (Z is male) 187 | %% then (Z is Y's brother) 188 | %% 189 | brother (Engine, {parent, X, Y}, {parent, X, Z}, {male, Z}) when Y =/= Z -> 190 | seresye:assert (Engine, {brother, Z, Y}). 191 | 192 | 193 | #### Concepts: grandmother and grandfather 194 | 195 | The former concept can be expressed by means of the rule: 196 | 197 | if X is Y's mother and Y is Z's parent, then X is Z's 198 | grandmother.* The latter concept is now obvious. 199 | 200 | Both can be implemented using the following SERESYE rules: 201 | 202 | %% 203 | %% if (X is Y's mother) and (Y is Z's parent) 204 | %% then (X is Z's grandmother) 205 | %% 206 | grandmother (Engine, {mother, X, Y}, {parent, Y, Z}) -> 207 | seresye:assert (Engine, {grandmother, X, Z}). 208 | 209 | %% 210 | %% if (X is Y's father) and (Y is Z's parent) 211 | %% then (X is Z's grandfather) 212 | %% 213 | grandfather (Engine, {father, X, Y}, {parent, Y, Z}) -> 214 | seresye:assert (Engine, {grandfather, X, Z}). 215 | 216 | 217 | Instantiating the Engine and Populating the Knowledge Base 218 | ---------------------------------------------------------- 219 | 220 | After writing the rules, we need to: 221 | 222 | - define the rules to seresye 223 | - instantiate the engine; 224 | - populate the knowledge base with a set of initial facts. 225 | 226 | We define the rules to SERESYE by defined a 'rules' attribute at the 227 | start of the module. 228 | 229 | %%% 230 | %%% relatives.erl 231 | %%% 232 | -module (relatives). 233 | 234 | -export([father/3, grandfather/3, grandmother/3, 235 | mother/3, brother/4, sister/4, start/0]). 236 | 237 | -rules([mother, father, brother, sister, grandfather, 238 | grandmother]). 239 | 240 | We continue on to instantiate the engine and popoulate the knowledge 241 | base in the function *start* below: 242 | 243 | start () -> 244 | application:start(seresye) % Only if it is not already started 245 | seresye:start(relatives), 246 | seresye:add_rules(relatives, ?MODULE) 247 | 248 | seresye:assert(relatives, 249 | [{male, bob}, {male, corrado}, {male, mark}, {male, caesar}, 250 | {female, alice}, {female, sara}, {female, jane}, {female, anna}, 251 | {parent, jane, bob}, {parent, corrado, bob}, 252 | {parent, jane, mark}, {parent, corrado, mark}, 253 | {parent, jane, alice}, {parent, corrado, alice}, 254 | {parent, bob, caesar}, {parent, bob, anna}, 255 | {parent, sara, casear}, {parent, sara, anna}]), 256 | ok. 257 | 258 | As the listing reports, creating a new SERESYE engine implies to call 259 | the function *seresye:start/1*, giving the name of the engine to be 260 | created 261 | 262 | Then, we have to add the rules to the engine by using the function 263 | *seresye:add_rule/2*: it takes two arguments, the name of the engine 264 | and a tuple representing the function in the form *{Module, 265 | FuncName}*; obviously the function *Module:FuncName* must be 266 | exported. Function *add_rule* has to be called for each rule that has 267 | to be added; for this reason, the code above has an iteration over the 268 | list of rules written before. 269 | 270 | Finally, we populate the inference engine with a set of sample facts 271 | by giving them, in a list, to the function *seresye:assert/2*. To test 272 | our rules, we considered the relationships in the Figure below and 273 | assert only the facts for *male*, *female* and *parent*. 274 | 275 | Testing the application 276 | ----------------------- 277 | 278 | The final complete code of our AI application is thus the following: 279 | 280 | 281 | %%% 282 | %%% relatives.erl 283 | %%% 284 | -module (relatives). 285 | -export([father/3, grandfather/3, grandmother/3, 286 | mother/3, brother/4, sister/4, start/0]). 287 | 288 | %% 289 | %% if (X is female) and (X is Y's parent) then (X is Y's mother) 290 | %% 291 | mother(Engine, {female, X}, {parent, X, Y}) -> 292 | seresye:assert(Engine, {mother, X, Y}). 293 | 294 | %% 295 | %% if (X is male) and (X is Y's parent) then (X is Y's father) 296 | %% 297 | father(Engine, {male, X}, {parent, X, Y}) -> 298 | seresye:assert(Engine, {father, X, Y}). 299 | 300 | %% 301 | %% if (Y and Z have the same parent X) and (Z is female) 302 | %% then (Z is Y's sister) 303 | %% 304 | sister(Engine, {parent, X, Y}, {parent, X, Z}, {female, Z}) when Y =/= Z -> 305 | seresye:assert(Engine, {sister, Z, Y}). 306 | 307 | %% 308 | %% if (Y and Z have the same parent X) and (Z is male) 309 | %% then (Z is Y's brother) 310 | %% 311 | brother(Engine, {parent, X, Y}, {parent, X, Z}, {male, Z}) when Y =/= Z -> 312 | seresye:assert(Engine, {brother, Z, Y}). 313 | 314 | %% 315 | %% if (X is Y's father) and (Y is Z's parent) 316 | %% then (X is Z's grandfather) 317 | %% 318 | grandfather (Engine, {father, X, Y}, {parent, Y, Z}) -> 319 | seresye:assert (Engine, {grandfather, X, Z}). 320 | 321 | %% 322 | %% if (X is Y's mother) and (Y is Z's parent) 323 | %% then (X is Z's grandmother) 324 | %% 325 | grandmother(Engine, {mother, X, Y}, {parent, Y, Z}) -> 326 | seresye:assert(Engine, {grandmother, X, Z}). 327 | 328 | start () -> 329 | application:start(seresye), 330 | seresye:start (relatives), 331 | seresye:add_rules(relatives, ?MODULE) 332 | 333 | seresye:assert (relatives, 334 | [{male, bob}, 335 | {male, corrado}, 336 | {male, mark}, 337 | {male, caesar}, 338 | {female, alice}, 339 | {female, sara}, 340 | {female, jane}, 341 | {female, anna}, 342 | {parent, jane, bob}, 343 | {parent, corrado, bob}, 344 | {parent, jane, mark}, 345 | {parent, corrado, mark}, 346 | {parent, jane, alice}, 347 | {parent, corrado, alice}, 348 | {parent, bob, caesar}, 349 | {parent, bob, anna}, 350 | {parent, sara, casear}, 351 | {parent, sara, anna}]), 352 | ok. 353 | 354 | Now it's time to test our application: 355 | 356 | 357 | Erlang (BEAM) emulator version 5.5 [source] [async-threads:0] [hipe] 358 | 359 | Eshell V5.5 (abort with ^G) 360 | 1> c(relatives). 361 | {ok,relatives} 362 | 2> relatives:start(). 363 | ok 364 | 3> 365 | 366 | Following the call to function *relatives:start/0*, the engine is 367 | created and populated; if no errors occurred, the rules should have 368 | been processed and the new facts derived. To check this, we can use 369 | the function *seresye:get_kb/1*, which returns the list of facts 370 | asserted into the knowledge base of a given engine: 371 | 372 | 373 | 4> seresye:get_kb(relatives). 374 | [{brother,bob,mark}, 375 | {sister,alice,bob}, 376 | {sister,alice,mark}, 377 | {brother,bob,alice}, 378 | {brother,mark,alice}, 379 | {grandmother,jane,caesar}, 380 | {grandfather,corrado,caesar}, 381 | {grandmother,jane,anna}, 382 | {grandfather,corrado,anna}, 383 | {sister,anna,caesar}, 384 | {brother,caesar,anna}, 385 | {sister,anna,casear}, 386 | {mother,sara,anna}, 387 | {mother,sara,casear}, 388 | {parent,sara,anna}, 389 | {father,bob,anna}, 390 | {parent,sara,casear}, 391 | {father,bob,caesar}, 392 | {parent,bob,anna}, 393 | {father,corrado,alice}, 394 | {parent,bob,caesar}, 395 | {mother,jane,alice}, 396 | {parent,corrado,alice}, 397 | {father,corrado,mark}, 398 | {parent,jane,alice}, 399 | {mother,jane,mark}, 400 | {parent,corrado|...}, 401 | {brother|...}, 402 | {...}|...] 403 | 5> 404 | 405 | The presence of facts representing concepts like *father*, *sister*, 406 | etc., proves that the rules seems to be working as expected. 407 | 408 | We can however query the knowledge base using specific fact templates. 409 | For example, if we want to know who are Alice's brothers, we can use 410 | the function *seresye:query_kb/2* as follows: 411 | 412 | 413 | 6> seresye:query_kb(relatives, {brother, '_', alice}). 414 | [{brother,bob,alice},{brother,mark,alice}] 415 | 7> 416 | 417 | The facts returned conform to the relationships depicted in the figure 418 | above, thus proving that the rules written are really working. 419 | 420 | As the example shows, function *seresye:query_kb/2* takes the engine 421 | name as the first argument, while, for the second parameter, a tuple 422 | has to be specified, representing the fact template to be matched; in 423 | such a tuple, the atom *'_'* plays the role of a wildcard. However, to 424 | specify a more complex matching, a *fun* can be used as a tuple 425 | element; this *fun* has to return a boolean value which indicates if 426 | the element matches the template. For example, to select both Alice's 427 | and Anna's brothers, we can use the following function call: 428 | 429 | 430 | 7> seresye:query_kb(relatives, {brother, '_', fun (X) -> (X == alice) or (X == anna) end}). 431 | [{brother,bob,alice},{brother,mark,alice},{brother,caesar,anna}] 432 | 8> 433 | 434 | 435 | Conclusions 436 | ----------- 437 | 438 | This HowTo not only shows how to use the SERESYE engine to write an AI 439 | application, but also highlights the versatility of the Erlang language: 440 | the characteristics of functional and symbolic programming, together with 441 | the possibility of performing *introspection* of function declaration, 442 | can be successfully exploited for application domains which are completely 443 | new for Erlang but can surely be very interesting. 444 | -------------------------------------------------------------------------------- /doc/assets/javascript/lang-xq.js: -------------------------------------------------------------------------------- 1 | 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/], 2 | ["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/], 3 | ["pln",/^[\w:-]+/],["pln",/^[\t\n\r \xa0]+/]]),["xq","xquery"]); 4 | --------------------------------------------------------------------------------