Holo is a scripting language inspired by Ruby, Lua and C#. It aims for:
- Simplicity (keep it simple)
- Expressiveness (code your way)
- Embeddability (with .NET)
sub fizzbuzz(n) do
for i in 1 to n do
if i % 3 == 0 and i % 5 == 0 do
log "FizzBuzz"
elseif i % 3 == 0 do
log "Fizz"
elseif i % 5 == 0 do
log "Buzz"
else do
log i
end
end
end
sub fibonacci(n)
if n in [0, 1] do return n end
return fibonacci(n - 1) + fibonacci(n - 2)
end
log fibonacci(10)
Holo uses "objects" which serve as templates and instances. Objects contain methods and variables, which are both public by default. There are no value types.
Code is separated into statements and expressions. Newlines are significant but spaces/tabs are ignored. Assignment is not a valid expression.
Variables are declared with var
and an optional type and value.
var cats:Int = 3
cats = 4
The :=
operator infers the type by calling class()
on the value.
var cats := 3
Variables default to null
.
An exception is thrown if a non-nullable variable is used before being assigned or is assigned null
.
Types can be made nullable with ?
.
var count:Int
log(count) # throws error
count = null # throws error
Variable identifiers can contain symbols if wrapped in backticks.
var `~#:@!` := 5
Code is organised into scopes which begin with do
and end with end
.
if true do
log("hi")
end
You can omit do
if there's a keyword and a newline.
while true
log("hi")
end
Holo has two number types: integers and decimals. Both are signed and arbitrarily large and precise (like Ruby).
var int := 3
var dec := 6.2
Strings are immutable and can span multiple lines. Double-quotes parse escape sequences whereas single-quotes ignore them (like Ruby).
var string := "formatted"
var string2 := 'not formatted'
Triple quotes trim the whitespace to the left of the closing quotes.
var string := """
formatted
"""
var string2 := '''
not formatted
'''
Escape sequences start with a backslash. Any unrecognised escape sequence is a syntax error.
'single quote: \''
"double quote: \""
"interpolation: \{expression}"
"backslash: \\"
"newline: \n"
"carriage return: \r"
"tab: \t"
"backspace: \b"
"alert: \a"
"binary: \0bInteger"
"hexadecimal: \0xInteger"
Ranges can be created with min to max (step value)
.
They are inclusive as Holo uses one-based indexing.
var numbers:Range = 1 to 10
var odd_numbers:Range = 1 to 10 step 2
Objects are created with curly brackets. Rather than inheriting from a base type, they include multiple components.
Both methods and variables are public by default. Methods have higher priority in dot notation, whereas variables have higher priority when named.
var Cat := {
include animal
include pathfinding
sub meow()
log("nya")
end
}
To instantiate an object, call the new
method which clones the object and calls the init
method.
var Cat := {
var name:Str
sub init(name:Str)
self.name = name
end
}
var tama := Cat.new("Tama")
Methods run under a target accessible with self
.
Methods are called by name. Brackets are optional.
cat.meow()
cat.meow
cat.eval(sub()
meow
end)
Methods are declared with sub name()
. Brackets are mandatory.
sub meow()
log("nya")
end
meow
Anonymous methods (stored in method objects) are declared with sub()
. Brackets are mandatory.
var meow = sub()
log("nya")
end
meow.call
Multiple overloads for a method are allowed.
sub say(message:String)
log(message)
end
sub say(message:Int)
log(message)
end
method("say").call # Call the best overload
method("say").overloads.first.call # Call the first overload
Excess arguments can be caught with the excess operator.
There can only be one excess operator, but it can be in any order (e.g. a, ..b, c
).
sub say(..things)
for thing in things
log(thing)
end
end
var one, ..two_three := [1, 2, 3]
Assignments on an object are translated to method calls (similar to Ruby).
var Cat := {
sub set_name(name:Str)
# ...
end
}
Cat.name = "Robbie"
Missing methods can be caught with missing
and set_missing
methods.
var Cat := {
sub missing(method)
# ...
end
sub set_missing(method, value)
# ...
end
}
Cat.length
Cat.length = 5
If a method is redefined, the original method can be called with the origin
method.
sub say(message)
log(message)
end
sub say(message)
log("I was here")
origin
end
Arguments can be passed by name.
Cat.say(message = "meow")
Extension methods are methods that refer to another object. When calling a method, extension methods take priority over normal methods.
var Extensions := {
sub Cat.say(message)
# ...
end
}
include Extensions
Cat.say("nyan")
Type annotations are treated as methods, which are called every time they are used.
This allows you to pass them as variables:
sub increment(value:Num):value
var big_value:value = value + 1
return big_value
end
The (of ..types)
operator (taken from Visual Basic) can be used on objects. If not overloaded, the arguments can be retrieved with generics()
or generic(index)
.
var ToyBox := {
var contents:generic(1)
# Example overload
sub of(types:Table):null
origin(types)
end
}
var toy_box := ToyBox(of int).new()
toy_box.contents = "ball" # error
Example using self
as a type:
var Animal := {
sub deep_fake():self
return self.new
end
}
var Cat := {
include Animal
}
var fake_cat := Cat.deep_fake # fake_cat:Cat
Examples of typing tables:
var items := ["red", "blue", "green"](of int, string)
var items:Table(of int, string) := ["red", "blue", "green"]
When an object is assigned to a variable of a different type, it is cast to that type.
var health:Decimal = 100
It does this by calling the cast(to_type)
method.
sub cast(to_type)
if to_type is decimal
return to_dec()
else
return origin(to_type)
end
end
Tables are a type of object that store key-value pairs. If the key is omitted, one-based indexing is used (like Lua). They can be created with square brackets.
var nicknames := [
"Kirito" = "Black Swordsman",
"Asuna" = "Lightning Flash",
]
for nickname in nicknames
log("\{nickname.key} a.k.a. \{nickname.value}")
end
Tables can be joined using the excess operator.
var joined := [..table1, ..table2]
Each object has a table of attributes for its variables and a table of attributes for its methods.
Attributes can be added with square brackets before a method or variable declaration.
They omit the _attribute
and .new
(like C#).
[summary("Classified.")]
sub get_nuclear_launch_codes()
# ...
end
method_attributes.get("get_nuclear_launch_codes").get(1)
Core attributes:
[private] (variable, method) - warn if accessed outside of object / derived object
[abstract] (variable, method) - if variable, warn if `new` called; if method, warn if called and warn if not overridden in derived object
[static] (variable, method) - warn if accessed from derived object (instances are derived)
[summary(message:string)] (variable, method) - description for intellisense
[deprecated(message:string? = null)] (variable, method) - warn if used
Comments are ignored by the parser. There are no "magic" comments or documentation comments.
Line comments start with a hashtag.
# line comment
Block comments start with multiple hashtags and end with the same number of hashtags.
## block comment ##
#####
block comment
#####
These operators are shorthand for method calls.
0 == 1 # 0.`==`(1)
0 != 1 # 0.`!=`(1)
0 > 1 # 0.`>`(1)
0 < 1 # 0.`<`(1)
0 >= 1 # 0.`>=`(1)
0 <= 1 # 0.`<=`(1)
-1 # 1.`-`()
+1 # 1.`+`()
0 + 1 # 0.`+`(1)
0 - 1 # 0.`-`(1)
0 * 1 # 0.`*`(1)
0 / 1 # 0.`/`(1)
0 // 1 # 0.`//`(1)
0 % 1 # 0.`%`(1)
0 ^ 1 # 0.`^`(1)
true and false # true.and(false)
true or false # true.or(false)
true xor false # true.xor(false)
not true # true.not()
0 in [1, 2, 3] # [1, 2, 3].contains(0)
0 not_in [1, 2, 3] # not 0.in([1, 2, 3])
0 is integer # 0.includes(integer)
0 is_not integer # not 0.is(integer)
0 as integer # 0.cast(integer)
object?.value # if object == null do return null else return object.value end
object ?? value # if object == null do return value else return object end
These assignment operators are shorthand for applying method operators to the current value.
name += value
name -= value
name *= value
name /= value
name %= value
name ^= value
name ??= value
There are two types of iteration.
While loops repeat every time a condition is not false
or null
.
while true
# ...
end
For loops repeat using an iterator returned from the each
method.
for i in 1 to 10
log i
end
Loops can be ended early with break
.
while true
break
end
The current iteration can be skipped with next
, which moves to the end of the loop body.
for i in 1 to 10
next
end
There are two types of selection.
If statements run the first branch whose condition is not false
or null
.
if access_level == "noob"
log "denied"
elseif access_level == "normie"
log "denied"
else
log "come on in"
end
Cases run the first branch matching the subject.
Valid when branches: when matches
, when == match
, when != match
, when in match
, when not_in match
, when > match
, when < match
, when >= match
, when <= match
.
case input
when 0, 1
log("zero or one")
when 2
log("two")
when in [3, 4, 5]
log("three, four or five")
when > 5
log("big")
else
log("negative?")
end
Most languages use try
/catch
blocks for exception handling.
Holo attempts to simplify this with try
/else
, where try
always catches the exception.
try
throw "exception"
else ex
log(ex.message)
ensure
log("always runs")
end
Ruby uses strings (symbols) as enums. It suffers from typos and doesn't support integer casts.
GDScript uses integers as enums. It suffers from poor debugging readability.
Holo uses enums as a type of object that can be created with enum
or Enum.new
. They contain a name:string and a value:number.
var entity_type := enum
player = 1
animal = 2
end
log(entity_type.get("player").name) # "player"
log(entity_type.get("animal").value) # 2
log(entity_type.animal.name) # calls missing method; works same as entity_type.get("animal").name
Events can be awaited and connected easily.
var on_fire := Event.new()
on_fire.invoke()
on_fire.wait()
on_fire.hook(sub()
# ...
end)
Holo uses collaborative multithreading with actors (like Lua/Luau). As such, race conditions are usually not a problem.
log(1)
log(2) # Actor is locked between log(1) and log(2)
wait()
log(3)
If you need to lock over asynchronous methods, you can use a mutex, which limits the number of calls that can run at the same time.
var mutex = Mutex.new(1)
mutex.run(sub()
# ...
end)
It could be useful.
goto hello
label hello
To avoid allocating an object every time some code is run, in many languages you'll have to create a constant.
var Hello:Symbol = "hello"
log Hello
In Holo, you can use lit
to inline this constant.
The value will be cached the first time the expression is called, avoiding repeated allocations.
log lit("hello")
Note that if the literal is cast, the cast literal will be stored.
sub log_symbol(sym:Symbol)
log sym
end
log_symbol(lit("hello")) # stores "hello" cast to a symbol
For simplicity, generic types ((of ...)
) have not been annotated.
Every object includes object, even if not in the components
table.
stringify():Str
- returns "object"boolify():Str
- returns true if not (false or null)new(..params):self
- creates a new object, adding this object as a component, settingclass
to return this object, and callinginit(params)
init(..params):null
- default initialise methodclass():Obj
- returns selfgenerics():Table
- returns the generic typesgeneric(index:Obj):Obj
- returns the generic type at the given index or nullcomponents():Table
- returns the objects included in this objectvariables():Table
- returns a table of [name, variable]methods():Table
- returns a table of [name, delegate] (includes extension methods)variable(name:Str):Variable
- returns the variable with the given namemethod(name:Str):Delegate
- returns the method with the given nameeval(code:Delegate):Obj
- executes the method in the objecteval(code:Str):Obj
- parses and executes the code in the objecthash_code():Int
- returns a lookup number==(other:Obj):Bool
- returns true if both objects have the same reference!=(other:Obj):Bool
- calls==
and inverses withnot
<=>(other:Obj):Bool
- calls comparison operators (may be redundant, only implement if useful for table lookups)
An object representing no object.
stringify():Str
- returns "null"
An object containing methods that can be called as if they were in self
.
stringify():Str
- returns "global"log(..messages):null
- logs each message to the standard outputwarn(..messages):null
- logs each warning message to the standard outputthrow(message:Obj):null
- creates an exception and throws itthrow(exception:Exception):null
- throws the exceptioninput():Str
- reads and returns a line of user inputwait(duration:Num = 0.001):Dec
- yields for the duration in seconds and returns the exact amount of time waitedlocal_variables():Table
- returns a table of [name, variable]rand(range1:Range):Num
- callsrandom.rand
and returns a random number in the rangerand(max:Num):max
- callsrandom.rand
and returns a random number in the rangeexit(code:Int = 0):null
- exits the application with the given exit codequit(code:Int = 0):null
- callsexit
holo_version():Str
- returns the Holo language versionholo_copyright():Str
- returns the Holo language copyrights
Has two instances: true
and false
.
stringify():Str
- returns "true" if equalstrue
, otherwise "false"
An immutable sequence of characters.
stringify():Str
- returns selfcount():Int
- returns the number of characterscount(sequence:Str):Int
- returns the number of times the sequence appearslength():Int
- callscount()
characters():Table
- gets a table of characters in the stringtrim(predicate:Delegate? = null):Str
- removes characters matching a predicate from the start and end of the string (defaults to whitespace)trim_start(predicate:Delegate? = null):Str
- removes characters matching a predicate from the start of the string (defaults to whitespace)trim_start(sequence:Str):Str
- removes the sequence from the start of the stringtrim_end(predicate:Delegate? = null):Str
- removes characters matching a predicate from the end of the string (defaults to whitespace)trim_end(sequence:Str):Str
- removes the sequence from the end of the stringto_case(case:StringCaseType):Str
- converts the string to PascalCase, lowerCamelCase, snake_case, kebab-case, flatcase, Title Case, Sentence caseto_upper():Str
- converts letters in the string to uppercaseto_lower():Str
- converts letters in the string to lowercasereplace(find:Str, with:Str, limit:Int? = null):Str
- replaces each appearance of a sequence with another sequence up to limit timesinsert(sequence:Str, position:Int):Str
- inserts the sequence at the positionsplit(separator:Str):Table
- separates the string into a table of stringssplit():Table
- separates the string by whitespace into a table of stringsremove_range(between:Range):Str
- removes characters within the range+(other:Obj):Str
- concatenates the string and other.stringify*(count:Int):Str
- repeats the string count times==(other:Obj):Bool
- returns true if other is an equal string<(other:Obj):Bool
- returns true if other is a string preceding alphabetically<=(other:Obj):Bool
- returns true if other is a string equal or preceding alphabetically>=(other:Obj):Bool
- returns true if other is a string succeeding alphabetically>(other:Obj):Bool
- returns true if other is a string equal or succeeding alphabetically
The base component for integers and decimals.
stringify():Str
- returns "number"to_dec():Dec
- converts the number to a decimalconvert_angle(from:AngleType, to:AngleType):Num
- converts the angle between different types (degrees, radians, gradians, turns)sqrt():Num
- returns the square root of the numbercbrt():Num
- returns the cube root of the numberlerp(to:Num, weight:Num):Dec
- linearly interpolates the numberabs():Num
- returns the positive value of the numberclamp(min:Num, max:Num):Num
- returns min if < min, max if > max, otherwise selffloor():Int
- returns the highest integer below the numberceil():Int
- returns the lowest integer above the numbertruncate():Int
- removes the decimal places of the number
A signed whole number with arbitrary size and precision.
stringify():Str
- returns the integer as a stringparse(str:Str):Int
- converts the string to an integer or throwsparse_or_null(str:Str?):Int?
- converts the string to an integer or returns nullInfinity():Int
- returns infinityNaN():Int
- returns not-a-number
A signed fractional number with arbitrary size and precision.
stringify():Str
- returns the decimal as a stringparse(str:Str):Dec
- converts the string to a decimal or throwsparse_or_null(str:Str?):Dec?
- converts the string to a decimal or returns nullInfinity():Dec
- returns infinityNaN():Dec
- returns not-a-number
Gets each item in a sequence.
stringify():Str
- returns "iterator"current():Entry
- returns the current item in the sequencemove_next():Bool
- tries to increment the sequence position
A key-value pair.
stringify():Str
- returns (key + " = " + value)key():Obj
- returns the keyset_key(key:Obj):null
- sets the keyvalue():Obj
- returns the valueset_value(value:Obj):null
- sets the value
An iterable, deferred sequence of key-value pairs.
Methods such as append
also have a matching with_append
which returns a new sequence.
stringify():Str
- returns "sequence"each():Iterator
- returns an iterator for each itemto_table():Table
- adds each item to a new tableall(predicate:Delegate):Bool
- returns true if all items match the predicateany(predicate:Delegate):Bool
- returns true if any item matches the predicateany():Bool
- returns true if the sequence has at least one itemcount():Int
- returns the number of itemscount(item:Obj):Int
- returns the number of times the item appearslength():Int
- callscount()
contains(item:Obj):Bool
- returns true if sequence contains itemfirst(predicate:Delegate):Obj
- returns the first item matching the predicate or throwsfirst_or_null(predicate:Delegate):Obj?
- returns the first item matching the predicate or nullfirst():Obj
- returns the first item or throwsfirst_or_null():Obj
- returns the first item or nulllast(predicate:Delegate):Obj
- returns the last item matching the predicate or throwslast_or_null(predicate:Delegate):Obj?
- returns the last item matching the predicate or nulllast():Obj
- returns the last item or throwslast_or_null():Obj
- returns the last item or nullmax(get_value:Delegate? = null):Num
- gets the biggest value in the sequence of numbersmin(get_value:Delegate? = null):Num
- gets the smallest value in the sequence of numbersaverage(type:AverageType = AverageType.mean):Num
- returns the average value of the sequence of numbers using mean, median, mode, or rangesum():Num
- adds all items in the sequence of numbersproduct():Num
- multiplies all items in the sequence of numbersappend(item:Obj):null
- adds an item to the endappend_each(items:Sequence):null
- adds each item to the endprepend(item:Obj):Sequence
- adds an item to the startprepend_each(items:Sequence):null
- adds each item to the startconcat(separator:str = "", stringify:Delegate? = null):str
- adds each item to a string by calling stringifyremove(item:Obj, limit:Int? = null):null
- removes each appearance of the item up to limit timesremove_where(predicate:Delegate, limit:Int? = null):null
- removes items matching a predicate up to limit timesremove_first(count:Int = 1)
- removes the first count itemsremove_last(count:Int = 1)
- removes the last count itemsremove_duplicates():null
- removes duplicate itemsclear():null
- removes all itemssort(comparer:Delegate? = null):null
- sorts the sequence into an order using the comparerreverse():null
- reverses the order of the sequencecopy():Sequence
- shallow-copies the sequence into another sequence
A sequence of key-value pairs.
stringify():Str
- returns a string like "[a = b, c = d]"each():Iterator
- returns an iterator for each (key, value)add(value:Obj):null
- adds a value at the key one above the highest ordinal keyadd_each(values:Table):null
- adds each value at the keys one above the highest ordinal keyset(entry:Entry):null
- adds an entryset(key:Obj, value:Obj):null
- creates and adds an entryset_each(values:Table):null
- sets each entryget(key:Obj):Obj
- finds a value from the key or throwsget_or_null(key:Obj?):Obj?
- finds a value from the key or returns nullkeys():null
- returns a table of keysvalues():null
- returns a table of valuescontains_key(key:Obj?):Bool
- returns true if there's an entry with the given keycontains_value(value:Obj?):Bool
- returns true if there's an entry with the given valuesample():Entry
- returns a random entryshuffle():Entry
- randomises the keysinvert():null
- swaps the keys and valuesweak():Bool
- returns true if the values are weakly referencedset_weak(value:Bool):null
- references values weakly so they can be garbage collectedon_set():Event
- returns an event that's invoked when a value is seton_get():Event
- returns an event that's invoked when a value is gotten
A range between two inclusive numbers.
stringify():Str
- returns (min + " to " + max + " step " + step)new(min:Num?, max:Num?, step:Num = 1):Range
- returns a new rangemin():min
- returns the minimum valueset_min(value:Num?):null
- sets the minimum valuemax():max
- returns the maximum valueset_max(value:Num?):null
- sets the maximum valuestep():step
- returns the step valueset_step(value:Num):null
- sets the step value
An object containing a method and a target.
stringify():Str
- returns (target.stringify
+ "." +method_name
)call(..arguments):Obj?
- calls the best overload on the targetoverloads():Table
- returns a table of method overloadstarget():Obj
- returns the method targetset_target(target:Obj):null
- sets the method targetmethod_name():Str
- returns the method nameset_method_name(name:Str):null
- sets the method name
An object containing information about a variable.
stringify():Str
- returns (``)name():Str
- returns the variable namevalue():Obj?
- returns the variable valuetypes():Table
- returns the allowed types of the variable
A date and time in the Gregorian calendar.
stringify(format:Str):Str
- returns the time formatted with the given (.NET) format stringstringify():Str
- returns the time formatted like "2024/08/04 15:46 +0000"new(total_seconds:Num)
- returns a new timenew(year:Num, month:Num, day:Num, hour:Num, minute:Num, second:Num, offset:Num = 0)
- returns a new timenew(year:Num, month:Num, day:Num, offset:Num = 0)
- returns a new timenow(offset:Num):Time
- returns the current time at the given offsetnow():Time
- returns the current time at the local system offsettotal_seconds():Dec
- returns the number of seconds since 0total_milliseconds():Dec
- returns the number of milliseconds since 0year():Int
- returns the year componentset_year(value:Num):null
- sets the year componentmonth():Int
- returns the month componentset_month(value:Num):null
- sets the month componentday():Int
- returns the day componentset_day(value:Num):null
- sets the day componenthour():Dec
- returns the hour componentset_hour(value:Num):null
- sets the hour componentminute():Dec
- returns the minute componentset_minute(value:Num):null
- sets the minute componentsecond():Dec
- returns the second componentset_second(value:Num):null
- sets the second componentoffset():Dec
- returns the offset componentset_offset(value:Num):null
- sets the offset componentparse(time:Str):Time
- converts the string to a time or throwsparse_or_null(time:Str?):Time?
- converts the string to a time or returns null
A period of time.
stringify(format:Str):Str
- returns the span formatted with the given (.NET) format stringstringify():Str
- returns the span formatted like "00:14:23.1294"new(total_seconds:Num)
- returns a new spannew(hours:Num, minutes:Num, seconds:Num)
- returns a new spantotal_seconds():Dec
- returns the total number of secondstotal_milliseconds():Dec
- returns the total number of millisecondshour():Dec
- returns the hour componentset_hour(value:Num):null
- sets the hour componentminute():Dec
- returns the minute componentset_minute(value:Num):null
- sets the minute componentsecond():Dec
- returns the second componentset_second(value:Num):null
- sets the second componentparse(span:Str):Span
- converts the string to a span or throwsparse_or_null(span:Str?):Span?
- converts the string to a span or returns null
An error or control code thrown up the call stack.
stringify():Str
- returns (message()
+ "\n" +stack_trace()
)new(message:Obj? = null)
- returns an exception instance with the given messagemessage():Obj
- returns the exception messageset_message():Str
- sets the exception messagestack_trace():Table
- returns each line of the call stack (including external lines from C#)
Holds a reference to an object without preventing it from being garbage collected.
stringify():Str
- returns (message()
+ "\n" +stack_trace()
)target():Obj
- returns the weakly referenced objectset_target(value:Obj):null
- sets the weakly referenced objectis_alive():Bool
- returns true if the weak reference is still valid
Runs code in the background collaboratively.
run(method:Delegate, arguments:Table = []):Thread
- calls a method in the threadwait():null
- waits for the thread to finish
Limits the number of threads that can run at once.
new(limit:Int = 1):Mutex
- returns a mutex with the given entry limitrun(method:Delegate, arguments:Table = []):Thread
- calls a method in the mutexlimit():Int
- returns the entry limitset_limit(limit:Int):null
- sets the entry limitremaining():Int
- returns the remaining entriesset_remaining(remaining:Int):null
- sets the remaining entries
Cancels a background task collaboratively.
cancel(delay:Num = 0):null
- callson_cancel()
after the delayon_cancel():Event
- an event to be invoked when cancelledis_cancelled():Bool
- returns true if cancelledreset():null
- un-cancels the canceller for reusecombined_with(other:canceller):Canceller
- combines two cancellers into one
A signal to be awaited and listened to.
invoke(arguments:Table = []):null
- calls each listener and waiterhook(method:Delegate, limit:Int? = null):null
- calls the method when the event is invoked up to limit timeswait():Table
- waits for the event to be invoked
A collection of nerdy maths methods.
Pi():Dec
- returns many digits of π (3.14...)Tau():Dec
- returns many digits of τ (6.28...)E():Dec
- returns many digits of e (2.71...)sin
,cos
,tan
,asin
,acos
,atan
,atan2
,sinh
,cosh
,tanh
,asinh
,acosh
,atanh
,exp
,log
,log10
,log2
,hypot
- who even cares about these
Access files on the hard drive.
read(path:Str):Str
- opens and reads text from the fileread_bytes(path:Str):Table
- opens and reads a table of bytes from the fileread_sequence(path:Str):Sequence
- opens and reads a sequence of bytes from the filewrite(path:Str, value:Str):null
- opens and writes text to the filewrite_bytes(path:Str, value:Table):null
- opens and writes a table of bytes to the filewrite_sequence(path:Str, value:Table):Sequence
- opens and writes a sequence of bytes to the fileappend(path:Str, value:Str):null
- opens and appends text to the fileappend_bytes(path:Str, value:Table):null
- opens and appends a table of bytes to the fileappend_sequence(path:Str, value:Sequence):Str
- opens and appends a sequence of bytes to the filedelete(path:Str):Bool
- deletes the fileexists(path:Str):Bool
- returns true if the file existscopy(from:Str, to:Str):null
- copies the file from the source to the destinationmove(from:Str, to:Str):null
- recursively moves the file from the source to the destination
Access folders/directories on the hard drive.
file_names(path:Str):Table
- returns a table of file namesfile_names_recursive(path:Str):Table
- returns a table of file names, including files in nested foldersfolder_names(path:Str):Table
- returns a table of folder namesfolder_names_recursive(path:Str):Table
- returns a table of folder names, including folders in nested foldersdelete(path:Str):Bool
- deletes the folderexists(path:Str):Bool
- returns true if the folder existscopy(from:Str, to:Str):null
- recursively copies the folder from the source to the destinationmove(from:Str, to:Str):null
- recursively moves the folder from the source to the destination
Handle file paths.
join(a:Str, b:Str):Str
- joins two paths into onefile(path:Str, extension:Bool = true):Str
- returns the file name from the path (e.g. "C://Documents/neko.jpg" becomes "neko.jpg")folder(path:Str):Str
- returns the folder path from the path (e.g. "C://Documents/neko.jpg" becomes "C://Documents")extension(path:Str):Str
- returns the extension from the path (e.g. "C://Documents/neko.jpg" becomes "jpg")trim_extension(path:Str):Str
- removes the path without the extension (e.g. "C://Documents/neko.jpg" becomes "C://Documents/neko")drive(path:Str):Str?
- returns the drive from the path (e.g. "C://Documents/neko.jpg" becomes "C")to_absolute(relative:Str):Str
- converts the relative path to an absolute pathto_relative(absolute:Str):Str
- converts the absolute path to a relative path
Generate pseudo-random numbers.
seed():Num
- returns the random seedset_seed(value:Num):null
- sets the random seedint(min:Num, max:Num):Int
- returns a random integer from min to maxint(max:Num):Int
- callsint(1, max)
int(range:Range):Int
- returns a random integer in the rangedec(min:Num, max:Num):Dec
- returns a random decimal from min to maxdec(range:Range):Dec
- returns a random decimal in the range
Holo uses versions like "1.0" and "2.4".
For developers:
- Increment the major version when adding new features or making breaking changes.
- Increment the minor version when fixing bugs or making small improvements.
For users:
- You usually want the latest major version, although it may require some changes to your project.
- You always want the latest minor version, and there should not be any issues upgrading.
Names should be clear and concise.
Variables should use lower_snake_case:
var health := 100
var armor := 50
Constant variables should use PascalCase:
var Player := {
var health := 100
}
Methods should use lower_snake_case unless they fetch a constant:
sub heal(amount:int) do
health += amount
end
sub PI()
return 3.14159265
end
Avoid:
get_hash_code()
return_username()
remove_item(item)
Instead:
hash_code()
username()
remove(item)
Avoid:
var freeze_objects:bool
var are_objects_frozen:bool
Instead:
var freeze_objects_on:bool
var freeze_objects_enabled:bool
Avoid:
var numbers := [1, 2].add(3)
Instead:
var numbers := [1, 2].with_add(3)
Use a number suffix or descriptive name to avoid naming collisions.
Avoid:
for i in 1 to 5 do
for j in 1 to 5 do
for k in 1 to 5 do
Instead:
for i in 1 to 5 do
for i2 in 1 to 5 do
for i3 in 1 to 5 do
for x in 1 to 5 do
for y in 1 to 5 do
for z in 1 to 5 do
Avoid:
var cat := Cat.new
var cat1 := Cat.new
var kitty := Cat.new
Instead:
var cat := Cat.new
var cat2 := Cat.new
var cat3 := Cat.new
var red_cat := Cat.new
var green_cat := Cat.new
var blue_cat := Cat.new
Comments should be clear and concise.
Insert a comment on its own line before each section:
# Players
john.respawn
mikasa.respawn
# Enemies
dullahan.respawn
Insert a comment on its own line before the section:
# Respawn player
player.dead = false
player.health = 100
player.teleport(0, 0)
Put a comment at the end of the line:
var shield := 50 # Percentage of health
Avoid:
# Create a 3x3 game grid
for i in 1 to 3 do
log("###")
end
Instead:
# Create a game grid
for i in 1 to 3 do
log("###")
end