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
ClassDeclaration
NamespaceDeclaration
VariableDeclaration:
LocalVarDeclaration
GlobalVarDeclaration
LocalVarDeclaration:
'local' Identifier {',' Identifier} ['=' ExpressionList]
GlobalVarDeclaration:
'global' Identifier {',' Identifier} ['=' ExpressionList]
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'
'class'
'instance' [(Identifier {'.' Identifier}) | ('(' Expression')')]
Identifier {'.' Identifier}
'namespace'
'thread'
'nativeobj'
'weakref'
ClassDeclaration:
['local' | 'global'] 'class' Identifier [':' Expression] '{' {ClassMember} '}'
ClassMember:
[Decorators] SimpleFunctionDeclaration
[Decorators] 'this' FunctionBody
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 class declarations, Classes.
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 (or variables), just write an assignment after the declaration:
local x = 5 // declares 'x' with value 5 local y, z = 1, 2 // declares 'y' and 'z' with values 1 and 2, respectively
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.
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 under normal circumstances 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 class declarations, and they work a lot like class 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 classes.
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.
