DDL Console

Tutorial

and

Example of Hosting the DDL

 

 

DDL Console  - A Tutorial 2

Getting Started. 2

Starting the Console. 2

open. 2

errors. 4

clear 5

dir / ls. 5

The Concept of Path. 6

Arrays in the Path. 7

cd <param> / seek <param>. 7

Digression on our example. 8

readall / ra. 10

recursivereadall / rra. 10

pwd / tell / cd. 13

dospath. 13

exit / x. 13

 


DDL Console  - A Tutorial

The DDL Console is a sample test application distributed with the DDL library. The DDL console can be used for getting an understanding of the way the DDL works and how it can be hosted. You can also test your scripts with the console application. You can also use the program’s source code to understand how to interface with the API exposed by the DDL. At relevant places I will list the C# code snippet that executes, corresponding to the user interaction, to make things more clear.

 

The DDL console application is written in C# and needs the Managed C++ assembly System.DDL.dll to function.

 

The DDL project homepage is http://ddl.sscli.net .

You can mail the authors at spark@sscli.net and dolly@sscli.net

 

Getting Started

To use the DDL console you need a DDL script file and a data-file corresponding to the script file. Some script and data files have been provided as examples. This tutorial uses the script binary_tree.ddl and the data file binary_tree_data.bin as an example.

 

This document contains a description of the commands as well as a walkthrough.

 

Starting the Console

You start the console by typing ddlconsole at the command prompt.

 

c:\ddl>ddlconsole

DDL Console v0.8

>

 

The console displays its standard prompt after a startup message. At this prompt you can enter the console commands.

 

open

The first thing you will want to do will be to open a DDL script and a data file. The script file contains the actual DDL code that you write and the data file contains the data that you can be read by the DDL interpreter using your script file.

 

c:\ddl>ddlconsole

DDL Console v0.8

>open

DDL Source File:

 

On typing the open command, the console prompts you for the source file. You can enter the relative or absolute path to the source file.

 

c:\ddl>ddlconsole

DDL Console v0.8

>open

DDL Source File: c:\ddl\samples\binary_tree.ddl

Data File:

 

The console prompts you for the data files name and path.

 

c:\ddl>ddlconsole

DDL Console v0.8

>open

DDL Source File: c:\ddl\samples\binary_tree.ddl

Data File: c:\ddl\samples\binary_tree_data.bin

>

 

After both file names have been given the DDL console silently displays the prompt ‘>’. At this point the source file has been loaded and parsed and the data file has been opened.

 

Now you can start querying the DDL for information. The default location the DDL rests is the instance of the init structure of you DDL script. Internally the DDL creates an instance of the init structure. The path to the init structure is represented as a single dot ‘.’. This path is analogous with paths in directory structures in a file system. The contents of any path is the member variables of the structure that is represented by that path.

This means that the contents of your ‘.’ Path is the contents of the init structure of your script.

 

As long as there are no errors the console is silent. If there are errors the console would display a message like this:

 

c:\ddl>ddlconsole

DDL Console v0.8

>open

DDL Source File: c:\ddl\samples\binary_tree.ddl

Data File: c:\ddl\samples\binary_tree_data.bin

DDL Exception:

DDLError:Failed to open Source file

>

 

You can refer to the commands errors and clear to understand what to do in the case of an error.

 

This is C# snippet that executed corresponding to the open command. If you are hosting the DDL in your application you will have a similar snippet. If you are currently trying to understand the DDL only, you can ignore the snippet.

 

void Open()

{

      if(m!=null)

            m.Dispose();

 

      Console.Write("DDL Source File: ");

      string src = Console.ReadLine();

      Console.Write("Data File: ");

      string data = Console.ReadLine();

 

      try

      {

            //this is how you intiialise the DDL

            m = new ManagedDDLEngine();

            m.LoadSourceFile(src);

            m.OpenDataFile(data);

            m.InterpretData();

      }

      catch(DDLException e)

      {

            Console.WriteLine("DDL Exception:\n{0}:{1}",e.DDLErrorType,e.DDLMessage);

      }

      catch(Exception e)

      {

            Console.WriteLine("Generic Exception:");

            Console.WriteLine(e.Message);

      }

}

 

