Cyen's system of variables is based on references to places where memory is stored. Variables are the chunks of memory, references define where they are. References are a readable alias for the address of a variable.
Variables
In Cyen, any identifier identifies a reference to some form of memory. It depends on the context where this memory is located. These chunks of memory are called variables. A reference is used to locate these variables. The runtime manages where these chunks of memory are, this should not be something the programmer needs to care about. However, it is up to the programmer to quickly create a local reference to some hard-to-reach variable.
println x ~~ Prints the value at the variable behind the name 'x'
Variable and reference, what’s the difference?
A variable is the runtime chunk of memory. This chunk of memory stores the value that is obtained when the variable is read through a reference.
A reference is the compile-time name that can be used to access a variable. These are translated to internal memory references by the compiler. Obviously, not direct memory addresses, but their names are transformed into logical directives to find the chunk of memory.
Local variables
Local variables are a simple type of variable only present during the lifetime of a function call. They are all created at the beginning of the function call, but their references are only valid after definition of the reference.
Definition of a local variable
Here we define a reference to some local variable in the simplest way, we call the reference i
. We may not read the reference before assigning it a value. The keyword loc
tells the compiler it references a local variable.
loc i
println i ~~ Compiler error: i is not assigned
Note, if the local variable reference is only defined but a value is never assigned, it may happen that the compiler omits it.
Pre-assign
You can join the assignment of a variable with its definition.
loc i = 4
Read-only
Use the fixed
keyword to make a variable read-only. The only moment it can be assigned is in the definition of the variable. It is not possible to split the assignment. However, it is legal, yet completely useless, to not assign a read-only variable.
loc fixed i = 4
~~ Reading and writing this variable both generate compiler errors
loc fixed voldemort
Predefined type
It is possible to give the local variable a type filter. This is the type stored in the variable for as long as the reference lasts.
loc i: int
Arguments of a function
The arguments of a function are also local variables, and they get pre-assigned as part of the function call. Their references can be defined in two ways:
- The function header
- With a parameter reference definition
The amount of local memory chunks created for parameters is either the amount of parameters in the signature, or (if the signature is absent) the amount of references declared in the header.
Parameters in the function header
Parameter references can be defined in the function header after the name of the function. They are writable: you can re-assign a parameter. It is illegal to define more parameter references than defined in the signature.
func myFunction(x, y, z):
~~ x, y, and z are references to the parameters of the function
::
@ int -> void
func illegal(x, y, z):
~~ Defined 3 references, but there is only one parameter in the signature!
::
Parameter reference definitions
It is also possible to refer to a parameter by parameter index. This index is zero-based and must be a compile-time constant. This is useful if one parameter is optional or has different meanings based on another parameter. It is illegal to request a parameter outside of the amount of parameters defined in the signature/header.
@ (int, str, float) -> void
func myFunction:
par x: 0 ~~ int
par y: 1 ~~ str
par z: 2 ~~ float
par illegal1: x ~~ Parameter index be a compile time constant!
par illegal2: 3 ~~ Out of bounds! There's only parameters at index 0, 1, and 2
::
Note that a var-args parameter is one parameter, of an array type. It is no privilege to endless parameter indices.
References
References can constructed manually as preferred. References can refer to any kind of variable. It is, for example, possible to define a second reference to the same local variable.
Creating references
A manual reference is defined using the ref
keyword. To create a reference, we need another reference.
loc l = 3 ~~ Define a local variable to refer to
ref r -> l ~~ Define the reference r to refer to the same variable as l
~~ Now we can interchange the names l and r seamlessly
r = 6
println l ~~ Prints 6
Note that when a local variable is not assigned, any reference to that variable may not be read.
Making a read-only reference
When the used reference is read-only, the new reference will be implicitly read-only too. However, it is possible to make a reference read-only when the used reference is writable.
loc writable = 3 ~~ This local variable reference is writable
ref fixed readonly -> writable ~~ This local variable reference is read-only
~~ The value of the variable can be altered with the writable reference
writable = 6
println readonly ~~ Prints 6
~~ ...but not with the read-only reference
readonly = 3 ~~ Illegal!
println writable
Redefinition and undefinition
It is possible to redefine or undefine a reference. Redefining is the act of altering what variable a reference refers to. Undefining is the act of revoking a reference. Both happen at compile time and compilers may optimize and reuse local variables in these cases.
Redefining a reference
Redefining can be done simply by creating a reference with the same name in any preferred way.
loc a = 0
loc b = 1
ref reference -> a
println reference ~~ 0
ref reference -> b
println reference ~~ 1
Undefining a reference
Undefining a reference is done with the del
keyword.
loc a = 0
ref reference -> a
println reference ~~ 0
del reference
println reference ~~ Compiler error: reference is not defined!
Scopes
Scopes play an important role in re-/undefining references: within a child scope, one can redefine a reference in the parent scope, but its redefinition will last only within the child scope.
Redefinition of a reference in a child scope
Any reference is inherited from the parent scope, until a child scope redefines the reference in the child scope. This redefinition is only valid within that child scope. When undefining a reference in a child scope, it will return back to the reference inherited from the parent scope - it is not possible to fully undefine a reference in a child scope.
loc a = "A"
loc b = "B"
loc c = "C"
ref reference -> a
if true:
println reference ~~ Prints A
~~ Redefine reference
ref reference -> b
println reference ~~ Prints B
~~ Undefine reference
del reference
println reference ~~ Prints A
~~ Redefine reference again
~~ (to demonstrate the effect of scopes)
ref reference -> c
println reference ~~ Prints C
::
~~ Here we exited the scope of the if block
~~ The reference will be back to A, it is not C!
println reference ~~ Prints A
Hi! I'm Samū, a furry, artist and game developer from the Netherlands. This is my blog, where I write about my projects and ideas.