8.16.0.1
GDLisp is a lisp dialect that compiles to
GDScript.
Produces (when run):
GDLisp is a way to write script files for the
Godot engine
without actually writing (directly) in the scripting language it
provides.
The main benefits this provides is that you can write macros and
(hopefully) enter into Lisp game jams. Also you don’t have to deal
with semantic whitespace.
1.1 The GDLisp script🔗ℹ
A GDLisp script has the following grammar:
program | | = | | top-level-stmt ... |
| | | | |
top-level-stmt | | = | | escape-stmt |
| | | | | top-class-stmt |
| | | | |
escape-stmt | | = | | (require require-spec ...) |
| | | | | (module module-body ...) |
| | | | |
top-class-stmt | | = | | (class-name name) |
| | | | | class-stmt |
| | | | |
class-stmt | | = | | (extends parent-name) |
| | | | | stmt |
Where escape-stmts are put directly into the surrounding
module, and top-class-stmts are interpreted as a GDLisp
class.
Statements and expressions have the following grammar:
stmt | | = | | (define maybe-var-prefix binding) |
| | | | | (var maybe-var-prefix binding) |
| | | | | (define maybe-static (name-id func-param ...) | body-expr ...+) |
|
| | | | | (func maybe-static (name-id func-param ...) | body-expr ...+) |
|
| | | | | (class name-id | class-stmt ...) |
|
| | | | | (signal name-id) |
| | | | | (begin stmt ...) |
| | | | | stmt-expr |
| | | | |
func-param | | = | | name-id |
| | | | | [binding] |
| | | | |
maybe-var-prefix | | = | | |
| | | | | export |
| | | | | (export export-arg ...) |
| | | | |
maybe-static | | = | | |
| | | | | static |
| | | | |
binding | | = | | name-id |
| | | | | name-id : type-id |
| | | | | name-id : type-id default-expr |
| | | | | name-id := default-expr |
| | | | |
expr | | = | | (begin body-expr ...) |
| | | | | variable-id |
| | | | | (.-field-name target-expr) |
| | | | | (.method-name target-expr method-arg-expr ...) |
| | | | | (function-expr function-arg-expr ...) |
| | | | | literal-expr |
| | | | | special-expr |
| | | | |
literal-expr | | = | | [list-element-expr ...] |
| | | | | {kv-pair ...} |
| | | | |
special-expr | | = | | cond-expr |
| | | | | let-expr |
| | | | | recur-expr |
| | | | | match-expr |
| | | | | for-expr |
| | | | |
kv-pair | | = | | key-expr val-expr |
| | | | |
body-expr | | = | | (define binding) |
| | | | | expr |
| | | | |
cond-expr | | = | | (cond | [pred-expr then-body-expr ...+] ... | maybe-else-clause) |
|
| | | | |
maybe-else-clause | | = | | |
| | | | | [else else-body-expr ...+] |
| | | | |
let-expr | | = | | (let maybe-let-name | (binding ...) | body-expr ...+) |
|
| | | | |
maybe-let-name | | = | | |
| | | | | name-id |
| | | | |
recur-expr | | = | | (recur recur-target-id arg-expr ...) |
| | | | |
match-expr | | = | | (match target-expr | [match-pattern body-expr ...+] | ...) |
|
| | | | |
match-pattern | | = | | (or subpattern ...) ; where subpattern has no (var _) patterns |
| | | | | subpattern |
| | | | |
subpattern | | = | | constant-pattern |
| | | | | variable-id |
| | | | | _ |
| | | | | (var name-id) |
| | | | | [subpattern ...] |
| | | | | [subpattern ... ..] |
| | | | | {kv-pattern ...} |
| | | | | {kv-pattern ... ..} |
| | | | |
kv-pattern | | = | | constant-pattern subpattern |
| | | | |
constant-pattern | | = | | constant-number |
| | | | | constant-string |
| | | | | constant-boolean |
| | | | |
for-expr | | = | | (for ([name-id target-expr]) | body-expr ...+) |
|
Syntax transformers
are expanded (roughly) as usual.
Analogous to the equivalent bindings from racket/base.
These have transformer bindings that prohibit them from being used
outside of their context.
There are a few caveats and things to note about GDLisp.
1.3.1 Name mangling🔗ℹ
Racket identifiers used as variables are mangled before being emitted
as GDScript, so they are valid GDScript identifiers. Most notably,
hyphens ("-") are converted to underscores ("_"). Names that mangle to
the same identifier will conflict. This does also mean
that macros which introduce variable bindings are unhygienic. Use
(gensym) if you need a name which doesn’t conflict.