‘m’ has been declared prior to this as

            ManagedDDLEngine m;

 

errors

The DDL internally maintains a log of all errors that occur in sequence. Whenever you ask the DDL to carry out an operation and there are errors, this list is filled. The DDL throws an exception which details the message of the first internal error. The exception also indicates that the host program should examine the internal error list and respond to the error situation.

 

The error command displays this internal error list. Each error will be displayed indicated by an error type and the error message. The error type indicates if the error originates in the lexical analyzer, parser or in some other part of the DDL.

 

This an example errors output

 

c:\ddl>ddlconsole

DDL Console v0.8

>open

DDL Source File: c:\ddl\samples\binary_tree.ddl

Data File: c:\ddl\samples\binary_tree_data.bin

DDL Exception:

DDLError:Failed to open Source file

>errors

DDLError: Failed to open Source file

DDLError: Unable to load the source file into a buffer

2 Error(s)

>

 

The DDL will not execute any other command as long as there are pending errors in the internal error list. The host of the DDL has to explicitly empty the error list. Corresponding to this the user will have to type the clear command to clear any current errors in the list.

 

The open command is an exception to this rule, as you can see from the above snippet the open command creates a new instance of the DDL interpreter thus automatically cleaning out any previous errors in the list.

 

This is the C# snippet that displays the errors

 

void GetErrors()

{

      try

      {

            int c=0;

            foreach(ErrorInfo ei in m.GetErrors())

            {

                  Console.WriteLine("{0}: {1}",ei.Type,ei.Message);

                  c++;

            }

            Console.WriteLine("{0} Error(s)",c);

      }

      catch(DDLException e)

      {

            Console.WriteLine("DDL Exception:\n{0}:{1}",e.DDLErrorType,e.DDLMessage);

      }

      catch(Exception e)

      {

            Console.WriteLine("Generic Exception:");

            Console.WriteLine(e.Message);

      }

}

 

clear

The clear command clears the internal error list (refer the errors command for details). The clear command produces no output and simply displays the prompt.

This is the clear commands internal implementation:

 

void ClearErrors()

{

      try

      {

            m.ClearErrors();

      }

      catch(DDLException e)

      {

            Console.WriteLine("DDL Exception:\n{0}:{1}",e.DDLErrorType,e.DDLMessage);

      }

      catch(Exception e)

      {

            Console.WriteLine("Generic Exception:");

            Console.WriteLine(e.Message);

      }

}

 

dir / ls

The dir command is used to list all the members in the current location. It is assumed that you have successfully opened the source and data files using open by now.

 

If you have just opened the source and data files, typing the dir command will give you an output like this

 

c:\ddl>ddlconsole

DDL Console v0.8

>open

DDL Source File: c:\ddl\samples\binary_tree.ddl

Data File: c:\ddl\samples\binary_tree_data.bin

>dir

               value  i16   16bits         #0

               Lflag   i8    8bits         #16

               Rflag   i8    8bits         #24

                 v10  EXP    0bits         #0

                left  STR   96bits         #32

               right  STR   32bits         #128

>

 

There are 5 columns of information here.

·         The first column has the name of the member variable (Ex value, Lflag)

·         The second column has its data type as declared in the DDL script file. Expression types are indicated as EXP and structure types are indicated as STR.

·         The third member indicates the size of the member in bits.

·         The fourth entry appears only is the member is an array. It indicates the length (number of elements) of the array in square brackets. (In this example none of the members are array types and so the array length indication is not given)

·         The fifth column, prefixed with a #, indicates the offset address of the member from the base address of the structure instance that it is a part of.

 

To fully understand this example output you must examine the DDL script that was loaded. This is the file binary_tree.ddl

 

//binary_tree.ddl

 

struct BinaryTreeNode : init

