问题描述
我一度陷入困境,因为我无法使用 Junit 测试用例来测试我的语法。下面是我完整的语法。
ExpressionModel:
expression=Expression;
Expression:
Comparison;
Comparison returns Expression:
Primary ({Comparison.left=current} op=("=" | "!=" | ">=" | "<=" | ">" | "<")right=Primary)* ;
Primary returns Expression:
'(' Expression ')' |
{Not} "!" expression=Primary |
Atomic;
Atomic returns Expression:
{IntConstant} value=INT |
{StringConstant} value=STRING |
{BoolConstant} value=('true' | 'false') |
{VariableRef} variable=[ecore::EAttribute|Qualifiedname];
Qualifiedname:
ID ('.' ID)*;
如果我通过启动一个 eclipse 实例来测试我的代码生成器的这个语法,我所要做的就是在 src 文件夹中创建一个“.ecore”文件和另一个我的语法文件,我可以很容易地访问我的变量在“.ecore”文件中创建。
我的意思是在启动 Eclipse 实例后,我创建了一个“test.ecore”文件,该文件具有一个“vars”类和一个 EString 类型的 EAttribute“alpha”,我创建了另一个文件“testModel.dsl” " 现在我可以轻松访问此文件中的 "vars.alpha"。如果我想在不启动 eclipse 实例的情况下测试我的代码生成器,任何人都可以帮助我如何执行相同的步骤。对我很有帮助。
我正在尝试测试以下测试用例-->
@RunWith(XtextRunner)
@InjectWith(ExtendedMyDslInjectorProvider)
class ExpressionDSLCompilationTest {
@Inject extension CompilationTestHelper
@Inject extension ParseHelper
@Inject extension ReflectExtensions
@Inject extension IQualifiednameProvider
@Inject Provider<XtextResourceSet> resourceSetProvider
@Test
def void ReturnVariable() {
val fooPackage = EcoreFactory::eINSTANCE.createEPackage
fooPackage.name = "foo"
fooPackage.nsprefix = "foo"
fooPackage.nsURI = "http://foo"
val fooClass = EcoreFactory::eINSTANCE.createEClass
fooClass.name = "vars"
fooPackage.EClassifiers.add(fooClass)
val fooattr = EcoreFactory::eINSTANCE.createEAttribute
fooattr.name = "alpha"
fooattr.EType = EcorePackage::eINSTANCE.EString
val resourceset = resourceSetProvider.get
val resource = resourceset.createResource(URI.createURI("hiTest.ecore"))
fooClass.EStructuralFeatures.add(attr)
resource.contents.add(fooPackage)
// val model = '''foo.vars.alpha'''.parse(resourceset)
'''foo.vars.alpha'''.compile [
getCompiledClass.newInstance => [
assertEquals(
"foo.vars.alpha",it.invoke("generateCodeForExpression")
)
]
]
}
class ExtendedMyDslInjectorProvider extends ExpressionDSLInjectorProvider {
override internalCreateInjector() {
EcoreSupportStandalonesetup.setup
return super.internalCreateInjector
}
}
我已经按照https://www.eclipse.org/forums/index.php/t/1081785/中提到的步骤操作 但这没有用,因为它在我运行测试用例时给了我空指针异常。任何帮助将不胜感激。
我正在添加我的代码生成器的一部分并突出显示出错的部分。希望足够了。
class ExpressionDSLGenerator extends AbstractGenerator {
@Inject extension IQualifiednameProvider
/*Generate Java Code with the name of java file as same as that of ".mexpression" file*/
override void doGenerate(Resource resource,IFileSystemAccess2 fsa,IGeneratorContext context) {
var str = ""
for (e : resource.allContents.toIterable.filter(ExpressionModel)) {
str += e.checkCompileForFunctionsOrExpresssion
}
fsa.generateFile('''«resource.URI.lastSegment.substring(0,resource.URI.lastSegment.indexOf("."))».java''',str)
}
/*Generates the body of .Java File having class and single checkExpression
Method for Expressions and Functions Separately */
def String checkCompileForFunctionsOrExpresssion(ExpressionModel model) {
'''
public class «model.eResource.URI.lastSegment.substring(0,model.eResource.URI.lastSegment.indexOf("."))»{
public «getExpressionReturnType(model)» generateCodeForExpression() {
return «getExpressionReturnBody(model.expression)»;
}
}
'''
}
def getExpressionReturnType(ExpressionModel model) {
/*If expression is not a comparison and just a single variable or value,we must return value's data type*/
if (model.eContents.size < 2) {
model.expression.getValueReturnType
} /* Return boolean since it will be a comparison*/ else {
"boolean"
}
}
// Utility method to get return type of an expression
def getValueReturnType(Expression e) {
if (e.isInt) {
"int"
} else if (e.isstring) {
"String"
} else if (e.isVariable) {
e.variableReturnsType
} else {
"boolean"
}
}
// Utility method to set return type on the basis of variable's return type
def getvariableReturnsType(Expression e) {
switch (e) {
VariableRef: {
<part giving error:-->e.variable.EType is coming to be null,hence null pointer exception>**e.variable.EType.name.equals("EString") ? "String" : e.variable.EType.name.equals(
"EInt") ? "int" : "boolean"**</part giving error>
}
}
}
// Utility method to get return body of an expression
def String getExpressionReturnBody(Expression e) {
switch (e) {
VariableRef: {
getvariableReturn(e.variable)
}
IntConstant: {
e.value.intConstantReturn
}
BoolConstant: {
e.value.booleanConstantReturn
}
StringConstant: {
e.value.stringConstantReturn
}
Not: {
e.expression.notExpressionReturn
}
Comparison: {
val left = e.left.getExpressionReturnBody as String
val right = e.right.getExpressionReturnBody as String
if (e.left.isstring) {
getStringCompareBody(left,right,e.op)
} else if (e.left.isBoolean) {
getBoolCompareBody(left,e.op)
} else if (e.left.isVariable) {
getvariableReturnsBody(e.left,left,e.op)
} else {
getothersCompareBody(left,e.op)
}
}
}
}
// return variable's full name
def getvariableReturn(EAttribute e) {
e.fullyQualifiedname + ""
}
// return integer value
def getIntConstantReturn(int value) {
value + ""
}
// return boolean value
def getBooleanConstantReturn(String value) {
Boolean::parseBoolean(value) + ""
}
// return string value
def getStringConstantReturn(String value) {
"\"" + value + "\""
}
// return not value of the given expression
def getNotExpressionReturn(Expression value) {
"!(" + value.getExpressionReturnBody + ")"
}
// Utility method to check if Expression is a String
def isstring(Expression e) {
switch (e) {
StringConstant: {
true
}
default: {
false
}
}
}
// Utility method to check if Expression is a boolean
def isBoolean(Expression e) {
switch (e) {
BoolConstant: {
true
}
default: {
false
}
}
}
// Utility method to check if Expression is a variable
def isVariable(Expression e) {
switch (e) {
VariableRef: {
true
}
default: {
false
}
}
}
// return body of comparison expression for string
def getStringCompareBody(String left,String right,String op) {
switch (op) {
case '=': "(" + left + ".equals(" + right + "))"
case '!=': "!(" + left + ".equals(" + right + "))"
default: false + ""
}
}
// return body of comparison expression for boolean
def getBoolCompareBody(String left,String op) {
switch (op) {
case '=': "(" + left + "==" + right + ")"
case '!=': "(" + left + "!=" + right + ")"
default: false + ""
}
}
// return body of comparison expression for other's
def getothersCompareBody(String left,String op) {
switch (op) {
case '<': "(" + left + "<" + right + ")"
case '>': "(" + left + ">" + right + ")"
case '>=': "(" + left + ">=" + right + ")"
case '<=': "(" + left + "<=" + right + ")"
case '=': "(" + left + "==" + right + ")"
case '!=': "!(" + left + "==" + right + ")"
default: false + ""
}
}
// body for variable type
def getvariableReturnsBody(Expression e,String left,String operator) {
switch (e) {
VariableRef: {
e.variable.EType.name.equals("EString")
? getStringCompareBody(left,operator) : e.variable.EType.name.equals(
"EBoolean") ? getBoolCompareBody(left,operator) : getothersCompareBody(left,operator)
}
}
}
}
解决方法
@Inject extension ValidationTestHelper h
...
val model = '''foo.vars.alpha'''.parse(resourceset)
model.assertNoErrors
这是论坛片段对您的上下文的适应效果非常好
如果你想使用CompilationTestHelper
,那么你必须为resourcetset自定义它以在那里添加资源
例如
val model = '''foo.vars.alpha'''.parse(resourceset)
model.assertNoErrors
compile(resourceset) [
getCompiledClass.newInstance => [
assertEquals(
"foo.vars.alpha",it.invoke("generateCodeForExpression")
)
]
]