3. Jess Language Basics
Most of the time, you'll write Jess rules in the Jess rule language. If you've never used Lisp, the Jess rule language may look a bit odd at first, but it doesn't take long to learn. The payoff is that it's very expressive, and can express complex logical relationships with very little code.
In this chapter, we'll look at the basic syntax of the Jess language. In subsequent chapters, we'll learn how to define high-level concepts like facts and rules, but here, we'll just be looking at the nuts and bolts.
In this language guide, I'll use an extremely informal notation to describe syntax. Basically strings in <angle-brackets> are some kind of data that must be supplied; things in [square brackets] are optional, things ending with + can appear one or more times, and things ending with * can appear zero or more times. In general, input to Jess is free-format. Newlines are generally not significant and are treated as whitespace; exceptions will be noted.
3.1. SymbolsThe symbol is a core concept of the Jess language. Symbols are very much like identifiers in other languages. A Jess symbol can contain letters, numbers, and the following punctuation: $*=+/<>_?#. . A symbol may not begin with a number; it may begin with some punctuation marks (some have special meanings as operators when they appear at the start of a symbol). Jess symbols are case sensitive: foo, FOO and Foo are all different symbols. The best symbols consist of letters, numbers, underscores, and dashes; dashes are traditional word separators. The following are all valid symbols:
foo first-value contestant#1 _abc
3.2. NumbersJess uses the Java functions parseInt(java.lang.String), parseLong(java.lang.String), and parseDouble(java.lang.String) to parse integer, long, and floating point numbers, respectively. See the documentation for those methods for a precise syntax description. The following are all valid numbers:
3 4. 5.643 5654L 6.0E4 1D
3.3. StringsCharacter strings in Jess are denoted using double quotes ("). Backslashes (\) can be used to escape embedded quote symbols. Note that Jess strings are unlike Java strings in several important ways. First, no "escape sequences" are recognized. You cannot embed a newline in a string using "\n", for example. On the other hand, real newlines are allowed inside double-quoted strings; they become part of the string. The following are all valid strings:
"foo" "Hello, World" "\"Nonsense,\" he said firmly." "Hello, There"
3.4. ListsAnother fundamental unit of syntax in Jess is the list. A list always consists of an enclosing set of parentheses and zero or more symbols, numbers, strings, or other lists. The following are valid lists:
(+ 3 2) (a b c) ("Hello, World") () (deftemplate foo (slot bar))
3.5. CommentsJess supports two kinds of programmer's comments: Lisp-style line comments and C-style block comments. Line comments begin with a semicolon (;) and extend to the end of the line of text. Here is an example of a line comment:
; This is a list (a b c)
/* Here is an example of a list (commented out): (a b c) */
3.6. Calling functions
As in Lisp, all code in Jess (control structures, assignments, procedure calls) takes the form of a function call. There are no "operators"; everything is a function call. However, some functions have names that look like Java operators, and in these cases, they operate much like their Java counterparts.
Function calls in Jess are simply lists. Function calls use a prefix notation; a list whose head is a symbol that is the name of an existing function can be a function call. For example, an expression that uses the + function to add the numbers 2 and 3 would be written (+ 2 3). When evaluated, the value of this expression is the number 5 (not a list containing the single element 5!). In general, expressions are recognized as such and evaluated in context when appropriate. You can type expressions at the Jess> prompt. Jess evaluates the expression and prints the result:
Jess> (+ 2 3)
Jess> (+ (+ 2 3) (* 3 3))
Jess> (printout t "The answer is " 42 "!" crlf)
The answer is 42!
Jess> (batch "examples/jess/hello.clp")
Each of these functions (along with all the others) is described more thoroughly in the Jess function guide.
3.7. VariablesProgramming variables in Jess are identifiers that begin with the question mark (?) character. The question mark is part of the variable's name. The name can contain any combination of letters, numbers, dash (-), underscore(_), colon (:) or asterisk (*) characters. A variable can refer to a single symbol, number, or string, or it can refer to a list. You can assign a value to to a variable using the bind function:
Jess> (bind ?x "The value")
Jess> (bind ?a 123)
3.7.1. Global variables (or defglobals)Any variables you create at the Jess> prompt, or at the "top level" of any Jess language program, are cleared whenever the reset command is issued. This makes them somewhat transient; they are fine for scratch variables but are not persistent global variables in the normal sense of the word. To create global variables that are not destroyed by reset, you can use the defglobal construct.
(defglobal [?<global-name> = <value>]+)
?*a* ?*all-values* ?*counter*
Jess> (defglobal ?*x* = 3)
Jess> (bind ?*x* 4)
Jess> (bind ?*x* 4)
Jess> (set-reset-globals nil)
3.8. Control flow
In Java, control flow -- branching and looping, exception handling, etc -- is handled by special syntax and keywords like if, while, for, and try. In Jess, as we said before, everything is a function call, and control flow is no exception. Therefore, Jess includes functions named if, while, for, and try, along with others like foreach. Each of these functions works similarly to the Java construct of the same name.
3.8.1. A simple loopFor example, a Jess "while" loop looks like this:
Jess> (bind ?i 3)
Jess> (while (> ?i 0) (printout t ?i crlf) (-- ?i))
3 2 1 FALSE
The first argument to while is a boolean expression. The while function evaluates its first argument and, if it is true, evaluates all its other arguments. It repeats this procedure until the first argument evaluates to FALSE; a while loop always returns FALSE.
3.8.2. Decisions and branching
The if function looks like this:
Jess> (bind ?x 1)
Jess> (if (> ?x 100) then (printout t "X is big" crlf) else (printout t "X is small" crlf))
X is small
Again, the first argument is a boolean expression, and the second is always the symbol then. If the expression is true, if will execute the remaining arguments up until it sees the (optional) symbol else. If else appears, then any arguments following it are evaluated if the boolean expression is FALSE.
To write "if-else if" constructs in Jess, you can nest calls to the if function, like this:
Jess> (bind ?x 75)
Jess> (if (> ?x 100) then (printout t "X is big" crlf) else (if (> ?x 50) then (printout t "X is medium" crlf) else (printout t "X is small" crlf)))
X is medium