{

      i16   value

      v10 = value * 10

     

      i8    Lflag

      i8    Rflag

      when(Lflag == 65)

      {

            BinaryTreeNode left

      };

      when(Rflag == 65)

      {

            BinaryTreeNode right

      };

}

 

If you correlate the member types you can see that each member name has been reported to have the appropriate type.

 

Notice that the members ‘left’ and ‘right’ have been displayed in the dir output while in the script, they occur within ‘when’ clauses. This means that the DDL has evaluated their when-blocks conditions and these conditions evaluated to true.

 

There maybe other instances of the same structure where left and right need not exist.

 

The Concept of Path

After you initialize the DDL engine you are at the path ‘.’. For the DDLConsole you would do this by executing the open command successfully.

 

A path represents a structure instance. A root or the default path represents the first structure instance created by your DDL which is your init structure.

 

The contents of a path represent the members that exist in the structure instance that is represented by the path. In the default path the members indicate the members of the init structure.

 

Now any structure can contain members that are instances of other structures. To be able to access these we must change our path to indicate the other structure.

 

For example in the init structure contained 2 members one of which was a 16 bit type named ‘a’ and the other an instance of a structure type called ‘Test’ which is named ‘x’. Let all structures of type ‘Test’ contain two members ‘a’ and ‘b’ that are 16 bit types.

 

So the content of path ‘.’ will be ‘a’ and ‘x’. If we change path to ‘.x’ we will have access to the member of instance ‘x’, i.e. this path will have members ‘a’ and ‘b’

The fully qualified names of all the types in this example are

‘.a’

‘.x.a’

‘.x.b’

 

You can think of this hierarchical structure to be similar to that is object oriented programming or to traversing a directory tree.

 

Arrays in the Path

When you are changing path through an array instance, you can indicate the instance you ant to change to by appending a ‘:’ (colon) followed by the instance number. For example to represent the 3rd member into an array of structures named ‘arr’ you can say ‘arr:3

 

cd <param> / seek <param>

The cd command is used to change the current path. The cd command takes one argument which can either be a absolute path or a relative path. An absolute path starts from the root, i.e. it start with a ‘.’ (dot).

 

In our example:

 

c:\ddl>ddlconsole

DDL Console v0.8

>open

DDL Source File: c:\ddl\samples\binary_tree.ddl

Data File: c:\ddl\samples\binary_tree_data.bin

>dir

               value  i16   16bits         #0

               Lflag   i8    8bits         #16

               Rflag   i8    8bits         #24

                 v10  EXP    0bits         #0

                left  STR   96bits         #32

               right  STR   32bits         #128

>

 

We know that left and right are member structures. So we can cd to either of them.

 

c:\ddl>ddlconsole

DDL Console v0.8

>open

DDL Source File: c:\ddl\samples\binary_tree.ddl

Data File: c:\ddl\samples\binary_tree_data.bin

>dir

               value  i16   16bits         #0

               Lflag   i8    8bits         #16

               Rflag   i8    8bits         #24

                 v10  EXP    0bits         #0

                left  STR   96bits         #32

               right  STR   32bits         #128

>cd left

.left>

 

After the cd your prompt will indicate you current path. Thus we are in the structure member called left that is part of your init structure.

 

If you execute a dir here you will see the results

 

c:\ddl>ddlconsole

DDL Console v0.8

>open

DDL Source File: c:\ddl\samples\binary_tree.ddl

Data File: c:\ddl\samples\binary_tree_data.bin

>dir

               value  i16   16bits         #0

               Lflag   i8    8bits         #16

               Rflag   i8    8bits         #24

                 v10  EXP    0bits         #0

                left  STR   96bits         #32

               right  STR   32bits         #128

>cd left

.left>dir

               value  i16   16bits         #0

               Lflag   i8    8bits         #16

               Rflag   i8    8bits         #24

                 v10  EXP    0bits         #0

                left  STR   64bits         #32

.left>

 

Notice that here there is only the left member, the right member does not exist. That means that the condition for right was not satisfied in this case. You can revert back to listing of the DDL script at this point.

 

To return to the root at any point, type ‘cd .’ .

There is currently no support for moving one level up the tree.

 

This the code snippet for the cd command

 

string Seek(string[] subcmds)

{

      try

      {

            m.Seek(subcmds[1]);

            return m.Tell();

      }

      catch(DDLException e)

      {

            Console.WriteLine("DDL Exception:\n{0}:{1}",e.DDLErrorType,e.DDLMessage);

      }

      catch(Exception e)

      {

            Console.WriteLine("Generic Exception:");

            Console.WriteLine(e.Message);

      }

      return "";

}

 

Digression on our example

Our example uses the following DDL script file:

 

//binary_tree.ddl

 

struct BinaryTreeNode : init

{

      i16   value

      v10 = value * 10

     

      i8    Lflag

      i8    Rflag

      when(Lflag == 65)

      {

            BinaryTreeNode left

      };

      when(Rflag == 65)

      {

            BinaryTreeNode right

      };

}

 

This is a hex dump of the contents of the data file using (good old) debug:

 

0000:0000 01 00 41 41 02 00 41 42-03 00 42 41 04 00 42 42   ..AA..AB..BA..BB

0000:0010 05 00 43 43                                       ..CC           

 

So now if you try mapping the DDL script to this data you will notice that the value of ‘value’ of the init structure will map the locations that contain 0x01 and 0x00 making i16 value = 0x0001 (for little-endian machines)

 

Similarly i8 Rflag will be 0x41 = 65 and i8 Lflag will be 0x41 = 65. Sicnce these are both 65, the when conditions are satisfied.

 

Which means that immediately following Rflag an instance of BinaryTreeNode called left must exist.

Therefore i16 left.value will be 0x02 and 0x00 = 0x0002. And similarly

i8 left.Lflag = 0x41 = 65 and

i8 left.Lflag = 0x42 = 66

 

Therefore ‘.left’ will contain the member ‘left’ and will not contain the member ‘right’ This is what our dir output had shown us.

 

If you form a mental image of the binary tree defined by the script and the data file it will look like this

 

 

 

readall / ra

This command will read all the data and expression values in the current location. If any member is a structure it will read the value of only its 0th member.

 

For our present example this is the output

 

.>ra

               value =        1 (i16)

               Lflag =       65 (i8)

               Rflag =       65 (i8)

                 v10 =       10 (ExpressionType)

                left =        0 (StructType)

               right =        0 (StructType)

.>cd left

.left>ra

               value =        2 (i16)

               Lflag =       65 (i8)

               Rflag =       66 (i8)

                 v10 =       20 (ExpressionType)

                left =        0 (StructType)

.left>

 

This is the code snippet for the readall command:

 

string ReadAll()

{

      try

      {

            ArrayList al = m.List();

            m.GetValues(al);

            foreach(MemberInfo mi in al)

            {

                  Console.WriteLine("{0,20} = {1,8} ({2})",

                        mi.Name,mi.Value,mi.Type);

            }

      }

      catch(DDLException e)

      {

            Console.WriteLine("DDL Exception:\n{0}:{1}",e.DDLErrorType,e.DDLMessage);

      }

      catch(Exception e)

      {

            Console.WriteLine("Generic Exception:");

            Console.WriteLine(e.Message);

      }

      return "";

}

 

recursivereadall / rra

The recursive-read-all command can be used to read the values of all values of the all the variables exposed by the DDL.

 

If there is an array of values then it will read the values of all the members or till the internal MaxArrayDisplay number of elements, which ever is lesser.

 

Example show rra executed at the root and one of the structures under the root.

 

.>rra

[i16] .value = 1

[ i8] .Lflag = 65

[ i8] .Rflag = 65

[EXP] .v10 = 10

[i16] .left.value = 2

[ i8] .left.Lflag = 65

[ i8] .left.Rflag = 66

