The use of Interactive Domain Specific Languages for

Generative Rule-based systems


Mark Rudolph, PhD.

                                     Rovsing Aerospace, Copenhagen, Denmark.





Domain Specific Languages (DSLs) [1] allow descriptive expressions of generative

code for the production of 3D scenegraph objects. In particular, DSL syntax allows the interactive creation of ‘axioms’ for a generative rule-based system, and for interactive development of any node or branch in the scenegraph tree at any point in the process of form development. The action of the DSL is an interactive guidance and intervention which helps shape the convergence of the process to a desired form and style when developing in a generative rule-based system. The Ruby language [2] supports a particularly elegant implementation methodology for a DSL representing 3D tree structures, and for rule-based actions which are triggered by pattern matches on structure and semantics of specific nodes. The 3D tree can be created dynamically by DSL using idioms of Ruby such as meta-programming, message interception, and the passing of dynamically chosen actions using closures. Specific 3D developmental method implementations are attached dynamically to nodes which are invoked simply by interactively asserting the names of the nodes in the simple syntax of the DSL and implementations passed in as an argument.  Particular forms can be placed at positions in the scenegraph by assertions in the DSL. At the same time a method of the same name as the node can be dynamically called on the node by further assertions of the name in the DSL. At the same time Rule objects may traverse the tree in a 'Visitor' design pattern [3], or dually, Transform3D facts can traverse rule subsets, modifying nodes and the tree structure by pattern matching  against properties of nodes and their associated transform and semantic values, and applying actions when a match is found.


1. Introduction

The motivation for using generative systems for production of 3D objects is the difficulty and tediousness of modelling by use of tools and hand construction. The need for skilled labour on each piece using expensive tools drives up the cost of production and slows completion times. The situation resembles that of a Medieval guild in which expensive skilled artists labour extensively at one piece at a time. Biology inspired practices, rule-based programming, and interactive DSL-based command syntax, offer a promising solution, allowing both automated and interactive production methodology based on 'growing' instances instead of building them. The methodology is efficient and automated, and the expression of stylistic preferences in rule sets permits decision making by an inference system instead of by the use of complex and tedious modelling toolsets. In addition, DSLs can be used to interactively guide the tendency of form creation at any point in the process. Sentences in the DSL represent branches in the scenegraph tree but at the same time are executing snippets of Ruby program code. At any time in the interactive session entire scripts of Ruby code can be run as well. In this manner automated rule actions and interactive assembly and shaping can be used in complementary sessions during the overall process of form development. 


The basic terminology is borrowed from genetics and corresponds essentially to biological meaning. The fact tree consisting of hierarchical 3D transform information is referred to as the ‘genotype’ which in biology refers to the inheritable information carried by all living organisms [4]  In the specialized meaning in 3D graphics the genotype is an abstract 3D structure with either no associated form or perhaps merely a simple bounding box at each leaf in the fact tree. The ‘populated genotype’ is a genotype in which all components contain a non-trivial String form in a text syntax which can be rendered and made visible in 3D space. Production rules act on an initial genotype ‘axiom’ and through iterative operation produce a more and more complex genotype. The real or potential form associated with each leaf is referred to as a ‘component.’


The ‘phenotype’ of an organism represents its actual physical properties, such as height, weight, color, shape and so on. In the context of 3D scenegraphs the phenotype is an isomorphic mapping of the genotype populated with a 3D form at each leaf expressed in a specialized syntax which can be both rendered visibly and programmed. Phenotypes are created by a different category of rules called population rules. A clear distinction between genotype and phenotype is that genotypes carry only structural information whereas phenotypes carry both structural information and phenomenal information about rendered forms.  The structural element of the description can be thought of as the objectified process of moving a component in 3D by a sequence of hierarchical 3D operations.  The form of a component refers to its shape, material, geometry, surface, and animation and behavioral properties.


                                               Figure 1: isomorphic trees



Each node in the scenegraph tree also has a name and can be referred to by name.

