Build Xbase Project

Here is more detail about how I attempted to build an xbase project. See the previous page here for an explanation of what I am trying to do.

Run new xtext project wizard build xbase project
I entered the project name, language name and extension (the part after the '.' which identifies the filetype of our DSL. build xbase project
Copy and paste the grammar below into the 'Editor.xtext' file. build xbase project
grammar com.euclideanspace.euclid.Editor with org.eclipse.xtext.xbase.annotations.XbaseWithAnnotations

generate euclidmodel "https://www.euclideanspace.com/euclid"

import "http://www.eclipse.org/xtext/xbase/Xbase" as xbase
import "http://www.eclipse.org/xtext/xbase/Xtype" as xtype
import "http://www.eclipse.org/Xtext/Xbase/XAnnotations" as annotations
import "http://www.eclipse.org/xtext/common/JavaVMTypes" as types

File returns EuclidFile :
    ('package' package=QualifiedName ';'?)?
(imports+=Import)*
(euclidTypes+=Type)*
;

Import returns EuclidImport :
'import' (
  (static?='static' extension?='extension'? importedType=[types::JvmType|QualifiedName] '.' '*')
  | importedType=[types::JvmType|QualifiedName]
  | importedNamespace=QualifiedNameWithWildCard) ';'?
;

QualifiedNameWithWildCard :
  QualifiedName '.' '*';

Type returns EuclidTypeDeclaration :
  {EuclidTypeDeclaration} annotations+=XAnnotation*
  ({EuclidClass.annotationInfo = current}
    'public'? abstract?='abstract'? 'class' name=ValidID ('<' typeParameters+=JvmTypeParameter (',' typeParameters+=JvmTypeParameter)* '>')?
    ("extends" extends=JvmParameterizedTypeReference)?
    ('implements' implements+=JvmParameterizedTypeReference (',' implements+=JvmParameterizedTypeReference)*)?'{'
    (members+=Member)*
    '}'
    |{EuclidAnnotationType.annotationInfo = current}
    'annotation' name=ValidID '{'
    (members+=AnnotationField)*
    '}'
  )
;

AnnotationField returns EuclidField :
  annotations+=XAnnotation*
  (type=JvmTypeReference | (final?='val' | 'var') type=JvmTypeReference?) name=ValidID ('=' initialValue=XExpression)? ';'?
;

Member returns EuclidMember:
  {EuclidMember} annotations+=XAnnotation*
  (
    {EuclidField.annotationInfo = current}
    visibility=Visibility?
    (extension?='extension' (final?='val' | 'var')? type=JvmTypeReference name=ValidID?
    | static?='static'? (type=JvmTypeReference | (final?='val' | 'var') type=JvmTypeReference?) name=ValidID)
    ('=' initialValue=XExpression)? ';'?
    | {EuclidFunction.annotationInfo = current}
    ('def' | override?='override') visibility=Visibility? static?='static'? (dispatch?='dispatch'?)
    ('<' typeParameters+=JvmTypeParameter (',' typeParameters+=JvmTypeParameter)* '>')?
    ( =>(returnType=JvmTypeReference createExtensionInfo=CreateExtensionInfo name=ValidID '(')
    | =>(returnType=JvmTypeReference name=ValidID '(')
    | =>(createExtensionInfo=CreateExtensionInfo name=ValidID '(')
    | name=ValidID '('
    )
    (parameters+=Parameter (',' parameters+=Parameter)*)? ')'
    ('throws' exceptions+=JvmTypeReference (',' exceptions+=JvmTypeReference)*)?
    (expression=XBlockExpression)?
    | {EuclidConstructor.annotationInfo = current}
    visibility=Visibility? 'new'
    ('<' typeParameters+=JvmTypeParameter (',' typeParameters+=JvmTypeParameter)* '>')?
    '(' (parameters+=Parameter (',' parameters+=Parameter)*)? ')'
    ('throws' exceptions+=JvmTypeReference (',' exceptions+=JvmTypeReference)*)?
    expression=XBlockExpression
) ;

CreateExtensionInfo:
  'create' (name=ValidID ':')? createExpression=XExpression
;

ValidID:
  ID | 'create' | 'annotation'
;

Parameter returns EuclidParameter:
  annotations+=XAnnotation*
  parameterType=JvmTypeReference varArg?='...'? name=ValidID;

enum Visibility returns types::JvmVisibility:
  PUBLIC = 'public' | PROTECTED = 'protected' | PRIVATE = 'private';

XStringLiteral returns xbase::XExpression:
  SimpleStringLiteral
;

SimpleStringLiteral returns xbase::XExpression:
  {xbase::XStringLiteral} value=STRING
;

Run mwe2 file. This seems to detect that this is a xbase project and creates 'jvmmodel/EditorJvmModelInferrer.xtend' instead of 'generator/EditorGenerator.xtend' to hold the code generator.

 
Copy and paste the following code into the 'jvmmodel/EditorJvmModelInferrer.xtend'file:  
package com.euclideanspace.euclid.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.eclipse.xtext.xbase.jvmmodel.JvmVisibilityExtension
import com.euclideanspace.euclid.euclidmodel.EuclidFile
import com.euclideanspace.euclid.euclidmodel.EuclidAnnotationType
import com.euclideanspace.euclid.euclidmodel.EuclidClass
import com.euclideanspace.euclid.euclidmodel.EuclidConstructor
import com.euclideanspace.euclid.euclidmodel.EuclidField
import com.euclideanspace.euclid.euclidmodel.EuclidFunction

import static org.eclipse.xtext.util.Strings.*
import org.eclipse.xtext.common.types.JvmOperation
import org.eclipse.xtext.common.types.JvmField
import org.eclipse.xtext.common.types.JvmTypeReference
import org.eclipse.xtext.common.types.JvmConstructor

/**
	 * 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 closure you pass to the returned
	 *            {@link org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor.IPostIndexingInitializing#initializeLater(org.eclipse.xtext.xbase.lib.Procedures.Procedure1)
	 *            initializeLater(..)}.
	 * @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
	 *            true.
	 */
class EditorJvmModelInferrer extends AbstractModelInferrer {
     
  @Inject extension JvmTypesBuilder
//  @Inject extension IQualifiedNameProvider
  def dispatch void infer(EuclidFile element,
      IJvmDeclaredTypeAcceptor acceptor,
      boolean isPrelinkingPhase) {
      for (classElement : element.euclidTypes) {
      	if (classElement instanceof EuclidClass) {
      	  val EuclidClass ec=classElement as EuclidClass
      	  buildClass(acceptor,ec)
        }
      	if (classElement instanceof EuclidAnnotationType) {
      	  val EuclidAnnotationType eat=classElement as EuclidAnnotationType
      	  buildAnnotation(acceptor,eat)
        }
      }
  }

  def void buildAnnotation(IJvmDeclaredTypeAcceptor acceptor,
  	                 EuclidAnnotationType eat){
  	// should be toAnnotationType ?
    //acceptor.accept('''//annotation type goes here''')
    //acceptor.accept(eat.toAnnotation(eat.name)).initializeLater [
      //documentation = element.documentation
     // documentation = eat.documentation
     // for (methodElement : eat.members) {
     //   if (methodElement instanceof EuclidFunction) {
     //     //val EuclidFunction me=methodElement as EuclidFunction
     //     //members += buildMethod(me)
     //   }
     //  }
     //]
    }

  def void buildClass(IJvmDeclaredTypeAcceptor acceptor,
  	                 EuclidClass ec){
    acceptor.accept(ec.toClass(ec.name)).initializeLater [
      documentation = ec.documentation
      for (methodElement : ec.members) {
        if (methodElement instanceof EuclidFunction) {
          val EuclidFunction me=methodElement as EuclidFunction
          members += buildMethod(me)
        }
        if (methodElement instanceof EuclidConstructor) {
          val EuclidConstructor me=methodElement as EuclidConstructor
          members += buildConstructor(me)
        }
        if (methodElement instanceof EuclidField) {
          val EuclidField fe=methodElement as EuclidField
          members += buildField(fe)
        }
       }
     ]
    }

    /**
     * method definition, starts with 'def' in xtend
     */
    def JvmOperation buildMethod(EuclidFunction me){
      var String methodName = me.name;
      var JvmTypeReference methodType = me.returnType
  	  return me.toMethod(methodName,methodType) [
        //body = [append('''«me.expression»''')]
        for (par : me.parameters) {
          if (par.name != null && par.parameterType != null)
            parameters += par.toParameter(par.name,par.parameterType)
          //else
          //  println("parameter name or type = null"+par.name)
        }
        //varArgs=true // set to give a variable number of arguments
        documentation = me.documentation
        //final=true
        visibility = me.visibility //JvmVisibility 'public'/'protected'/'private'
        body = me.expression	
      ]
  	}

    /**
     * constructor, starts with 'new' in xtend
     */
    def JvmConstructor buildConstructor(EuclidConstructor me){
  	  return me.toConstructor() [
        for (par : me.parameters) {
          if (par.name != null && par.parameterType != null)
            parameters += par.toParameter(par.name,par.parameterType)
        }
        //varArgs=true // set to give a variable number of arguments
        documentation = me.documentation
        //final=true
        visibility = me.visibility //JvmVisibility 'public'/'protected'/'private'
        body = me.expression	
      ]
  	}

    /**
     * variable/value definition, starts with 'var' or 'val' in xtend
     */
    def JvmField buildField(EuclidField fe){
      if (fe.type == null) {
      	println("type = null")
      	return null;
      }
  	  return fe.toField(fe.name,fe.type) [
        //body = [append('''«me.expression»''')] 	
        //body = fe.expression	
      ]
  	}

}
Run mwe2 file again  
We then right click on 'plugin.xml' and select Run As -> Eclipse Application which starts up a new instance of Eclipse. We then use File -> New -> Plug in Project to create a project. build xbase project
We click on 'Next' and give the project a name. build xbase project

Click on 'Next' again.

I'm not sure if the project needs an activator?

build xbase project
Don't select a template and click on finish. build xbase project
When prompted for 'Open Associated Perspective' choose yes. build xbase project

add xbase.lib bundle as a dependency. This can be done by selecting MANIFEST.MF and on the 'Dependency tab click 'add'.

Select org.eclipse.xtext.xbase.lib.

build xbase project
Create a new file in the source directory with the appropriate extension for the project. build xbase project
Choose 'yes' when prompted to add Xtext nature. build xbase project

If we get problems with operators as on this screen shot:

scoping

It may be because the xtend Library is not yet added to the build path, if so we can do the following:

In the second instance of Eclipse, in the package exporer, right click on the JRE System Library directory. Then select Build Path, configure build path. xtend build path
In the Libraries tab click on the 'add library' button. xtend library
Select 'Xtend Library' and when this dialog button comes up click on finish. xtend library

Next Steps

For other information that I am gathering about xbase see these pages:


metadata block
see also:

 

Correspondence about this page

This site may have errors. Don't use for critical systems.

Copyright (c) 1998-2023 Martin John Baker - All rights reserved - privacy policy.