Cellular automata, WinCA application VI
We continue with the series dedicated to cellular automata and the WinCA application, dedicated to build and executing them. In this article I will explain the code related to the expression system that allows establishing the conditions to change from one state to another.
Here you can find the first article of the series about WinCA application.
In this link you can download the WinCA application executables, and in this other one you can download the source code of the WinCA solution, written in csharp with Visual Studio 2015.
Language compiler
Language expressions are compiled using the BNFUP class library. You can read more about it in the article about the BNFUP universal object compiler. This is the syntax in the form of BNF rules:
<<boolexpr>>::=<boolexpr1>['|'<boolexpr>];
<boolexpr1>::=<boolexpr0>['&'<boolexpr1>];
<boolexpr0>::=['!']<boolelement>;
<boolelement>::=<pboolexpr>
|<kbool>
|<truthfunction>
|<boolstrfunction>
|<boolaggregate>
|<relexpr>;
<pboolexpr>::='{'<boolexpr>'}';
<kbool>::='$t','$f'
<truthfunction>::='ist(','isf('<valarg>')';
<boolaggregate>::='and(','or(' <boolexpr> ')';
<boolstrfunction>::='cont(','stw(','endw(','match(',
'streq(','streqcs(' <strexpr>','<strexpr>')';
<valarg>::=<valref>
|<propref>
|<indexedprop>
|<iteratorprop>;
<relexpr>::=<expr>'<<','<=','==','>=','>>','<>'<expr>;
<expr>::=<expr2>['+','-','@','#'<expr>];
<expr2>::=<expr1>['*','/','%','?'<expr2>];
<expr1>::=<expr0>['^'<expr1>];
<expr0>::=['-']<element>;
<element>::=<pexpr>
|<number>
|<kvalue>
|<valarg>
|<intfunction>
|<intaggregate>
|<intstrfunction>
|<dblfunction>
|<dblaggregate>;
<pexpr>::='('<expr>')';
<number>::=<digit>[<rnumber>]
|'.'<rdecimal>;
<rnumber>::=<digit>[<rnumber>]
|'.'<rdecimal>;
<rdecimal>::=<digit>[<rdecimal>];
<digit>::={0-9};
<idvalue>::=<bslash><letter>[<ridentifier>];
<idprop>::=<bbar><letter>[<ridentifier>];
<ridentifier>::=<validchar>[<ridentifier>];
<bslash>::={_};
<bbar>::={\\};
<letter>::={A-Za-z};
<validchar>::={_A-Za-z0-9};
<typedef>::='int','double','bool','string';
<valref>::=<idvalue>[':' <typedef>];
<propref>::= <idprop>[':' <typedef>];
<indexedprop>::='['<expr>']' '.' <propref>;
<iteratorprop>::=';'<propref>;
<kvalue>::='$e','$pi','$runi','$rnorm';
<strchars>::={^"}[<strchars>]
|'\"'[<strchars>];
<string>::='"'[<strchars>]'"';
<intaggregate>::=<icountfunction>
|<iaggfunction>;
<icountfunction>::='icount(' <boolexpr> ')';
<iaggfunction>::='isum(','iprod(','imax(','imin(','iavg(' <expr> ')';
<intfunction>::='int(','isign(','ifloor(','iceil(','iabs(',
'red(','green(','blue(' <expr> ')';
<intstrfunction>::='cmp(','pos('<strexpr>','<strexpr>')';
<dblaggregate>::=<dcountfunction>
|<daggfunction>;
<dcountfunction>::='dcount(' <boolexpr> ')';
<daggfunction>::='dsum(','dprod(','dmax(','dmin(','var(',
'sd(','svar(','ssd(','davg(' <expr> ')';
<dblfunction>::='double(','dsign(','ln(','sqrt(','exp(',
'log(','log2(','dfloor(','dceil(','sin(','cos(','tan(','sinh(',
'cosh(','tanh(','dabs(','hue(','sat(','bri(' <expr> ')';
<strexpr>::=<string>
|<strsubs>
|<strstr>
|<strrepl>
|<valarg>;
<strsubs>::='subs('<strexpr>','<expr>','<expr>')';
<strstr>::='strc('<strexpr>','<strexpr>')';
<strrepl>::='repl('<strexpr>','<strexpr>','<strexpr>')';
The file with the compiled rules is expressions.bnf, which is embedded in the Resources.resx resource file of the CellularAutomata class library. This file can be modified using the application BNFUPEditor, which you can found in the BNFUP solution, in the previous link.
In this article, about the WinCA language syntax, you can find an explanation on the use of the different elements that compose these expressions.
The expression generator is implemented in the ExpressionFactory class, in the Expressions namespace of the CellularAutomata class library.
Expressions implementation
In addition to the BNFUP related interfaces, the WinCA expression elements implement the IExpressionValue interface, defined in the CommonObjects class library Interfaces namespace, as follows:
public interface IExpressionValue
{
IValueProvider ValueProvider { get; set; }
int AsInt { get; }
double AsDouble { get; }
string AsString { get; }
bool AsBool { get; }
Color AsColor { get; }
ExprType PreferredType { get; }
}
ValueProvider gets or sets the object that allows access to the values of the properties of the automaton cells; usually it is the object that implements the automaton itself. With the AsInt, AsDouble, AsString, AsBool, and AsColor properties, you can get the value of the expression with different data types, and PreferredType is the native data type of the expression.
The IValueProvider interface, in the same class library and namespace, is defined as follows:
public interface IValueProvider
{
IVariantValue GetVarValue(string name);
IVariantValue GetPropertyValue(string name);
IVariantValue GetIteratorPropertyValue(string name);
IVariantValue GetIndexedPropertyValue(int ix, string name);
void BeginIterator();
bool Next();
void EndIterator();
}
The GetVarValue method returns the value of a variable identified by its name, while GetPropertyValue does the same with the value of the property named name of the automaton current cell state.
With GetIndexedPropertyValue, you can get the value of the state property named name of the current cell neighbor with the index ix.
To iterate through all neighboring cells of the current cell, you should start a loop with the BeginIterator method, then get the value of the property with GetIteratorPropertyValue, passing the name of the property in the name parameter, and continue with the Next method to move to the next neighboring cell. The method Next will return true until all the neighbors are processed. The EndIterator method must be called when the loop is finished.
The classes that implement the different elements of the expressions are in the Expressions namespace of the CommonObjects class library. All are derived from the abstract base class ExprBase. This class implements the ICompilableObject interface, required for the BNFUP compiler, and IExpressionValue, in addition to the following properties and methods:
public bool Parenthesis { get; set; }
public virtual bool Indexed { get; }
public virtual string AsText { get; }
public ITestProvider TestProvider { get; set; }
public virtual int OperandCount { get; }
public virtual ExprBase Operand(int n);
- Parenthesis: Used to alter the operators precedence. The subexpression and its components are considered atomic when the expression which contains them is simplified.
- Indexed: Returns true if there is an operand in the expression that references any of the neighboring cells by their index.
- AsText: Returns a version of the object as a text string, used to represent it in the user interface.
- TestProvider: This property gives the object access to a form that implements expression testing for debugging. This property is currently not used.
- OperandCount: Returns the number of operands in the expression.
- Operand: Returns the nth operand of the expression.
To implement expression operators, there is the base class Operator, which only implements the ICompilableObject interface. In addition, it offers the following properties and methods:
public virtual int Precedence { get; }
public virtual double Operate(double v);
public virtual int Operate(int v);
public virtual bool Operate(bool v);
public virtual double Operate(double v1, double v2);
public virtual int Operate(int v1, int v2);
public virtual bool Operate(bool v1, bool v2);
Precedence returns a value that represents the precedence of the operator. It is used to simplify and regrouping subexpressions. The different versions of Operate allow performing the current operation with one or two arguments, using different combinations of data types.
From this base class they are derived the three types of operators used by the language, BoolOperator, for logical operations, ArithmeticOperator, for arithmetic operations and RelOperator for relational operations.
There are three kinds of expressions in the language: logical, arithmetic and relational. In addition, there are functions that use arguments of these three types and text strings. For example, an arithmetic expression cannot use Boolean arguments, and in the logical expressions the arguments cannot be numbers. To differentiate argument types, there are some abstract base classes, derived from ExprBase: UniversalArgument, which are used for arguments that can be used in any expression, such as those representing the value of a property, ArithmeticArgument, for numeric arguments, BoolArgument, for Boolean arguments, and StringArgument, for arguments that are text strings.
All the operands are derived from one of these classes, so that, the compiler can check that their types are appropriate for the expression being building.
The different expression types are implemented in the classes LogicExpression, ArithmeticExpression and RelExpression.
The simplest components are the numeric constants, implemented in the Number class, the text string constants, in the StringLiteral class, the logical constants, in the BoolValue class, the arithmetic constants and pseudo-constants, such as the pi number or the random values generators, in the ArithmeticValue class, and variable references, in the VariableReference class.
To access the values of the state properties, using the different access modalities, there are the PropertyReference class, which allows access to the current cell state properties, IndexedProperty, to access the properties of neighboring cells by their index, and IteratorPropertry, to access all neighboring cells in an iteration process.
The TypeDef class implements the functions used to force a type change. The other functions are implemented in the classes IntFunction, the functions working with integers, DoubleFunction, the functions working with double values, StrBoolFunction, the functions with string type arguments and bool type result, StrIntFunction¸ the functions with string arguments that return integers, and specific functions working with strings in the StrSubstringFunction, StrConcatFunction, and StrReplaceFunction classes. Finally, the TruthFunction class implements the two functions that check whether the argument is true or false.
The aggregate functions, which perform the calculations using all current cell neighborhood, are implemented in the BoolAggregate class, with the logical functions, IntCountAggregate and DoubleCountAggregate, derived from CountAggregate, that implement the counting functions, DoubleAggregateFunction, the aggregation functions working with double values, and IntAggregateFunction, the aggregation functions working with integer values.
Finally, there are some auxiliary classes that are only used at compile time, such as FunctionName, Identifier, VarIdentifier, and PropIdentifier, with which the function names, variable and property identifiers are packed in ICompilableObject objects so they can be used to build complex objects.
Forms used to edit expressions
In the WinCA application, the form that implements state change expressions editing is frmTransitions, in the Forms namespace.
In this form you can compile the expressions to check the syntax. The form that presents the compilation messages is frmMessages, in the Utils namespace. To test the expressions, the frmTest form, in the Expressions namespace of the CellularAutomata class library, is used.
And that is all regarding expressions, in the following article I will show the interfaces and classes related with the automata cells.