A DSL is a specialized language which has linguistic meaning but is also executable program code. The use of a DSL enables the speaking of names in the scenegraph as a way to initially create a node of that name, and define a method on that node of the same which is then executed in subsequent speaking of that name.  Ruby is an ideal language to implement DSLs and the interactive version of Ruby called ‘irb’ is an ideal environment in which to run the development process. Ruby is an interpreted language so there is no compilation of source code to executable code. The source code is also the executable code which runs in the Ruby interpreter ‘ruby.’ The irb interactive Ruby interpreter is the same interpreter but runs line by line in an interactive session. The process of creating scenegraph structures is to describe their structure in the language of the DSL which is run in the interpreter to gradually create the tree described step by step in the sentences of the DSL which are written at the command line. The typing of sentences of the DSL speak and represent the structure of a branching tree. However, the sentence is also executable Ruby code which runs after enter is pressed to start irb.  A simple example is the following:





This utterance creates a unique ‘root’ node in the scenegraph tree by invoking the method ‘tree’ on the ‘Main’ context Object, parent class of all ruby classes. The ‘tree’ method is defined on Object so as to be available at the start of the DSL and generative process.


root.branch1{p “branch1 method” }



This speaking adds a child node named ‘branch1’ to root and defines a method on root having name ‘branch1.’





This second speaking of the names executes the method branch1 on root which in this case is ‘p “branch1 method”’ which simply prints out “branch1 method.” Any node in the tree can be the leftmost word in the sentence. This generality permits interactive development of any node or branch in the tree.


These sentences in the DSL are run one by one in the irb interactive shell. However,

at any point in the process an entire Ruby program can also be run by entering

‘require <program_name>’ in the command line of irb. In this manner interactive and automated sessions can be interleaved. The DSL and its interpretation in Ruby is an ideal medium in which to apply both automated and interactive sessions of generative form creation.  The following discussion will concentrate on the particular syntax and techniques in Ruby enabling the use of an interactive DSL and automated applications of rules. The methodology of use is to alternate between interactive DSL sessions and automated development of the scenegraph tree by rule sets.  The goal is to guide the rule-based development of form to converge to a final form which has desired characteristics and style.


2. Generative Rule-based systems

A rule-based system is a software system for reasoning about and transforming a dynamic fact base according to sets of rules. In generative systems rule sets capture stylistic preferences for iteratively defining and elaborating 3D objects as they develop in space [5]. 


Rule sets are ‘swarms’ of tendencies operating on a potential form and in ways that social insects might construct a hive.  There is no central control program but complex and intricate structures are produced by sequences of small transforms. ´

A rule-based inference system consists of a dynamic set of facts called ‘working memory,’ sets of rules called a ‘rule base,’ and an inference engine consisting of a pattern matcher, an agenda which holds the set of rules activated by matches on the present cycle, and an execution engine which fires one rule per cycle.  A rule consists of a left hand side (LHS) ‘antecedent’ which is the specification of some state of fact(s), and a right hand side (RHS) ‘consequent’ which consists of actions such as method invocations, modifications assertions or retractions of facts, introductions of new rules, or modification of the set of rules able to fire in the next cycle.




                                                    Figure 2: Rule-based system



A generative rule-based system is one in which the rules act to create and modify facts corresponding to some type of structural form such as the scenegraph of a renderable 3D object. The form could also be music, text or images.  Fractals and L-systems are types of generative systems.


In the Ruby system (detailed discussion to follow) rules and facts are classes and the inference engine is eliminated since facts are transform nodes in a scenegraph tree, and rules simply traverse nodes or branches of transforms. Rules carry their own means to pattern match and execute actions upon discovery of a node which generates a match. In the application discussed here, the generation of 3D objects, the facts are graphical transforms and 3D components, and the rules perform pattern matching on transform properties, semantic propositions, and meta-information such as depth of the node in the tree, iteration number, number of nodes of a certain component from, etc.


In the generative 3D system the facts are all Transform3D objects which contain attributes of name, parent, children, depth in the tree, relative translation, rotation and scale, and absolute location, orientation and dimensions, and a set of propositions (possibly empty) which characterizes the node and/or relations between the node and other nodes.  The parent is the unique node for which the node is a child, and the children are the nodes contained in the particular node. The set of all Transform3D facts is a tree which describes the hierarchical arrangement of transform operations which operate on the a node at the root and move it out to its location, rotate it to its orientation, and scale it to its spatial dimensions.


