ayla lang
ayla lang is a small interpreted language written in go, designed to make you forget everything
Because f-ck you. - Linus Torvalds
about
our team
Installation and Usage
See INSTRUCTIONS.md for full step-by-step instructions for macOS and Windows.
built in functions!
explode(...) – prints values to stdout
explodeln(...) — prints values to stdout and adds '\n' at the end
scanln(x) – scans console input after clicking enter and stores it in variable
scankey(x) – scans key press in console and stores it in variable
toBool(x) – parses a value to boolean
toString(x) – parses a value to string
toInt(x) – parses a value to integer
toFloat(x) – parses a value to float
toArr(x) – parses a value to array
typeof(x) – returns type of value as string
len(x) – returns length of arrays or strings
push(arr, val) – append to array
pop(arr) – remove and return last element
insert(arr, index, val) – insert value
remove(arr, index) – remove element at index
clear(arr) – remove all elements
wait(ms) – wait for a duration in milliseconds
randi() or randi(max) or randi(min, max)
randf() or randf(max) or randf(min, max)
See docs/builtins.md for more about built-in functions.
type casts
int() – converts value to integer
float() – converts value to float
string() – converts value to string
bool() – converts value to bool
the features
declaration and assignment
to declare a normal mutable, reassignable variable use egg
egg x = "wowie"
variables can be declared without an initial value, like so.
egg x
they default to nil
to declare a constant, use rock
rock x = "i will never change"
constants cannot be declared without an intitial value.
rock x
output: Runtime error at 1:5: const x must be initialised
you can also use type annotation for both egg and rock
the available types are:
egg x int = 3
explodeln(x)
output: 3
and you will also come across Runtime errors if you address the wrong type
rock x string = 5
output: runtime error at 1:5: type mismatch: 'int' assigned to a 'string'
multi-declaration and multi-assignment
you can also assign and declare multiple variables to a tuple
rock a, b = 4, 2
explodeln("${a} ${b}")
output: 4 2
you can also just declare them without an inital value like normal
egg a, b
you can do type annotation, but not for every variable, the type at the end dictates all the other's types like so
egg a, b, c int
explodeln(typeof(a), typeof(b), typeof(c))
explodeln(a)
output:
int
int
int
0
and also like single declaration, using multi constant declaration you must initialise it
rock a, b
output: runtime error at 1:5: constants, a, b, must be initialised
same principles for assignment
egg a, b
a, b = 4, 2
explodeln("${a} ${b}")
output: 4 2
you can also assign and declare variables to a function with multiple return values
fun operation(x int, y int) (int, int) {
back x + y, x - y
}
egg a, b int = operation(5, 3)
explodeln(a, b)
output:
8
2
slice types
you can also annotate slice types like this
egg a []int = [1, 2, 3]
explode(a[0])
output: 1
egg a [][]int = [[1], [2], [3]]
explode(a[0][0])
output: 1
the thing type
the thing type is equivalent to interface{} or any from go
you can assign any value to it
egg x thing = 2
explode(x)
output: 2
but, you must use type assertion to do operations with it
egg x thing = 2
explode(x.(int) + 1)
output: 3
otherwise you will come across a Runtime error
egg x thing = 2
explode(x + 1)
output: runtime error at 3:11: cannot use 'thing' in operations, assert a type first
semicolon
semicolons are optional! put them if you want, or leave them out if you're more comfortable with that
egg x = 5;
this is totally fine
egg x = 5
also valid
you can use both single line and multiline comments
// is for single line comments:
egg x = 5 // this is a comment
// and this is also a comment
/* open and */ close for multiline comments:
egg x = 5
/* this is
a really
big comment */
booleans
for booleans, use the constants yes and no
egg foo = yes
ayla foo {
explode("foo is yes")
} elen {
explode("foo is no")
}
output: foo is yes
string concatenation
you can concatenate strings using the + operator.
egg a = "hello "
egg b = "world"
explode(a + b)
output: hello world
you can also concatenate strings with other types by parsing.
explode(toString(4) + toString(2))
output: 42
string interpolation
you can also interpolate strings using ${}
unlike JavaScript, you just use the normal quotation marks, "", not ``
egg rand = randi(10)
explode("Random number: ${rand}")
output: 0 - 10
string indexing
you can index into strings almost like arrays
egg text = "Hello"
explodeln(text[0])
output: H
if/else if/else
in ayla-lang, if has been renamed to ayla, and else renamed to elen. therefore else if has been aptly renamed to elen ayla.
egg x = 5
ayla x <= 9 {
explode("number is single digits")
} elen ayla x >= 10 {
explode("number is double digits")
}
loops
four loop
the for loop has been renamed to four loop, for convenience
oh yea also no brackets
for convenience
this is a classic c-style for loop
four egg i = 0; i < 5; i = i + 1 {
explode(i)
}
output: 1 2 3 4 5
but you can also do a for loop with range to iterate over maps, arrays, and strings, and ints
arrays:
x := [1, 2, 3]
four i, v := range x {
explodeln(v)
}
output:
1
2
3
maps:
x := {"a": 1, "b": 2}
four k, v := range x {
explodeln(k)
}
output:
a
b
strings:
x := "hiya"
four i, v := range x {
explodeln(v)
}
output:
h
i
y
a
ints:
this is used as a repeat
four i := range 5 {
explodeln(i)
}
output:
0
1
2
3
4
you can also use _ to discard a variable like this
x := [1, 2, 3]
four _, v := range x {
explodeln(v)
}
output:
1
2
3
why loop
the while loop has been renamed to why loop, for convenience
no brackets here either
:>
egg i = 0
why i < 7 {
i = i + 1
explode(i)
}
output: 1 2 3 4 5 6 7
kitkat and next
Take a break, take a kitkat
because we are so nice, we renamed break to kitkat so it sticks in your memory
oh yea we also renamed continue to next
egg i = 0
why i < 7 {
i = i + 1
ayla i == 4 {
kitkat
}
explode(i)
}
output: 1 2 3
egg i = 0
why i < 7 {
i = i + 1
ayla i == 4 {
next
}
explode(i)
}
output: 1 2 3 5 6 7
switch-case
switch has been renamed to decide
and case to when
and default to otherwise
egg x = 2
decide x {
when 2 {
explode("x is 2")
}
when 3 {
explode("x is 3")
}
otherwise {
explode("x is neither 2 or 3")
}
}
output: x is 2
you can also use conditionals in the switch expression, like this
egg x = 5
decide x < 10 {
when yes {
explode("x is less than 10")
}
otherwise {
explode("x is more than 10")
}
}
you can also implement conditionals into case expressions by making the switch expression a boolean value
egg x = 5
decide yes {
when x < 10 {
explode("x is less than 10")
}
otherwise {
explode("x is more than 10")
}
}
with statement
the with statement evaluates a value and makes it available inside a block as it
with x {
explodeln(it)
}
with does not modify the original value
it behaves like a const
egg x int = 5
with x {
explodeln(it) // 5
}
Nested with blocks shadow it
with a {
with b {
explodeln(it) // b
}
}
functions
to declare a function use fun
return has been renamed to back, haha
fun add(x, y) {
back x + y
}
explode(add(5, 7))
output: 12
you can have a designated return type like this
fun add(x, y) (int) {
back x + y
}
explodeln(add(4, 2))
output: 6
to see more about functions see docs/functions.md
arrays
to initialise an array use square brackets: []
egg arr = [0, 1, 2, 3]
explode(arr)
output: [0, 1, 2, 3]
you can also index into an array, like normal
egg arr = [1, 2, 5]
explode(arr[2])
output: 5
ayla also supports mixed arrays, but you must use []thing
egg arr []thing = [1, 2, "3"]
like thing you must use type assertion to do operations
egg arr []thing = [1, 2, "3"]
explode(arr[1].(int) + 1)
output: 3
maps
maps are created using the curly braces {}
usually the type of the keys and values are inferred from the first key/value pair like so
egg x = {"a": 1, "b": 2}
you would get a Runtime error if you mismatched the types
egg x = {"a": 1, 3: 2}
output: runtime error at 1:9: map key 1 expected string but got int
it also uses 0-based indexing so here map key 1 would be referring to 3
you can also use type annotation using this go-like syntax
egg x map[string]int = {"a": 1, "b": 2}
here, string correlates to the key type, whilst int correlates to the value type as shown in "a": 1
you can also use thing if you want
in operator
the in keyword checks for membership inside maps and arrays and strings
maps:
for maps it checks whether a key exists
egg x = {"a": 1, "b": 1}
ayla "a" in x {
explodeln("exists")
}
output: exists
arrays:
for arrays it checks a value exists
egg x = [1, 2, 3]
ayla 2 in x {
explodeln("exists")
}
output: exists
strings:
for strings it checks for the substring's existence
egg x = "hello"
ayla "he" in x {
explodeln("exists")
}
output: exists
structs
ayla supports typed, and anonymous structs
typed:
type struct Person {
Name string
Age int
}
egg p = Person {
Name: "Ziad",
Age: 13
}
explode(p.Name + "is " + string(p.Age))
for anonymous structs, since there is no struct type, just use the struct keyword to denote an anonymous struct
anonymous:
egg p = struct {
Name: "Ziad",
Age: 13
}
explode(p.Name + "is " + string(p.Age))
for more about structs, check out docs/structs.md
user defined types and aliases
ayla supports creating your own named types and aliases
named types are a completely new type with an underlying type like int
for example, in this snippet, you need to cast the integer to int since otherwise it would have a type of int
type Age int
egg age = Age(13)
you can also use the defined types as type annotation
type Age int
egg age Age = Age(13)
aliases are just a new name for a type, so they act exactly like their underlying type
this means you dont need a type cast
type Name = string
egg n Name = "string"
see more about type definitons and aliases here: docs/type-definitions.md
block scope
in ayla, every block has its own Environment.
ayla yes {
egg x = 2 // define inside if statement
}
explode(x) // error
output: runtime error at 5:10: undefined variable: x
if the lower scope cant find the variable, it will look for it in the parent environment
egg x = 4
ayla yes {
x = 2
}
explode(x)
output: 2
but you can also define a variable with the same name in the child environment, and it wont affect the one above
egg x = 5
ayla yes {
explodeln(x) // 5
x = 3
explodeln(x) // 3
egg x = 7
explodeln(x) // 7
}
explodeln(x) // 3, because was assigned 3 in child
output:
5
3
7
concurrency
ayla supports basic concurrency using the spawn keyword.
spawn runs a block of code in parallel with the rest of the program.
spawn {
why yes {
explodeln("tick")
wait(1000)
}
}
explodeln("running at the same time")
Both blocks execute concurrently.
shared state:
Spawned blocks share variables with the parent scope.
egg counter = 0
spawn {
why counter < 5 {
counter = counter + 1
wait(500)
}
}
why counter < 5 {
explodeln(counter)
wait(500)
}
there is currently no synchronization (locks, mutexes, channels)
so be careful when mutating shared variables from multiple spawns.
For full details, see docs/concurrency.md.
runtime errors
error handling for runtime errors
rock i = 1
i = 2
Runtime error at 3:2: cannot reassign to const: i
parse errors
error handling for parse errors
ayla {
}
output: parse error at 1:6: missing condition in if (got {)
parse errors will default to (got nothing) if there is nothing after the token
egg x =
output: parse error at 1:8: expected expression after '=' (got nothing)#� �v�s�c�o�d�e�-�a�y�l�a�
�
