0.1.0 |
|
---|
#20 in #eventually
83KB
2.5K
SLoC
hibiscus
Hibiscus is an embeddable, statically typed scripting language.
Progress
- Milestone 1
- Minimum viable programming language (untyped, few features)
- Syntax highlighting in editor
- Milestone 2
- Type system added
- Signature types
- Struct types
- Milestone 3 (v0.1 release)
- Multithreaded VM (minimum viable)
- Iterators (including for loops, array filtering/mapping/etc)
- User-denotable exceptions
- Tuples
- Sum types
- Pattern matching
- Modules (including OCaml-style functors)
- Full stdlib
- Milestone 4 (v0.2 release)
- LSP server
Samples
Some of the below samples are out of date with where the language will eventually move, especially the ones about mutability. Runnable samples are located in the samples/
folder.
hello_world.bi
print("Hello, world!")
Hello, world!
fibonacci.bi
let first_val = 0
let second_val = 1
-- The last expression of a function is its return value.
fun fibonacci(n: int) -> int =
-- multiple statements can be separated with semicolons
print(f"finding fibonacci for {n}");
-- you can return from functions early
if n < 0 then
return 0
end;
-- the last expression is the function's return value
if n = 0 then
first_val
else if n = 1 then
second_val
else
fib(n - 2) + fib(n - 1)
end
end
print(fibonacci(3))
2
objects.bi
-- literal object types don't have to be defined beforehand
let sample_plants = {
evergreen: [
"fir",
"redwood",
],
deciduous: [
"maple",
"apple",
],
flowering: "lavender",
}
print(signature(sample_plants));
print(sample_plants.evergreen[0])
{evergreen: list<string>, deciduous: list<string>, flowering: string}
fir
signature_types.bi
-- signature types are compared structurally, and accept any object with
-- matching fields
sig Gem = {
cut: string,
facet: int,
powers: list<string>,
... -- the trailing ... tells Hibiscus to allow extra fields
}
-- implicit () return type if none is given
fun print_gem(gem: Gem) =
let cut = gem.cut;
let facet = gem.facet;
let powers = ", ".join(gem.powers.map(fun(power) => f"\"{power}\""));
print(f"\{cut: \"{cut}\", facet: \"{gem.facet}\", powers: [{powers}]\}")
end
-- signature types allow any object with exactly identical fields
-- if the signature ends with `...`, it also allows extra fields
print_gem({
cut: "8XM",
facet: 5,
powers: ["spin dash", "electro-whip"],
extra_field: "whatever"
})
{cut: "8XM", facet: 5, powers: ["spin dash"]}
methods.bi
-- signatures can also require methods
sig PrintableId = {
id_number: int
} with
-- commas are used to separate declarations
describe_self(self) -> string,
set_id_number(mutable self, new_id: int) -> ()
end
-- if this was declared with 'let my_id' instead of 'mutable my_id', the call to
-- 'my_id.set_id_number(6)' would be an error
mutable my_id = {
id_number: 5
} with
describe_self(self) -> string =
f"{self.id_number}"
end
-- fields can only be updated when 'self' is mutable
set_id_number(mutable self, new_id: int) =
self.id_number <- new_id
end
end
print(my_id.describe_self());
my_id.set_id_number(6);
print(my_id.describe_self())
5
6
union_types.bi
sig MySig = {
name: string
}
type UnionType =
| Tagged MySig
| AlsoTagged {name: string, id: int} -- types can be defined inline
-- union cases are also types
fun get_tagged_name(item: Tagged MySig) -> string =
item.name
end
-- union types need to be pattern matched
fun get_name(item: UnionType) -> string =
match item with
| Tagged tagged => get_tagged_name(tagged)
| AlsoTagged also_tagged => also_tagged.name
end
let item = Tagged {name: "my item"};
print(get_name(item));
let item2 = {name: "my item2"};
print(get_name(AlsoTagged item2))
my item
my item2
structs.bi
-- structs are nominally typed, so only an instance of MyItem can be passed to a
-- function that expects a MyItem.
struct MyItem = {
name: string,
id: int,
_secret_field: string -- fields that start with '_' are private
} with
get_name(self) -> string =
self.name
end
set_name(mutable self, new_name: string) =
self._secret_method();
self.name <- new_name
end
-- methods that start with '_' can only be accessed by other methods
_secret_method(self) =
print("secret method!")
end
-- no 'self' parameter means static method
default_name() -> string = "standard" end
new(name: string, id: int) -> MyItem =
-- the struct's name can be used as a function to create a new struct
-- from the given object
-- if there are any private fields, structs can only be created in a
-- static method
MyItem {
name,
id,
_secret_field: "secret"
}
end
end
-- mutable items can be local, arguments, or self, but cannot be assigned to
-- fields
mutable my_item = MyItem.new("name", 5)
-- unless overridden, all values have a default conversion to string
print(my_item);
my_item.set_name(MyItem.default_name());
print(my_item)
MyItem { name: "name", id: 5, _secret_field: "secret"}
MyItem { name: "standard", id: 5, _secret_field: "secret"}
Dependencies
~1MB
~22K SLoC