Rules may act on facts, or vice-versa, since each extends the Node class which contains methods permitting one node to traverse the tree and perform pattern matching and actions, and also for accepting node visitors traversing the tree.  The Visitor design pattern is used in which the visitor A calls the method ‘accept’ on a tree node B with itself(A) as argument, and the implementation of accept calls the method ‘visit’ on A with itself(B) as argument. This technique is termed double dispatch.  In all cases the effect is that the rule checks if the fact has a state which matches some Boolean expression of properties, and if so the rule fires some set of actions such as creating new nodes or modifying the properties of the visited node.


Rules may be applied to facts interactively in the DSL syntax, or by programmatic iteration through nodes or branches of the scenegraph tree. When rules modify the transform properties of a particular fact A the modification must be applied to A and transmitted down the nodes of all branches beginning at A. Each node modifies its absolute transform coordinates by the modification made at A. This is achieved by the node A calling the method traverse on itself and sending itself as argument which results in A visiting itself and the entire sub-tree of A.

At each node the absolute coordinates are updated by the relative transform applied to A.


3. Semantics for Facts

Geometric aspects of the Transform3D fact provide a wealth of properties with which to match the LHS of the rules. However there is no way to determine ‘qualities’ or ‘functions’ of the component form at that node by geometric properties alone. By including a Semantics module in Transform3D the geometric properties are augmented with the ability of the Transform3D fact to contain propositions representing descriptive characteristics of its form and appearance, its function in the overall structure, its qualities as simple ‘tags,’ or its relationships to other objects. This vastly increases the decision-making capability of rules.


Instead of knowing simply the position, orientation and dimensions of an object, descriptive functionality can be encoded such as (:corner, wall1, wall2) which states the relation of :corner exists between objects wall1 and wall2. Then rules can match against (:corner,*,*) and will fire actions on all objects which are at a corner. The rule could then extract the names of the corner pair object satisfying the proposition, say :wall1 (the visited node) and :wall2 . The action might be to add a proposition (:base_of_roof, wall1, wall2) to wall1, and add a key-value pair <:base_of_roof, [[:wall1,:wall2]]> to a Hash which is accessible to all Rules.

The rule has added a key :base_of_roof with value an array of pairs consisting of the two names of the nodes which form a corner. Similar pattern matching on (:corner,*,*) at other nodes may cause additional pairs to be added. Another subset of rules might pattern mach on the :base_of_roof value in the Hash waiting for the number of pairs to equal four which would then stimulate the construction of nodes as a roof. This use of semantic ‘phermones’ models the behaviour of social insects. Termites build huge complicated mounds by simple rules and a simple system of social communication. Initially groups of termites begin to (perhaps) randomly deposit particles of moist particles. Their behaviour is to deposit the particles at points which are accumulating the most particles. Groups of towers begin to rise with all but the tallest few abandoned. Then one or more termite may sense a particular height has been attained and leave a pheromone message to cause the group to begin to angle the columns toward each other until arches are created.  Finally, another message is left to fill in the walls.


Simple propositions such as (:quality, :self, :self) act as tags which characterize the properties of the Transform3D node.  Rules can then pattern match for the presence of a quality and act accordingly.  In addition, the action of a rule may modify the set of propositions, and thus mark some location in the structure and some behavioural role as ‘completed.’ 

The DSL can select branches in the tree for which a proposition holds:


