Skip to content

Examples

Hello World

name = "World"
puts "Hello, #{name}!"
puts "Welcome to Frankie — stitched together from the finest languages."

Fizzbuzz

for i in 1..100
  if i % 15 == 0
    puts "FizzBuzz"
  elsif i % 3 == 0
    puts "Fizz"
  elsif i % 5 == 0
    puts "Buzz"
  else
    puts i
  end
end

Fibonacci

def fibonacci(n)
  if n <= 1
    return n
  end
  return fibonacci(n - 1) + fibonacci(n - 2)
end

for i in 0..10
  puts "fib(#{i}) = #{fibonacci(i)}"
end

Stats

data = [23, 45, 12, 67, 34, 89, 15, 56, 78, 42]

puts "Mean   : #{mean(data)}"
puts "StdDev : #{stdev(data)}"
puts "Median : #{median(data)}"

# Pipe operator
data |> sum |> puts

# Vectorized operations
v = [1, 2, 3, 4, 5]
puts v * 2          # [2, 4, 6, 8, 10]

Sorting

def bubble_sort(arr)
  n = length(arr)
  i = 0
  while i < n - 1
    j = 0
    while j < n - i - 1
      if arr[j] > arr[j + 1]
        temp = arr[j]
        arr[j] = arr[j + 1]
        arr[j + 1] = temp
      end
      j = j + 1
    end
    i = i + 1
  end
  return arr
end

def selection_sort(arr)
  n = length(arr)
  i = 0
  while i < n
    min_idx = i
    j = i + 1
    while j < n
      if arr[j] < arr[min_idx]
        min_idx = j
      end
      j = j + 1
    end
    temp = arr[i]
    arr[i] = arr[min_idx]
    arr[min_idx] = temp
    i = i + 1
  end
  return arr
end

puts "=== Frankie Sorting Algorithms ==="
puts ""

data1 = [64, 34, 25, 12, 22, 11, 90]
data2 = [29, 10, 14, 37, 13, 0, 99, 55]

puts "Original (1): #{data1}"
sorted1 = bubble_sort(data1)
puts "Bubble Sort : #{sorted1}"

puts ""

puts "Original (2): #{data2}"
sorted2 = selection_sort(data2)
puts "Select Sort : #{sorted2}"

puts ""
puts "=== Using built-in sort ==="
v = [5, 3, 8, 1, 9, 2, 7, 4, 6]
puts "Unsorted: #{v}"
puts "Sorted  : #{v.sort}"

Leds

leds = {
  0: [' _  ', '| | ', '|_| '],
  1: ['  ',   '| ',   '| '  ],
  2: [' _  ', ' _| ', '|_  '],
  3: ['_  ',  '_| ',  '_| ' ],
  4: ['    ', '|_| ', '  | '],
  5: [' _  ', '|_  ', ' _| '],
  6: [' _  ', '|_  ', '|_| '],
  7: ['_   ', ' |  ', ' |  '],
  8: [' _  ', '|_| ', '|_| '],
  9: [' _  ', '|_| ', ' _| ']
}

print "Enter a number: "
number = input("")

for i in 0...3
  for j in 0...length(number)
    print leds[to_int(number[j])][i]
  end
  puts ""
end

Database

puts "╔══════════════════════════════════════════╗"
puts "║     Frankie Database Demo (SQLite)       ║"
puts "╚══════════════════════════════════════════╝"
puts ""

# ── 1. Open an in-memory database ────────────────────────────────
db = db_open(":memory:")
puts "── 1. Opened in-memory database ──"
puts db
puts ""

# ── 2. Create tables ─────────────────────────────────────────────
puts "── 2. Create Tables ──"
db.exec("CREATE TABLE departments (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE)")

db.exec("CREATE TABLE employees (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL, department TEXT NOT NULL, salary REAL NOT NULL)")

puts "Tables created: #{db.tables}"
puts ""

# ── 3. Insert rows ───────────────────────────────────────────────
puts "── 3. Insert Rows ──"

