Declarations
The various declarations in the language are defined by the following syntax.
DeclarationStatement:
VariableDeclaration StatementTerminator
[Decorators] OtherDeclaration
Decorators:
Decorator {Decorator}
Decorator:
'@' Identifier {'.' Identifier} ['(' ('with' Expression [',' ExpressionList] | [ExpressionList]) ')']
OtherDeclaration:
FunctionDeclaration
ObjectDeclaration
NamespaceDeclaration
VariableDeclaration:
LocalVarDeclaration
GlobalVarDeclaration
LocalVarDeclaration:
'local' Identifier {',' Identifier} ['=' Expression]
GlobalVarDeclaration:
'global' Identifier {',' Identifier} ['=' Expression]
FunctionDeclaration:
['local' | 'global'] SimpleFunctionDeclaration
SimpleFunctionDeclaration:
'function' Identifier FunctionBody
FunctionBody:
'(' Parameters ')' (Statement | '=' Expression)
Parameters:
[('this' ':' Type | Parameter) {',' Parameter} [',' 'vararg']]
'vararg'
Parameter:
Identifier [':' Type] ['=' Expression]
Type:
BasicType
'!' 'null'
'any'
BasicType:
BasicType '|' BasicType
'null'
'bool'
'int'
'float'
'char'
'string'
'table'
'array'
'function'
'object' [(Identifier {'.' Identifier}) | ('(' Expression')')]
Identifier {'.' Identifier}
'namespace'
'thread'
ObjectDeclaration:
['local' | 'global'] 'object' Identifier [':' Expression] '{' {ObjectMember} '}'
ObjectMember:
[Decorators] SimpleFunctionDeclaration
Identifier ['=' Expression] StatementTerminator
NamespaceDeclaration:
['local' | 'global'] 'namespace' Identifier [':' Expression] '{' {NamespaceMember} '}'
NamespaceMember:
SimpleFunctionDeclaration
Identifier ['=' Expression] StatementTerminator
This page only documents variable and namespace declarations; for information on function declarations, see Functions, and for object declarations, Objects.
Variable Declarations
Variables are defined much like any other C-style language, except in place of a type, you use the "local" keyword:
local x
You can declare multiple variables on one line:
local x, y, z
Variables can optionally have an initializer, which allows you to give the variable a value along with declaring it, rather than declaring it and giving it a value on separate lines. To initialize a variable, just write an assignment after the declaration:
local x = 5
This declares the variable x and sets its value to 5.
Variable initializers can always be a non-constant expression. There is no difference between a variable declared at top-level and one declared within a function.
You can only initialize multiple declarations with a function call, vararg, or yield expression:
local x, y, z = func() // x, y, z are given the return values of func local a, b = vararg // a, b are given the first two variadic arguments local i, j = yield() // i and j are given the first two values that this coroutine was resumed with
If the variable is given no initializer, it defaults to the value null:
function func() { return 1, 2 } local x // is null local w, y, z = func() // w is 1, y is 2, z is null
When declared at top-level, 'local' variables are only visible within the module.
Global Variables
Another kind of variable is a global variable. These are declared in much the same way as local variables:
global x = 5
Globals are inserted into the nearest non-local namespace at runtime. This means that they are inserted as symbols into the current module's namespace. After the module has been initialized, other modules will be able to see its global members.
Attempting to declare a global variable more than once in the same namespace (i.e. in the same module) will result in a runtime error.
Attempting to access a global variable which doesn't exist will also result in a runtime error.
You should only use globals when you need to expose a symbol to other modules. Everything else should be local.
Namespaces
NamespaceDeclaration:
['local' | 'global'] 'namespace' Identifier [':' Expression] '{' {NamespaceMember} '}'
NamespaceMember:
SimpleFunctionDeclaration
Identifier ['=' Expression] StatementTerminator
MiniD uses namespaces to hold symbols. Namespaces are implicitly used to hold class, instance, and module members. You can also explicitly declare your own namespaces.
Namespace declarations look a lot like object declarations, and they work a lot like object declarations as well. You can create either local or global namespaces. If no 'local' or 'global' keyword is present, it defaults to global at module-level, and local everywhere else (i.e. inside functions). All namespaces have a name. Namespaces optionally can have a 'parent' namespace, which is given after a colon after the name. If no parent is given, it defaults to the environment namespace of the current function. You can force a namespace to have no parent by putting null as the parent. Namespaces can have functions and variables declared within them, and the syntax for doing so is identical to that of objects.
There are two things which deserve mention, and they are linked. One is the idea of a parent namespace. Module namespaces in MiniD all have parents: each module namespace's parent is its owning package, or the global namespace if there is none. So with module "a.b", b's parent is namespace a, and a's parent is the global namespace. The global namespace has no parent. This is used to create a hierarchy of namespaces for performing global lookup from functions. If a global is looked up in a namespace, and it is not found, it goes to the parent and looks up there, continuing up the chain until either it finds the global or it runs out of namespaces to check. This leads into the second item: function environments.
When you declare a function, it is implicitly associated with a namespace called its environment. Whenever a global access occurs within that function, it looks up the global in the function's environment using the process described above. By default, function environments are set to the same environment as the function that created them. In most cases, this will be the enclosing module, but within namespaces, it is set to the namespace in which it was declared.
Changes from MiniD 1
Namespaces now default to the current function's environment as their parent, and functions inside namespaces have their environment set to the namespace.
