Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » Iterating through Xtext model and making decisions for Code Gen
Iterating through Xtext model and making decisions for Code Gen [message #1154233] Fri, 25 October 2013 04:31 Go to next message
Gary Worsham is currently offline Gary WorshamFriend
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!!! Very Happy

GW

[Updated on: Fri, 25 October 2013 04:48]

Report message to a moderator

Re: Iterating through Xtext model and making decisions for Code Gen [message #1154249 is a reply to message #1154233] Fri, 25 October 2013 04:48 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
Hi

Did you do the Xtext tutorials? Did you do some reading on Xtend.

A very convenient way of doing this is to use Xtends dispatch methods

--
Need training, onsite consulting or any other kind of help for Xtext?
Go visit http://xtext.itemis.com or send a mail to xtext at itemis dot de


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Iterating through Xtext model and making decisions for Code Gen [message #1154285 is a reply to message #1154249] Fri, 25 October 2013 05:19 Go to previous messageGo to next message
Uli Merkel is currently offline Uli MerkelFriend
Messages: 250
Registered: June 2013
Senior Member
Hi Gary,

you can iterate through all your elements and use a switch as a typequard to handle the different types.
If you don't want comments to be processed, just leave them out of the switch cases.

An example from my Generator which keeps the sequence intact:
«FOR Module modul : sm.modules»
«switch modul {
Module1:{genCodeModule1(modul)}
Module2:{genCodeModule2(modul)}
Trigger:{genTriggerCode(modul)}
EntitySupport:{genEntitySupport(modul)}
Entry:{genEntryCode(modul)}

«ENDFOR»


is based an an XTEXT definition of:
Model: modules+=Module+;
Module: (Module1 | Module2 | Trigger | Entry | EntitySupport);
Re: Iterating through Xtext model and making decisions for Code Gen [message #1154806 is a reply to message #1154285] Fri, 25 October 2013 13:16 Go to previous messageGo to next message
Gary Worsham is currently offline Gary WorshamFriend
Messages: 176
Registered: September 2013
Senior Member
Hi Christian,

Yes I've done a few of the tutorials and I have read about Xtend. I will admit that I have a hard time reading some of this material because I am not a professional developer and I don't understand what is meant by a lot of it. I will look into the dispatch method, thanks for the tip.

Hi Uli, thanks for the suggestion. I added this to my Xtend code:

			public void generateCode(SpinFXBlock sfxb) {
			«FOR Element e : pr.elements»
				«switch e {
					Comment:{genComment(e)}
					Instruction:{genInstruction(e)}
					Mem:{genMem(e)}
					Equate:{genEquate(e)}
					Label:{genLabel(e)}
				}»
			«ENDFOR»
				}


At this point I'm just printing out some debug stuff to the console.

		def genInstruction(Instruction inst) '''
		«switch inst {
			ReadRegister: println("ReadRegister")
			WriteRegister: println("WriteRegister")
		}»


And it does seem to be working. I'll keep working on it! Thanks so much.

GW
Re: Iterating through Xtext model and making decisions for Code Gen [message #1154834 is a reply to message #1154806] Fri, 25 October 2013 13:46 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
Hmm,

then i wonder why you did not stumble around something like

	def generate(List<Element> elements)
		'''
		«FOR it : elements»
			«handle»
		«ENDFOR»
		'''
	def dispatch handle(AElement e) '''
		Here we are
	'''
	def dispatch handle(OtherElement e) '''
		there we are
	'''


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Iterating through Xtext model and making decisions for Code Gen [message #1158698 is a reply to message #1154834] Mon, 28 October 2013 03:53 Go to previous messageGo to next message
Gary Worsham is currently offline Gary WorshamFriend
Messages: 176
Registered: September 2013
Senior Member
Hi Christian,

I went ahead and used Uli's suggestion for the Typeguard switch statement and it seems to be working pretty well so far. Is the "dispatch" approach better for any reason?

Thanks,

GW
Re: Iterating through Xtext model and making decisions for Code Gen [message #1158841 is a reply to message #1158698] Mon, 28 October 2013 06:09 Go to previous message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
Maybe this one: Xtext handles order of type hierarchy automatically.
In your example you have to care yourself.

--
Need training, onsite consulting or any other kind of help for Xtext?
Go visit http://xtext.itemis.com or send a mail to xtext at itemis dot de


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Previous Topic:JvmModelInferrer and JvmModelGenerator: generating error-less elements
Next Topic:Problem with XbaseCompiler
Goto Forum:
  


Current Time: Sat Apr 20 00:33:10 GMT 2024

Powered by FUDForum. Page generated in 0.03953 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top