ISML's syntax is heavily inspired in Java. Blocks are represented with curly brackets { }
and lines of code are separated by semicolons ;
.
The rest of this document assumes that the reader has some basic knowledge of Java or a language with similar syntax.
ISML models are stored in several files with the extension .isml
. Each file begins with a package declaration, followed by the elements declared within that package. The following is an example of an ISML file that contains an entity:
package com.example.entities;
entity Person {
String name;
Data dob;
Integer taxID;
}
The following sections provide more details about the above syntax.
Primitive types are very similar to Java. ISML provides the following data types:
Integer
String
Float
Double
Date
Boolean
Any
Null
Void
TODO: Document primitives
ISML provides the following collection data types:
Array
: An array of elements that can be accessed by their index in the arraySet
: A collection of unique elements, where the position in the collection is not relevant
ISML is based on the [Model-View-Controller (MVC) pattern][MVC]. This pattern provides three components for applications: models that represent the underlying information the system manages, views that display this information to users, and controllers that determine the way users interact with views and models.
ISML specifies models with the keyword entity
, views with the keyword page
, and controllers with the keyword controller
.
In addition, ISML provides services to denote elements in the system that provide an interface with operations that can be invoked from controllers. Services are denoted with the keyword service
.
An entity in ISML denotes a persistent data structure in the system. It is very similar to a class. An entity can have attributes and inherit from other entities, but it does not support methods or visibility labels.
The following is an example of an entity:
entity Person {
String name;
Date dob;
Integer taxID;
}
All entities begin with the keyword entity
An entity can also inherit attributes from another entity:
entity Employee extends Person {
}
Associations between entities are represented with the keyword opposite
. The keyword opposite
indicates the name of the attribute in the opposite class that is part of the association.
entity Company {
String name;
Integer taxID;
Array<Employee> employees opposite company;
}
In this example, Company
has an association with Employee
. One end of the association is the attribute employees
of Company
. The other end of the association is the attribute company
of Employee
, which is necessary to add to the Employee
entity:
entity Employee {
Company company;
}
Pages describe the way to visualize information from entities. Pages receive entities as arguments and specify several widgets to visualize the entities attributes.
The following is an example of a page:
page ViewCompany(Company company) controlledBy CompanyManager {
Panel("Company") {
Label("Name")
Label(company.name);
Label("Tax ID")
Label(company.taxID);
Button("Ok", false) -> listAll();
}
}
Each page is declared through the page
keyword. A page receives one or more entities as arguments (in this example, ViewCompany
receives an instance of Company
as argument). The keyword controlledBy
indicates the controller that will manage the events of the ViewCompany
page (in this case it is the CompanyManager
controller).
The main body of the page is declared within curly brackets { }
and contains all the widgets that will depict the entity attributes. In the example above, the name and tax ID of the company are represented as a Label
widget.
Some widgets can trigger controller actions. Action triggering is denoted with the ->
operator. In this example, when the Button
widget is clicked, it triggers the the action listAll
contained in the CompanyManager
controller.
Widget support varies from one code generator to another. The widgets provided by ISML are the following
Button
Label
Image
Link
Text
OutputText
Password
CheckBox
ListChooser
ComboChooser
RadioChooser
Spinner
PickList
Calendar
GMap
Menu
TODO: Document widgets
Controllers specify the actions a user can perform over a system and the pages that will be shown after each action is executed. The following is an example of a controller:
controller CompanyManager {
has Persistence<Company> persistence;
/**
* Lists all instances of Company.
*/
listAll() {
Array<Company> allCompanies = persistence.findAll();
show CompanyList(allCompanies);
}
/**
* Saves an instance of Company.
* @param company the Company to save.
*/
saveCompany(Company company) {
if(persistence.isPersistent(company)){
persistence.save(company);
} else {
persistence.create(company);
}
-> listAll()
}
}
Controllers are denoted with the keyword controller
and have two main components: declared services and actions.
Declared services are all of the services to which the controller has access. Declared services begin with the keyword has
. In the example above, CompanyManager
has access to the Persistence
service.
Actions are represented as an identifier followed by parenthesis and curly brackets. In this example there two actions: listAll
and saveCompany
.
An action is similar to a class method in an object oriented language. An action receives parameters and has a body with a sequence of commands. However, an Action does not has a return type, because it never returns in the way as a regular method or function does. Instead, the action execution may end in two ways:
- By showing a
page
- By calling another action
The first case is depicted the listAll
action. The line
show CompanyList(allCompanies);
Ends the execution of listAll
and displays the page CompanyList
to the user.
The second case is depicted in the saveCompany
action. The line
-> listAll()
Means that the execution flow continues at the listAll
action.
Services are interfaces with sets of methods that can be called from controllers. Structurally they are very similar to Java classes or interfaces. Conceptually they represent existing or new services provided by the system.
The following is an example of a service:
service Persistence <T> {
native Void create(T obj);
native Array<T> findAll();
native Void save(T obj);
}
A service is declared with the keyword service
, followed by the service name and (optionally), a list of generic parameters. The service body contains method declarations utilizing a syntax very similar to Java.
In the example above, each method is preceded by the keywork native
. This means that the method is no implemented in ISML, but in the target language of the code generation. Although the ISML interpreter supports the explicit definition of method bodies (instead of declaring them as native
), this feature is not yet supported in the code generators.
TODO: Complete this section [MVC]: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller