Refacola Simple Fact Base DSL

The simple fact base DSL is a very simply language for defining program elements and facts in a text file rather then using a language adapter. It comes with an Xtext generated editor and a reader for creating actual refacola program elements and facts.

Dependencies

The refacola fact base DSL is not dependent from any concrete language. Thus, no validation can be performed during editing. The DSL required for reading a fact base is found in
de.feu.ps.refacola.sample.simple.factbase
The editor is defined in
de.feu.ps.refacola.sample.simple.factbase.ui

How it works

The fact base which is usually created by a special manually written language adapter, such as the EiffelInfoReader, can be defined in a simple text file. The format is pretty simple as you can see from its grammar:

FactBase:       name=ID "{" 
                    ( Element |FactDef)* 
                "}";

Element:        kind=ID name=ID 
                    (Attribute)* 
                ";" ; 	
Attribute:      StringAttr | ReferenceAttr | EnumAttr;
StringAttr:     propName=ID     value=STRING;
ReferenceAttr:  propName=ID     value=[Element];
EnumAttr:       propName=ID "#" value=ID;
	
FactDef:        factName=ID "(" Arg "," Arg ("," Arg)* ")" ";";
Arg:            ElementRef | Index;
ElementRef:     ref=[Element];
Index:          "@" value=INT; 

That is, the fact base consists of elements and fact definitions.

An element has a kind, such as "Class" or "TypeReference". These kinds are language specific, but since the fact base DLS has no dependencies to any concrete language (due to simplicity) you have to manually ensure the kind actually exist. Element have also a name, which is used within the fact base to reference the element.

An attribute has a name and a value, again you have to manually ensure the property actually exist for the given kind. There exist two kind of attributes: Simple string attributes (e.g., for identifiers), and references to elements. Note that these references link to the element defined in the fact base, and they do not imply new elements (for references) to be created.

Facts have a name, again you have to manually ensure the fact/query actually exist. The arguments of facts are either references to program elements or index values.

Example

The following snippet shows a very simple fact base:

Sample1 {
	Class B
		identifier "B";
		
	Class A
		identifier "A";
	Feature A_foo
		identifier "foo" 	
		location A;
	has(A, A_foo);	
	
	TypeReference ref_A1
		identifier "A"
		location A;
	parameterType(A_foo, ref_A1, @0);
	
	Feature A_bar	
		identifier "bar"
		location A;
	has(A, A_bar); 
	
	TypeReference ref_B1
		identifier "B"
		location A;	
	parameterType(A_bar, ref_B1, @0);
}

Note that the fact "parameterType" requires a type reference. This type reference has to be defined as a program element as well!

Tooling

The fact base DSL comes with a syntax aware editor. It provides content assist for all references and also for kinds, properties, and fact names. The proposed kinds, properties, and fact names are derived from all used names within the fact base and do not reflect the actual language definition.

Fact Base Writer

When testing constraints for an arbitrary language, usually a special parser (IProgramInfoProvider) is used providing the program elements and facts. During development, not only the constraints but also the parser are likely to be changed. When a test fails, it is then not always clear whether the constraint set is buggy, or if it is the parser. In order to avoid this ambiguity, it is possible to write out the program elements and facts to a fact base, using the fact base file format even if a different reader or parser has been used to create the program elements and facts in the first place. By comparing the fact base output with elder (working) versions, it is probably easier to identify existing problems.

The fact base writer writes the program elements and facts using the grammar described above. As elements are required to have a unique name, and program elements do not necessarily have name, this information has to be derived. There are several strategies for computing a name of an element:

  1. the value of a property can be interpreted as a name, such as the value of the "identifier" property.
  2. the (string representation of the) data element can be used as a name. This is especially useful if the data element already represents a unique identifier.
  3. the internal id can be used as a name, the writer adds a prefix "id" (e.g. "id1234")
The strategy can be configured, that is strategy 1) and 2) can be enabled or disabled. If a program element has no property used for creating the name in strategy 1), strategy 2) is used (if enabled). If the program element has no data element associated or this element cannot be converted to a string, the internal id (which is always available) is used. If a derived name already exists (e.g. because the property value is not necessarily unique), an index is added to the name. In order to improve the readability, reference name always have an index.

Using with resource base tests

The main idea of fact base definitions is to test special refacola features, without relying on a concrete language adapter, which may not be available or does not provide you with all necessary information. So, you probably want to use the face base with a resource test. You have to set up the test as follows:

  1. Create a new plugin project and add the following plugins to the list of dependencies:
  2. Set up the test structure according to the standard resource based structure
  3. In test properties file, specify the following properties: Example given:
    programInfoProviderClass=de.feu.ps.refacola.factbase.FactBaseReader
    programInfoProviderClass.factory=de.feu.ps.refacola.lang.simplesample.SimpleSampleFactory
    testProgramFile=in/simple.factbase
         

The fact base writer can be activated and configured with the following test properties: If test property "factbase.write" is set to true, the fact base, i.e. all elements and facts, are written to an output file using the factbase file format (regardless of the initial reader).

The format can be configured with these properties:

factbase.write
true or false, if true, the output file is created in testfolder/out, default: false
factbase.write.useDataAsName
if true, {@link IProgramElement#getData()} is used to create the name of an element, default: true
factbase.write.usePropertyAsName
if true, a property value is used as name, default: false
factbase.write.nameProperty
name of property used to retrieve element name, if usePropertyAsName is true, default: "identifier"
useDataAsName and usePropertyAsName can be set both, if an element has no property specified by nameProperty, the data field is used. If the data field is not set (or useDataAsName is false), the internal id is used to create a name. In all cases, duplicate names are avoided by adding an index to the name, if necessary.