-
Notifications
You must be signed in to change notification settings - Fork 5
Spaghetti Syntax
Spaghetti modules define their interfaces using the Spaghetti Interface Definition Language (IDL). It's a simple language that borrows from C, Java, Haxe and TypeScript alike. An important difference compared to all of these is that Spaghetti IDL does not use semi-colons to separate clauses.
The following primitive types are available in the Spaghetti IDL, with mappings to JavaScript types:
-
bool
-- boolean type, maps toboolean
-
int
-- integer type, maps tonumber
-
float
-- floating point type, maps tonumber
-
string
-- string type, maps toString
-
any
-- maps to any type
The void
type is available to signal no return value in method definitions and type chains.
You can declare an array of any Spaghetti type where types are accepted.
Syntax:
{type}[]
For multi-dimensional arrays repeat the []
array qualifier the required number of times.
Example:
multiply(data: float[][], factor: float) float[][];
You can use array qualifiers in type chains and type arguments as well.
Function types map an n-tuple of types to a type:
Syntax:
([{type}]*) -> {type}
Example:
(string) -> string
(int, int) -> int
() -> void
Interfaces, structs and methods can take type parameters. These work similar to generics in Haxe and TypeScript. There is no support for constraints on the values of type parameters (see #107).
Syntax:
<{type_parameter}[, {type_parameter}]*>
Some language elements in Spaghetti can take annotations. The usable annotations are listed at the definition of each element.
Syntax:
@{name}
Some language elements can be documented in a Javadoc-like syntax.
Syntax:
/**
* {documentation}
*/
{definition}
C-style line and block comments are accepted. Block comments cannot be nested.
Example:
// Line comment
/*
block comment
*/
(Documented, annotated)
Module definitions are blocks starting module name and alias. The module name is a hierarchy of lowercase names separated by periods. The alias is a single title-cased name. If no alias is specified, the default value is calculated by taking the last part of the module name capitalized, and adding a Module
suffix.
A module block contains a list of import and extern declarations, and the definition of the module's types and methods.
Syntax:
module {module_name} [as {module_alias}] {
{imports, externs, complex types and methods}
}
Example:
module com.example.test as TestComponent {
import org.spaghetti.Pasta;
import org.spaghetti.Sugo;
import org.spaghetti.Cheese;
import org.spaghetti.Dish;
interface Chef {
cook(pasta: Pasta, sugo: Sugo, @optional cheese: Cheese): Dish;
}
createChef(): Chef;
Similar to languages like Java, import
s can be used to give a short alias to types declared in other modules. The alias by default is the same as the name of the imported type.
Syntax:
import {fully_qualified_name} [as {alias}];
Example:
import org.spaghetti.Pesto;
import org.spaghetti.Anchovies as Fish;
Refers to an interface provided by the JavaScript runtime. External interfaces can be generic. Even though the names of the type parameters of a generic external interface are never referred to, a name must still be specified for them.
Syntax:
extern interface {fully_qualified_name}[<{type_parameters}>];
Example:
extern interface CanvasRenderingContext2D;
Types are named entities. Their fully qualified names are the concatenation of their module's name and their own name.
Interfaces can contain methods only. They can extend other interfaces. It is possible to extend multiple interfaces. When extending a generic interface, a list of type arguments must be specified that matches the number of type parameters of the extended interface.
Syntax:
interface <name>[<{type_parameters}>] [extends {interface}[<{type_arguments}>] {
{interface_method}*
}
Example:
interface Carbonara extends Pasta, Delicious {
consume(): StomachState;
}
Structs can hold properties and methods alike. They can extend maximum one other struct. When extending a generic struct, a list of type arguments must be specified that matches the number of type parameters of the extended struct. Structs are immutable by default and can be made mutable using the @mutable
annotation.
Syntax:
struct {name}[<{type_parameters}>] [extends {super_struct}[<{type_parameters}>]] {
[{property}|{method}]*
}
Example:
struct TodoItem<T> extends NamedItem {
item: T;
finished: bool;
}
An enum is a finite set of named constant values. This means that each value must be unique per enum.
Enum entry values can be either determined implicitly from the order in which the names appear, or explicitly by assignment of integer values. A mix of both styles within an enum is not allowed.
Syntax:
Either
enum {enum_name} {
[{entry_name}]*
}
or
enum {enum_name} {
[{entry_name} = {integer}]*
}
Example:
enum Utensils {
KNIFE,
FORK,
SPOON
}
enum Fruit {
APPLE = 1,
ORANGE = 3,
BANANA = 5
}
Constant types contain a list of named primitive values with optional type annotations.
Syntax:
const {name} {
{constant_name} [: {type}] = {value};
}
Example:
const ExampleConstants {
id: int = 1;
name = "constant";
}
Interfaces, structs and modules can define methods. Methods can take local type parameters that can be referred to as type arguments in the declaration of their return types and the types of their parameters. The @optional
annotation can be used to mark optional parameters.
Method overloading is not possible (see #113).
Syntax:
{method_name}[{type_parameters}]({parameter_name}: {parameter_type}[, {parameter_name}: {parameter_type}]*): {return_type};
Example:
sayHello(user: string): void;
concatAsString<T>(elem1: T, elem2: T): string;
For more information see the ANLTR grammar of the Spaghetti IDL.