Variables
- Must start with a lowercase letter or _
- Can be followed by comma as shorthand for `= nil`
nothing = nil
something = 123
empty, # equivalent to `empty = nil`
Functions
- Must start with a lowercase letter or _
- The function body is surrounded with
{} braces - The arguments are declared before the arguments/body delimiter
; - The body comes after the arguments/body delimiter
; - The last expression is the return value
- Return early using
return keyword
# func_name { [args]; [body] }
func_with_args { arg1, arg2 = 1, etc = true;
# body
}
without_args {;
# body
}
add { a, b;
a + b
}
add(4, 8) #=> 12
Classes
- Must start with an uppercase character
- Can have an initializer `new`
My_Class {
input,
new { input;
.input = input # .input is equivalent to this.input or self.input
@puts 'Initted with "`input`'"
}
}
instance = My_Class('some input') #=> Initted with "some input"
Constants
- Must be UPPERCASE
- Cannot be reassigned after initial declaration
PI = 3.14159
MAX_SIZE = 100
APP_NAME = 'My App'
- Single-line comments use
# - Multiline comments use triple backticks
# This is a single-line comment
```
This is a
multiline comment
```
String Interpolation
- Use backticks inside strings to interpolate expressions
- Escape with backslash to prevent interpolation
name = 'World'
greeting = "Hello, `name`!" #=> "Hello, World!"
math = "2 + 2 = `2 + 2`" #=> "2 + 2 = 4"
escaped = "Literal `backticks`"
Scope Operators
. accesses current instance scope only./ accesses current type/class scope only../ accesses global scope
My_Class {
./count = 0 # Type-level (static) variable
new { value;
.value = value # Instance variable (like this.value)
./count += 1 # Access static from instance
}
get_global {;
../SOME_CONSTANT # Access global scope
}
}
Static Declarations
- Use
./ to declare type-level (static) members - Shared across all instances
- Accessed on the type itself:
Type.member
Counter {
./count = 0
./increment {;
count += 1
}
new {;
./count += 1
}
}
Counter()
Counter()
Counter.count #=> 2
Type Composition
| union: merge all members from both types& intersection: keep only shared members~ removal: remove members of right type from left^ symmetric difference: keep non-shared members
Movable {
x = 0
y = 0
move { dx, dy; x += dx; y += dy }
}
Drawable {
color = 'black'
draw {; "Drawing in `color`" }
}
# Combine types
Sprite | Movable | Drawable {
name = 'sprite'
}
s = Sprite()
s.move(10, 5)
s.draw()
Conditionals
if/elif/else/endunless is the negation of if- Can be used as inline modifiers
if x > 10
'big'
elif x > 5
'medium'
else
'small'
end
unless logged_in
redirect('/login')
end
# Inline conditionals
@puts 'yes' if condition
@puts 'no' unless condition
While & Until Loops
while loops while condition is trueuntil loops until condition becomes trueelwhile chains another loop when prior condition becomes false
i = 0
while i < 5
@puts i
i += 1
end
j = 0
until j == 5
@puts j
j += 1
end
# Chained loops with elwhile
x = 0
y = 0
while x < 4
x += 1
elwhile y > -8
y -= 1
else
@puts 'done'
end
For Loops
- Iterate over arrays, ranges, or any iterable
it is the current elementat is the current index
for [1, 2, 3]
@puts it # Current element
@puts at # Current index
end
for 1..5
@puts it # 1, 2, 3, 4, 5
end
# With stride (chunks)
for [1, 2, 3, 4, 5, 6] by 2
@puts it # [1,2], [3,4], [5,6]
end
For Loop Verbs
map transforms each elementselect filters where body is truthyreject filters where body is falsycount counts where body is truthy
doubled = for [1, 2, 3] map
it * 2
end #=> [2, 4, 6]
evens = for [1, 2, 3, 4, 5] select
it % 2 == 0
end #=> [2, 4]
odds = for [1, 2, 3, 4, 5] reject
it % 2 == 0
end #=> [1, 3, 5]
even_count = for [1, 2, 3, 4, 5, 6] count
it % 2 == 0
end #=> 3
Loop Control
skip continues to next iterationstop breaks out of loopreturn exits the function (propagates through loops)
for items
skip if it.invalid # Continue to next
stop if it.last # Break out
end
find_first { predicate;
for items
return it if predicate(it)
end
nil
}
Unpack Operator
@param in function signature unpacks instance members into scope@ += and @ -= manually control sibling scopes
Vector { x = 0; y = 0 }
# Auto-unpack in parameters
magnitude { @vec;
(x ** 2 + y ** 2).sqrt() # Access x, y directly
}
v = Vector()
v.x = 3
v.y = 4
magnitude(v) #=> 5
# Manual sibling scope control
@ += some_instance # Add members to scope
@ -= some_instance # Remove from scope
Arrays
- Created with
[] brackets - Access elements with subscript or dot notation
arr = [1, 2, 3, 4, 5]
arr[0] #=> 1
arr.0 #=> 1 (dot notation)
arr.push(6) # Add to end
arr.pop() # Remove from end
arr.length() #=> 5
arr.first(2) #=> [1, 2]
arr.last(2) #=> [4, 5]
arr.reverse()
arr.include?(3) #=> true
arr.empty?() #=> false
arr.map({ x; x * 2 })
arr.filter({ x; x > 2 })
Dictionaries
- Created with
{} braces and key-value pairs - Keys can be symbols, strings, or identifiers
- Access with subscript
dict[:key]
dict = {x: 10, y: 20}
dict[:x] #=> 10
dict[:z] = 30 # Assignment
dict.keys() #=> [:x, :y, :z]
dict.values() #=> [10, 20, 30]
dict.has_key?(:x) #=> true
dict.count() #=> 3
dict.empty?() #=> false
dict.delete(:z)
dict.merge({a: 1})
dict.fetch(:missing, 'default')
Strings
s = 'Hello, World!'
s.length #=> 13
s.upcase() #=> 'HELLO, WORLD!'
s.downcase() #=> 'hello, world!'
s.split(', ') #=> ['Hello', 'World!']
s.trim() # Remove whitespace
s.chars() #=> ['H', 'e', 'l', ...]
s.reverse()
s.include?('World') #=> true
s.start_with?('He') #=> true
s.end_with?('!') #=> true
s.gsub('World', 'Ore')
s.to_i() # Convert to integer
s.empty?() #=> false
Numbers
n = 42
n.abs() # Absolute value
n.floor() # Round down
n.ceil() # Round up
n.round() # Round to nearest
n.sqrt() # Square root
n.even?() #=> true
n.odd?() #=> false
n.to_s() #=> '42'
n.clamp(0, 100) # Clamp to range
Ranges
.. inclusive range.< exclusive end>. exclusive start>< exclusive both
1..5 #=> 1, 2, 3, 4, 5 (inclusive)
1.< 5 #=> 1, 2, 3, 4 (exclusive end)
>. 1 5 #=> 2, 3, 4, 5 (exclusive start)
>< 1 5 #=> 2, 3, 4 (exclusive both)
for 1..10
@puts it
end
File I/O
@use 'ore/inout.ore'
content = Inout.read('./file.txt')
Inout.write_string_to_file('./out.txt', 'Hello!')
@use Directive
- Imports another Ore file
- Files are only loaded once
@use 'ore/string.ore'
@use 'ore/array.ore'
@use './my_module.ore'
@puts Directive
@puts 'Hello, World!'
@puts variable
@puts "Value: `expression`"
Web Server
- Compose with
Server type - Define routes with HTTP method syntax
- Start with
@start directive
@use 'ore/server.ore'
App | Server {
new {;
.port = 3000
}
get:// {;
'Hello, World!'
}
get://about {;
'About page'
}
}
@start App()
Routes
- HTTP methods:
get://, post://, put://, delete://, patch:// - URL parameters with
:param syntax - Query params via
request.query
App | Server {
# Static route
get://users {;
'All users'
}
# URL parameter
get://users/:id { id;
"User `id`"
}
# Multiple params
get://posts/:post_id/comments/:id { post_id, id;
"Comment `id` on post `post_id`"
}
# Query strings: /search?q=term
get://search {;
query = request.query[:q]
"Searching for `query`"
}
}
Request & Response
post://login {;
username = request.body[:username]
password = request.body[:password]
if authenticate(username, password)
response.redirect('/dashboard')
else
response.status = 401
'Unauthorized'
end
}
get://api/data {;
response.headers['Content-Type'] = 'application/json'
'{"status": "ok"}'
}
Database
- Use
Sqlite for SQLite databases - Connect with
@connect directive
@use 'ore/database.ore'
db = Sqlite('./data/app.db')
@connect db
db.create_table('users', {
id: 'primary_key',
name: 'String',
email: 'String'
})
db.table_exists?('users') #=> true
db.tables() #=> ['users']
db.delete_table('users')
Record ORM
- Compose with
Record type - Set static
./database and instance table_name
@use 'ore/record.ore'
User | Record {
./database = ../db
table_name = 'users'
}
# CRUD operations
User.create({name: 'Alice', email: 'alice@example.com'})
users = User.all() #=> Array of Dictionaries
user = User.find(1) #=> Dictionary
User.delete(1)
HTML Elements
- Compose with HTML element types from
ore/html.ore css_* prefix sets inline CSS propertieshtml_* prefix sets HTML attributes
@use 'ore/html.ore'
Card | Div {
css_padding = '1rem'
css_border_radius = '8px'
css_background_color = '#fff'
# html_ prefix becomes attribute
html_class = 'card' #=> id="foo"
html_data_value = 42 #=> data-value=42
html_aria_label, #=> aria-label (nil omits value)
}
Link | A {
html_href = '#'
html_target = '_blank'
}
page = Html([
Head(Title('My Page'))
Body([
H1('Welcome')
Card([
P('Hello!')
Link('Click me')
])
])
])
Operators
Arithmetic
+ - * / % # Basic math
** # Exponentiation
<< >> # Bitwise shift / Array append
Comparison
== != # Equality
=== !== # Strict equality
< <= > >= # Relational
<=> # Spaceship (three-way)
=~ !~ # Regex match
Logical
&& and # Logical AND
|| or # Logical OR
! not # Logical NOT
Assignment
= # Basic assignment
+= -= *= /= # Compound assignment
&&= ||= # Logical compound
<<= >>= # Shift compound
Nil Initialization
- Trailing comma declares variable as nil if undefined
- Returns existing value if already defined
undefined_var, #=> nil
undefined_var #=> nil
existing = 42
existing, #=> 42 (unchanged)