Fly Language Reference
Version: 1.0
Project: Fly Programming Language
License: Apache License v2.0
Table of Contents
- Introduction
- Lexical Elements
- Types
- Variables
- Functions
- Classes and Structures
- Enumerations
- Expressions
- Statements
- Namespaces and Imports
- Modifiers
- Comments
- Grammar Summary
1. Introduction
Fly is a compiled, high-level, general-purpose programming language with particular attention to simplicity, readability, and multi-paradigm support. Fly is built on LLVM infrastructure and aims to provide optional Garbage Collection.
Design Principles:
- Simple - Easy to read and write
- Fast - Compiled with LLVM for optimal performance
- Powerful - Multi-paradigm with modern features
2. Lexical Elements
2.1 Keywords
Fly reserves the following keywords:
as bool break byte case
char class const continue default
double elsif else enum error
fail false float for handle
if import interface int long
namespace new null private protected
public return short static string
struct switch true uint ulong
ushort while
2.2 Identifiers
Identifiers must start with a letter or underscore, followed by any combination of letters, digits, or underscores.
Syntax:
Identifier ::= [a-zA-Z_][a-zA-Z0-9_]*
Examples:
myVariable
_privateVar
counter123
MyClass
getValue
2.3 Literals
2.3.1 Numeric Literals
42 // integer literal
0 // zero
3.14 // floating-point literal
0.0 // floating-point zero
2.3.2 Boolean Literals
true // boolean true
false // boolean false
2.3.3 Character Literals
'a' // character
'Z' // uppercase character
'\n' // newline escape
2.3.4 String Literals
"Hello, World!"
"Fly Language"
"" // empty string
2.3.5 Null Literal
null // null value for reference types
2.4 Operators and Punctuators
Arithmetic Operators
+ // addition
- // subtraction
* // multiplication
/ // division
% // modulo
++ // increment
-- // decrement
Compound Assignment Operators
+= // add and assign
-= // subtract and assign
*= // multiply and assign
/= // divide and assign
%= // modulo and assign
Comparison Operators
== // equal to
!= // not equal to
< // less than
> // greater than
<= // less than or equal
>= // greater than or equal
Logical Operators
&& // logical AND
|| // logical OR
! // logical NOT
Bitwise Operators
& // bitwise AND
| // bitwise OR
^ // bitwise XOR
<< // left shift
>> // right shift
&= // bitwise AND and assign
|= // bitwise OR and assign
^= // bitwise XOR and assign
<<= // left shift and assign
>>= // right shift and assign
Other Operators and Punctuators
= // assignment
?: // ternary conditional
. // member access
[] // array subscript
() // function call / grouping
{} // block delimiters
, // separator
; // statement terminator (optional)
: // label/case separator
... // ellipsis
@ // annotation
3. Types
3.1 Built-in Types
3.1.1 Integer Types
| Type | Size | Range | Description |
|---|---|---|---|
byte | 8-bit | 0 to 255 | Unsigned byte |
short | 16-bit | -32,768 to 32,767 | Signed short integer |
ushort | 16-bit | 0 to 65,535 | Unsigned short integer |
int | 32-bit | -2,147,483,648 to 2,147,483,647 | Signed integer |
uint | 32-bit | 0 to 4,294,967,295 | Unsigned integer |
long | 64-bit | -9,223,372,036,854,775,808 to ... | Signed long integer |
ulong | 64-bit | 0 to 18,446,744,073,709,551,615 | Unsigned long integer |
Examples:
byte age = 25
short temperature = -10
ushort port = 8080
int count = 1000
uint id = 12345
long bigNum = 9999999999
ulong hugeNum = 18446744073709551615
3.1.2 Floating-Point Types
| Type | Size | Description |
|---|---|---|
float | 32-bit | Single-precision float |
double | 64-bit | Double-precision float |
Examples:
float pi = 3.14
double precise = 3.14159265359
3.1.3 Other Built-in Types
| Type | Description |
|---|---|
bool | Boolean type (true or false) |
char | Character type |
string | String type |
error | Error type for error handling |
Examples:
bool isActive = true
char letter = 'A'
string name = "Fly"
3.2 Array Types
Arrays can be declared with or without explicit size.
Syntax:
ArrayType ::= Type '[' [ Expression ] ']'
Examples:
// Dynamic array (size unspecified)
byte[] dynamicArray
int[] numbers
// Fixed-size array
byte[10] fixedBuffer
int[5] coordinates
// Multi-dimensional arrays
int[][] matrix
byte[][][] cube
3.3 Named Types
User-defined types include classes, structures, and enumerations.
Examples:
MyClass obj
Point location
Status currentStatus
3.4 Qualified Type Names
Types can be qualified with namespace prefixes.
Examples:
// Using dotted notation
utils.Helper helper
mylib.DataType data
4. Variables
4.1 Local Variables
Local variables are declared within functions or blocks.
Syntax:
LocalVar ::= [ Modifiers ] Type Identifier [ '=' Expression ]
Examples:
func() {
// Simple declaration
int x = 10
// Without initialization
bool flag
// Constant local variable
const int limit = 100
}
4.2 Variable Initialization
4.2.1 Basic Types
bool flag = true
int count = 42
float value = 3.14
string message = "Hello"
4.2.2 Null Initialization
MyClass obj = null
Type instance = null
4.2.3 Array Initialization
// Empty array
byte[] empty = {}
// Array with values
byte[] values = {1, 2, 3, 4, 5}
int[] numbers = {10, 20, 30}
// Fixed-size array
byte[3] buffer = {1, 2, 3}
5. Functions
5.1 Function Declaration
Functions and methods in Fly are always void—they never return a value. Because all functions are implicitly void, there is no need to specify a return type in the declaration.
Syntax:
Function ::= [ Modifiers ] Identifier '(' [ Parameters ] ')' ( Block | ';' )
Examples:
// Simple function
doSomething() {
// function body
}
// Function with parameters (all parameters require const modifier)
add(const int a, const int b) {
// function body
}
5.2 Function Parameters
All function parameters must use the const modifier. Parameters are always immutable within the function body.
Syntax:
Parameters ::= Parameter ( ',' Parameter )*
Parameter ::= 'const' Type Identifier
Examples:
// Multiple parameters (all const)
process(const int x, const float y, const bool flag) {
// implementation
}
// Array parameters
processArray(const byte[] data, const int[] indices) {
// implementation
}
5.3 Visibility Modifiers
Functions can have different visibility levels:
// Default visibility (package-private)
defaultFunction() {}
// Private function (internal use only)
private privateHelper() {}
// Public function (exported)
public publicAPI() {}
// Protected function (for inheritance)
protected protectedMethod() {}
5.4 Return Statement
Since all functions are void, the return statement is used only to exit the function early. It never carries a value.
// Return exits the function
noReturn() {
return
}
// Return used for early exit
process(const int x) {
if (x < 0) {
return // exit early
}
// continue processing
}
5.5 The Main Function
The main() function is the entry point of a Fly application.
Syntax:
main() {
// Application code
}
Key Characteristics:
- Function signature: Must be declared as
main()with no parameters and no return type - Entry point: The application starts execution from
main() - Automatic error handling: The main function has special error handling behavior
Error Handling and Return Codes:
When the application runs, main() automatically returns an exit code to the operating system:
- Return 0: If no unhandled errors occur (success)
- Return 1: If an unhandled error occurs (failure)
This behavior is automatic—you don't explicitly return an integer from main().
Example 1: Successful Execution
main() {
// Code executes successfully
int x = 10
int y = 20
// Automatically returns 0 (success)
}
Example 2: Unhandled Error
err0() {
fail "Something went wrong"
}
main() {
err0() // Error is not handled
// Automatically returns 1 (failure)
}
Example 3: Handled Error
err0() {
fail "Something went wrong"
}
main() {
handle err0() // Error is caught and handled
// Continues execution
// Automatically returns 0 (success)
}
Example 4: Captured Error with Graceful Handling
riskyOperation() {
fail "Operation failed"
}
main() {
error err handle {
riskyOperation()
}
if (err) {
// Error was caught and handled
// Continue with fallback logic
}
// Automatically returns 0 (success)
}
Best Practices:
- Always handle errors in main: Unhandled errors will cause the application to exit with code 1
- Use handle blocks: Wrap risky operations in
handleblocks to ensure graceful error handling - Check error variables: Use
if (err)to detect and respond to errors appropriately - Provide fallback logic: When errors occur, provide alternative execution paths
Summary:
main()is required (no return type, no parameters)- Exit code 0 = success (no unhandled errors)
- Exit code 1 = failure (unhandled error occurred)
- Use
handleto catch errors and ensure successful exit
6. Classes and Structures
6.1 Class Declaration
Classes support single inheritance. A class can extend one struct or one interface.
Syntax:
Class ::= [ Modifiers ] 'class' Identifier [ ':' Identifier ] '{' ClassMember* '}'
Examples:
// Simple class
class MyClass {
}
// Public class
public class Application {
}
// Class extending a struct
class Derived : BaseStruct {
}
// Class extending an interface
class MyImpl : Drawable {
}
6.2 Structure Declaration
Structures are value types similar to classes. A struct can only extend another struct.
Syntax:
Struct ::= [ Modifiers ] 'struct' Identifier [ ':' Identifier ] '{' StructMember* '}'
Examples:
// Simple structure
struct Point {
int x
int y
}
// Public structure
public struct Vector {
float x
float y
float z
}
// Struct extending another struct
struct Point3D : Point {
int z
}
6.3 Interface Declaration
Interfaces define contracts for classes. An interface can only extend another interface.
Syntax:
Interface ::= [ Modifiers ] 'interface' Identifier [ ':' Identifier ] '{' InterfaceMember* '}'
Examples:
// Simple interface
interface Drawable {
draw()
}
// Public interface
public interface Serializable {
serialize(const string path)
deserialize(const string data)
}
// Interface extending another interface
interface Resizable : Drawable {
resize(const int width, const int height)
}
6.4 Class Members
Classes can contain fields (attributes) and methods.
Examples:
public class Person {
// Private fields
private string name
private int age
// Public field
public bool isActive
// Constructor-like method (no return type)
public initialize(const string personName, const int personAge) {
name = personName
age = personAge
isActive = true
}
// Public method
public getName(const string[] out) {
out[0] = name
}
// Private method
private validate() {
// validation logic
}
// Static field
static int instanceCount = 0
// Static method
public static getCount(const int[] out) {
out[0] = instanceCount
}
}
6.5 Object Creation
Examples:
// Create instance
MyClass obj = new MyClass()
// Use with initialization
Person person = new Person()
person.initialize("John", 30)
7. Enumerations
7.1 Enum Declaration
Enumerations define a set of named constants. Enums cannot extend any other type.
Syntax:
Enum ::= [ Modifiers ] 'enum' Identifier '{' EnumEntryList '}'
EnumEntryList ::= EnumEntry ( ',' EnumEntry )*
EnumEntry ::= Identifier
Examples:
// Simple enum with comma-separated entries
enum Color {
RED, GREEN, BLUE
}
// Public enum
public enum Status {
IDLE, RUNNING, STOPPED, FAILED
}
// Multi-line enum for readability
enum Direction {
NORTH,
SOUTH,
EAST,
WEST
}
7.2 Using Enums
Examples:
processColor() {
// Declare and initialize
Color c = Color.RED
// Assignment
c = Color.BLUE
// Pass to function
setColor(Color.GREEN)
// Compare
if (c == Color.RED) {
// handle red
}
}
setColor(const Color c) {
// use color
}
8. Expressions
8.1 Primary Expressions
8.1.1 Literals
42 // integer literal
3.14 // float literal
true // boolean literal
'c' // character literal
"string" // string literal
null // null literal
8.1.2 Identifiers
myVariable // simple identifier
obj.field // member access
array[0] // array access
8.1.3 Parenthesized Expressions
(a + b)
(x * y + z)
8.2 Unary Expressions
Syntax:
UnaryExpr ::= ( '++' | '--' | '!' | '-' | '+' ) Expression
| Expression ( '++' | '--' )
Examples:
// Pre-increment/decrement
++counter
--index
// Post-increment/decrement
value++
count--
// Logical negation
!flag
!isActive
// Unary minus/plus
-value
+number
8.3 Binary Expressions
8.3.1 Arithmetic Operators
a + b // addition
x - y // subtraction
m * n // multiplication
p / q // division
r % s // modulo
8.3.2 Comparison Operators
a == b // equal to
x != y // not equal to
m < n // less than
p > q // greater than
i <= j // less than or equal
k >= l // greater than or equal
8.3.3 Logical Operators
flag1 && flag2 // logical AND
cond1 || cond2 // logical OR
8.3.4 Bitwise Operators
a & b // bitwise AND
x | y // bitwise OR
m ^ n // bitwise XOR
p << 2 // left shift
q >> 1 // right shift
8.4 Assignment Expressions
Syntax:
Assignment ::= Identifier AssignOp Expression
AssignOp ::= '=' | '+=' | '-=' | '*=' | '/=' | '%='
| '&=' | '|=' | '^=' | '<<=' | '>>='
Examples:
// Simple assignment
x = 10
name = "Fly"
// Compound assignment
a += 5 // a = a + 5
b -= 3 // b = b - 3
c *= 2 // c = c * 2
d /= 4 // d = d / 4
e %= 7 // e = e % 7
// Bitwise compound assignment
f &= mask // f = f & mask
g |= flag // g = g | flag
h ^= toggle // h = h ^ toggle
i <<= 2 // i = i << 2
j >>= 1 // j = j >> 1
8.4.1 Assignment vs Equality: Important Distinction
Fly clearly distinguishes between the assignment operator = and the equality comparison operator ==:
=(Assignment): Stores a value into a variable. This is a statement-level operation.==(Equality): Compares two values for equality. This is an expression that evaluates to a boolean.
Examples:
// Assignment: stores the value 5 into variable x
x = 5
// Equality comparison: compares x with 5, evaluates to boolean
if (x == 5) {
// x is equal to 5
}
// Assignment with equality comparison on right side
result = x == 5 // result gets true or false
// Complex example
a = a + 1 // a = (a + 1) - addition then assignment
b = a == 10 // b = (a == 10) - comparison then assignment
Parser Representation: Under the hood, the parser creates different AST structures:
- Assignment
a = exprcreatesASTBinaryOp(OP_BINARY_ASSIGN)with left=aand right=expr - Equality
a == bcreatesASTBinaryOp(OP_BINARY_EQ)with left=aand right=b - Assignment with equality
a = (b == c)creates nested structure:- Outer:
ASTBinaryOp(OP_BINARY_ASSIGN)with left=a - Right child:
ASTBinaryOp(OP_BINARY_EQ)with left=band right=c
- Outer:
Common Mistake:
// WRONG: Using = instead of == in condition
if (x = 5) { // This assigns 5 to x, then evaluates the result
// ...
}
// CORRECT: Using == for comparison
if (x == 5) { // This compares x with 5
// ...
}
8.5 Ternary Conditional Expression
Syntax:
TernaryExpr ::= Condition '?' TrueExpr ':' FalseExpr
Examples:
result = condition ? valueIfTrue : valueIfFalse
max = a > b ? a : b
status = isActive ? Status.RUNNING : Status.IDLE
8.6 Function Call Expressions
Examples:
// Function call without arguments
result = calculate()
// Function call with arguments
sum = add(10, 20)
process(x, y, z)
// Method call
obj.doSomething()
person.getName()
8.7 Array Value Expressions
Examples:
// Empty array
empty = {}
// Array with values
values = {1, 2, 3, 4, 5}
matrix = {{1, 2}, {3, 4}}
9. Statements
9.1 Expression Statements
Any expression can be used as a statement.
Examples:
// Function call
doSomething()
calculate()
// Increment/decrement
counter++
--index
// Assignment
x = 42
9.2 Block Statements
Syntax:
Block ::= '{' Statement* '}'
Examples:
{
int x = 10
int y = 20
int z = x + y
}
9.3 If Statements
Syntax:
IfStmt ::= 'if' [ '(' ] Expression [ ')' ] Statement
( 'elsif' [ '(' ] Expression [ ')' ] Statement )*
[ 'else' Statement ]
Examples:
// Simple if
if (condition) {
// code
}
// If without parentheses
if condition {
// code
}
// If-else
if (x > 0) {
positive = true
} else {
positive = false
}
// If-elsif-else
if (a == 1) {
b = 0
} elsif (a == 2) {
b = 1
} elsif (a == 3) {
b = 2
} else {
b = -1
}
// Inline if (without braces)
if (condition) doSomething()
9.4 Switch Statements
Syntax:
SwitchStmt ::= 'switch' [ '(' ] Expression [ ')' ] '{' CaseClause* [ DefaultClause ] '}'
CaseClause ::= 'case' Expression ':' Statement*
DefaultClause ::= 'default' ':' Statement*
Examples:
switch (value) {
case 1:
// code for case 1
break
case 2:
// code for case 2
break
case 3:
case 4:
// code for case 3 and 4 (fall-through)
break
default:
// default code
}
// Without parentheses
switch value {
case 0:
result = "zero"
break
default:
result = "other"
}
9.5 Loop Statements
9.5.1 While Loop
Syntax:
WhileStmt ::= 'while' [ '(' ] Expression [ ')' ] Statement
Examples:
// While with parentheses
while (count < 10) {
count++
}
// While without parentheses
while count < 10 {
count++
}
// Infinite loop
while true {
// loop body
if (shouldBreak) break
}
// Inline while
while condition doSomething()
9.5.2 For Loop
Syntax:
ForStmt ::= 'for' VarDecl ( ',' VarDecl )* ';' Expression ';'
Expression ( ',' Expression )* Statement
Examples:
// Standard for loop
for int i = 0; i < 10; i++ {
// loop body
}
// Multiple initialization and post expressions
for int i = 0, int j = 10; i < j; i++, j-- {
// loop body
}
// For loop without parentheses
for int i = 0; i < length; i++ {
process(array[i])
}
9.6 Jump Statements
9.6.1 Return Statement
Since all functions are void, return is used only to exit the function early.
Syntax:
ReturnStmt ::= 'return'
Examples:
// Return exits the function
return
// Early return based on condition
if (done) {
return
}
9.6.2 Break Statement
Syntax:
BreakStmt ::= 'break'
Examples:
while true {
if (condition) {
break // exit loop
}
}
switch (value) {
case 1:
doSomething()
break // exit switch
}
9.6.3 Continue Statement
Syntax:
ContinueStmt ::= 'continue'
Examples:
for int i = 0; i < 10; i++ {
if (i % 2 == 0) {
continue // skip even numbers
}
process(i)
}
9.7 Error Handling Statements
Fly uses a unique error handling mechanism based on fail and handle keywords, which differs from traditional try-catch exception handling found in other languages.
Key Differences from Try-Catch:
failthrows an exception (similar tothrow)handlecatches the exception (similar totry-catch)- The
errortype is used to capture exception information - More concise syntax with implicit error propagation
9.7.1 The Error Type
The error type is a built-in type used to represent exceptions and error states.
Declaration:
error err // Declares an error variable
error myError // Error variable to capture exceptions
9.7.2 Fail Statement
The fail keyword throws an exception. You can fail with nothing, an integer, a string, or an object.
Syntax:
FailStmt ::= 'fail' [ Expression ]
Examples:
// 1. Fail without a value (void failure)
err0() {
fail // Simple failure
}
// 2. Fail with an integer error code
err1() {
fail 404 // Fail with error code
}
// 3. Fail with a string error message
err2() {
fail "Error occurred" // Fail with message
}
// 4. Fail with an expression
validateAge(const int age) {
if (age < 0) {
fail "Age cannot be negative"
}
if (age > 150) {
fail 1001 // Custom error code
}
}
Fail Statement Behavior:
- Immediately terminates the current function
- Propagates the exception to the caller
- Can carry data: nothing (void), integers, strings, or objects
- Any code after
failis unreachable
9.7.3 Handle Statement
The handle keyword catches exceptions thrown by fail. It executes a block of code and captures any failures.
Syntax:
HandleStmt ::= [ 'error' Identifier ] 'handle' ( Statement | Block )
Forms of Handle:
1. Simple Handle (No Error Capture):
main() {
// Just handle the exception, ignore details
handle err0()
// Handle a block of code
handle {
riskyOperation()
anotherRiskyCall()
}
}
2. Handle with Error Variable Declaration:
main() {
// Declare error variable and handle in one statement
error err0 handle {
riskyOperation()
}
// The error variable 'err0' is accessible after the handle block
// and contains exception information if an error occurred
if (err0) {
// Handle the error
}
}
3. Handle with Statement Block:
processData() {
error err1 handle {
dangerousOperation()
anotherRiskyCall()
}
if (err1) {
// Error occurred, err1 contains the error information
return
}
}
4. Handle with Single Statement:
quickCheck() {
error err2 handle checkData() // Handle single statement
if (err2) {
// err2 contains error information if checkData() failed
}
}
9.7.4 Complete Error Handling Examples
Example 1: Simple Void Error Handling
err0() {
fail // Throw exception
}
main() {
handle err0() // Catch exception
// Application continues and returns 0 (success)
}
Note: In main(), if you don't handle the error, the application will automatically return exit code 1 (failure). When the error is handled (as shown above), the application returns 0 (success). See Section 5.5: The Main Function for details.
Example 2: Integer Error Codes
divide(const int a, const int b) {
if (b == 0) {
fail 1001 // Error code for division by zero
}
}
calculate() {
error divErr = handle {
divide(10, 0)
}
if (divErr) {
// Handle division error
// divErr contains error code 1001
}
}
Example 3: String Error Messages
loadFile(const string path) {
if (path == "") {
fail "Invalid file path"
}
// ... load file logic
}
processFile() {
error fileErr handle {
loadFile("")
}
if (fileErr) {
// fileErr contains "Invalid file path"
}
}
Example 4: Multiple Operations in Handle Block
complexOperation() {
bool success = false
error err handle {
operation1() // May fail
operation2() // May fail
operation3() // May fail
}
if (err) {
// Any of the operations failed
// err contains the error information
} else {
// All operations succeeded
success = true
}
}
9.7.5 Error Handling Patterns
Pattern 1: Graceful Degradation
getValue() {
error err = handle {
riskyOperation()
}
if (err) {
// Use fallback logic on error
return
}
}
Pattern 2: Error Propagation
caller() {
// If handle captures an error, you can re-fail
error err = handle {
mayFail()
}
if (err) {
fail // Propagate error to caller
}
}
Pattern 3: Logging and Recovery
process() {
error err = handle {
criticalOperation()
}
if (err) {
// Log the error
log("Error occurred")
// Attempt recovery
fallbackOperation()
}
}
9.7.6 Comparison with Try-Catch
| Feature | Fly (fail/handle) | Traditional (try-catch) |
|---|---|---|
| Throw exception | fail | throw |
| Catch exception | handle | try-catch |
| Error variable | error err = handle { } | catch (Exception e) { } |
| No error value | fail | throw |
| Error with value | fail 404 or fail "msg" | throw new Exception(msg) |
| Syntax | Concise, inline | Verbose, block-based |
| Error type | Built-in error type | Exception classes |
Summary:
- fail = throw an exception (void, int, string, or object)
- handle = catch exceptions in a block
- error = type to store exception information
- More concise than traditional try-catch
- Supports multiple error payload types
10. Namespaces and Imports
10.1 Namespace Declaration
Namespaces organize code and prevent name conflicts.
Syntax:
Namespace ::= 'namespace' Identifier ( '.' Identifier )*
Examples:
// Single namespace
namespace mylib
// Nested namespace (dotted notation)
namespace my.library
namespace company.project.module
Rules:
- A namespace declaration must appear before any imports or top-level declarations
- Only one namespace declaration per file
- If no namespace is declared, a default namespace based on the filename is used
10.2 Import Declaration
Imports make symbols from other namespaces available.
Syntax:
Import ::= 'import' Identifier ( '.' Identifier )* [ 'as' Identifier ( '.' Identifier )* ]
Examples:
// Simple import
import utils
// Nested namespace import
import my.library
// Import with alias
import standard as std
import external.package as pkg
// Multiple imports
import utils
import helpers
import data.models
10.3 Using Imported Symbols
Examples:
// File: utils.fly
namespace utils
public getB(const int[] out) {
out[0] = 10
}
// File: main.fly
import utils
main() {
int[] result = {0}
utils.getB(result)
}
// With alias
import standard as std
process() {
std.doSomething()
}
11. Modifiers
11.1 Visibility Modifiers
Control the accessibility of declarations.
| Modifier | Scope | Applies To |
|---|---|---|
private | Only within the same file/class | Functions, classes, members |
protected | Within the class and derived classes | Class members |
public | Accessible from anywhere | Functions, classes, members |
| (default) | Package-private (same namespace) | Functions, classes |
Examples:
// Private function
private internalHelper() {}
// Protected member
class Base {
protected int value
}
// Public class
public class PublicAPI {
public exportedMethod() {}
}
// Default visibility
packageFunction() {}
class DefaultClass {}
11.2 Constant Modifier
The const modifier marks values as immutable.
Examples:
// Constant function parameter (const is required for all parameters)
process(const int size) {
// size cannot be modified
}
// Constant local variable
func() {
const int limit = 50
// limit = 100 // Error: cannot modify const
}
11.3 Static Modifier
The static modifier creates class-level members.
Examples:
class Counter {
static int totalCount = 0
public static getTotal(const int[] out) {
out[0] = totalCount
}
public increment() {
totalCount++
}
}
// Usage
Counter c = new Counter()
c.increment()
11.4 Combining Modifiers
Multiple modifiers can be combined.
Examples:
// In a class context
class Configuration {
// Public constant (class-level)
public const int BUFFER_SIZE = 1024
// Private static field
private static int instanceCounter = 0
// Public static constant
public static const string APP_NAME = "FlyApp"
}
// In a function
process() {
// Constant local variable
const int maxRetries = 3
}
12. Comments
12.1 Line Comments
Line comments start with // and continue to the end of the line.
Examples:
// This is a line comment
int value = 42 // End-of-line comment
// Multiple line comments
// can be used for
// multi-line documentation
12.2 Block Comments
Block comments are enclosed between /* and */.
Examples:
/* This is a block comment */
/*
* Multi-line block comment
* for detailed documentation
*/
calculate() {
/* inline comment */ return
}
Note: Block comments can span multiple lines and are preserved by the parser for documentation purposes.
13. Grammar Summary
13.1 Program Structure
Program ::= [ Namespace ] Import* TopDecl*
Namespace ::= 'namespace' Name ( '.' Name )*
Import ::= 'import' Name ( '.' Name )* [ 'as' Name ( '.' Name )* ]
TopDecl ::= Comment
| ClassDecl
| EnumDecl
| FunctionDecl
Modifiers ::= ( 'public' | 'private' | 'protected' | 'const' | 'static' )*
13.2 Type System
Type ::= BuiltinType
| NamedType
| ArrayType
BuiltinType ::= 'bool' | 'byte' | 'char'
| 'short' | 'ushort' | 'int' | 'uint'
| 'long' | 'ulong' | 'float' | 'double'
| 'string' | 'error'
NamedType ::= Name ( '.' Name )*
ArrayType ::= Type '[' [ Expression ] ']'
13.3 Declarations
ClassDecl ::= Modifiers ( 'class' | 'struct' )
Identifier [ ':' Identifier ] '{' ClassMember* '}'
InterfaceDecl ::= Modifiers 'interface'
Identifier [ ':' Identifier ] '{' InterfaceMember* '}'
EnumDecl ::= Modifiers 'enum' Identifier '{' EnumEntryList '}'
EnumEntryList ::= EnumEntry ( ',' EnumEntry )*
EnumEntry ::= Identifier
FunctionDecl ::= Modifiers Identifier '(' [ ParamList ] ')' ( Block | ';' )
ParamList ::= Param ( ',' Param )*
Param ::= 'const' Type Identifier
Inheritance Rules:
- Class: Single inheritance; can extend one struct or one interface
- Struct: Can extend only another struct
- Interface: Can extend only another interface
- Enum: Cannot extend anything
13.4 Statements
Statement ::= Block
| IfStmt
| SwitchStmt
| WhileStmt
| ForStmt
| ReturnStmt
| BreakStmt
| ContinueStmt
| FailStmt
| HandleStmt
| ExprStmt
| VarDeclStmt
| AssignStmt
Block ::= '{' Statement* '}'
IfStmt ::= 'if' [ '(' ] Expr [ ')' ] Statement
( 'elsif' [ '(' ] Expr [ ')' ] Statement )*
[ 'else' Statement ]
SwitchStmt ::= 'switch' [ '(' ] Expr [ ')' ] '{'
CaseClause* [ DefaultClause ] '}'
WhileStmt ::= 'while' [ '(' ] Expr [ ')' ] Statement
ForStmt ::= 'for' VarDecl ( ',' VarDecl )* ';' Expr ';'
Expr ( ',' Expr )* Statement
ReturnStmt ::= 'return'
BreakStmt ::= 'break'
ContinueStmt ::= 'continue'
FailStmt ::= 'fail' [ Expr ]
HandleStmt ::= [ 'error' Identifier '=' ] 'handle' ( Statement | Block )
VarDeclStmt ::= Modifiers Type Identifier [ '=' Expr ]
AssignStmt ::= Identifier AssignOp Expr
13.5 Expressions
Expression ::= TernaryExpr
TernaryExpr ::= LogicalOrExpr [ '?' Expr ':' Expr ]
LogicalOrExpr ::= LogicalAndExpr ( '||' LogicalAndExpr )*
LogicalAndExpr ::= BitwiseOrExpr ( '&&' BitwiseOrExpr )*
BitwiseOrExpr ::= BitwiseXorExpr ( '|' BitwiseXorExpr )*
BitwiseXorExpr ::= BitwiseAndExpr ( '^' BitwiseAndExpr )*
BitwiseAndExpr ::= EqualityExpr ( '&' EqualityExpr )*
EqualityExpr ::= RelationalExpr ( ( '==' | '!=' ) RelationalExpr )*
RelationalExpr ::= ShiftExpr ( ( '<' | '>' | '<=' | '>=' ) ShiftExpr )*
ShiftExpr ::= AddExpr ( ( '<<' | '>>' ) AddExpr )*
AddExpr ::= MultExpr ( ( '+' | '-' ) MultExpr )*
MultExpr ::= UnaryExpr ( ( '*' | '/' | '%' ) UnaryExpr )*
UnaryExpr ::= ( '++' | '--' | '!' | '-' | '+' ) UnaryExpr
| PostfixExpr
PostfixExpr ::= PrimaryExpr ( '++' | '--' | '(' ArgList ')'
| '[' Expr ']' | '.' Identifier )*
PrimaryExpr ::= Literal
| Identifier
| '(' Expr ')'
| 'new' Identifier '(' ArgList ')'
| ArrayValue
ArrayValue ::= '{' [ Expr ( ',' Expr )* ] '}'
AssignOp ::= '=' | '+=' | '-=' | '*=' | '/=' | '%='
| '&=' | '|=' | '^=' | '<<=' | '>>='
14. Complete Example
Here's a comprehensive example demonstrating various Fly language features:
namespace myapp
import utils
import data.models as models
// Enum declaration (enums cannot extend anything)
public enum Status {
IDLE, RUNNING, PAUSED, STOPPED
}
// Class declaration (single inheritance)
public class Application {
// Private fields
private string name
private int value
private Status currentStatus
// Static field
static int instanceCount = 0
// Public method (no return type — all functions are void)
public initialize(const string appName) {
name = appName
value = 0
currentStatus = Status.IDLE
instanceCount++
}
// Public method with error handling
public process() {
error err = handle {
calculateResult()
}
if (err) {
// Error occurred
currentStatus = Status.STOPPED
}
}
// Private helper method that may fail
private calculateResult() {
if (value < 0) {
fail "Invalid value" // Fail with string message
}
if (value > 1000) {
fail 999 // Fail with error code
}
}
// Method demonstrating void error handling
public validate() {
error validationErr = handle {
if (name == "") {
fail "Name cannot be empty"
}
}
if (validationErr) {
currentStatus = Status.STOPPED
}
}
// Setter method
public setValue(const int newValue) {
value = newValue
}
// Static method
public static incrementCount() {
instanceCount++
}
}
// Structure declaration (struct can extend only struct)
public struct Point {
int x
int y
}
// Struct extending another struct
public struct Point3D : Point {
int z
}
// Interface declaration (interface can extend only interface)
public interface Drawable {
draw()
}
// Class implementing an interface (single inheritance)
public class Shape : Drawable {
private int width
private int height
draw() {
// drawing logic
}
}
// Main entry point
// Note: main() automatically returns 0 if all errors are handled,
// or returns 1 if an unhandled error occurs
main() {
// Create application instance
Application app = new Application()
app.initialize("MyApp")
// Error handling example: validate the application
handle app.validate()
// Set status
Status status = Status.RUNNING
// Control flow with error handling
if (status == Status.RUNNING) {
error processErr = handle {
app.process()
}
if (processErr) {
// Handle error gracefully
status = Status.STOPPED
} else {
handleResult()
}
} elsif (status == Status.PAUSED) {
// Handle paused state
} else {
// Handle other states
}
// Loop through array
int[] numbers = {1, 2, 3, 4, 5}
for int i = 0; i < 5; i++ {
processNumber(numbers[i])
}
// While loop
int count = 0
while (count < 10) {
count++
}
// Switch statement
switch (count) {
case 10:
// count is 10
break
default:
// other value
}
// Create structure
Point p = new Point()
p.x = 10
p.y = 20
// Error handling with structure
error distErr = handle {
int dist = p.x * p.x + p.y * p.y
if (dist > 1000) {
fail "Distance too large"
}
}
}
// Private helper function (all parameters require const)
private handleResult() {
// handle result logic
}
// Function with const parameter
private processNumber(const int num) {
if (num % 2 == 0) {
// even number
} else {
// odd number
}
}
15. Best Practices
15.1 Naming Conventions
- Classes, Structs, Enums: Use PascalCase (e.g.,
MyClass,StatusType) - Functions, Variables: Use camelCase (e.g.,
calculateTotal,userName) - Constants: Use UPPER_SNAKE_CASE (e.g.,
MAX_SIZE,DEFAULT_VALUE) - Private members: Prefix with underscore or use clear naming (e.g.,
_internal,privateHelper)
15.2 Code Organization
- One namespace per file
- Group related functionality in the same namespace
- Use imports to reference external code
- Keep functions focused and small
15.3 Error Handling
- Use
failfor unrecoverable errors - Use
handleblocks to catch and recover from errors - Validate inputs at function boundaries
15.4 Comments
- Use line comments for brief explanations
- Use block comments for detailed documentation
- Document public APIs thoroughly
- Explain complex algorithms and business logic
Appendix A: Reserved Keywords
All keywords are reserved and cannot be used as identifiers:
as bool break byte case
char class const continue default
double elsif else enum error
fail false float for handle
if import interface int long
namespace new null private protected
public return short static string
struct switch true uint ulong
ushort while
Appendix B: Operator Precedence
From highest to lowest precedence:
- Postfix:
++,--,(),[],. - Unary:
++,--,!,-,+(prefix) - Multiplicative:
*,/,% - Additive:
+,- - Shift:
<<,>> - Relational:
<,>,<=,>= - Equality:
==,!= - Bitwise AND:
& - Bitwise XOR:
^ - Bitwise OR:
| - Logical AND:
&& - Logical OR:
|| - Ternary:
?: - Assignment:
=,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=
Appendix C: Error Handling Quick Reference
Fly uses fail and handle keywords for error handling, which differs from traditional try-catch mechanisms.
Quick Comparison
| Concept | Fly Syntax | Traditional (Java/C++) |
|---|---|---|
| Throw exception | fail | throw |
| Throw with message | fail "Error message" | throw new Exception("Error message") |
| Throw with code | fail 404 | throw 404 or custom exception |
| Catch exception | handle { ... } | try { ... } catch { ... } |
| Catch with variable | error err = handle { ... } | catch (Exception err) { ... } |
| Error type | error | Exception or custom class |
Common Patterns
// Pattern 1: Simple fail
operation() {
fail // Throw exception
}
// Pattern 2: Fail with integer code
check() {
fail 404 // Error code
}
// Pattern 3: Fail with string message
load() {
fail "File not found" // Error message
}
// Pattern 4: Simple handle
handle operation() // Catch and ignore
// Pattern 5: Handle with error capture
error err = handle {
riskyOperation()
}
if (err) {
// Handle error
}
// Pattern 6: Handle with recovery
error err = handle {
computation()
}
if (err) {
fallbackOperation()
}
Error Types
- void:
fail(no value) - integer:
fail 404,fail 500,fail -1 - string:
fail "Error message",fail "Not found" - object:
fail errorObject
Key Points
failimmediately terminates function executionhandlecatches exceptions in the enclosed blockerrortype stores exception information- Multiple operations can be wrapped in a single
handleblock - Error handling is more concise than traditional try-catch
- No exception type hierarchy needed—use simple values
main()function: Unhandled errors cause the application to return exit code 1; handled errors allow return code 0
Main Function and Exit Codes
The main() function has special error handling behavior:
main() {
// If no error occurs or all errors are handled: returns 0
// If an unhandled error occurs: returns 1
}
Examples:
// Returns 0 (success)
main() {
handle mayFail()
}
// Returns 1 (failure)
main() {
mayFail() // Error not handled
}
// Returns 0 (success) - error is caught and handled
main() {
error err = handle {
riskyOperation()
}
if (err) {
// Handle gracefully
}
}
© Fly Project - https://flylang.org
Licensed under Apache License v2.0
Documentation Version 1.0 - December 2025