Edit me

Arguments define the values of atoms, functors, and constraints. They can be constants, variables, record constructors, ADT constructors, type conversions,aggregators, and invocations to binary/unary operations, and user-defined operations.

Constants

TBD.

Variables

TBD.

Intrinsic Functors and Operations

Intrinsic functor cat(string, string) is used to concatenate two strings together. It can be nested to concatenate more than two strings.

.decl Y(a:symbol, b:symbol)
.decl Z(a:symbol, b:symbol, c:symbol)
.output Z
Y("a","b").
Y("c","d").
Z(a,b, cat(cat(a,b), a)) :- Y(a,b).

The output would be:

a	b	aba
c	d	cdc

The intrinsic functor ord(string) is used to return the ordinal number associated with string. This is not a lexicographic ordering. The ordinal number is based on the order of appearance (see example below).

.decl n(x:symbol)
n("Homer").
n("Marge").
n("Bart").
n("Lisa").
n("Maggie").
.decl r(x:number)
.output r
r(1) :- n(x), n(y), ord(x) < ord(y), x="Homer", y="Bart".
r(2) :- n(x), n(y), ord(x) > ord(y), x="Maggie", y="Homer".
r(3) :- n(x), n(y), ord(x) > ord(y), x="Marge", y="Bart".

The output would be:

1
2

r(3) is not set, since ord(“Marge”) is less than ord(“Bart”) (the string “Marge” appears before the string “Bart”, therefore it has a smaller ordinal number).

Functor strlen(string) returns the length of string as number.

.decl length(n:number)
.output length
length(n) :- n=strlen("Hello").
length(n) :- n=strlen("World!").

The output would be:

5
6

Functor substr(string, index, length) is used to return the substring starting at index with length length of string. The index is zero-based.

.decl substring(s:symbol)
.output substring
substring(s) :- s=substr("Hello_", 2, 3).
substring(s) :- string="World!", s=substr(string, 3, strlen(string)).

The output would be:

llo
ld!

Functor to_number(string) transforms a string representing a number to its associated number, it also recognises hex and binary numbers with a prefix.

.decl tonumber(n:number)
.output tonumber
tonumber(n) :- n=to_number("123").
tonumber(n) :- n=to_number("1534").
tonumber(n) :- n=to_number("0xff").
tonumber(n) :- n=to_number("0b111").

The output would be:

123
1534
255
7

The reverse operation to_string(number) also exists, which turns a number to its string representation.

Soufflé supports standard arithmetic operations +, -, *, /, ^ and %. Examples of this are given below.

.decl e(x:number, t:symbol, y:number)
e(10 * 2,"10*2", 20).
e(10 + 2,"10+2", 12).
e(10 / 2,"10/2", 5).
e(10 ^ 2 , "10^2", 100).
e(10 % 3, "10%3", 1).
e(2^4%13 , "2^4%13",3).

Binary + operation is defined as the string concatenation operator when both operands are symbol.

.decl string_concat(result:symbol)
string_concat("a" + "b").
string_concat(to_string(10) + "b").
string_concat(to_string(3) + "." + to_string(14) + to_string(159)).

Soufflé supports bitwise logical operations: band (bitwise and), bor (bitwise or), bxor (bitwise exclusive-or), bshl (bitwise shift left), bshr (bitwise shift right), and bshru (bitwise shift right/unsigned).

Examples of this are given below.

e(0xFFF1 band 0xF, "0xFFF1 band 0xF", 0x1).
e(0xFF00 bor 0x000F, "0xFF00 bor 0x000F", 0xFF0F).
e(0xFFFF bxor 0x000F, "0xFFFF bxor 0x000F", 0xFFF0).

Soufflé supports logical operations that consider every non-zero number as true and always return 1 or 0: land (logical and), lor (logical or), lxor (logical exclusive-or), and lnot (logical not).

Examples of this are given below.

e(1 land 2, "1 land 2", 1).
e(1 land 0, "1 land 0", 0).
e(1 lor 0, "1 lor 0", 1).

