😺 felico 😻
Welcome to the Felico programming language documentation!
Felico is an experimental programming language inspired by Rust and Zig, designed to explore ideas about programming language design with a focus on being Fun, Friendly, and Fast (in that order).
What is Felico?
Felico is a fun, responsive and type scripting language experiment. It aims to combine:
- Extremely fast feedback loops for development
- Friendly error messages that help you understand what went wrong
- Compile-time type checks for safety
- Light-weight effect type system for managing side effects
Core Values
1. Fun 🎉
Programming should be fun. Both using and creating the language and infrastructure should be enjoyable. Fun comes in all shapes and sizes: it may be humour, whimsy, a challenging problem to solve, or a job well done.
2. Friendly 🤝
Programming should be friendly. The language, as well as its ecosystem and community should be welcoming of new ideas, mutual understanding, and assuming good intentions. We all make mistakes, and that is ok.
A friendly environment is fun to work in, which neatly ties into the first goal.
3. Fast ⚡
Programming should be fast. Fast feedback loops are essential for a fun development environment. This holds for the actual writing of code as well as compilation, test, and execution times.
Fast feedback loops are more fun and more friendly to you as a user since they respect your time.
Design Philosophy
Felico deliberately includes some features while avoiding others:
Desired features:
- Extremely fast feedback
- Friendly error messages
- Compile time type checks
- Light-weight effect type system
Undesired features:
- Function coloring
- Implementation inheritance
Getting Started
If you’re new to Felico, we recommend starting with:
- Installation - Set up Felico on your system
- Quick Start - Write your first Felico program
- Language Guide - Learn the language step by step
Project Status
Felico is an experimental language and is under active development. Expect things to change as we explore different ideas and approaches.
Installation
Prerequisites
To build and run Felico, you’ll need:
- Rust 1.70 or later (install from rustup.rs)
- Git for cloning the repository
Building from Source
Currently, Felico is only available by building from source. Follow these steps:
1. Clone the Repository
git clone https://github.com/mwoelker/felico.git
cd felico
2. Build the Project
cargo build --release
The compiled binary will be available at target/release/felico.
3. (Optional) Add to PATH
To use felico from anywhere, add the binary to your PATH:
Linux/macOS:
export PATH="$PATH:$(pwd)/target/release"
Add this line to your ~/.bashrc or ~/.zshrc to make it permanent.
Windows:
Add C:\path\to\felico\target\release to your system PATH environment variable.
4. Verify Installation
felico --version
Development Setup
If you plan to contribute to Felico, also set up the git hooks:
./scripts/git-hooks/install-hooks.sh
This will enable pre-commit checks that run formatting, linting, and tests.
Next Steps
Now that you have Felico installed, head to the Quick Start guide to write your first program!
Quick Start
Let’s write your first Felico program!
Hello, World!
Create a new file called hello.felico:
print("Hello, World!")
Run it with:
felico run hello.felico
You should see:
Hello, World!
Basic Arithmetic
Felico supports basic arithmetic operations:
let x = 10
let y = 20
print(x + y) // 30
Variables and Types
Felico has compile-time type checking:
let name = "Felico"
let version = 1
let is_fun = true
print(name)
Functions
Define and call functions:
fn greet(name) {
print("Hello, " + name + "!")
}
greet("World")
Next Steps
- Learn more about Syntax Basics
- Explore Types in detail
- Understand Functions
Interactive REPL
(Coming soon: Interactive REPL for experimenting with Felico)
Syntax Basics
This chapter introduces the fundamental syntax of Felico.
Comments
// Single-line comment
/* Multi-line
comment */
Variables
Variables are declared with the let keyword:
let x = 42
let name = "Felico"
let is_ready = true
Literals
Numbers
let integer = 42
let negative = -10
let float = 3.14
Strings
let greeting = "Hello, World!"
let empty = ""
Booleans
let yes = true
let no = false
Expressions
Most things in Felico are expressions that return values:
let x = 5
let y = x + 10 // 15
let z = if x > 0 { 1 } else { -1 }
Statements
Print Statement
print("Hello")
print(42)
print(true)
Block Statements
Group multiple statements with curly braces:
{
let x = 5
let y = 10
print(x + y)
}
Semicolons
Felico is designed to minimize required punctuation. Semicolons are optional in most cases and used primarily for style or to separate statements on the same line.
Next Steps
- Learn about Types
- Explore Functions
- Understand Control Flow
Types
Felico has a static type system that checks types at compile time.
Basic Types
Integers
let x: int = 42
Floats
let pi: float = 3.14159
Booleans
let flag: bool = true
Strings
let message: string = "Hello"
Type Inference
Felico can infer types automatically:
let x = 42 // inferred as int
let name = "Bob" // inferred as string
let ok = true // inferred as bool
Type Annotations
You can explicitly annotate types:
let x: int = 42
let y: float = 3.14
Unit Type
Functions without a return value return the unit type ():
fn greet() {
print("Hello")
} // returns ()
Coming Soon
- Custom types (structs, enums)
- Generic types
- Type aliases
- Optional types
- Result types for error handling
Next Steps
- Learn about Functions
- Explore Control Flow
Functions
Functions are declared with the fn keyword.
Basic Functions
fn greet() {
print("Hello, World!")
}
greet()
Parameters
Functions can accept parameters:
fn greet(name) {
print("Hello, " + name + "!")
}
greet("Alice")
Return Values
Functions return the last expression:
fn add(x, y) {
x + y
}
let result = add(5, 3) // 8
Explicit return:
fn max(x, y) {
if x > y {
return x
}
return y
}
Type Annotations
Specify parameter and return types:
fn add(x: int, y: int) -> int {
x + y
}
First-Class Functions
Functions are first-class values:
fn apply(f, x) {
f(x)
}
fn double(x) {
x * 2
}
let result = apply(double, 5) // 10
Closures
(Coming soon: Closures that capture variables from their environment)
Next Steps
- Learn about Control Flow
- Explore the Language Reference
Control Flow
Felico provides several constructs for controlling program flow.
Conditional Expressions
If Expressions
let x = 10
if x > 5 {
print("x is greater than 5")
}
If-Else
let x = 3
if x > 5 {
print("x is greater than 5")
} else {
print("x is 5 or less")
}
If-Else If-Else
let score = 85
if score >= 90 {
print("A")
} else if score >= 80 {
print("B")
} else if score >= 70 {
print("C")
} else {
print("F")
}
If as Expression
if is an expression and returns a value:
let x = 10
let message = if x > 5 { "big" } else { "small" }
print(message) // "big"
Loops
While Loops
let i = 0
while i < 5 {
print(i)
i = i + 1
}
For Loops
(Coming soon: For loops with ranges and iterators)
// Future syntax
for i in 0..5 {
print(i)
}
Loop Control
break- Exit the loopcontinue- Skip to next iteration
let i = 0
while i < 10 {
i = i + 1
if i == 5 {
continue
}
if i == 8 {
break
}
print(i)
}
Pattern Matching
(Coming soon: Match expressions for pattern matching)
// Future syntax
match value {
0 => print("zero"),
1 => print("one"),
_ => print("other"),
}
Next Steps
- Explore the Language Reference
- Read about Design Philosophy
Grammar
This page describes the formal grammar of Felico.
Notation
The grammar is described using EBNF-like notation:
?means optional (zero or one)*means zero or more repetitions+means one or more repetitions|means alternation (or)()groups items
Lexical Structure
Keywords
if else fn let return while break continue true false
Operators
+ - * / % == != < <= > >= && || !
Delimiters
( ) { } [ ] , ; :
Syntax Grammar
Program
program = statement*
Statements
statement = let_statement
| expression_statement
| block_statement
let_statement = "let" IDENTIFIER (":" type)? "=" expression
expression_statement = expression ";"?
block_statement = "{" statement* "}"
Expressions
expression = assignment_expr
assignment_expr = logical_or_expr
| IDENTIFIER "=" assignment_expr
logical_or_expr = logical_and_expr ("||" logical_and_expr)*
logical_and_expr = equality_expr ("&&" equality_expr)*
equality_expr = comparison_expr (("==" | "!=") comparison_expr)*
comparison_expr = additive_expr (("<" | "<=" | ">" | ">=") additive_expr)*
additive_expr = multiplicative_expr (("+" | "-") multiplicative_expr)*
multiplicative_expr = unary_expr (("*" | "/" | "%") unary_expr)*
unary_expr = ("!" | "-") unary_expr
| call_expr
call_expr = primary_expr ("(" argument_list? ")")*
primary_expr = IDENTIFIER
| literal
| "(" expression ")"
| if_expr
| block_expr
| function_expr
if_expr = "if" expression block_expr ("else" (if_expr | block_expr))?
block_expr = "{" statement* expression? "}"
function_expr = "fn" "(" parameter_list? ")" ("->" type)? block_expr
Literals
literal = INTEGER
| FLOAT
| STRING
| BOOLEAN
BOOLEAN = "true" | "false"
INTEGER = [0-9]+
FLOAT = [0-9]+ "." [0-9]+
STRING = '"' ([^"\\] | escape_sequence)* '"'
Types
type = "int" | "float" | "bool" | "string" | "()"
Tokens
Identifiers
IDENTIFIER = [a-zA-Z_][a-zA-Z0-9_]*
Comments
line_comment = "//" [^\n]*
block_comment = "/*" ([^*] | "*" [^/])* "*/"
Operator Precedence
From highest to lowest:
- Primary expressions, function calls
- Unary operators (
!,-) - Multiplicative (
*,/,%) - Additive (
+,-) - Comparison (
<,<=,>,>=) - Equality (
==,!=) - Logical AND (
&&) - Logical OR (
||) - Assignment (
=)
Notes
- Semicolons are optional after expressions
- Whitespace is generally insignificant
- Comments are treated as whitespace
- The grammar is subject to change as Felico evolves
Keywords
This page lists all keywords in Felico and their usage.
Control Flow
if
Conditional expression.
if condition {
// code
}
else
Alternative branch for if.
if condition {
// code
} else {
// alternative
}
while
Loop while condition is true.
while condition {
// code
}
break
Exit from a loop early.
while true {
if done {
break
}
}
continue
Skip to next loop iteration.
while i < 10 {
i = i + 1
if i % 2 == 0 {
continue
}
print(i)
}
return
Return from a function.
fn max(x, y) {
if x > y {
return x
}
return y
}
Declarations
let
Variable declaration.
let x = 42
let name: string = "Felico"
fn
Function declaration.
fn greet(name) {
print("Hello, " + name)
}
Literals
true
Boolean true value.
let flag = true
false
Boolean false value.
let flag = false
Reserved for Future Use
The following keywords are reserved for potential future features:
match- Pattern matchingfor- For loopsin- Iterator syntaxstruct- Custom data typesenum- Algebraic data typestrait- Type classes/interfacesimpl- Trait implementationstype- Type aliasesconst- Compile-time constantsmut- Mutable bindingspub- Public visibilitymod- Module declarationsuse- Import statementsas- Type casting/aliasingasync- (Maybe not, due to function coloring concerns)await- (Maybe not, due to function coloring concerns)
Contextual Keywords
Some identifiers have special meaning only in certain contexts. These are not strictly reserved and may be used as variable names:
self- Receiver in method syntax (planned)super- Parent module reference (planned)
Operators
This page describes all operators available in Felico.
Arithmetic Operators
+ Addition
let x = 5 + 3 // 8
- Subtraction
let x = 10 - 3 // 7
* Multiplication
let x = 4 * 5 // 20
/ Division
let x = 15 / 3 // 5
% Modulo (Remainder)
let x = 10 % 3 // 1
- Unary Negation
let x = -5 // -5
Comparison Operators
== Equal
let result = 5 == 5 // true
!= Not Equal
let result = 5 != 3 // true
< Less Than
let result = 3 < 5 // true
<= Less Than or Equal
let result = 5 <= 5 // true
> Greater Than
let result = 10 > 5 // true
>= Greater Than or Equal
let result = 5 >= 5 // true
Logical Operators
&& Logical AND
let result = true && false // false
Short-circuits: the right side is not evaluated if the left side is false.
|| Logical OR
let result = true || false // true
Short-circuits: the right side is not evaluated if the left side is true.
! Logical NOT
let result = !true // false
Assignment Operators
= Assignment
let x = 42
x = 10
Operator Precedence
From highest to lowest precedence:
| Precedence | Operators | Description |
|---|---|---|
| 1 | () | Function call, grouping |
| 2 | !, - | Unary NOT, negation |
| 3 | *, /, % | Multiplication, division, modulo |
| 4 | +, - | Addition, subtraction |
| 5 | <, <=, >, >= | Comparison |
| 6 | ==, != | Equality |
| 7 | && | Logical AND |
| 8 | ` | |
| 9 | = | Assignment |
String Concatenation
The + operator works with strings:
let greeting = "Hello, " + "World!" // "Hello, World!"
Future Operators
Planned operators for future versions:
+=,-=,*=,/=- Compound assignment++,--- Increment, decrement (maybe)?.- Optional chaining??- Null coalescing|>- Pipeline operator (maybe)..,..=- Range operators
Operator Overloading
(Future feature: Custom types will be able to define operator behavior)
Design Philosophy
The design of Felico is guided by three core principles, in order of priority:
1. Fun First
Programming should be fun. This might seem obvious, but it’s often overlooked in language design. Felico takes this seriously:
- Whimsical elements: The name “Felico” (combining “feline” and “code”) and the cat emoji mascots 😺 😻 remind us not to take ourselves too seriously.
- Rapid experimentation: Fast feedback loops make trying new ideas enjoyable.
- Clear error messages: Debugging shouldn’t be frustrating—it should feel like solving a puzzle with helpful hints.
Fun doesn’t mean sacrificing power or correctness. It means making the journey of writing code enjoyable.
2. Friendly by Design
A programming language isn’t just syntax and semantics—it’s an ecosystem and community:
- Welcoming error messages: Errors should teach, not scold.
- Inclusive community: All skill levels welcome, all ideas considered.
- Assuming good intentions: We all make mistakes, and that’s how we learn.
A friendly environment naturally leads to more fun, creating a virtuous cycle.
3. Fast When It Matters
Speed is important, but it’s the third priority for good reason:
- Fast feedback loops: Quick compilation and testing cycles respect your time.
- Responsive tooling: IDE integration, formatters, and linters should be instant.
- Runtime performance: Efficient execution, but not at the expense of fun or friendliness.
Fast feedback makes programming more fun and more friendly by reducing friction.
Influences
Felico draws inspiration from:
- Rust: Memory safety, ownership concepts, friendly error messages
- Zig: Simplicity, compile-time execution, explicit control
- TypeScript: Gradual typing, developer experience
- Elm: Pure functions, helpful errors, no runtime exceptions
What Felico Avoids
Some common language features are intentionally excluded:
Function Coloring
Async/await often creates “colored functions” where async and sync functions can’t easily interoperate. Felico aims to handle effects without splitting the world into two incompatible halves.
Implementation Inheritance
Classical OOP inheritance often leads to rigid hierarchies and tight coupling. Felico favors composition and traits over inheritance.
Experimental Nature
Felico is explicitly an experiment. This means:
- Breaking changes are expected: We’re exploring, not maintaining compatibility.
- Ideas are tested: Some will work, some won’t—that’s the point.
- Community input matters: Your feedback shapes the direction.
The goal isn’t to create the “perfect” language, but to learn what works and what doesn’t in service of our three core values: Fun, Friendly, and Fast.
Goals
Felico has clear goals that guide its development, prioritized in order:
1. Fun
Programming should be fun.
Both using and creating the language and infrastructure should be fun. Fun comes in all shapes and sizes: it may be humour, whimsy, a challenging problem to solve or a job well done.
How Felico achieves this:
- Quick feedback loops for rapid iteration
- Playful language design with cat mascots 😺 😻
- Enjoyable error messages that don’t feel like punishment
- Simple, clear syntax that doesn’t get in your way
2. Friendly
Programming should be friendly.
The language, as well as its ecosystem and community should be friendly. This means welcoming of new ideas, mutual understanding and assuming good intentions. We all make mistakes, and that is ok.
A friendly environment is fun to work in, which neatly ties into the first goal.
How Felico achieves this:
- Helpful, educational error messages
- Clear documentation with examples
- Welcoming community guidelines
- Gentle learning curve
- Forgiving syntax that reads naturally
3. Fast
Programming should be fast.
Fast feedback loops are essential for a fun development environment. This holds for the actual writing of code as well as compilation, test and execution times.
Fast feedback loops are more fun, and more friendly to me as a user since they respect my time.
How Felico achieves this:
- Extremely fast compilation times
- Quick test execution
- Responsive IDE integration
- Incremental compilation
- Minimal startup overhead
Feature Goals
Desired Features
- Extremely fast feedback: Sub-second compile times even for large projects
- Friendly error messages: Clear, actionable, educational diagnostics
- Compile time type checks: Catch bugs early with static typing
- Light-weight effect type system: Track side effects without function coloring
Undesired Features
- Function coloring: No async/await split that creates incompatible function types
- Implementation inheritance: Avoid complex class hierarchies; prefer composition
Success Metrics
Felico will be considered successful when:
- Developers actively choose it for fun projects
- Newcomers can write their first program within 5 minutes
- Compile-test-run cycles complete in under a second
- Error messages regularly help solve problems on first reading
- The community remains welcoming and supportive
Future Aspirations
As the language matures, we hope to add:
- Standard library with common utilities
- Package manager for sharing code
- Language server for IDE integration
- Online playground for trying Felico in the browser
- Rich ecosystem of community libraries
But these will only be pursued if they align with our core values of Fun, Friendly, and Fast.
Contributing
Thank you for your interest in contributing to Felico! This guide will help you get started.
Getting Started
1. Set Up Development Environment
First, clone and set up the repository:
git clone https://github.com/mwoelker/felico.git
cd felico
cargo build
2. Install Git Hooks
Install the pre-commit hooks to ensure code quality:
./scripts/git-hooks/install-hooks.sh
The pre-commit hook runs:
cargo fmt- Format codecargo clippy -- -D warnings- Lint with strict warningscargo test- Run all tests withRUSTFLAGS='-D warnings'
Building and Testing
Build Commands
cargo build # Build project (dev profile)
cargo build --release # Build with optimizations
cargo test # Run all tests
cargo fmt # Format code
cargo clippy # Lint (use: cargo clippy -- -D warnings)
Run a Single Test
cargo test test_name
Build Profiles
- dev: Fast compilation (opt-level=0), dependencies optimized (opt-level=3)
- release: Optimized for size (opt-level=“z”), LTO enabled, symbols stripped
Making Changes
Commit Message Format
Felico uses Conventional Commits. Format: type(scope): description
Valid types: wip, build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test
Examples:
feat(parser): add support for while loops
fix(interpreter): resolve variable scope issue
docs(readme): update installation instructions
test(lexer): add tests for string escaping
Code Style
- Follow Rust idioms and conventions
- Run
cargo fmtbefore committing - Ensure
cargo clippypasses with no warnings - Add tests for new functionality
- Update documentation as needed
Project Structure
felico/
├── crates/
│ ├── felico_interpreter/ # Tree-walking interpreter
│ └── ... # Other crates
├── docs/ # This documentation (mdBook)
├── scripts/ # Build and development scripts
└── examples/ # Example programs
Areas to Contribute
Code
- Parser: Improve syntax support
- Interpreter: Add new features or optimize performance
- Type system: Enhance type checking
- Error messages: Make errors more helpful
- Testing: Add test coverage
Documentation
- Fix typos or unclear explanations
- Add examples and tutorials
- Improve API documentation
- Translate documentation
Community
- Answer questions in issues
- Help review pull requests
- Share your Felico projects
- Report bugs with clear reproduction steps
Pull Request Process
- Fork the repository and create a feature branch
- Make your changes with clear, focused commits
- Ensure all tests pass and add new tests if needed
- Update documentation if you changed behavior
- Submit a pull request with a clear description
Community Guidelines
Remember Felico’s core values:
- Fun: Keep contributions enjoyable and creative
- Friendly: Be respectful and assume good intentions
- Fast: Respect everyone’s time with clear communication
We welcome contributors of all experience levels. Don’t hesitate to ask questions!
Getting Help
- Issues: Open an issue on GitHub
- Discussions: Use GitHub Discussions for questions
- Documentation: Check this documentation site
License
By contributing, you agree that your contributions will be licensed under the same license as the project.