Dynamic Objects, Expressions & Scripts in .NET

Download DOES.NET (free) http://does.dataodyssey.com http://www.dataodyssey.com
DOES.NET technology allows you writing and executing expressions and scripts in NET languages without compilation. And you can declare and use dynamic variables (objects) right in scripts without compilation too. If you used dynamic languages before then you be glad receiving ability to use dynamic code, macro substitutions and other powers of simple interpreter in NET languages (C# & VB). DOES.NET is a minimal set of means to create any dynamic code (except type declarations).
This part of documentation describes DOES.NET technology using without internal details.
DOES.NET net components include two library for each NET platform:
Include references to these dll into your project and add using DOES; using DOESExplWPF; into code.
Just implementation for NET FrameWork 4.0 is available now. But I hope to create solutions for Silverlight and NET FrameWork 3.5 in the future (but for WPF interface only).
But! DOES.NET public solutions are just implementations based on technologies for internal use which I create for my projects. So. Be DOES.NET public technologies developing further or not, depends on your requests for its utility. And I will try keeping this technology free of charges while my main projects bring me income.
Instead of using of constructor overloads you should call Init method of Does class object to initialize. Before this you can change any default settings to optimize memory use. These settings depend on how many dynamic objects and functions you plan to use in your scripts.
Does.PublicObjectsDefault -
default length of table to store public objects. The default value is 5000;Does.PrivateObjectsDefault -
default length of table to store private objects. The default value is 5000. Table of private objects is created for each level of stack when you call DOES.NET function;Does.PublicFunctionsDefault -
default length of table to store public functions. The default value is 1000;Does.PrivateFunctionsDefault -
default length of table to store private functions. The default value is 1000. Table of private functions is created for each level of stack when you call DOES.NET function;Does.MaxStackLevels -
default length of stack. The default value is 1000;These are just default values. DOES.NET uses these values to create tables. But if the length of any table is exceeded when DOES.NET try to create new object, then DOES.NET increases a size of table. But this operation takes additional time and memory.
So. Your C# code can look like:
Does oDoes;
Error occurs if you don't call Init() method before Does class object use.
Next example demonstrates few main DOES.NET abilities and has not any sense. You can see the technique when DOES.NET dynamic objects of any types can be declared, you can assign values to them before executing of DOES.NET scripts, and take values back from DOES.NET objects after script executing.
Inside script you can also: declare objects of any types; assign values; evaluate expressions with references to DOES.NET objects, NET types, members of objects and types; can create arrays and reference their elements in expressions; can use constructions IF-ELSE-END and WHILE-CONTINUE-BREAK-END loops; can declare and call functions, use macro substitutions, and ets...
Note! Use comma or point in numeric constants to convert them to Decimal (0,00 or 0.0). Else constant will be converted to Int32.
Dynamic objects in DOES.NET can be one of three status: Public, Private, Local. And they can be declared and be accessible from NET language code and inside DOES.NET script. Object names are case insensitive.
Public objects are declared in root (zero) stack level and can be accessible from any function in any level of stack.
Private objects are declared in any stack level and can be accessible in current function and in functions called from current (in next levels of stack).
Local objects are declared in any stack level and can be accessible only in current function.
To declare DOES.NET objects in C# code you can use methods Declare and Set of Does class object.
public
bool Declare(String Name) - this method declares object without value assignment.public
bool Declare(String Name, Object Value) - this overload of Declare method allows declaring object and assign value of any type.public
bool Set(String Name, Object Value) - actually equals to Declare overload.If an object with the same name already exists then two last methods just replace its value without exception creating. First method do nothing.
Examples:
If dynamic objects was declared in script (in root stack level) then they also can be visible after script execution and accessible from C# and VB# code.
Use commands: Public, Create and Local to declare empty objects inside DOES.NET script. For example:
DOES.NET creates dynamic objects for each name listed after directive and delimited with comma.
Declaring objects of specified type
Use function New(sType) to declare DOES.NET objects of specified types or to replace existing objects with new values of specified types.
"...; Var10=New(\"System.String\"); Var10=\"This is string\";...; Var11=String.Copy(Var10); ...;"
You have to pass valid type name as string as parameter into function New. And assembly should be accessible.
Use function Array(sType,Dim1[,Dim2[,...,DimN]]) to create arrays of objects of specified types. For example:
"...; Arr1=Array(\"System.String\",10); ...;" - this script fragment creates one-dimension array String[10];
"...; Arr2=Array(\"System.Windows.Controls.TextBloc\k",10,3,7); ...;" - this script fragment creates three-dimension array TextBlock[10,3,7];
You can refer to elements of arrays in script using square brackets:
Public objects can be accessible from anywhere. Private objects can be accessible in current function and functions called from current. But if private object with the same name is created in called function (sublevels of stack) , then public object and private objects of higher levels with the same names are inaccessible from this function and functions called from this function. All local variables, declared in function, hide public variables with the same names and private variables with the same names, declared in external functions (higher levels of stack), but only inside current function except functions called from it.
You don't need declare private variables inside script using Private command. When you assign value to variable then DOES.NET creates new private object if object with this name doesn't exist.
DOES.NET clears all dynamic objects in all levels of stack except root (zero) after script execution ( oDoes.Execute(sScript); ). To decrease a quantity of operations with memory and to increase a speed DOES.NET doesn't erase private objects, functions and their tables after each function call, because this operation takes a time. So. If some variables prevent you work then you should clear they manually. You can do this:
in script:
"...; Clear nVar1,nVar2,nVar3; Clear *Var1,nVar*,*Var*;"
in code:
Any way you can use * in beginning or end of mask or full object names.
After script executed, you can refer to DOES.NET object of root stack level in C# or VB code using Get method of Does class object:
Expressions in DOES.NET can consist from references, DOES.NET functions calls, constants and operators.
References can be to: DOES.NET objects; Types in available assemblies; members of objects; static members of types; elements of arrays.
DOES.NET use the next order when identify each identifier found in expression:
Operators can be:
The priority of operators is: (* /) (+ -) (Comparison) (Logic)
Example of expression:
(nVar1+nVar2)*sVar1.Length > Func1(nVar3) || Func2(nVar4) == nVar5/123
Script lines can include expressions. And you can evaluate expressions in DOES.NET explorer.
DOES.NET expression syntax allows to use constants:
Simply use DOES.NET object names to operate their values in expressions. Object names are case insensitive.
Assemblies classes referencing.
If a type has static members then you can call such static methods or refer to static fields and properties of this type. If you use type name without assembly specification then DOES.NET searches type in all available assemblies and only if dynamic object and functions with the same name aren't found before. But you can optimize this searching. Type names are case sensitive.
You can refer to members of dynamic object or static members of type with using of common syntax Object.Member.Member... (point as delimiter)
and ets... Member names are case sensitive.
DOES.NET functions referencing.
Each name-identifier, found in expression, DOES.NET searches in objects tables first. Second - in functions tables. Order is from current stack level to root. The scope of visibility is the same to objects. The number of parameters can be less or equal to number of parameters declared for this function. Function names are case insensitive.
nFunction3(nFunction1(nVar1,nVar2)+sFunction2(sVar1).Lenght,5)
In cases when you have declared a lot of dynamic objects and functions and have loaded a lot of assemblies, the search time can grow, especially if you refer to type which DOES.NET searches in last time according to order. Expression syntax allows you optimize references by pointing a way to search name-identifier. You can use next syntax:
~ prefix - specifies that this name is DOES.NET function and prevents searching in DOES.NET object tables. ( ~Func1(nVar1) );
# prefix - specifies that this name is type, which should be found in assemblies, and prevents searching in dynamic objects and functions. ( #String.Copy(\"sting\") )
^ delimiters to define a full type. If name includes ^ symbol then DOES.NET replaces these symbols with points and accepts the result as full name of type. In this case DOES.NET does not search type in all assemblies and try invoking member without checking of its existence. Make sure a full type is correct! ( #System^String.Copy(\"sting\") )
You can essentially increase a speed of script executing with using of this references optimization.
You can use Does class method static bool Not(Object _val) or static bool not(Object _val) to make unary inversion. It looks like "...; IF Does.Not(False); ...;"
In general DOES.NET is a technology of macro substitutions for NET languages (C# and VB). But you can use nested evaluations inside scripts. In all cases you can evaluate just completed commands and can't substitute separated clauses (like in FoxPro). And any member of expressions (except operators) can be as string subexpression stored in variable.
Eval(sExpression) function allows to use expression evaluations instead of macro substitutions in DOES.NET . For example:
If you need to run a script inside another script then you can use Exec(sScript) function. For example:
Method Execute(sScript) allows script executing.
Script is a set of commands in one string. Each command should be ended with semicolon (;) symbol. Command can be:
You can declare:
Usually commands are:
Each element of this construction should be closed with semicolon (;) too. Any quantity of any commands can be placed between elements, including nested IF ... ELSE ... END and WHILE ... END. For example:
WHILE ... CONTINUE ... BREAK ... END
Each element of this construction should be closed with semicolon (;) too. Any quantity of any commands can be placed between elements, including nested WHILE ... END and IF ... ELSE ... END. For example:
Class Does has a field Console of type:
If you create an object which inherits this interface and store the reference to this object to Does.Console field, then you can use directive ? to write e result of expression onto your console. See script example above.
If a command of DOES.NET script begins with // symbols then Does.Execute accepts this command as comments and does nothing.
DOES.NET functions can be public if declared in root stack level and private (if declared in stack sublevels). Private functions will be released after script executing by Does.Execute(sScript).
Public functions can be declared before scripts executions with using method of Does class:
bool DeclareFunction(String Name, String[] Args, String Code)public
First parameter is a function name.
Second parameter is a list of names of arguments (just names).
Third parameter is a script of function body.
For example:
Public and private functions can be declared right in script during executing. Function declarations should be placed in beginning of script before lines of script. Each function declaration should be ended with EndFunc directive. For example:
Use Return command to return value from function. For example: "Return Parm1-Parm3;"
Return value is also placed into variable with name ReturnValue in function or script which called function returned value.
To use DOES.NET Explorer you should:
XAML:
or in C#:
After this you can call DOESExplorer in your code.
!!! DOES Explorer creates new Does class object as internal field! You should pass a reference to existing Does class object as parameter into DOESExplorer(Does _ds) constructor to explore this Does class object! DOES Explorer window code looks like:
DOES Explorer consists of four pages:

In page with objects you can create new objects of specified type with selection from list of assemblies and types, which (list) called by "New..." button.
In page with functions you can just list functions and see their code.
In "Expression" page you can write any DOES.NET expressions, evaluate them and see the results of evaluations.
Main page is "Script" page. In this page you can:
If you have created DOES Explorer window then you can call it on errors occur during script executing and see what are problems. For example:
Debugging DOES.NET scripts (WPF).
You can call script debugger from DOES Explorer by "Begin trace" button or in code of your NET program.
Class Does has field Trace of interface
There is a class DOESTrace declared in DOESExplWPF.dll which inherits IDOESTrace and you can create object of this type and call to debug from your code. Create object of type DOESTrace and store reference to this object into Does.Trace before Does.Execute call. Does.Execute calls trace window on each line of your script.
Syntax is:

System.Type class and System.Reflection namespace allow to work with types and their members. I use these means as base of DOES.NET technology. Second part of technology is name tables. This part was inherited from other dynamic languages like FoxPro. And third part is a simple interpreter with minimal abilities, but a set of abilities is sufficient to create any dynamic code (scripts and expressions) except type declaration. I had to mix syntax C# and VFP to create DOES.NET script syntax and I tried to create this syntax most simple. But some internal details, I describe in this part of documentation, can be useful to implement more complex solutions with using DOES.NET
DOES.NET uses internal tables to store references to dynamic objects and functions and their names. We call them "name tables" (inherited from FoxPro). On each level of stack DOES.NET creates two name tables for objects and functions. A length of each type of name table depends on stack level (root or not) and Does class properties:
You can change a default sizes of table before oDoes.Init() method call. But the size of table is not fixed, and DOES.NET can expand table dimensions if size is exceeded.
When you declare new object or function then DOES.NET does:
Each element of stack (each level) has objects name table as field
Each element of objects name table keeps: a name of object, reference to object and "local" status.
To clear object DOES.NET just replace a reference to
name-table-element and object with null. Further just NET FrameWork knows how and
when to free memory (if there are no other references to this object). So, you
have to control objects life time by yourself if needed (for example with using
Function name table is similar to object name table. But element of function-name-table keeps: the name of function, list of argument names and script of function body as string.
Each element of stack (each level) has a structure.
Size of stack table is fixed! (1000 levels) If a size of stack table is exceeded during next function call, then DOES.NET doesn'texpand stack table. So if you plan more nested calls then increase
Does.MaxStackLevels property before Does.Init() call.
Methods:
| Bigger or bigger | public static
Object Bigger(Object
_val1, Object _val2) public static Object bigger(Object _val1, Object _val2) Returns first argument if it bigger than second, else returns second. |
| Declare |
|
| DeclareFunction | public bool DeclareFunction(String Name, String[] Args, String Code) Declares dynamic function with name Name, list of arguments Args and body script Code. |
| DeclareLocal | public bool
DeclareLocal(String Name)
Declares local dynamic object with name Name
and null value. public bool DeclareLocal(String Name, Object Value) Declares local dynamic object with name Name and assigns Value. |
| DeclarePublic | public bool
DeclarePublic(String Name)
Declares public dynamic object with name Name
and null value public bool DeclarePublic(String Name, Object Value) Declares public dynamic object with name Name and assigns Value. |
| DoCmd | public Object DoCmd(String Cmd) Executes single command. In difference to expression evaluation command can be assignment and can have left and right sides. |
| Evaluate | public Object Evaluate(String Expr) Executes expression and returns result. |
| Execute | public
Object
Execute(ref
String Script)
public Object Execute(String Script) Executes script. Script is a set of commands. |
| Iif or iif | public static
Object Iif(bool Cond,
Object iftrue, Object iffalse) public static Object iif(bool Cond, Object iftrue, Object iffalse) Returns second argument if first argument is true else returns third argument. |
| Init | public bool Init() Initializes Does class object. Should be called before Does class object using. You can change default values of properties PublicObjectsDefault, PrivateObjectsDefault, PublicFunctionsDefault, PrivateFunctionsDefault and MaxStackLevels before Init(). |
| Get | public Object Get(String Name) Returns value of dynamic object by name. |
| New | public Object New(String Type) Creates and returns object of specified type Type. Type is full name of type in assembly. |
| Not or not | public static
bool
Not(Object _val) public static bool not(Object _val) Negation. Unary operation. Boolean inversion. |
| Release | public void Release(String Name) Deletes dynamic object. Name can be mask like *mask, mask* or *mask*, or name of object. |
| Set | public bool Set(String Name, Object Value) Replaces value of dynamic object with name Name with reference to object Value. If dynamic object with name Name doesn't exist then private object with this name will be created. |
| Smaller or smaller | public static
Object
Smaller(Object
_val1, Object _val2) public static Object smaller(Object _val1, Object _val2) Returns first argument if it smaller than second else returns second argument |
Properties and fields:
| Console | public IDOESConsole Console Replace this field with reference to IDOESConsole object to output values by ? directive. |
| ErrorCode | public int ErrorCode Check after Does.Execute() , Does.DoCmd() and Does.Evaluate() . If there are no errors then Does.ErrorCode is 0. Else has a code of error. |
| ErrorFunction | public String ErrorFunction The name of function where error occurred. |
| ErrorException | public Exception ErrorException Exception object for unhandled error occurred. |
| ErrorLine | public int ErrorLine Line of script or function where error occurred. |
| ErrorMessage | public String ErrorMessage Error message. |
| MaxStackLevels | public int MaxStackLevels = 1000; A length of stack. Change this value if needed but before Does.Init() call. |
| PrivateFunctionsDefault | public int PrivateFunctionsDefault = 1000; A default length of private functions name table. Change this value if needed but before Does.Init() call. |
| PrivateObjectsDefault | public int PrivateObjectsDefault = 5000; A default length of private objects name table. Change this value if needed but before Does.Init() call. |
| PublicFunctionsDefault | public int PublicFunctionsDefault = 1000; A default length of public functions name table. Change this value if needed but before Does.Init() call. |
| PublicObjectsDefault | public int PublicObjectsDefault = 5000; A default length of public objects name table. Change this value if needed but before Does.Init() call. |
| Stack | public DOESStackElement[] Stack; Stack table. |
| StackLevel | public int StackLevel Current level of stack. |
| Trace | public IDOESTrace Trace Replace this field with reference to DOESTrace object to begin a debugging on Does.Execute() call. |
Initially DOES.NET technology began developing to move parts of VFP projects to NET platform. Visual FoxPro programmers, who was "spoilt" by great abilities of FoxPro as interpreter (evaluation, execution, macro substitutions), couldn't find the same means in NET languages (C# and VB). Some attempts to create converters from VFP into NET had crushes because of these abilities absence. But a lot of VFP projects still haven't prospects because of politic of Microsoft who stopped VFP development.
Certainly this is not possible to interpret all NET Framework syntax without compilation. And DOES.NET solution provides a minimal set of means to solve the problem of dynamic code in NET (C# and VB). Next level of means, which can be useful to conversion of VFP projects to NET, probably like specified framework with sets of means which equal to means in VFP. Some good programmers already began creating such frameworks. And I don't plan developing additional variant of such framework. Instead of this I offer asking somebody like "Visual FoxPro Toolkit for .NET Team" http://foxcentral.net/microsoft/vfptoolkitnet.htm expanding their toolkit to SCATTER, GATHER, and other operations with VFP variables using DOES.NET. Ask them if you need this help.
(c) Andrey Evsikov ( www.dataodyssey.com )
Book:
