InnCant
InnCant
InnCant is a simple scripting language designed to express “favorite roll formulas” by specifying die rolls and basic operations and control structures to convert those rolls to meaningful results.
Overview
InnCant is a simple, basic subset of InnScript. It lacks the full rich object model that InnScript provides, the richer syntax, and complex runtime architecture. It is instead designed to represent “favorite rolls formulas” - roll dice, and optionally do something based on the results.
InnCant gets its name from “InnScript Cant”, where cant is “language peculiar to a specified group or profession” (with “Thieves Cant” being the most famous example). It also is a pun on the word incantation.
It is based on dice specifications - see Dice Specification
for full details of what is possible, but essentially common dice “formula” such as “3d6” will roll 3 six sided dice.
InnCant can also be extended to expose global variables and functions defined in InnScript, or other contextual information (for example, custom roll macros use fragments of InnCant, passing in the current and past die roll values as variables)
One important thing about InnCant is that it is designed to be free of side effects. The same InnCant fragment can be evaluated multiple times with no changes. This is especially true when using InnCant to roll dice - the result of one roll may determine what is rolled in the future, and so the results of the first roll shouldn’t change when the second roll happens. For example:
if 1d20 ≥ TARGET then "Hit for" & 1d8 else "Miss" end
When the d20 is rolled, it is compared against a target value, and if it equal to or higher, it rolls damage (a d8). But when we try to evaluate the damage, we don’t want
TARGET
to have changed and causing this to actually miss. By default, InnCant will prevent this. However, if you interact with external InnScript, there is a chance this might be an issue. If, for example, after being hit we increase the target value, this can cause a problem:if 1d20 ≥ TARGET then TARGET = TARGET + 5 "Hit for" & 1d8 else "Miss" end
Essentially, every time new dice are rolled, the fragment will be re-evaulated.
Values
InnCant is roughly based around JSON in terms of the values it can work with:
- Boolean values (true and false)
- Undefined values (null)
- Integers
- Lists of other values
- Frames, a mapping between a collection of constant strings and another value
For example:
%% This is a boolean
var FLAG = true
%% This is a string
var GREETING = "Hello"
%% This is an integer
var X = 10
%% This is a list (with a nested list)
var LIST = [1, "a", true, [3, 4]]
%% The first value in the list
LIST[1]
%% The last value in the list
LIST[-1]
%% This is a frame
var FRAME = {a: 10, b: 20, c: "The letter C"}
%% The value 10
FRAME.a
Note that unlike JavaScript, arrays are “1 based” (i.e, the first value is at index 1, not index 0). If a negative value is passed in, that returns the value relative to the end of the list, with -1 being the last element of the list. If the index is beyond either end of the list, the value null
is returned.
Also note that values are constant values (not references to mutable objects like JavaScript). As a result, you can not assign a new value to a list index or frame slot (you will need to create a new list/index).
Lists also support distributing values with scalar operators. For example:
var LIST = [1,2,3]
LIST + 1
Will create a new list with 1 added to every value in the list (i.e., [2,3,4]
).
Lists also support the following functions:
- list.count() : The number of elements in the list (as well as the number of characters in a string and the number of slots in a frame)
- list.sort() : A sorted list
- list.reversed() : The list reversed
- list.max() : The maximum value in the list
- list.min() : The minimum value in the list
- list.sum() : The sum total of the list
Expressions
InnCant support a variety of operators, including common and a few less common ones:
+
,-
: Addition, subtraction×
/*
,/
,%
: Multiplication, division, modulo (remainder)&
: Concatenate∧
//\
,∨
/\/
: minimum, maximum (which are also and/or for boolean values)==
,≠
/!=
,<
,≤
/<=
,>
,≥
/>=
: Equal, not equal, less than, less than or equal to, greater than, greater than or equal to
Technically, in InnCant (and InnScript) statements are also expressions, so it is possible to include statement constructs as part of an expression - this is most useful for if
-then
-else
statements that produce a value based on the branch evaluated. So, for example:
var TEST = true
var VALUE = (if TEST then 2d6 else 1d4 end)
VALUE + 3
Will result in whatever 2d6+3
rolls.
Statements
InnCant has basic statements for simple flow control, including conditional test and looping constructs (all of which are, as mentioned above, also valid expressions). These include:
var <NAME> = <value>
: Used to set a temporary variable to a valueif <test> then <truevalue> end
: If the ‘test’ expression is true, then the ‘truevalue’ expression is evaluated (otherwise the statement has the value ofnull
)if <test> then <truevalue> else <falsevalue> end
: If the ‘test’ expression is true, then the ‘truevalue’ expression is evaluated, otherwise the ‘falsevalue’ is evaluated.while <test> do <body> end
: If thetest
expression is true, execute thebody
expression. Repeat until thetest
expression returns false. The value of the statement is the last value thatbody
evaluated to (ornull
if it never was executed).repeat <body> until <test>
: Execute thebody
expression. Repeat doing this until thetest
expression is false. The value of the statement is the last value ofbody
for <NAME> = <initialvalue> [down] to <endvalue> [by <stepvalue>] do <body> end
: Set the variableNAME
to the initial value. So long as the initial value is less than or equal to theendvalue
(or greater than or equal to it ifdown
is included), execute thebody
expression. Each time, the value is incremented bystepvalue
(or 1 if not specified).for <NAME> in <expr> do <body> end
. Assumes thatexpr
is a list, we evaluatebody
for each element in the list, setting the variableNAME
to that value.