Table of Contents

Preface

Mosaic

Algebraic structures

Semigroup

Monoid:

Conway's game of life:

Analysis of Java code.

Key concepts

Systems, rewriting rules and strategies.

Command line interpreter.

Domain tree.

Java API – start not with "Hello, world".

Trivial transformation: xyz

Adding own facts database.

Transfer of information throught propositional variables.

More systematic introdution.

Types of terms.

Primitive types.

Lists

Set

Java objects

Propositional variabes.

Complex terms.

Rules

Set of rules

Import

Order of rule application.

Let and Where expressions.

Systems

Domains

What next



Preface

TermWare is symbolic programming engine designed to be embedded in Java applications. Symbolic computations in TermWare are based on paradigm of rewriting rules with actions that can gracefully combine logical and imperative programming styles. In this step-by-step guide the main elements of the TermWare language and TermWare Java API are described .

Other useful documents are:

Mosaic

Some high-level examples illustrating TermWare language are given here. Without going deep into detailed explanations, we will help you to make the first impression using just a few lines of code. More or less systematic introduction will begin in the next chapter.


Algebraic structures

Semigroup

Semigroup is an entity that has associative operation.


domain(algebra,
system(SemiGroup,default,
ruleset( ($x*$y)*$z -> $x*($y*$z) ),
FirstTop)
);

Monoid:

And monoid is a semigroup with identity element.


domain(algebra,
system(Monoid,default,
ruleset(
import(SemiGroup),
$x*One -> $x,
One*$x -> $x
),
FirstTop)
);

It is time to show something more vital:

Conway's game of life:

domain(examples,
system(Life1,
javaFacts(Life1DB,"ua.kiev.gradsoft.TermWareDemos.Life.Life1Facts"),
ruleset(
# $T - set of pairs to test.
{l($i,$j):$T} [ n($i,$j)==3 ] -> $T [ putCell($i,$j) ],
{l($i,$j):$T} [ n($i,$j)==2 ] -> $T
[ existsCell($i,$j) ? putCell($i,$j) : removeCell($i,$j)] ,

{l($i,$j):$T} [ n($i,$j)>3||n($i,$j)< 2 ] -> $T [ removeCell($i,$j) ],
{ } -> checkEmpty($T) [ [showGeneration(), generateNextTestSet($T) ] ],

checkEmpty({$x:$Y}) -> { $x:$Y },
checkEmpty({}) -> END

),
FirstTop)
);


Or something useful :

Analysis of Java code.


This example looks for a typical code error such as ignoring of exceptions in the code matching the following pattern:


try {
.....
}catch(...)
{ }

The following system is used:


domain(examples,
system(JECheck,
javaFacts(JECheckDB,"ua.kiev.gradsoft.TermWareDemos.jsa.JECheckFacts"),
ruleset(
Catch($formalParameter, Block([]))->PROBLEM
[found("empty cath statement")]
),
BottomUp
) );

Now, as promised, we will turn to a more systematic description of TermWare. In the next chapter I will illustrate the basic concept of language with examples, but still leaving the low-level details for later.

Key concepts

In this chapter necessary basis is given, so then we will pass to chapter 3 where the most interesting begins.

Systems, rewriting rules and strategies.

Thus, the main object in TermWare is the term system. What it is : in terms of logic, it is a quartet <Name, Ruleset, Facts, Strategy> where :

Q: Why do we need this system?

A: For interpretation of rewriting rules.

Q: And what is rewriting rule ?

A: In the elementary case this expression of type x-> y which rewrite a term x in y.

Q: And what is a term?

A: It is a symbolical expression. The concept of a term has come from mathematical logic where next definition is accepted:

In fact, any symbolic expression that can be written on paper can be expressed as a term. In general, TermWare follows this definition, adding objects of primitive types (such as strings, numbers) and encapsulated Java objects to the set of basic terms. For Java programmers, term is a class inherited from Term


Using the functional notation of type i(x1 ... xn) is not always convenient, therefore in TermWare exists some syntax sugar:

Let's return to rewriting rules: so, a simple rewriting rule looks like a-> b (or rule(a, b)). Now we will rewrite a term matched with a in a term, matched with b according to it. Propositional variables xi play a special role in substitution process: they acts as "empty places" in rewriting, i.e. during matching with left part of rule we fill their values and later we substitute these values in the result. Example:

In TermWare, propositional variables looks as identifiers, started with dollar sign ($x, $myVariable). Rule from previous example can be written as: p($x,$y) -> q($y,$x) .

This process is called unification, the details can be found in any book on mathematical logic.

What is the main difference between programming of rewriting rules and traditional imperative style that we can work with abstract symbols rather than constructive predefined objects. Let's recollect school and show some rules for symbolical differentiation:


Applying those rules to term diff(x*y+x+1,x) we receive 1*y+x*diff(y,x)+diff(1,x). We can remember, that the derivative of the constants will be zero.

This is a rule with condition -- we apply the rule only if the condition is true. With help of just introduced rule, our term will be redused to 1*y+x*diff(y,x)+0. Incidentally, differential on a constant is meaningless:

This is a rule with condition and action – after performing of substitution we evaluate actions, which is listed in square brackets after the result pattern.

Hope, now it is clear what the rewriting rules is. Set of rules is denoted as ruleset(r1 ... rn). Let's write our rules of differentiation as term system:

domain(examples,
system(Diff,default,
ruleset(
diff($a+$b,$x) -> diff($a,$x)+diff($b,$x) ,
diff($a-$b,$x) -> diff($a,$x)-diff($b,$x) ,
diff(+$a,$x) -> diff($a,$x) ,
diff(-$a,$x) -> - diff($a,$x) ,
diff($a*$b,$x) -> diff($a,$x)*$b+$a*diff($b,$x) ,
diff($x,$x) -> 1 ,
diff($a,$x) [ isInt($a) || isFloat($a) ] -> 0,
diff($a,$x) [ isInt($x) || isFloat($x) ] -> ERROR
[ println("x must not be constant") ]
),
BottomUp
) );


The first, what is possible to pay attention to - for some reason the system is entered in domain. Domains are tools for the organization of information, similarly to packages in Java or namespaces in C++. The name of the system is Diff, full name is examples::Diff. Now it is left to understand what is default and BottomUp. In short words, it is a name of a facts DataBase, and a name of strategy. And here is explanation:


System definition is a term system(n,f,r,s), where:

domain(algebra,  
system(Monoid,default,
ruleset(
import(SemiGroup),
$x*One -> $x,
One*$x -> $x
),
FirstTop)
);


As entertainment we can say a word about mathematics behind: so we see that exists different strategies, some from ones are good, some (as Top) – not so good, and that exists set of rules, which reduce one term to another. Rulesets, which transform term A to term B with any good strategy are named as Neter rulesets, or confluent term rewriting rules. Note, that in TermWare rules can be non-conflued and result can depend not only from order of application, but also from external events.


Now we can do somethings by hands. For example, put our systems for differentiation in file Diff.def in directory $TermWareRoot/systems/examples, start TermWare.cmd.sh and type there examples::Diff.diff(x+x*2,x)

Command line interpreter.


So, command line interpreter, which named TermWare.cmd.sh or TermWare.cmd.bat, in depend on platform and situated in directory $TermWareRoot/bin.

Typical seance:


01:TermWare>let x->y

02:true

03:TermWare>x

04:y

05:TermWare>p(x)

06:p(y)

07:TermWare>let p($x,$y) -> $x+$y

08:true

09:TermWare> p(3,2)

10:5

11:TermWare> p(3,x)

12:3+y

13:TermWare>subst("aaab","a+","b")

14:subst("aaab","a+","b")

15:TermWare>String.subst("aaab","a+","b")

16:"bb"

17:TermWare>setProperty(debug,true)

18:true

19:TermWare>x

20:ua.kiev.gradsoft.TermWare.strategies.TopDownStrategy: t=x

21:ua.kiev.gradsoft.TermWare.utils.RuleTransformer: apply rule, term=x

22:rule=[x->y]

23:ua.kiev.gradsoft.TermWare.utils.RuleTransformer:substitution=[]

24:ua.kiev.gradsoft.TermWare.utils.RuleTransformer:result=y

25:ua.kiev.gradsoft.TermWare.strategies.TopDownStrategy: t=y

26:ua.kiev.gradsoft.TermWare.strategies.TopDownStrategy: t=y

27:ua.kiev.gradsoft.TermWare.strategies.TopDownStrategy: t=y

28:y

 


Note the following:

Domain tree.

Once we have started to tell about predefined systems and names, let's closer look on naming of systems. So, TermWare provide mechanism for hierarchical naming – domains, similar to namespaces in C++ or packages in Java.

We briefly mentioned that if the definition of system is given by domain(D,system(n,f,r,s)) then full name of system is given by D::n (which is shortcut for _name(D,n)). 'domain' is like a 'subject area'. Different systems are packaged in different subject areas. Of course, domains can be . If termware interpreter found a complex name, used in context of system, then it search in OS directory tree file with path, where directories are named as domains, files with extension .def contains system with appropriate names. I. e. all as with java packages and classes. Starting points for search (similar to java classpath) can be passed to termware using java property TermWare.path or set programmatic from Java with help of methods of TermWareInstance. (I. e. TermWare.getInstance() will return us default instance, then we can use Instance API). It is also possible to set TermLoader callback interface, which will search for system definitions from alternative sources (such as relational database) instead file system.

Java API – start not with "Hello, world".

So, let's start some programming. The first trick – a classical example (output of "Hello, world" to screen) does not have any action semantics (we just do not have the facility for analysis), so it is not possible to create rules.

Trivial transformation: xyz

What except "Hello, world" can be simple ? For example, next ruleset:


ruleset(
x->y,
y->z
)


Will transform atom x to atom y. So, let's write a program which would create such system and apply one to atom x:



01 package ua.gradsoft.termwareexamples;
02
03 import ua.gradsoft.termware.*;
04 import ua.gradsoft.termware.strategies.*;
05
06
07 /**
08 *Example, which illustrate TermWare using.
09 * let's create system and transform x into z.
10 * @author Ruslan Shevchenko
11 */
12 public class Example1 {
13
14
15 /**
16 *@param args the command line arguments
17 */
18 public static void main(String[] args)
19 {
20 try{
21
22
23
24 TermWare.getInstance().init(args);
25
26 ITermRewritingStrategy strategy=new FirstTopStrategy();
27
28
29 IFacts facts=new DefaultFacts();
30
31
32 TermSystem termSystem=new TermSystem(strategy,facts,TermWare.getInstance());
33
34
35 termSystem.addRule("x->y");
36 termSystem.addRule("y->z");
37
38 Term inputTerm=TermWare.getInstance().getTermFactory().createAtom("x");
39 Term outputTerm=termSystem.reduce(inputTerm);
40
41 if(outputTerm.getName().equals("z")){
42 System.out.println("success");
43 }else{
44 System.out.println("failure");
45 }
46 }catch(TermWareException ex){
47 System.err.println("eror:"+ex.getMessage());
48 ex.printStackTrace();
49 }
50
51 }
52
53 }

Lines 3-5 – import declarations for TermWare packages. Practically any program, which use TermWare needs in such packages.

Lines 26-28 – Create strategy "FirstTop".

Lines 29-31 – Create empty facts database. Class DefaultFacts is used.

Line 33 – At last, create term system, passing to constructor program environment, strategy and facts as arguments.

Lines 35-36 – add rules. That's all. System is ready to work.

Now we want put some term to reduce with help of our system.

Line 38 – create atom x with the help of method of interface ITermFactory. (see API )

Line 39 – reduce term. In outputTerm receive result of reduce.

Check that we receive 'z'.

Check for exceptions in try/catch block and handle one at lines 41-46. All TermWare exceptions is inherieted from TermWareException (see API ) .


Let's mark what we found:



Adding own facts database.

Now most interesting – how to handle data which come to us from external world. In termware external environment is represented as facts database: Java class which can be created by developer. Let's try simple example: environment which have simple boolean value.

Let's add reading of boolean value into our system, for example:

        x [ getW() ] -> w,
x [ !getW() ] -> y,
y->z

I.e. External environment provide some information with method getW(), and we transform x to w or y conditionaly, in dependency from getW().

Facts database:


package ua.gradsoft.termwareexamples;

import ua.gradsoft.termware.DefaultFacts;
import ua.gradsoft.termware.TermWareException;


/**
* example of facts database.
*/
public class Example2Facts extends DefaultFacts {

public Example2Facts() throws TermWareException
{}

public boolean getW() { return w_; }

public void setW(boolean w) { w_=w; }

private boolean w_=false;
}

And use one:



package ua.gradsoft.termwareexamples;

