|
|
(25 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
− | {| border=0
| + | #REDIRECT [[OAW]] |
− | | width=90% |
| + | |
− | == Successor to Xpand/Xtend (Work in progress - feedback is highly appreciated) ==
| + | |
− | | + | |
− | It's time to think about a possible successor to Xpand/Xtend. Although the languages have proven well compared to alternatives there are a number of things which can be better and clearer based on our two years of experience with Xpand and Xtend.
| + | |
− | The main improvements we want to incorporate are:
| + | |
− | | + | |
− | === Imports ===
| + | |
− | The import mechanism should be reworked, so that every import is explicit.
| + | |
− | We won't need any metamodel configuration in the workflow nor in the editors anymore.
| + | |
− | This will not only make the setup simpler but will also improve the performance.
| + | |
− | | + | |
− | The syntax would change to something like the following:
| + | |
− | | + | |
− | import org:openarchitectureware:Extension; // native import
| + | |
− | import EMF "my/package/model.ecore"; // non-native import
| + | |
− | import UML "my/test.profile.uml" as profile; // non-native import with name space definition
| + | |
− | import Java "my.test.Class"; // non-native import introduces java types and features form my.test.Class
| + | |
− | import XSD "http://www.mywebsite.org/xsd/metamodel.xsd" // non-native import importing the types from an XSD file
| + | |
− | ... (think of Hibernate mapping files, Groovy, etc.)
| + | |
− | | + | |
− | ==== Native import ====
| + | |
− | A native import refers to another extension file imports all public members (types, functions, extensions).
| + | |
− | | + | |
− | ==== Non-native Import ====
| + | |
− | A non native import starts with an identifier pointing to an installed adapter.
| + | |
− | The adapter is responsible for loading and converting the type information from the given string. The syntax in the string is defined by the adapter. The token directly after the
| + | |
− | import keyword defines which adapter to use.
| + | |
− | | + | |
− | ==== Namespace definition ====
| + | |
− | All members are included without namespace information. If you need a namespace you can explicitely define one per import.
| + | |
− | | + | |
− | ==== Reexport ====
| + | |
− | The reexport keyword will be supported, so that
| + | |
− | imported stuff (types and functions) will be reexported.
| + | |
− | | + | |
− | Example:
| + | |
− | | + | |
− | myext1.ext
| + | |
− | | + | |
− | foo() : 'foo';
| + | |
− | | + | |
− | myext2.ext
| + | |
− | | + | |
− | reexported import myext1;
| + | |
− | bar() : 'bar';
| + | |
− | | + | |
− | client.ext
| + | |
− | | + | |
− | import myext2;
| + | |
− | fooBar() : foo()+bar();
| + | |
− | | + | |
− | === Generics ===
| + | |
− | We need full-fledged generics, which can conceptually be copied from Java's generics.
| + | |
− | | + | |
− | (complicated) Example:
| + | |
− | List<M> sort<T extends Comparable<T>, M>(List<M> toSort, (M)=>T closure)
| + | |
− |
| + | |
− | which can be used like this
| + | |
− | ['aa','b'].sort(e|e.size);
| + | |
− | | + | |
− | You don't have to deal with this complexity if you don't want to define functions which uses generics ;-)
| + | |
− | | + | |
− | === Closures ===
| + | |
− | We'll have real closures, not the built-in stuff we have now.
| + | |
− | Closure syntax:
| + | |
− | | + | |
− | { parameterList '|' expression-using-parameters-and-scope }
| + | |
− | | + | |
− | Where parameter list must be typed, either implicitly or explicitly.
| + | |
− | | + | |
− | Example:
| + | |
− | | + | |
− | {
| + | |
− | String myText := "test";
| + | |
− | (Attribute)=>Boolean myClosure := {e|e.name==myText}; // e is inferred from the declared type of the assignee
| + | |
− | myList.select(myClosure);
| + | |
− | }
| + | |
− | | + | |
− | alternatively declare the parameter types explicitly
| + | |
− | {
| + | |
− | var myText := "test";
| + | |
− | var myClosure := {Attribute e|e.name==myText};
| + | |
− | myList.select(myClosure);
| + | |
− | }
| + | |
− | | + | |
− | // type of e is inferred from the declaration of the 'select()' function, you don't have to use the curly brackets.
| + | |
− | myList.select(e|e.name == "test")
| + | |
− | | + | |
− | | + | |
− | | + | |
− | ==== Type signatures of functions ====
| + | |
− | The syntax for of a function's type signature looks as follows:
| + | |
− | | + | |
− | (parameterTypes)=>returnType
| + | |
− | | + | |
− | Examples:
| + | |
− | | + | |
− | ()=>Object
| + | |
− | (String)=>Boolean
| + | |
− | (String, Entity)=>Entity
| + | |
− | | + | |
− | Example 2: declaration of higher-order functions using generics :
| + | |
− | | + | |
− | List<T> select<T>(List<T> this, (T)=>Boolean closure) {
| + | |
− | ...
| + | |
− | }
| + | |
− | | + | |
− | === Functions ===
| + | |
− | | + | |
− | Functions can be invoked either using the functional syntax or using the member syntax (operation like, aka extensions):
| + | |
− | | + | |
− | myFunction(foo, bar) == foo.myFuntion(bar)
| + | |
− | | + | |
− | A function is declared as follows:
| + | |
− | | + | |
− | (private|cached|cached) ReturnType? functionName(declaredParameterList) ( guardExpression )? : bodyExpression ;
| + | |
− | | + | |
− | Example:
| + | |
− | private doStuff(Entity this) name!=null :
| + | |
− | name+"Stuff";
| + | |
− | | + | |
− | The detailed semantics of how the polymorphic resolution works (what role guards play here) and is described in the upcoming section.
| + | |
− | | + | |
− | or
| + | |
− | (private|cached) functionName(declaredParameterList) guardExpression blockExpression
| + | |
− | | + | |
− | Example:
| + | |
− | cached makeAbstract(Entity this) {
| + | |
− | abstract := true;
| + | |
− | name := 'Abstract'+name;
| + | |
− | this;
| + | |
− | }
| + | |
− | | + | |
− | Block expressions are explained in their own section.
| + | |
− | | + | |
− | ==== Polymorphic Resolution with signatures and guards ====
| + | |
− | Usually polymorphism is based on the types of parameters. The same applies for Xtend++.
| + | |
− | In contrast to e.g. Java we use the dynamic types (actual types at runtime) of a given set of parameters in order to find the function which
| + | |
− | best fits (has the most specific declared paramter types).
| + | |
− | | + | |
− | Example:
| + | |
− | given the following two functions
| + | |
− | | + | |
− | foo(String x) : "string";
| + | |
− | foo(Object o) : "object";
| + | |
− | | + | |
− | this assertions can be made:
| + | |
− | | + | |
− | foo('S') == "string"
| + | |
− | foo(34) == "object"
| + | |
− | foo((Object)'S') == "string" // would be "object" in Java
| + | |
− |
| + | |
− | In addition to these concept, commonly known as "multiple dispatch" or "multi method", we introduce guards which can be used to controll the resolution based on the state of a given object not only the type.
| + | |
− | | + | |
− | Example:
| + | |
− | | + | |
− | foo(String x) x.length>5 : "long";
| + | |
− | foo(String x) : "short";
| + | |
− |
| + | |
− | this assertions can be made:
| + | |
− | | + | |
− | foo('honolulu') == 'long'
| + | |
− | foo("bar") == 'short'
| + | |
− | | + | |
− | The semantics are as follows:
| + | |
− | | + | |
− | * First select all possible features (functions and operations), based on the name and the given parameter types.
| + | |
− | * Then order those features by parameter types (best match first).Those functions with the same parameter type should be order so that the ones having a guard defined come first.
| + | |
− | | + | |
− | pseudo code
| + | |
− | | + | |
− | for (Feature f : features) {
| + | |
− | if (f.hasGuard()) {
| + | |
− | if (f.guard.evaluate())
| + | |
− | return f; // return the feature where the guard evaluates to true
| + | |
− | } else {
| + | |
− | return f; // return the feature without a guard
| + | |
− | }
| + | |
− | }
| + | |
− | return null; // no invocation
| + | |
− | | + | |
− | * if there are features, but the guards evaluate to false, return null:
| + | |
− | | + | |
− | The static semantics are straight forward:
| + | |
− | * The guard must be of type boolean.
| + | |
− | | + | |
− | ====Extensions overwrite semantics====
| + | |
− | Functions and Operations can be overwritten.
| + | |
− | The precedence is based on th order of imports.
| + | |
− | Functions from later declared imports overwrite functions introduced before.
| + | |
− | Local functions overwrite imported functions.
| + | |
− | Consider overwriting the toString() Operation (which is invoked on String concatenations) for arbitrary meta types.
| + | |
− | This will allow very readable templates.
| + | |
− | | + | |
− | ==== dynamically scoped extension overwriting ====
| + | |
− | Another thing we want to address is the way one can extend generators provided by third parties (like the one shipped with GMF).
| + | |
− | So far everybody used AOP to "weave" customization and implementation in. The problem is that the generator designer does not really develop for extensibility and every Join Point becomes public API.
| + | |
− | We've been thinking about a concept called "dynamic extensions" which is a way to register extensions for a specific call graph.
| + | |
− | | + | |
− | Example:
| + | |
− | | + | |
− | with(&toString(Entity)) {
| + | |
− | callGMFCartridge(myEntity);
| + | |
− | }
| + | |
− | | + | |
− | Just to explain: '&toString(Entity) is a literal pointing to the 'toString(Entity)'-function. So one could write '&toString(Entity).evaluate(myEntity)' instead of 'myEntity.toString()' for example. The implementation of the function will be used whenever such a function is invoked within the callGMFCartridge(Entity) function (the third party cartridge).
| + | |
− | In other words one overwrites the toString() function for Entities for the following block.
| + | |
− | So what you as a generator developer could do is, provide a list of function which can be overwritten. In addition there is a final keyword, which prevents overwriting the corresponding function.
| + | |
− | | + | |
− | This won't be a replacement for AOP, but we think that the AOP feature has been missused in order to provide extensibility.
| + | |
− | In addition the extension emchanism was bound to the static context so far which really is a limitation in some situations.
| + | |
− | | + | |
− | === Code blocks ===
| + | |
− | | + | |
− | A code block is the replacement for chain expressions ( a-> b-> x) with the additional features:
| + | |
− | * provides variable declarations (Expression returning the assigned value)
| + | |
− | | + | |
− | It's something like a pseudo imperative syntax (but still is an expression!).
| + | |
− | | + | |
− | Variables are assign-once!
| + | |
− | | + | |
− | Example:
| + | |
− | | + | |
− | myExtension(String stuff) {
| + | |
− | def x := stuff.length();
| + | |
− | if x>56 then
| + | |
− | "Foo"
| + | |
− | else
| + | |
− | "Bar";
| + | |
− | endif
| + | |
− | }
| + | |
− | | + | |
− | A code block is itself an expression consisting of a list of expressions.
| + | |
− | It returns the value returned by the last expression.
| + | |
− | | + | |
− | It is possible to overwrite the scope.
| + | |
− | Example:
| + | |
− | | + | |
− | doStuff() {
| + | |
− | def x := "Foo";
| + | |
− | {
| + | |
− | def x:= "Bar";
| + | |
− | x;
| + | |
− | }
| + | |
− | }
| + | |
− | | + | |
− | will return "Bar"
| + | |
− | | + | |
− | === Object creation expression ===
| + | |
− | We are thinking about a syntax to create model graphs inline.
| + | |
− | We need this not only for model transformations but also for writing model transformation tests.
| + | |
− | | + | |
− | Example:
| + | |
− | | + | |
− | new Entity {
| + | |
− | name := "Person";
| + | |
− | references += new Reference {
| + | |
− | name := "someRef"
| + | |
− | type := anotherEntity
| + | |
− | }
| + | |
− | }
| + | |
− | | + | |
− | ==== Assignment Expressions ====
| + | |
− | They are just another syntax for invoking a setter resp. adder-operation.
| + | |
− | They return the assigned value.
| + | |
− | | + | |
− | ==== create / cache semantics ====
| + | |
− | The creation expression should replace the "create extension" mechanism from Xtend 1.0.
| + | |
− | A creation of an Object is cached if the type name is suffixed with parenthesis containing any number of arguments.
| + | |
− | The arguments act as a key.
| + | |
− | | + | |
− | The scope of the caching is per execution context, which can be reused in several invocations?
| + | |
− |
| + | |
− | Examples:
| + | |
− | | + | |
− | var paramPerOperationAndName := new Parameter cachedwith op,name {
| + | |
− | this.name := name;
| + | |
− | type := aDatatype;
| + | |
− | }
| + | |
− | | + | |
− | var localSingleton := new Foo cachedwith {
| + | |
− | stuff := "bla";
| + | |
− | }
| + | |
− | | + | |
− | ==== cross referencing ====
| + | |
− | | + | |
− | We need a way to specify cross references within a declared tree. The problem is that we need a reference to a created type after it has been created and before it will be initialized. This can be accomplished by adding a special assignment construct:
| + | |
− | | + | |
− | var x := new Entity as localRef {
| + | |
− | // x is not visible here, because the right hand expression has not been evaluated so far.
| + | |
− | // localRef holds a reference to the created but not yet initialized entity.
| + | |
− | name := "Person";
| + | |
− | references += new Reference {
| + | |
− | name := "partner"
| + | |
− | type := localRef
| + | |
− | }
| + | |
− | }
| + | |
− | | + | |
− | === Operator Overloading ===
| + | |
− | There will be predefined operators which can be used instead of the usual function invocation syntax if there is an operator for a name and a specific number of parameters.
| + | |
− | | + | |
− | Some examples:
| + | |
− | | + | |
− | add(Object a, Object b) => a + b
| + | |
− | subtract(Object a, Object b) => a- b
| + | |
− | not(Object a) => !a
| + | |
− | | + | |
− | The && and || operators are not overwriteable because of there special semantics wrt lazy evaluation.
| + | |
− | | + | |
− | === Overloading accessors ===
| + | |
− | If a function's signature follows the pattern
| + | |
− | String getFoo()
| + | |
− | | + | |
− | it can be invoked using property syntax:
| + | |
− | this.foo
| + | |
− | | + | |
− | If a function's signature follows the pattern
| + | |
− | void setFoo(String)
| + | |
− | | + | |
− | it overwrites the modify of the 'foo' property, hence it will be used within assignments:
| + | |
− | foo := 'Holla'
| + | |
− | | + | |
− | It is also possible to use such functions without having a corresponding property like:
| + | |
− | | + | |
− | getJavaName(Entity this) {
| + | |
− | if abstract then
| + | |
− | 'Abstract'name
| + | |
− | else
| + | |
− | name
| + | |
− | endif
| + | |
− | }
| + | |
− | | + | |
− | which can be used like:
| + | |
− | | + | |
− | myTemplate(Entity this) :»
| + | |
− | public class «javaName» {
| + | |
− | }
| + | |
− | «;
| + | |
− | | + | |
− | === Templates ===
| + | |
− | A template is essentially a function returning a String. I always disliked that it is not possible to mix functions and templates within one file. I also find the invocation of templates (EXPAND bla FOR foo) too verbose.
| + | |
− | | + | |
− | Example:
| + | |
− | myTemplate() :»
| + | |
− | package «packageName»;
| + | |
− | public class «name» {
| + | |
− | «foreach (attributes as a)»
| + | |
− | «if (a.type!=null) then»
| + | |
− | public «a.type» «a.name»;
| + | |
− | «endif»
| + | |
− | «endforeach»
| + | |
− | }
| + | |
− | «;
| + | |
− | | + | |
− | It's just a string literal with the xpand syntax within. Instead of '«' and '»' one can also use the common literal syntax 'foo' and "bar".
| + | |
− | | + | |
− | The FILE statement will be removed.
| + | |
− | Files can be opened through extensions:
| + | |
− | | + | |
− | generateCode(Entity e) :
| + | |
− | writeToFile(e.JavaFile(),e.toJava());
| + | |
− | | + | |
− | ==== XString - mutable, streamable, lazy string ====
| + | |
− | We want to come up with a special datatype called XString, which is mutable, streamable and evaluated lazy (on invocation of toString()).
| + | |
− | Because XStrings are mutable and are converted to a string late, it is possible to create a tree structure containing XStrings, where
| + | |
− | you can add XStrings (or normal Strings) later on.
| + | |
− | | + | |
− | Example:
| + | |
− | toJava(Entity this) :"""
| + | |
− | package «packageName()»;
| + | |
− | «(def imports:=»
| + | |
− | import java.util.*;
| + | |
− | «)»
| + | |
− | public class ...
| + | |
− | »assertImported(imports,"java.math.*")«
| + | |
− | }
| + | |
− | «;
| + | |
− | | + | |
− | assertImported(XString this, String import) :
| + | |
− | if !this.contains(import) then
| + | |
− | this.append("import "import";");
| + | |
− | ;
| + | |
− |
| + | |
− | | + | |
− | | + | |
− | === if expression ===
| + | |
− | As seen in the previous example, we want an if-expression. using if, else keywords.
| + | |
− | | + | |
− | if (predicate) expression (else if (predicate) expression)* (else expression)? endif
| + | |
− | | + | |
− | The else part is optional and will return null if not defined.
| + | |
− | | + | |
− | Example:
| + | |
− | // The following expression will return null:
| + | |
− | if (false) "Holla" endif
| + | |
− | | + | |
− | 'endif' is optional.
| + | |
− | | + | |
− | | + | |
− | | + | |
− | | + | |
− | | + | |
− | === BK added --- To be discussed ===
| + | |
− | ==== AOP for Checks ====
| + | |
− | When modifying an expression using AOP, there might be the situation that a check is no longer valid. It has to be modified as well
| + | |
− | | + | |
− | SE: We can't modify expressions using AOP. We just can wave an Advice around a function call.
| + | |
− | Shouldn't the designer of a cartridge have this in mind when declaring Join Points to be adviced by clients?
| + | |
− | I think if an advice breaks an existing constraint it breaks the contract of the adviced function.
| + | |
− | | + | |
− | === PF added --- to be discussed ===
| + | |
− | ==== Add a FOLDER keyword ====
| + | |
− | In order to create empty directory structures, it would be nice to have a FOLDER keyword.
| + | |
− | Syntax:
| + | |
− | «FOLDER expression»
| + | |
− | | + | |
− | SE: I think such things should be implemented using library functions.
| + | |
− | | + | |
− | | + | |
− | == Examples, etc. ==
| + | |
− | | + | |
− | <pre>
| + | |
− | context Entity {
| + | |
− | private String doStuff(Foo bar) {
| + | |
− | def x := 'holla';
| + | |
− | x + bar;
| + | |
− | }
| + | |
− | | + | |
− | cached Entity<T> foo<T extends Comparable<T>>(Entity<T> bla)
| + | |
− | guard bla.abstract && bla.name.startsWith('Abstract')
| + | |
− | {
| + | |
− | new Entity<T> [bla] as myEntity {
| + | |
− | bla.address := new Address as address {
| + | |
− | address.street := 'Holla Strasse 42';
| + | |
− | };
| + | |
− | bla.adress.zip := bla.zip;
| + | |
− | };
| + | |
− | }
| + | |
− | | + | |
− | private myTemplate(Entity this) :»
| + | |
− | package «packageName()»
| + | |
− | | + | |
− | public class «name» «if ^extends!=null then»extends «^extends.name»«endif» {
| + | |
− | «foreach (x := fields)»
| + | |
− | private «x.type» «x.name»;
| + | |
− | «endforeach»
| + | |
− | }
| + | |
− | «;
| + | |
− | }
| + | |
− | </pre>
| + | |
− | | + | |
− | == Grammar ==
| + | |
− | <pre>
| + | |
− | grammar Xtend;
| + | |
− | options{backtrack=true; memoize=true;}
| + | |
− | | + | |
− | @lexer::header {
| + | |
− | package org.eclipse.m2t.xtend.frontend.parser;
| + | |
− | }
| + | |
− | | + | |
− | @parser::header {
| + | |
− | package org.eclipse.m2t.xtend.frontend.parser;
| + | |
− | }
| + | |
− | | + | |
− | parse :
| + | |
− | ruleXtendFile EOF
| + | |
− | ;
| + | |
− | | + | |
− | ruleXtendFile :
| + | |
− | (ruleImport)*
| + | |
− | (ruleConcept)*
| + | |
− | ;
| + | |
− | | + | |
− | ruleImport :
| + | |
− | ('reexported')? 'import' (RULE_ID)? RULE_STRING ';'
| + | |
− | ;
| + | |
− | | + | |
− | ruleConcept :
| + | |
− | ruleContext | ruleFunction ;
| + | |
− | | + | |
− | ruleContext :
| + | |
− | 'context' ruleTypeRef '{'
| + | |
− | ruleFunction+
| + | |
− | '}'
| + | |
− | ;
| + | |
− | | + | |
− | ruleFunction :
| + | |
− | RULE_DOCUMENTATION?
| + | |
− | ( 'private'|'final'|'cached')* (ruleTypeRef)? RULE_ID('<' ruleTypeParamDeclaration (',' ruleTypeParamDeclaration )*'>')? '(' (ruleDeclaredParameter (',' ruleDeclaredParameter )*)? ')'
| + | |
− | ('if' ruleExpression )?
| + | |
− | (':' ruleExpression ';' | ruleBlockExpression)
| + | |
− | ;
| + | |
− | | + | |
− | ruleTypeParamDeclaration :
| + | |
− | RULE_ID
| + | |
− | (('extends' ruleTypeRef ('&' ruleTypeRef )*) |
| + | |
− | ('super' ruleTypeRef))?
| + | |
− | ;
| + | |
− | | + | |
− | ruleDeclaredParameter :
| + | |
− | ruleTypeRef ? RULE_ID
| + | |
− | ;
| + | |
− | | + | |
− | ruleExpression :
| + | |
− | ruleAssignment;
| + | |
− |
| + | |
− | ruleAssignment :
| + | |
− | ruleOrExpression (':=' ruleOrExpression)? |
| + | |
− | ('def' | ruleTypeRef ) RULE_ID ':=' ruleOrExpression
| + | |
− | ;
| + | |
− | | + | |
− | ruleOrExpression :
| + | |
− | ruleAndExpression ( '||' ruleAndExpression )*
| + | |
− | ;
| + | |
− | | + | |
− | ruleAndExpression :
| + | |
− | ruleRelationalExpression ('&&' ruleRelationalExpression)*
| + | |
− | ;
| + | |
− | | + | |
− | ruleRelationalExpression :
| + | |
− | ruleAdditiveExpression (('=='|'!='|'>='|'<='|'>'|'<' ) ruleAdditiveExpression )*
| + | |
− | ;
| + | |
− | | + | |
− | ruleAdditiveExpression :
| + | |
− | ruleMultiplicativeExpression (('+'|'-') ruleMultiplicativeExpression )*
| + | |
− | ;
| + | |
− | | + | |
− | ruleMultiplicativeExpression :
| + | |
− | ruleOtherOperatorExpression (('*'|'/') ruleOtherOperatorExpression )*
| + | |
− | ;
| + | |
− | | + | |
− | ruleOtherOperatorExpression :
| + | |
− | ruleUnaryExpression (('+='|'..') ruleUnaryExpression )*
| + | |
− | ;
| + | |
− | | + | |
− | ruleUnaryExpression :
| + | |
− | ruleInfixExpression
| + | |
− | | (('!'|'-') ruleInfixExpression)
| + | |
− | ;
| + | |
− | | + | |
− | ruleInfixExpression :
| + | |
− | rulePrimaryExpression ('.' ruleFeatureCall )*
| + | |
− | ;
| + | |
− | | + | |
− | rulePrimaryExpression :
| + | |
− | ruleFeatureCall |
| + | |
− | ruleBooleanLiteral |
| + | |
− | ruleNumberLiteral |
| + | |
− | ruleNullLiteral |
| + | |
− | ruleConstructorCall |
| + | |
− | ruleParanthesizedExpression |
| + | |
− | ruleBlockExpression |
| + | |
− | ruleWithExpression |
| + | |
− | ruleParanthesizedClosure |
| + | |
− | ruleCollectionLiteral |
| + | |
− | ruleTemplateExpression |
| + | |
− | ruleIfExpression |
| + | |
− | ruleSwitchExpression |
| + | |
− | ruleConstructLiteral |
| + | |
− | ruleCast |
| + | |
− | ruleCurryingWildcard;
| + | |
− | | + | |
− | ruleCast :
| + | |
− | '(' ruleTypeRef ')' ruleExpression;
| + | |
− | | + | |
− | | + | |
− | ruleCurryingWildcard
| + | |
− | :
| + | |
− | '_';
| + | |
− |
| + | |
− | ruleConstructLiteral :
| + | |
− | '&' RULE_ID ('::' RULE_ID)* ('('('..'|(ruleTypeRef (',' ruleTypeRef)*)?)')')?;
| + | |
− | | + | |
− | ruleIfExpression :
| + | |
− | 'if' ruleExpression 'then'
| + | |
− | ruleExpression
| + | |
− | ('elseif' ruleExpression 'then' ruleExpression)*
| + | |
− | ('else' ruleExpression)?
| + | |
− | 'endif'?
| + | |
− | ;
| + | |
− | | + | |
− | ruleSwitchExpression :
| + | |
− | 'switch' ruleExpression?
| + | |
− | ('case' ruleExpression ':' ruleExpression)+
| + | |
− | ('default' ':' ruleExpression)?
| + | |
− | 'endswitch'?
| + | |
− | ;
| + | |
− | | + | |
− | ruleWithExpression :
| + | |
− | 'with' '(' ruleFunctionMapping (',' ruleFunctionMapping)* ')' ruleBlockExpression
| + | |
− | ;
| + | |
− | | + | |
− | ruleFunctionMapping :
| + | |
− | ruleExpression ('as' RULE_ID )?
| + | |
− | ;
| + | |
− | | + | |
− | ruleBlockExpression :
| + | |
− | '{'
| + | |
− | (ruleExpression ';')*
| + | |
− | '}'
| + | |
− | ;
| + | |
− | | + | |
− | ruleParanthesizedClosure :
| + | |
− | '{'
| + | |
− | ruleClosure
| + | |
− | '}'
| + | |
− | ;
| + | |
− | | + | |
− | ruleClosure :
| + | |
− | (ruleDeclaredParameter (',' ruleDeclaredParameter)*)? '|' ruleExpression
| + | |
− | ;
| + | |
− | | + | |
− | ruleParanthesizedExpression :
| + | |
− | '(' ruleExpression ')'
| + | |
− | ;
| + | |
− | | + | |
− | ruleFeatureCall :
| + | |
− | ruleOperationCall |
| + | |
− | rulePropertyCall;
| + | |
− | | + | |
− | rulePropertyCall :
| + | |
− | RULE_ID
| + | |
− | ;
| + | |
− | | + | |
− | ruleOperationCall :
| + | |
− | RULE_ID '('(ruleExpression (','ruleExpression )* | ruleClosure)?')'
| + | |
− | ;
| + | |
− | | + | |
− | ruleConstructorCall :
| + | |
− | 'new' ruleTypeRef ('['(ruleExpression (','ruleExpression)*)?']')?
| + | |
− | ('as' RULE_ID)?
| + | |
− | ruleBlockExpression
| + | |
− | ;
| + | |
− | | + | |
− | ruleBooleanLiteral :
| + | |
− | 'false'|'true'
| + | |
− | ;
| + | |
− | | + | |
− | ruleNullLiteral :
| + | |
− | 'null'
| + | |
− | ;
| + | |
− | | + | |
− | ruleNumberLiteral :
| + | |
− | ruleIntLiteral |
| + | |
− | ruleDoubleLiteral;
| + | |
− |
| + | |
− | ruleCollectionLiteral
| + | |
− | : ruleListLiteral |
| + | |
− | ruleMapLiteral;
| + | |
− | | + | |
− | ruleListLiteral :
| + | |
− | '['(ruleExpression(',' ruleExpression)*)? ']'
| + | |
− | ('<' ruleTypeRef '>')?;
| + | |
− | | + | |
− | ruleMapLiteral :
| + | |
− | '['(':' | ruleMapEntry (',' ruleMapEntry)*)']'
| + | |
− | ('<' ruleTypeRef ',' ruleTypeRef '>')?
| + | |
− | ;
| + | |
− | | + | |
− | ruleMapEntry :
| + | |
− | ruleExpression ':' ruleExpression
| + | |
− | ;
| + | |
− | | + | |
− | ruleIntLiteral :
| + | |
− | RULE_INT
| + | |
− | ;
| + | |
− | | + | |
− | ruleDoubleLiteral :
| + | |
− | RULE_REAL
| + | |
− | ;
| + | |
− | | + | |
− | ruleTypeRef :
| + | |
− | ruleSimpleTypeRef |
| + | |
− | ruleClosureTypeRef ;
| + | |
− | | + | |
− | ruleClosureTypeRef :
| + | |
− | ('('ruleTypeRef (',' ruleTypeRef )*')')? '=>' ruleTypeRef
| + | |
− | ;
| + | |
− | | + | |
− | ruleSimpleTypeRef :
| + | |
− | RULE_ID ('::' RULE_ID)* ('<'ruleTypeParam (',' ruleTypeParam)*'>')?
| + | |
− | ;
| + | |
− | | + | |
− | ruleTypeParam :
| + | |
− | ruleTypeRefParam | ruleWildcardParam ;
| + | |
− | | + | |
− | ruleTypeRefParam :
| + | |
− | ruleTypeRef
| + | |
− | ;
| + | |
− | | + | |
− | ruleWildcardParam :
| + | |
− | '?'
| + | |
− | (('extends' ruleTypeRef (',' ruleTypeRef )*) |
| + | |
− | ('super' ruleTypeRef))?
| + | |
− | ;
| + | |
− | | + | |
− | ruleTemplateExpression :
| + | |
− | (ruleStaticText
| + | |
− | ((ruleExpressionStatement |
| + | |
− | ruleForeachStatement)?))+
| + | |
− | ;
| + | |
− |
| + | |
− | ruleStaticText :
| + | |
− | RULE_STRING
| + | |
− | ;
| + | |
− |
| + | |
− | ruleExpressionStatement :
| + | |
− | ruleExpression
| + | |
− | ;
| + | |
− |
| + | |
− | ruleForeachStatement :
| + | |
− | 'foreach' '('? ruleExpression 'as' RULE_ID ')'? ('separator' ruleExpression)?
| + | |
− | ruleTemplateExpression
| + | |
− | 'endforeach'
| + | |
− | ;
| + | |
− | | + | |
− | RULE_REAL :
| + | |
− | ('0'..'9')*'.'('0'..'9')+
| + | |
− | ;
| + | |
− | | + | |
− | RULE_ID :
| + | |
− | ('^')?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
| + | |
− | ;
| + | |
− | | + | |
− | RULE_STRING :
| + | |
− | '"' ( '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\') | ~('\\'|'"') )* '"' |
| + | |
− | '\'' ( '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\') | ~('\\'|'\'') )* '\''|
| + | |
− | '»' ( '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\') | ~('\\'|'«') )* '«'
| + | |
− | ;
| + | |
− |
| + | |
− | RULE_INT :
| + | |
− | ('0'..'9')+
| + | |
− | ;
| + | |
− | | + | |
− | RULE_WS :
| + | |
− | (WS)+ {$channel=HIDDEN;}
| + | |
− | ;
| + | |
− | fragment WS
| + | |
− | : ' '|'\t'|'\r'|'\n';
| + | |
− | | + | |
− | RULE_ML_COMMENT :
| + | |
− | '/*' WS ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
| + | |
− | ;
| + | |
− | | + | |
− | RULE_DOCUMENTATION :
| + | |
− | '/**' ( options {greedy=false;} : . )* '*/'
| + | |
− | ;
| + | |
− | | + | |
− | | + | |
− | | + | |
− | RULE_SL_COMMENT :
| + | |
− | '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
| + | |
− | ;
| + | |
− | RULE_ANYTHINGELSE :
| + | |
− | .
| + | |
− | ;
| + | |
− | </pre>
| + | |