Christian Schneider, Miro Spönemann TypeFox.io DEBUGGING DSLS WITH XTEXT’S NEW TRACING API 1
Debugging of Generated Code
What’s needed? • Mapping of code regions in source to code regions in target • Data structures implemented in org.eclipse.xtext
What’s already there? • Xtext maps AST elements to code regions in source
What’s missing? • Mapping of AST elements to code regions in target public class Debugging { public static void main(final String[] args) throws Throwable { IntegerRange _upTo = new IntegerRange(2, 100); final List<Integer> candidates = IterableExtensions.<Integer>toList(_upTo); List<Integer> _immutableCopy = ImmutableList.<Integer>copyOf(candidates); final Function1<Integer, Boolean> _function = new Function1<Integer, Boolean>() { @Override public Boolean apply(final Integer i) { return Boolean.valueOf(candidates.contains(i)); } }; Iterable<Integer> _filter = IterableExtensions.<Integer>filter(_immutableCopy final Function2<List<Integer>, Integer, List<Integer>> _function_1 = new Function2<List<Integer>, Integer, List<Integer>>() { @Override public List<Integer> apply(final List<Integer> res, final Integer i) { final Function1<Integer, Boolean> _function = new Function1<Integer, Boolean>() { @Override public Boolean apply(final Integer j) { return Boolean.valueOf((((j).intValue() % (i).intValue()) == 0)); } }; Iterable<Integer> _filter = IterableExtensions.<Integer>filter(candidates final List<Integer> sieved = IterableExtensions.<Integer>toList(_filter Iterables.removeAll(res, sieved); return res; } }; List<Integer> _fold = IterableExtensions.<Integer, List<Integer>>fold(_filter String _join = IterableExtensions.join(_fold, ", "); InputOutput.<String>println(_join); InputOutput.<String>println("done"); }
Requirements • API for tracing code regions in code generators • Should integrate nicely with Xtend’s template expressions • Trace code regions per eObject • Attributes: Distinguish code regions corresponding to attributes • Containment: Distinguish code regions corresponding to children
XTEND http://xtend-lang.org def foo(Object parameter) ''' @Accessors Code code «parameter» code @FinalFieldsConstructor class Foo implements Bar { code with leading white space @Delegate «IF guard» val Bar bar guard is satisfied code } «ELSE» guard is missed code «ENDIF» «FOR element : list» element «element.name» code: def String name(Object o) { more element specific code ... «ENDFOR» } '''
Example https://github.com/TypeFox/tracing_codegen
Model: types+=ClassDeclaration*; class Person { ClassDeclaration: name: string 'class' name=ID '{' bag: Bag members+=Member* getCash(): number { '}'; bag.wallet.cash } Member: } Property | Operation; Property: name=ID ':' type=TypeRef; Operation: name=ID '(' parameters+=Parameter* ')' ':' type=TypeRef '{' expression=Expression? '}';
class MyDslGenerator extends AbstractGenerator { override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { val model = resource.contents.head as Model val baseName = resource.baseName fsa.generateFile(baseName + '.h', ''' /******************************** * Header file for «resource.URI.lastSegment» */ #ifndef «baseName.toUpperCase»_H #define «baseName.toUpperCase»_H «FOR c : model.types» «generateHeader(c)» «ENDFOR» #endif ''') } }
class MyDslGenerator extends AbstractGenerator { override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { val model = resource.contents.head as Model val baseName = resource.baseName fsa.generateTracedFile(baseName + '.h', model, ''' /******************************** * Header file for «resource.URI.lastSegment» */ #ifndef «baseName.toUpperCase»_H #define «baseName.toUpperCase»_H «FOR c : model.types» «generateHeader(c)» «ENDFOR» #endif ''') } }
protected def generateHeader(ClassDeclaration decl) ''' /* * Declaration of «decl.name» class */ struct «decl.name» { «FOR prop : decl.members.filter(Property)» /* Property «decl.name».«prop.name» */ «generateDeclaration(prop)» «ENDFOR» }; '''
@Traced protected def generateHeader(ClassDeclaration decl) ''' /* * Declaration of «decl.name» class */ struct «decl.name» { «FOR prop : decl.members.filter(Property)» /* Property «decl.name».«prop.name» */ «generateDeclaration(prop)» «ENDFOR» }; '''
@Traced protected def generateHeader(ClassDeclaration decl) ''' /* * Declaration of «decl.name» class */ struct «decl._name» { «FOR prop : decl.members.filter(Property)» /* Property «decl.name».«prop.name» */ «generateDeclaration(prop)» «ENDFOR» }; '''
ClassDeclaration: ClassDeclaration MyDslFactory 'class' name=ID '{' name: String createClassDeclaration() members+=Member* members: Member[] … '}'; Xtext + EMF @TracedAccessors(MyDslFactory) class MyDslTraceExtensions {} Xtend Annotation Processor public IGeneratorNode _name(ClassDeclaration target) { … } @Inject extension MyDslTraceExtensions
The Generator Tree double ; __local_2 cash = -> __local_1 double __local_2 = __local_1->cash;
The Generator Tree AST Location double ; Location __local_2 cash = Location -> __local_1
Building a Generator Tree val node = trace(expression) node.append(generateType(expression.feature.type)) node.append(' ') node.append(resultVar) node.append(' = ') node.append(receiverVar ?: 'this') node.append('->') node.append(expression.feature.name)
Demo
What else? • What if debugging the DSL is not possible? • Feeding back changes in target to the source is strongly recommended
What else?
To Go Further https://github.com/TypeFox/tracing_codegen
Evaluate the Sessions Sign in and vote at eclipsecon.org -1 0 +1
Recommend
More recommend