Coder Social home page Coder Social logo

ale-lang's Introduction

ALE logo

Breathe life into your Ecore metamodels!

Website Jenkins p2 update site

Demo of ALE

ALE (Action Language for EMF) is an interpreted language to define behaviors on the top of Ecore models and make them executable. Concretely, ALE allows to “re-open” EClasses with the aim of implementing existing EOperations and weaving new attributes or new operations.

Main features

  • Executable metamodeling: re-open existing EClasses to insert new methods along with their implementations
  • Metamodel extension: the very same mechanism can be used to extend existing Ecore metamodels and insert new features (e.g. attributes) in a non-intrusive way
  • Quick iterations: no need to deploy Eclipse plugins, just execute a model directly in your modeling environment
  • Extensibility: if ALE doesn’t fit your needs, register Java classes as services and invoke them inside your implementations of EOperations.

Usage

ALE allows to easily define the behavior of an Ecore model and to test it on the spot, right in your modeling environment. It is not production-ready yet but allows for quick iterations and fast feedbacks. As such it is ideal for prototyping a solution.

See the tutorials for concrete examples.

Code example

ALE is an interpreted, strongly typed an object-oriented language. It is based on AQL which means that any valid AQL expression is a valid ALE expression.

The following snippet is part of the Hello World example project and prints Hello World! when executed:

behavior helloworld;

open class HelloWorld {

    @main
    override void greeting () {
        'Hello World!'.log();
    }

}

Please see the online documentation for an in-depth presentation of the language.

Installation

Show detailed instructions
  1. Open Eclipse IDE
  2. Go to Help > Install New Software...
  3. Copy the update site’s URL in the Work with textbox:
  4. Hit Enter and wait for the list to load
  5. Check Action Language for EMF
  6. Click Next then Finish

ALE can be installed from its update site: http://www.kermeta.org/ale-lang/updates/latest/

ℹ️ nightly resulting from the last successful build in the CI

Documentation

ALE's website provides:

Contributing

Requirements
Import the projects in the IDE
  1. File > Import... > Team > Team Project Set
  2. Fill URL with "https://raw.githubusercontent.com/gemoc/ale-lang/master/eclipse-projectSet.psf"
  3. Click on Finish

Tip: use Working Sets for a better workspace organization:

  • Open Project Explorer's menu > Top Level Elements > Working Sets
  • Open Project Explorer's menu > Select Working Sets > Check "examples", "features", "plugins", "tests" and "releng"
Setup your dev environment
  1. Open the org.eclipse.emf.ecoretools.ale.target-platform/org.eclipse.emf.ecoretools.ale.target-platform.target file
  2. Click on Set as Target Platform
  3. Wait for dependencies to be loaded (may take a while)
Technical documentation

ALE's architecture is presented in plugins/README.md; further documentation will be provided as soon as possible.

See the contributing guide for further details.

Publications

ALE is the result of research conducted at Inria's DiverSE team. It is used to define Domain-Specific Languages (DSLs): the Ecore model and the ALE source files being respectively the abstract syntax and the semantics of those DSLs.

See below for a list of related publications:

  • Manuel Leduc, Thomas Degueule, Benoit Combemale, Tijs van der Storm, Olivier Barais
    Revisiting Visitors for Modular Extension of Executable DSMLs
    In 2017 ACM/IEEE 20th International Conference on Model Driven Engineering Languages and Systems (MODELS)
    [Publication at IEEE Xplore Digital Library]

ale-lang's People

Contributors

combemale avatar dvojtise avatar ebousse avatar echebbi avatar fcoulon avatar pjeanjean avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ale-lang's Issues

Opening a class that does not exist should raise a warning in the editor

When using the "open" keyword I expect that it will open an existing class defined elsewhere (in ecore most probably)

so I expect to have an error/warning if there is no other class to open.

This would be very useful in metamodel evolution scenarios.

