Flow DSL is designed to mimic haxe language syntax and remove some Xml syntax weirdness.
Lets's begin first by summarizing why we want to have an alternative to xml protocol:
- Xml uses a tree hierarchy to define informations hierarchy, but hexMachina final grammar uses a flat hierarchy. So, Xml nodes structure (parent/children) brings more complexity than real advantages.
- Xml is verbose. To describe a simple information, like a primitive value assignment, you have to write tons of useless informations.
- Xml is hard to read by its node redundancy structure, and as well, for the reasons I listed above.
- Xml is not the best protocol to express complex engineering structures/informations. Let's take a common example: Class definition.
Now, let me add a few quick comparisons beetween the both DSL formats (Xml and Flow).
Xml first:
<test id="age" type="Int" value="45"/>
And now, the Flow version:
age = 45;
Xml first:
<serviceLocator id="serviceLocator" type="hex.collection.HashMap<Class<Dynamic>, Class<Dynamic>">
<item>
<key type="Class" value="mock.IMockService"/>
<value type="Class" value="mock.MockService"/>
</item>
<item>
<key type="Class" value="mock.IAnotherMockService"/>
<value type="Class" value="mock.AnotherMockService"/>
</item>
</serviceLocator>
And now, the Flow version:
serviceLocator = new hex.collection.HashMap<Class<IService>, Class<IService>>
([
mock.IMockService => mock.MockService,
mock.IAnotherMockService => mock.AnotherMockService
]);
Defining context
@context( name = 'myContextName' )
{
myString = 'hello world';
}
File compilation
var assembler = BasicFlowCompiler.compile( "context/flow/testBuildingString.flow" );
Locate ID
factory = assembler.getApplicationContext( "myContextName", ApplicationContext ).getCoreFactory();
var myString = factory.locate( 'myString' );
Null value assignment to an ID
@context( name = 'applicationContext' )
{
value = null;
}
Boolean value assignment to an ID
@context( name = 'applicationContext' )
{
b = true;
}
String value assignment to an ID
@context( name = 'applicationContext' )
{
s = 'hello';
}
Int value assignment to an ID
@context( name = 'applicationContext' )
{
i = -3;
}
UInt value assignment to an ID
@context( name = 'applicationContext' )
{
i = 3;
}
Hexadecimal value assignment to an ID
@context( name = 'applicationContext' )
{
i = 0xFFFFFF;
}
Anonymous object
@context( name = 'applicationContext' )
{
obj = { name: "Francis", age: 44, height: 1.75, isWorking: true, isSleeping: false };
}
Simple class instance
@context( name = 'applicationContext' )
{
instance = new hex.mock.MockClassWithoutArgument();
}
Simple class instance with primitive arguments passed to the constructor
@context( name = 'applicationContext' )
{
size = new hex.structures.Size( 10, 20 );
}
Building an instance with primitive references passed to its constructor
@context( name = 'applicationContext' )
{
x = 1;
y = 2;
position = new hex.structures.Point( x, y );
}
Building multiple instances and pass some of them as constructor arguments
@context( name = 'applicationContext' )
{
rect = new hex.mock.MockRectangle( rectPosition.x, rectPosition.y );
rect.size = rectSize;
rectSize = new hex.structures.Point( 30, 40 );
rectPosition = new hex.structures.Point();
rectPosition.x = 10;
rectPosition.y = 20;
}
Building instances with multiple references passed to the constructor
@context( name = 'applicationContext' )
{
chat = new hex.mock.MockChat();
receiver = new hex.mock.MockReceiver();
proxyChat = new hex.mock.MockProxy( chat, chat.onTranslation );
proxyReceiver = new hex.mock.MockProxy( receiver, receiver.onMessage );
}
Array filled with references
@context( name = 'applicationContext' )
{
fruits = new Array<hex.mock.MockFruitVO>( fruit0, fruit1, fruit2 );
empty = [];
text = [ "hello", "world" ];
fruit0 = new hex.mock.MockFruitVO( "orange" );
fruit1 = new hex.mock.MockFruitVO( "apple" );
fruit2 = new hex.mock.MockFruitVO( "banana" );
}
Assign class reference to an ID
@context( name = 'applicationContext' )
{
RectangleClass = hex.mock.MockRectangle;
classContainer = { AnotherRectangleClass: RectangleClass };
}
Hashmap filled with references
@context( name = 'applicationContext' )
{
fruits = new hex.collection.HashMap<Dynamic, hex.mock.MockFruitVO>
([
"0" => fruit0,
1 => fruit1,
stubKey => fruit2
]);
fruit0 = new hex.mock.MockFruitVO( "orange" );
fruit1 = new hex.mock.MockFruitVO( "apple" );
fruit2 = new hex.mock.MockFruitVO( "banana" );
stubKey = new hex.structures.Point();
}
Get instance from static method
@context( name = 'applicationContext' )
{
gateway = "http://localhost/amfphp/gateway.php";
service = hex.mock.MockServiceProvider.getInstance();
service.setGateway( gateway );
}
Get instance from static method with arguments
@context( name = 'applicationContext' )
{
rect = hex.mock.MockRectangleFactory.getRectangle( 10, 20, 30, 40 );
}
Get instance from object's method call returned by static method
@context( name = 'applicationContext' )
{
point = hex.mock.MockPointFactory.getInstance().getPoint( 10, 20 );
}
Building multiple instances with arguments
@context( name = 'applicationContext' )
{
rect = new hex.mock.MockRectangle( 10, 20, 30, 40 );
size = new hex.structures.Size( 15, 25 );
position = new hex.structures.Point( 35, 45 );
}
Inject into an instance
@context( name = 'applicationContext' )
{
@inject_into instance = new hex.mock.MockClassWithInjectedProperty();
}
Class instance with its abstract type mapped to context's injector
@context( name = 'applicationContext' )
{
@map_type( 'hex.mock.IMockInterface' ) instance = new hex.mock.MockClass();
}
Class instance mapped to 2 abstract types in context's injector
@context( name = 'applicationContext' )
{
@map_type( 'hex.mock.IMockInterface',
'hex.mock.IAnotherMockInterface' )
instance = new hex.mock.MockClass();
}
HashMap with mapped type
@context( name = 'applicationContext' )
{
@map_type( 'hex.collection.HashMap<String, hex.mock.MockFruitVO>' )
fruits = new hex.collection.HashMap<Dynamic, hex.mock.MockFruitVO>
([
"0" => fruit0,
"1" => fruit1
]);
fruit0 = new hex.mock.MockFruitVO( "orange" );
fruit1 = new hex.mock.MockFruitVO( "apple" );
}
Array instanciation mapped to abstract types thorugh context's injector
@context( name = 'applicationContext' )
{
@map_type( 'Array<Int>', 'Array<UInt>' ) intCollection = new Array<Int>();
@map_type( 'Array<String>' ) stringCollection = new Array<String>();
}
Instances mapped to abstract types with type params
@context( name = 'applicationContext' )
{
i = 3;
@map_type( 'hex.mock.IMockInterfaceWithGeneric<Int>',
'hex.mock.IMockInterfaceWithGeneric<UInt>' )
intInstance = new hex.mock.MockClassWithIntGeneric( i );
@map_type( 'hex.mock.IMockInterfaceWithGeneric<String>' )
stringInstance = new hex.mock.MockClassWithStringGeneric( 's' );
}
Properties assignment
@context( name = 'applicationContext' )
{
rect = new hex.mock.MockRectangle();
rect.size = size;
size = new hex.structures.Point();
size.x = width;
size.y = height;
width = 10;
height = 20;
}
Assign class reference and static variable as object's property
@context( name = 'applicationContext' )
{
object = { property: hex.mock.MockClass.MESSAGE_TYPE };
object2 = { property: hex.mock.MockClass };
instance = new hex.mock.ClassWithConstantConstantArgument
( hex.mock.MockClass.MESSAGE_TYPE );
}
Simple method call on an instance
@context( name = 'applicationContext' )
{
caller = new hex.mock.MockCaller();
caller.call( "hello", "world" );
}
Method call with argument typed from class with type paramemeters
@context( name = 'applicationContext' )
{
fruitsInterfaces = new Array<hex.mock.IMockFruit>( fruit0, fruit1, fruit2 );
fruit0 = new hex.mock.MockFruitVO( "orange" );
fruit1 = new hex.mock.MockFruitVO( "apple" );
fruit2 = new hex.mock.MockFruitVO( "banana" );
caller = new hex.mock.MockCaller();
caller.callArray( fruitsInterfaces );
}
Building multiple instances and call methods on them
@context( name = 'applicationContext' )
{
rect = new hex.mock.MockRectangle();
rect.size = rectSize;
rect.offsetPoint( rectPosition );
rectSize = new hex.structures.Point( 30, 40 );
rectPosition = new hex.structures.Point();
rectPosition.x = 10;
rectPosition.y = 20;
anotherRect = new hex.mock.MockRectangle();
anotherRect.size = rectSize;
anotherRect.reset();
}
Assign static variable to an ID
@context( name = 'applicationContext' )
{
constant = hex.mock.MockClass.MESSAGE_TYPE;
}
Pass static variable as a constructor argument
@context( name = 'applicationContext' )
{
instance = new hex.mock.ClassWithConstantConstantArgument
( hex.mock.MockClass.MESSAGE_TYPE );
}
Pass a static variable as a method call argument
@context( name = 'applicationContext' )
{
instance = new hex.mock.MockMethodCaller();
instance.call( hex.mock.MockMethodCaller.staticVar );
}
Example with DSL preprocessing
@context( ${context} )
{
${node};
}
Parse and make Xml object
@context( name = 'applicationContext' )
{
fruits = Xml.parse
(
'<root>
<node>orange</node>
<node>apple</node>
<node>banana</node>
</root>'
);
}
Parse Xml with custom parser and make custom instance
@context( name = 'applicationContext' )
{
fruits = Xml.parse
(
'<root>
<node>orange</node>
<node>apple</node>
<node>banana</node>
</root>',
hex.mock.MockXmlParser
);
}
Conditional parsing
@context( name = 'applicationContext' )
{
#if ( test || release )
message = "hello debug";
#elseif production
message = "hello production";
#else
message = "hello message";
#end
}
Use a custom application context class
@context(
name = 'applicationContext',
type = hex.ioc.parser.xml.context.mock.MockApplicationContext )
{
test = 'Hola Mundo';
}
Instantiate mapping configuration
@context( name = 'applicationContext' )
{
config = new hex.di.mapping.MappingConfiguration
([
hex.mock.IMockInterface => hex.mock.MockClass,
hex.mock.IAnotherMockInterface => instance
]);
instance = new hex.mock.AnotherMockClass();
}
Import another context to a parent one
@context( name = 'applicationContext' )
{
childContext = new Context( 'context/flow/static/childcontext.flow' );
}
Import another context to a parent one with passed references
@context( name = 'applicationContext' )
{
childContext = new Context( 'context/flow/static/message.flow', {message: message, to: name} );
message = "hello";
name = "world";
}
Import another context to a parent one with passed parameters
@context( name = 'applicationContext'
params = {x: Float, y:Float} )
{
width = sizeContext.size.width;
height = sizeContext.size.height;
sizeContext = new Context( 'context/flow/static/childcontext.flow', {xParameter: x, yParameter: y} );
}
Import two contexts with passed references from one to another
@context( name = 'applicationContext' )
{
childContext2 = new Context( 'context/flow/static/childContext.flow', {message: childContext1.message, to: childContext1.name} );
childContext1 = new Context( 'context/flow/static/anotherChildcontext.flow' );
}
Import xml context in flow context
@context( name = 'applicationContext'
params = {x: Float, y:Float} )
{
childContext = new Context( 'context/xml/static/childContext.xml', {xParameter: x, yParameter: y} );
}
Call a method in a child context with other children context references as arguments
@context( name = 'applicationContext' )
{
childContext3.o.owner.setCollection( a );
childContext3 = new Context( 'context/flow/static/importedCollectionOwner.flow' );
a = hex.mock.MockUtil.concat( childContext1.o.p, childContext2.o.p );
childContext1 = new Context( 'context/flow/static/beImportedArrayProperty.flow', { value: 3 } );
childContext2 = new Context( 'context/flow/static/beImportedArrayProperty.flow', { value: 4 } );
}
Use children context references as a parent's instance arguments
@context( name = 'applicationContext'
params = {x: Float, y:Float} )
{
size = new hex.structures.Size( xContext.x, yContext.y );
xContext = new Context( 'context/flow/static/childContext1.flow', {xParameter: x} );
yContext = new Context( 'context/flow/static/childContext2.flow', {yParameter: y} );
}
Composite runtime parameters structure
@context( name = 'applicationContext',
params = {
p:{x:Float, y:Float}, test:{p: hex.mock.IMockInterface}
} )
{
size = new hex.structures.Size( p.x, p.y );
alias = test.p;
}
Use parser metadata on the fly to define new `sum` keyword
@context( name = 'applicationContext' )
@parser( package.MyCustomSumParser )
{
s = sum( "hello", space, "world", space, "!" );
space = " ";
i = sum( 6, five );
five = 5;
p = sum( p1, new hex.structures.Point( 3, 4 ), p2 );
p1 = new hex.structures.Point( 5, 5 );
p2 = new hex.structures.Point( 3, 4 );
}