NAME
Muldis::D::Dialect::PTMD_STD - How to format Plain Text Muldis D
VERSION
This document is Muldis::D::Dialect::PTMD_STD version 0.92.0.
PREFACE
This document is part of the Muldis D language specification, whose root document is Muldis::D; you should read that root document before you read this one, which provides subservient details.
DESCRIPTION
This document outlines the grammar of the Plain Text Muldis D dialect named PTMD_STD
. The fully-qualified name of this Muldis D dialect, in combination with the base language spec it is bundled with, is Muldis_D:"http://muldis.com":"N.N.N":PTMD_STD
(when the bundled base language version is substituted for the N.N.N
).
This dialect is designed to exactly match the Muldis D system catalog (the possible representation of Muldis D code that is visible to or updateable by Muldis D programs at runtime) as to what non-critical meta-data it explicitly stores; so code in the PTMD_STD
dialect should be round-trippable with the system catalog with the result maintaining all the details that were started with. Since it matches the system catalog, this dialect should be able to exactly represent all possible Muldis D base language code (and probably all extensions too), rather than a subset of it. That said, the PTMD_STD
dialect does provide a choice of multiple syntax options for writing Muldis D value literals and DBMS entity (eg type and routine) declarations, so several very distinct PTMD_STD
code artifacts may parse into the same system catalog entries. There is even a considerable level of abstraction in some cases, so that it is easier for programmers to write and understand typical PTMD_STD
code, and so that this code isn't absurdly verbose.
This dialect is designed to be as small as possible while meeting the above criteria, and is designed such that a parser that handles all of this dialect can be fairly small and simple. Likewise, a code generator for this dialect from the system catalog can be fairly small and simple.
A significant quality of the PTMD_STD
dialect is that it is designed to work easily for a single-pass parser, or at least a single-pass lexer; all the context that one needs to know for how to parse or lex any arbitrary substring of code is provided by prior code, or any required lookahead is just by a few characters in general. Therefore, a PTMD_STD
parser can easily work on a streaming input like a file-handle where you can't go back earlier in the stream. Often this means a parser can work with little RAM.
Also the dialect is designed that any amount of whitespace can be added or omitted next to most non-alphanumeric characters (which happen to be next to alphanumeric tokens) without that affecting the meaning of the code at all, except obviously for within character string literals. And long binary or character or numeric or identifier strings can be split into arbitrary-size substrings, without affecting the meaning. And many elements are identified by name rather than ordinal position, so to some degree the order they appear has no effect on the meaning. So programmers can easily format (separate, indent, linewrap, order) code how they like, and making an automated code reformatter shouldn't be difficult. Often, named elements can also be omitted entirely for brevity, in which case the parser would use context to supply default values for those elements.
Given that plain text is (more or less) universally unambiguously portable between all general purpose languages that could be used to implement a DBMS, it is expected that every single Muldis D implementation will natively accept input in the PTMD_STD
dialect, which isn't dependent on any specific host language and should be easy enough to process, so it should be considered the safest official Muldis D dialect to write in by default, when you don't have a specific reason to use some other dialect.
See also the dialects HDMD_Perl6_STD and HDMD_Perl5_STD, which are derived directly from PTMD_STD
, and represent possible Perl 6 and 5 concrete syntax trees for it; in fact, most of the details in common with those other dialects are described just in the current file, for all 3 dialects.
GENERAL STRUCTURE
A PTMD_STD
Muldis D code file consists just of a Muldis D depot definition, which begins with a language name declaration, and then has a Database
value literal defining the depot's catalog, and finally has, optionally, a Database
value literal defining the depot's data. This is conceptually what a PTMD_STD
file is, and it can even be that literally, but PTMD_STD
provides a canonical further abstraction for defining the depot's catalog, which should be used when doing data-definition. And so you typically use syntax resembling routine and type declarations in a general purpose programming language, where simply declaring such an entity will cause it to be part of the system catalog. Fundamentally every Muldis D depot is akin to a code library, and a Muldis D "main program" is nothing more than a depot having a procedure that is designated to execute automatically after a mount event of its host depot.
As a special extension feature, a PTMD_STD
Muldis D code file may alternately consist just of a (language-qualified) Muldis D value literal, which mainly is intended for use in mixed-language environments as an interchange format for data values between Muldis D and other languages.
The grammar in this file is formatted as a hybrid between various BNF flavors and Perl 6 rules (see http://perlcabal.org/syn/S05.html for details on the latter) with further changes. It is only meant to be illustrative and human readable, and would need significant changes to actually be a functional parser, which are different for each parser toolkit.
The grammar consists mainly of named tokens which define matching rules. Loosely speaking, each parser match of a token corresponds to a capture node or node element in the concrete syntax tree resulting from the parse; in practice, the parser may make various alterations to the match when generating a node, such as adding guide keywords corresponding to the token name, or by merging series of trivial tokens or doing escaped character substitutions. No explicit capture syntax such as parenthesis is used in the grammar.
To help understand the grammar in this file, here are a few guidelines: 1. The grammar is exactly the same as that of a Perl 6 rule except where these guidelines state otherwise; this includes that square brackets mean grouping not optionality, and that when multiple sub-pattern alternatives match, the one that is the longest wins. 2. The grammar portion that actually declares a token, that is what associates a token name with its definition body, is formatted like EBNF, as <footok> ::= ...
rather than the Perl 6 way like token footok { ... }
or rule footok { ... }
. 3. All non-quoted whitespace is not significant and just is formatting the grammar itself; rather, whitespace rules in the grammar are spelled out explicitly such as with \s*
(optional whitespace) and \s+
(mandatory whitespace).
The root grammar token for the entire dialect is Muldis_D
.
START
Grammar:
<Muldis_D> ::=
<language_name> \s+ [<value> | <depot>]
A Muldis_D
node has 2 ordered elements where the first element is a language_name
node and the second element is either a value
node or a depot
node.
See the pod sections in this file named "LANGUAGE NAME", "VALUE LITERALS AND SELECTORS", and "DEPOT DECLARATION", for more details about the aforementioned tokens/nodes.
When Muldis D is being compiled and invoked piecemeal, such as because the Muldis D implementing virtual machine (VM) is attached to an interactive user terminal, or the VM is embedded in a host language where code in the host language invokes Muldis D code at various times, many value
may be fed to the VM directly for inter-language exchange, and not every one would then have its own language_name
. Usually a language_name
would be supplied to the Muldis D VM just once as a VM configuration step, which provides a context for further interaction with the VM that just involves Muldis D code that isn't itself qualified with a language_name
.
LANGUAGE NAME
Grammar:
<language_name> ::=
<ln_base_name>
\s* ':' \s* <ln_base_authority>
\s* ':' \s* <ln_base_version_number>
\s* ':' \s* <ln_dialect>
[\s* ':' \s* <ln_extensions>]?
<ln_base_name> ::=
Muldis_D
<ln_base_authority> ::=
<Name_payload>
<ln_base_version_number> ::=
<Name_payload>
<ln_dialect> ::=
PTMD_STD
<ln_extensions> ::=
<Tuple_payload>
As per the VERSIONING pod section of Muldis::D, code written in Muldis D must start by declaring the fully-qualified Muldis D language name it is written in. The PTMD_STD
dialect formats this name as a language_name
node having 4-5 ordered elements:
ln_base_name
-
This is the Muldis D language base name; it is simply the bareword character string
Muldis_D
. -
This is the base authority; it is a character string formatted as per a specific-context
Name
value literal; it is typically the delimited character stringhttp://muldis.com
. ln_base_version_number
-
This is the base version number; it is a character string formatted as per
ln_base_authority
; it is typically a character string like1.2.3
. ln_dialect
-
This is the dialect name; it is simply the bareword character string
PTMD_STD
. ln_extensions
-
Optional; this is a set of chosen pragma/parser-config options as per a
Tuple
SCVL; see the "MULDIS D STANDARD DIALECT PRAGMAS" pod section for more details.
Examples:
Muldis_D:"http://muldis.com":"1.2.3":PTMD_STD
Muldis_D:"http://muldis.com":"1.2.3":PTMD_STD:{
with_rtn_inv_alt_syn => true
}
VALUE LITERALS AND SELECTORS
Grammar:
<value> ::=
<opaque_value_literal>
| <coll_value_selector>
<opaque_value_literal> ::=
<Bool>
| <Order>
| <RatRoundMeth>
| <Int>
| <Rat>
| <Blob>
| <Text>
| <Name>
| <NameChain>
| <DeclNameChain>
| <Comment>
| <Instant>
| <Duration>
| <UTCInstant>
| <FloatInstant>
| <UTCDuration>
| <RatRoundRule>
| <String>
<coll_value_selector> ::=
<Scalar>
| <Tuple>
| <Database>
| <Relation>
| <Set>
| <Maybe>
| <Array>
| <Bag>
A value
node is a Muldis D value literal, which is a common special case of a Muldis D value selector.
Unlike value selectors in general, which must be composed beneath a depot
because they actually represent a Muldis D value expression tree of a function or updater or type definition, a value
node does not represent an expression tree, but rather a value constant; by definition, a value
can be completely evaluated at compile time. A Muldis_D
node with a value
second element is hence just a serialized Muldis D value.
The PTMD_STD grammar subsection for value literals (having the root grammar token value
) is completely self-defined and can be used in isolation from the wider grammar as a Muldis D sub-language; for example, a hosted-data Muldis D implementation may have an object representing a Muldis D value, which is initialized using code written in that sub-language.
Every grammar token, and corresponding capture node, representing a Muldis D value literal is similarly formatted and has 1-3 elements; the following pod section "Value Literal Common Elements" describes the similarities once for all of them, in terms of an alternate value
token definition which is called x_value
. And then the other pod sections specific to each kind of value literal then just focus on describing their unique aspects, namely their payloads.
An opaque_value_literal
node represents a conceptually opaque Muldis D value, such that every one of these values is defined with its own literal syntax that is compact and doesn't look like a collection of other nodes; this includes the basic numeric and string literals.
A coll_value_selector
node represents a conceptually transparent Muldis D value, such that every one of these values is defined visibly in terms of a collection of other nodes; this includes the basic tuple and relation selectors.
Value Literal Common Elements
A generic context value literal (or GCVL) is a value literal that can be properly interpreted in a context that is expecting a value but has no expectation that said value belongs to a specific data type; in the general case, a GCVL includes explicit value kind meta-data (such as, "this is an Int
" or "this is a Name
"); but with a few specific data types (see the value_kind
node description for details) that meta-data may be omitted for brevity because the main literal has mutually uniquely identifying characteristics. For example, each element of a generic Muldis D collection value, such as a member of an array or tuple, could potentially have any type at all. In contrast, a specific context value literal (or SCVL) is a value literal that does not include explicit value kind meta-data, even when the main literal doesn't have uniquely identifying characteristics, because the context of its use supplies said meta-data. For example, in a tuple value literal it is assumed that a value literal in an attribute name position must denote a Name
. The grammar token value
|x_value
denotes a GCVL, as do most short-named grammar tokens, like Int
or Name
; in contrast, a grammar token containing payload
denotes a SCVL, like Int_payload
or Name_payload
.
Every GCVL has 1-3 elements, illustrated by this grammar:
<x_value> ::=
[
<value_kind> \s* ':' \s*
[<type_name> \s* ':' \s*]?
]?
<payload>
<value_kind> ::=
Bool
| Order
| RatRoundMeth
| Int | NNInt | PInt
| Rat | NNRat | PRat
| Blob | OctetBlob
| Text
| Name
| NameChain
| DeclNameChain
| Comment
| Instant
| Duration
| UTC [Instant | DateTime | Date | Time]
| Float [Instant | DateTime | Date | Time]
| UTCDuration
| RatRoundRule
| String | BString | OString | UCPString
| DH? Scalar
| DH? Tuple
| Database
| DH? Relation
| DH? Set
| DH? [Maybe | Single]
| DH? Array
| DH? Bag
<type_name> ::=
<NameChain_payload>
<payload> ::=
<Bool_payload>
| <Order_payload>
| <RatRoundMeth_payload>
| <Int_payload>
| <Rat_payload>
| <Blob_payload>
| <Text_payload>
| <Name_payload>
| <NameChain_payload>
| <DeclNameChain_payload>
| <Comment_payload>
| <Instant_payload>
| <Duration_payload>
| <UTCInstant_payload>
| <FloatInstant_payload>
| <UTCDuration_payload>
| <RatRoundRule_payload>
| <String_payload>
| <Scalar_payload>
| <Tuple_payload>
| <Database_payload>
| <Relation_payload>
| <Set_payload>
| <Maybe_payload>
| <Array_payload>
| <Bag_payload>
So a x_value
|value
node has 1-3 elements in general:
value_kind
-
This is a character string of the format
<[A..Z]> <[ a..z A..Z ]>+
; it identifies the data type of the value literal in broad terms and is the only external meta-data ofpayload
generally necessary to interpret the latter; what grammars are valid forpayload
depend just onvalue_kind
.For all values of just the 7 data types [
Bool
,Order
,RatRoundMeth
,Int
,Rat
,Blob
,Text
], thevalue_kind
portion of a GCVL may be omitted for brevity, but the code parser should still be able to infer it easily by examining the first few characters of thepayload
, which for each of said 7 data types has a mutually uniquely identifying format, which is also distinct from all possiblevalue_kind
. Note that omission ofvalue_kind
is only allowed when the GCVL doesn't include atype_name
element.Note that, by the above criteria, the
Comment
type could easily have been an 8th member of the list; however, it is excluded on purpose because in practice Muldis D code can contain SCVLComment
literals almost anywhere as meta-data for that code, and so having an explicitvalue_kind
ofComment
means that that particularComment
literal is to be treated as ordinary data like with any GCVL.For just these certain special values of other data types, the same option of omitting the
value_kind
(andtype_name
) applies:Tuple:d0
,Relation:d0c0
,Relation:d0c1
,Maybe:nothing
. type_name
-
This is a Muldis D data type name, for example
sys.std.Core.Type.Int
; it identifies a specific subtype of the generic type denoted byvalue_kind
, and serves as an assertion that the Muldis D value denoted bypayload
is a member of the named subtype. Iffvalue_kind
is[|DH]Scalar
thentype_name
is mandatory; otherwise,type_name
is optional for allvalue
, except thattype_name
must be omitted whenvalue_kind
is one of the 2 [Bool
,Order
]; this isn't because those 2 types can't be subtyped, but because in practice doing so isn't useful.How a Muldis D parser treats a
value
node with atype_name
element depends on the wider context. In the general case where thevalue
is anexpr
beneath the context of adepot
node, thevalue
is treated as if it had an extra parentfunc_invo
node that invokes thetreated
function and whose 2 argument nodes are as follows:topic
gets thevalue
without thetype_name
element, andas
gets thetype_name
element. This means that in general thetype_name
assertion is done at runtime. In the common special case where bothvalue
is anopaque_value_literal
andtype_name
refers to a system-defined type, then thetype_name
assertion is done at compile time, and then thetype_name
element is simply eliminated, so thevalue
ends up simply as itself with no newfunc_invo
parent. payload
-
This is mandatory for all
value
.
For GCVL and SCVL examples, see the subsequent documentation sections.
OPAQUE VALUE LITERALS
Boolean Literals
Grammar:
<Bool> ::=
[Bool \s* ':' \s*]?
<Bool_payload>
<Bool_payload> ::=
false | ⊥ | true | ⊤
A Bool
node represents a logical boolean value. It is interpreted as a Muldis D sys.std.Core.Type.Bool
value as follows: The Bool_payload
is a bareword character string formatted as per a Name
SCVL, and it maps directly to the name
possrep of the Bool
type.
Examples:
Bool:true
false
⊤
⊥
Order-Determination Literals
Grammar:
<Order> ::=
[Order \s* ':' \s*]?
<Order_payload>
<Order_payload> ::=
increase | same | decrease
An Order
node represents an order-determination. It is interpreted as a Muldis D sys.std.Core.Type.Cat.Order
value as follows: The Order_payload
is a bareword character string formatted as per a Name
SCVL, and it maps directly to the name
possrep of the Order
type.
Examples:
Order:same
decrease
Rational Rounding Method Literals
Grammar:
<RatRoundMeth> ::=
[
RatRoundMeth \s* ':' \s*
[<type_name> \s* ':' \s*]?
]?
<RatRoundMeth_payload>
<RatRoundMeth_payload> ::=
half_down | half_up
| half_even
| to_floor | to_ceiling
| to_zero | to_inf
A RatRoundMeth
node represents a rational rounding method. It is interpreted as a Muldis D sys.std.Core.Type.Cat.RatRoundMeth
value as follows: The RatRoundMeth_payload
is a bareword character string formatted as per a Name
SCVL, and it maps directly to the only possrep of the RatRoundMeth
type.
Examples:
RatRoundMeth:half_up
to_zero
General Purpose Integer Numeric Literals
Grammar:
<Int> ::=
[
[Int | NNInt | PInt] \s* ':' \s*
[<type_name> \s* ':' \s*]?
]?
<Int_payload>
<Int_payload> ::=
<num_max_col_val> \s* ';' \s* <int_body>
| <d_int_body>
<num_max_col_val> ::=
<pint_head>
<int_body> ::=
[0 | \-?<pint_body>]
<nnint_body> ::=
[0 | <pint_body>]
<pint_body> ::=
<pint_head> <pint_tail>?
<pint_head> ::=
<[ 1..9 A..Z ]>
<pint_tail> ::=
[[_?<[ 0..9 A..Z ]>+]+] ** [\s* '~' \s*]
<d_int_body> ::=
[0 | \-?<d_pint_body>]
<d_nnint_body> ::=
[0 | <d_pint_body>]
<d_pint_body> ::=
<d_pint_head> <d_pint_tail>?
<d_pint_head> ::=
<[ 1..9 ]>
<d_pint_tail> ::=
[[_?<[ 0..9 ]>+]+] ** [\s* '~' \s*]
An Int
node represents an integer numeric value. It is interpreted as a Muldis D sys.std.Core.Type.Int
value as follows:
If the Int_payload
is composed of a num_max_col_val
plus int_body
, then the int_body
is interpreted as a base-N integer where N might be between 2 and 36, and the num_max_col_val
says which possible value of N to use. Assuming all int_body
column values are between zero and N-minus-one, the num_max_col_val
contains that N-minus-one. So to specify, eg, bases [2,8,10,16], use num_max_col_val
of [1,7,9,F].
If the Int_payload
is a d_int_body
, then it is interpreted as a base 10 integer.
Fundamentally the body part of an Int
node consists of a string of digits and uppercased (but not lowercased) letters, where each digit (0..9
) represents its own number and each letter (A..Z
) represents a number in [10..35]. A body may optionally contain underscore characters (_
), which exist just to help with visual formatting, such as for 10_000_000
, and these are ignored/stripped by the parser. A body may optionally be split into 1..N segments where each segment is separated by a tilde token (~
); this segmenting ability is provided to support code that contains very long numeric literals while still being well formatted (no extra long lines); the tilde tokens are also ignored/stripped by the parser, and the body is interpreted as if all its alphanumeric characters were contiguous.
If the value_kind
of a value
node is NNInt
or PInt
rather than Int
, then the value
node is interpreted simply as an Int
node whose type_name
is NNInt
or PInt
, and the allowed body is appropriately further restricted.
Examples:
Int:1;11001001 # binary #
7;0 # octal #
7;644 # octal #
-34 # decimal #
42 # decimal #
F;DEADBEEF # hexadecimal #
Z;-HELLOWORLD # base-36 #
3;301 # base-4 #
B;A09B # base-12 #
General Purpose Rational Numeric Literals
Grammar:
<Rat> ::=
[
[Rat | NNRat | PRat] \s* ':' \s*
[<type_name> \s* ':' \s*]?
]?
<Rat_payload>
<Rat_payload> ::=
<num_max_col_val> \s* ';' \s* <rat_body>
| <d_rat_body>
<rat_body> ::=
<int_body>\.<pint_tail>
| <int_body> \s* \/ \s* <pint_body>
| <int_body> \s* \* \s* <pint_body> \s* \^ \s* <int_body>
<d_rat_body> ::=
<d_int_body>\.<d_pint_tail>
| <d_int_body> \s* \/ \s* <d_pint_body>
| <d_int_body> \s* \* \s* <d_pint_body> \s* \^ \s* <d_int_body>
A Rat
node represents a rational numeric value. It is interpreted as a Muldis D sys.std.Core.Type.Rat
value as follows:
Fundamentally a Rat
node is formatted and interpreted like an Int
node, and any similarities won't be repeated here. The differences of interpreting a Rat_payload
being composed of a num_max_col_val
plus rat_body
versus the Rat_payload
being a d_rat_body
are as per the corresponding differences of interpreting an Int_payload
. Also interpreting a NNRat
or PRat
is as per a NNInt
or PInt
.
If the body part of a Rat
node contains a radix point (.
), then it is interpreted as is usual for a programming language with such a literal.
If the body part of a Rat
node contains a solidus (/
), then the rational's value is interpreted as the leading integer (a numerator) divided by the trailing positive integer (a denominator); that is, the two integers collectively map to the ratio
possrep of the Rat
type.
If the body part of a Rat
node contains a asterisk (*
) plus a circumflex accent (^
), then the rational's value is interpreted as the leading integer (a mantissa) multiplied by the result of the middle positive integer (a radix) taken to the power of the trailing integer (an exponent); that is, the three integers collectively map to the float
possrep of the Rat
type.
Examples:
Rat:1;-1.1
-1.5 # same val as prev #
3.14159
A;0.0
F;DEADBEEF.FACE
Z;0.000AZE
Rat:6;500001/1000
B;A09B/A
Rat:1;1011101101*10^-11011
45207196*10^37
1/43
314159*10^-5
General Purpose Binary String Literals
Grammar:
<Blob> ::=
[
[Blob | OctetBlob] \s* ':' \s*
[<type_name> \s* ':' \s*]?
]?
<Blob_payload>
<Blob_payload> ::=
<blob_max_col_val> \s* ';' \s* <blob_body>
<blob_max_col_val> ::=
<[137F]>
<blob_body> ::=
[
<[']>
<[ 0..9 A..F ]>*
<[']>
] ** [\s* '~' \s*]
A Blob
node represents a general purpose bit string. It is interpreted as a Muldis D sys.std.Core.Type.Blob
value as follows: Fundamentally the body part of a Blob
node consists of a delimited string of digits and uppercased (but not lowercased) letters, where each digit (0..9
) represents its own number and each letter (A..F
) represents a number in [10..15]; this string is qualified with a blob_max_col_val
character ([137F]
), similarly to how an int_body
is qualified by a num_max_col_val
. Each character of the delimited string specifies a sequence of one of [1,2,3,4] bits, depending on whether blob_max_col_val
is [1,3,7,F]. If the value_kind
of a value
node is OctetBlob
rather than Blob
, then the value
node is interpreted simply as an Blob
node whose type_name
is OctetBlob
, and the delimited string is appropriately further restricted.
Examples:
Blob:1;'00101110100010' # binary #
3;''
F;'A705E' # hexadecimal #
7;'523504376'
General Purpose Character String Literals
Grammar:
<Text> ::=
[
Text \s* ':' \s*
[<type_name> \s* ':' \s*]?
]?
<Text_payload>
<Text_payload> ::=
[
<[']>
[<-[\\\'\t\n\f\r]> | <escaped_char>]*
<[']>
] ** [\s* '~' \s*]
<escaped_char> ::=
'\b' | '\a' | '\q' | '\h'
| '\s'
| '\t' | '\n' | '\f' | '\r'
| '\c<' [
[<[ A..Z ]>+] ** ' '
| [0 | <[ 1..9 ]> <[ 0..9 ]>*]
| <[ 1..9 A..Z ]> ';' [0 | <[ 1..9 A..Z ]> <[ 0..9 A..Z ]>*]
] '>'
A Text
node represents a general purpose character string. It is interpreted as a Muldis D sys.std.Core.Type.Text
value as follows:
The Text_payload
is interpreted generally as is usual for a programming language with such a delimited character string literal.
A Text_payload
may optionally be split into 1..N segments where each segment is delimited by apostrophes/single-quotes ('
) and separated by a tilde token (~
); this segmenting ability is provided to support code that contains long string literals while still being well formatted (no extra long lines); the tilde tokens and adjoining string delimiters are ignored/stripped by the parser, and the Text_payload
is interpreted as if it just consisted of a single delimited string.
All Muldis D delimited character string literals (generally the 3 Text
, Name
, Comment
) may contain some characters denoted with escape sequences rather than literally. The Muldis D parser would substitute the escape sequences with the characters they represent, so the resulting character string values don't contain those escape sequences. Currently there are 2 classes of escape sequences, called simple and complex.
The meanings of the simple escape sequences are:
Esc | Unicode | Unicode | Chr | Literal character used
Seq | Codepoint | Character Name | Lit | for when not escaped
----+-----------+-----------------+-----+------------------------------
\b | F;5C | REVERSE SOLIDUS | \ | esc seq lead (aka backslash)
\a | F;27 | APOSTROPHE | ' | delim Text literals
\q | F;22 | QUOTATION MARK | " | delim quoted Name literals
\h | F;23 | NUMBER SIGN | # | delim Comment lit (aka hash)
\s | F;20 | SPACE | | space char
\t | F;9 | CHAR... TAB... | | control char horizontal tab
\n | F;A | LINE FEED (LF) | | ctrl char line feed / newline
\f | F;C | FORM FEED (FF) | | control char form feed
\r | F;D | CARR. RET. (CR) | | control char carriage return
One design decision of PTMD_STD that is distinct from typical other languages is that an escape sequence for any character used as a delimiter never contains that literal character. For example, while in SQL or Perl character strings delimited by '
, they typically escape literal apostrophes/single-quotes as ''
or \'
; while this is unambiguous, the task of parsing such code is considerably more difficult than it could be. In contrast, while in PTMD_STD character strings delimited by '
, a literal of the same is escaped with \a
; so parsing such code is an order of magnitude easier because the parser doesn't have to understand the internals of any character string literal in order to separate out the character string from its surrounding code.
Another design decision of PTMD_STD that is distinct at least from Perl is that non-"space" whitespace characters in character string literals must never appear literally, but must instead be denoted with escape sequences. The main reason for this is to ensure that the actual values being selected by the string literals were not variable per the kind of linebreaks used to format the Muldis D source code itself.
There is currently just one complex escape sequence, of the format \c<...>
, that supports specifying characters in terms of their Unicode abstract codepoint name or number. If the ...
consists of just uppercased (not lowercased) letters and the space character, then the ...
is interpreted as a Unicode character name. If the ...
looks like an Int_payload
, sans that underscores and tilde segmentation aren't allowed here, then the ...
is interpreted as a Unicode abstract codepoint number. One reason for this feature is to empower more elegant passing of Unicode-savvy PTMD_STD source code through a communications channel that is more limited, such as to 7-bit ASCII.
Examples:
Text:'Ceres'
'サンプル'
''
'Perl'
'\c<LATIN SMALL LETTER OU>\c<F;263A>\c<65>'
DBMS Entity Name Literals
Grammar:
<Name> ::=
Name \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Name_payload>
<Name_payload> ::=
<nonquoted_name_str> | <quoted_name_str>
<nonquoted_name_str> ::=
<[ a..z A..Z _ ]><[ a..z A..Z 0..9 _ - ]>*
<quoted_name_str> ::=
[
<["]>
[<-[\\\"\t\n\f\r]> | <escaped_char>]*
<["]>
] ** [\s* '~' \s*]
<NameChain> ::=
NameChain \s* ':' \s*
[<type_name> \s* ':' \s*]?
<NameChain_payload>
<NameChain_payload> ::=
['.' \s*]? <Name_payload> ** [\s* '.' \s*]
<DeclNameChain> ::=
DeclNameChain \s* ':' \s*
[<type_name> \s* ':' \s*]?
<DeclNameChain_payload>
<DeclNameChain_payload> ::=
<NameChain_payload> | '[]'
A Name
node represents a canonical short name for any kind of DBMS entity when declaring it; it is a character string type, that is disjoint from Text
. It is interpreted as a Muldis D sys.std.Core.Type.Cat.Name
value as follows:
Fundamentally a Name
node is formatted and interpreted like a Text
node, and any similarities won't be repeated here. Unlike a Text_payload
literal which must always be delimited, a Name_payload
has 2 variants, one delimited (quoted_name_str
) and one not (nonquoted_name_str
). The delimited Name_payload
form differs from Text_payload
only in that each string segment is delimited by double-quotes rather than apostrophes/single-quotes.
A nonquoted_name_str
is composed of a single alphabetic or underscore character followed by zero or more characters that are each alphanumeric or underscore or hyphen. It can not be segmented, so you will have to use the quoted_name_str
equivalent if you want a segmented string. The definitions of alphabetic and numeric should include appropriate Unicode characters, but at the moment this isn't reflected in the grammar; TODO: fix this.
A NameChain
node represents a canonical long name for invoking a DBMS entity in some contexts; it is conceptually a sequence of entity short names. This node is interpreted as a Muldis D sys.std.Core.Type.Cat.NameChain
value as follows: A NameChain_payload
consists of a sequence of 1 or more Name_payload
where the elements of the sequence are separated by period (.
) tokens; each element of the sequence, in order, defines an element of the array
possrep's attribute of the result NameChain
value.
Now, strictly speaking, a Muldis D NameChain
value is supposed to have at least 2 elements in its sequence, and the first element of any sequence must be one of these 7 Name
values, which is a top-level namespace: sys
, mnt
, tpc
, fed
, dep
, sdp
, lex
. (Actually, type
is a 8th option, but that will be treated separately in this discussion.) In the general case, a NameChain_payload
must be written out in full, so it is completely unambiguous (and is clearly self-documenting), and it is always the case that a NameChain
value in the system catalog is written out in full. But the PTMD_STD grammar also has a few commonly used special cases where a NameChain_payload
may be a much shorter substring of its complete version, such that a simple parser, with no knowledge of any user-defined entities besides said shorter NameChain_payload
in isolation, can still unambiguously resolve it to its complete version; exploiting these typically makes for code that is a lot less verbose, and much easier to write or read.
The first special case involves any context where a type or routine is being referenced by name. In such a context, when the referenced entity is a standard system-defined type or routine, programmers may omit any number of consecutive leading chain elements from such a NameChain_payload
, so long as the remaining unqualified chain is distinct among all standard system-defined (sys.std
-prefix) DBMS entities (but that as an exception, a non-distinct abbreviation is allowed iff exactly 1 of the candidate entities is in the language core, sys.std.Core
-prefix, in which case that 1 is unambiguously the entity that is resolved to). This feature has no effect on the namespace prefixes like type
or tuple_from
or array_of
; one still writes those as normal prepended to the otherwise shortened chains. When a NameChain_payload
, whose context indicates it is a type or routine invocation, is encountered by the parser, and its existing first chain element isn't one of the other 8 top-level namespaces, then the parser will assume it is an unqualified chain in the sys
namespace and lookup the best / only match from the known sys.std
DBMS entities, to resolve to. So for example, one can just write Int
rather than sys.std.Core.Type.Int
, is_identical
rather than sys.std.Core.Universal.is_identical
, Tuple.attr
rather than sys.std.Core.Tuple.attr
, min
rather than sys.std.Ordered.min
, array_of.Rat
rather than array_of.sys.std.Core.Type.Rat
, and so on. In fact, the Muldis D spec itself uses such abbreviations frequently.
The second special case involves any context where a value expression (including a parameter) or a variable is being referenced by name, such as with an expr_name
node. In such a context, any leading lex
element may be omitted; when a NameChain_payload
, whose context indicates it is a value expression / etc reference, is encountered by the parser, and its existing first chain element isn't one of the other 8 top-level namespaces, then the parser will assume it is an unqualified chain in the lex
namespace and will prepend a lex
element to it. So for example a $foo
is treated as being $lex.foo
while a $dep.data.foo
is treated as itself. Furthermore, if such an unqualified chain has more than exactly 1 element in it, it is further assumed that the chain is referring to an attribute of a tuple (or database) typed value expression / etc.
The third special case is an extension to the second special case that involves any context where a referenced-by-name value expression / etc has the declaration name topic
, and topic
is tuple (or database) typed. In only such a context, a NameChain_payload
may be prefixed with a chain-element-separator / period token instead of having a leading (post lex
omission) topic
element; a parser encountering a chain with a leading period will assume the chain sans that period is unqualified and will prepend both a lex
and topic
element to it. So for example a $.foo
is treated as being $lex.topic.foo
. Note that PTMD_STD doesn't confuse this with the use of an empty string chain element because those must always be delimited, so $"".foo
is still treated as $lex."".foo
. Note that the third special case may only be used to reference attributes of $lex.topic
(or attributes of those, etc), not $lex.topic
itself; you still have to use $topic
for that.
The fourth special case involves any context where a type is being referenced using the type
namespace prefix feature described in "Referencing Data Types" in Muldis::D::Basics. In such a context, when the namespace prefix contains either of the optional chain elements [|dh_][tuple|relation]_from
or [|dh_][set|maybe|single|array|bag]_of
, programmers may omit the single prefix-leading type
chain element. So for example, one can just write array_of.Rat
rather than type.array_of.Rat
, or tuple_from.var.lex.myrelvar
rather than type.tuple_from.var.lex.myrelvar
. This fourth special case is completely orthogonal to which of the 7 normal top-level namespaces is in use (implicitly or explicitly) by the chain being prefixed, and works for all 7 of them.
A DeclNameChain
node represents a canonical long name for declaring a DBMS entity in N-depth contexts; the format and interpretation of a DeclNameChain_payload
(but as a sys.std.Core.Type.Cat.DeclNameChain
value) is the same as a NameChain_payload
but that the chain may have as few as zero parts rather than as few as 1 or 2, and a zero-element chain is represented by the special DeclNameChain_payload
syntax of []
.
Examples:
Name:login_pass
Name:"First Name"
NameChain:fed.data.the_db.gene.sorted_person_names
NameChain:fed.data.the_db.stats."samples by order"
NameChain:.attr # same as NameChain:lex.topic.attr #
DeclNameChain:gene.sorted_person_name
DeclNameChain:stats."samples by order"
DeclNameChain:[]
Code Comment Literals
Grammar:
<Comment> ::=
Comment \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Comment_payload>
<Comment_payload> ::=
[
'#' ** 2..*
| '#' ' '*
[<-[\\\#\t\n\f\r]> | <escaped_char>]*
' '* '#'
] ** \s+
A Comment
node represents the text of a Muldis D code comment; it is a character string type, that is disjoint from both Text
and Name
. It is interpreted as a Muldis D sys.std.Core.Type.Cat.Comment
value as follows:
Fundamentally a Comment
node is formatted and interpreted like a Text
node, and any similarities won't be repeated here. The Comment_payload
differs from Text_payload
only in that each string segment is delimited by number-signs/hash-marks rather than apostrophes/single-quotes, and also that:
Note that any leading or trailing space (F;20) characters inside the #
delimiters of a Comment_payload
are also part of the delimiters, and are not part of the selected Comment
value; if you want to denote a Comment
value with leading or trailing space chars, you must write those space chars in an escaped form such as with \s
.
Note that a run of 3+ #
is equivalent to exactly 2 adjacent ones, which denotes an empty comment segment. This feature exists to empower things like making visual dividing lines in the code just out of hash-marks.
Note that the hash-mark does have other uses in PTMD_STD code besides delimiting comments, so since Comment_payload
may conceptually be placed almost anywhere in code, the other parts of the grammar that specifically enable this need to ensure appropriate measures are taken to avoid ambiguity, for example mandating that the comments are bounded by whitespace.
Examples (the first is GCVL, the second is a SCVL):
Comment:# This does something. #
# So does this. #
TAI Temporal Literals
Grammar:
<Instant> ::=
Instant \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Instant_payload>
<Instant_payload> ::=
<Rat_payload>
<Duration> ::=
Duration \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Duration_payload>
<Duration_payload> ::=
<Rat_payload>
An Instant
node represents a single point in time which is specified in terms of atomic seconds; it is a rational numeric type, that is disjoint from both Rat
and Duration
. This node is interpreted as a Muldis D sys.std.Core.Type.Instant
value as follows: An Instant_payload
is formatted and interpreted in the same way as a Rat_payload
.
A Duration
node represents a single amount of time (the difference between two instants) which is specified in terms of atomic seconds; it is a rational numeric type, that is disjoint from both Rat
and Instant
. This node is interpreted as a Muldis D sys.std.Core.Type.Duration
value as follows: A Duration_payload
is formatted and interpreted in the same way as a Rat_payload
.
Examples:
Instant:1235556432.0
Instant:854309115.0
Duration:3600.0
Duration:-50.0
Duration:3.14159
Duration:1;1011101101*10^-11011
Duration:1/43
UTC and Float Temporal Literals
Grammar:
<UTCInstant> ::=
UTC [Instant | DateTime | Date | Time] \s* ':' \s*
[<type_name> \s* ':' \s*]?
<UTCInstant_payload>
<UTCInstant_payload> ::=
<UTCDuration_payload>
<FloatInstant> ::=
Float [Instant | DateTime | Date | Time] \s* ':' \s*
[<type_name> \s* ':' \s*]?
<FloatInstant_payload>
<FloatInstant_payload> ::=
<UTCDuration_payload>
<UTCDuration> ::=
UTCDuration \s* ':' \s*
[<type_name> \s* ':' \s*]?
<UTCDuration_payload>
<UTCDuration_payload> ::=
<num_max_col_val> \s* ';' \s* <utc_duration_body>
| <d_utc_duration_body>
<utc_duration_body> ::=
'[' \s*
[<int_body>? [\s* ',' \s*]] ** 5
\s* ',' \s* <rat_body>?
\s* ']'
<d_utc_duration_body> ::=
'[' \s*
[<d_int_body>? [\s* ',' \s*]] ** 5
\s* ',' \s* <d_rat_body>?
\s* ']'
A UTCInstant
node represents an "instant"/"datetime" value that is affiliated with the UTC time-zone. This node is interpreted as a Muldis D sys.std.Temporal.Type.UTCInstant
value whose instant
possrep attribute values are defined as follows:
A UTCInstant_payload
consists mainly of a bracket-delimited sequence of 6 comma-separated elements, where each element is either a valid numeric literal or is completely absent. The 6 elements correspond in order to the 6 attributes: year
, month
, day
, hour
, minute
, second
. For each element that is absent or defined, the corresponding attribute has the nothing
or a Single
value, respectively. For each of the first 5 elements, when it is defined, it must qualify as a valid body part of an Int
node; for the 6th element, when it is defined, it must qualify as a valid body part of a Rat
node.
Fundamentally each UTCInstant
node element is formatted and interpreted like an Int
or Rat
node, and any similarities won't be repeated here.
A defined year
may be any integer, each of [month
, day
] must be a positive integer, each of [hour
, minute
] must be a non-negative integer, and second
must be a non-negative rational number. If all 6 attributes are defined, then the new UTCInstant
value is also a UTCDateTime
; if just the first 3 or last 3 are defined, then the value is not a UTCDateTime
but rather a UTCDate
or UTCTime
, respectively; if any other combination of attributes are defined, then the value is just a UTCInstant
and not of any of the other 3 subtypes.
If the value_kind
of a value
node is UTCDateTime
or UTCDate
or UTCTime
rather than UTCInstant
, then the value
node is interpreted simply as a UTCInstant
node whose type_name
is UTCDateTime
or UTCDate
or UTCTime
, and the allowed body is appropriately further restricted.
A FloatInstant
node represents an "instant"/"datetime" value that is "floating" / not affiliated with any time-zone. This node is interpreted as a Muldis D sys.std.Temporal.Type.FloatInstant
value in an identical fashion to how a UTCInstant
node is interpreted, whose format it completely shares. Likewise regarding Float[DateTime|Date|Time]
.
A UTCDuration
node represents a duration value, an amount of time, which is not fixed to any instant in time. This node is interpreted as a Muldis D sys.std.Temporal.Type.UTCDuration
value whose duration
possrep attribute values are defined as follows:
A UTCDuration_payload
consists mainly of a bracket-delimited sequence of 6 comma-separated elements, where each element is either a valid numeric literal or is completely absent. The 6 elements correspond in order to the 6 attributes: years
, months
, days
, hours
, minutes
, seconds
. For each element that is absent or defined, the corresponding attribute has the nothing
or a Single
value, respectively. For each of the first 5 elements, when it is defined, it must qualify as a valid body part of an Int
node; for the 6th element, when it is defined, it must qualify as a valid body part of a Rat
node.
Mostly a UTCDuration
is formatted and interpreted like a UTCInstant
node, and any similarities won't be repeated here.
A defined [years
, months
, days
, hours
, minutes
] may be any integer, and seconds
may be any rational number. Currently, UTCDuration
has no system-defined subtypes, but that may change later.
Examples:
UTCInstant:[1964,10,16,16,12,47.5] # a UTCDateTime #
UTCInstant:[2002,12,16,,,] # a UTCDate #
UTCInstant:[,,,14,2,29.0] # a UTCTime #
FloatInstant:[2003,4,5,2,,] # min,sec unknown or N/A #
FloatInstant:[1407,,,,,] # just know its sometime in 1407 #
UTCDuration:[3,5,1,6,15,45.000012]
Rational Rounding Rule Literals
Grammar:
<RatRoundRule> ::=
RatRoundRule \s* ':' \s*
[<type_name> \s* ':' \s*]?
<RatRoundRule_payload>
<RatRoundRule_payload> ::=
'[' \s*
<radix> \s* ',' \s* <min_exp> \s* ',' \s* <round_meth>
\s* ']'
<radix> ::=
<Int_payload>
<min_exp> ::=
<Int_payload>
<round_meth> ::=
<RatRoundMeth_payload>
A RatRoundRule
node represents a rational rounding rule. It is interpreted as a Muldis D sys.std.Core.Type.Cat.RatRoundRule
value whose attributes are defined by the RatRoundRule_payload
. A RatRoundRule_payload
consists mainly of a bracket-delimited sequence of 3 comma-separated elements, which correspond in order to the 3 attributes: radix
(a PInt2_N
), min_exp
(an Int
), and round_meth
(a RatRoundMeth
). Each of radix
and min_exp
must qualify as a valid Int_payload
, and round_meth
must qualify as a valid RatRoundMeth_payload
.
Examples:
RatRoundRule:[10,-2,half_even]
RatRoundRule:[2,-7,to_zero]
Low Level Integer String Literals
Grammar:
<String> ::=
[String | BString | OString | UCPString] \s* ':' \s*
[<type_name> \s* ':' \s*]?
<String_payload>
<String_payload> ::=
<num_max_col_val> \s* ';' \s* <string_body>
| <d_string_body>
<string_body> ::=
'[' \s*
[<int_body> ** [\s* ',' \s*]]?
\s* ']'
<d_string_body> ::=
'[' \s*
[<d_int_body> ** [\s* ',' \s*]]?
\s* ']'
A String
node represents an integer string value. It is interpreted as a Muldis D sys.std.Core.Type.Cat.String
value as follows: A String_payload
consists mainly of a bracket-delimited sequence of 0..N elements, where each element must qualify as a valid body part of a Int
node, and the new String
is conceptually that sequence of integers. Fundamentally each String
node element is formatted and interpreted like an Int
node, and any similarities won't be repeated here.
Examples:
String:[80,101,114,109] # Unicode abstract codepoints = 'Perl' #
String:F;[50,65,72,6C] # same thing #
COLLECTION VALUE SELECTORS
Note that, with each of the main value selector nodes documented in this main POD section (members of coll_value_selector
etc), any occurrences of child expr
nodes should be read as being value
nodes instead in contexts where instances of the main nodes are being composed beneath value
nodes. That is, any expr
node options beyond what value
options exist are only valid within a depot
node.
Scalar Selectors
Grammar:
<Scalar> ::=
DH? Scalar \s* ':' \s*
<type_name> \s* ':' \s*
<Scalar_payload>
<Scalar_payload> ::=
<possrep_name> \s* ';' \s* <possrep_attrs>
| <possrep_attrs>
<possrep_name> ::=
<Name_payload>
<possrep_attrs> ::=
<Tuple_payload>
A Scalar
node represents a literal or selector invocation for a scalar subtype value. It is interpreted as a Muldis D sys.std.Core.Type.Scalar
subtype value whose declared type is specified by the node's (mandatory for Scalar
) type_name
and whose attributes are defined by the Scalar_payload
. If the Scalar_payload
is just a possrep_attrs
, then it is interpreted as if it also had an explicit possrep_name
that is the empty string. The possrep_attrs
is interpreted specifically as attributes of the declared type's possrep which is specified by the possrep_name
. Each name+expr pair of the possrep_attrs
defines a named possrep attribute of the new scalar; the pair's name and expr specify, respectively, the possrep attribute name, and the possrep attribute value. If the value_kind
of a value
node is DHScalar
rather than Scalar
, then the value
node is interpreted simply as an Scalar
node that is appropriately further restricted; the type_name
must name a DHScalar
subtype, and the possrep_attrs
must specify only deeply homogeneous typed attribute values.
See also the definition of the catalog data type sys.std.Core.Type.Cat.ScaPRSelExprNodeSet
, a tuple of which is what in general a Scalar
node distills to when it is beneath the context of a depot
node, as it describes some semantics.
Examples:
Scalar:sys.std.Core.Type.Cat.Name:{ "" => 'the_thing' }
Scalar:sys.std.Core.Type.Rat:float;{
mantissa => 45207196,
radix => 10,
exponent => 37
}
Scalar:sys.std.Temporal.Type.UTCDateTime:datetime;{
year => 2003,
month => 10,
day => 26,
hour => 1,
minute => 30,
second => 0.0
}
Scalar:fed.lib.the_db.WeekDay:name;{
"" => "monday"
}
Scalar:fed.lib.the_db.WeekDay:number;{
"" => 5
}
Tuple Selectors
Grammar:
<Tuple> ::=
DH? Tuple \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Tuple_payload>
<Tuple_payload> ::=
<tuple_list> | <tuple_d0>
<tuple_list> ::=
'{' \s*
[[<attr_name> \s* '=>' \s* <expr>] ** [\s* ',' \s*]]?
\s* '}'
<attr_name> ::=
<Name_payload>
<tuple_d0> ::=
d0
A Tuple
node represents a literal or selector invocation for a tuple value. It is interpreted as a Muldis D sys.std.Core.Type.Tuple
value whose attributes are defined by the Tuple_payload
.
Iff the Tuple_payload
is a tuple_list
then each name+expr pair of the Tuple_payload
defines a named attribute of the new tuple; the pair's name and expr specify, respectively, the attribute name, and the attribute value. If the value_kind
of a value
node is DHTuple
rather than Tuple
, then the value
node is interpreted simply as an Tuple
node that is appropriately further restricted; the Tuple_payload
must specify only deeply homogeneous typed attribute values.
Iff the Tuple_payload
is a tuple_d0
then the Tuple
node is interpreted as the special value Tuple:d0
aka d0
, which is the only Tuple
value with exactly zero attributes. Note that this is just an alternative syntax, as tuple_list
can select that value too.
See also the definition of the catalog data type sys.std.Core.Type.Cat.TupSelExprNodeSet
, a tuple of which is what in general a Tuple
node distills to when it is beneath the context of a depot
node, as it describes some semantics.
Examples:
Tuple:{}
Tuple:d0 # same as previous #
d0 # same as previous #
Tuple:type.tuple_from.var.fed.data.the_db.account.users:{
login_name => 'hartmark',
login_pass => 'letmein',
is_special => true
}
Tuple:{
name => 'Michelle',
age => 17
}
Database Selectors
Grammar:
<Database> ::=
Database \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Database_payload>
<Database_payload> ::=
<Tuple_payload>
A Database
node represents a literal or selector invocation for a 'database' value. It is interpreted as a Muldis D sys.std.Core.Type.Database
value whose attributes are defined by the Database_payload
. Each name+relation pair of the Database_payload
defines a named attribute of the new 'database'; the pair's name and relation specify, respectively, the attribute name, and the attribute value. While this grammar mentions that Database_payload
is a Tuple_payload
, it is in fact significantly further restricted, such that every attribute value of the Database
can only be a DHRelation
.
See also the definition of the catalog data type sys.std.Core.Type.Cat.TupSelExprNodeSet
, a tuple of which is what in general a Database
node distills to same as when Tuple
does.
Relation Selectors
Grammar:
<Relation> ::=
DH? Relation \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Relation_payload>
<Relation_payload> ::=
<r_empty_body_payload>
| <r_nonordered_attr_payload>
| <r_ordered_attr_payload>
| <relation_d0>
<r_empty_body_payload> ::=
'{' \s*
[<attr_name> ** [\s* ',' \s*]]?
\s* '}'
<r_nonordered_attr_payload> ::=
'{' \s*
[<Tuple_payload> ** [\s* ',' \s*]]?
\s* '}'
<r_ordered_attr_payload> ::=
'[' \s*
[<attr_name> ** [\s* ',' \s*]]?
\s* ']'
\s* ';' \s*
'{' \s*
[<ordered_tuple_attrs> ** [\s* ',' \s*]]?
\s* '}'
<ordered_tuple_attrs> ::=
'[' \s*
[<expr> ** [\s* ',' \s*]]?
\s* ']'
<relation_d0> ::=
d0c0 | d0c1
A Relation
node represents a literal or selector invocation for a relation value. It is interpreted as a Muldis D sys.std.Core.Type.Relation
value whose attributes and tuples are defined by the Relation_payload
, which is interpreted as follows:
Iff the Relation_payload
is composed of just a nonord_list_[open|close]
pair with zero elements between them, then it defines the only relation value having zero attributes and zero tuples.
Iff the Relation_payload
is a r_empty_body_payload
with at least one attr_name
element, then it defines the attribute names of a relation having zero tuples.
Iff the Relation_payload
is a r_nonordered_attr_payload
with at least one <Tuple_payload> element, then each element defines a tuple of the new relation; every <Tuple_payload> must define a tuple of the same degree and have the same attribute names as its sibling <Tuple_payload>; these are the degree and attribute names of the relation as a whole, which is its heading for the current purposes.
Iff the Relation_payload
is a r_ordered_attr_payload
, then: The new relation value's attribute names are defined by the attr_name
elements, and the relation body's tuples' attribute values are defined by the ordered_tuple_attrs
elements. This format is meant to be the most compact of the generic relation selector formats, as the attribute names only appear once for the relation rather than repeating for each tuple. As a trade-off, the attribute values per tuple from all of the ordered_tuple_attrs
elements must appear in the same order as their corresponding attribute names appear in the collection of attr_name
elements, as the names and values in the relation literal are matched up by ordinal position here.
Iff the Relation_payload
is a relation_d0
then the Relation
node is interpreted as one of the 2 special values Relation:d[0|1]
aka d[0|1]
, which are the only Relation
values with exactly zero attributes. Note that this is just an alternative syntax, as other Relation_payload
formats can select those values too.
If the value_kind
of a value
node is DHRelation
rather than Relation
, then the value
node is interpreted simply as an Relation
node that is appropriately further restricted; the Relation_payload
specify only deeply homogeneous typed attribute values.
See also the definition of the catalog data type sys.std.Core.Type.Cat.RelSelExprNodeSet
, a tuple of which is what in general a Relation
node distills to when it is beneath the context of a depot
node, as it describes some semantics.
Examples:
Relation:{} # zero attrs + zero tuples #
Relation:d0c0 # same as previous #
Relation:{ x, y, z } # 3 attrs + zero tuples #
Relation:{ {} } # zero attrs + 1 tuple #
d0c1 # same as previous #
Relation:{
{
login_name => 'hartmark',
login_pass => 'letmein',
is_special => true
}
} # 3 attrs + 1 tuple #
Relation:fed.lib.the_db.gene.Person:[ name, age ];{
[ 'Michelle', 17 ]
} # 2 attrs + 1 tuple #
Set Selectors
Grammar:
<Set> ::=
DH? Set \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Set_payload>
<Set_payload> ::=
'{' \s*
[<expr> ** [\s* ',' \s*]]?
\s* '}'
A Set
node represents a literal or selector invocation for a set value. It is interpreted as a Muldis D sys.std.Core.Type.Set
value whose elements are defined by the Set_payload
. Each expr
of the Set_payload
defines a unary tuple of the new set; each expr
defines the value
attribute of the tuple. If the value_kind
of a value
node is DHSet
rather than Set
, then the value
node is further restricted.
See also the definition of the catalog data type sys.std.Core.Type.Cat.SetSelExprNodeSet
, a tuple of which is what in general a Set
node distills to when it is beneath the context of a depot
node, as it describes some semantics.
Examples:
Set:fed.lib.the_db.account.Country_Names:{
'Canada',
'Spain',
'Jordan',
'Thailand'
}
Set:{
3,
16,
85
}
Maybe Selectors
Grammar:
<Maybe> ::=
DH? [Maybe | Single] \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Maybe_payload>
<Maybe_payload> ::=
<maybe_list> | <maybe_nothing>
<maybe_list> ::=
'{' \s* <expr> \s* '}'
<maybe_nothing> ::=
nothing | '∅'
A Maybe
node represents a literal or selector invocation for a maybe value. It is interpreted as a Muldis D sys.std.Core.Type.Maybe
value whose elements are defined by the Maybe_payload
.
Iff the Maybe_payload
is a maybe_list
then it defines either zero or one expr
; in the case of one, the expr
defines the unary tuple of the new maybe, which is a 'single'; the expr
defines the value
attribute of the tuple. If the value_kind
of a value
node is DHMaybe
or [|DH]Single
rather than Maybe
, then the value
node is further restricted, either to having only deeply homogeneous resulting expr
or to having exactly one expr
, as appropriate.
Iff the Maybe_payload
is a maybe_nothing
then the Maybe
node is interpreted as the special value Maybe:nothing
, aka nothing
, aka empty set, aka ∅
, which is the only Maybe
value with zero elements. Note that this is just an alternative syntax, as set_expr_list
can select that value too. As a further restriction, the value_kind
must be just one of [|DH]Maybe
when the Maybe_payload
is a maybe_nothing
.
See also the definition of the catalog data type sys.std.Core.Type.Cat.SetSelExprNodeSet
, a tuple of which is what in general a Maybe
node distills to same as when Set
does.
Examples:
Maybe:{ 'I know this one!' }
Maybe:nothing
Maybe:∅
nothing
∅
Array Selectors
Grammar:
<Array> ::=
DH? Array \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Array_payload>
<Array_payload> ::=
'[' \s*
[<expr> ** [\s* ',' \s*]]?
\s* ']'
A Array
node represents a literal or selector invocation for a array value. It is interpreted as a Muldis D sys.std.Core.Type.Array
value whose elements are defined by the Array_payload
. Each expr
of the Array_payload
defines a binary tuple of the new sequence; the expr
defines the value
attribute of the tuple, and the index
attribute of the tuple is generated such that the first expr
gets an index
of zero and subsequent ones get consecutive higher integer values. If the value_kind
of a value
node is DHArray
rather than Array
, then the value
node is further restricted.
See also the definition of the catalog data type sys.std.Core.Type.Cat.ArySelExprNodeSet
, a tuple of which is what in general a Array
node distills to when it is beneath the context of a depot
node, as it describes some semantics.
Examples:
Array:[
'Alphonse',
'Edward',
'Winry'
]
Array:fed.lib.the_db.stats.Samples_By_Order:[
57,
45,
63,
61
]
Bag Selectors
Grammar:
<Bag> ::=
DH? Bag \s* ':' \s*
[<type_name> \s* ':' \s*]?
<Bag_payload>
<Bag_payload> ::=
<bag_payload_counted_values>
| <bag_payload_repeated_values>
<bag_payload_counted_values> ::=
'{' \s*
[[<expr> \s* '=>' \s* <count>] ** [\s* ',' \s*]]?
\s* '}'
<count> ::=
<num_max_col_val> \s* ';' \s* <pint_body>
| <d_pint_body>
<bag_payload_repeated_values> ::=
'{' \s*
[<expr> ** [\s* ',' \s*]]?
\s* '}'
A Bag
node represents a literal or selector invocation for a bag value. It is interpreted as a Muldis D sys.std.Core.Type.Bag
value whose elements are defined by the Bag_payload
, which is interpreted as follows:
Iff the Bag_payload
is composed of just a nonord_list_[open|close]
pair with zero elements between them, then it defines the only bag value having zero elements.
Iff the Bag_payload
is a bag_payload_counted_values
with at least one expr
/count
-pair element, then each pair defines a binary tuple of the new bag; the expr
defines the value
attribute of the tuple, and the count
defines the count
attribute.
Iff the Bag_payload
is a bag_payload_repeated_values
with at least one <expr> element, then each expr
contributes to a binary tuple of the new bag; the expr
defines the value
attribute of the tuple. The bag has 1 tuple for every distinct (after normalization or evaluation) expr
and expr
-derived value in the Bag_payload
, and the count
attribute of that tuple says how many instances of said value
there were.
See also the definition of the catalog data type sys.std.Core.Type.Cat.BagSelExprNodeSet
, a tuple of which is what in general a Bag
node distills to when it is beneath the context of a depot
node, as it describes some semantics.
Further concerning bag_payload_counted_values
, because of how BagSelExprNodeSet
is defined, a count
has to be a compile time constant, since an integer is stored in the system catalog rather than the name of an expression node like with value
; if you actually want the bag value being selected at runtime to have runtime-determined count
values, then you must use a Relation
node rather than a Bag
node.
Examples:
Bag:fed.lib.the_db.inventory.Fruit:{
'Apple' => 500,
'Orange' => 300,
'Banana' => 400
}
Bag:{
'Foo',
'Quux',
'Foo',
'Bar',
'Baz',
'Baz'
}
DEPOT DECLARATION
TODO: ALL OF THIS HERE MAIN POD SECTION!
Grammar:
<depot> ::=
depot \s+ catalog \s+ [<Database> | <depot_or_subdepot>]
[\s+ depot \s+ data \s+ <Database>]?
<depot_or_subdepot> ::=
'{' \s*
[[
<named_subdepot>
| <named_material>
] ** \s+]?
\s* '}'
<named_subdepot> ::=
subdepot \s+ <Name_payload> \s+ <depot_or_subdepot>
<named_material> ::=
[private | public]? ... \s+ <Name_payload> \s+ ...
GENERIC VALUE EXPRESSIONS
Grammar:
<expr> ::=
<expr_core_options>
<expr_core_options> ::=
<expr_name>
| <named_expr>
| <opaque_value_literal>
| <coll_value_selector>
| <accessor>
| <func_invo>
| <if_else_expr>
| <given_when_def_expr>
| <lib_entity_ref_selector>
<expr_name> ::=
<data_sigil> <Name_payload>
<data_sigil> ::=
'$'
<named_expr> ::=
<expr_name> \s* <infix_bind_op> \s* <expr>
<infix_bind_op> ::=
'::='
An expr
node is the general case of a Muldis D value expression tree (which normally denotes a Muldis D value selector), which must be composed beneath a depot
, or specifically into a function or updater or type or constraint (etc) definition, because in the general case an expr
can not be completely evaluated at compile time.
An expr
node is a proper superset of a value
node, and any occurrences of expr
nodes in this document may optionally be substituted with value
nodes on a per-instance basis.
An expr
node in the PTMD_STD grammar corresponds directly to a tuple of an attribute of a value of the catalog data type sys.std.Core.Type.Cat.ExprNodeSet
, which is how a value expression node is actually represented in Muldis D's nonsugared form, which is as a component of the system catalog. Or more specifically, an entire tree of PTMD_STD expr
nodes corresponds to a set of said attribute values, one attribute value per expr
node. In the nonsugared form, every expr
node has an explicitly designated name, as per a PTMD_STD named_expr
node, and all child nodes are not declared inline with their parent nodes but rather are declared in parallel with them, and the parents refer to their children by their names. A feature of the PTMD_STD grammar is that expression nodes may be declared without explicit names, such that the parser would generate names for them when deriving system catalog entries, and that is why PTMD_STD supports, and encourages the use of for code brevity/readability, the use of inline-declared expression nodes, especially so when the expr
in question is an opaque_value_literal
.
Iff an expr
is an expr_name
, then this typically means that the parent expr
is having at least one of its children declared with an explicit name rather than inline, same as the corresponding system catalog entry would do, and then the expr_name
is the invocation name of that child. Alternately, the expr_name
may be the invocation name of one of the expression-containing routine's parameters, in which case the expr
in question represents the current argument to that parameter; this also is exactly the same as a corresponding catalog entry for using an argument.
Iff an expr
is a named_expr
, then the expr
element of the named_expr
is being declared with an explicit name, and the expr_name
element of the named_expr
is that name. But if the expr
element of the named_expr
is an expr_name
(or a named_expr
TODO: or a param
), then the named_expr
is in fact declaring a new node itself (rather than simply naming its child node), which is a tuple of a Muldis D sys.std.Core.Type.Cat.AccExprNodeSet
value; the new node is simply declaring an alias for another node, namely the expr
element.
Examples:
# an expr_name node #
$foo_expr
# a named_expr node #
$bar_expr ::= factorial( $foo_expr )
Generic Expression Attribute Accessors
Grammar:
<accessor> ::=
<acc_via_named> | <acc_via_anon>
<acc_via_named> ::=
<data_sigil> <NameChain_payload>
<acc_via_anon> ::=
<expr> \s* '.' \s* <DeclNameChain_payload>
An accessor
node represents an accessor or alias for an attribute of another, tuple-valued expression node. It is interpreted as a tuple of a Muldis D sys.std.Core.Type.Cat.AccExprNodeSet
value. If an accessor
is an acc_via_named
, then the NameChain_payload
element specifies the target
attribute of the new AccExprNodeSet
. If an accessor
is an acc_via_anon
, then the target
is derived from a catenation of the node name that expr
has (explicitly or that will be generated for it by the parser) with the DeclNameChain_payload
in that order. Note that an acc_via_anon
whose expr
is an expr_name
is also an acc_via_named
, and vice-versa.
Examples:
# an accessor node of a named tuple-valued node #
$foo_t.bar_attr
# an accessor node of an anonymous tuple-valued node #
sdp.lib.tuple_res_func( $arg ).quux_attr
Generic Function Invocation Expressions
Grammar:
<func_invo> ::=
<routine_name> \s* <func_arg_list>
<routine_name> ::=
<NameChain_payload>
<func_arg_list> ::=
'(' \s*
[[<named_ro_arg> | <anon_ro_arg>] ** [\s* ',' \s*]]?
\s* ')'
<named_ro_arg> ::=
<param_name> \s* '=>' \s* <expr>
<param_name> ::=
<Name_payload>
<anon_ro_arg> ::=
<expr>
A func_invo
node represents the result of invoking a named function with specific arguments. It is interpreted as a tuple of a Muldis D sys.std.Core.Type.Cat.FuncInvoExprNodeSet
value. The routine_name
element specifies the function
attribute of the new FuncInvoExprNodeSet
, which is the name of the function being invoked, and the func_arg_list
element specifies the args
attribute.
In the general case of a function invocation, all of the arguments are named, as per named_ro_arg
, and formatting a func_invo
node that way is always allowed. In some (common) special cases, some (which might be all) arguments may be anonymous, as per anon_ro_arg
.
With just functions in the top-level namespaces sys.std
, these 4 special cases apply: If a function has exactly one parameter, then it may be invoked with a single anonymous argument and the latter will bind to that parameter. Or, if a function has multiple parameters but exactly one of those is mandatory, then it may be invoked with just one anonymous argument, which is assumed to bind to the single mandatory parameter, and all optional arguments must be named. Or, if a function has multiple mandatory parameters and one of them is named topic
, then it may be invoked with a single anonymous argument and the latter will bind to that parameter. Or, if a function has multiple mandatory parameters and two of them are named topic
and other
, then it may be invoked with two anonymous arguments and the latter will bind to those parameters in sequential order, the first one to topic
and the second one to other
.
With just functions in all top-level namespaces except sys.std
, these 2 special cases apply (similar to the prior-mentioned latter 2): If a function invocation has either 1 or 2 anonymous arguments, then they will be treated as if they were named arguments for the topic
and other
parameters; the only or sequentially first argument will bind to topic
, and any sequentially second argument will bind to other
.
One reason for this difference between treatment of top-level namespaces is it allows the Muldis D parser to convert all the anonymous arguments to named ones (all arguments in the system catalog are named) when parsing the expression-containing routine/etc in isolation from any other user-defined entities. The other reason for this limitation is that it helps with self-documentation; programmers wanting to know an anonymous argument's parameter name won't have to look outside the language spec to find the answer.
Maybe TODO: Consider adding a language pragma to enable use of the first 4 special cases with functions in all top-level namespaces, where the cost of enabling is added implementation complexity and a reduction of the ability to parse exploiting Muldis D code piecemeal.
Examples:
# zero params #
nothing()
# single mandatory param #
Integer.median( Bag:{ 22, 20, 21, 20, 21, 21, 23 } )
# single mandatory param #
factorial( topic => 5 )
# two mandatory params #
Rational.quotient( dividend => 43.7, divisor => 16.9 )
# same as previous #
Rational.quotient( divisor => 16.9, dividend => 43.7 )
# one mandatory 'topic' param, two optional #
sdp.lib.barfunc( $mand_arg, oa1 => $opt_arg1, oa2 => $opt_arg2 )
# same as previous #
sdp.lib.barfunc( oa2 => $opt_arg2, $mand_arg, oa1 => $opt_arg1 )
# a user-defined function #
dep.lib.foodb.bazfunc( a1 => 52, a2 => 'hello world' )
# two params named 'topic' and 'other' #
is_identical( $foo, $bar )
Generic If-Else Expressions
Grammar:
<if_else_expr> ::=
'(' \s*
[
[if \s+ <if_expr> \s+ then \s+ <then_expr> \s+ else \s+]*
| [<if_expr> \s+ '??' \s+ <then_expr> \s+ '!!' \s+]*
]
<else_expr>
\s* ')'
<if_expr> ::=
<expr>
<then_expr> ::=
<expr>
<else_expr> ::=
<expr>
An if_else_expr
node represents an N-way if-else control flow expression. It is interpreted as a tuple of a Muldis D sys.std.Core.Type.Cat.IfElseExprNodeSet
value. The whole collection of sequential 0..N if_expr
+ then_expr
elements specifies the if_then
attribute of the new IfElseExprNodeSet
, which is a sequence of arbitrary but Bool
-resulting if expressions, and for just the first one of those in the sequence that at runtime evaluates to Bool:true
, its associated then result value is the result of the if_else_expr
. The else_expr
element specifies the else
attribute, which determines the result value of the if_else_expr
at runtime if either <if_then> is an empty sequence or all of its conditionals evaluate to Bool:false
.
Examples:
(if ($foo > 5) then $bar else $baz)
(if ($ary is empty) then $empty_result else $ary.[0])
(if (($x = ∅) or ($y = ∅)) then ∅
else (s ((v $x) I+ ((v $y) I^ 3)))
(if ($val isa T->Int) then ($val I^ 3)
else if ($val isa T->Text) then ($val Tx 5)
else true)
('My answer is: ' T~ ($maybe ?? 'yes' !! 'no'))
Generic Given-When-Default Expressions
Grammar:
<given_when_def_expr> ::=
'(' \s*
given \s+ <given_expr> \s+
[when \s+ <when_expr> \s+ then \s+ <then_expr> \s+]*
default \s+ <default_expr>
\s* ')'
<given_expr> ::=
<expr>
<when_expr> ::=
<expr>
<then_expr> ::=
<expr>
<default_expr> ::=
<expr>
A given_when_def_expr
node represents an N-way given-when-default switch control flow expression that dispatches based on matching a single value with several options. It is interpreted as a tuple of a Muldis D sys.std.Core.Type.Cat.GivenWhenDefExprNodeSet
value. The given_expr
element specifies the given
attribute of the new GivenWhenDefExprNodeSet
, which is the control value for the expression. The whole collection of nonordered 0..N when_expr
+ then_expr
elements specifies the when_then
attribute, which is a set of when comparands; if any of these when values matches the value of given
, its associated then result value is the result of the given_when_def_expr
. The default_expr
element specifies the default
attribute, which determines the result value of the given_when_def_expr
at runtime if either <when_then> is an empty set or none of its comparands match given
.
Examples:
(given $digit
when 'T' then 10
when 'E' then 11
default $digit
)
Library Entity Reference Selector
Grammar:
<lib_entity_ref_selector> ::=
<func_ref>
| <imp_ref>
| <type_ref>
| <ord_det_func_ref>
<func_ref> ::=
'F->' <routine_name>
<imp_ref> ::=
'P->' <routine_name>
<type_ref> ::=
'T->' <type_name>
<ord_det_func_ref> ::=
'ODF->' <routine_name>
A [func|proc|type|ord_det_func]_ref
node represents a literal or selector invocation for a DBMS routine or type reference value. It is interpreted as a tuple of a Muldis D sys.std.Core.Type.Cat.MaterialRefSelExprNodeSet
value, which when evaluated at runtime would result in a sys.std.Core.Type.Cat.MaterialRef
value. The [routine|type]_name
element specifies the referencing
attribute of the new sys.std.Core.Type.Cat.MaterialRefSelExprNodeSet
, which is the name of the routine or type being invoked by way of the new reference value.
Examples:
F->sdp.lib.filter
P->sdp.lib.try_block
T->sdp.lib.foo_type
ODF->sdp.lib.order_bars
MULDIS D STANDARD DIALECT PRAGMAS
All 3 of the Muldis D standard dialects, PTMD_STD
and HDMD_Perl[6|5]_STD
, support these same pragmas, and have the same semantics: with_rtn_inv_alt_syn
.
with_rtn_inv_alt_syn
One of Muldis D's primary features is that, as much as possible, the system-defined language features are defined in terms of ordinary types and routines. This means for one thing that users are empowered to create their own types and routines with all of the capabilities, flexibility, and syntax as the language's built-in features have. This also means that it should be relatively simple to parse Muldis D code because the vast majority of language features don't have their own special syntax to account for, and the "Generic Function Invocation Expressions" syntax covers most of them, in terms of the common prefix/polish notation that in practice most invocations of user-defined routines are formatted as anyway.
However, in practice a huge payoff of improved user code brevity and readability (and writability) can be gained by adding special syntax for a lot of commonly used built-in routines, such as infix syntax for common math operators or postcircumfix syntax for attribute accessors. The tradeoff for this user code brevity is a significant amount of extra complexity in parsers, due to all the extra special cases, though this complexity can be mitigated somewhat by standardizing these additions in format where possible. But because this extra flexibility isn't actually needed to write Muldis D code effectively, it has been relegated to an optional language extension rather than a mandatory one, and each Muldis D implementation can choose whether or not to support it. The expectation is that, in general, minimal Muldis D implementations won't support it but non-minimal ones would.
If the declared language_name
of Muldis D code has the ln_extensions
element and the latter contains a name+value pair of with_rtn_inv_alt_syn
+ Bool:true
, then this activates the optional with_rtn_inv_alt_syn
(With Routine Invocation Alternate Syntax) pragma. When with_rtn_inv_alt_syn
is active, the following grammar redefinitions are in effect:
<expr> ::=
<expr_core_options>
| <func_invo_alt_syntax>
So then the syntax described in these pod sections in this file can be used, where otherwise they couldn't be: L/<FUNCTION INVOCATION ALTERNATE SYNTAX EXPRESSIONS>.
FUNCTION INVOCATION ALTERNATE SYNTAX EXPRESSIONS
Grammar:
<func_invo_alt_syntax> ::=
<comm_infix_reduce_op_invo>
| <noncomm_infix_reduce_op_invo>
| <sym_dyadic_infix_op_invo>
| <nonsym_dyadic_infix_op_invo>
| <monadic_prefix_op_invo>
| <monadic_postfix_op_invo>
| <postcircumfix_op_invo>
| <rat_op_invo_with_round>
| <ord_compare_op_invo>
| ...
A func_invo_alt_syntax
node represents the result of invoking a named system-defined function with specific arguments. It is interpreted as a tuple of a Muldis D sys.std.Core.Type.Cat.FuncInvoExprNodeSet
value. A func_invo_alt_syntax
node is a lot like a func_invo
node in purpose and interpretation but it differs in several significant ways.
While a func_invo
node can be used to invoke any function at all, a func_invo_alt_syntax
node can only invoke a fraction of them, and only standard system-defined functions. While a func_invo
node uses a simple common format with all functions, written in prefix notation with generally named arguments, a func_invo_alt_syntax
node uses potentially unique syntax for each function, often written in infix notation, although inter-function format consistency is still applied as much as is reasonably possible.
One basic format commonality with all func_invo_alt_syntax
tokens is that the entire token is delimited by a pair of parenthesis, whose conceptual purpose is to group together all the parts of the token, so there are no precedence issues to make difficulty in telling the token from its parent, no matter how complicated it is; this is like with the common grouping parenthesis in mathematical expressions. A practical purpose of this is that the delimiting parenthesis is a feature mostly unique to a func_invo_alt_syntax
token, which almost all other grammar tokens don't have, if_else_expr
and given_when_def_expr
being the main exceptions, and so a parser encountering an opening parenthesis at the start of a context where a generic expr
is expected can be reasonably sure it is dealing with a func_invo_alt_syntax
(or if-else or given-when) and not something else. A major exception to this format commonality is with postcircumfix_op_invo
, which does not have delimiting parenthesis.
Broadly speaking, a func_invo_alt_syntax
node has 2-3 kinds of payload elements: The first is the determinant of what function to invoke, hereafter referred to as a op or keyword. The second is an ordered list of 1-N mandatory function inputs, hereafter referred to as main op args, whose elements typically have generic names like expr
or lhs
or rhs
. The (optional) third is a named list of optional function inputs, hereafter referred to as extra op args, whose elements tend to have more purpose-specific names such as using_clause
, though note that things like using_clause
can be either mandatory or optional depending on the op they are being used with.
The decision of which system-defined functions get the special alternate syntax treatment partly comes down to respecting common good practices in programming languages, letting people write code more like how they're comfortable with. Most programming languages only have special syntax for a handful of their operators, such as common comparison and boolean and mathematical and string and element extraction operators, and so Muldis D mainly does likewise. Functions get special alternate syntax if they would be frequently used and the syntax would significantly aid programmers in quickly writing understandeable code.
Simple Commutative N-adic Infix Reduction Operators
Grammar:
<comm_infix_reduce_op_invo> ::=
'(' \s*
<expr> ** [\s+ <comm_infix_reduce_op> \s+]
\s* ')'
<comm_infix_reduce_op ::=
and | '∧' | or | '∨' | xnor | '↔' | iff | xor | '⊻' | '↮'
| 'I+' | 'I*'
| 'N+' | 'N*'
| '∪' | 'R+' | union | '∩' | 'R*' | intersect
| '∆' | 'R%' | exclude | symdiff
| '⋈' | join | '×' | times | 'cross join'
A comm_infix_reduce_op_invo
node is for using infix notation to invoke a (homogenous) commutative N-adic reduction operator function. Such a function takes exactly 1 actual argument, which is unordered-collection typed (set or bag), and the elements of that collection are the inputs of the operation; the inputs are all of the same type as each other and of the result. A single comm_infix_reduce_op_invo
node is equivalent to a single func_invo
node whose func_arg_list
element defines a single argument, whose value is a Set
or Bag
node, which has a payload expr
element for each expr
element of the comm_infix_reduce_op_invo
, and the relative sequence of the expr
elements isn't significant. A comm_infix_reduce_op_invo
node requires at least 2 input value providing child nodes (expr
must match at least twice), which are its 2-N main op args; if you already have your inputs in a single collection-valued node then use func_invo
to invoke the function instead. If comm_infix_reduce_op
matches more than once in the same comm_infix_reduce_op_invo
, then all of the comm_infix_reduce_op
matches must be identical / the same operator.
Some of the keywords are aliases for each other:
keyword | aliases
--------+--------
and | ∧
or | ∨
xnor | ↔ iff
xor | ⊻ ↮
∪ | R+ union
∩ | R* intersect
∆ | R% exclude symdiff
⋈ | join
× | times 'cross join'
This table indicates which function is invoked by each keyword:
and -> Core.Bool.and( Set:{ $expr[0], ..., $expr[n] } )
or -> Core.Bool.or( Set:{ $expr[0], ..., $expr[n] } )
xnor -> Bool.xnor( Bag:{ $expr[0], ..., $expr[n] } )
xor -> Bool.xor( Bag:{ $expr[0], ..., $expr[n] } )
I+ -> Integer.sum( Bag:{ $expr[0], ..., $expr[n] } )
I* -> Integer.product( Bag:{ $expr[0], ..., $expr[n] } )
N+ -> Rational.sum( Bag:{ $expr[0], ..., $expr[n] } )
N* -> Rational.product( Bag:{ $expr[0], ..., $expr[n] } )
∪ -> Core.Relation.union( Set:{ $expr[0], ..., $expr[n] } )
∩ -> Core.Relation.intersection( Set:{ $expr[0], ..., $expr[n] } )
∆ -> Relation.exclusion( Bag:{ $expr[0], ..., $expr[n] } )
⋈ -> Core.Relation.join( Set:{ $expr[0], ..., $expr[n] } )
× -> Core.Relation.product( Set:{ $expr[0], ..., $expr[n] } )
Examples:
(true and false and true)
(true or false or true)
(true xor false xor true)
(14 I+ 3 I+ -5)
(-6 I* 2 I* 25)
(4.25 N+ -0.002 N+ 1.0)
(69.3 N* 15*2^6 N* 49/23)
(Set:{ 1, 3, 5 } ∪ Set:{ 4, 5, 6 } ∪ Set:{ 0, 9 })
(Set:{ 1, 3, 5, 7, 9 } ∩ Set:{ 3, 4, 5, 6, 7, 8 } ∩ Set:{ 2, 5, 9 })
Simple Non-commutative N-adic Infix Reduction Operators
Grammar:
<noncomm_infix_reduce_op_invo> ::=
'(' \s*
<expr> ** [\s+ <noncomm_infix_reduce_op> \s+]
\s* ')'
<noncomm_infix_reduce_op ::=
'[<=>]'
| 'B~' | 'T~' | 'A~'
| '//' | '//d'
A noncomm_infix_reduce_op_invo
node is for using infix notation to invoke a (homogenous) non-commutative N-adic reduction operator function. Such a function takes exactly 1 actual argument, which is ordered-collection typed (array), and the elements of that collection are the inputs of the operation; the inputs are all of the same type as each other and of the result. A single noncomm_infix_reduce_op_invo
node is equivalent to a single func_invo
node whose func_arg_list
element defines a single argument, whose value is a Array
node, which has a payload expr
element for each expr
element of the noncomm_infix_reduce_op_invo
, and the expr
elements have the same relative sequence. A noncomm_infix_reduce_op_invo
node requires at least 2 input value providing child nodes (expr
must match at least twice), which are its 2-N main op args; if you already have your inputs in a single collection-valued node then use func_invo
to invoke the function instead. If noncomm_infix_reduce_op
matches more than once in the same noncomm_infix_reduce_op_invo
, then all of the noncomm_infix_reduce_op
matches must be identical / the same operator. Exception: with some of these, the actual func_arg_list
derived from this has 2 actual arguments, the first a collection and the second taking a different type of value, from the last op input list element.
This table indicates which function is invoked by each keyword:
[<=>] -> Core.Cat.Order.reduction( Array:{ $expr[0], ..., $expr[n] } )
B~ -> Blob.catenation( Array:{ $expr[0], ..., $expr[n] } )
T~ -> Text.catenation( Array:{ $expr[0], ..., $expr[n] } )
A~ -> Array.catenation( Array:{ $expr[0], ..., $expr[n] } )
// -> Set.Maybe.attr_or_value(
Array:{ $expr[0], ..., $expr[n-1] }, value => $expr[n] )
//d -> Set.Maybe.attr_or_default(
Array:{ $expr[0], ..., $expr[n-1] }, default => $expr[n] )
Examples:
(same [<=>] increase [<=>] decrease)
(F;'DEAD' B~ 1;'10001101' B~ F;'BEEF')
('hello' T~ ' ' T~ 'world')
(Array:[ 24, 52 ] A~ Array:[ -9 ] A~ Array:[ 0, 11, 24, 7 ])
($a // $b // 42)
($a //d $b //d T->sdp.lib.foo_type)
Simple Symmetric Dyadic Infix Operators
Grammar:
<sym_dyadic_infix_op_invo> ::=
'(' \s*
<expr> \s+ <sym_dyadic_infix_op> \s+ <expr>
\s* ')'
<sym_dyadic_infix_op> ::=
'=' | '≠' | '!='
| nand | '⊼' | '↑' | nor | '⊽' | '↓'
| 'I|-|' | 'N|-|'
A sym_dyadic_infix_op_invo
node is for using infix notation to invoke a symmetric dyadic operator function. Such a function takes exactly 2 arguments, which are the inputs of the operation; the inputs are all of the same type as each other but the result might be of either that type or a different type. A single sym_dyadic_infix_op_invo
node is equivalent to a single func_invo
node whose func_arg_list
element defines 2 arguments, and the 2 expr
elements of the sym_dyadic_infix_op_invo
supply the values of those arguments, and which arguments get which expr
isn't significant.
Some of the keywords are aliases for each other:
keyword | aliases
--------+--------
≠ | !=
nand | ⊼ ↑
nor | ⊽ ↓
This table indicates which function is invoked by each keyword:
= -> Core.Universal.is_identical( $expr[0], $expr[1] )
≠ -> Core.Universal.is_not_identical( $expr[0], $expr[1] )
nand -> Bool.nand( $expr[0], $expr[1] )
nor -> Bool.nor( $expr[0], $expr[1] )
I|-| -> Integer.abs_diff( $expr[0], $expr[1] )
N|-| -> Rational.abs_diff( $expr[0], $expr[1] )
Examples:
($foo = $bar)
($foo ≠ $bar)
(false nand true)
(15 I|-| 17)
(7.5 N|-| 9.0)
Simple Non-symmetric Dyadic Infix Operators
Grammar:
<nonsym_dyadic_infix_op_invo> ::=
'(' \s*
<lhs> \s+ <nonsym_dyadic_infix_op> \s+ <rhs>
\s* ')'
<lhs> ::=
<expr>
<rhs> ::=
<expr>
<nonsym_dyadic_infix_op> ::=
| isa | '!isa' | 'not isa' | as | asserting
| imp | '→' | implies | nimp | '↛' | if | '←' | nif | '↚'
| 'I-' | 'I/' | '%' | mod | 'I^'
| 'N-' | 'N/'
| Bx | Tx | Ax
| '∈' | '¬in;' | '∋' | '∌'
| 'S∈' | 'S¬in;' | 'S∋' | 'S∌'
| 'B∈' | 'B¬in;' | 'B∋' | 'B∌'
| '⊆' | '⊈' | '⊇' | '⊉'
| '⊂' | '⊄' | '⊃' | '⊅'
| '∖' | 'R-' | minus | except
| '⊿' | '!matching' | 'not matching' | antijoin | semiminus
| '⋉' | matching | semijoin
| '÷' | 'R/' | divideby
| like | '!like' | 'not like'
A nonsym_dyadic_infix_op_invo
node is for using infix notation to invoke a non-symmetric dyadic operator function. Such a function takes exactly 2 arguments, which are the inputs of the operation; the inputs and the result may possibly be all of the same type, or they might all be of different types. A single nonsym_dyadic_infix_op_invo
node is equivalent to a single func_invo
node whose func_arg_list
element defines 2 arguments, and the 2 expr
elements of the nonsym_dyadic_infix_op_invo
supply the values of those arguments, which are associated in the appropriate sequence.
Some of the keywords are aliases for each other:
keyword | aliases
--------+--------
!isa | 'not isa'
imp | → implies
nimp | ↛
if | ←
nif | ↚
% | mod
∖ | R- minus except
⊿ | !matching 'not matching' antijoin semiminus
⋉ | matching semijoin
÷ | R/ divideby
!like | 'not like'
Currently the alternate syntaxes for 20 functions, those testing set membership or sub/superset relationships, only come in versions that use trans-ASCII characters; if you are stuck using plain ASCII then you'll just have to use the generic function invocation syntax to invoke them for now. Plain ASCII infix syntax that is reasonable is yet to be determined.
This table indicates which function is invoked by each keyword:
isa -> Core.Universal.is_value_of_type( $lhs, type => $rhs )
!isa -> Core.Universal.is_not_value_of_type( $lhs, type => $rhs )
as -> Core.Universal.treated( $lhs, as => $rhs )
asserting -> Core.Universal.assertion( $lhs, is_true => $rhs )
imp -> Bool.imp( $lhs, $rhs )
nimp -> Bool.nimp( $lhs, $rhs )
if -> Bool.if( $lhs, $rhs )
nif -> Bool.nif( $lhs, $rhs )
I- -> Integer.diff( minuend => $lhs, subtrahend => $rhs )
I/ -> Integer.quotient( dividend => $lhs, divisor => $rhs )
% -> Integer.remainder( dividend => $lhs, divisor => $rhs )
I^ -> Integer.power( radix => $lhs, exponent => $rhs )
N- -> Rational.diff( minuend => $lhs, subtrahend => $rhs )
N/ -> Rational.quotient( dividend => $lhs, divisor => $rhs )
Bx -> Blob.replication( $lhs, count => $rhs )
Tx -> Text.replication( $lhs, count => $rhs )
Ax -> Array.replication( $lhs, count => $rhs )
∈ -> Core.Tuple.is_member( t => $lhs, r => $rhs )
¬in; -> Core.Tuple.is_not_member( t => $lhs, r => $rhs )
∋ -> Core.Relation.has_member( r => $lhs, t => $rhs )
∌ -> Core.Relation.has_not_member( r => $lhs, t => $rhs )
S∈ -> Set.value_is_member( value => $lhs, set => $rhs )
S¬in; -> Set.value_is_not_member( value => $lhs, set => $rhs )
S∋ -> Set.has_member( set => $lhs, value => $rhs )
S∌ -> Set.has_not_member( set => $lhs, value => $rhs )
B∈ -> Bag.value_is_member( value => $lhs, bag => $rhs )
B¬in; -> Bag.value_is_not_member( value => $lhs, bag => $rhs )
B∋ -> Bag.has_member( bag => $lhs, value => $rhs )
B∌ -> Bag.has_not_member( bag => $lhs, value => $rhs )
⊆ -> Core.Relation.is_subset( $lhs, $rhs )
⊈ -> Core.Relation.is_not_subset( $lhs, $rhs )
⊇ -> Core.Relation.is_superset( $lhs, $rhs )
⊉ -> Core.Relation.is_not_superset( $lhs, $rhs )
⊂ -> Relation.is_proper_subset( $lhs, $rhs )
⊄ -> Relation.is_not_proper_subset( $lhs, $rhs )
⊃ -> Relation.is_proper_superset( $lhs, $rhs )
⊅ -> Relation.is_not_proper_superset( $lhs, $rhs )
∖ -> Core.Relation.diff( source => $lhs, filter => $rhs )
⊿ -> Core.Relation.semidiff( source => $lhs, filter => $rhs )
⋉ -> Core.Relation.semijoin( source => $lhs, filter => $rhs )
÷ -> Core.Relation.quotient( dividend => $lhs, divisor => $rhs )
like -> sys.std.Text.is_like( look_in => $lhs, look_for => $rhs )
!like -> sys.std.Text.is_not_like( look_in => $lhs, look_for => $rhs )
Note that while the is[|_not]_like
functions also have an optional third parameter escape
, you will have to use a func_invo
node to exploit it; for simplicity, the infix like
and !like
don't support that customization; but most actual uses of like/etc don't use escape
anyway.
Examples:
($bar isa T->sdp.lib.foo_type)
($bar !isa T->sdp.lib.foo_type)
($scalar as T->Int)
($int asserting ($int ≠ 0))
(true implies false)
(34 I- 21)
(5 I/ 3)
(5 % 3)
(2 I^ 63)
(9.2 N- 0.1)
(1;101.01 N/ 1;11.0)
('-' Tx 80)
(Set:{ 8, 4, 6, 7 } ∖ Set:{ 9, 0, 7 })
(Relation:[ x, y ];{ [ 5, 6 ], [ 3, 6 ] } ÷ Relation:{ { y => 6 } })
Simple Monadic Prefix Operators
Grammar:
<monadic_prefix_op_invo> ::=
'(' \s*
<monadic_prefix_op> \s+ <expr>
\s* ')'
<monadic_prefix_op> ::=
d | not | '¬' | '!' | 'I||' | 'N||' | 'R#' | t | r | s | v
A monadic_prefix_op_invo
node is for using prefix notation to invoke a monadic operator function. Such a function takes exactly 1 argument, which is the input of the operation. A single monadic_prefix_op_invo
node is equivalent to a single func_invo
node whose func_arg_list
element defines 1 argument, and the 1 expr
element of the monadic_prefix_op_invo
supplies the value of that argument.
Some of the keywords are aliases for each other:
keyword | aliases
--------+--------
not | ¬ !
This table indicates which function is invoked by each keyword:
d -> Core.Universal.default( of => $expr )
not -> Core.Bool.not( $expr )
I|| -> Integer.abs( $expr )
N|| -> Rational.abs( $expr )
R# -> Core.Relation.cardinality( $expr )
t -> Core.Relation.Tuple_from_Relation( $expr )
r -> Core.Relation.Relation_from_Tuple( $expr )
s -> Set.Maybe.single( value => $expr )
v -> Set.Maybe.attr( $expr )
Examples:
(d T->sdp.lib.foo_type)
(not true)
(I|| -23)
(N|| -4.59)
(R# Set:{ 5, -1, 2 })
(t $relvar)
(r $tupvar)
(s ((v $a) N+ (v $b)))
Simple Monadic Postfix Operators
Grammar:
<monadic_postfix_op_invo> ::=
'(' \s*
<expr> \s+ <monadic_postfix_op>
\s* ')'
<monadic_postfix_op> ::=
'++' | '--' | 'I!'
A monadic_postfix_op_invo
node is for using prefix notation to invoke a monadic operator function. Such a function takes exactly 1 argument, which is the input of the operation. A single monadic_postfix_op_invo
node is equivalent to a single func_invo
node whose func_arg_list
element defines 1 argument, and the 1 expr
element of the monadic_postfix_op_invo
supplies the value of that argument.
This table indicates which function is invoked by each keyword:
++ -> Integer.inc( $expr )
-- -> Integer.dec( $expr )
I! -> Integer.factorial( $expr )
Examples:
(13 ++)
(4 --)
(5 I!)
Simple Postcircumfix Operators
Grammar:
<postcircumfix_op_invo> ::=
<pcf_acc_op_invo>
| <s_pcf_op_invo> | <t_pcf_op_invo> | <r_pcf_op_invo>
| <pcf_ary_op_invo>
<pcf_acc_op_invo> ::=
<pcf_s_acc_op_invo> | <pcf_t_acc_op_invo>
<pcf_s_acc_op_invo> ::=
<expr> \s* '.${' [\s* <possrep_name> \s* ';']? \s*
<attr_name>
\s* '}'
<pcf_t_acc_op_invo> ::=
<expr> \s* '.%{' \s* <attr_name> \s* '}'
<s_pcf_op_invo> ::=
<expr> \s* '${' [\s* <possrep_name> \s* ';']? \s*
[<pcf_projection> | <pcf_cmpl_proj>]
\s* '}'
<t_pcf_op_invo> ::=
<expr> \s* '%{' \s*
[
<pcf_rename>
| <pcf_projection> | <pcf_cmpl_proj>
| <pcf_wrap> | <pcf_cmpl_wrap>
| <pcf_unwrap>
]
\s* '}'
<r_pcf_op_invo> ::=
<expr> \s* '@{' \s*
[
<pcf_rename>
| <pcf_projection> | <pcf_cmpl_proj>
| <pcf_wrap> | <pcf_cmpl_wrap>
| <pcf_unwrap>
| <pcf_group> | <pcf_cmpl_group>
| <pcf_ungroup>
| <pcf_count_per_group>
]
\s* '}'
<pcf_rename> ::=
<pcf_rename_map>
<pcf_rename_map> ::=
[<atnm_after> \s* '<-' \s* <atnm_before>] ** [\s* ',' \s*]
<atnm_after> ::=
<attr_name>
<atnm_before> ::=
<attr_name>
<pcf_projection> ::=
<pcf_atnms>?
<pcf_cmpl_proj> ::=
'!' \s* <pcf_atnms>
<pcf_atnms> ::=
<attr_name> ** [\s* ',' \s*]
<pcf_wrap> ::=
'%' <outer_atnm> \s* '<-' \s* <inner_atnms>
<pcf_cmpl_wrap> ::=
'%' <outer_atnm> \s* '<-' \s* '!' \s* <cmpl_inner_atnms>
<pcf_unwrap> ::=
<inner_atnms> \s* '<-' \s* '%' <outer_atnm>
<pcf_group> ::=
'@' <outer_atnm> \s* '<-' \s* <inner_atnms>
<pcf_cmpl_group> ::=
'@' <outer_atnm> \s* '<-' \s* '!' \s* <cmpl_inner_atnms>
<pcf_ungroup> ::=
<inner_atnms> \s* '<-' \s* '@' <outer_atnm>
<pcf_count_per_group> ::=
'#@' <count_atnm> \s* '<-' \s* '!' \s* <cmpl_inner_atnms>
<outer_atnm> ::=
<attr_name>
<count_atnm> ::=
<attr_name>
<inner_atnms> ::=
<pcf_atnms>
<cmpl_inner_atnms> ::=
<pcf_atnms>
<pcf_ary_op_invo> ::=
<pcf_ary_acc_op_invo> | <pcf_ary_slice_op_invo>
<pcf_ary_value_op_invo> ::=
<expr> \s* '.[' \s* <index> \s* ']'
<index> ::=
<num_max_col_val> \s* ';' \s* <nnint_body>
| <d_nnint_body>
<pcf_ary_slice_op_invo> ::=
<expr> \s* '[' \s* <first_index> \s* '..' \s* <last_index> \s* ']'
<first_index> ::=
<index>
<last_index> ::=
<index>
A postcircumfix_op_invo
node is for using postcircumfix notation to invoke a relational operator function whose operation involves deriving a single tuple|relation from another single tuple|relation customized only by further inputs that are attribute names. Such a function takes exactly 2 (expr
and pcf_rename_map
|pcf_atnms
) or 3 (expr
and outer_atnm
and inner_atnms
|cmpl_inner_atnms
) or 3 (expr
and count_atnm
and cmpl_inner_atnms
) primary arguments, which are the inputs of the operation. A single postcircumfix_op_invo
node is equivalent to a single func_invo
node whose func_arg_list
element defines 2-3 arguments, and the 2-3 expr|pcf[_rename_map|atnms]|[outer|count]_atnm|[|cmpl_]inner_atnms
elements of the postcircumfix_op_invo
supply the values of those arguments, which are associated in the appropriate sequence.
This table indicates which function is invoked by each format-keyword:
.${} -> Core.Scalar.attr( $expr, possrep => $possrep_name,
name => $attr_name )
.%{} -> Core.Tuple.attr( $expr, name => $attr_name )
%{<-} -> Core.Tuple.rename( $expr,
map => Relation:{
{ after => $atnm_after[0], before => $atnm_before[0] },
...,
{ after => $atnm_after[n], before => $atnm_before[n] },
} )
@{<-} -> Core.Relation.rename( $expr,
map => Relation:{
{ after => $atnm_after[0], before => $atnm_before[0] },
...,
{ after => $atnm_after[n], before => $atnm_before[n] },
} )
${} -> Core.Scalar.projection( $expr, possrep => $possrep_name,
attr_names => Set:{ $pcf_atnms[0], ..., $pcf_atnms[n] } )
%{} -> Core.Tuple.projection( $expr,
attr_names => Set:{ $pcf_atnms[0], ..., $pcf_atnms[n] } )
@{} -> Core.Relation.projection( $expr,
attr_names => Set:{ $pcf_atnms[0], ..., $pcf_atnms[n] } )
${!} -> Core.Scalar.cmpl_proj( $expr, possrep => $possrep_name,
attr_names => Set:{ $pcf_atnms[0], ..., $pcf_atnms[n] } )
%{!} -> Core.Tuple.cmpl_proj( $expr,
attr_names => Set:{ $pcf_atnms[0], ..., $pcf_atnms[n] } )
@{!} -> Core.Relation.cmpl_proj( $expr,
attr_names => Set:{ $pcf_atnms[0], ..., $pcf_atnms[n] } )
%{%<-} -> Core.Tuple.wrap( $expr, outer => $outer_atnm,
inner => Set:{ $inner_atnms[0], ..., $inner_atnms[n] } )
@{%<-} -> Core.Relation.wrap( $expr, outer => $outer_atnm,
inner => Set:{ $inner_atnms[0], ..., $inner_atnms[n] } )
%{%<-!} -> Core.Tuple.cmpl_wrap( $expr, outer => $outer_atnm,
cmpl_inner => Set:{ $cmpl_inner_atnms[0], ... } )
@{%<-!} -> Core.Relation.cmpl_wrap( $expr, outer => $outer_atnm,
cmpl_inner => Set:{ $cmpl_inner_atnms[0], ... } )
%{<-%} -> Core.Tuple.unwrap( $expr,
inner => Set:{ $inner_atnms[0], ..., $inner_atnms[n] },
outer => $outer_atnm )
@{<-%} -> Core.Relation.unwrap( $expr,
inner => Set:{ $inner_atnms[0], ..., $inner_atnms[n] },
outer => $outer_atnm )
@{@<-} -> Core.Relation.group( $expr, outer => $outer_atnm,
inner => Set:{ $inner_atnms[0], ..., $inner_atnms[n] } )
@{@<-!} -> Core.Relation.cmpl_group( $expr, outer => $outer_atnm,
group_per => Set:{ $cmpl_inner_atnms[0], ... } )
@{<-@} -> Core.Relation.ungroup( $expr,
inner => Set:{ $inner_atnms[0], ..., $inner_atnms[n] },
outer => $outer_atnm )
@{#@<-!} -> Core.Relation.cardinality_per_group( $expr,
count_attr_name => $count_atnm,
group_per => Set:{ $cmpl_inner_atnms[0], ... } )
.[] -> Array.value( $expr, index => $index )
[..] -> Array.slice( $expr, first_index => $first_index,
last_index => $last_index )
Examples:
$birthday.${date;day}
$pt.%{city}
$pt%{pnum<-pno, locale<-city}
$pr@{pnum<-pno, locale<-city}
$birthday${date;year,month}
$pt%{color,city}
$pr@{color,city}
$pt%{} # null projection #
$pr@{} # null projection #
$rnd_rule${!round_meth} # radix,min_exp #
$pt%{!pno,pname,weight}
$pr@{!pno,pname,weight}
$person%{%name <- fname,lname}
$people@{%name <- fname,lname}
$person%{%all_but_name <- !fname,lname}
$people@{%all_but_name <- !fname,lname}
$person%{fname,lname <- %name}
$people@{fname,lname <- %name}
$orders@{@vendors <- vendor}
$orders@{@all_but_vendors <- !vendor}
$orders@{vendor <- @vendors}
$people@{#@count_per_age_ctry <- !age,ctry}
$ary.[3]
$ary[10..14]
Rational Operators That Do Rounding
Grammar:
<rat_op_invo_with_round> ::=
'(' \s*
<rat_op_invo> \s+ <rounded_with_rule_clause>
\s* ')'
<rat_op_invo> ::=
<expr>
| <infix_rat_op_invo>
| <prefix_rat_op_invo>
| <postfix_rat_op_invo>
<infix_rat_op_invo> ::=
<lhs> \s+ <infix_rat_op> \s+ <rhs>
<infix_rat_op> ::=
'N^' | log
<prefix_rat_op_invo> ::=
<expr> \s+ <prefix_rat_op>
<prefix_rat_op>
'e^'
<postfix_rat_op_invo> ::=
<expr> \s+ <postfix_rat_op>
<postfix_rat_op>
loge
<rounded_with_rule_clause> ::=
round \s+ <round_rule>
<round_rule> ::=
<expr>
A rat_op_invo_with_round
node is for using infix or prefix or postfix notation to invoke a rational numeric operator function whose operation involves rounding a number to one with less precision. Such a function takes exactly 1 (expr
) or 2 (lhs
and rhs
) primary arguments, which are the inputs of the operation, plus a special round_rule
argument which specifies explicitly the semantics of the numeric rounding in a declarative way (all 2 or 3 of these are main op args). A single rat_op_invo_with_round
node is equivalent to a single func_invo
node whose func_arg_list
element defines 2-3 arguments, and the expr|lhs|rhs|round_rule
elements of the rat_op_invo_with_round
supply the values of those arguments, which are associated in the appropriate sequence.
This table indicates which function is invoked by each keyword:
-> Rational.round( $expr, round_rule => $round_rule )
N^ -> Rational.power( radix => $lhs, exponent => $rhs,
round_rule => $round_rule )
log -> Rational.log( $lhs, radix => $rhs, round_rule => $round_rule )
e^ -> Rational.natural_power( $expr, round_rule => $round_rule )
loge -> Rational.natural_log( $expr, round_rule => $round_rule )
Examples:
($foo round RatRoundRule:[10,-2,half_even])
(2.0 N^ 0.5 round RatRoundRule:[2,-7,to_zero])
(309.1 log 5.4 round RatRoundRule:[10,-4,half_up])
(e^ 6.3 round RatRoundRule:[10,-6,to_ceiling])
(17.0 loge round RatRoundRule:[3,-5,to_floor])
Order Comparison Operators
Grammar:
<ord_compare_op_invo> ::=
'(' \s*
[<sca_ord_det_op_invo> | <gen_compare_op_invo>]
\s* ')'
<sca_ord_det_op_invo> ::=
<lhs> \s+ '<=>' \s+ <rhs>
[\s+ <assuming_clause>]?
[\s+ <reversed_clause>]?
<gen_compare_op_invo> ::=
[
<compare_reduce_op_invo>
| <dyadic_compare_op_invo>
| <triadic_compare_op_invo>
]
[\s+ <gen_compare_op_ordered_clause>]?
<compare_reduce_op_invo> ::=
<expr> ** [\s+ <compare_reduce_op> \s+]
<compare_reduce_op ::=
min | max
<dyadic_compare_op_invo> ::=
<lhs> \s+ <dyadic_compare_op> \s+ <rhs>
<dyadic_compare_op> ::=
<is_before_op> | <is_after_op>
<is_before_op> ::=
'<' | '≤' | '<='
<is_after_op> ::=
'>' | '≥' | '>='
<triadic_compare_op_invo> ::=
[[not | '!'] \s+]?
<min> \s+ <is_before_op> \s+ <expr> \s+ <is_before_op> \s+ <max>
<min> ::=
<expr>
<max> ::=
<expr>
<gen_compare_op_ordered_clause> ::=
ordered \s+ <using_clause> | <ordered_by_clause>
An ord_compare_op_invo
node is for using infix notation to invoke an order comparison operator function. Such a function takes exactly 2 (lhs
and rhs
) or 3 (min
and expr
and max
) or N/2+ (expr
) main op args, which are the inputs of the operation, plus 2 extra op args (assuming
and reversed
for the <=>
op, or func
and assuming
for any other op) which let you customize the semantics of the operation. A single ord_compare_op_invo
node is equivalent to a single func_invo
node whose func_arg_list
element defines 2-N arguments, and the expr|lhs|rhs|min|max|func|assuming|reversed
elements of the ord_compare_op_invo
supply the values of those arguments, which are associated in the appropriate sequence, except for the N-adic operators which are commutative (and associative and idempotent) so the relative order of the main op args isn't significant. Details on the extra op args are pending.
Some of the keywords are aliases for each other:
keyword | aliases
--------+--------
≤ | <=
≥ | >=
≤≤ | <=<=
≤< | <=<
<≤ | <<=
!≤≤ | !<=<=
!≤< | !<=<
!<≤ | !<<=
This table indicates which function is invoked by each keyword:
<=> -> Core.Scalar.order( $lhs, $rhs )
min -> Ordered.min( Set:{ $expr[0], ..., $expr[n] } )
max -> Ordered.max( Set:{ $expr[0], ..., $expr[n] } )
< -> Ordered.is_before( $lhs, $rhs )
> -> Ordered.is_after( $lhs, $rhs )
≤ -> Ordered.is_before_or_same( $lhs, $rhs )
≥ -> Ordered.is_after_or_same( $lhs, $rhs )
≤≤ -> Ordered.is_inside_range( $expr, min => $min, max => $max )
≤< -> Ordered.is_inside_range( $expr, min => $min, max => $max
max_is_outside => true )
<≤ -> Ordered.is_inside_range( $expr, min => $min, max => $max
min_is_outside => true )
<< -> Ordered.is_inside_range( $expr, min => $min, max => $max
min_is_outside => true, max_is_outside => true )
!≤≤ -> Ordered.is_outside_range( $expr, min => $min, max => $max )
!≤< -> Ordered.is_outside_range( $expr, min => $min, max => $max
max_is_outside => true )
!<≤ -> Ordered.is_outside_range( $expr, min => $min, max => $max
min_is_outside => true )
!<< -> Ordered.is_outside_range( $expr, min => $min, max => $max
min_is_outside => true, max_is_outside => true )
Details regarding the extra op args is pending. But most of the time you wouldn't be using them, so just the main args represents typical usage.
Examples (for now sans any use of extra op args, which are atypical):
($foo <=> $bar)
($a min $b min $c)
($a max $b max $c)
($foo < $bar)
($foo > $bar)
($foo ≤ $bar)
($foo ≥ $bar)
($min ≤ $foo ≤ $max)
($min ≤ $foo < $max)
(not $min < $foo ≤ $max)
(not $min < $foo < $max)
SEE ALSO
Go to Muldis::D for the majority of distribution-internal references, and Muldis::D::SeeAlso for the majority of distribution-external references.
AUTHOR
Darren Duncan (perl@DarrenDuncan.net
)
LICENSE AND COPYRIGHT
This file is part of the formal specification of the Muldis D language.
Muldis D is Copyright © 2002-2009, Muldis Data Systems, Inc.
See the LICENSE AND COPYRIGHT of Muldis::D for details.
TRADEMARK POLICY
The TRADEMARK POLICY in Muldis::D applies to this file too.
ACKNOWLEDGEMENTS
The ACKNOWLEDGEMENTS in Muldis::D apply to this file too.