[EXP] .left.v10 = 20

[i16] .left.left.value = 3

[ i8] .left.left.Lflag = 66

[ i8] .left.left.Rflag = 65

[EXP] .left.left.v10 = 30

[i16] .left.left.right.value = 4

[ i8] .left.left.right.Lflag = 66

[ i8] .left.left.right.Rflag = 66

[EXP] .left.left.right.v10 = 40

[i16] .right.value = 5

[ i8] .right.Lflag = 67

[ i8] .right.Rflag = 67

[EXP] .right.v10 = 50

.>cd left

.left>rra

[i16] .left.value = 2

[ i8] .left.Lflag = 65

[ i8] .left.Rflag = 66

[EXP] .left.v10 = 20

[i16] .left.left.value = 3

[ i8] .left.left.Lflag = 66

[ i8] .left.left.Rflag = 65

[EXP] .left.left.v10 = 30

[i16] .left.left.right.value = 4

[ i8] .left.left.right.Lflag = 66

[ i8] .left.left.right.Rflag = 66

[EXP] .left.left.right.v10 = 40

.left>

 

Note that rra displays the fully qualified names of all the elements and also indicates their types.

 

Internally the C# snippet is implemented as a recursive function that creates all the path values and recursively  moves down each path possible.

 

string RecursiveReadAll(string path)

{

      try

      {

            string realpath = path;

            m.Seek(path);

            if(path==".")

                  path="";

 

            ArrayList ls = m.List();

            foreach(MemberInfo mi in ls)

            {

                  if(mi.Type == MemberTypes.StructType)

                  { //recurse

                        if(mi.Length == 1)

                        {

                              RecursiveReadAll(path+"."+mi.Name);

                              m.Seek(realpath);

                        }

                        else

                        {

                              for(int i=0;i<mi.Length && i<MaxArrayDisplay;i++)

                                    RecursiveReadAll(path+"."+mi.Name+":"+i.ToString());

                              m.Seek(realpath);

                              if(mi.Length > MaxArrayDisplay)

                                    Console.WriteLine("Remaining {0} array members not displayed",

                                          mi.Length - MaxArrayDisplay);

                        }

                  }

                  else if(mi.Length > 1)

                  { //loop

                        string type;

                        if(mi.Type == MemberTypes.ExpressionType)

                              type="EXP";

                        else

                              type=mi.Type.ToString();

                        for(int i=0;i<mi.Length && i<MaxArrayDisplay;i++)

                        {

                              Console.WriteLine("[{2,3}] {0,1} = {1,1}",

                                    path+"."+mi.Name+":"+i.ToString(),

                                    m.GetValue(mi.Name,i),

                                    type);

                        }

                        if(mi.Length > MaxArrayDisplay)

                              Console.WriteLine("Remaining {0} array members not displayed",

                                    mi.Length - MaxArrayDisplay);

                  }

                  else

                  {

                        string type;

                        if(mi.Type == MemberTypes.ExpressionType)

                              type="EXP";

                        else

                              type=mi.Type.ToString();

                        Console.WriteLine("[{2,3}] {0} = {1,1}",

                              path+"."+mi.Name,

                              m.GetValue(mi.Name),

                              type);

                  }

            }

      }

      catch(DDLException e)

      {

            Console.WriteLine("DDL Exception:\n{0}:{1}",e.DDLErrorType,e.DDLMessage);

      }

      catch(Exception e)

      {

            Console.WriteLine("Generic Exception:");

            Console.WriteLine(e.Message+e.StackTrace);

      }

      return "";

}

 

If you understand this snippet, you would have understood all the essentials required to host and interact with the DDL engine.

 

pwd / tell / cd

This displays the current path (the same as is displayed in the prompt). So I don’t think this would be used very much. However you will notice that pwd immediately after the open will display a ‘.’ Even though the prompt does not reflect it.

(The cd command when used with no parameters display the path)

 

dospath

This displays the current running directory of the console application.

 

exit / x

Will exit from the console.