HistoryCal - V0.0.7 Manual
Script Expressions

Home Development V0.0.7 Manual Script Expressions

Value Types

Expressions use the following value types.


A field is a integer value. The meaning will depend upon the context. It can represent an individual element for a date of any given calendar scheme. If the scheme is the Julian Day Number (jdn), then it represents a single day in the time continuum. It may also be used as a number without reference to any calendar.

A field may hold one of the following special values.

pastThe earliest possible day that can be represented.
futureThe last possible day that can be represented.
today The current day number (jdn) according to the underling operating system.
The value is substituted when the keyword is first encountered.
?An unknown or indeterminate value.


A range is expressed as two field values which denote the first and last values of an inclusive, continuous list. Both values may be the same, which implies a list of one value. A range is written using the '~' operator. (a ~ b) is identical to (b ~ a). When a range is output as a string, it is always written with the lower value first.

The 'past' and 'future' values are permitted in a range but not the '?' value.

A field may be promoted to a range with the upper and lower values the same.

A range may only be demoted to a field if both upper and lower values are the same.


An rlist is a list of ranges. The list may contain zero or more ranges. If the rlist has more than one range, it is written using the '|' union operator. When an rlist is output, it is always written as a well ordered list.

A range may be promoted to an rlist with a single range.

An rlist may only be demoted to a range if, after conversion to a well ordered list, it contains a single range.


This part of the specification is still under development.

A record is a specialist type to represent the separate fields of a particular calendar scheme. The scheme must be defined before the record can be used. The format of the record type is '{"sig", field_1, field_2, field_3}' where 'sig' is the scheme code and the field values are in record-default order. If not all field values are given, the '?' value is assumed for those missing. If the scheme code is missing, the default input scheme is assumed.


A string is sequence of unicode (UTF-8) characters. It is written by enclosing the text in double quotes '"'. If a double quote is required in the text, use two double quotes together. For example, the value "Say ""Hello""" would print as Say "Hello".


A boolean value holds one of the two keywords 'true' or 'false'.


An error value is created whenever an operation is carried which has undefined results. When written out, the value should print text that describes where and why the error was created.


A date type is not a separate type but a convenient way to refer to one of the three ways of holding a date, a field, a range or an rlist. A date value can promote or demote a value between these types as required. An error should only occur if a demotion is required but the value is such that it can not take place.

A date type strongly implies that the values are julian day numbers.


A number type is similar to a date type, but there is no implied promotion or demotion. Not all the field, range or rlist types may be valid in any given context.


Any text in the script which is not an operator, literal or keyword is termed a 'name'. How a name is treated depends upon the context, as detailed below.

Names are constructed from alphanumeric, '_' underline, ':' colon and '.' dot characters. They can not start with a numeric or dot character.

In some cases the script may accept either a name or a literal string.


Variables are created using the 'let' statement. They link a name to a value. Once created the 'let' statement name is optional as any known variable name (other than a statement name) is treated as a let statement.

The life of a variable is controlled by the 'mark' and 'clear' statements. It should be noted that unless directed otherwise, the variable names and values are remembered after a script has finished running. So subsequent scripts can use values calculated by previous ones.

Entity Codes

The vocab, grammar, scheme and format entities are identified by (normally short) codes. These codes are written as names or literal strings. The characters used in the codes can be any UTF-8 character except the ':' and '#' characters. (Note, other restrictions may be included in future versions.) If the code uses characters not permitted in a name, then it must be written as a literal string.


A Signature is the scheme and/or the format codes to be used when converting between different value types.

schemewrite string,j today;
scheme:formatwrite string,"g:dmy+" today;
:formatwrite string,:iso today;


Arithmetic Operations

-Unary minus- number_typenumber_type
+Plusnumber_type + number_typenumber_type
-Minusnumber_type - number_typenumber_type
*Multiplyfield_type * field_typefield_type
/Dividefield_type / field_typefield_type
modModulofield_type mod field_typefield_type

For field values, the arithmetic operators '+' (addition), '-' (subtraction, negation) and '*' (multiplication) retain their normal mathematical meanings, division '/' and 'mod' provides integer division and modulus (remainder). Note that the modulo operator always returns a positive value and that if q = a / b and r = a mod b then a = b * q + r.

Unary Minus

Unary minus has normal meaning of negation. The special field values are negated as follows,
-past = future
-future = past
-? = ?

Negation of a range negates both limits, so -(a ~ b) = (-b ~ -a).

Negation of an rlist negates all the ranges, but retains the well orderedness,
so -(a ~ b | c ~ d) = (-d ~ -c | -b ~ -a).

Addition and Subtraction

Normal addition is applied when both sides of an expression are field values. If one side of an expression is a range then the field value is added to both limits of the range. If both sides of an expression are ranges, then both the lower limits and the upper limits are added together. If one side of an expression is a rlist, then the other side is added to each range in turn. Adding two rlists together is currently an error.

Subtraction is carried out by adding the negated right hand side. So a - b = a + -b and (a ~ b) - (c ~ d) = (a ~ b) + (-d ~ -c).

field_type + field_type5 + 10
past + 123
past + future
range_type + field_type
field_type + range_type
(8 ~ 16) + 20
250 + (500 ~ future)
past + (500 ~ future)
28 ~ 36
750 ~ future
range_type + range_type(8 ~ 16) + (10 ~ 20)18 ~ 36
rlist_type + field_type
field_type + rlist_type
(8 ~ 16 | 20 ~ 50) + 20
250 + (past ~ 90 | 500 ~ future)
28 ~ 36 | 40 ~ 70
past ~ 340 | 750 ~ future
rlist_type + range_type
range_type + rlist_type
(8 ~ 16 | 20 ~ 50) + (10 ~ 20)
(10 ~ 25) + (past ~ 9 | 50 ~ future)
18 ~ 36 | 30 ~ 70
past ~ 34 | 60 ~ future
rlist_type + rlist_type(8 ~ 16 | 20 ~ 50) + (15 ~ 18 | 25 ~ 27)Error. Unable to add or subtract rlists.

String Concatenation

The '+' operator is also used to combine string values. If both sides of the operator are string values, the right hand side will be appended to the left hand. If just one side is a string value then the other is stringified first (see write Statement).

Multiplication, Division and Modulo

Currently, these operators can only be used with field types. The division and modulus operators are based on Euclidean integer division.

Multiplication of any number with the ? value is an error.
Multiplication of any number with past (except ? and future) results in past.
Multiplication of any number with future (except ? and past) results in future.
Multiplication of past and future values is an error.

For the division operator, the following conditions apply.
The divisor must be a non-zero integer value. Using the values 0, ?, past or future results in an error.
If the dividend is past and the divisor valid, the result is also past.
If the dividend is future and the divisor valid, the result is also future.
Division of the ? value results in an error.

For the modulo operator, the following conditions apply.
Use of ?, past or future on either side the operation results in an error.
Using zero on the right hand side of the operation results in an error.

field_type * field_type 5 * 10
past * 123
past * future
field_type / field_type 10 / 5
past / 123
123 / future
field_type mod field_type 21 mod 5
-100 mod -6
past mod 2

Range Operator

The binary range operator creates a range value which is just large enough to include both its operands.
The order of the operands is not significant.
The operands can be either field or range types.
The values past and future may be used.
Use of the ? value is an error.

5 ~ 105 ~ 10
123 ~ pastpast ~ 123
? ~ 40Error
(5 ~ 10) ~ 65 ~ 10
(5 ~ 10) ~ (15 ~ 8)5 ~ 15
48 ~ 25 ~ 5 ~ 105 ~ 48

Set Operators

The Complement '!' is a unary prefix operator which operates on a rlist. The Union '|', Intersection '&', Symmetric difference '^' and Relative complement '\' operators are binary. These operate on an rlist on left hand side and a range on right hand side. If necessary, range and field types are be promoted to an rlist or range.

Details of the set operators are found here.

Comparison Operators

The comparison operators must have operands of the same type, after type allowing for type promotion. The output is always a boolean type.

Any types, other than error types, can be compared for equality or non-equality.

Comparison operators that compare magnitude can only be used with field or string types. Use with any other type results in an error.

Boolean Operators

The unary prefix operator 'not' and the binary operators 'and' and 'or' can be used with boolean types only. Any other type will produce an error.

Subscript operators

The subscript operators allow access to to the various parts of the more complex value types.

Index operator

Used in the form Value[ Index ], the square brackets are used to enclose an index into left hand value.

If the value is a rlist type and the index a number (field), then the result is the range pointed to by the (zero based) index.

If the value is a record type and the index a number (field), then the result is the field pointed to by the (zero based) index.
If the index is a name or string value then this is treated as a field name, and the result is the corresponding value of the field.

If the value is a field, then it is assumed to be a date jdn. The index must be a name or string signature corresponding to a scheme and field name. If the scheme is not given, the default input scheme will be used. The result is the field value.

Note, the field names used may be the default record or the alias field name.

(8~16|20~50|75~99)[1]20 ~ 50
let i="g:Month"; 2432814[(i)]9

Property operator

A subscript starting with a dot '.' and followed by a name will return a property of the value, as listed below.

low(7~16)[.low]7Return the lower bound of a range or rlist.
high(7~16)[.high]16Return the upper bound of a range or rlist.
span(7~16)[.span]10Return the inclusive count between upper and lower bounds of a range or rlist.
size(21|79~88|100~120|200~210)[.size]4Return the number of elements in a rlist or record.
envelope(21|79~88|100~120|200~210)[.envelope]21 ~ 210Return the enclosing range af an rlist.
scheme{g,1948,9,19}[.scheme]"g"Return the scheme code string of a record.
type2432814[.type]"field"Return the type of value as a string.

The [.type] property returns one of the following strings: "field", "range", "rlist", "string", "record", "bool", "null". Since if the value is an error, the error will be propagated and so will not be detected.


A function first has to be defined using the function statement. It can then be called directly using the call statement, but in this case the function result (if one is assigned) is ignored. It can also be called in an expression using the at '@' operator.

function power(number, power) { result = 1; do while power > 0 result *= number; power -= 1; loop }
4 * @power(2,4) + 1074

Type Conversions

The cast operators are used to covert values between different types. If additional information is required (such as scheme and/or format) then a comma and signature can be added to the cast. If it is omitted, the system wide default values are assumed.

Date Cast

A date, in this context, means a rlist or an well ordered list of ranges. As well as an rlist, a date may be encapsulated as a text string or a record type. The date cast puts these two record types into a consistent rlist type. The rlist will then be demoted to a range or field if appropriate.

If the original type is a string, then the cast operation will need to know the scheme and format to be used, and so a signature can be added or else the system output default settings are used. Note the a string description of an rlist may include embedded range and union symbols, but this is different from a HistoryCal Date Expression. One significant difference is that only one scheme format can be used for the conversion.

date "19sep1948"2432814
date "1940~1950|1945~1955"2429630 ~ 2435473
date,g:iso "[1940..1950,1945..1955]"2429630 ~ 2435473
date {g, 1948, 9, 19}2432814
date {g, 1948}2432552 ~ 2432917
The above examples assume a system input setting of "g:dmy".

String Cast

The string cast operator converts a field, range, rlist or record type into a string type.

Except for a record type, the scheme code signature must be provided. If the format code is not included in the signature, then the default for that scheme is used.

string,"g:dmy+" 2432814"19 September 1948"
string,g 2429630~2435473"1940 ~ 1955"
string,g:iso 2429630 ~ 2429995 | 2433283 ~ 2435473"[1940,1950..1955]"
string {g, 1948, 9, 19}"19 Sep 1948"
string {j, 1948}"1948"
The above examples assume a system output setting of "g:dmy".

Record Cast

The record cast operator converts a field (as a julian day number) or string type into a record type. At this time, using the record cast with a range or rlist type is an error.

If a scheme:format signature is not given, then to cast a field the output default setting is used, whereas if casting a string, the input default is used.

Note that a record created from a cast always has all the fields included, even the optional ones. Only main fields are given values though. Records created using the record operator '{}' only include the given fields, even if this does not fully qualify a date.

record,g:dmy "19sep1948"{"g", 1948, 9, 19, ?}
record "21??"{"g", ?, ?, 21, ?}
record,:u "1948Year 9Month 19Day"{"g", 1948, 9, 19, ?}
record,j 2432814{"j", 1948, 9, 6, ?}
record 2429630~2435473Error
The above examples assume a system input and output setting of "g:dmy".

Error Cast

The error cast operator converts a string into an error. This can be used to signal an unusable value in a calculation or function.

The prefix "Error (" + line + "): " is added to message, where line is the line number of the the error cast operator.

error "Age is negative.""Error (10): Age is negative."
error ("Age of " + age + " is too high.")"Error (10): Age of 150 is too high."

Operator Precedence

Expressions are evaluated according to the operators detailed in the table. Operators with higher precedence are listed first. When precedence is equal, operations are carried out from left to right.

Operator List
()Parenthesized expression( expr )value
notLogical notnot bool_valuebool_value
-Unary minus-number_valuenumber_value
dateDate castdate,sig string_value
date record_value
recordRecord castrecord,sig string_value
record field_value
stringString caststring,sig date_value
string,sig record_value
errorError casterror string_valueerror_value
@name(expr, ...)
*Multiplyfield_value * field_valuefield_value
/Dividefield_value / field_valuefield_value
modModulofield_value mod field_valuefield_value
+Plus number_value + number_value
string_value + value
value + string_value
-Minusnumber_value - number_valuenumber_value
~Range field_value ~ field_value
range_value ~ range_value
|Unionrlist_value | range_valuerlist_value
&Intersectionrlist_value & range_valuerlist_value
^Symmetric differencerlist_value ^ range_valuerlist_value
\Relative complementrlist_value \ range_valuerlist_value
<Less thanvalue < valuebool_value
<=Less than or equalvalue <= valuebool_value
>Greater thanvalue > valuebool_value
>=Greater than or equalvalue >= valuebool_value
=Equalvalue = valuebool_value
<>Not equalvalue <> valuebool_value
andLogical andbool_value and bool_valuebool_value
orLogical inclusive orbool_value or bool_valuebool_value


The following is a complete list of keywords. These words cannot be used as variable names and, if used as signature codes, they must be enclosed in double quotes.

and, date, do, endif, else, elseif, error, false, future, if, invalid, loop, mod, not, or, past, record, string, today, true, until, while

The other statement words call, clear, end, format, function, grammar, let, mark, scheme, set, vocab, write, writeln are not strictly keyword, but must be used with care. If they are used as variable names then all assignments must start with 'let'.

Valid XHTML 1.0 Strict

21st October 2015