# Low-level exec with params (? placeholders — safe from SQL injection)
db.exec("INSERT INTO departments (name) VALUES (?)", ["Engineering"])
db.exec("INSERT INTO departments (name) VALUES (?)", ["Marketing"])
db.exec("INSERT INTO departments (name) VALUES (?)", ["Finance"])

# Convenience insert with hash
db.insert("employees", {name: "Alice",   age: 32, department: "Engineering", salary: 95000})
db.insert("employees", {name: "Bob",     age: 28, department: "Engineering", salary: 87000})
db.insert("employees", {name: "Carol",   age: 35, department: "Marketing",   salary: 72000})
db.insert("employees", {name: "Dave",    age: 41, department: "Finance",     salary: 105000})
db.insert("employees", {name: "Eve",     age: 29, department: "Engineering", salary: 91000})
db.insert("employees", {name: "Frank",   age: 38, department: "Marketing",   salary: 68000})
db.insert("employees", {name: "Grace",   age: 45, department: "Finance",     salary: 118000})
db.insert("employees", {name: "Hector",  age: 27, department: "Engineering", salary: 82000})

puts "Inserted #{db.count("employees")} employees"
puts "Inserted #{db.count("departments")} departments"
puts ""

# ── 4. Query all rows ────────────────────────────────────────────
puts "── 4. All Employees ──"
all = db.find_all("employees")
all.each do |row|
  puts "  [#{row["id"]}] #{row["name"]} | #{row["department"]} | age #{row["age"]} | $#{row["salary"]}"
end
puts ""

# ── 5. Filtered queries ──────────────────────────────────────────
puts "── 5. Engineering Team ──"
engineers = db.find("employees", {department: "Engineering"})
engineers.each do |e|
  puts "  #{e["name"]} (age #{e["age"]}) — $#{e["salary"]}"
end
puts ""

# ── 6. Raw SQL queries ───────────────────────────────────────────
puts "── 6. Employees Over 35 ──"
seniors = db.query("SELECT name, age, salary FROM employees WHERE age > ? ORDER BY age", [35])
seniors.each do |row|
  puts "  #{row["name"]} | age #{row["age"]} | $#{row["salary"]}"
end
puts ""

# ── 7. Aggregation ──────────────────────────────────────────────
puts "── 7. Department Summary ──"
summary = db.query("SELECT department, COUNT(*) AS headcount, AVG(salary) AS avg_salary, MAX(salary) AS top_salary FROM employees GROUP BY department ORDER BY avg_salary DESC")
summary.each do |row|
  avg = to_int(row["avg_salary"])
  puts "  #{row["department"]}: #{row["headcount"]} people | avg $#{avg} | top $#{row["top_salary"]}"
end
puts ""

# ── 8. find_one ──────────────────────────────────────────────────
puts "── 8. Find One ──"
alice = db.find_one("employees", {name: "Alice"})
puts "Found: #{alice["name"]} in #{alice["department"]}"
missing = db.find_one("employees", {name: "Nobody"})
puts "Missing: #{missing}"
puts ""

# ── 9. Update ────────────────────────────────────────────────────
puts "── 9. Update ──"
updated = db.update("employees", {salary: 100000}, {name: "Bob"})
puts "Updated #{updated} row(s)"
bob = db.find_one("employees", {name: "Bob"})
puts "Bob's new salary: $#{bob["salary"]}"
puts ""

# ── 10. Transaction ──────────────────────────────────────────────
puts "── 10. Transaction ──"
db.transaction do
  db.insert("employees", {name: "Ivan",  age: 31, department: "Finance",     salary: 97000})
  db.insert("employees", {name: "Julia", age: 26, department: "Engineering", salary: 79000})
end
puts "After transaction: #{db.count("employees")} employees"
puts ""

# ── 11. Transaction rollback on error ────────────────────────────
puts "── 11. Transaction Rollback ──"
count_before = db.count("employees")
begin
  db.transaction do
    db.insert("employees", {name: "Karl", age: 33, department: "Marketing", salary: 71000})
    raise "simulated error mid-transaction"
    db.insert("employees", {name: "Lena", age: 29, department: "Marketing", salary: 66000})
  end
rescue e
  puts "Rolled back: #{e}"
end
puts "Count unchanged: #{db.count("employees") == count_before}"
puts ""

# ── 12. Delete ───────────────────────────────────────────────────
puts "── 12. Delete ──"
deleted = db.delete("employees", {name: "Hector"})
puts "Deleted #{deleted} row(s)"
puts "Remaining: #{db.count("employees")} employees"
puts ""

# ── 13. Schema introspection ─────────────────────────────────────
puts "── 13. Schema Introspection ──"
puts "Tables: #{db.tables}"
cols = db.columns("employees")
puts "employees columns:"
cols.each do |col|
  puts "  #{col["name"]} (#{col["type"]})"
end
puts ""

# ── 14. Frankie-style stats on query results ─────────────────────
puts "── 14. Stats on Query Results ──"
salaries = db.query("SELECT salary FROM employees")
salary_list = salaries.map do |row|
  row["salary"]
end
puts "Salaries   : #{salary_list}"
puts "Mean salary: $#{to_int(mean(salary_list))}"
puts "Max salary : $#{max(salary_list)}"
puts "Min salary : $#{min(salary_list)}"
puts "Std dev    : $#{to_int(stdev(salary_list))}"
puts ""

# ── 15. Persistent file database ─────────────────────────────────
puts "── 15. File Database ──"
db2 = db_open("/tmp/frankie_demo.db")
db2.exec("CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, content TEXT)")
db2.insert("notes", {content: "Frankie has SQLite support!"})
db2.insert("notes", {content: "Zero external dependencies."})
notes = db2.find_all("notes")
notes.each do |n|
  puts "  Note #{n["id"]}: #{n["content"]}"
end
db2.close
file_delete("/tmp/frankie_demo.db")
puts "File DB created, used, and cleaned up."
puts ""

# ── Done ─────────────────────────────────────────────────────────
db.close
puts "Database connection closed."
puts "Frankie + SQLite — alive and querying! 🧟🗄️"

Hashmaps

puts "╔══════════════════════════════════╗"
puts "║     Frankie Hashmap Demo         ║"
puts "╚══════════════════════════════════╝"
puts ""

# ─── 1. Creating a Hash ───────────────────────────────────────────────────────
puts "── 1. Creating a Hash ──"
person = {name: "Alice", age: 30, city: "Lima"}
puts person
puts ""

# ─── 2. Reading Values ────────────────────────────────────────────────────────
puts "── 2. Reading Values ──"
puts "Name : #{person[:name]}"
puts "Age  : #{person[:age]}"
puts "City : #{person[:city]}"
puts ""

# ─── 3. Adding & Updating Keys ───────────────────────────────────────────────
puts "── 3. Adding & Updating ──"
person[:job] = "Engineer"
person[:age] = 31
puts "After update: #{person}"
puts ""

# ─── 4. Keys & Values ────────────────────────────────────────────────────────
puts "── 4. Keys & Values ──"
puts "Keys   : #{person.keys}"
puts "Values : #{person.values}"
puts "Size   : #{person.size}"
puts ""

# ─── 5. has_key? ─────────────────────────────────────────────────────────────
puts "── 5. Checking Keys ──"
puts "Has :name?   #{person.has_key?(:name)}"
puts "Has :email?  #{person.has_key?(:email)}"
puts ""

# ─── 6. delete ───────────────────────────────────────────────────────────────
puts "── 6. Deleting a Key ──"
person.delete(:city)
puts "After deleting :city: #{person}"
puts ""

# ─── 7. fetch with default ───────────────────────────────────────────────────
puts "── 7. Fetch with Default ──"
job    = person.fetch(:job, "Unknown")
salary = person.fetch(:salary, 0)
puts "Job    : #{job}"
puts "Salary : #{salary}"
puts ""