Note: this remind me that we had the same in Kermeta 1&2, with the keyword "aspect" http://www.kermeta.org/docs/kermeta2/html/Kermeta-Manual.docb/Kermeta-Manual.docb.single.html#section_weaving.link

  • with an ecore defining a class Foo,
open class foo {}

must be OK
while

class foo {}

should detect a conflict

  • without an ecore defining a class Foo
    we may define on ALE side one or more files declaring a class Foo
class Foo {}

is OK

open class Foo {}

should raise a warning

open class Foo {}
open class Foo {}  // in a another ale file for example

is OK

class Foo {}
open class Foo {}  // in a another ale file for example

is OK

class Foo {}
class Foo {}  // in a another ale file for example

should raise a warning

⚠️ As the information of which ale files and which ecore files are used is contained in the .dsl file it might be dependent on dsl file and may increase priority of other but such as #4 and about how to report dsl error about imported ale files

Cannot instantiate Collections defined as attributes

The following code generates an exception:
java.lang.ClassCastException: The value of type 'class java.util.ArrayList' must be of type 'interface org.eclipse.emf.common.util.EList'

behavior testale_behavior;

class ClassB {
	Sequence(testale::ClassA) seq;
}

open class ClassA {
	@main
	def void run() {
		testale_behavior::ClassB b := testale_behavior::ClassB.create();
		b.seq := Sequence{};
	}
}

As such, it is not currently possible to create attributes with a cardinality > 1 with ALE.

The exact same code works fine if b is a local variable instead.

Typecheck error when using aspect on subclass

When defining attribute aspect on a subclass

then, in several situations the typechecker fails to find the attribute and produce errors.

in this figure, B inherits from A in the ecore.

image

Also note that, if there is no open class A then the faulty lines correctly typechecks...
image

project for reproducing this bug:
ale-lang-issue.zip

Missing return in not void operation

The validator doesn't complain about this code:

open class ClassA {
	
	@main
	def boolean main() {

	}
}

It should detect the missing return value.

Typecheck error when initializing Sequence

according to #29 initializing a sequence feature is done using the := Sequence{};

