// Part 1 | |
org.xtext.homeautomation | |
// To Be Parsed | |
Device Window can be OPEN, CLOSED | |
Device Heating can be ON, OFF | |
Rule 'Close Window, when heating turned on' | |
when Heating.ON | |
then Window.CLOSED | |
Rule 'Switch off heating, when windows gets opened' | |
when Window.OPEN | |
then Heating.OFF | |
// Grammar | |
Model: | |
declarations+=Declaration* | |
; | |
Declaration : | |
Device | Rule | |
; | |
Device : | |
'Device' name=ID 'can' 'be' | |
(states+=State (',' states+=State)*)? | |
; | |
State : | |
name=ID | |
; | |
Rule: | |
'Rule' description=STRING | |
'when' when=[State|QualifiedName] | |
'then' then=[State|QualifiedName]; | |
QualifiedName : ID ('.' ID)*; | |
// Test | |
@Inject extension ParseHelper<Model> | |
@Inject ValidationTestHelper validator | |
@Inject IGenerator generator | |
@Test def void testSimpleFile() { | |
val model = ''' | |
Device Window can be opened, closed | |
Device Heating can be on, off | |
Rule 'Close the window when the heating is on' | |
when Heating.on | |
then Window.closed | |
Rule 'Turn off heating when window gets opened' | |
when Window.opened | |
then Heating.off | |
'''.parse | |
validator.assertNoErrors(model) | |
val fsa = new InMemoryFileSystemAccess | |
generator.doGenerate(model.eResource, fsa) | |
fsa.allFiles.values.head.toString.contains(''' | |
public static void fire(String event) { | |
if (event.equals("opened")) { | |
System.out.println("Window is now opened!"); | |
} | |
if (event.equals("closed")) { | |
System.out.println("Window is now closed!"); | |
} | |
if (event.equals("on")) { | |
System.out.println("Heating is now on!"); | |
} | |
if (event.equals("off")) { | |
System.out.println("Heating is now off!"); | |
} | |
if (event.equals("on")) { | |
fire("closed"); | |
} | |
if (event.equals("opened")) { | |
fire("off"); | |
} | |
} | |
''') | |
} | |
// Generator | |
/* | |
* generated by Xtext | |
*/ | |
package org.xtext.homeautomation.generator | |
import java.util.Scanner | |
import org.eclipse.emf.ecore.resource.Resource | |
import org.eclipse.xtext.generator.IFileSystemAccess | |
import org.eclipse.xtext.generator.IGenerator | |
import org.xtext.homeautomation.rules.Declaration | |
import org.xtext.homeautomation.rules.Device | |
import org.xtext.homeautomation.rules.Rule | |
/** | |
* Generates code from your model files on save. | |
* | |
* See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation | |
*/ | |
class RulesGenerator implements IGenerator { | |
override void doGenerate(Resource resource, IFileSystemAccess fsa) { | |
val simpleClassName = resource.getURI.trimFileExtension.lastSegment | |
if (resource.contents?.head == null) { | |
return; | |
} | |
val declarations = resource.contents.head.eContents.filter(Declaration) | |
fsa.generateFile(simpleClassName + '.java', ''' | |
public class «simpleClassName» { | |
public static void fire(String event) { | |
«FOR device : declarations.filter(Device)» | |
«FOR state : device.states» | |
if (event.equals("«state.name»")) { | |
System.out.println("«device.name» is now «state.name»!"); | |
} | |
«ENDFOR» | |
«ENDFOR» | |
«FOR rule : declarations.filter(Rule)» | |
if (event.equals("«rule.when.name»")) { | |
fire("«rule.then.name»"); | |
} | |
«ENDFOR» | |
} | |
public static void main(String... args) { | |
try («Scanner.name» scanner = new «Scanner.name»(System.in)) { | |
System.out.println("Welcome home!"); | |
System.out.println("Available commands : "); | |
«FOR device : declarations.filter(Device)» | |
«FOR state : device.states» | |
System.out.println(" «device.name» «state.name»" ); | |
«ENDFOR» | |
«ENDFOR» | |
System.out.println("Have fun!"); | |
while(true) { | |
String command = scanner.next(); | |
«FOR device : declarations.filter(Device)» | |
if (command.equalsIgnoreCase("«device.name»")) { | |
String secondaryCommand = scanner.next(); | |
«FOR state : device.states» | |
if (secondaryCommand.equalsIgnoreCase("«state.name»")) { | |
fire("«state.name»"); | |
} else | |
«ENDFOR» | |
{ | |
System.out.println("«device.name» can only have the following states: «device.states.map[name]. | |
join(',')»."); | |
} | |
} | |
«ENDFOR» | |
if (command.equalsIgnoreCase("bye")) { | |
System.out.println("Ciao!"); | |
break; | |
} | |
} | |
} | |
} | |
} | |
''') | |
} | |
def ruleMethodName(Rule device) { | |
'execute' + device.description.replaceAll('\\W', '_') | |
} | |
} | |
// domain model | |
package com.acme { | |
entity Person { | |
name : String | |
givenName : String | |
op getFullName() : String { | |
if (givenName == null) { | |
return name | |
} else { | |
return givenName + " " + name | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment