Interpret
Methods
Angle
#A bare number is not an angle; the unit is what makes it one. Named units may be separated from the number by whitespace, but the degree and percent symbols may not, since "50 %" reads as a number and a stray symbol rather than as a slope.
| view (Fragments) |
Bind<T>
#This is the entry point for a type assistant which wants to accept constructor notation of its own. The bound views are the values as written, one per entry in the signature and null where an optional argument was left out, ready to be read by whichever interpreter suits them.
| arguments (Fragments) | View of the arguments, without the brackets. |
| syntax (ArgumentSyntax[]) | Description of each argument the notation accepts. |
| bound (out Argument[]) | Argument for each entry, or null where absent. |
| failure (out Result<T>) | Description of the mismatch, if there was one. |
Boolean
#| view (Fragments) |
Colour
#Either a name, which covers adjective and name combinations, name pairs and hexadecimal by way of ColourConversions.TryParseNamedColour(string, Color, bool, bool), or three channels in a named space: "rgb(0.6, 0.3, 0)", with or without the commas.
The space is always the name in front of the brackets. There is no notation for naming the channels individually, since "rgb" already says what the three numbers behind it are.
Complex
#The exponent binds tighter than the scale, as it does everywhere else, so "5i²" is minus five rather than minus twenty five. Write "(5i)²" for the other reading, which is an expression rather than a literal and belongs to NumericExpression(Fragments, CancellationToken).
Notation which joins a real and an imaginary part, such as "4+5i", is addition and likewise belongs to the expression interpreter. Implementing it here as well would be a second, weaker version of the same operator, and one which could never reach "(2+3i)*(1-i)".
| view (Fragments) |
Coordinate
#| view (Fragments) |
Date
#The text is matched against a fixed set of layouts rather than walked fragment by fragment, because the dotted form collides with decimal notation: the scanner quite reasonably reads "2024.05" as a number.
| view (Fragments) |
Frame
#Shapes which sit on a plane, such as circles and boxes, almost always want it in one of two ways: fully, or as a centre with the orientation left alone. This reads either, so "circle(1,2,3, r: 5)" and "circle(xy, r: 5)" both work.
Function
#The notation is parsed by the same tree parser which serves NumericExpression(Fragments, CancellationToken), and the tree is then lowered into the standard function types wherever a shape matches: "2pi*x" becomes a linear function, "x²+1" a parabola, "sin(2x)" a sine. Whatever has no native equivalent is compiled into an ExpressionFunction instead.
Functions are real valued, so where the expression evaluator would step into the complex plane, a function reports NaN, and a complex constant in the body is refused outright.
| view (Fragments) |
Gradient
#Either the name of a standard gradient, or a comma separated list of colours. Commas are what separate the pieces, which is why a colour with a space in it needs nothing around it: "red, dark blue" is two grips, not three.
Grips are either all parameterised or none of them are. Bare colours are spread evenly from zero to one, so "red, yellow, blue" puts yellow at a half; a leading "0.25:" on each says exactly where they go. Mixing the two says nothing coherent, so it is rejected.
A colour space and a progression may appear anywhere in the list, at most one of each: "hsv, cubic, red, blue".
Any of it may be followed by "from a to b", which remaps the domain. That phrase belongs to the whole notation rather than to the grips, so it sits outside any brackets around them.
| view (Fragments) |
Guid
#A guid has no internal structure worth interpreting, and the scanner will have cut it into an arbitrary mixture of numbers and words depending on which characters happen to be digits. So the text is handed to the framework whole, which also means every format it accepts is accepted here.
| view (Fragments) |
Integer
#This asks how the number was written rather than what it is worth, so "4.0" is rejected even though four is a whole number. Use Number(Fragments) and test the value if that is not what you want.
| view (Fragments) |
Integer32
#Too large is a different complaint from not being a whole number at all, and this says which.
| view (Fragments) |
Interval
#Half open notation like "[5,8)" is recognised, but note that Interval has nowhere to record inclusivity, so at present the bracket shapes are only used to tell interval notation apart from point notation.
| view (Fragments) |
List
#Square brackets, and only square brackets, are what make a list a list. Without them "1, 2, 3" is a point, and there would be no way to tell a one item list from the item itself. Leaving the other two shapes out keeps them free for the things which are written with them, so that "(1, 2, 3)" is a point and "(4 to 6)" is an interval which happens to have been bracketed.
Items are read by Sniff(Fragments, Notation, Notation, CancellationToken), so a list may hold anything a field could, and a mixed list is perfectly acceptable. Collections do not nest, since a twig holds values rather than more twigs; a list or a range written inside one is kept as the text it was written as.
| view (Fragments) | View to interpret. |
| allowed (Notation = Notation.SingleValue) | Notations the items are allowed to use. |
| token (CancellationToken = null) | Token for abandoning a long running evaluation. |
Null
#| view (Fragments) |
Number
#Only values on the plain number line qualify. An angle carries a unit and a complex number carries an imaginary part, so neither is a number here; use NumericExpression(Fragments, CancellationToken) to accept those too.
| view (Fragments) |
NumericExpression
#| view (Fragments) | View to interpret. |
| token (CancellationToken = null) | Token for abandoning a long running evaluation. Nothing here is capped, so a large factorial can run for a while, and cancelling one throws rather than returning a failure. |
| OperationCanceledException | Thrown when the token is signalled part way through an evaluation. |
Path
#The elements of a data path are whole numbers and nothing else. Wildcards and expressions belong to path mapping, which cuts the same notation up with PathPattern(Fragments) and reads the pieces its own way.
An item, if one is written, is not part of the path and is ignored here; use PathPattern(Fragments) when it matters.
Elements are literals rather than expressions, unlike everywhere else in the notation. Paths are read constantly and almost never written by hand, so the evaluator would cost far more than it could pay for.
| view (Fragments) |
PathPattern
#Notation is a braced, semicolon separated list, optionally followed by a bracketed item: "{0;1}", "{0;1}[4]", "{*;3}[end]".
Nothing is said here about what an element contains. That is the point of splitting first: a data path holds whole numbers, a path map holds wildcards and expressions as well, and both want the same cut made before they start reading. Each element arrives as a view of its own, so whatever reads it can consume the whole thing or fail.
| view (Fragments) | View to cut up. |
Plane
#| view (Fragments) |
Point2
#| view (Fragments) |
Point3
#| view (Fragments) |
Range
#The notation is its own small language, handled in full by RangeNotation. All this does is establish that a range was meant before handing the text over, so that ordinary numbers are never put through it.
What comes back are the numbers themselves rather than the ranges which describe them, one twig per range, since a range is notation for a sequence rather than a value in its own right.
| view (Fragments) |
Sniff
#Notations are tried from the most constrained to the least, so a reader which only accepts a narrow shape cannot take work from a broader one. Whole view reading does most of the disambiguating on its own: a reader which would once have matched the "45" of "45deg" and left the rest behind now simply fails, so the order only has to settle genuine ambiguities, of which there are few.
The built in types read their own constructor notation, so "point(1,2,3)" is read by the point reader and reported as Notation.Point. Notation.Constructor covers the rest: names claimed by any other assistant, including ones from plug-ins, which are looked up in the registry.
| view (Fragments) | View to interpret. |
| allowed (Notation) | Notations the caller is willing to accept. |
| used (out Notation) | Notation which was read, or None when nothing was. |
| token (CancellationToken = null) | Token for abandoning a long running evaluation. |
Text
#A single quoted literal yields its content with the escapes resolved. Anything else yields the characters as written, which is what makes text the last resort of a dispatcher: whatever nothing else could read is still perfectly good text.
Defects are deliberately ignored here, unlike everywhere else. Prose is full of apostrophes and unmatched brackets, and "it's fine" has to remain readable as the sentence it obviously is.
| view (Fragments) |