Codeblocks in Lynxer
This document describes the codeblock feature in Lynxer, which allows storing and executing blocks of code dynamically. Codeblocks are a powerful tool for implementing callbacks, higher-order functions, and dynamic behavior.
Table of Contents
- Introduction
- Syntax
- Storing Codeblocks
- Executing Codeblocks
- Parameterized Codeblocks
- Inline Execution
- Named Codeblocks
- Limitations
- Examples
- See Also
Introduction
Codeblocks in Lynxer allow you to store executable blocks of code in variables and execute them dynamically. They are useful for implementing callbacks, higher-order functions, and dynamic behavior where the logic is determined at runtime.
Syntax
Codeblocks are defined using the codeblock type. They can be stored in variables, passed as arguments, and executed using the exec() function.
Storing Codeblocks
You can store a block of code in a variable using the codeblock type:
codeblock helloWorld = {
println("Hello world!");
};
Executing Codeblocks
To execute a stored codeblock, use the exec() function:
exec(){{helloWorld}}
Parameterized Codeblocks
Codeblocks can declare parameters and their types. These parameters are checked against the provided values when the codeblock is executed.
Declaring Parameters
codeblock example = {
println(name, age);
}[str name, int age]
Executing with Parameters
exec("Alice", 30){{example}}
- The parameter list is positional and typed. Each name used in the codeblock must appear in the list, and each supplied value is checked against its declared type.
- The semicolon after the closing
]is optional.
Inline Execution
You can execute a codeblock inline using the exec() function with a parameter list:
str text = "left";
exec(str text) {
println(text);
}
- Each declared value is looked up in the surrounding scope, checked against its declared type, and is available under its declared name while the block runs.
- The declaration is temporary and does not create magic
execvariables.
Named Codeblocks
Codeblocks can be passed as arguments to functions and executed dynamically:
Example: Passing Codeblocks to Functions
global setup() {
codeblock code1 = {
println("A");
};
}
global something(){handler} {
exec(){{handler}}
}
global main() {
global.something(){{code1}}
}
- Codeblock identifiers are unique across the entire source file, regardless of where they are declared.
- A
codeblockvariable and a caller-supplied code-block parameter cannot reuse the same identifier. - Named blocks retain their identity when passed to functions.
Caller-Supplied Blocks on Any Function Form
The {...} block list is not specific to global. A func or local declaration takes caller-supplied blocks the same way: the list follows the () parameter list and precedes the body, and the parser does not distinguish the three forms when reading it.
global setup(){}
global runTwice(){first}{second}{
exec(){{first}}
exec(){{second}}
}
func applyTwice(int value){apply}{
exec(value){{apply}}
exec(value){{apply}}
}
global main(){
runTwice(){
println("one");
}{
println("two");
}
codeblock triple = {
println(value * 3);
};
applyTwice(4){{triple}} // 12 printed twice
local notify(){block}{
exec(){{block}}
}
notify(){
println("done");
}
}
- A
funcreached through an import also takes blocks at the call site:global.lib.applyTwice(4){ ... }. - Callers may mix inline blocks (
(){ ... }) and named references ((){{var}}) in one call. - Entry points (
global setup()/global main()) may not declare codeblock parameters.
See functions for the three declaration forms and their calling conventions.
Inferred Parameters
If the parameter list is omitted, the names and types are inferred from the variables referenced in the codeblock:
codeblock example = {
println(text);
};
exec("Hello!"){{example}}
- The first value is bound to
text, the next value to the next referenced variable, and so on. - The number of provided values must match the number of variables required by the codeblock.
- These bindings are temporary and restored after execution.
Limitations
- No Nested Scopes: Codeblocks do not introduce nested scopes. Variables declared inside a codeblock are not visible outside of it, and vice versa.
- No Block Expressions: Codeblocks cannot be used as expressions (e.g., returning their result directly). Use explicit
returnstatements. - Unique Identifiers: Codeblock identifiers must be unique across the entire source file.
- No Async/Await: Codeblocks cannot be used with
async/awaitsyntax. - Old Syntax: The old
exec({name})form is replaced byexec(){{name}}.
Examples
Example 1: Basic Codeblock
codeblock greet = {
println("Hello, " + name + "!");
}[str name] // requires a user provided parameter named "name"
exec("Alice"){{greet}}
Example 2: Inline Execution
str message = "World";
exec(str message) {
println("Hello, " + message + "!");
}
Example 3: Passing Codeblocks to Functions
global runCode(){code} {
exec(){{code}}
}
global main() {
codeblock task = {
println("Executing task...");
};
global.runCode(){{task}}
}
Example 4: Inferred Parameters
codeblock printValues = {
println(x, y);
};
exec(10, 20){{printValues}}
See Also
- Language Reference — For more details on Lynxer syntax and types.
- functions — The
global,funcandlocaldeclaration forms and their calling conventions. - Built-in Functions — For built-in higher-order functions.
- Limitations — For constraints on codeblocks and other features.