however, this raises a typechecker exception in the editor.
( Expected Collection(helloworld::HelloWorld) but was [Sequence(Nothing(Empty Sequence defined in extension))] when using a variable
or Expected [ecore::EEList] but was [Sequence(Nothing(Empty Sequence defined in extension))] when using a feature

image

image

step to reproduce: get the ale helloworld example, add the lines as in the figures.

Allow to provide custom version of the DiagnosticLogger and log service to redirect standard output

In the current version, the DiagnosticLogger and the default log service print on the System.out

This would be fine if the ALE engine/interpreter was run in a separate JVM, in this case, we can redirect the system.out to a dedicated stream (console or file for example)

However, the interpreter is actually run directly in Eclipse JVM, this implies that the System.out get all messages from eclipse and not only those from the ALE execution.

Writing an ALE interpreter running in a separate JVM is not possible in the short term (this implies to fully implement a Debug Server Protocol 😉 ), so as a workaround (similar to current GEMOC engine solution) we can offer a way to provide a custom version of the 2 classes LogService and DiagnosticLogger. This custom version would be able to send the stream to a dedicated location (a specific console, a file, etc) This would be up to the launch configuration to provide a custom version or not. If not provided we would fall back to the version sending everything to the system.out.

CF.

System.out.println("\n[AQL eval fail] At line "+ line +" in " + file + " :\n" + errorMsg);
and https://github.com/gemoc/ale-lang/blob/master/plugins/org.eclipse.emf.ecoretools.ale.core/src/org/eclipse/emf/ecoretools/ale/core/interpreter/services/LogService.java
and

Need for an Exception raise/catch concept in ALE

Currently, ALE does not provide Exception mechanism (ie. allowing to raise /catch exceptions)

It is actually useful to have that in many situations allowing the language designer to stop the execution on an incorrect situation (for example. the user's model is incorrect and the designed language needs to report some message about it).

the catch could also be used in some recovery process higher in the call stack.

(Note: I'm also open to any other concept that may replace this (like go's panic/recover))

Expected Foo but was [EClassifier=Foo] on a collection navigation at(i) or first()

what can cause this kind of messages ?

fcl::Event evt0 := fcl::ExternalEvent.create();
RuntimeContext context := fclModel.runtimeContext;
fcl::Event evt1 := context.receivedEvents->at(1); 

the typechecker reports Expected Event but was [EClassifier=Event] on the line with the ->at(1)

same error with ->first()
note: the receivedEvent is defined as reference in a vm.ecore file that is referenced in the dsl file.

AQL classes have the highest priority for name resolution

The following code fails:

behavior testale_behavior;

class Expression {
	int a := 0;
}

open class ClassA {
	@main
	def void run() {
		Expression.create().a.log();
	}
}

The issue is that a class named Expression is also defined in Acceleo, and it is the one actually referenced here.
Opening a class Expression defined in the associated meta-model without its qualified name would also fail.

Ideally, the user's classes should have a higher priority when not using full qualified names.

support multiplicity on operation

In Ecore, the return type of an EOperation supports multiplicity

In order to be complete, (ie. allow correct alignment of ALE with Ecore) ALE should also be able to define such method.

Note (using a syntax as proposed in #43 ):

override MyType[0..*] myOperation() {}is not the same as override Sequence(MyType) myOperation(){} because :

  • in the first one we use MyType with multiplicity, and in the second we use ecore::EEList with a generic parameter MyType.
  • in the first one, I expect to be able to directly do result += operations, while on the second I need to first do result = Sequence{} then result += .

Execution not stopping and not reporting error on += operator

The issue #38 is not completely fixed

There are still some situations where the engine does not stop and does not report any error.

on the following example:

the typechecker does not report any error on the code and on execution, the program continues without any error message.

image

ALE uses existing packages to declare new classes

When generating an ALE behavior file from an ecore file in the GEMOC Studio, the default package used by ALE is the root package from the ecore meta-model.

As a consequence, there is an issue with the following code:

behavior testale;

class ClassB {}

open class ClassA {
        @main
	def void run() {
		testale::ClassB b := testale::ClassB.create();
		b.log();
	}
}

The root package of my meta-model is also named testale, and as such the ALE engine is unable to find the class ClassB to instantiate it (b is still null when logging it).

Changing the behavior value is enough to fix this issue, but the user shouldn't need to do it when using the auto-generation tools of the GEMOC Studio.

Add a way to configure serialization

ALE allows to add new attributes & reference into EClass

We can manage this in 3 ways:

  • Write them into the .ale file
  • Write them into the .ecore file with an annotation (ex: @ dynamic)
  • Write them into a new .ecore representing the dynamics parts of EClasses

Actually we add this new attributes through the availables tools from the Behavior layer in the Ecore editor
and we update the .ale file.

We need a Preference page to support the two others options

Abstract classes and methods

I propose to integrate the abstract keyword for classes and methods.

For instance:

open abstract class Node  {
	abstract def boolean canFire()
	abstract def boolean hasToken()
	abstract def void fire()
	abstract def void removeToken()
	abstract def void addToken()
}

open class Transition  {
	override boolean canFire() {
		!incomming.filter[canFire].empty
	}
	
	override boolean hasToken() {
		false
	}
	
	override void fire() {
		//println('fire')
		incomming.forEach[removeToken]
		outgoing.forEach[addToken]		
	}
	
	override void removeToken() {
		throw new RuntimeException("Should not be called")
	}
	
	override void addToken() {
		throw new RuntimeException("Should not be called")
	}
}

Block statement skipped during parsing

In the code snippet below, the xs += a::B.create(); statement is skipped during parsing.

In other words, when observing the ExtendedClass instance using eclipse's debug tools, the object has a single test method (as expected), which contains 7 statements (instead of the expected 8), the one missing being the xs += a::B.create();.

behavior test0;

open class A {
	
	@main
	def void test() {
		self.m := 1;
		self.ms += a::B.create();
		Sequence(a::B) xs := self.ms;
		'A'.log();
		xs.log();
		xs += a::B.create();
		'B'.log();
		xs.log();
	}
}

"Double" does not seem to be accepted as parameter type

in the following example

behavior bugs;

open class Foo {
	
	@main 
	def void main() {
		self.myMethod1(1.1);
		self.myMethod2(2.2);
		self.myMethod3(3.3);
	}
	
	def void myMethod1(Double d) {
		('myMethod1').log();
		self.myDouble1 := d;
	}
	def void myMethod2(Real d) {
		('myMethod2').log();
		self.myDouble2 := d;
	}
	def void myMethod3(ecore::EDouble d) {
		('myMethod3').log();
		self.myDouble3 := d;
	}
}

myMethod1 is never called (without error message)

image

strangely, the assignment self.myDouble1 := d; seems to typecheck correctly in the editor (ie. using Doooble as type, correctly reports an error) but when the model is run, the method is ignored.
ale-lang-issue-36.zip

ale-lang-issue-36-models.zip

Add Switch instruction

I have some enumeration in my language and I need to change the behaviour depending on the enum value.

How can I avoid the nested if?

if(self.operator = fcl::BinaryOperator::EQUAL){	
} else { if(self.operator = fcl::BinaryOperator::PLUS){
} else { if(self.operator = fcl::BinaryOperator::MINUS){
} else { if(self.operator = fcl::BinaryOperator::MULT){
} else {
}}}}
....

Can we have some switch instruction?

Limitations of AQL's subpackage management

In AQL, there is a depth limit when referencing subpackages.
Namely, only one subpackage can be specified when accessing a specific class:

  • subpackage::class works
  • package::subpackage::class doesn't work

As such, the following code has a very unexpected behavior when the syntax subpackage::class can refer to multiple classes:

behavior testale_behavior;

open class ClassA {
	@main
	def void run() {
		subpackage::ClassB b := subpackage::ClassB.create();
		b.log();
		('Size: ' + b->size()).log();
	}
}
testale.subpackage.impl.ClassBImpl@6ab6d9f (val: 0)
testale.test.subpackage.impl.ClassBImpl@37672f40 (val: 0)
Size: 2

The create() method returns a list containing an object for each potential class candidate, which might be a feature but probably not the one expected by the user.

Official update site with dates

in order to be able to deploy ALE in the studio (see eclipse/gemoc-studio#119) we need an official update site that can track versions instead of directly relying on jenkins which shows only the latest build

(ie. something similar to k3 updates sites)

Using `override` for any kind of operation

Right now, the override keyword can only be used when defining the body of an EOperation declared in the static metamodel.
I propose to also use it when redefining an inherited method, which would lead to a better checking (multiple definitions of the same method shouldn't be authorized) and would make more sense for the language designer.

Error in the dsl file does not produce helpful message when ALE validator fails

in the dsl file when there is some issue that makes the validator fails
for example trying to use multiple ecore files, or simply if the referenced ecore file doesn't exist (or if the target project is closed)
then the only error we get is Error executing EValidator in the Problem view without any further information.

Missing method in Sequence/OrderedSet to remove an element at a given position

Is there a method to remove an element at a given position of a Sequence ?

something opposite to the OrderedSet{'a', 'b', 'c'}->insertAt(2, 'f') method

the -= operator supposes that the elements are unique in the collection. This is not relevant for Sequence which can contain duplicate elements.

Creating a new sequence without the element and replacing the original sequence with it should work, but sounds very innefficient.

Stopping the execution on unexpected error

Currently, the interpreter continues to execute even in case of very strange errors (such as missing method), 0 divisions, ...).
At most, it reports the error (see #28)

while this can make sense for constraint checking to continue to check all constraints even if one fails, I don't think it is relevant while running a model.
I would prefer my execution to stop because in that case, I'm not confident anymore about the remaining of the execution.

If one thinks the execution should continue after such error, why not, but in that case, an option of the interpreter should enable/disable this

Integration of the Object type in the type system

So far, writing the following statement returns the error below:

Expected EObject but was [Sequence(java.lang.Integer)]

EObject o := Sequence{1};

I propose to integrate the Object type as the parent element of all the types of Ale's type system.

Cannot read "result" variable

In ALe documentation, the result is supposed to be a variable

however, I cannot read it or use it (for example print some debug or update its value: result := result + someotherstuf;

image

Type checking error

Error marker Expected EBoolean but was [EClassifier=EEList] in :

open class HelloWorld {
    def void greeting (Sequence(Boolean) a) {
        Boolean i := a->first();
    }
}

a->first() should be typed boolean

Support .genmodel in .dsl

syntax=platform:/resource/helloworld/model/HelloWorld.genmodel
behavior=platform:/resource/helloworld/model/HelloWorld.ale

Resolution of methods with list parameters not working

When using lists (or sequences in ALE) as parameters for a method, the engine is unable to resolve the right method to use.

For example, the following minimal behavior file:

behavior testale_behavior;

open class ClassA {
	def int mysum(Sequence(Integer) list) {
		result := list->sum();
	}
	
	@main
	def void run() {
		Sequence(Integer) list := Sequence{1, 2, 3};
		self.mysum(list).log();
	}
}

Returns the following output:

Couldn't find the 'mysum(EClassifier=ClassA,java.util.ArrayList)' service

Missing documentation about initializing Sequence and OrderedSet

the reference documentation does not explain how to initialize a Sequence or OrderedSet in a variable or in an open class attribute.

For example:
the syntax that is explained in the documentation about object creation would suggest:

self.receivedEvents := Sequence(fcl::Event).create();

Actually, it looks accepted by the editor, and probably due to #28 the instruction is just ignored, so leaving the variable/attribute to null.

subsequent += have no effect too on this null object. (no effect and no crash ...)

the correct way to initialize it (empty) is to use the following syntax:

self.receivedEvents := Sequence{};

Is there collection methods that modify its content "in-place" ?

related to #25 , is there any method on collections that works in-place? Ie that modifies the existing collection instead of replacing it with a new one.

I haven't checked how the += opertor in implemented but it sounds to be a syntactic sugar for
myCollection := mycllection->insertAt(myCollection.size(), newElem) which creates a brand new collection and replace the existing one with it.

I understand that the aql syntax with -> operator is side effect free (this comes from OCL)
but it would be nice if there were a serie of methods (using the classic . operator) that would work in-place (ie. without the collection creation/duplication overhead)

Variable type checking not enforced

The following code does not generate any error:

behavior testale_behavior;

open class ClassA {
	@main
	def void run() {
		int i := false;
		i.log();
	}
}

An error should be displayed, at least in the editor.

Extending classes defined in the semantic

Right now, it is not possible to define a hierarchy for dynamic classes.
In order to specify an inheritance relation between runtime data, the only way is to put them in the static metamodel, which doesn't make sense as we want to keep the distinction between the two.

I believe that it would make sense to be able to simply use extends between two classes defined in the behavior file, but this leads to several questions:

  • Should dynamic classes only inherit from other dynamic classes, and aspects only from other aspects? or
  • Could a dynamic class also extend an opened class?
  • What about an opened class getting its aspects from a dynamic class?

`+=` operator not working with locally defined Collections

Considering the following example:

behavior testale_behavior;

open class ClassA {
	@main
	def void run() {
		Sequence(testale::ClassA) l := Sequence{};
		l += testale::ClassA.create();
		l := l->including(testale::ClassA.create());
		l.log();
	}
}

l will only contain a single element when logged.

The += operator, which should work with any kind of Collection, fails silently here.

Typechecker does not accept java services using objects of the current metamodel

Services that work on top of object instances of the current metamodel doesn't typecheck correctly

Only primitive type or classs from ecore metamodel are correctly typechecked.

Note that the typecheck issues are for the first paamter (selfà, the other parameters and also the return type

the only workaround for input paramters is currently is to pass onlyEBojects and then cast to the concrete type on the java side. this workaround does not work for return type.

image

image

Typechecker not called on GEMOC official example

when installing the GEMOC ALE official example I was expecting it to be configured as a nominal configuration and to be able to raise typechecking errors

but introducing errors in the ALE code does not raise any error. (both trying to change Ecore attribute or local attribte for example)

might be related to #4

note: using current ale distributed with GEMOC

`contains` modifier has no effect on sequences

The contains modifier is used to set the defined attribute as a containment.
This works well for a cardinality of one-to-one, but not for a cardinality of one-to-many (sequences).

As such, the following behavior file:

behavior testale_behavior;

class ClassB {

}

open class ClassA {
	contains Sequence(testale_behavior::ClassB) c_list;
	
	@main
	def void run() {
		self.c_list := Sequence{testale_behavior::ClassB.create()};
		self.c_list->first().eContainer().log();
	}
}

Produces:

null

Support for class diagram showing ALE runtime data for ale file not in the ecore project

Currently, a "behavior" layer allows showing the ALE addition on top of an ecoretool class diagram (ie. aird in the ecore project.)

When splitting the ale project and ecore projects, this layer does work any more. It seems to be able to handle only runtime classes (ie. ale open classes) located next to the ecore file.

It would be nice to have a proper runtime class view or allow to create the runtime class in a specific location (not in the same project, I usually wish to separate the static and the dynamic parts because several dynamic parts for different purposes might be relevant to develop using ALE)

Improve syntax for multiplicity

Currently in ALE, it is possible to define attribute multiplicity using the following syntax

1 ..* MyType myAttribute
this is not very convenient because:

  • the whitespace in 1 ..* seems mandatory for the parser
  • the multiplicity put before the type is not clearly identifiable

I would suggest using square brackets like kermeta 1 and 2 or xcore

ie. MyType[0..*] myAttribute

with possible shorthand similar to xcore:
MyType myAttribute ↔️ MyType[0..1] myAttribute
MyType[] myAttribute ↔️ MyType[0..*] myAttribute

service eContainer(EClass) produces an infinite loop

in AQL, there is the service

EObject.eContainer(EClass) : EObject
Returns the first container of the specified EObject that matches the given type

which is supposed to crawl the containment hierachy up to the specified type.

when run on a nested object the execution never ends (the log before the instruction is written but not the one after it) and the CPU load is high (I highly suspect some kind of infinite loop)

Here is a project to reproduce the bug (variant of GEMOC FSM official example)

ale-lang-issue-econtainer-infinite-loop.zip

ale-lang--econtainer-inifinite-loop-models.zip

image

image

will print the log for lines 54 and 55, but never reach 57 and 58

Return null in boolean operation

This code is fine for the validator:

open class ClassA {
	
	@main
	def boolean main() {
		result := null;
	}
}

It should raise an error for the null value returned, because we are expecting boolean

Typecheck error when Sequence used in for loops

The following code return an error on lst:

Expected Collection but was [EClassifier=EEList]

open class MyClass {
	def void loopOver(Sequence(String) lst) {
		for(l in lst) {
			
		}
	}
}

I guess the current implementation of the for loop is only expecting EMF EList data structures and should be extended to the standard aql collections.

use fully qualified name when reporting type error

related to #30 and #31

when several Eclass exist with the same name the error message is not clear enough

In my example
image

the error can easily be solved using a fully qualified name on the variable, but the message does not inform about the package used by ALE in the expression.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.