Welcome to my very first programming language! I hope you like it. I implore you to use RichTea in all your production environments regardless of use-case or the fact that it has many limitations... just kidding.
RichTea is a hobby project which, if nothing else, is an interesting resource and reference point to use when learning to design and implement a simple programming language.
RichTea makes use of the gradle build process - If you are familiar with gradle compilation should be straight forward.
./gradlew buildTo run a RichTea program:
./gradlew run -Pargs="path/to/program.tea"RichTea programs are sequences of function calls. Every construct — variables, loops, conditionals — is a function. The syntax follows this general shape:
FunctionName(attribute1:value1 attribute2:value2 :branchName{
// child functions
})
The first (unnamed) attribute is the implicit attribute. _ always refers to the last returned value.
Every program needs the standard library imported first. The runtime only ships with Import built-in.
Import("*" from:"modules/std")
Let declares variables in the current scope. Set mutates an existing variable. Multiple attributes can be declared in one Let.
Import("*" from:"modules/std")
// Declare a single variable
Let(name:"Alice")
Print("Hello, { name }!") // "Hello, Alice!"
// Declare multiple variables at once
Let(x:10 y:20)
Print("x = { x }, y = { y }") // "x = 10, y = 20"
// Mutate an existing variable
Set(attribute:name to:"Bob")
Print("Hello, { name }!") // "Hello, Bob!"
Use { variable } inside any string literal to embed a value.
Import("*" from:"modules/std")
Let(language:"RichTea")
Let(year:2016)
Print("{ language } was created around { year }.")
RichTea supports the full set of operators: + - * / % ^ and compound-assignment forms += -= *= /=.
Import("*" from:"modules/std")
Let(a:10 b:3)
Print("Sum: { a + b }") // 13
Print("Difference: { a - b }") // 7
Print("Product: { a * b }") // 30
Print("Quotient: { a / b }") // 3.333...
Print("Remainder: { a % b }") // 1
Print("Power: { a ^ b }") // 1000.0
// Increment shorthand
Increment(attribute:a by:1)
Print("a after increment: { a }") // 11
If uses a then/else branch. The implicit attribute is the boolean expression.
Import("*" from:"modules/std")
Let(score:75)
If(score >= 60 :{
Print("Pass!")
} :else{
Print("Fail.")
})
Switch dispatches on a string value. Branch names are the case labels; default is the fallback.
Import("*" from:"modules/std")
Let(day:"Monday")
Switch(value:day
:Monday{
Print("Start of the week!")
}
:Friday{
Print("TGIF!")
}
:default{
Print("Midweek: { day }")
}
)
The loop index is exposed as the as variable inside the do branch.
Import("*" from:"modules/std")
For(5 as:"i" :{
Print("Iteration { i }")
})
// Prints: Iteration 0 ... Iteration 4
Import("*" from:"modules/std")
Let(count:0)
While(condition:count < 5 :{
Print("count = { count }")
Increment(attribute:count by:1)
})
The current element is exposed via the as variable.
Import("*" from:"modules/std")
Let(colours:["red", "green", "blue"])
ForEach(in:colours as:"colour" :{
Print("Colour: { colour }")
})
Arrays are declared with [...]. Map transforms every element via a mapFunction branch and returns a new list. _ holds the current element value inside the branch.
Import("*" from:"modules/std")
Let(numbers:[1, 2, 3, 4, 5])
// Double every number
Map(input:numbers :mapFunction{
Return(_ * 2)
})
// _ now holds [2, 4, 6, 8, 10]
ForEach(in:_ as:"n" :{
Print("{ n }")
})
_ is always the result of the most recently executed function, making it easy to chain calls without intermediate variables.
Import("*" from:"modules/std")
Let(words:["hello", "world", "richTea"])
// Map to upper-case, then print the results
Map(input:words :mapFunction{
Return(_.toUpperCase())
})
ForEach(in:_ as:"word" :{
Print(word)
})
// HELLO
// WORLD
// RICHTEA
Prefix a function with @ to capture a reference to it rather than calling it. The reference can be stored in a variable and called later.
Import("*" from:"modules/std")
// Store a greeting function as a value
Let(greeter:@Print(message:"Hello from a stored function!"))
// Call the reference
greeter()
Parameterised first-class functions work the same way:
Import("*" from:"modules/std")
Let(adder:@(x:0 y:0 :{
Return(x + y)
}))
adder(x:3 y:4)
Print("3 + 4 = { _ }") // 3 + 4 = 7
An anonymous scope (:{...}) groups statements and creates a new variable scope. Return sets the last-return value and exits the current scope.
Import("*" from:"modules/std")
(:{
Let(temp:42)
Print("Inside scope: { temp }")
Return(temp)
})
// _ == 42 here; temp is not accessible outside the scope
Print("Returned from scope: { _ }")
Import("*" from:"modules/std")
// Write a file
WriteFile(path:"output.txt" contents:"Hello, file!\n")
// Read it back (returns a string by default)
ReadFile(path:"output.txt")
Print("File contents: { _ }")
Timer injects timeStarted, elapsedTime, and currentTime into its branch scope.
Import("*" from:"modules/std")
Timer(:{
For(1000000 as:"i" :{}) // Do some work
Print("Elapsed: { elapsedTime }ms")
})
Man prints the definition of any function reference — its class, implicit attribute/branch names, and default attribute values.
Import("*" from:"modules/std")
Man(target:@Print())
Print(_)
// Print (richTea.std.exports.Print)
// Implicit attribute name: message
// Attributes:
// appendNewLine (Default value: true)
// prependNewLine (Default value: false)
Import("*" from:"modules/std")
For(count:20 as:"i" :{
Let(n:i + 1)
Switch(value:(n % 15 == 0 ? "fizzbuzz" : (n % 3 == 0 ? "fizz" : (n % 5 == 0 ? "buzz" : "number")))
:fizzbuzz{ Print("FizzBuzz") }
:fizz{ Print("Fizz") }
:buzz{ Print("Buzz") }
:number{ Print("{ n }") }
)
})
| Construct | Function | Key Attributes |
|---|---|---|
| Declare variable | Let |
any named attributes |
| Mutate variable | Set |
attribute, to |
| Increment | Increment |
attribute, by |
| Output | Print |
message (implicit), appendNewLine, prependNewLine |
| Branch | If |
expression (implicit), : (then), :else |
| Multi-branch | Switch |
value, branch per case, :default |
| Counted loop | For |
count (implicit), as, : (do) |
| Condition loop | While |
condition, : (do) |
| List loop | ForEach |
in, as, : (do) |
| Transform list | Map |
input, :mapFunction |
| Read file | ReadFile |
path, asString |
| Write file | WriteFile |
path, contents |
| Time a block | Timer |
: (block), exposes elapsedTime |
| Exit | SystemExit |
code (implicit) |
| Last return value | _ |
— |
| Function reference | @Fn(...) |
— |