Soufflé supports max and min operations over numbers.

e(max(3, 4), "max(3, 4)", 4).
e(min(3, 4), "min(3, 4)", 3).

Soufflé supports standard unary operation -.

e(-2*10,"-20", -20).
e(-2,"-2", -2).
e(--2,"--2", 2).

Functor $ is used to generate unique random values to populate a table. It should be used with care as it may result in stepping outside the standard Datalog semantics.

.decl A (n:number)
.decl B (a:number, b:number)
.decl C (a:number, b:number)
.output C
A(0).
A(i+1) :- A(i), i<1000.		
B($,i) :- A(i).		
C(i,j) :- B(c,i), B(c,j), i!=j.

The above example does not output anything.

User-Defined Functors

TBD.

Syntax

In the following, we define constraints and argument values in Soufflé more formally using syntax diagrams and EBNF. The syntax diagrams were produced with Bottlecaps.

Argument Value

Arguments define values for predicates. They can be constants, (unnamed) variables, record terminator (nil), record constructors, ADT constructors, type conversions, aggregators, user-defined functor invocations, unary and binary operations on other arguments, and arguments in paraenthesis for changing the operation bindings.

Argument

argument ::= 
      constant
    | variable
    | 'nil'
    | '[' argument_list ']'
    | '$' IDENT ( '(' argument_list ')' )? 
    | '(' argument ')' 
    | 'as' '(' argument ',' type_name ')'
    | ( userdef_functor | intrinsic_functor ) '(' argument_list ')'
    | aggregator
    | ( unary_operation | argument binary_operation ) argument 

Constant

A constant is either a string, number, unsigned or float constant. Numbers have different representation including binary, hexadecimal, and decimal representations.

Constant

constant ::= STRING | NUMBER | UNSIGNED | FLOAT 

Variable

A variable is either a named variable or an unnamed variable. A named variable has an identifier. An unnamed variable has the underscore symbol. There are also unnamed named variables which are identifiers, which begin with the underscore symbol.

Variable

variable ::= IDENT | '_'

Argument List

An argument list is a list of arguments separated by comma.

Argument List

argument_list ::= ( argument ( ',' argument )* )?

User-Defined Functor

A user-defined functor invocation has a ‘@’ followed by the identifier of the user-defined functor.

User-Defined Functor Identifier

userdef_functor ::= '@' IDENT

Unary Operation

Unary operations are arithmetic negation, binary compliment, and logical not.

Unary Operation

unary_operation ::= '-' | 'bnot' | 'lnot'

Binary Operation

There are binary operations for arithmetic, logical, and binary expressions.

Binary Operation

binary_operation ::= 
     '+' | '-' | '*' | '/' | '%' | '^' | 'land' | 'lor' | 'lxor' | 'band' | 'bor' | 'bxor' | 'bshl' | 'bshr' | 'bshru' 

Intrinsic Functor

There are intrisnic functors for strings, type conversions, and generative functors.

Intrinsic Functor

intrinsic_functor ::= 'ord' | 'to_float' | 'to_number' | 'to_string' | 'to_unsigned' | 'cat' | 'strlen' | 'substr' | 'autoinc'

Type Name

A type name is needed for type conversions. Soufflé has pre-defined types such as number, symbol, unsigned, and float. Used-defined types have a name. If a type has been defined in a component, the type can be still accessed outside the component using a qualified name.

Type Name

type_name ::=  "number" | "symbol" |"unsigned" | "float"  | IDENT ("." IDENT )*

Legacy Syntax

The syntax of Soufflé changed over time. Older code bases can be still used with modern versions of Soufflé. In older versions of Soufflé we used the dollar symbol

.decl A(x:number)
A($):-true.

for auto increment, which is equivalent to

.decl A(x:number)
A(autoinc()):-true.

You can enable the old legacy syntax using the command-line flag --legacy, but you will receive a warning that the dollar symbol is deprecated.