Getting Started
To write your first program in Lucia, follow the steps below:
1. Install Lucia
First, you need to install the Lucia language. Follow the installation guide to set it up on your system.
You can also customize your Lucia preferences by modifying the config.json file. For details on how to configure the file, refer to the Guide to config.json.
2. Create a "Hello, World!" Program
Create a new file named hello.lucia
and write the
following code:
print("Hello, World!")
3. Run the Program
Once the program is saved, you can run it by executing the following command in your terminal:
lucia hello.lucia
Output:
Hello, World!
Variables & Data Types
In Lucia, variables are declared using a type annotation followed
by the =
assignment operator. Here's how you define variables and assign
values:
x: int = 10 // Integer
name: str = "Lucia" // String
pi: float = 3.14 // Float
isActive: bool = true // Boolean
result: void = null // Void (no value)
data: any = "This can be anything" // Any type
numbers: list = [1, 2, 3, 4] // List
person: map = {"name": "Lucia", "age": 20} // Map
Supported Data Types:
- int: Whole numbers (e.g., 10, -5).
- str: Text values (e.g., "Hello, World!").
- bool: Logical values (
true
,false
ornull
). - void: Represents the absence of a value (
null
). - float: Decimal numbers (e.g., 3.14, -0.001). The default precision for floating-point numbers is 28 decimal places.
- any: A type that can hold any value (e.g., can be a string, number, list, etc.).
- list: Ordered collection of values (e.g.,
[1, 2, 3]
). - map: Key-value pairs (e.g.,
{"name": "Lucia", "age": 25}
).
F-Strings
Lucia supports f-strings for string interpolation, allowing you to
embed variables or expressions directly within strings using curly braces {}
.
Basic Usage
name: str = "Lucia"
version: str = "1.3"
greeting: str = f"Hello, {name}! Welcome to Lucia version {version}."
print(greeting) // Output: Hello, Lucia! Welcome to Lucia version 1.2.1.
Including Expressions
You can also include expressions inside f-strings:
x: int = 5
y: int = 10
result: str = f"The sum of {x} and {y} is {x + y}."
print(result) // Output: The sum of 5 and 10 is 15.
Operators
Lucia supports a variety of operators that can be used for arithmetic, comparison, logical operations, and more. Below are the categories of supported operators:
Arithmetic Operators
Lucia supports the basic arithmetic operators for mathematical calculations:
x: int = 10 + 5 // 15
product: int = 4 * 2 // 8
division: float = 10 / 2 // 5
exponent: float = 2 ^ 3 // 8
remainder: int = 10 % 3 // 1
Comparison Operators
These operators allow you to compare two values:
isEqual: bool = (5 == 5) // true
isGreater: bool = (10 > 3) // true
isLessThan: bool = (5 < 10) // true
isGreaterOrEqual: bool = (5 >= 5) // true
isNotEqual: bool = (5 != 3) // true
Logical Operators
Logical operators are used to perform logical operations:
isTrue: bool = (true && false) // false
isNotTrue: bool = !true // false
isEitherTrue: bool = (true || false) // true
isMember: bool = (3 ~ [1, 2, 3]) // true
Assignment Operators
Lucia also supports shorthand assignment operators for modifying variable values:
x: int = 10 // x = 10
x += 5 // x = 15
x -= 3 // x = 12
x *= 2 // x = 24
x /= 4 // x = 6
Supported Operators
In addition to the standard arithmetic, comparison, and logical operators, Lucia includes several other operators:
- +: Addition
- -: Subtraction
- *: Multiplication
- /: Division
- ^: Exponentiation
- %: Modulo (remainder of division)
- ==: Equality comparison
- >: Greater than
- <: Less than
- >=: Greater than or equal to
- <=: Less than or equal to
- !=: Not equal to
- &&: Logical AND
- ||: Logical OR
- !: Logical NOT
- ~: In operator (checks if left value exists in a list or map)
- +=: Add and assign
- -=: Subtract and assign
- *=: Multiply and assign
- /=: Divide and assign
- abs: Absolute value, usedas
|x|
Blocks in Lucia
In Lucia, blocks of code are used to group statements together.
This can be seen in control structures like if
statements, loops, functions,
and more. Lucia uses the end
keyword to mark the end of a block, which is
similar to using braces in C or indentation in Python.
Block Syntax
Blocks in Lucia start with a statement that is followed by a
:
, and the body of the block is indented (optional, but recommended for
readability). The block is terminated using the end
keyword.
Example of a Block
Here's an example of a block used with an if
statement:
age: int = 18
if (age >= 18):
print("You are an adult.")
end
The code within the block (in this case, the print()
function) is indented, and the block is terminated with end
.
Why Use Blocks?
Blocks help organize code into logical groups. By using the
end
keyword, Lucia ensures that the boundaries of each block are clearly
defined, which reduces errors and improves readability.
Indentation
Unlike Python, Lucia does not rely on indentation to define
blocks. Blocks are explicitly marked using the :
after a statement and are
terminated with the end
keyword. While indentation is not required, it is
recommended for better readability.
Example
Both of the following examples are valid in Lucia:
age: int = 18
if (age >= 18):
print("You are an adult.")
end
age: int = 18
if (age >= 18):
print("You are an adult.")
end
Since blocks in Lucia are clearly defined by :
and
end
, indentation does not affect how the code runs. However, using consistent
indentation is recommended to improve readability.
Summary
- A block in Lucia is created by using a statement followed by a colon (
:
) and indented code. - Blocks are terminated with the
end
keyword. - Indentation is important in Lucia, and the code inside a block must be consistently indented.
- Blocks improve code organization and readability, ensuring that related code is grouped together.
Control Flow
If Statements
Control the flow of your program using if
statements.
An if
statement evaluates a condition and executes the code block based on
whether the condition is true or false.
Basic Example
Here's an example of how an if
statement works in
Lucia:
age: int = 18
if (age >= 18):
print("You are an adult.")
end
if (age == 69):
print("Nice!")
end else:
print("You are not 69.")
end
Example Breakdown
In the above example:
- The condition
if (age >= 18):
checks if theage
variable is greater than or equal to 18. - If the condition is true, the block under the
if
statement is executed, printing "You are an adult." - If the condition
age == 69
is true, it prints "Nice!". If not, theelse
block is executed, printing "You are not 69."
Functions
In Lucia, functions are defined using the fun
keyword, but if any modifiers are used, the fun
keyword is not required.
Basic Function Declaration
A function can be defined with parameters and an optional return type:
fun add(a: int, b: int) -> int:
return a + b
end
Modifiers
Functions can have different modifiers that change their behavior:
public
– The function is accessible from other modules (default).private
– The function is only accessible within the same module.static
– The function belongs to the class itself rather than an instance.non-static
– The function belongs to an instance of the class (default behavior).final
– The function cannot be redefined later.mutable
– Explicitly states that the function can be redefined (default behavior).
By default, every function is
public non-static mutable
.
Using Modifiers
When using modifiers, the fun
keyword is not
required:
public non-static final main() -> int:
return 0
end
The above function is equivalent to:
fun main() -> int:
return 0
end
Empty Functions
If a function has no body, the colon :
is not
required:
fun main() -> void end
However, if the function has a body, the colon must be included:
fun main() -> void:
print("Hello, World!")
end
Example Function with Default Parameters
Functions can have default parameter values:
public static final fun name(arg1, arg2: int, arg3 = 10, arg4: int = 10) -> int:
return 0
end
Calling Functions
To call a function, use the function name followed by parentheses and any required arguments:
result: int = add(5, 10)
To call a function with a named parameter, use the parameter name
followed by a =
and the value:
result: int = add(b = 10, a = 5)
Loops
For Loop
Lucia provides a for
loop for iterating over lists. A
valid range must be enclosed in parentheses:
for (i in [1...10]):
print(i) // Output: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
end
List Patterns
Lucia allows pattern-based list generation inside ranges:
[1, 3, 5...101] // Generates: [1, 3, 5, 7, ..., 101]
[0, 2, 4, 6...100] // Generates: [0, 2, 4, 6, ..., 100]
If the pattern is not recognized, a
ListPatternRecognitionWarning
is raised, and the list remains unchanged:
[1, 2, 6...14] // Warning: Unrecognized pattern, returns [1, 2, 6, 14]
While Loop
Lucia also supports a while
loop:
while (count < max):
print(count)
count += 1
end
Error Handling
Lucia handles errors using try
and catch
blocks. Functions can throw errors using the throw
keyword, and you can specify
an error type if needed.
Function with Error Throwing
In Lucia, errors can be thrown using throw
and caught
with try
and catch
:
fun divide(a: int, b: int) -> int:
if (b == 0):
throw "Cannot divide by zero!" from ZeroDivisionError
end
return a / b
end
Try and Catch
The try
block is used to attempt the operation that
might throw an error, and the catch
block handles the error:
try:
result = divide(10, 0)
end catch (e):
print("Error: ", e)
end
Also, you don't need to use the catch
block if you
don't want to handle the error:
try:
result = divide(10, 0)
end
Default Error Type
If no specific error type is mentioned, Lucia will raise an error
from LuciaException
by default:
fun divide(a: int, b: int) -> int:
if (b == 0):
throw "Cannot divide by zero!"
end
return a / b
end
In the example above, if throw
is used without a
specified error type, the system will raise a LuciaException
by default.
Defining a new Exception
You can define a new exception type by using the
Exception
keyword:
Exception CustomError
throw "Error message" from CustomError
Defining a new Warning
You can define a new warning type by using the
Warning
keyword:
Warning CustomWarning
throw "Warning message" from CustomWarning
Basic Input/Output
Output
Use the print()
function to display output:
print("Hello, Lucia!") // Output: Hello, Lucia!
Input
Use the input()
function to get user input:
name: str = input("Enter your name: ")
print("Hello, ", name, "!") // Output: Hello, [name]!
Other Statements
Import
Lucia allows flexible importing of modules. The
import
statement can use modifiers such as as
and
from
, and they can appear in any order or be omitted entirely:
import math // imports math module
import math as m // imports math module and renames it as 'm'
import math from "math/" // imports math from the specified directory
The modifiers as
and from
can be stacked
in any order or omitted:
import math
import math as m
import math from "math/"
import math from "math/" as m
With
The with
statement is used for context management:
with (TestContext()) as tc:
end
with (File()) as f:
print(f.open("test.txt"))
end
Forget
The forget
statement removes a variable from memory:
i: int = 10
forget i
After using forget
, the variable i
is no
longer available:
i: any = "str"
Objects
Object Declaration
In Lucia, objects can be declared using the object
keyword. Here's an example:
object O:
init():
i: int = 10
end
fun main() -> void:
print(i)
end
end
Object Usage
Once declared, you can create an instance of an object and call its methods:
o = O()
o.main()
Property
A property is any variable, function, or object that is called
inside another. For example, math.pi
is a variable property of the
math
module.
Predefs
Predefs are special commands in Lucia that allow you to modify or define certain tokens before they are sent to the interpreter. They are processed during the pre-interpretation phase of your code. All predefs start with the # symbol.
Predef Types
1. #alias
The #alias
predef allows you to create an alias,
which means defining the first argument to be equivalent to the second argument.
Example:
#alias true -> false
This command makes true
behave as false
throughout the code. After this alias, any reference to true
will be treated as
false
.
2. #del
The #del
predef removes an alias that has been
previously defined with #alias
. It restores the original meaning of the token.
Example:
#del true
This command removes the alias for true
, making it
behave as it originally did, i.e., true
again.
3. #config
The #config
predef allows you to modify the
configuration of the interpreter. It can be used to set various options or preferences.
Example:
#config debug = true
Can be reset to default by:
#config debug reset
This command sets the debug mode to true, enabling additional
debugging features in the interpreter. It's the same as using the config.json
file.
You can use predefs to modify the behavior of built-in tokens and tailor the language to your specific needs before the interpreter executes your code.
Code Blocks
Lucia supports code blocks, which allow you to execute code written in other languages directly within Lucia scripts. Supported languages include:
- C
- ASM
- Python
Configuration Requirement
Code blocks require the execute_code_blocks
option to be set to
true
for your language in your config.json
file:
{
...
"execute_code_blocks": {
"C": true,
"ASM": true,
"PY": true
},
...
}
Syntax
Code blocks are written using a language prefix followed by an
end
keyword to mark the end of the block.
C Example
C:
#include <stdio.h>
int main() {
printf("Hello, World!");
return 0;
}
end
ASM Example
ASM:
mov eax, 1
mov ebx, 0
int 0x80
end
Python Example
PY:
print("Hello, World!")
end
Exporting Variables from C Blocks
To retrieve values from C code, use the export
functions provided by the export.h
library. This library is
automatically included when executing C code blocks.
Example: Exporting an Integer
C:
#include <stdio.h>
int main() {
int a = 10;
export_int("a", a);
export_flush();
return 0;
}
end
print(a) // Output: 10
Export Functions
Function | Description |
---|---|
export_int(name: str, value: int)
|
Export an integer variable. |
export_float(name: str, value: float)
|
Export a float variable. |
export_double(name: str, value: double)
|
Export a double variable. |
export_str(name: str, value: str)
|
Export a string variable. |
export_bool(name: str, value: bool)
|
Export a boolean variable. |
export_list(...)
|
Export a list. |
export_map(...)
|
Export a map. |
export_flush()
|
Save exports to
export.json .
|
export_clear()
|
Clear all previously exported data. |
Conclusion
Lucia is a straightforward language designed for ease of use and flexibility. You've covered essential topics like variable declaration, control flow, functions, error handling, and objects. The syntax promotes clarity, and the use of blocks and indentation improves code organization.
To move forward, continue practicing with more complex projects, experiment with its features, and explore how to structure programs using Lucia's modular capabilities.
Happy coding with Lucia.
Comments
Lucia supports single-line and multi-line comments:
Single-line Comment
Multi-line Comment
In-line Comment
The in-line comment is used to add comments inside the code. It's defined by
<#
and#>
: