Structure of a GDLisp Source File
A GDLisp source file is, by convention, written with a .lisp file
extension. GDLisp source files will be compiled into GDScript source
files (.gd) in a one-to-one fashion, with the resulting compiled
GDScript file taking the same filename as the input file, merely with
the extension changed. So, for example, Player.lisp would be
compiled to Player.gd. GDLisp source files are always encoded in
UTF-8.
A GDLisp source file defines a module. Syntactically, a GDLisp source file consists of zero or more S-expressions. With the exception of quoted and quasiquoted terms, all lists appearing in GDLisp source code must be proper lists. These top-level S-expressions will be interpreted as declarations and will be compiled together to form a top-level GDScript class body.
There are six types of declarations: defn, defmacro,
defconst, defclass, defenum, and define-symbol-macro.
Additionally, there is one pseudo-declaration, called progn, which
is treated specially in this context. Finally, use directives
appear in declaration context, despite not being declarations
themselves.
Macros are discussed in Macros. Classes are discussed in
Classes and Objected-Oriented Programming. use directives are discussed in Import Directives.
The other declaration forms are discussed below.
Namespaces
All GDLisp declarations fall into one of three namespaces. The namespaces are intentionally kept distinct, and it is possible to define the same name in all three namespaces in a given scope.
The value namespace consists of variables and constants and their values. The value of a variable or constant can be a first-class function object (reified as a value), as well as any other type of object in GDLisp. Names in the value namespace are declared at the top-level by
defconst,defclass,defenum, anddefine-symbol-macro. Names can be locally introduced to the value namespace through several special forms, most prominentlylet.The function namespace consists of functions that have been explicitly bound to a function name. Functions are called by simply indicating the name of the function as the head of an S-expression in the source code. A name in the function namespace is always bound to a value of function type or to a macro. At the top-level, names in the function namespace are declared by
defnanddefmacro. Names can be locally introduced to the function namespace through several special forms, such asfletandlabels.The final namespace is the signal namespace. This namespace only exists inside the scope of a class declaration, never at the top-level of a module. The signal namespace is the namespace which contains signals within a class defined by
defsignal. Classes are discussed later.
Functions
(defn function-name (args ...)
body ...)
(defn function-name (args ...) public-or-private
body ...)
A function is the fundamental unit of code execution in GDLisp. A function defined at the top-level of a module compiles to a static function in the resulting GDScript file.
A function declaration introduces a name into the function namespace
for the current module. When the function is called, the arguments
will be bound to the formal arguments of the function in a new lexical
scope, and the body of the function will be executed, in order, in
that lexical scope. The final expression of the function body is
always returned from the function, but it is also possible to return
early using the return special form. If a function body is empty,
then the function is a no-op which silently returns the null object
().
The list of formal arguments is an ordinary lambda list. When a function is called, the number of arguments passed to the function is validated at compile-time, and an error is issued if the count is incompatible with the function’s lambda list.
Compilation of Functions
A GDLisp function translates to a GDScript function with its name normalized. No guarantees are currently made about the GDScript signature of the resulting function if optional or variable arguments are used. However, if the function only accepts required arguments, then the resulting function is guaranteed to compile to a function that accepts exactly the same number of required arguments. Therefore, if you intend to call a GDLisp function from GDScript, then the GDLisp function should take only required arguments, in order to maximize compatibility.
Constants
(defconst constant-name constant-value)
(defconst constant-name constant-value public-or-private)
A constant is a top-level immutable name which binds to a value. The value of a constant must be an expression whose value is known at compile time.
The notion of a constant expression is recursively defined as
The name of another constant.
A literal value. (Due to Godot limitations, it is not currently possible to assign symbol literals to constants. This limitation will hopefully be lifted in a future version of GDLisp.)
A
prognblock with zero terms, or aprognblock with exactly one term which is a constant expression.A built-in function call that performs basic arithmetic, such as
+orabs, which is called with only constant expressions as arguments.An array or dictionary literal consisting of only constant expressions as elements.
A field access
foo:bar, wherefoois the name of an enum andbaris a valid option for that enum.A call to the
preloadspecial form.
A constant declaration defines a name in the value namespace for the current module.
Enumerations
(defenum enum-name entries ...)
(defenum enum-name public-or-private entries ...)
An enumeration is a scoped namespace containing a finite number of values, useful for representing a collection of choices.
Each entry in the enumeration can be specified either as a symbol literal or as a two-element list, where the first element is the symbol literal and the second is the constant expression indicating the value of the enum entry.
Examples:
(defenum PlayerChoice
ATTACK DEFEND HEAL PASS)
(defenum Color
(RED 0) (GREEN 1) (BLUE 2))
An enumeration defines a name in the value namespace. This name
behaves like a GDLisp object and has fields defined corresponding to
the names indicated in the entries. In the first example above,
PlayerChoice is a value for which PlayerChoice:ATTACK,
PlayerChoice:DEFEND, PlayerChoice:HEAL, and
PlayerChoice:PASS are all distinct integer values. In the second
example above, Color is a value, and Color:RED is the value 0,
Color:GREEN is the value 1, and Color:BLUE is the value 2. If
provided, the value of an enumeration constant must be an integer.
The progn Directive
(progn body ...)
progn is a special sort of directive, in that it can be used as a
declaration or an expression. In declaration context, it takes zero
or more declarations and evaluates them in order in the current
scope, as though the progn wasn’t even there.
A progn directive is never useful directly in a file in
declaration context, since it would be easier and more readable to
simply place the declarations at the top-level. However, it is useful
in macro expansions, when a macro wishes to define multiple
declarations but must evaluate to a single declaration S-expression.
Visibility Modifiers
Several module-level declarations take an optional visibility
modifier. A visibility modifier, if provided, must either be the
symbol private or the symbol public. If a visibility modifier
is not provided, it is always assumed to be public.
A name with public visibility can be accessed from anywhere. Any other module is free to import the name and reference, call, or instantiate it at their liberty.
A name with private visibility is only directly usable within the current module. The current module can freely use the name, but it is an error to attempt to import the name in another module.
Name Normalization
GDLisp is far more lenient than GDScript when it comes to identifiers.
In particular, GDLisp allows several non-standard characters such as
- and ? in identifiers, as well as Unicode characters.
Additionally, GDLisp does not have a notion of “keywords”, and it’s
perfectly kosher to define a variable called if or while
(though it may confuse the readers of your code).
When the GDLisp compiler translates your code into GDScript, it must convert these identifiers into valid GDScript identifiers. The exact translation rules are an implementation detail that may change in future releases of GDLisp, but some guarantees are made in order to maximize compatibility.
Any name which is a valid GDScript identifier and not a GDScript keyword will be left unchanged. So
foo,foobar,player_health1, andiwill all be left untouched by the GDLisp compiler.An ASCII arrow
->in a name will be translated to_to_. This allows a name likearray->listto translate into GDScript asarray_to_list.A dash
-that is not part of an ASCII arrow is translated to an underscore_. So a conventional Lisp function likecreate-playerwill have its name translated into the GDScript functioncreate_player.A question mark
?at the end of a name will be translated tois_at the beginning. For instance, a Lisp predicate calledpositive-number?will be translated to the GDScript functionis_positive_number.If the name is a reserved word in GDScript, then an underscore will be prefixed, so
iftranslates to_ifwhen used as a variable name.Any other characters, or a
?that is not at the end of a name, will translate in an implementation-defined way.
Warning
It is undefined behavior to define two names in the same
scope and namespace that will normalize to the same name under
these rules. So, for example, it is undefined behavior to define
functions called foo-bar and foo_bar in the same scope,
since these names will both translate to foo_bar.
Reserved Names
While GDLisp allows the programmer to define variables and functions with a very broad set of identifier names, some names are specifically reserved for use by the GDLisp compiler. GDLisp programs should never define any names, in any namespace, that conflict with a GDLisp reserved name. The compiler may or may not flag such behavior.
The names
self,super, andGDLispare reserved.All names beginning with
sys/(including the forward slash) or__gdlispare reserved.
Further, names defined in the sys/ namespace are strictly reserved
for internal use and are an implementation detail of the compiler.
GDLisp programmers should never directly invoke such functions or
reference such values, and the behavior of those names may change at
any time, even in a maintenance release.
Order of Definition
Generally speaking, classes and functions defined in a GDLisp module can reference each other freely and can be defined in any order. However, there is an important exception to this rule, and that is macros.
When a macro foo is defined, whether by defmacro,
define-symbol-macro, macrolet, or symbol-macrolet, that
macro and all of its dependencies must be fully defined. That is,
every function that foo calls and every constant, enum, or class
name that foo references must already be defined in a preloaded
file or earlier in the current file, and the same must be
recursively true of all of the names referenced by the macro foo.
This is a very strong constraint which is necessary to allow the macro
to be loaded during compilation.