# ─── 8. Iterating with each ──────────────────────────────────────────────────
puts "── 8. Iterating (each) ──"
person.each do |key, val|
  puts "  #{key} => #{val}"
end
puts ""

# ─── 9. merge ────────────────────────────────────────────────────────────────
puts "── 9. Merging Hashes ──"
extra = {email: "alice@example.com", country: "Peru"}
full  = person.merge(extra)
puts "Merged: #{full}"
puts ""

# ─── 10. Nested Hashes ───────────────────────────────────────────────────────
puts "── 10. Nested Hashes ──"
company = {
  name: "Frankieworks",
  address: {street: "Av. Larco", city: "Miraflores"},
  employees: 42
}
puts "Company : #{company[:name]}"
puts "Street  : #{company[:address][:street]}"
puts "City    : #{company[:address][:city]}"
puts ""

# ─── 11. Hash of Vectors ─────────────────────────────────────────────────────
puts "── 11. Hash of Vectors ──"
scores = {
  alice: [90, 85, 92],
  bob:   [78, 80, 88],
  carol: [95, 99, 97]
}

scores.each do |student, s|
  avg = mean(s)
  puts "  #{student}: avg = #{avg}"
end
puts ""

# ─── 12. Building a Hash dynamically ─────────────────────────────────────────
puts "── 12. Building Dynamically ──"
word_count = {}
words = ["frankie", "is", "fun", "frankie", "is", "fast", "frankie"]

for word in words
  if word_count.has_key?(word)
    word_count[word] = word_count[word] + 1
  else
    word_count[word] = 1
  end
end

puts "Word counts:"
word_count.each do |word, count|
  puts "  #{word}: #{count}"
end
puts ""

puts "Done! Frankie hashmaps are alive 🧟"

Webapp

# Run with:   frankiec run examples/webapp.fk
# Then visit: http://localhost:3000

app = web_app()

# ── Simple HTML response ──────────────────────────────────────────────────────
app.get("/") do |req|
  html_response("<h1>Hello from Frankie! 🧟</h1><p>Try <a href='/greet/world'>/greet/world</a></p>")
end

# ── Path parameters ───────────────────────────────────────────────────────────
app.get("/greet/:name") do |req|
  name = req.params["name"]
  response("Hello, #{name}!")
end

# ── Query parameters ──────────────────────────────────────────────────────────
# GET /search?q=frankie
app.get("/search") do |req|
  q = req.query["q"]
  if q == nil
    return response("Please provide ?q=something")
  end
  response("You searched for: #{q}")
end

# ── JSON response ─────────────────────────────────────────────────────────────
app.get("/api/status") do |req|
  json_response({status: "ok", language: "Frankie", version: "1.4"})
end

# ── In-memory notes store ─────────────────────────────────────────────────────
# Use a wrapper hash so handlers can mutate the list without scoping issues
store = {notes: []}

app.get("/notes") do |req|
  json_response(store["notes"])
end

app.post("/notes") do |req|
  data = req.json
  if data == nil
    return halt(400, "Expected JSON body")
  end
  note = {id: length(store["notes"]) + 1, text: data["text"]}
  store["notes"].push(note)
  json_response(note, 201)
end

app.get("/notes/:id") do |req|
  id = to_int(req.params["id"])
  found = nil
  store["notes"].each do |n|
    if n["id"] == id
      found = n
    end
  end
  if found == nil
    return halt(404, "Note not found")
  end
  json_response(found)
end

app.delete("/notes/:id") do |req|
  id = to_int(req.params["id"])
  store["notes"] = store["notes"].select do |n|
    n["id"] != id
  end
  response("Deleted")
end

# ── Custom 404 ────────────────────────────────────────────────────────────────
app.not_found do |req|
  html_response("<h1>404</h1><p>No route for #{req.path}</p>", 404)
end

# ── Start ─────────────────────────────────────────────────────────────────────
app.run(3000)