import ua.gradsoft.termware.IEnv;
import ua.gradsoft.termware.ITermRewritingStrategy;
import ua.gradsoft.termware.Term;
import ua.gradsoft.termware.TermFactory;
import ua.gradsoft.termware.TermSystem;
import ua.gradsoft.termware.TermWare;
import ua.gradsoft.termware.TermWareException;
import ua.gradsoft.termware.strategies.FirstTopStrategy;

/**
*Example, which illustrate TermWare using.
* let's create system and transform x into y or w,
* depend from value in facts database.
* @author Ruslan Shevchenko
*/
public class Example2 {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
try {
TermWare.getInstance().init(args);
ITermRewritingStrategy strategy=new FirstTopStrategy();
Example2Facts facts=new Example2Facts();

TermSystem termSystem=new TermSystem(strategy,facts,TermWare.getInstance());

termSystem.addRule("x [ getW() ] -> w");
termSystem.addRule("x [ !getW() ] -> y");
termSystem.addRule("y->z");

TermFactory termFactory=TermWare.getInstance().getTermFactory();

Term inputTerm=termFactory.createAtom("x");
Term outputTerm=termSystem.reduce(inputTerm);

if (outputTerm.getName().equals("z")) {
System.out.println("success");
}else{
System.out.println("failure");
}

facts.setW(true);
outputTerm=termSystem.reduce(inputTerm);

if (outputTerm.getName().equals("w")) {
System.out.println("success");
}else{
System.out.println("failure");
}

}catch(TermWareException ex){
System.err.println("eror:"+ex.getMessage());
ex.printStackTrace();
}
}

}


So, facts database is just a java object, which methods can be called from rules of term system.

Recall that in general rule have look x[condition] -> y[action]. In previous example it was reading of value in condition.


        x    [ getW() ] -> w,
x [ !getW() ] -> y,
y->z [ setW(true) ]

So, during evaluation of rule y -> r setW() will be called. Type argument will be transformed from Term to appropriative Java type if this is possible.

Yet one note about syntax. Previous example can be rewriten as:

        x    [ getW() ] -> w
!-> y,
y->z [ setW(true) ]
(i.e. !-> part of rule is used, when conditions is not evaluated to true)

Transfer of information throught propositional variables.

Our rules framework will be very limited in use withou information excahnge between declarative part (rules) and Java part (facts). So, how to pass something to java method – throught propositional variables, just passing ones as arguments.

Example:

Let's look on next ruleset:


access($x,$page) [ allowed($x,$page) ] -> true [ go($page) ],
 access($x,$page) !-> false 
