Value Types
Expressions use the following value types.
Field
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.
Keyword | Meaning |
---|---|
past | The earliest possible day that can be represented. |
future | The 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 wildcard value, currently only used for fields within records. |
Range
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. Currently the '~' character may also be used, but this may be removed in future versions. Note, (a .. b) is identical to (b .. a), but 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 '?' or '*' values.
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.
Rlist
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 between ranges. 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.
Record
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.
The record can only be compared for equality. When they are, wildcard '*' values are considered equal to unknown '?' values.
String
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".
Boolean
A boolean value holds one of the two keywords 'true' or 'false'.
Null
A null value holds the keyword 'null'.
Only the equal '=' and not equal '<>' operators may be used with null values. The equals operator returns 'true' if both values are null and 'false' if only one value is null.
Error
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.
Date
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.
Number
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.
Names
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 and ':' colon characters. They can not start with a numeric character.
In some cases the script may accept either a name, a literal string or a string expression in '()' parentheses.
Variables
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 lexicon, 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.
Signatures
A Signature is the scheme and/or the format codes to be used when converting between different value types.
Signature | Example |
---|---|
scheme | write string.j today; |
scheme:format | write string."g:dmy+" today; |
:format | write string.:iso today; |
Operators
Arithmetic Operations
Operator | Name | Use | Result |
---|---|---|---|
- | Unary minus | - number_type | number_type |
+ | Plus | number_type + number_type | number_type |
- | Minus | number_type - number_type | number_type |
* | Multiply | field_type * field_type | field_type |
/ | Divide | field_type / field_type | field_type |
mod | Modulo | field_type mod field_type | field_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).
Expression | Example | Result |
---|---|---|
field_type + field_type | 5 + 10 past + 123 past + future | 15 past Error |
range_type + field_type field_type + range_type |
(8 .. 16) + 20 250 + (500 .. future) past + (500 .. future) |
28 .. 36 750 .. future Error |
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 normal 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.
Division where both sides are record values results in the over mask' operation.
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.
Expression | Example | Result |
---|---|---|
field_type * field_type | 5 * 10 past * 123 past * future |
50 past Error |
field_type / field_type | 10 / 5 past / 123 123 / future |
2 past Error |
field_type mod field_type | 21 mod 5 -100 mod -6 past mod 2 |
1 2 Error |
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 ? or * value is an error.
Example | Result |
---|---|
5 .. 10 | 5 .. 10 |
123 .. past | past .. 123 |
? .. 40 | Error |
(5 .. 10) .. 6 | 5 .. 10 |
(5 .. 10) .. (15 .. 8) | 5 .. 15 |
48 .. 25 .. 5 .. 10 | 5 .. 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.
Over Masking Operator
The masking operator 'over' is implemented for record types only and overloads the '/' operator. It allow one record value to form a mask with wildcard values. For a / b any wildcard field values in a are replaced with the corresponding field value from b.
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.
Example | Result |
---|---|
(8..16|20..50|75..99)[1] | 20 .. 50 |
{g,1948,9,19}[0] | 1948 |
{g,1948,9,19,?}[wday] | ? |
2432814[g:wday] | 7 |
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.
Property | Use | Result | Comment |
---|---|---|---|
low | (7..16)[.low] | 7 | Return the lower bound of a range or rlist. |
high | (7..16)[.high] | 16 | Return the upper bound of a range or rlist. |
span | (7..16)[.span] | 10 | Return the inclusive count between upper and lower bounds of a range or rlist. |
span | (21|79..88|100..120|200..210)[.span] | 190 | |
size | (21|79..88|100..120|200..210)[.size] | 4 | Return the number of elements in a rlist or record. |
size | {g,1948,9,19}[.size] | 3 | |
size | "Hello"[.size] | 5 | Return the number of charaters in a string. May not match the number of unicode code points. |
envelope | (21|79..88|100..120|200..210)[.envelope] | 21 .. 210 | Return the enclosing range af an rlist. |
scheme | {g,1948,9,19}[.scheme] | "g" | Return the scheme code string of a record. |
type | 2432814[.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.
Functions
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.
Example | Result |
---|---|
function power(number, power) { result = 1; do while power > 0 result *= number; power -= 1; loop } | |
4 * @power(2,4) + 10 | 74 |
Built-in Functions
There are a number of built-in functions that are always available.
@if( comp, t, f ) Built-in Function
The @if function behaves similar to the C language ? : tertiary operator. If the comp argument is true the result is the t argument, otherwise the result is the f argument. The function is equivalent to the following statement.
function if( comp, t, f ) { if comp result = t; else result = f; endif } |
@read( prompt = "" ) Built-in Function
The current version is limited to writing and reading from the standard in/out streams.
If given, the prompt is written to standard out. Then the standard is stream is read up to the next new line.
The result of the function is always a string value. Use the convert cast to read a field value.
@phrase( date_phrase_text ) Built-in Function
The phrase function takes a date phrase and results in a date field, range or rlist.
Type Conversions
The cast operators are used to convert values between different types. If additional information is required (such as scheme and/or format) then a dot and signature can be added to the cast. If it is omitted, the system wide default values are assumed.
Cast date
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.
Example | Result |
---|---|
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". |
Cast string
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.
Example | Result |
---|---|
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". |
Cast record
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.
Example | Result |
---|---|
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..2435473 | Error |
The above examples assume a system input and output setting of "g:dmy". |
Cast convert (lexicons)
The convert cast converts between a string and a field. If a lexicon code is given with the cast then that lexicon is used to provide the conversion. Using the cast on any type other than a field or string results in an error.
Example | Result |
---|---|
convert.w 7 | "Sunday" |
convert.ws:a 7 | "Sat" |
convert.w "wed" | 3 |
convert "10" | 10 |
convert 10 | "10" |
Cast error
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.
Example | Result |
---|---|
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 | |||
---|---|---|---|
Operator | Name | Use | Result |
() | Parenthesized expression | ( expr ) | value |
{} | Record | {sig,field_value,...} | record_value |
! | Complement | !rlist_value | rlist_value |
not | Logical not | not bool_value | bool_value |
- | Unary minus | -number_value | number_value |
date | Date cast | date.sig string_value date record_value | date_value |
record | Record cast | record.sig string_value record field_value | record_value |
string | String cast | string.sig date_value string.sig record_value | string_value |
convert | Lexicon cast | convert.sig field_value convert.sig string_value | string_value field_value |
error | Error cast | error string_value | error_value |
@ | Function | @name @name(expr, ...) | value |
[] | Subscript | date_value[string_value] | field_value |
[.name] | Property | value[.name] | value |
* | Multiply | field_value * field_value | field_value |
/ | Divide Over mask |
field_value / field_value record_value / record_value |
field_value record_value |
mod | Modulo | field_value mod field_value | field_value |
+ | Plus | number_value + number_value string_value + value value + string_value |
number_value string_value string_value |
- | Minus | number_value - number_value | number_value |
.. | Range | field_value .. field_value range_value .. range_value |
range_value range_value |
| | Union | rlist_value | range_value | rlist_value |
& | Intersection | rlist_value & range_value | rlist_value |
^ | Symmetric difference | rlist_value ^ range_value | rlist_value |
\ | Relative complement | rlist_value \ range_value | rlist_value |
< | Less than | value < value | bool_value |
<= | Less than or equal | value <= value | bool_value |
> | Greater than | value > value | bool_value |
>= | Greater than or equal | value >= value | bool_value |
= | Equal | value = value | bool_value |
<> | Not equal | value <> value | bool_value |
and | Logical and | bool_value and bool_value | bool_value |
or | Logical inclusive or | bool_value or bool_value | bool_value |
Keywords
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, convert, date, do, endif, else, elseif, error, false, future, if, invalid, loop, mod, not, null, or, past, record, string, today, true, until, while
The other statement words call, clear, end, format, function, grammar, let, mark, scheme, set, lexicon, 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'.