简体   繁体   中英

Is it possible to run Xbase script from Java in Runtime?

I want to call some scripts in runtime from my Java application. But I want to hide internal model from user. So my idea to write DSL using Xtext. And add ability to user write script in this DSL and then execute it in application.

Is it possible?

There is no generic Tutorial on that. It highly depends on your rules. On your Environment etc.

here is a oversimplyfied example

grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.xbase.Xbase

generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"

Model:
    "model" name=ID "{"
        expressions+=NamedExpression*
    "}"
;

NamedExpression:
    name=ID "=" body=XExpression
;

inferrer

/*
 * generated by Xtext 2.16.0-SNAPSHOT
 */
package org.xtext.example.mydsl.jvmmodel

import com.google.inject.Inject
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.xtext.example.mydsl.myDsl.Model

/**
 * <p>Infers a JVM model from the source model.</p> 
 *
 * <p>The JVM model should contain all elements that would appear in the Java code 
 * which is generated from the source model. Other models link against the JVM model rather than the source model.</p>     
 */
class MyDslJvmModelInferrer extends AbstractModelInferrer {

    /**
     * convenience API to build and initialize JVM types and their members.
     */
    @Inject extension JvmTypesBuilder

    /**
     * The dispatch method {@code infer} is called for each instance of the
     * given element's type that is contained in a resource.
     * 
     * @param element
     *            the model to create one or more
     *            {@link org.eclipse.xtext.common.types.JvmDeclaredType declared
     *            types} from.
     * @param acceptor
     *            each created
     *            {@link org.eclipse.xtext.common.types.JvmDeclaredType type}
     *            without a container should be passed to the acceptor in order
     *            get attached to the current resource. The acceptor's
     *            {@link IJvmDeclaredTypeAcceptor#accept(org.eclipse.xtext.common.types.JvmDeclaredType)
     *            accept(..)} method takes the constructed empty type for the
     *            pre-indexing phase. This one is further initialized in the
     *            indexing phase using the lambda you pass as the last argument.
     * @param isPreIndexingPhase
     *            whether the method is called in a pre-indexing phase, i.e.
     *            when the global index is not yet fully updated. You must not
     *            rely on linking using the index if isPreIndexingPhase is
     *            <code>true</code>.
     */
    def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
        // Here you explain how your model is mapped to Java elements, by writing the actual translation code.

        // An implementation for the initial hello world example could look like this:
        acceptor.accept(element.toClass("my.company.greeting."+ element.name)) [
            for (x : element.expressions) {
                members += x.toMethod(x.name, inferredType) [
                    body = x.body
                ]
            }
        ]
    }
}

Interpreter call

package org.xtext.example.mydsl

import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.xtext.util.CancelIndicator
import org.eclipse.xtext.util.StringInputStream
import org.eclipse.xtext.xbase.interpreter.impl.DefaultEvaluationContext
import org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter
import org.xtext.example.mydsl.myDsl.Model

class Sample {

    def static void main(String[] args) {
        val userInput = '''
        model x {
            a = 1
            b = 1 + 2
            c = "Hello"
        }
        '''
        val injector = new MyDslStandaloneSetup().createInjectorAndDoEMFRegistration
        val resourceSet = injector.getInstance(ResourceSet)
        val resource = resourceSet.createResource(URI.createURI("dummy.mydsl"))
        resource.load(new StringInputStream(userInput), null)
        val model = resource.contents.head as Model
        val interpreter = injector.getInstance(XbaseInterpreter)
        for (g : model.expressions) {
            val ctx = new DefaultEvaluationContext
            val result = interpreter.evaluate(g.body, ctx, CancelIndicator.NullImpl)
            println(result.result)
        }
    }

}

you may have to customize the interpreter and additionally put stuff to the context to get it running with your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM