Iterating through Xtext model and making decisions for Code Gen [message #1154233] |
Fri, 25 October 2013 04:31 |
Gary Worsham Messages: 176 Registered: September 2013 |
Senior Member |
|
|
OK, in just 4 short weeks I've gone from knowing nothing at all about Xtext or Xtend to actually being able to generate some code! Happy days.
My Xtext grammar is being used to parse some DSP assembly code and then translate that into an equivalent Java representation for further manipulation.
Here's my grammar:
grammar com.holycityaudio.spincad.SpinCAD with org.eclipse.xtext.common.Terminals
generate spinCAD "http://www.holycityaudio.com/spincad/SpinCAD"
Program:
(elements += Element)+
;
Element:
Equate | Mem | Comment | Instruction | Label
;
//-----------------------------------------------------------------------------
Equate:
'equ' ename = ID value = SPINDOUBLE
;
Mem:
'mem' buffer = ID length = INT
;
Comment:
cname = SC_COMMENT
;
Label:
name = DR_LABEL
;
Instruction:
Inst_B15_S1_9 |
Inst_B6_S1_14 |
Inst_S1_14_S1_10 |
Inst_B6 |
Inst_B24 |
Inst |
Skip |
Jam |
LoadSinLFO |
LoadRampLFO |
ChorusReadDelay |
ChorusReadDelayPointer |
ChorusScaleOffset |
Log |
ReadDelayPointer
;
Inst_B6_S1_14:
ReadRegister |
WriteRegister |
ReadRegisterFilter |
Maxx |
WriteRegisterHighPass |
WriteRegisterLowPass
;
Inst_B15_S1_9:
ReadDelay |
WriteDelay |
WriteAllPass
;
Inst_B6:
Mulx |
Ldax
;
Inst_B24:
And |
Or |
Xor
;
Inst:
Clr | Not | Absa
;
Inst_S1_14_S1_10:
Exp | ScaleOffset
;
ReadRegister: 'rdax' arg1 = SPINREGISTER ',' arg2 = SPINDOUBLE;
WriteRegisterLowPass: 'wrlx' arg1 = SPINREGISTER ',' arg2 = SPINDOUBLE;
WriteRegisterHighPass: 'wrhx' arg1 = SPINREGISTER ',' arg2 = SPINDOUBLE ;
WriteRegister: 'wrax' arg1 = SPINREGISTER ',' arg2 = SPINDOUBLE ;
Maxx: 'maxx' arg1 = SPINREGISTER ',' arg2 = SPINDOUBLE ;
ReadRegisterFilter: 'rdfx' arg1 = SPINREGISTER ',' arg2 = SPINDOUBLE ;
Mulx: 'mulx' arg1 = ID ;
Ldax: 'ldax' arg1 = ID ;
Exp: 'exp' arg1 = SPINDOUBLE ',' arg2 = SPINDOUBLE ;
Log: 'log' arg1 = SPINDOUBLE ',' arg2 = SPINDOUBLE ;
ScaleOffset: 'sof' arg1 = SPINDOUBLE ',' arg2 = SPINDOUBLE ;
WriteDelay: 'wra' arg1 = ID ('^' | '#' | ('+' arg3 = SPINREGISTER))? ',' arg2 = SPINDOUBLE;
WriteAllPass: 'wrap' arg1 = ID ('^' | '#' | ('+' arg3 = SPINREGISTER))? ',' arg2 = SPINDOUBLE;
ReadDelay: 'rda' arg1 = ID ('^' | '#' | ('+' arg3 = SPINREGISTER))? ',' arg2 = SPINDOUBLE;
LoadSinLFO: 'wlds' arg1 = SPINREGISTER ',' arg2 = INT ',' arg3 = INT;
LoadRampLFO: 'wldr' arg1 = SPINREGISTER ',' arg2 = INT ',' arg3 = INT;
// Chorus instructions
ChorusReadDelay: 'cho rda' ',' arg1 = SPINREGISTER ',' arg2 = SPINCHOREGFLAGS ',' arg3 = SPINMEM;
ChorusReadDelayPointer: 'cho rdal' ',' arg1 = SPINREGISTER;
ChorusScaleOffset: 'cho sof' ',' arg1 = ID ',' arg2 = SPINCHOREGFLAGS ',' arg3 = SPINDOUBLE;
ReadDelayPointer: 'rmpa' arg1 = SPINDOUBLE;
// single argument instructions
And: 'and' arg1 = SPINREGISTER;
Or: 'or' arg1 = SPINREGISTER;
Xor: 'xor' arg1 = SPINREGISTER;
Jam: 'jam' arg1=ID;
// zero argument instructions
Clr: 'clr';
Not: 'not';
Absa: 'absa';
Skip: 'skp' arg1 = ID ',' arg2 = SPINREGISTER;
//------------------------
terminal SC_COMMENT : ';' !('\n'|'\r')* ('\r'? '\n');
// data type rules
SPINDOUBLE: '-'? (ID | (INT ( '.' INT)?));
SPINREGISTER: '%'?(ID | INT);
SPINCHOREGFLAGS: (INT | (ID ('|' ID)*)?);
SPINMEM: ID ('^' | '#' | ('+' arg4 = SPINREGISTER))?;
DR_LABEL: (ID ":");
And my Xtend code generator:
/*
* generated by Xtext
*/
package com.holycityaudio.spincad.generator
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.IGenerator
import org.eclipse.xtext.generator.IFileSystemAccess
import com.holycityaudio.spincad.spinCAD.Element
import com.holycityaudio.spincad.spinCAD.Program
import com.holycityaudio.spincad.spinCAD.Instruction
import org.eclipse.xtext.naming.IQualifiedNameProvider
import com.google.inject.Inject
class SpinCADGenerator implements IGenerator {
@Inject extension IQualifiedNameProvider
override void doGenerate(Resource resource, IFileSystemAccess fsa) {
fsa.generateFile(resource.className+"CADBlock"+".java", toCADBlockCode(resource.contents.head as Program))
fsa.generateFile(resource.className+"ControlPanel"+".java", toControlPanelCode(resource.contents.head as Program))
}
def className(Resource res) {
var name = res.URI.lastSegment
return name.substring(0, name.indexOf('.'))
}
def toCADBlockCode(Program pr) '''
package com.holycityaudio.SpinCAD.CADBlocks;
import com.holycityaudio.SpinCAD.SpinCADPin;
import com.holycityaudio.SpinCAD.fxblocks.basic.SpinFXBlock;
public class «pr.eResource.className+"CADBlock"» extends SpinCADBlock {
public «pr.eResource.className+"CADBlock"»(int x, int y) {
super(x, y);
setName("«pr.eResource.className+"CADBlock"»");
}
public void editBlock(){
new «pr.eResource.className+"ControlPanel"»(this);
}
public void generateCode(SpinFXBlock sfxb) {
«FOR c : pr.elements»
«c.declareElement»
«ENDFOR»
}
}
}
'''
def toControlPanelCode(Program pr) '''
package com.holycityaudio.SpinCAD.CADBlocks;
public class «pr.eResource.className+"ControlPanel"» {
private JFrame frame;
private «pr.eResource.className+"CADBlock"» spbCB;
public «pr.eResource.className+"ControlPanel"»(«pr.eResource.className+"CADBlock"» genericCADBlock) {
spbCB = genericEQCADBlock;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame = new JFrame();
frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
«pr.eResource.className»SliderListener bEQSL = new BassmanEQSliderListener();
frame.setVisible(true);
frame.pack();
frame.setResizable(false);
frame.setLocation(spbCB.getX() + 100, spbCB.getY() + 100);
}
});
}
class «pr.eResource.className»SliderListener implements ChangeListener {
public void stateChanged(ChangeEvent ce) {
}
'''
def declareElement(Element element) '''
// «element»
'''
}
Some of this is obviously just a place holder for future functionality. Let's just zoom in on the important parts.
My model (Program) is composed of Elements, each of which could be an Equate, Mem, Instruction, Comment, or Label:
Program:
(elements += Element)+
;
Element:
Equate | Mem | Comment | Instruction | Label
;
In the Xtend I'm using this to iterate through all the elements:
def toCADBlockCode(Program pr) '''
package com.holycityaudio.SpinCAD.CADBlocks;
import com.holycityaudio.SpinCAD.SpinCADPin;
import com.holycityaudio.SpinCAD.fxblocks.basic.SpinFXBlock;
public class «pr.eResource.className+"CADBlock"» extends SpinCADBlock {
public void generateCode(SpinFXBlock sfxb) {
«FOR c : pr.elements»
«c.declareElement»
«ENDFOR»
}
}
}
'''
and all I'm doing right now is creating a Java comment with the element printed out:
def declareElement(Element element) '''
// «element»
'''
Here's what I want to do:
1) Iterate through all Elements. Ignore Comments.
2) Depending on whether it's an Equate, Mem, or Label, do some substitution and arrangement of the arguments (which currently don't all have the same names)
3) If it's an Instruction, I further want to figure out which one it is and then access the correct translation per Instruction. Notice that I have a few subgroups of instructions which are there because the arguments follow certain patterns in those groups and I wanted to write a single validator per group. Some instructions are unique and don't fall into a group.
All I've figured out is how to do #1, but I have no idea how to do #2 or #3.
Any hints?
Thanks again!!!
GW
[Updated on: Fri, 25 October 2013 04:48] Report message to a moderator
|
|
|
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.03741 seconds