[ notice(access atempt:\",$x,$page) ]

Which implements access to some web page.

Facts :


package ua.gradsoft.termwareexamples;

import java.util.HashMap;
import java.util.HashSet;
import ua.gradsoft.termware.DefaultFacts;
import ua.gradsoft.termware.IEnv;
import ua.gradsoft.termware.TermWareException;



/**
* example of facts database.
*/
public class Example3Facts extends DefaultFacts {

public Example3Facts() throws TermWareException
{ super(); }

public boolean allowed(String name,String page)
{
HashSet<String> hs=acl_.get(name);
if (hs==null) return false;
return hs.contains(page);
}

public void go(String page)
{ System.out.println("go:"+page); }

public void notice(String msg,String name,String page)
{ System.out.println(msg+"("+name+","+page+")"); }

private HashMap<String,HashSet<String>> acl_;

{
acl_=new HashMap<String,HashSet<String>>();
HashSet<String> p1s=new HashSet<String>();
p1s.add("page1");
p1s.add("page2");
p1s.add("page3");
HashSet<String> p2s=new HashSet<String>();
p2s.add("page1");
acl_.put("mark",p1s);
acl_.put("lyuds",p1s);
acl_.put("guest",p2s);
}
}

And some code to use:



package ua.gradsoft.termwareexamples;

import ua.gradsoft.termware.ITermRewritingStrategy;
import ua.gradsoft.termware.Term;
import ua.gradsoft.termware.TermFactory;
import ua.gradsoft.termware.TermSystem;
import ua.gradsoft.termware.TermWare;
import ua.gradsoft.termware.TermWareException;
import ua.gradsoft.termware.strategies.FirstTopStrategy;

/**
* Example of passing values of propositional variables into facts
*/
public class Example3 {

/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
try {
TermWare.getInstance().init(args);
ITermRewritingStrategy strategy=new FirstTopStrategy();

Example3Facts facts=new Example3Facts();
TermSystem termSystem=new TermSystem(strategy,facts,TermWare.getInstance());
termSystem.addRule(
"access($x,$page) [ allowed($x,$page) ] -> true [ go($page) ]");
termSystem.addRule(
"access($x,$page) [ !allowed($x,$page) ] -> false [ [go(deny), “+
“ notice(\"access atempt:\",$x,$page)] ]");

TermFactory termFactory=TermWare.getInstance().getTermFactory();

Term inputTerm=termFactory.createTerm("access",
termFactory.createString("mark"),
termFactory.createString("page1"));
Term outputTerm=termSystem.reduce(inputTerm);

if (outputTerm.isBoolean()) {
if (outputTerm.getBoolean()) {
System.out.println("success");
}else{
System.out.println("failure");
}
}else{
System.out.println("failure");
}
}catch(TermWareException ex){
System.err.println("eror:"+ex.getMessage());
ex.printStackTrace();
}

}

}


In previous example we show, how to pass information from ruleset to facts. Now let's do reverse thing: set some propositional variable in rules from facts database.

Let slightly complicate the system from previous example: introduce the concept of group to which user belong.



access($name,$page)-> groupAccess($group,$name,$page)
[getGroup($group,$name)],

groupAccess($group,$name,$page)
[allowedGroup($group,$page)] -> true [ go($page) ]
!-> false
[notice("access atempt:",$name,$page)]

In first rule we determinate user group, in second – check group access. So, what's new: we must fill propositional variable $group from method of facts database. Implementation DefaultFacts allows us to define methods with first argument of type TransformationContext, from which we can use API of current transformation state. Method TransformationContext.getCurrentSubstitution() return us current substitution of propositional variables, in which we can add relation between variable and value with help of put method.



package ua.gradsoft.termwareexamples;

import java.util.HashMap;
import java.util.HashSet;
import ua.gradsoft.termware.DefaultFacts;
import ua.gradsoft.termware.Substitution;
import ua.gradsoft.termware.Term;
import ua.gradsoft.termware.TermFactory;
import ua.gradsoft.termware.TermWare;
import ua.gradsoft.termware.TermWareException;
import ua.gradsoft.termware.TransformationContext;
import ua.gradsoft.termware.exceptions.AssertException;
/**
* example of facts database.
*/
public class Example4Facts extends DefaultFacts {
public Example4Facts() throws TermWareException
{ super(); }

public void getGroup(TransformationContext ctx, Term groupTerm, String name)
throws TermWareException
{
if (!groupTerm.isX()) {
throw new AssertException(
"first argument of getGroup must be propositional variable");
}
Object o=groups_.get(name);
TermFactory termFactory = TermWare.getInstance().getTermFactory();
Term value = ((o==null) ? termFactory.createNil() :
termFactory.createString((String)name) );
Substitution s = ctx.getCurrentSubstitution();
s.put(groupTerm,value);
}

public boolean allowedGroup(String groupName,String page)
{
Object o=acl_.get(groupName);
if (o==null) return false;
HashSet hs=(HashSet)o;
return hs.contains(page);
}

public void go(String page)
{ System.out.println("go:"+page); }

public void notice(String msg,String name,String page)
{ System.out.println(msg+"("+name+","+page+")"); }
private HashMap<String,HashSet<String>> acl_;
private HashMap<String,String> groups_;
{
groups_=new HashMap<String,String>();
groups_.put("mark","admin");
groups_.put("lyuds","admin");
groups_.put("guest","guest");
acl_=new HashMap<String,HashSet<String>>();
HashSet<String> p1s=new HashSet<String>();
p1s.add("page1");
p1s.add("page2");
p1s.add("page3");
HashSet<String> p2s=new HashSet<String>();
p2s.add("page1");
acl_.put("admin",p1s);
acl_.put("guest",p2s);
}
}


Setting of propositional variable is shown in method getGroup. At the beginning of method we check, that first argument is a propositional variable. At the end of the method we add relation between groupTerm and value in current substitution.

Make our example closer to reality, usually the user can belong to several different groups. Modify previous example for case, when facts database returns us list of groups.




access($name,$page) -> groupsAccess($group,$name,$page)
[getGroups($group,$name)],

groupsAccess([$group:$rest],$name,$page) -> groupAccess($group,$name,$page)||
groupsAccess($rest,$name,$page),

groupsAccess([],$name,$page) -> deny($name,$page),

groupAccess($name,$group,$page)
[allowedGroup($group,$page)] -> true[ go($name,$page) ]
!-> deny($name,$page),

true || $x -> true ,
$x || true -> true ,

deny($name,$page) -> false [ notice("access attempt") ],

I.e. rules for groupsAccess keep checking of list of groups to checking of one group. Note, that evaluation of this ruleset is depend from strategy – if we reduce at first the main term, then rule


deny($name,$page) -> false [ notice("access attempt") ]


will be applied only after redusing of all disunctions.


Now getGroups must return list of groups instead one group. What is list – recall, that [x,y] является is a shortcut to cons(x,cons(y,NIL)) , NIL and empty lists are synonims. I.e. lists in TermWare are identical to LISP ones. Generation of such list on Java is shown below:



public void getGroups(TransformationContext ctx, Term groupTerm, String name)
throws TermWareException
{
TermFactory termFactory=TermWare.getInstance().getTermFactory();
if (!groupTerm.isX()) {
throw new AssertException(
"first argument of getGroups must be a propositional variable");
}
String[] groups=groups_.get(name);
if (groups==null) {
ctx.getCurrentSubstitution().put(groupTerm, termFactory.createNIL(), true);
}else{
Term retval=termFactory.createNil();
String[] groups=(String[])o;
for(int i=0; i< groups.length; ++i) {
retval=termFactory.createTerm("cons",
termFactory.createString(groups[i]),
retval);
}
ctx.getCurrentSubstitution().put(groupTerm, retval);
}
}


i.e. we generate cons entries with help of termFactory.createTerm

More systematic introdution.

This sections follows the semantics description, but without mathematical details, that it is possible to read one fluently.

Types of terms.

Primitive types.

Integer types with different dimensions (Short, Int, Long, BigInteger), floating-point values (Double, Float); fixed-point values (BigDecimal), strings (String), symbols (Char), logical values (Boolean), atoms (Atom) and special dedicated term NIL.


In general anything unusual. If you notice, a list of primitive types in termware coincides with a simular list in Java language.

Appropriate lexemes also the same as in Java. One difference – exists special notation for big integer and big decimal numbers.

Types of numbers with appropriated suffixes on lexems are shown in next table:


Type

Java Type

Suffix

Example

Integer

Integer


1

Long integer

Long

L

10000000000L

Big Integer

BigInteger

B

1000000000022222B

Double precision floating point number

Double


1.1


Double

D

1.1D

Ordinary precision floating point number.

Float

F

1.1F

Decimal point number

BigDecimal

B

1.1B


Lists

Lists are just syntax sugar for complex terms: [x] is a shortcut for cons(x,NIL), [x,y] for cons(x,[y]) and so on. [ ] (empty list) and NILS are synonyms.

For matching of list also exists special syntax: [ x : y ] is a shortcut for cons(x,y) and during matching of pattern [ $x: $y] with list, for example, [1,2,3,4] $x will be matched to 1 and $y to [2,3,4] .


Let's look on next ruleset:


sum([$x:$y]) -> $x + sum($y)
 sum([]) -> 0

sum([1,2]) will be reduced to 1+sum([2]), which will be reduced to 1+2+sum(NIL). sum(NIL) is 0 by second rule, so all will reduced to 1+2+0 = 3.

Set

Set {x1, .. xN} is a shortcut for term set(x1...xN) . I.e. { 1, 2, 3, 4, 5 } means set(1,2,3,4,5) , { 1, 2 } - set(1,2) , and { } - set() .

The characteristic feature of set is that element can be present in set only once, i.e. { 1,2,2,3 } is the same as { 1,2,3}.

As with lists, exists special syntax for set-pattern: { x : y } (or set_pattern(x,y)) which can matched with set, where x one of elements of set, y – set with all rest elements.


Java objects


Arbitrary Jaba object can be encapsulated in terms, taking part in unifications and substitutions. From Java side such term can be created with method TermFactory.createJTerm.

For matching of java objects of some class exists special syntax: @class("class-name",$v).

For example next rule:

@class("java.io.StringStream",$x) -> true

will transform to true all instances of java.io.StringStream.

Propositional variabes.

Denoted as $x, $y, ... etc. Scope – rule. Means "places" where terms are substituted during rules evaluations. On Java level represented as instances of class Xterm (API). Internally identified by number: minFv()=maxFv()="number identified this variable".

Complex terms.

And at last – complex terms (i.e. all other). If a1,... an – terms, f – identifier, than f(a1...an) – term. 'f' will be called functional symbol, a1...an – subterms. Number of subterms called arity. In general, all complex structures is a complex term (may be decorated by syntax sugar).

Alse exists special syntax for mathing: pattern f($x) matched with term with arity 1 and functioanal symbol f, f($x,$y) – term with arity 2 and so on.

Another unusial and quite powerfull syntax for matching: $f..($x)

$f..($x) matched with any complex term, $f will be associated with functional system. (atom with the name same as functional symbol), $x – with list of arguments. f..($x) will be succesfully matched with f(1) [ x <- [1] ) and with f(1,2) [ x <- [1,2]) and $f..(NIL) will be associated with any 0-arity functions.

Rules

Rule in simplified general case is a expression like:


Evaluation follows next sequence of events: try match term with pattern x, if matching failed – rule does not applied, otherwise – check condition. If condition is evaluated to true – rewrite x to y and execute action, otherwise – check condition condition1, if true – rewrite x to y1 and do action1, otherwise next with y2 and so on. If none from conditions is evaluated to true – rewrite to yLast and do action actionLast. Of course in real life most rules are abbreviated.

Yet one question – how exactly conditions are evaluated and actions are performed ? It's depend from class of facts database, associated with system. In DefaultFacts(see API) implemented the following algorithm:


Conditions:

Actions: the overall algorithm is the same as for conditions, difference is in ignoring return values and few actions in lists are transformed consistently.


Also note, that rule can be decorated by let and where parts.


Set of rules


Several rules can be combined in rulesets.

Example were given in abundance above, now let's show something new:



ruleset(
import(general)

# handle new invoice and set one to paid if possible.
@class("ua.gradsoft.termwaredemos.invoicing.Invoice", $invoice)
[ ! $invoice.isPayed()
&&
$invoice.getCustomer().getAccountBalance()
-
$invoice.getAmount()
+
$invoice.getCustomer().getCreditLimit()
> 0
]
-> true
[ $invoice.getCustomer().decrementAccount($invoice.getAmount())
&&
$invoice.setPayed(true)
],

# set credit limit in depend from summary payments.
@class("ua.gradsoft.termwaredemos.invoicing.Customer", $customer)
[ $customer.getAccountBalance() > 0
&& $customer.getSummaryPayments() > 2000
] -> true [ $customer.setCreditLimit(500) ]
)



These rules are working on Java objects, the first looks at the invoices and bills from the customer account; second set credit limit for customer.

Classes for Customer and Invoices can be defined as follows:



/**
*The definition of customer
*/
public class Customer {


/** Get the name of this customer. */
public String getName()
{
.......
}

/** Get the credit limit of this customer. */
public BigDecimal getCreditLimit()
{
........
}

/** set the credit limit of this customer **/
public void setCreditLimit(BigDecimal creditLimit)
{
........
}

/** get current account balance **/
public BigDecimal getAccountBalance()
{
.........
}

public void incrementAccountBalance(BigDecimal amount)
{
.......
}

public void decrementAccountBalance(BigDecimal amount)
{
.......
}

public BigDecimal getSummaryPayments()
{
.........
}

}



I.e. using class can be POJO or incapsulate database queries.

$customer.getAccountBalance() is works, since this is shortcut for apply($customer,getAccountBalance()). In general system apply for Java object is evaluated as appropriative methods call. After variable substitution we have java object in $customer, so all is works as expected.


Import


Also note the phrase import(general). Rules can be imported from other systems, this mechanism is very similar to the mechanism of inheritance in object-oriented programming. Another import options is importTransformed where we can change rules during import.

Order of rule application.

The exact order of rules evaluation is depend from strategy. But exists one general rule for conflicts resolution, which can be described as "at first look at more partial cases", i.e. when one expression can be reduced by two different rules (let r1 and r2) and one from rules have more partial pattern, then other (let r1), then r1 is applied first.


For example, next ruleset:


ruleset(
p(q($x)) -> A,
p($x) -> B
)


Will return A for p(q(1)) and B for p(1). Yet one example:


ruleset(
p($x,$x) -> same,
p($x,$y) -> different
)


will return same for p(a,a) and different for p(a,b).


Let and Where expressions.

When we said, that the sequence of reductions is defined by strategy, we said not all: exists kinds of expressions, which is reduced in special way. This is so-called let and where expressions. Consider a phrase:

let (v1 <- e1, v2 <- e2,... vN <- eN) x

Reduction of this expression is hapenning in the next way: first e1 .. eN are redused and received values are associated with propositional variables v1 ... vN, then ones are substituted in x, then substituted x is redused.

where expressions diffrers from let only by syntax – next expression is just a synonim:

x where (v1 <- e1, v2 <- e2,... vN <- eN)


Also exists specialized syntax for using let and where in rules:

x [ condition ] where (v1 <- e1, v2 <- e2,... vN <- eN) -> y | [cn1] -> y1 | ... !-> yK

or alternate form:

x let (v1 <- e1, v2 <- e2,... vN <- eN) [ condition ] -> y | [cn1] -> y1 | ... !-> yK

then after successfull matching of x, at first e1 ... eN are redused and substituted in all conditions and right patterns of rules.



Systems


At last – system. This is a name + facts database + set of rules + strategy.

Name – any identifier.

Facts database – Java class, which implements interface IFacts. Facts databases can be called by name after registering in TermWare instance;


IFacts facts = new MyFacts();
TermWare.getInstance().addFacts("MyFacts",facts);


after this it is possible to write system definition as: system(MySystem, MyFacts, ruleset(....), MyStrategy)


This approach is usefull when we need to create more that one system, works one one facts database. Note, that TermWare.getInstance().addFacts() will create top level name in hierarchical name system. For creation of nested name you can addFacts() in class Domain.


Also you can use load classes of facts database by name, i. e. In system definition instead name of registered facts write expression loadFacts(name,className), in this case TermWare will load class with name className (of course one must implements Ifacts), then will create instance object, bind one with name name and associate loaded system with received instance object.


The same with strategies – it is possible to use ones by name (exists predefined strategies with names 'FirstTop' and 'BottomTop') or load own strategy with help loadStrategy expression in system definition.

Domains


Systems can be grouped into domains. What is domain – just analog of namespace in С++ or package in Java. TermWare can load system definitions from file system or current class loader using directory structure simular to stucture of domain names. Set of starting points for search can be passed to termware as Java property termware.path. Of course, it is possible to set one from program: TermWare.getInstance().getTermLoader().addSearchPath(String path).


Small illustation:


Let we have two subdirectories in termware.path: A and B.

In A situated file X.def, with next content:


domain(A,
system(X,MyFacts,
ruleset(
a($x) -> A,
b($x) -> B
),
)
)


And in B we also have X.def:


domain(B,
system(X,MyFacts,
ruleset(
a($x) -> B,
b($x) -> C
),
)
)


Then call of apply(A::X,a(1)) will return A, call of apply(B::X,a(1)) – B.

So, full name is denoted as C1::C2::...::Cn and means (for systems) system Сn, which is situated in domain C(n-1) ... which is situated in domain С1. Without syntax sugar this can be expressed as _name(C1,..Cn). By the way, note that apply(x,y) also can be writted in short form (with dot) i.e. apply(A::X,a(1)) is equal to A::X.a(1). Such notation is used quite frequently. You can pay some attention to class Domain (API), which provide API to contents of domains, loaded in system. Also notes, when sometimes we want keep systems definitions somewhere else than file system, in such cases TermWare allows to write own implementation of callback interface (TermLoader) and register one in instance of termware engine.



What next


Links to reference documentation (the same as in preface):

Also you can look fot articles on GradSoft site for description of semantics in more readable form then formal document. Next article: TermWare: A Rewriting Framework for Rule-Based Programming Dynamic Applications can be used instead introduction.

Also look at examples, which situated in demo subdirectory of installation. Exists few non-trivial open source projects, which use TermWare, for example – JavaChecker (http://www.gradsoft.ua/products/javachecker_eng.html), it is possible to determinate typical patterns of usage by reading source code.

Hope, this introduction was interesting. With any questions about this document, please, contact author – Ruslan@Shevchenko.Kiev.UA.

Regards.