Skip to content

Scrolls & Tomes

A Scroll is an ordered, indexable collection.

Scroll heroes is ["Gandalf", "Frodo", "Aragorn"];
Scroll numbers is [1, 2, 3, 4, 5];
Scroll empty is [];

Zero-based indexing:

Chant(heroes[0]); // "Gandalf"
Chant(heroes[2]); // "Aragorn"

Extract a sub-array with slice syntax [start:end]:

Scroll nums is [10, 20, 30, 40, 50];
Chant(nums[1:3]); // [20, 30]
Chant(nums[:2]); // [10, 20] (from beginning)
Chant(nums[3:]); // [40, 50] (to end)

Slicing also works on strings:

Runestone word is "RuneLang";
Chant(word[0:4]); // "Rune"
Chant(word[4:]); // "Lang"

Scrolls declared with Mutable can be modified:

Mutable Scroll inventory is ["Sword", "Shield"];
inventory.push("Potion");
inventory.pop();
MethodDescriptionExample
.push(val)Add element to endlist.push("item")
.pop()Remove and return last elementlist.pop()
.length()Number of elementslist.length()
.contains(val)Check if element existslist.contains("x")
.map(fn)Transform each elementlist.map((Countstone n) => n * 2)
.filter(fn)Keep matching elementslist.filter((Countstone n) => n > 0)
.reduce(init, fn)Accumulate a resultlist.reduce(0, (Countstone a, Countstone b) => a + b)
.forEach(fn)Execute function on eachlist.forEach((Runestone s) => Chant(s))
.find(fn)First matching elementlist.find((Countstone n) => n > 10)
.sort()Return sorted copylist.sort()
.reverse()Return reversed copylist.reverse()
.flat()Flatten nested arrays[[1,2],[3]].flat()
.join(sep)Join into stringlist.join(", ")
.indexOf(val)Index of first occurrencelist.indexOf("x")
.slice(start, end)Extract sub-array (method)list.slice(1, 3)
Scroll heroes is ["Gandalf", "Frodo", "Aragorn"];
// for-each
Odyssey for hero in heroes is
Chant(hero);
end of Odyssey
// index-based
Odyssey for i from 0 to heroes.length() is
Chant(i + ": " + heroes[i]);
end of Odyssey
Chant("Frodo" in heroes); // Truth
Chant("Sauron" in heroes); // Falsehood

A Tome is a key-value dictionary. Keys can be strings, integers, or booleans.

Tome stats is {"str": 18, "dex": 14, "con": 16};
Tome config is {"host": "localhost", "port": 8080, "debug": Truth};
Chant(stats["str"]); // 18
Chant(config["host"]); // "localhost"

If the key is a valid identifier, you can use dot notation:

Chant(config.host); // "localhost"
Mutable Tome bag is {"gold": 100};
bag["gold"] is written as bag["gold"] + 50;
bag["gems"] is written as 3; // adds new key
MethodDescriptionExample
.keys()Array of all keysstats.keys()
.values()Array of all valuesstats.values()
.entries()Array of [key, value] pairsstats.entries()
.has(key)Check if key existsstats.has("str")
.length()Number of entriesstats.length()
.remove(key)Remove and return valuestats.remove("dex")
.merge(other)Return merged copystats.merge(extra)
Tome stats is {"str": 18, "dex": 14, "con": 16};
// iterate over entries
Odyssey for entry in stats is
Chant(entry); // prints ["str", 18], ["dex", 14], etc.
end of Odyssey
// iterate over keys
Odyssey for key in stats.keys() is
Chant(key + ": " + stats[key]);
end of Odyssey

The in operator checks keys:

Chant("str" in stats); // Truth
Chant("wis" in stats); // Falsehood

Scrolls and Tomes can be nested:

Scroll party is [
{"name": "Gandalf", "class": "Wizard", "level": 20},
{"name": "Aragorn", "class": "Ranger", "level": 15},
{"name": "Legolas", "class": "Archer", "level": 18}
];
Odyssey for member in party is
Chant(member["name"] + " the " + member["class"]);
end of Odyssey