有什么方法可以测试我引用 EAttributes 的语法吗?

Is there some way to test my grammar which refer to EAttributes?

我一度陷入困境,因为我无法使用 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”文件,其中包含一个 class“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, right, e.op)
                } else if (e.left.isVariable) {
                    getVariableReturnsBody(e.left, left, right, e.op)
                } else {
                    getOthersCompareBody(left, right, 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 right, 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 right, 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 right, String operator) {
        switch (e) {
            VariableRef: {
                e.variable.EType.name.equals("EString")
                    ? getStringCompareBody(left, right, operator) : e.variable.EType.name.equals(
                    "EBoolean") ? getBoolCompareBody(left, right, operator) : getOthersCompareBody(left, right,
                    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")
        )
    ]
]