Just like the language implementation, this document is still 'in development' and subject of progression and change.
The intention of this document is to give the reader at least some entry point to extract ideas and core features of the LITTLE language. Surely, this specification can't convincingly mime a tutorial
LITTLE's design goal is to be the incarnation of a programming language relying on exactly one concept and paradigm, providing a minimal, but sufficient set of syntactical structures, thus avoiding ambiguities omnipresent in other languages, but nevertheless offering anything needed to write sophisticated applications.
Here are the supported features at a glance:
The LITTLE language borrows it's syntax from LISP and Scheme. It uses a strict, parenthesed prefix notation. Unlike LISP and Scheme, there is no equivalency between data and code - LITTLE code can't just be quoted to delay evaluation. Runtime evaluation of string and/or lists would be matter of a concrete runtime system's implementation. LITTLE also makes difference between expressions and statements; on the other hand, statements (syntactical structures, which can't be part of expressions) are used for declarative purposes only. Everything within an executable block actually is an expression, and thus has a return value.
The language itself includes syntax to manipulate objects: create them (classes), access properties (attribute load/store), send messages, process exception conditions. Only a few more stuff is part of the language specification itself: a bit of control flow (one loop, conditional execution), and grouping of expressions plus declaration of local variables.
Everything else has to be implemented by an incarnation of a runtime class library, which is meant to be exchangible - the language should be as customizable as possible, as it's main purpose is to serve as an extension language rather than as a standalone application language.
The following conventions will be used in the descriptions below:
Symbols represent compiler terminals, class, method, constant and variable identifiers, modifiers, and might themselves be values of variables.
Definition:
[A-Za-z_][A-Za-z0-9_]*
{.[A-Za-z_][A-Za-z0-9_]*}*
Class names are just symbols. Full classnames must consist of a prefix resembling the package the class belongs to, a dot, and the package-unique short class name itself (which, therefore, must not contain dots). Class names refer to global memory containing class objects - the only kind of globals supported by the LITTLE system.
In LITTLE, classes, attributes and methods are declared explicitly. Furthermore, package membership ,classname aliases and macros are formed via declarative statements.
Class Declaration Statement:
(class
{meta:<metaclass-symbol>}
<symbol> ({<ancestor-symbol>}*)
{:<modifier-symbol>}*
{<feature>}*
)
Via meta-keyword, the programmer can overridethe implicit object-class relation set-up, which usually creates a class object Meta<symbol> for a class named >symbol<. This implicitly created class, or the class given by meta, will be <symbol>'s class.
The feature is intended to be used by runtime system implementors rather than by application programmers.
Valid modifiers are:
abstract
This class (and it's metaclass, if implicitly defined) is abstract,
ie. may have abstract methods (non-abstract classes may not be owner of such a
method)
final
Inheritance of final classes is forbidden
hidden
Class is dynamically accessible by package members only
native
Class has native properties - descendants must not add
attributes and will implicitly be native as well. Also,
descendants with multiple parent classes must be native
as well or must not have attributes of their own.
sticky
This modifier is useful for implementors of native libraries and
complete runtime systems only - it enforces the set of attributes of
such a class' instances to be always be located at the same memory
offset within the object; only one base class within an inheritance tree
may be sticky - if this rule is violated, the VM class loader will raise
an exception
Feature:
attribute declaration or method declaration statement
(attribute <attributedef> {<attributedef>}*)
Attributedef:
<symbol>{:<modifier-symbol>}*
The following modifiers might be used:
const
The attribute can be initialized once, but must remain unchanged
from this point on
local
The feature is not inherited do descentands
static
Marks the attribute as class attribute (only possible within class definitions
which don't use the explicit meta feature)
(method <methodsym> ([<argsym 1> ... <argsym N>]) [:<modifier 1> ... :<modifier N>] <expressionlist>)
For methods, the following list of modifiers can be specified:
abstract
This method is part of an interface to be defined by descendants; classes
which declare or inherit abstract methods (without overriding them) must
be declared abstract as well
final
The method may not be overridden
hidden
The method may be invoked by members of the class and it's descendants only
local
The feature is not inherited do descentands
static
Like for atttributes, the method is marked as class, not instance feature
(only possible within class definitions which don't use the explicit
meta feature)
Feature names may be overloaded in a simple manner: the same feature name may be used for a set of features, provided, their incarnations can be distinguished by parametization. Here, the criterion is simple: just the number of arguments matters and must be unique within a set of name-sharing features. For example, the attribute 'foo', and methods foo (), foo (n), foo (x y) might all be defined within the same class' scope.
Class features (features marked static) might share name and parametrization with
instance features of the same LITTLE class definition, as they, in fact, belong to different
class implementations. For example, the LITTLE code
will be compiled into a class A, and another one MetaA, both of them
owning a method 'foo ()'.
(class A ()
(method foo () 4711)
(method foo () :static 4711))
Each class must be associated to a package. This is achieved by the package delaration statement - after it's occurrence, any class goes into the corresponding package.
(package <symbol> {({<symbol>}*) {<feature>}*)
Instead of fully qualified classnames including package name, any LITTLE source code can contain alias declarations, which allow to use the simple name, omitting package specification. This is done via the following declarative statement:
(using <item> {<item>}*)
where item may either be a full classname, or a package declaration statement like explained above - just with the difference, that this statement may include more than one package name; all contained package symbols will be used as prefix in order of declaration within the statement, to resolve class name aliases during lookup
Macros mostly are not part of the base language syntax. The compiler just implements a hook, which allows arbitrary classes to be loaded and manipulate it's syntax tree.
They are described here.
Macro expansion of expressions can be suppressed by preceding them with the ampersand character &. For reasons and uses of this syntax, see the above document.
This is planned for LITTLE V.0.8.
The form
(doc <docstring> {<docstring>}*)
will be available to integrate API documentation immediately into LITTLE source.
doc-statements will be legal within the following contexts:
Everything inside a method body is an expression, ie. returns a result and can be used as subexpression of compound expressions.
'<symbol>
"<characters>"{s}
"<character>"c
{[+-]}[0-9]*{i}
{[+-]}[0-9]*{.[0-9]*}{d}
{[+-]}[0-9]*{.[0-9]*}{f}
true
nil
<symbol>
self:<symbol>
self
Compound expressions have the common form
(<key> ... )
Any compound expression, where key matches a macro name, will be expanded to the replacement pattern, if possible. If the expansion fails (due to errative parametrization), a compile time exception will be raised.
The standard form to send messages to objects is:
(<expression>:<symbol> {<argument>}*)
Each argument might be any expression, if present. This would invoke the method named symbol on the object returned by expression.
To send a message, overriding it's polymorphic behaviour, the following form is provided:
(<expression>:(super <classname> <symbol>) {<argument>}*)
classname and symbol are used to resolve the message for the receiver object - the invocation will fail, if classname doesn't refer to a class conjunct with the receiver's class.
(cond (<expression 1> <expressionlist 1>) ... (<expression N> <expressionlist N>))
(if <expression 1> <expression 2> <expression 3>)
This form is currently hard-coded syntax, but will later be implemented via
macro.
(let ((<localvar1> <expression 1>) ... (<localvarN> <expression N>)) <expressionlist>)
(prog <expressionlist>)
(return <expression>)
(set <lhs> <expression>)
(try <expressionlist 1> catch (<localvar>) <expressionlist2>)
(while <expression> <expressionlist>)
The following symbols are terminal symbols known by the compiler:
attribute,catch,class,cond,final,hidden,if,let,local,meta,method,
package,prog,return,set,static,super,try,using,while,_doc_,_file_,_if_,_line_