branch1.children_satisfy?(:relation).each{|node| p "inference method”; node }.etc…



This sentence selects all child nodes of branch1 which satisfy the proposition :relation

and takes action on those nodes. Rules can also use the DSL in applying actions. Since all DSL methods are added to Object, the superclass of all classes, Rule can execute DSL sentences in the context of all added methods to Object.  Therefore the actions of a rule (RHS) may include DSL syntax since the base context for all DSL messages is Object.  Thus the action ‘branch1.leaf1’ may be in the set of actions of the Rule, since Rule has the method ‘branch1’ already defined on Object, and so on Rule.


3. Use of the DSL syntax

The class Object is the parent of all classes. First we add three methods to all objects by adding them to the class Object.  These methods are used to dynamically add methods to the nodes of the tree.


class Object



  def add_singleton_method(sym,proc)

    class << self






  def add_instance_method(sym,proc)





  def add_class_method(sym,proc)

    class << self.class





These methods are defined in what in Ruby is called a module.  Modules are distinct namespaces and when ‘included’ in an object supply additional methods to the object as if the module were a superclass. In particular this module supplies three dynamic method creation operations on the ancestor of all classes, Object. The first method allows singleton methods to be added to single object instance. The second allows instance method to be added, so they are defined in every instance of a class.  The third variant allows class methods to be added to a particular class. It is interesting to note the symmetry of the singleton method addition on objects with the class method addition on classes – a class is an instance of class Class so the singleton method additions to the Class object are class methods on the specific Class.


Next we add methods to Object to permit starting of the DSL syntax at any point in the tree since the methods required are by the following defined in every object because they are define in class Object.  The method tree returns the created Transform3D node.


#global 'main' reference



class Object


  attr_reader :children


  def add_child(key,object)

    @children ||={}




  def has_child?(name)

    return @children.member?(name) ? true : false



  def tree(&block)

    if block_given? ? Object.add_instance_method(:root,lambda(&block))  :

     Object.add_instance_method(:root,lambda {@children[:root]}),$zen,{})





Ruby always executes in the context of some object called ‘self.’  Instructions not contained in an enclosing object (which forms ‘self’) execute in the context of a ‘Main’ Object. Thus the leftmost element in every DSL sentence executes on the ‘Main’ Object whereas words further right execute in the context of the preceding word object. The fundamental idea of the DSL is that the first ‘speaking’ of a DSL sentence creates a Transform3D for each word and at the same time defines a method of the same name on the preceding object which is either another Transform3D, or in the case of the leftmost word in the sentence, the ‘Main’ Object.  Therefore a sentence of the DSL like ‘a{…}.b{…}’ defines a Transform3D named a in the context of the ‘Main’ Object, defines a method also named ‘a’ on Object with the implementation contained in brackets just to the right of a. The ability enclose functionality and pass it around as an argument is the very powerful feature in Ruby called a ‘closure.’  This feature is found Ruby in all so-called ‘functional’ languages.  Continuing the DSL sentence, a Transform3D ‘b’ is created as a ‘child’ of ‘a.’ This means that b is transformed first by the translation rotation and scale of ‘a’, and then by its own transform components. This means that a form associated with ‘b’ is moved in space by the combined transforms of ‘a’ and ‘b.’  This cumulative transformation defines, for example, the position and orientation of the hand as the cumulative transforms provided by the floor on which someone is standing, the trunk of the body, and the upper and lower arms.  Further transforms associated with the wrist and fingers define the position and orientation of the fingertips.


We start the DSL construction by creating the ‘root’ of the tree which at the same time defines a method with name ‘root’ on the ‘Main’ Object. The implementation of the method ‘root’ contained in the block of code inside the parentheses is simplified for the purpose of clearer discussion. The implementation simply prints “method root.” Normally it would do something like pattern match against a property of the execution context node and if there is a match modify some properties of the node. 


root=tree{p "method root"}



This code creates root node and adds it as child of the underlying Main Object  identified by the global reference '$zen'  At the same time an instance_method is added to Object with the name :root and implementation given in the optional block. If no block is passed in the default behavior is to return a reference to the newly created node so that further DSL syntax has a Transform3D reference to support additional methods ‘spoken’ in the DSL syntax. The reason for this operation is so that we can then type 'root' and execute the method :root on the global reference to the ‘Main’ Object $zen (example rotate the entire scenegraph)

Now a method ‘branch1’ is invoked on ‘root.’


root.branch1{p "method branch1"}



However Transform3D :root has no method branch1 so another key feature of Ruby can be used to good effect. When a Ruby object receives a message to invoke a method for which there is no defined implementation a special method of class Object ‘method_missing’ is called.  Its default behavior is simply to inform that the called method name does not exist in the object at which the call was made. However, as in the technique first developed in Groovy Builders, and used to create Ruby Builders: method_missing can be overridden to do something useful. In the case of the DSL, an implementation is provided for ‘method_missing’ which has two behaviors depending on whether the call to the method has occurred before or not. If the call is the first a new Transform3D is created as a child of the node which has the operating context ‘self,’ and an instance method is defined on self so that the next invocation of the method name will find a method of that name defined on the node=self. The node which is ‘self’ is either the ‘Main’ Object (referenced also by $zen) when the method name is leftmost in a DSL sentence, or the previous Transform3D node whose name is the DSL word directly to the left of the method word. In the DSL sentence above ‘branch1’ is not defined on ‘root,’ so method_missing is called with symbol :branch1 and the block supplied.  The block passes in the implementation of the method named ‘branch1’ and the new Transform3D created is also given the name ‘branch1’ since it is now the child of ‘root’ so is at the node position ‘branch1.’ Thus the result is to define a method on Object :branch1 which has the implementation given in the block. Finally it returns a reference to the Transform3D ,child of both Object and Transform3D so the DSL sentence can continue left to right with the addition of more words.  If the sentence were continued by the following:





the sequence of events would that the method :root would be called on the Object $zen,

the method :branch would be called on the Transform3D ‘root,’ and finally ‘method_missing’ would be called on :branch1 since there is no method :branch2 defined yet.  However, as explained before ‘method_missing’ creates a Transform3D called :branch2 placed as a child of :branch1, and defines a method :branch2 on :branch1 with the implementation passed in via the block.


The implementation of ‘method_missing is given below:



  def method_missing(key,&block)


    if self.has_child?(key) then





        block_given? ? $zen.add_instance_method(key,lambda(&block)) :




    #NOTE:self is ‘parent’





The method ‘method_missing’ is called to create a Transform3D object and defines a method

‘branch1’ on Object which thus defines the method on all objects including Transform3D

The fully defined method at :branch1 includes the following terse but powerful code:


root.branch1 do  |*proc|

               yield if block_given?   #execute block-Proc

               proc.nil?  ? (puts "method branch1") : proc.each{|p|[:branch1])}

               return @children[:branch1]

             en vd

branch1.leaf1{p "method leaf1";yield if block_given?;@children[:leaf1]}.form=filename



branch1.method_missing adds a :branch2 node to the tree and adds a method  :branch2 to node :branch1. The created method :branch2 executes the code that is written, executes a passed in block if given via ‘yield,’ and returns the node :branch2=branch1.children[:branch2]

Finally branch1.method_missing returns the :branch2 node and adds to it a 3D form described in the file ‘filename.


In addition, Transform3D includes the module Seed which defines parameterized Proc factory methods used to execute geometrical developments emerging from the point in the tree at which the seed is attached.  An example Seed method is ‘helix_y’ which cretes and emerging helix of nodes in the positive y-direction from the node at which the seed is attached.  The code is below:


def helix_y(angle,translation,spiral)

  lambda do |n| n.times{


    spiral.each{|node| node.r[0]+=angle;node.t[1]+=translation}




public :helix


This Proc factory method creates a helical development of sixteen nodes from :branch1 in the y-direction at a ‘yaw’ (rotation about the y-axis) angular increment of 0.1 radians and translation increment of 2 units, , and returns the Proc defined within helix, which is ‘called’ with an argument of the number of nodes to be created in the development. The Proc returns a reference to the last Transform3D created so that the DSL sentence can continue, and thus finally adds a leaf node to the last node of the helical development with the form at that leaf given by the filename or URL supplied. By the principle of ‘duck typing’ either a filename, URL or URI can read in and utilized to record a renderable instance of text as the form associated with a node:



root.branch1.helix_y(0.1,2,[]).call(16).leaf1{p "method leaf1";yield if  block_given?;                     @children[:leaf1]}.form=filename|URL|URI                



Now assume there is a multi-branched tree-axiom i.e. many branches such as  root.branch1.branch<2..n>{} Assume also a set of semantic propositions has been added to these nodes. Now semantic tests can be posed to the children of nodes to filter the tree branches at which to apply modification and growth.  For example:\


branch1.children_satisfy?(:relation).each{|node| p "inference method”; node }.etc…



Finally we return to the fundamental growth methodology of apply LHS Rule pattern matching to facts with the action of modification and/or growth of nodes being the RHS of Rules.  We use the simple Ruby facility for implementing the Visitor design pattern to the scenegraph tree.  The effect is for Rules to traverse the tree of facts testing a set of boolean conditions on node properties and, if there is a match, applying an array of actions on the visited node and possibly its neighbors.


The methodology is captured in several methods defined on Rule (and also defined on Fact so that Facts may also traverse the collection of Rules. In both cases Rules test conditions on properties of Transform3D Facts, and if the Boolean match expressions return true, a set of actions is executed. The difference is that Rules traversing Facts utilizes ‘double dispatch’ whereas Facts traversing Rules utilizes ‘single dispatch.’  Here is the pair of methods used to enable the traversal of a Fact tree by Rules. That is, Facts have these methods defined: 


 def traverse(visitor)

   #calls node.accept(visitor) for all nodes in the subtree



  def accept(visitor)

    return self if visitor.nil?

    if visitor.respond_to(:visit,false) then

      visitor.visit(self)      #double dispatch





Then Rules implement visit which is the following where conditions_satisfied detects the truth or falsity of the boolean expression contained in the attribute of Rule ‘conditions.’


def visit(fact)

  actions.each{|a| if conditions_satisfied_by(fact)}




So a typical usage is the following:

branch<k>.children.each{|b| b.traverse(rule_visitor); b}.etc..



Dually, Facts traversing Rules use the following methods on Rules:


def traverse(visitor)

   #calls rule..accept(visitor) for all rules in the subcollection



def accept(visitor)

   return self if visitor.nil?

      visit(visitor)      #single dispatch





The final step in the DSL is to traverse the scenegraph tree with a Rule which pattern matches and assigns any forms to nodes which are unpopulated, and writes the form of each node to a declarative file able to be rendered by an interactive 3D player.



4. Summary


The methodology of both interactive and automated form creation is implemented elegantly in the Ruby language.


First is the simple Rule traversal over the Fact tree, or conversely the Fact traversal of the rule collection.  Rules traverse facts to find appropriate objects to act upon.  Facts traverse rules to stimulate some appropriate behavior at the chosen fact node. The difference is that the action of rules acting on facts is less focused than a fact walking the rule collection.  If a fact walks the collection of rules it invites all rules of an appropriate match to act on the traveling fact. This allows amplification of an effect, as in the case of termite mound creation when a small hill stimulates large column development. The action of rules acting on facts, or facts stepping over a collection of rules, can also be used interactively in the DSL language.


The DSL is perhaps best used to make the starting ‘axiom’ for rule-based systems, and for specific intervention in the trajectory of stylistic process to influence the convergence to a desired form. The DSL enables a step by step creation and subsequent action on a branched Transform3D scenegraph tree. The structure ‘speaks’ itself over and over, the first time creating a Transform3D node object, and possibly also a 3D form, and also defining a method of the same name as the node on the Transfrom3D. Subsequent speaking of the name of the node invokes the dynamically created method which can be used to elaborate some specific 3D development at the added node. In addition the automatic rule-based system can be guided and influenced by DSL based interactive construction by adding to the rule set or modifying the behavior of existing rules.





[1] B. Tate, 2006, Crossing borders: Domain-specific languages in Active Record and Java programming, IBM Developerworks.


[2] D. Thomas with C. Fowler and A. Hunt, 2005, Programming Ruby, The Pragmatic Programmer’s LLC, USA


[3] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, 1995, Design Patterns: Elements of Reusable Object-Oriented Software, Reading, Mass., Addison-Wesley


[4] ] T. Bäck, 1996, Evolutionary Algorithms in Theory and Practice. Oxford University Press, New York.


[5] M. Rudolph, 2005, Metaforms Methodology for specifying and growing Structures of Forms for Aesthetic and Programming Content, Generative Arts Conference Milan