GMac Scripting Guide


-- Download GMac Scripting Guide as PDF --


Scripting

In this guide we will explore GMac scripting by covering the following:

  1. GMac Scripting by Example
    1. Initialization, Input, and Output
    2. Using GMacAST Data in the Script
    3. Computing with GMac Commands and Expressions
    4. Using Mathematica for Additional Tasks
  2. GMac Scripting: The Full Picture
    1. The Base: CS-Script Library
    2. The Translator: GMacScriptGenerator Class
    3. The Server: GMacScriptInterpreter Class
    4. The Manager: GMacScriptManager Class

1. GMac Scripting by Example

GMac scripting is essentially based on writing C# code that calls GMac and Mathematica services through a set of subroutines (methods). Some syntactic sugar is used to simplify the common GMac scripting tasks. C# is a, quite big, general purpose object oriented language. However, scripting with C# doesn’t require the use of its full power. Just a bit of variable declarations, arrays, loops, conditions, and expressions is enough for many purposes. The Core C# and .NET Quick Reference is a good place to start. More details can be found in this excellent C# Tutorial. If you need to use the beautiful Linq to objects feature in your scripts, which I highly recommend for complex tasks, read a copy of Troy Magennis’s book “LINQ to Objects Using C# 4.0: Using and Extending LINQ to Objects and Parallel LINQ (PLINQ)“. For more in-depth understanding of C# you can read “Illustrated C# 2012” by Daniel Solis and “C# 4.0 in a Nutshell: The Definitive Reference Fourth Edition” by Joseph Albahari and Ben Albahari.

Wolfram Mathematica is a huge symbolic computer algebra system with many years of developments. To learn Mathematica you need to experiment with specific tasks in mind. Several online quick references exist like this and this. A huge list of Mathematica recipes by Nasser M. Abbasi can be found here and here. You can always go to the online comprehinsive help system for detailed information. In addition, several good books can be found here.

In this section we will see examples for various tasks coded as GMac scripts and in the following section we will give a detailed explanation of the GMac scripting engine to fully understand and utilize its capabilities. We will assume the following GMacDSL code is used and compiled into a GMacAST as described in the GMacAST Guide:

1.1. Initialization, Input, and Output

The first step in getting familiar with any scripting environment is to know how to initialize the script, how to input data and how to output information. The first line of code in any GMac script should take the following form:

This command simply tells the GMac script engine to reference any following GMac symbols (frames, constants, structures, macros, etc.) starting from the scope of the geometry2d.cga frame. So for example if we later need to declare a GMac multivector variable under the cga frame we would write declare Mv : Multivector instead of the fully qualified name declare Mv : geometry2d.cga.Multivector.

For our data input and general processing we can use normal C# variable declaration and initialization syntax in the GMac script. All core C# datatypes are available to the script to use, along with the C# standard operators, decision making statements, looping statements, arrays, string manipulation, file input\output, and many more advanced C# features. The script writer can select whatever is necessary from all C# features from the simple to the complex.

For information output we can use two kinds of reporting mechanisms in the GMac script:

  1. Use the script’s Log object to report formatted text to the script user. The Log object is of type LinearComposer described in the TextComposerLib Guide.
  2. Use the script’s Output object to store text, images, or GMac multivector values for later viewing, each value can have its own text title and description string.

For example the following script displays the even numbers from 1 to 10 to the Log object:

While the following script will store the same numbers each with its own descriptive title for later viewing:

The use of GMac script commands between <: :> delimiters is a simplified syntax that will be internally translated into C# code calling GMac methods as we will explain in the second section in detail. We could have written two previous example as follows and the results will be exactly the same:

1.2. Using GMacAST Data in the Script

The GMac script engine is initialized using a GMacAST structure compiled from GMacDSL code, like the code in the beginning of this guide. The script user can access any information in the GMacAST using members and methods described in full details in the GMacAST Guide.

Example40

The following script will display the qualified names for namespaces, frames, and macros in our compiled GMacAST. Here we use the Root member (the same as the Ipr.Root member) that directly points to the AstRoot object of the compiled GMacAST:

Example40

The following script will display the names of all basis blades in the geometry2d.cga frame ordered by their grade. To reference a GMacAST symbol by its name we can use the GMac script commands frame, namespace, macro, structure, etc. These commands will return a reference for the symbol’s AST node object. More about these commands in the second section of this guide:

1.3. Computing with GMac Commands and Expressions

Now we come to the interesting part: how to compute with multivectors in a GMac script. The method is very simple, just pass a string containing GMac declare and let commands to a script command called Execute, also Exec for short, and GMac will create its own local variables and compute values from GMacDSL expressions and assign them to the GMac local variables. The GMac local variables are completely separate from the C# local variables in the main script, so you can use the same names, although this might reduce readability of your script. At any time after executing the commands you can query values of GMac local variables, or any part of them, using the ValueOf script command. You can also evaluate GMac expressions without assigning them to GMac local variables using the Evaluate script command. This technique is intended to close the gap between the non-Turing complete GMacDSL code and typical scripting requirements using the full power of C# syntax.

Example40

For example this script verifies a geometric algebra identity in a vector and a blade in the geometry3d.cga frame:

First of all this code contains many lines with GMac script commands on the form [: argument |> ValueOf |> AsString |> AppendLine :]. These are examples for composition of GMac commands where the result of the ValueOf command is passed to the AsString command then to the AppendLine command.

The lines 8:16 from the script request the creation of 4 local multivector variables (inside GMac not C#): a, b, c, and X, and assign certain symbolic values for a, b, and c while computing X from a and b using a simple GMac expression. The script then outputs the values of the 4 GMac local variables using the ValueOf and AsString GMac script commands. Finally several expressions are evaluated and displayed using the Evaluate command.

One other thing about this script is the use of GMac script commands delimited by [: :] rather than <: :>. This is another syntax simplification that can reduce script code like this:

Into the simpler equivalent script code:

The [: :] syntax is called a square bracket command while the <: :> syntax is an angle bracket command, and we can mix them in a single script as needed. More details on the differences between the two in the second section of this guide.

Example40

Another example is the following script that will produce the inner product matrix of basis vectors for the selected frame:

Most of the script is clear, I hope, but we need to comment on the code in line 30 that I will re-write in the equivalent angle bracket syntax:

The argument is a parametric string surrounded by "| |" delimiters. Inside the parametric string we see some text blocks surrounded by the |{ }|  delimiters (in this example |{id1}| and |{id2}|). The GMac scripting engine recognizes this format and replaces the |{ }| delimited blocks by the C# expressions inside them, functionally equivalent to a concatenation of several strings and values. This is a very useful shorthand to enable the script user to construct GMac code to be executed based on C# expressions. Effectively the two loops in the script request the evaluation of these 16 GMac expressions, see the multivector constructor expression in the GMacDSL Guide for more details:

1.4. Using Mathematica for Additional Tasks

One important feature of the GMac scripting engine is the ability to execute Mathemetica’s symbolic text code using commands similar to the ones we used when executing GMac code. We can always use Mathematica to create symbolic values and then assign the values to the scalar coefficients of GMac multivectors or scalar members of GMac structures. We can also use the GMac value components as inputs to some Mathematica symbolic manipulation algorithm; C# gluing everything together.

Example40

The following is an example for plotting the results of a script using Mathematica’s Plot[]  function:

The ComputeToInputForm and ComputeToImage commands take a string containing a Mathematica expression or code and return a C# string and an C# Image object respectively. The equation found by this script is:

And the plotted signal is:

Square Wave Approximation

Square Wave Approximation using GA-based GMac Scripting


2. GMac Scripting: The Full Picture

GMac scripting is intended for exploration of geometric ideas through GA-based geometric models and algorithms. Three languages can be integrated in a single GMac script, each for a specific purpose:

  1. C# code comprises the main body of the script. C# is not originally designed for scripting purposes, nevertheless C# is a very powerful compiled statically typed object oriented language that can be used for scripting. To simplify using C# as a scripting interface for GMac some syntactic sugar is used to “sweeten” the process of reading and writing GMac scripts.
  2. GMacDSL code can be executed on multivector values and structures. The scalar coefficients of multivectors can be numerical, symbolic, or a mix of both. GMac code is passed, as C# public method calls, to GMac internal services in string form to be automatically compiled and executed by GMac. The most important GMacDSL elements in use are the let and declare  commands and all kinds of multivector expressions described in detail in the GMacDSL Guide.
  3. Mathematica code can be executed on the Mathematica kernel used in the background by GMac. The Mathematica text code is passed through C# service method calls to the Mathematica kernel. This can be used to exploit the full power of Mathematica through GMac to perform many symbolic manipulation and graphing tasks related to the purpose of the GA-based script exploration.

The main input to the script is always a GMacAST structure compiled from the main GMacDSL code. Detailed information of GMacAST symbols, like frames, basis blades, constants, structures, and macros, are accessible through the script’s  Root object. The combination of GMacAST structure, C#, GMac, and Mathematica code, make GMac scripting a very powerful method for GA-models exploration from the very simple to the very complex.

Scripting in GMac is not intended for efficient execution of GA-based algorithms. If the user is satisfied by the final algorithm, GMacAPI can be used by a good software designer to implement an efficient, well-structured version of the script in any desired programming language, with specific types of multivectors and GA algorithms. See the GMacAPI and TextComposerLib Guides for more details on efficient GA-based code generation in GMac.

GMac Scripting

2.1. The Base: CS-Script Library

The library on which GMac scripting is based is called CS-Script, that I found having many features suitable for GMac scripting. In Wikipedia the library is described as:

CS-Script is a CLR (Common Language Runtime) based scripting system which uses ECMA-compliant C# as a programming language. CS-Script currently targets Microsoft implementation of CLR (.NET 2.0/3.0/3.5/4.0) and with full support for Mono. CS-Script is a statically typed language and it allows unlimited access to .NET/CLR functionality with plain vanilla C# syntax. CS-Script as a scripting environment offers stand alone script execution as well as hosting the script engine from CLR application. Because of statically typed nature of the script execution, CS-Script demonstrates no performance degradation comparing to the compiled managed binaries.

When the user enters a GMac Script to be executed, the final step of execution involves passing pure C# code into the CS-Script system. The code is actually a fully generated C# class that implements the following GMac interface:

The simplest form, corresponding to a GMac script having no code, of the the passed C# class is as follows:

Several GMac classes under the  GMac.GMacScripting namespace collaborate to produce the desired class, initialize an instance, and call its Process() method to execute the script as described in the following sub-sections.

2.2. The Translator: GMacScriptGenerator Class

C# is a great Object Oriented Programming language suitable for creating very complex projects. the CS-Script system enables using C# as a scripting language. Unfortunately the C# syntax is not originally designed for scripting which typically requires simple uncluttered code and small overhead. GMac scripting is based on a simplified set of specially formatted commands that translate into C# code without intervention from the user. This translation is the responsibility of the GMacScriptGenerator class. The sequence of translation steps goes as follows:

1- Square Bracket Commands:

The first step is to translate all square bracket commands. A square bracket command takes the general form:

This is translated into the following intermediate form:

Essentially this commands uses a “pipe-forward” syntax to pass the arguments to a composition of methods (similar in meaning to the right composition /* Mathematica operator). In later stages the @f1, @f2, @f3, and "| textArgs |"  parts will be processed into correct meaningful C# code parts. We can pipe-forward to any number of methods we need as long as each is defined to take a single argument compatible with the result of the previous command. For example this square bracket command will compile a given string into an AstExpression GMacAST object, evaluate the AstExpression into an AstValue object, convert the AstValue into a string, and finally append the string into a separate line in the output log of the script:

After translation the command will take the form:

Eventually this will be translated into the C# code:

If the bracket command has no method names, just the text argument, it will be translated into a string. For example the bracket command [: Sin[ |{i}| ] :] translates into "| Sin[ |{i}| ] |" and eventually into the C# code  Ipr.Substitute(" Sin[ |{0}| ] ", i), while the command [: Sin[x] :] eventually translates into the string " Sin[x] " because it has no parameters between |{ }| delimiters.

2- Angle Bracket Commands:

The second step is to translate all angle bracket commands. Angle bracket commands have the general form:

This is translated into the following intermediate form:

The two main differences between square and angle bracket commands are:

  1. Square bracket commands get translated first, so we can use square bracket commands inside arguments to angle bracket commands safely, but not the other way around. Generally we can’t nest square or angle commands, the only safe combination is to use square bracket commands as arguments to angle bracket commands.
  2. All text before the first |> operator of the square bracket command get converted into a single string argument, while the arguments of the angle bracket command are left exactly as they are. Angle bracket commands can have multiple arguments before the first pipe-forward operator   |> separated by commas.

For example the command <: A, B |> f |> g :> would translate into @g( @h( A, B ) ) while the command [: A, B |> f |> g :] would translate into @g( @h( "| A, B |" ) )

3- Parametric Strings:

The third step is to find all strings on the form "|any text here|" (i.e. string literals starting with "| and ending with |") and replace them by a Ipr.Substitute() method call, functionally equivalent to the C# String.Format() method. Inside the given string if a block of text on the form |{arg}| is found it’s replaced by a numbered placeholder |{n}| in the final Ipr.Substitute() code. For example the string:

Translates into the code:

This is a very useful shorthand for parametric strings generally suitable for scripting purposes.

Note that a square bracket command [: arg |> func :] is first translated into @func( "|arg|" ) so that the argument is actually converted into a parametric text to be later translated into a Ipr.Substitute() method call if needed. In effect we can use a square bracket command with a parametric text argument as in this simple script:

After the first step, this will translate into:

And after the parametric string is translated the script becomes:

4- Method Shortcuts:

The final step before the script is passed to the CS-Script system is to replace all shortcut names for methods by their correct names. A shortcut is any part of the code on the form @identifier. where  An internal dictionary holds all correspondences between shortcut names, which are case insensitive in contrast to C# identifiers, and their actual method names. For example the code:

Translates into:

Then into:

Then after the final step it becomes:

The shortcuts are assigned initially by GMac to the most useful services methods especially implemented by the Ipr member of the generated class. Method shortcuts can be added, changed, and reset using the Ipr.Shortcuts  member’s methods. The default shortcuts are presented in the shown table (an html version is here):

Example40

As a final example take a look at the following script and compare the translated text after each of the 4 steps:

The Original GMac script written by the user:

 

The script after translating square bracket commands:

 

The script after translating angle bracket commands:

 

The script after translating parametric strings:

 

The generated C# class with full code after translating method shortcut names:

2.3. The Server: GMacScriptInterpreter Class

After translation of the script into C# code it is passed to the CS-Script system for compilation. If no errors are found a class of type  InteractiveScript is added in memory to the GMac system and an instance of the generated class is created and initialized by assigning an existing object of type  GMacScriptInterpreter to the Ipr member. The Ipr member is responsible for accessing most scripting services provided by GMac through its public methods. The services provided by this member fall under the following categories:

  • Initialization of the GMacDSL Code’s Computational Context: Using the overloaded methods Reset() , OpenScope() , and CloseScope() to set the context in which any following commands will be executed and how symbols are referenced inside script expressions.
  • Accessing GMacAST Information: Through the Root member and several methods for accessing GMacAST symbols by their names including the Namespace(), Frame(), BasisVector(), FrameMultivector(), Subspace(), Constant(), Structure(), Macro() , LocalVariable(), Symbol(), and GMacType() methods.
  • Compilation, Execution, and Evaluation of GMacDSL Commands and Expressions: Using the ValueAccess(), GMacTypeOf(), ValueAccessExists(), Expression(), Declare(), Assign(), Execute(), ValueOf(), and Evauate() methods.
  • Initialize Multivector Values from Patterns and Subspaces: Using the SubspaceToMultivector()  methods.
  • Low-Level Communication with the Symbolic Engine: GMac uses Mathematica for all its symbolic computations. A set of methods can be used to access the active Mathematica server and execute code directly on its kernel using the ComputeToExpr() , ComputeToString() , ComputeToInputForm() , ComputeToOutputForm() , ComputeToTypeset() , and ComputeToImage()  methods.
  • Report Values Generated During Script Execution: Through the Substitute(), AsString(), and  Output.Store() methods and the Output.Log member (of type LinearComposer explained in the TextComposerLib Guide).
  • Create and Update Shortcut Names for Methods: Using the  Ipr.ResetShortcuts() , and  Ipr.SetShortcuts() methods the user can change, add, or reset the shortcuts associated with methods available for the GMac scripting engine.

2.4. The Manager: GMacScriptManager Class

The GMacScriptManager  class is used by the GMacIDE to:

  1. Translate the script entered by the user into correct C# class definition code, using the GMacScriptGenerator class.
  2. Compile the generated C# class code in memory using the CS-Script library.
  3. Execute the compiled code that typically contains many calls to GMac services implemented as public methods by the GMacScriptInterpreter class.
  4. Report any errors during any of the previous 3 phases to the GMac interface.

 

Leave a Reply