Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

😺 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:

  1. Installation - Set up Felico on your system
  2. Quick Start - Write your first Felico program
  3. 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:

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

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("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

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

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

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 loop
  • continue - 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

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:

  1. Primary expressions, function calls
  2. Unary operators (!, -)
  3. Multiplicative (*, /, %)
  4. Additive (+, -)
  5. Comparison (<, <=, >, >=)
  6. Equality (==, !=)
  7. Logical AND (&&)
  8. Logical OR (||)
  9. 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 matching
  • for - For loops
  • in - Iterator syntax
  • struct - Custom data types
  • enum - Algebraic data types
  • trait - Type classes/interfaces
  • impl - Trait implementations
  • type - Type aliases
  • const - Compile-time constants
  • mut - Mutable bindings
  • pub - Public visibility
  • mod - Module declarations
  • use - Import statements
  • as - Type casting/aliasing
  • async - (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:

PrecedenceOperatorsDescription
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:

  1. Developers actively choose it for fun projects
  2. Newcomers can write their first program within 5 minutes
  3. Compile-test-run cycles complete in under a second
  4. Error messages regularly help solve problems on first reading
  5. 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 code
  • cargo clippy -- -D warnings - Lint with strict warnings
  • cargo test - Run all tests with RUSTFLAGS='-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 fmt before committing
  • Ensure cargo clippy passes 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

  1. Fork the repository and create a feature branch
  2. Make your changes with clear, focused commits
  3. Ensure all tests pass and add new tests if needed
  4. Update documentation if you changed behavior
  5. 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.