Interpret

public static class Interpret
Interprets Fragments views as specific types. Think of Fragments as the tokenizer for value notation and the Interpret class as the parser.

Methods

Angle

#
public static Result<Angle> Angle(Fragments view)
Interpret a view as an angle, written as a number followed by a unit.

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>

#
public static bool Bind<T>(Fragments arguments, ArgumentSyntax[] syntax, out Argument[] bound, out Result<T> failure)
Read the arguments of a constructor notation against a signature.

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.
True if every argument found a home.

Boolean

#
public static Result<bool> Boolean(Fragments view)
Interpret a view as a boolean. Note that '0' and '1' are not parsed as valid booleans, you will need to interpret integers as well if that is desired.
view (Fragments)

Colour

#
public static Result<Colour> Colour(Fragments view, bool fuzzy = false)
Interpret a view as a 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.

view (Fragments)View to interpret.
fuzzy (bool = false)If true, approximate matches against known colour names are accepted.

Complex

#
public static Result<Complex> Complex(Fragments view)
Interpret a view as a single complex literal, which takes the shape [sign] [scale] i [exponent], i.e. "i", "5i", "i³" or "-5i^3". A real number is also accepted, yielding a zero imaginary part, so that a real leaf can be lifted into the complex plane.

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

#
public static Result<double[]> Coordinate(Fragments view)
Interpret a view as a list of at least two numbers, separated either by commas or by whitespace.
view (Fragments)

Date

#
public static Result<DateTime> Date(Fragments view)
Interpret a view as a date, i.e. "2024-05-01", "2024/May/1" or "today".

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

#
public static Result<Plane> Frame(Fragments view, Plane? existing = null)
Interpret a view as a plane, accepting a bare point in place of one.

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.

view (Fragments)View to interpret.
existing (Plane? = null)Plane whose axes are kept when only a point is given. Defaults to the world XY plane.

Function

#
public static Result<Function> Function(Fragments view)
Interpret a view as a univariate function of x, i.e. "sin(x)" or "2x+1".

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

#
public static Result<Gradient> Gradient(Fragments view)
Interpret a view as a colour 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

#
public static Result<Guid> Guid(Fragments view)
Interpret a view as a globally unique identifier.

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

#
public static Result<BigInteger> Integer(Fragments view)
Interpret a view as an 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

#
public static Result<int> Integer32(Fragments view)
Interpret a view as an integer which fits in an int.

Too large is a different complaint from not being a whole number at all, and this says which.

view (Fragments)

Interval

#
public static Result<Interval> Interval(Fragments view)
Interpret a view as an interval, written either as "3 to 7" or as a bracketed pair such as "[3,7]".

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

#
public static Result<ITwig> List(Fragments view, Notation allowed = Notation.SingleValue, CancellationToken token = default(CancellationToken))
Interpret a view as a list of values, i.e. "[1, 2, 3]" or "[red, green]".

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

#
public static Result<object> Null(Fragments view)
Interpret a view as the absence of a value. A successful result carries a null value, which is the whole point of it.
view (Fragments)

Number

#
public static Result<double> Number(Fragments view)
Interpret a view as a number, whether it was written as one or works out to one, so "2*3" is as good a number as "6".

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

#
public static Result<object> NumericExpression(Fragments view, CancellationToken token = default(CancellationToken))
Interpret a view as a numeric expression, i.e. "4+78", "(1+2)/3" or "45°+30°". The value carried by a successful result is a BigInteger, a double, a Complex or an Angle, depending on what the arithmetic produced; whole number work stays exact until something forces it not to.
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.
OperationCanceledExceptionThrown when the token is signalled part way through an evaluation.

Path

#
public static Result<Path> Path(Fragments view)
Interpret a view as a data path, i.e. "{0;1}".

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

#
public static Result<(Fragments[] elements, Fragments item)> PathPattern(Fragments view)
Cut a path notation into a view of each of its elements, and a view of its item when it has one.

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

#
public static Result<Plane> Plane(Fragments view)
Interpret a view as a plane, either constructed, i.e. "plane(1,2,3)" or "pl(o: 0,0,0, n: z)", as a bare list of named arguments, i.e. "o:(0,0,0) z:(0,0,1)", or as one of the world planes, i.e. "xy", "world-zx" or "-yz".
view (Fragments)

Point2

#
public static Result<Point2d> Point2(Fragments view)
Interpret a view as a two dimensional point, i.e. "3, 4" or "uv(3, 4)".
view (Fragments)

Point3

#
public static Result<Point3d> Point3(Fragments view)
Interpret a view as a three dimensional point. A two dimensional coordinate is accepted, and lies on the world XY plane.
view (Fragments)

Range

#
public static Result<ITree> Range(Fragments view)
Interpret a view as numeric range notation, i.e. "1..10" or "0..1..#5".

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

#
public static IResult Sniff(Fragments view, Notation allowed, out Notation used, CancellationToken token = default(CancellationToken))
Work out what a view most likely says, and read it as that.

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

#
public static Result<string> Text(Fragments view)
Interpret a view as 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)

Time

#
public static Result<TimeSpan> Time(Fragments view)
Interpret a view as a duration or a time of day, i.e. "12:30", "2 days 6:00" or "elevenses".

A colon is required, which is what keeps "12.5" a number rather than half past noon.

view (Fragments)

Vector3

#
public static Result<Vector3d> Vector3(Fragments view)
Interpret a view as a three dimensional vector, either as a coordinate or as the name of an axis.
view (Fragments)