#!/usr/bin/env dub
/+ dub.sdl:
name "property_tree_path_addressing"
targetPath "build"
dflags "-preview=in" "-preview=dip1000"
buildType "checked" {
buildOptions "optimize" "inline" "debugInfo"
}
+/
/**
* The path as the node address (fork D3), in both directions.
*
* Grammar: path := seg ( "." name | "[" digits "]" | "[#" digits "]" )*
* seg := name | '["' quoted '"]' (backslash-escaped ", \)
*
* Under test:
* C8. The SAME path text resolves two ways: at compile time it becomes a
* direct field access (`subject.material.stops[1].weight`, no lookup,
* `ref`-returning, so writes are ordinary assignments); at run time it
* walks a generated dispatch with the same segment semantics.
* C9. The runtime walk is generated from the type, parameterised by TYPE
* ONLY ([`open-set-descent.d`](./open-set-descent.d)'s rule), so it terminates on recursive types and needs
* no registry.
* C10. Both directions agree — verified by differential test over every
* path the planner emits.
* C11. Index paths are POSITIONAL, and that is a real defect, not a
* nitpick: deleting element 0 silently re-points every later element's
* expansion, selection and in-progress edit (rjsf's synthetic-key
* finding).
* C25. The fix is element-provided identity, not an adapter side table: an
* element type opting in with `ulong propElementKey() const` makes
* `[#7]` resolve BY KEY through the same generated walk, unmoved by
* removal or reorder of earlier elements. A duplicate or absent key —
* and `[#…]` on a collection that never opted in — is refused, never
* resolved positionally. (Added with the spec review; the earlier
* revision minted the id in an adapter-owned table, the ambient side
* table PRN1/PRN2 argue against.)
* C26. Names outside the identifier subset (erased children: JSON keys with
* `.`, `[`, spaces, leading digits) use a QUOTED segment `["…"]` with
* backslash escapes. The emitter picks bare exactly when the name is
* identifier-shaped, so every emitted path re-parses to the same
* segments; the quoted and bare spellings of the same member resolve
* identically.
*
* Run: `dub run --single path-addressing.d`
*/
module (module) property_tree_path_addressingThe path as the node address (fork D3), in both directions.
Grammar
path := seg ( "." name | "" digits "" | "#" digits "" )*
seg := name | '"' quoted '"' (backslash-escaped ", )
Under test:
C8. The SAME path text resolves two ways: at compile time it becomes a
direct field access (subject.material.stops[1].weight, no lookup,
ref-returning, so writes are ordinary assignments); at run time it
walks a generated dispatch with the same segment semantics.
C9. The runtime walk is generated from the type, parameterised by TYPE
ONLY (open-set-descent.d's rule), so it terminates on recursive types and needs
no registry.
C10. Both directions agree — verified by differential test over every
path the planner emits.
C11. Index paths are POSITIONAL, and that is a real defect, not a
nitpick: deleting element 0 silently re-points every later element's
expansion, selection and in-progress edit (rjsf's synthetic-key
finding).
C25. The fix is element-provided identity, not an adapter side table: an
element type opting in with ulong propElementKey() const makes
[#7] resolve BY KEY through the same generated walk, unmoved by
removal or reorder of earlier elements. A duplicate or absent key —
and [#…] on a collection that never opted in — is refused, never
resolved positionally. (Added with the spec review; the earlier
revision minted the id in an adapter-owned table, the ambient side
table PRN1/PRN2 argue against.)
C26. Names outside the identifier subset (erased children: JSON keys with
., [, spaces, leading digits) use a QUOTED segment ["…"] with
backslash escapes. The emitter picks bare exactly when the name is
identifier-shaped, so every emitted path re-parses to the same
segments; the quoted and bare spellings of the same member resolve
identically.
Run
dub run --single path-addressing.d
property_tree_path_addressing;
import (package) stdstd.(module) std.convA one-stop shop for converting values from one type to another.
Category Functions Generic asOriginalType castFrom parse to toChars bitCast Strings text wtext dtext writeText writeWText writeDText hexString Numeric octal roundTo signed unsigned Exceptions ConvException ConvOverflowException
Source
std/conv.d
conv : (alias template) property_tree_path_addressing.text = std.conv.text(T...)(T args) if (T.length > 0)Convenience functions for converting one or more arguments
of any type into text (the three character widths).
text, (alias template) property_tree_path_addressing.to = std.conv.to(T)The to template converts a value from one type to another.
The source type is deduced and the target type must be specified, for example the
expression to`!int(42.0)` converts the number 42 from
`double` to `int`. The conversion is "safe", i.e.,
it checks for overflow; to!int(4.2e10) would throw the
ConvOverflowException exception. Overflow checks are only
inserted when necessary, e.g., ``to!double(42) does not do
any checking because any int fits in a double.
Conversions from string to numeric types differ from the C equivalents
atoi() and atol() by checking for overflow and not allowing whitespace.
For conversion of strings to signed types, the grammar recognized is:
Integer:
Sign UnsignedInteger
UnsignedInteger
Sign:
+
-
For conversion to unsigned types, the grammar recognized is:
UnsignedInteger:
DecimalDigit
DecimalDigit UnsignedInteger
to;
import (package) stdstd.(module) std.traitsTemplates which extract information about types and symbols at compile time.
Category Templates Symbol Name traits fullyQualifiedName mangledName moduleName packageName Function traits isFunction arity functionAttributes hasFunctionAttributes functionLinkage FunctionTypeOf isSafe isUnsafe isFinal ParameterDefaults ParameterIdentifierTuple ParameterStorageClassTuple Parameters ReturnType SetFunctionAttributes variadicFunctionStyle Aggregate Type traits BaseClassesTuple BaseTypeTuple classInstanceAlignment EnumMembers FieldNameTuple Fields hasAliasing hasElaborateAssign hasElaborateCopyConstructor hasElaborateDestructor hasElaborateMove hasIndirections hasMember hasStaticMember hasNested hasUnsharedAliasing InterfacesTuple isInnerClass isNested MemberFunctionsTuple RepresentationTypeTuple TemplateArgsOf TemplateOf TransitiveBaseTypeTuple Type Conversion CommonType AllImplicitConversionTargets ImplicitConversionTargets CopyTypeQualifiers CopyConstness isAssignable isCovariantWith isImplicitlyConvertible isQualifierConvertible Type Constructors InoutOf ConstOf SharedOf SharedInoutOf SharedConstOf SharedConstInoutOf ImmutableOf QualifierOf Categories of types allSameType ifTestable isType isAggregateType isArray isAssociativeArray isAutodecodableString isBasicType isBoolean isBuiltinType isCopyable isDynamicArray isEqualityComparable isFloatingPoint isIntegral isNarrowString isConvertibleToString isNumeric isOrderingComparable isPointer isScalarType isSigned isSIMDVector isSomeChar isSomeString isStaticArray isUnsigned Type behaviours isAbstractClass isAbstractFunction isCallable isDelegate isExpressions isFinalClass isFinalFunction isFunctionPointer isInstanceOf isIterable isMutable isSomeFunction isTypeTuple General Types ForeachType KeyType Largest mostNegative OriginalType PointerTarget Signed Unconst Unshared Unqual Unsigned ValueType Promoted Misc lvalueOf rvalueOf Select select User-Defined Attributes hasUDA getUDAs getSymbolsByUDA
Source
std/traits.d
traits : (alias template) property_tree_path_addressing.isAggregateType = std.traits.isAggregateType(T)Detect whether type T is an aggregate type.
isAggregateType, (alias template) property_tree_path_addressing.isArray = std.traits.isArray(T)Detect whether type T is an array (static or dynamic; for associative
arrays see isAssociativeArray).
isArray, (alias template) property_tree_path_addressing.isDynamicArray = std.traits.isDynamicArray(T)Detect whether type T is a dynamic array.
isDynamicArray, (alias template) property_tree_path_addressing.isSomeString = std.traits.isSomeString(T)Detect whether T is one of the built-in string types.
The built-in string types are Char[], where Char is any of char,
wchar or dchar, with or without qualifiers.
Static arrays of characters (like char[80]) are not considered
built-in string types.
isSomeString;
@safe:
// ── path segments ────────────────────────────────────────────────────────────
struct (struct) property_tree_path_addressing.SegSeg { (alias) object.string = stringstring (field) string property_tree_path_addressing.Seg.namename; (alias) object.size_t = ulongsize_t (field) ulong property_tree_path_addressing.Seg.indexindex; bool (field) bool property_tree_path_addressing.Seg.isIndexisIndex; bool (field) bool property_tree_path_addressing.Seg.isKeyisKey; ulong (field) ulong property_tree_path_addressing.Seg.keykey; }
/// Parses at CTFE and at run time — the same function.
(struct) property_tree_path_addressing.SegSeg[] property_tree_path_addressing.Seg[] property_tree_path_addressing.segments(const(char)[] path) pure @safeParses at CTFE and at run time — the same function.
segments(const(char)[] (parameter) const(char)[] pathpath) pure
{
(struct) property_tree_path_addressing.SegSeg[] (local variable) property_tree_path_addressing.Seg[] segssegs;
(alias) object.size_t = ulongsize_t (local variable) ulong ii;
while ((local variable) ulong ii < (parameter) const(char)[] pathpath.(field) ulong const(char)[].lengthlength)
{
if ((parameter) const(char)[] pathpath[(local variable) ulong ii] == '.') { (local variable) ulong ii++; continue; }
if ((parameter) const(char)[] pathpath[(local variable) ulong ii] == '[')
{
if ((local variable) ulong ii + 1 < (parameter) const(char)[] pathpath.(field) ulong const(char)[].lengthlength && (parameter) const(char)[] pathpath[(local variable) ulong ii + 1] == '#') // [#key] (C25)
{
(alias) object.size_t = ulongsize_t (local variable) ulong jj = (local variable) ulong ii + 2;
while ((local variable) ulong jj < (parameter) const(char)[] pathpath.(field) ulong const(char)[].lengthlength && (parameter) const(char)[] pathpath[(local variable) ulong jj] != ']') (local variable) ulong jj++;
(struct) property_tree_path_addressing.SegSeg (local variable) property_tree_path_addressing.Seg ss = { isKey: true, key: ulong std.conv.to!ulong.to!(const(char)[])(const(char)[] __param_0) pure @safeThe to template converts a value from one type to another.
The source type is deduced and the target type must be specified, for example the
expression to`!int(42.0)` converts the number 42 from
`double` to `int`. The conversion is "safe", i.e.,
it checks for overflow; to!int(4.2e10) would throw the
ConvOverflowException exception. Overflow checks are only
inserted when necessary, e.g., ``to!double(42) does not do
any checking because any int fits in a double.
Conversions from string to numeric types differ from the C equivalents
atoi() and atol() by checking for overflow and not allowing whitespace.
For conversion of strings to signed types, the grammar recognized is:
Integer:
Sign UnsignedInteger
UnsignedInteger
Sign:
+
-
For conversion to unsigned types, the grammar recognized is:
UnsignedInteger:
DecimalDigit
DecimalDigit UnsignedInteger
Examples
Converting a value to its own type (useful mostly for generic code)
simply returns its argument.
int a = 42;
int b = to!int(a);
double c = to!double(3.14); // c is double with value 3.14
Converting among numeric types is a safe way to cast them around.
Conversions from floating-point types to integral types allow loss of
precision (the fractional part of a floating-point number). The
conversion is truncating towards zero, the same way a cast would
truncate. (To round a floating point value when casting to an
integral, use roundTo.)
import std.exception : assertThrown;
int a = 420;
assert(to!long(a) == a);
assertThrown!ConvOverflowException(to!byte(a));
assert(to!int(4.2e6) == 4200000);
assertThrown!ConvOverflowException(to!uint(-3.14));
assert(to!uint(3.14) == 3);
assert(to!uint(3.99) == 3);
assert(to!int(-3.99) == -3);
When converting strings to numeric types, note that D hexadecimal and binary
literals are not handled. Neither the prefixes that indicate the base, nor the
horizontal bar used to separate groups of digits are recognized. This also
applies to the suffixes that indicate the type.
To work around this, you can specify a radix for conversions involving numbers.
auto str = to!string(42, 16);
assert(str == "2A");
auto i = to!int(str, 16);
assert(i == 42);
Conversions from integral types to floating-point types always
succeed, but might lose accuracy. The largest integers with a
predecessor representable in floating-point format are 2^24-1 for
float, 2^53-1 for double, and 2^64-1 for real (when
real is 80-bit, e.g. on Intel machines).
// 2^24 - 1, largest proper integer representable as float
int a = 16_777_215;
assert(to!int(to!float(a)) == a);
assert(to!int(to!float(-a)) == -a);
Conversion from string types to char types enforces the input
to consist of a single code point, and said code point must
fit in the target type. Otherwise, ConvException is thrown.
import std.exception : assertThrown;
assert(to!char("a") == 'a');
assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
assert(to!wchar("ñ") == 'ñ');
assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
assert(to!dchar("😃") == '😃');
// Using wstring or dstring as source type does not affect the result
assert(to!char("a"w) == 'a');
assert(to!char("a"d) == 'a');
// Two code points cannot be converted to a single one
assertThrown(to!char("ab"));
Converting an array to another array type works by converting each
element in turn. Associative arrays can be converted to associative
arrays as long as keys and values can in turn be converted.
import std.string : split;
int[] a = [1, 2, 3];
auto b = to!(float[])(a);
assert(b == [1.0f, 2, 3]);
string str = "1 2 3 4 5 6";
auto numbers = to!(double[])(split(str));
assert(numbers == [1.0, 2, 3, 4, 5, 6]);
int[string] c;
c["a"] = 1;
c["b"] = 2;
auto d = to!(double[wstring])(c);
assert(d["a"w] == 1 && d["b"w] == 2);
Conversions operate transitively, meaning that they work on arrays and
associative arrays of any complexity.
This conversion works because to`!short` applies to an `int`, to!wstring
applies to a string, to`!string` applies to a `double`, and
to!(double[]) applies to an int[]. The conversion might throw an
exception because ``to!short might fail the range check.
int[string][double[int[]]] a;
auto b = to!(short[wstring][string[double[]]])(a);
Object-to-object conversions by dynamic casting throw exception when
the source is non-null and the target is null.
import std.exception : assertThrown;
// Testing object conversions
class A {}
class B : A {}
class C : A {}
A a1 = new A, a2 = new B, a3 = new C;
assert(to!B(a2) is a2);
assert(to!C(a3) is a3);
assertThrown!ConvException(to!B(a3));
Stringize conversion from all types is supported.
String to string conversion works for any two string types having
(char, wchar, dchar) character widths and any
combination of qualifiers (mutable, const, or immutable).
Converts array (other than strings) to string.
Each element is converted by calling ``to!T.
Associative array to string conversion.
Each element is converted by calling ``to!T.
Object to string conversion calls toString against the object or
returns "null" if the object is null.
Struct to string conversion calls toString against the struct if
it is defined.
For structs that do not define toString, the conversion to string
produces the list of fields.
Enumerated types are converted to strings as their symbolic names.
Boolean values are converted to "true" or "false".
char, wchar, dchar to a string type.
Unsigned or signed integers to strings.
: Convert integral value to string in radix radix.
radix must be a value from 2 to 36.
value is treated as a signed value only if radix is 10.
The characters A through Z are used to represent values 10 through 36
and their case is determined by the letterCase parameter.
All floating point types to all string types.
Pointer to string conversions convert the pointer to a size_t value.
If pointer is char*, treat it as C-style strings.
In that case, this function is @system.
See formatValue on how toString should be defined.
// Conversion representing dynamic/static array with string
long[] a = [ 1, 3, 5 ];
assert(to!string(a) == "[1, 3, 5]");
// Conversion representing associative array with string
int[string] associativeArray = ["0":1, "1":2];
assert(to!string(associativeArray) == `["0":1, "1":2]` ||
to!string(associativeArray) == `["1":2, "0":1]`);
// char* to string conversion
assert(to!string(cast(char*) null) == "");
assert(to!string("foo\0".ptr) == "foo");
// Conversion reinterpreting void array to string
auto w = "abcx"w;
const(void)[] b = w;
assert(b.length == 8);
auto c = to!(wchar[])(b);
assert(c == "abcx");
Strings can be converted to enum types. The enum member with the same name as the
input string is returned. The comparison is case-sensitive.
A ConvException is thrown if the enum does not have the specified member.
import std.exception : assertThrown;
enum E { a, b, c }
assert(to!E("a") == E.a);
assert(to!E("b") == E.b);
assertThrown!ConvException(to!E("A"));
to!ulong((parameter) const(char)[] pathpath[(local variable) ulong ii + 2 .. (local variable) ulong jj]) };
(local variable) property_tree_path_addressing.Seg[] segssegs ~= (local variable) property_tree_path_addressing.Seg ss;
(local variable) ulong ii = (local variable) ulong jj + 1;
}
else if ((local variable) ulong ii + 1 < (parameter) const(char)[] pathpath.(field) ulong const(char)[].lengthlength && (parameter) const(char)[] pathpath[(local variable) ulong ii + 1] == '"') // ["name"] (C26)
{
(alias) object.size_t = ulongsize_t (local variable) ulong jj = (local variable) ulong ii + 2;
(alias) object.string = stringstring (local variable) string namename;
while ((local variable) ulong jj < (parameter) const(char)[] pathpath.(field) ulong const(char)[].lengthlength && (parameter) const(char)[] pathpath[(local variable) ulong jj] != '"')
{
if ((parameter) const(char)[] pathpath[(local variable) ulong jj] == '\\') (local variable) ulong jj++;
(local variable) string namename ~= (parameter) const(char)[] pathpath[(local variable) ulong jj];
(local variable) ulong jj++;
}
(local variable) property_tree_path_addressing.Seg[] segssegs ~= (struct) property_tree_path_addressing.SegSeg((local variable) string namename, 0, false);
(local variable) ulong ii = (local variable) ulong jj + 2; // past the closing `"` and `]`
}
else
{
(alias) object.size_t = ulongsize_t (local variable) ulong jj = ++(local variable) ulong ii;
while ((local variable) ulong jj < (parameter) const(char)[] pathpath.(field) ulong const(char)[].lengthlength && (parameter) const(char)[] pathpath[(local variable) ulong jj] != ']') (local variable) ulong jj++;
(local variable) property_tree_path_addressing.Seg[] segssegs ~= (struct) property_tree_path_addressing.SegSeg(null, ulong std.conv.to!ulong.to!(const(char)[])(const(char)[] __param_0) pure @safeThe to template converts a value from one type to another.
The source type is deduced and the target type must be specified, for example the
expression to`!int(42.0)` converts the number 42 from
`double` to `int`. The conversion is "safe", i.e.,
it checks for overflow; to!int(4.2e10) would throw the
ConvOverflowException exception. Overflow checks are only
inserted when necessary, e.g., ``to!double(42) does not do
any checking because any int fits in a double.
Conversions from string to numeric types differ from the C equivalents
atoi() and atol() by checking for overflow and not allowing whitespace.
For conversion of strings to signed types, the grammar recognized is:
Integer:
Sign UnsignedInteger
UnsignedInteger
Sign:
+
-
For conversion to unsigned types, the grammar recognized is:
UnsignedInteger:
DecimalDigit
DecimalDigit UnsignedInteger
Examples
Converting a value to its own type (useful mostly for generic code)
simply returns its argument.
int a = 42;
int b = to!int(a);
double c = to!double(3.14); // c is double with value 3.14
Converting among numeric types is a safe way to cast them around.
Conversions from floating-point types to integral types allow loss of
precision (the fractional part of a floating-point number). The
conversion is truncating towards zero, the same way a cast would
truncate. (To round a floating point value when casting to an
integral, use roundTo.)
import std.exception : assertThrown;
int a = 420;
assert(to!long(a) == a);
assertThrown!ConvOverflowException(to!byte(a));
assert(to!int(4.2e6) == 4200000);
assertThrown!ConvOverflowException(to!uint(-3.14));
assert(to!uint(3.14) == 3);
assert(to!uint(3.99) == 3);
assert(to!int(-3.99) == -3);
When converting strings to numeric types, note that D hexadecimal and binary
literals are not handled. Neither the prefixes that indicate the base, nor the
horizontal bar used to separate groups of digits are recognized. This also
applies to the suffixes that indicate the type.
To work around this, you can specify a radix for conversions involving numbers.
auto str = to!string(42, 16);
assert(str == "2A");
auto i = to!int(str, 16);
assert(i == 42);
Conversions from integral types to floating-point types always
succeed, but might lose accuracy. The largest integers with a
predecessor representable in floating-point format are 2^24-1 for
float, 2^53-1 for double, and 2^64-1 for real (when
real is 80-bit, e.g. on Intel machines).
// 2^24 - 1, largest proper integer representable as float
int a = 16_777_215;
assert(to!int(to!float(a)) == a);
assert(to!int(to!float(-a)) == -a);
Conversion from string types to char types enforces the input
to consist of a single code point, and said code point must
fit in the target type. Otherwise, ConvException is thrown.
import std.exception : assertThrown;
assert(to!char("a") == 'a');
assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
assert(to!wchar("ñ") == 'ñ');
assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
assert(to!dchar("😃") == '😃');
// Using wstring or dstring as source type does not affect the result
assert(to!char("a"w) == 'a');
assert(to!char("a"d) == 'a');
// Two code points cannot be converted to a single one
assertThrown(to!char("ab"));
Converting an array to another array type works by converting each
element in turn. Associative arrays can be converted to associative
arrays as long as keys and values can in turn be converted.
import std.string : split;
int[] a = [1, 2, 3];
auto b = to!(float[])(a);
assert(b == [1.0f, 2, 3]);
string str = "1 2 3 4 5 6";
auto numbers = to!(double[])(split(str));
assert(numbers == [1.0, 2, 3, 4, 5, 6]);
int[string] c;
c["a"] = 1;
c["b"] = 2;
auto d = to!(double[wstring])(c);
assert(d["a"w] == 1 && d["b"w] == 2);
Conversions operate transitively, meaning that they work on arrays and
associative arrays of any complexity.
This conversion works because to`!short` applies to an `int`, to!wstring
applies to a string, to`!string` applies to a `double`, and
to!(double[]) applies to an int[]. The conversion might throw an
exception because ``to!short might fail the range check.
int[string][double[int[]]] a;
auto b = to!(short[wstring][string[double[]]])(a);
Object-to-object conversions by dynamic casting throw exception when
the source is non-null and the target is null.
import std.exception : assertThrown;
// Testing object conversions
class A {}
class B : A {}
class C : A {}
A a1 = new A, a2 = new B, a3 = new C;
assert(to!B(a2) is a2);
assert(to!C(a3) is a3);
assertThrown!ConvException(to!B(a3));
Stringize conversion from all types is supported.
String to string conversion works for any two string types having
(char, wchar, dchar) character widths and any
combination of qualifiers (mutable, const, or immutable).
Converts array (other than strings) to string.
Each element is converted by calling ``to!T.
Associative array to string conversion.
Each element is converted by calling ``to!T.
Object to string conversion calls toString against the object or
returns "null" if the object is null.
Struct to string conversion calls toString against the struct if
it is defined.
For structs that do not define toString, the conversion to string
produces the list of fields.
Enumerated types are converted to strings as their symbolic names.
Boolean values are converted to "true" or "false".
char, wchar, dchar to a string type.
Unsigned or signed integers to strings.
: Convert integral value to string in radix radix.
radix must be a value from 2 to 36.
value is treated as a signed value only if radix is 10.
The characters A through Z are used to represent values 10 through 36
and their case is determined by the letterCase parameter.
All floating point types to all string types.
Pointer to string conversions convert the pointer to a size_t value.
If pointer is char*, treat it as C-style strings.
In that case, this function is @system.
See formatValue on how toString should be defined.
// Conversion representing dynamic/static array with string
long[] a = [ 1, 3, 5 ];
assert(to!string(a) == "[1, 3, 5]");
// Conversion representing associative array with string
int[string] associativeArray = ["0":1, "1":2];
assert(to!string(associativeArray) == `["0":1, "1":2]` ||
to!string(associativeArray) == `["1":2, "0":1]`);
// char* to string conversion
assert(to!string(cast(char*) null) == "");
assert(to!string("foo\0".ptr) == "foo");
// Conversion reinterpreting void array to string
auto w = "abcx"w;
const(void)[] b = w;
assert(b.length == 8);
auto c = to!(wchar[])(b);
assert(c == "abcx");
Strings can be converted to enum types. The enum member with the same name as the
input string is returned. The comparison is case-sensitive.
A ConvException is thrown if the enum does not have the specified member.
import std.exception : assertThrown;
enum E { a, b, c }
assert(to!E("a") == E.a);
assert(to!E("b") == E.b);
assertThrown!ConvException(to!E("A"));
to!(alias) object.size_t = ulongsize_t((parameter) const(char)[] pathpath[(local variable) ulong ii .. (local variable) ulong jj]), true);
(local variable) ulong ii = (local variable) ulong jj + 1;
}
}
else
{
(alias) object.size_t = ulongsize_t (local variable) ulong jj = (local variable) ulong ii;
while ((local variable) ulong jj < (parameter) const(char)[] pathpath.(field) ulong const(char)[].lengthlength && (parameter) const(char)[] pathpath[(local variable) ulong jj] != '.' && (parameter) const(char)[] pathpath[(local variable) ulong jj] != '[') (local variable) ulong jj++;
(local variable) property_tree_path_addressing.Seg[] segssegs ~= (struct) property_tree_path_addressing.SegSeg((parameter) const(char)[] pathpath[(local variable) ulong ii .. (local variable) ulong jj].string object.idup!(const(char))(const(char)[] a) pure nothrow @property @safeProvide the .idup array property, which creates an immutable duplicate.
idup, 0, false);
(local variable) ulong ii = (local variable) ulong jj;
}
}
return (local variable) property_tree_path_addressing.Seg[] segssegs;
}
/// The inverse: how the walk mints a child's path. A name outside the bare
/// identifier subset is emitted as a quoted segment, so every emitted path
/// re-parses to the same segments (C26).
(alias) object.string = stringstring string property_tree_path_addressing.childPath(string parent, string member) pure nothrow @safeThe inverse: how the walk mints a child's path. A name outside the bare
identifier subset is emitted as a quoted segment, so every emitted path
re-parses to the same segments (C26).
childPath((alias) object.string = stringstring (parameter) string parentparent, (alias) object.string = stringstring (parameter) string membermember) pure nothrow
{
bool (local variable) bool barebare = (parameter) string membermember.(field) ulong string.lengthlength > 0 && !((parameter) string membermember[0] >= '0' && (parameter) string membermember[0] <= '9');
foreach ((parameter) immutable(char) cc; (parameter) string membermember)
(local variable) bool barebare &= (local variable) immutable(char) cc == '_' || ((local variable) immutable(char) cc >= 'a' && (local variable) immutable(char) cc <= 'z') || ((local variable) immutable(char) cc >= 'A' && (local variable) immutable(char) cc <= 'Z')
|| ((local variable) immutable(char) cc >= '0' && (local variable) immutable(char) cc <= '9');
if ((local variable) bool barebare)
return (parameter) string parentparent.(field) ulong string.lengthlength ? (parameter) string parentparent ~ "." ~ (parameter) string membermember : (parameter) string membermember;
(alias) object.string = stringstring (local variable) string qq = `["`;
foreach ((parameter) immutable(char) cc; (parameter) string membermember)
{
if ((local variable) immutable(char) cc == '"' || (local variable) immutable(char) cc == '\\') (local variable) string qq ~= '\\';
(local variable) string qq ~= (local variable) immutable(char) cc;
}
return (parameter) string parentparent ~ (local variable) string qq ~ `"]`;
}
/// ditto
(alias) object.string = stringstring string property_tree_path_addressing.elementPath(string parent, ulong i) pure @safeThe inverse: how the walk mints a child's path. A name outside the bare
identifier subset is emitted as a quoted segment, so every emitted path
re-parses to the same segments (C26).
elementPath((alias) object.string = stringstring (parameter) string parentparent, (alias) object.size_t = ulongsize_t (parameter) ulong ii) pure
=> (parameter) string parentparent ~ "[" ~ (parameter) ulong ii.string std.conv.to!string.to!ulong(ulong __param_0) pure nothrow @safeThe to template converts a value from one type to another.
The source type is deduced and the target type must be specified, for example the
expression to`!int(42.0)` converts the number 42 from
`double` to `int`. The conversion is "safe", i.e.,
it checks for overflow; to!int(4.2e10) would throw the
ConvOverflowException exception. Overflow checks are only
inserted when necessary, e.g., ``to!double(42) does not do
any checking because any int fits in a double.
Conversions from string to numeric types differ from the C equivalents
atoi() and atol() by checking for overflow and not allowing whitespace.
For conversion of strings to signed types, the grammar recognized is:
Integer:
Sign UnsignedInteger
UnsignedInteger
Sign:
+
-
For conversion to unsigned types, the grammar recognized is:
UnsignedInteger:
DecimalDigit
DecimalDigit UnsignedInteger
Examples
Converting a value to its own type (useful mostly for generic code)
simply returns its argument.
int a = 42;
int b = to!int(a);
double c = to!double(3.14); // c is double with value 3.14
Converting among numeric types is a safe way to cast them around.
Conversions from floating-point types to integral types allow loss of
precision (the fractional part of a floating-point number). The
conversion is truncating towards zero, the same way a cast would
truncate. (To round a floating point value when casting to an
integral, use roundTo.)
import std.exception : assertThrown;
int a = 420;
assert(to!long(a) == a);
assertThrown!ConvOverflowException(to!byte(a));
assert(to!int(4.2e6) == 4200000);
assertThrown!ConvOverflowException(to!uint(-3.14));
assert(to!uint(3.14) == 3);
assert(to!uint(3.99) == 3);
assert(to!int(-3.99) == -3);
When converting strings to numeric types, note that D hexadecimal and binary
literals are not handled. Neither the prefixes that indicate the base, nor the
horizontal bar used to separate groups of digits are recognized. This also
applies to the suffixes that indicate the type.
To work around this, you can specify a radix for conversions involving numbers.
auto str = to!string(42, 16);
assert(str == "2A");
auto i = to!int(str, 16);
assert(i == 42);
Conversions from integral types to floating-point types always
succeed, but might lose accuracy. The largest integers with a
predecessor representable in floating-point format are 2^24-1 for
float, 2^53-1 for double, and 2^64-1 for real (when
real is 80-bit, e.g. on Intel machines).
// 2^24 - 1, largest proper integer representable as float
int a = 16_777_215;
assert(to!int(to!float(a)) == a);
assert(to!int(to!float(-a)) == -a);
Conversion from string types to char types enforces the input
to consist of a single code point, and said code point must
fit in the target type. Otherwise, ConvException is thrown.
import std.exception : assertThrown;
assert(to!char("a") == 'a');
assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
assert(to!wchar("ñ") == 'ñ');
assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
assert(to!dchar("😃") == '😃');
// Using wstring or dstring as source type does not affect the result
assert(to!char("a"w) == 'a');
assert(to!char("a"d) == 'a');
// Two code points cannot be converted to a single one
assertThrown(to!char("ab"));
Converting an array to another array type works by converting each
element in turn. Associative arrays can be converted to associative
arrays as long as keys and values can in turn be converted.
import std.string : split;
int[] a = [1, 2, 3];
auto b = to!(float[])(a);
assert(b == [1.0f, 2, 3]);
string str = "1 2 3 4 5 6";
auto numbers = to!(double[])(split(str));
assert(numbers == [1.0, 2, 3, 4, 5, 6]);
int[string] c;
c["a"] = 1;
c["b"] = 2;
auto d = to!(double[wstring])(c);
assert(d["a"w] == 1 && d["b"w] == 2);
Conversions operate transitively, meaning that they work on arrays and
associative arrays of any complexity.
This conversion works because to`!short` applies to an `int`, to!wstring
applies to a string, to`!string` applies to a `double`, and
to!(double[]) applies to an int[]. The conversion might throw an
exception because ``to!short might fail the range check.
int[string][double[int[]]] a;
auto b = to!(short[wstring][string[double[]]])(a);
Object-to-object conversions by dynamic casting throw exception when
the source is non-null and the target is null.
import std.exception : assertThrown;
// Testing object conversions
class A {}
class B : A {}
class C : A {}
A a1 = new A, a2 = new B, a3 = new C;
assert(to!B(a2) is a2);
assert(to!C(a3) is a3);
assertThrown!ConvException(to!B(a3));
Stringize conversion from all types is supported.
String to string conversion works for any two string types having
(char, wchar, dchar) character widths and any
combination of qualifiers (mutable, const, or immutable).
Converts array (other than strings) to string.
Each element is converted by calling ``to!T.
Associative array to string conversion.
Each element is converted by calling ``to!T.
Object to string conversion calls toString against the object or
returns "null" if the object is null.
Struct to string conversion calls toString against the struct if
it is defined.
For structs that do not define toString, the conversion to string
produces the list of fields.
Enumerated types are converted to strings as their symbolic names.
Boolean values are converted to "true" or "false".
char, wchar, dchar to a string type.
Unsigned or signed integers to strings.
: Convert integral value to string in radix radix.
radix must be a value from 2 to 36.
value is treated as a signed value only if radix is 10.
The characters A through Z are used to represent values 10 through 36
and their case is determined by the letterCase parameter.
All floating point types to all string types.
Pointer to string conversions convert the pointer to a size_t value.
If pointer is char*, treat it as C-style strings.
In that case, this function is @system.
See formatValue on how toString should be defined.
// Conversion representing dynamic/static array with string
long[] a = [ 1, 3, 5 ];
assert(to!string(a) == "[1, 3, 5]");
// Conversion representing associative array with string
int[string] associativeArray = ["0":1, "1":2];
assert(to!string(associativeArray) == `["0":1, "1":2]` ||
to!string(associativeArray) == `["1":2, "0":1]`);
// char* to string conversion
assert(to!string(cast(char*) null) == "");
assert(to!string("foo\0".ptr) == "foo");
// Conversion reinterpreting void array to string
auto w = "abcx"w;
const(void)[] b = w;
assert(b.length == 8);
auto c = to!(wchar[])(b);
assert(c == "abcx");
Strings can be converted to enum types. The enum member with the same name as the
input string is returned. The comparison is case-sensitive.
A ConvException is thrown if the enum does not have the specified member.
import std.exception : assertThrown;
enum E { a, b, c }
assert(to!E("a") == E.a);
assert(to!E("b") == E.b);
assertThrown!ConvException(to!E("A"));
to!(alias) object.string = stringstring ~ "]";
/// ditto — stable identity for an opted-in element (C25)
(alias) object.string = stringstring string property_tree_path_addressing.keyedPath(string parent, ulong key) pure @safeditto — stable identity for an opted-in element (C25)
keyedPath((alias) object.string = stringstring (parameter) string parentparent, ulong (parameter) ulong keykey) pure
=> (parameter) string parentparent ~ "[#" ~ (parameter) ulong keykey.string std.conv.to!string.to!ulong(ulong __param_0) pure nothrow @safeThe to template converts a value from one type to another.
The source type is deduced and the target type must be specified, for example the
expression to`!int(42.0)` converts the number 42 from
`double` to `int`. The conversion is "safe", i.e.,
it checks for overflow; to!int(4.2e10) would throw the
ConvOverflowException exception. Overflow checks are only
inserted when necessary, e.g., ``to!double(42) does not do
any checking because any int fits in a double.
Conversions from string to numeric types differ from the C equivalents
atoi() and atol() by checking for overflow and not allowing whitespace.
For conversion of strings to signed types, the grammar recognized is:
Integer:
Sign UnsignedInteger
UnsignedInteger
Sign:
+
-
For conversion to unsigned types, the grammar recognized is:
UnsignedInteger:
DecimalDigit
DecimalDigit UnsignedInteger
Examples
Converting a value to its own type (useful mostly for generic code)
simply returns its argument.
int a = 42;
int b = to!int(a);
double c = to!double(3.14); // c is double with value 3.14
Converting among numeric types is a safe way to cast them around.
Conversions from floating-point types to integral types allow loss of
precision (the fractional part of a floating-point number). The
conversion is truncating towards zero, the same way a cast would
truncate. (To round a floating point value when casting to an
integral, use roundTo.)
import std.exception : assertThrown;
int a = 420;
assert(to!long(a) == a);
assertThrown!ConvOverflowException(to!byte(a));
assert(to!int(4.2e6) == 4200000);
assertThrown!ConvOverflowException(to!uint(-3.14));
assert(to!uint(3.14) == 3);
assert(to!uint(3.99) == 3);
assert(to!int(-3.99) == -3);
When converting strings to numeric types, note that D hexadecimal and binary
literals are not handled. Neither the prefixes that indicate the base, nor the
horizontal bar used to separate groups of digits are recognized. This also
applies to the suffixes that indicate the type.
To work around this, you can specify a radix for conversions involving numbers.
auto str = to!string(42, 16);
assert(str == "2A");
auto i = to!int(str, 16);
assert(i == 42);
Conversions from integral types to floating-point types always
succeed, but might lose accuracy. The largest integers with a
predecessor representable in floating-point format are 2^24-1 for
float, 2^53-1 for double, and 2^64-1 for real (when
real is 80-bit, e.g. on Intel machines).
// 2^24 - 1, largest proper integer representable as float
int a = 16_777_215;
assert(to!int(to!float(a)) == a);
assert(to!int(to!float(-a)) == -a);
Conversion from string types to char types enforces the input
to consist of a single code point, and said code point must
fit in the target type. Otherwise, ConvException is thrown.
import std.exception : assertThrown;
assert(to!char("a") == 'a');
assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
assert(to!wchar("ñ") == 'ñ');
assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
assert(to!dchar("😃") == '😃');
// Using wstring or dstring as source type does not affect the result
assert(to!char("a"w) == 'a');
assert(to!char("a"d) == 'a');
// Two code points cannot be converted to a single one
assertThrown(to!char("ab"));
Converting an array to another array type works by converting each
element in turn. Associative arrays can be converted to associative
arrays as long as keys and values can in turn be converted.
import std.string : split;
int[] a = [1, 2, 3];
auto b = to!(float[])(a);
assert(b == [1.0f, 2, 3]);
string str = "1 2 3 4 5 6";
auto numbers = to!(double[])(split(str));
assert(numbers == [1.0, 2, 3, 4, 5, 6]);
int[string] c;
c["a"] = 1;
c["b"] = 2;
auto d = to!(double[wstring])(c);
assert(d["a"w] == 1 && d["b"w] == 2);
Conversions operate transitively, meaning that they work on arrays and
associative arrays of any complexity.
This conversion works because to`!short` applies to an `int`, to!wstring
applies to a string, to`!string` applies to a `double`, and
to!(double[]) applies to an int[]. The conversion might throw an
exception because ``to!short might fail the range check.
int[string][double[int[]]] a;
auto b = to!(short[wstring][string[double[]]])(a);
Object-to-object conversions by dynamic casting throw exception when
the source is non-null and the target is null.
import std.exception : assertThrown;
// Testing object conversions
class A {}
class B : A {}
class C : A {}
A a1 = new A, a2 = new B, a3 = new C;
assert(to!B(a2) is a2);
assert(to!C(a3) is a3);
assertThrown!ConvException(to!B(a3));
Stringize conversion from all types is supported.
String to string conversion works for any two string types having
(char, wchar, dchar) character widths and any
combination of qualifiers (mutable, const, or immutable).
Converts array (other than strings) to string.
Each element is converted by calling ``to!T.
Associative array to string conversion.
Each element is converted by calling ``to!T.
Object to string conversion calls toString against the object or
returns "null" if the object is null.
Struct to string conversion calls toString against the struct if
it is defined.
For structs that do not define toString, the conversion to string
produces the list of fields.
Enumerated types are converted to strings as their symbolic names.
Boolean values are converted to "true" or "false".
char, wchar, dchar to a string type.
Unsigned or signed integers to strings.
: Convert integral value to string in radix radix.
radix must be a value from 2 to 36.
value is treated as a signed value only if radix is 10.
The characters A through Z are used to represent values 10 through 36
and their case is determined by the letterCase parameter.
All floating point types to all string types.
Pointer to string conversions convert the pointer to a size_t value.
If pointer is char*, treat it as C-style strings.
In that case, this function is @system.
See formatValue on how toString should be defined.
// Conversion representing dynamic/static array with string
long[] a = [ 1, 3, 5 ];
assert(to!string(a) == "[1, 3, 5]");
// Conversion representing associative array with string
int[string] associativeArray = ["0":1, "1":2];
assert(to!string(associativeArray) == `["0":1, "1":2]` ||
to!string(associativeArray) == `["1":2, "0":1]`);
// char* to string conversion
assert(to!string(cast(char*) null) == "");
assert(to!string("foo\0".ptr) == "foo");
// Conversion reinterpreting void array to string
auto w = "abcx"w;
const(void)[] b = w;
assert(b.length == 8);
auto c = to!(wchar[])(b);
assert(c == "abcx");
Strings can be converted to enum types. The enum member with the same name as the
input string is returned. The comparison is case-sensitive.
A ConvException is thrown if the enum does not have the specified member.
import std.exception : assertThrown;
enum E { a, b, c }
assert(to!E("a") == E.a);
assert(to!E("b") == E.b);
assertThrown!ConvException(to!E("A"));
to!(alias) object.string = stringstring ~ "]";
// ── C8: compile-time resolution ──────────────────────────────────────────────
/// `at!"a.b[2].c"(subject)` — a direct, `ref`-returning field access. The
/// mixin is the path text with `[i]` left as-is, so D's own indexing applies;
/// a typo is a compile error at the USE site.
ref auto uint property_tree_path_addressing.at!("fill.tint", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at(string P, T)(return ref (alias) T = property_tree_path_addressing.LayerT (parameter) property_tree_path_addressing.Layer subjectsubject)
=> mixin("subject." ~ P);
// ── C9: runtime resolution, generated from the type ──────────────────────────
/// Resolves `path` against `subject` and calls `sink(leafRef, typeName)`.
/// `sink` is an alias, so it is instantiated per leaf type — no `void*`, no
/// registry, no virtual call.
bool bool property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string).resolve!(__lambda_L225_C14, property_tree_path_addressing.Fill)(ref property_tree_path_addressing.Fill subject, in property_tree_path_addressing.Seg[] segs, ulong at_ = 0LU) pure nothrow @nogc @safeResolves path against subject and calls sink(leafRef, typeName).
sink is an alias, so it is instantiated per leaf type — no void*, no
registry, no virtual call.
resolve(alias sink, T)(ref (alias) T = property_tree_path_addressing.FillT (parameter) property_tree_path_addressing.Fill subjectsubject, in (struct) property_tree_path_addressing.SegSeg[] (parameter) const(property_tree_path_addressing.Seg[]) segssegs, (alias) object.size_t = ulongsize_t (parameter) ulong at_at_ = 0)
{
static if ((template instance) std.traits.isAggregateType!(property_tree_path_addressing.Fill)isAggregateType!(alias) T = property_tree_path_addressing.FillT && !(template instance) std.traits.isSomeString!(property_tree_path_addressing.Fill)isSomeString!(alias) T = property_tree_path_addressing.FillT)
{
if ((parameter) ulong at_at_ == (parameter) const(property_tree_path_addressing.Seg[]) segssegs.(field) ulong const(property_tree_path_addressing.Seg[]).lengthlength) { sink((parameter) property_tree_path_addressing.Fill subjectsubject, (struct) property_tree_path_addressing.FillT.(constant) string property_tree_path_addressing.Fill.stringof = "Fill"stringof); return true; }
if ((parameter) const(property_tree_path_addressing.Seg[]) segssegs[(parameter) ulong at_at_].(field) bool property_tree_path_addressing.Seg.isIndexisIndex) return false;
switch ((parameter) const(property_tree_path_addressing.Seg[]) segssegs[(parameter) ulong at_at_].(field) string property_tree_path_addressing.Seg.namename)
{
static foreach (name; __traits(allMembers, T))
{{
static if (__traits(compiles, typeof(__traits(getMember, T, name)))
&& !is(typeof(__traits(getMember, T, name)) == function))
{
case (constant) string property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string).resolve!(__lambda_L225_C14, property_tree_path_addressing.Fill).name = "stops"name:
return bool property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string).resolve!(__lambda_L225_C14, property_tree_path_addressing.Stop[])(ref property_tree_path_addressing.Stop[] subject, in property_tree_path_addressing.Seg[] segs, ulong at_ = 0LU) pure nothrow @nogc @safeResolves path against subject and calls sink(leafRef, typeName).
sink is an alias, so it is instantiated per leaf type — no void*, no
registry, no virtual call.
resolve!sink(__traits(getMember, subject, name),
(parameter) const(property_tree_path_addressing.Seg[]) segssegs, (parameter) ulong at_at_ + 1);
}
}}
default: return false;
}
}
else static if (is((alias) T = uintT == U*, U))
{
// C10a: the ASYMMETRY. `subject.parent.fill` compiles as an implicit
// dereference, so the compile-time form crosses a pointer silently and
// faults on null. The runtime walk must decide explicitly — here: a
// null pointer is "no such path", never a fault.
if ((parameter) ulong at_at_ == (parameter) const(property_tree_path_addressing.Seg[]) segssegs.(field) ulong const(property_tree_path_addressing.Seg[]).lengthlength) { sink((parameter) property_tree_path_addressing.Layer* subjectsubject, void property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string).__lambda_L225_C14(__T2)(ref v, string tn)(ref property_tree_path_addressing.Layer* v, string tn) pure nothrow @nogc @safeT.(constant) string property_tree_path_addressing.Layer*.stringof = "Layer*"stringof); return true; }
if ((parameter) property_tree_path_addressing.Layer* subjectsubject is null) return false;
return bool property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string).resolve!((ref v, string tn)
{
static if (__traits(compiles, v = value))
{
v = value;
ok = true;
}
}
, property_tree_path_addressing.Layer)(ref property_tree_path_addressing.Layer subject, in property_tree_path_addressing.Seg[] segs, ulong at_ = 0LU) @safeResolves path against subject and calls sink(leafRef, typeName).
sink is an alias, so it is instantiated per leaf type — no void*, no
registry, no virtual call.
resolve!sink(*(parameter) property_tree_path_addressing.Layer* subjectsubject, (parameter) const(property_tree_path_addressing.Seg[]) segssegs, (parameter) ulong at_at_);
}
else static if ((template instance) std.traits.isArray!uintisArray!(alias) T = uintT && !(template instance) isSomeString!TisSomeString!(alias) T = property_tree_path_addressing.Stop[]T)
{
if ((parameter) ulong at_at_ == (parameter) const(property_tree_path_addressing.Seg[]) segssegs.(field) ulong const(property_tree_path_addressing.Seg[]).lengthlength) { sink((parameter) property_tree_path_addressing.Stop[] subjectsubject, void property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string).__lambda_L225_C14(__T2)(ref v, string tn)(ref property_tree_path_addressing.Stop[] v, string tn) pure nothrow @nogc @safeT.(constant) string property_tree_path_addressing.Stop[].stringof = "Stop[]"stringof); return true; }
static if (__traits(hasMember, typeof(subject[0]), "propElementKey"))
{
// C25: keyed identity — `[#k]` resolves by the element's OWN key.
// A duplicate or absent key is refused, never resolved positionally.
if ((parameter) const(property_tree_path_addressing.Seg[]) segssegs[(parameter) ulong at_at_].(field) bool property_tree_path_addressing.Seg.isKeyisKey)
{
(alias) object.size_t = ulongsize_t (local variable) ulong foundfound, (local variable) ulong hitshits;
foreach ((local variable) ulong idxidx; 0 .. (parameter) property_tree_path_addressing.KStop[] subjectsubject.(field) ulong property_tree_path_addressing.KStop[].lengthlength)
if ((parameter) property_tree_path_addressing.KStop[] subjectsubject[(local variable) ulong idxidx].ulong property_tree_path_addressing.KStop.propElementKey() const pure nothrow @nogc @safepropElementKey == (parameter) const(property_tree_path_addressing.Seg[]) segssegs[(parameter) ulong at_at_].(field) ulong property_tree_path_addressing.Seg.keykey)
{ (local variable) ulong foundfound = (local variable) ulong idxidx; (local variable) ulong hitshits++; }
if ((local variable) ulong hitshits != 1) return false;
return bool property_tree_path_addressing.readPath!(property_tree_path_addressing.KRoot).resolve!(__lambda_L211_C14, property_tree_path_addressing.KStop)(ref property_tree_path_addressing.KStop subject, in property_tree_path_addressing.Seg[] segs, ulong at_ = 0LU) pure @safeResolves path against subject and calls sink(leafRef, typeName).
sink is an alias, so it is instantiated per leaf type — no void*, no
registry, no virtual call.
resolve!sink((parameter) property_tree_path_addressing.KStop[] subjectsubject[(local variable) ulong foundfound], (parameter) const(property_tree_path_addressing.Seg[]) segssegs, (parameter) ulong at_at_ + 1);
}
}
if (!(parameter) const(property_tree_path_addressing.Seg[]) segssegs[(parameter) ulong at_at_].(field) bool property_tree_path_addressing.Seg.isIndexisIndex || (parameter) const(property_tree_path_addressing.Seg[]) segssegs[(parameter) ulong at_at_].(field) ulong property_tree_path_addressing.Seg.indexindex >= (parameter) property_tree_path_addressing.Stop[] subjectsubject.(field) ulong property_tree_path_addressing.Stop[].lengthlength) return false;
return bool property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string).resolve!(__lambda_L225_C14, property_tree_path_addressing.Stop)(ref property_tree_path_addressing.Stop subject, in property_tree_path_addressing.Seg[] segs, ulong at_ = 0LU) pure nothrow @nogc @safeResolves path against subject and calls sink(leafRef, typeName).
sink is an alias, so it is instantiated per leaf type — no void*, no
registry, no virtual call.
resolve!sink((parameter) property_tree_path_addressing.Stop[] subjectsubject[(parameter) const(property_tree_path_addressing.Seg[]) segssegs[(parameter) ulong at_at_].(field) ulong property_tree_path_addressing.Seg.indexindex], (parameter) const(property_tree_path_addressing.Seg[]) segssegs, (parameter) ulong at_at_ + 1);
}
else
{
if ((parameter) ulong at_at_ != (parameter) const(property_tree_path_addressing.Seg[]) segssegs.(field) ulong const(property_tree_path_addressing.Seg[]).lengthlength) return false;
sink((parameter) uint subjectsubject, void property_tree_path_addressing.readPath!(property_tree_path_addressing.Layer).__lambda_L211_C14(__T1)(ref v, string tn)(ref uint v, string tn) pure nothrow @safeT.(constant) string uint.stringof = "uint"stringof);
return true;
}
}
/// Read a leaf as text through the runtime path.
(alias) object.string = stringstring string property_tree_path_addressing.readPath!(property_tree_path_addressing.KRoot)(ref property_tree_path_addressing.KRoot subject, string path) pure @safeRead a leaf as text through the runtime path.
readPath(T)(ref (alias) T = property_tree_path_addressing.KRootT (parameter) property_tree_path_addressing.KRoot subjectsubject, (alias) object.string = stringstring (parameter) string pathpath)
{
(alias) object.string = stringstring (local variable) string outpoutp = "<no such path>";
static void void property_tree_path_addressing.readPath!(property_tree_path_addressing.KRoot).nothing() pure nothrow @nogc @safenothing() {}
bool property_tree_path_addressing.readPath!(property_tree_path_addressing.KRoot).resolve!((ref v, string tn)
{
static if (__traits(compiles, text(v)))
{
outp = text(v);
}
else
{
outp = "<" ~ tn ~ ">";
}
}
, property_tree_path_addressing.KRoot)(ref property_tree_path_addressing.KRoot subject, in property_tree_path_addressing.Seg[] segs, ulong at_ = 0LU) pure @safeResolves path against subject and calls sink(leafRef, typeName).
sink is an alias, so it is instantiated per leaf type — no void*, no
registry, no virtual call.
resolve!((ref v, string tn) {
static if (__traits(compiles, text(v)))
outp = text(v);
else
outp = "<" ~ tn ~ ">";
})((parameter) property_tree_path_addressing.KRoot subjectsubject, property_tree_path_addressing.Seg[] property_tree_path_addressing.segments(const(char)[] path) pure @safeParses at CTFE and at run time — the same function.
segments((parameter) string pathpath));
return (local variable) string outpoutp;
}
/// Write a leaf through the runtime path; fails (returns false) when the
/// value's type is not assignable to the addressed field.
bool bool property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string)(ref property_tree_path_addressing.Layer subject, string path, string value) @safeWrite a leaf through the runtime path; fails (returns false) when the
value's type is not assignable to the addressed field.
writePath(T, V)(ref (alias) T = property_tree_path_addressing.LayerT (parameter) property_tree_path_addressing.Layer subjectsubject, (alias) object.string = stringstring (parameter) string pathpath, (alias) V = stringV (parameter) string valuevalue)
{
bool (local variable) bool okok;
bool property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string).resolve!((ref v, string tn)
{
static if (__traits(compiles, v = value))
{
v = value;
ok = true;
}
}
, property_tree_path_addressing.Layer)(ref property_tree_path_addressing.Layer subject, in property_tree_path_addressing.Seg[] segs, ulong at_ = 0LU) @safeResolves path against subject and calls sink(leafRef, typeName).
sink is an alias, so it is instantiated per leaf type — no void*, no
registry, no virtual call.
resolve!((ref v, string tn) {
static if (__traits(compiles, v = value))
{
v = value;
ok = true;
}
})((parameter) property_tree_path_addressing.Layer subjectsubject, property_tree_path_addressing.Seg[] property_tree_path_addressing.segments(const(char)[] path) pure @safeParses at CTFE and at run time — the same function.
segments((parameter) string pathpath));
return (local variable) bool okok;
}
// ── the subject ──────────────────────────────────────────────────────────────
struct (struct) property_tree_path_addressing.StopStop { (alias) object.string = stringstring (field) string property_tree_path_addressing.Stop.namename; double (field) double property_tree_path_addressing.Stop.weightweight = 0; }
struct (struct) property_tree_path_addressing.FillFill { (struct) property_tree_path_addressing.StopStop[] (field) property_tree_path_addressing.Stop[] property_tree_path_addressing.Fill.stopsstops; uint (field) uint property_tree_path_addressing.Fill.tinttint; }
struct (struct) property_tree_path_addressing.LayerLayer
{
(alias) object.string = stringstring (field) string property_tree_path_addressing.Layer.namename;
(struct) property_tree_path_addressing.FillFill (field) property_tree_path_addressing.Fill property_tree_path_addressing.Layer.fillfill;
(struct) property_tree_path_addressing.LayerLayer* (field) property_tree_path_addressing.Layer* property_tree_path_addressing.Layer.parentparent; // recursive: proves C9's termination
}
/// C25's subject: an element that OWNS its identity by opting in.
struct (struct) property_tree_path_addressing.KStopC25's subject: an element that OWNS its identity by opting in.
KStop
{
ulong (field) ulong property_tree_path_addressing.KStop.idid;
(alias) object.string = stringstring (field) string property_tree_path_addressing.KStop.namename;
double (field) double property_tree_path_addressing.KStop.weightweight = 0;
ulong ulong property_tree_path_addressing.KStop.propElementKey() const pure nothrow @nogc @safepropElementKey() const pure nothrow @nogc => (field) ulong property_tree_path_addressing.KStop.idid;
}
struct (struct) property_tree_path_addressing.KRootKRoot { (struct) property_tree_path_addressing.KStopC25's subject: an element that OWNS its identity by opting in.
KStop[] (field) property_tree_path_addressing.KStop[] property_tree_path_addressing.KRoot.stopsstops; }
// ── driving ──────────────────────────────────────────────────────────────────
void void D main() @safemain()
{
import (package) stdstd.(module) std.stdioCategory Symbols File handles _popen File isFileHandle openNetwork stderr stdin stdout Reading chunks lines readf readfln readln Writing toFile write writef writefln writeln Misc KeepTerminator LockType StdioException
Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio
is publically imported when importing std.stdio.
There are three layers of I/O:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to $(D writef(fmt, args, '\n')).
writefln, (alias template) writeln = std.stdio.writeln(T...)(T args)Equivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Params:
args = the items to write to stdout
Throws:
In case of an I/O error, throws an $(LREF StdioException).
Example:
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
---
writeln;
(struct) property_tree_path_addressing.LayerLayer (local variable) property_tree_path_addressing.Layer ll = (struct) property_tree_path_addressing.LayerLayer("root");
(local variable) property_tree_path_addressing.Layer ll.(field) property_tree_path_addressing.Fill property_tree_path_addressing.Layer.fillfill.(field) property_tree_path_addressing.Stop[] property_tree_path_addressing.Fill.stopsstops = [(struct) property_tree_path_addressing.StopStop("a", 0.0), (struct) property_tree_path_addressing.StopStop("b", 0.5), (struct) property_tree_path_addressing.StopStop("c", 1.0)];
(local variable) property_tree_path_addressing.Layer ll.(field) property_tree_path_addressing.Fill property_tree_path_addressing.Layer.fillfill.(field) uint property_tree_path_addressing.Fill.tinttint = 0x336699;
() @trusted { (local variable) property_tree_path_addressing.Layer ll.(field) property_tree_path_addressing.Layer* property_tree_path_addressing.Layer.parentparent = &(local variable) property_tree_path_addressing.Layer ll; }();
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("C8 — compile-time resolution: a direct, ref-returning access");
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" at!\"fill.stops[1].name\" = %s", string property_tree_path_addressing.at!("fill.stops[1].name", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at!"fill.stops[1].name"((local variable) property_tree_path_addressing.Layer ll));
double property_tree_path_addressing.at!("fill.stops[1].weight", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at!"fill.stops[1].weight"((local variable) property_tree_path_addressing.Layer ll) = 0.75; // ← an ordinary assignment
void std.stdio.writefln!(char, double)(in char[] fmt, double __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" after write, weight = %s", double property_tree_path_addressing.at!("fill.stops[1].weight", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at!`fill.stops[1].weight`((local variable) property_tree_path_addressing.Layer ll));
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" a typo is a BUILD error: %s",
__traits(compiles, (template function) property_tree_path_addressing.at(string P, T)(return ref T subject)at!"fill.stpos[1].name"((local variable) property_tree_path_addressing.Layer ll)) ? "no" : "yes");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\nC9/C10 — the same paths at run time, differential against C8");
struct (struct) property_tree_path_addressing.main.CaseCase { (alias) object.string = stringstring (field) string property_tree_path_addressing.main.Case.pathpath; (alias) object.string = stringstring (field) string property_tree_path_addressing.main.Case.ctct; }
const (local variable) const(property_tree_path_addressing.main.Case[]) casescases = [
(struct) property_tree_path_addressing.main.CaseCase("name", string property_tree_path_addressing.at!("name", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at!"name"((local variable) property_tree_path_addressing.Layer ll)),
(struct) property_tree_path_addressing.main.CaseCase("fill.tint", string std.conv.text!uint(uint __param_0) pure nothrow @safeConvenience functions for converting one or more arguments
of any type into text (the three character widths).
text(uint property_tree_path_addressing.at!("fill.tint", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at!"fill.tint"((local variable) property_tree_path_addressing.Layer ll))),
(struct) property_tree_path_addressing.main.CaseCase("fill.stops[0].name", string property_tree_path_addressing.at!("fill.stops[0].name", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at!"fill.stops[0].name"((local variable) property_tree_path_addressing.Layer ll)),
(struct) property_tree_path_addressing.main.CaseCase("fill.stops[1].weight", string std.conv.text!double(double __param_0) pure @safeConvenience functions for converting one or more arguments
of any type into text (the three character widths).
text(double property_tree_path_addressing.at!("fill.stops[1].weight", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at!"fill.stops[1].weight"((local variable) property_tree_path_addressing.Layer ll))),
(struct) property_tree_path_addressing.main.CaseCase("fill.stops[2].name", string property_tree_path_addressing.at!("fill.stops[2].name", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at!"fill.stops[2].name"((local variable) property_tree_path_addressing.Layer ll)),
(struct) property_tree_path_addressing.main.CaseCase("parent.fill.stops[2].weight", string std.conv.text!double(double __param_0) pure @safeConvenience functions for converting one or more arguments
of any type into text (the three character widths).
text(double property_tree_path_addressing.at!("parent.fill.stops[2].weight", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at!"parent.fill.stops[2].weight"((local variable) property_tree_path_addressing.Layer ll))),
];
(alias) object.size_t = ulongsize_t (local variable) ulong agreedagreed;
foreach ((parameter) const(property_tree_path_addressing.main.Case) cc; (local variable) const(property_tree_path_addressing.main.Case[]) casescases)
{
const (local variable) const(string) rtrt = string property_tree_path_addressing.readPath!(property_tree_path_addressing.Layer)(ref property_tree_path_addressing.Layer subject, string path) @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.Layer ll, (local variable) const(property_tree_path_addressing.main.Case) cc.(field) string property_tree_path_addressing.main.Case.pathpath);
const (local variable) const(bool) samesame = (local variable) const(string) rtrt == (local variable) const(property_tree_path_addressing.main.Case) cc.(field) string property_tree_path_addressing.main.Case.ctct;
(local variable) ulong agreedagreed += (local variable) const(bool) samesame;
void std.stdio.writefln!(char, string, string, string, string)(in char[] fmt, string __param_1, string __param_2, string __param_3, string __param_4) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %-30s ct=%-8s rt=%-8s %s", (local variable) const(property_tree_path_addressing.main.Case) cc.(field) string property_tree_path_addressing.main.Case.pathpath, (local variable) const(property_tree_path_addressing.main.Case) cc.(field) string property_tree_path_addressing.main.Case.ctct, (local variable) const(string) rtrt,
(local variable) const(bool) samesame ? "✓" : "✗ DISAGREE");
}
void std.stdio.writefln!(char, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %s/%s agree", (local variable) ulong agreedagreed, (local variable) const(property_tree_path_addressing.main.Case[]) casescases.(field) ulong const(property_tree_path_addressing.main.Case[]).lengthlength);
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" (the pointer hop needed an explicit branch: `a.b` where `a` is a");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" pointer is an IMPLICIT deref at compile time — it faults on null,");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" while the runtime walk answers <no such path>.)");
{
(struct) property_tree_path_addressing.LayerLayer (local variable) property_tree_path_addressing.Layer orphanorphan = (struct) property_tree_path_addressing.LayerLayer("orphan"); // parent is null
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" null pointer, runtime: parent.name → %s",
string property_tree_path_addressing.readPath!(property_tree_path_addressing.Layer)(ref property_tree_path_addressing.Layer subject, string path) @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.Layer orphanorphan, "parent.name"));
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" null pointer, compile-time: at!\"parent.name\"(orphan) would FAULT");
}
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\n bad paths are refused, not crashed:");
foreach ((parameter) string badbad; ["fill.nope", "fill.stops[9].name", "fill[0]", "name.x"])
void std.stdio.writefln!(char, string, string)(in char[] fmt, string __param_1, string __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %-22s → %s", (local variable) string badbad, string property_tree_path_addressing.readPath!(property_tree_path_addressing.Layer)(ref property_tree_path_addressing.Layer subject, string path) @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.Layer ll, (local variable) string badbad));
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\n writes through the runtime path:");
void std.stdio.writefln!(char, bool)(in char[] fmt, bool __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" writePath(\"fill.stops[0].name\", \"zero\") = %s",
bool property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string)(ref property_tree_path_addressing.Layer subject, string path, string value) @safeWrite a leaf through the runtime path; fails (returns false) when the
value's type is not assignable to the addressed field.
writePath((local variable) property_tree_path_addressing.Layer ll, "fill.stops[0].name", "zero"));
void std.stdio.writefln!(char, bool)(in char[] fmt, bool __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" writePath(\"fill.tint\", \"not a uint\") = %s",
bool property_tree_path_addressing.writePath!(property_tree_path_addressing.Layer, string)(ref property_tree_path_addressing.Layer subject, string path, string value) @safeWrite a leaf through the runtime path; fails (returns false) when the
value's type is not assignable to the addressed field.
writePath((local variable) property_tree_path_addressing.Layer ll, "fill.tint", "not a uint"));
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" fill.stops[0].name is now %s", string property_tree_path_addressing.at!("fill.stops[0].name", property_tree_path_addressing.Layer)(return ref property_tree_path_addressing.Layer subject) pure nothrow @nogc ref @safe``at!"a.b[2].c"(subject) — a direct, ref-returning field access. The
mixin is the path text with [i] left as-is, so D's own indexing applies;
a typo is a compile error at the USE site.
at!"fill.stops[0].name"((local variable) property_tree_path_addressing.Layer ll));
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\nC11 — index paths are positional. Reader opens fill.stops[1]:");
(alias) object.string = stringstring (local variable) string openedopened = "fill.stops[1]";
void std.stdio.writefln!(char, string, string)(in char[] fmt, string __param_1, string __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" opened %s → element %s", (local variable) string openedopened, string property_tree_path_addressing.readPath!(property_tree_path_addressing.Layer)(ref property_tree_path_addressing.Layer subject, string path) @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.Layer ll, (local variable) string openedopened ~ ".name"));
(local variable) property_tree_path_addressing.Layer ll.(field) property_tree_path_addressing.Fill property_tree_path_addressing.Layer.fillfill.(field) property_tree_path_addressing.Stop[] property_tree_path_addressing.Fill.stopsstops = (local variable) property_tree_path_addressing.Layer ll.(field) property_tree_path_addressing.Fill property_tree_path_addressing.Layer.fillfill.(field) property_tree_path_addressing.Stop[] property_tree_path_addressing.Fill.stopsstops[1 .. $]; // element 0 removed elsewhere
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" after removing element 0, the SAME key now points at %s",
string property_tree_path_addressing.readPath!(property_tree_path_addressing.Layer)(ref property_tree_path_addressing.Layer subject, string path) @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.Layer ll, (local variable) string openedopened ~ ".name"));
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" → expansion, selection and in-progress edits silently move.");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\nC25 — the fix is element-provided identity: `[#key]` resolves");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" by `propElementKey`, through the same generated walk:");
(struct) property_tree_path_addressing.KRootKRoot (local variable) property_tree_path_addressing.KRoot kk;
(local variable) property_tree_path_addressing.KRoot kk.(field) property_tree_path_addressing.KStop[] property_tree_path_addressing.KRoot.stopsstops = [(struct) property_tree_path_addressing.KStopC25's subject: an element that OWNS its identity by opting in.
KStop(7, "b", 0.5), (struct) property_tree_path_addressing.KStopC25's subject: an element that OWNS its identity by opting in.
KStop(9, "c", 1.0)];
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" stops[#9].name → %s", string property_tree_path_addressing.readPath!(property_tree_path_addressing.KRoot)(ref property_tree_path_addressing.KRoot subject, string path) pure @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.KRoot kk, "stops[#9].name"));
(local variable) property_tree_path_addressing.KRoot kk.(field) property_tree_path_addressing.KStop[] property_tree_path_addressing.KRoot.stopsstops = (local variable) property_tree_path_addressing.KRoot kk.(field) property_tree_path_addressing.KStop[] property_tree_path_addressing.KRoot.stopsstops[1 .. $]; // element 0 removed elsewhere
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" after removing [#7] → %s (address unmoved)",
string property_tree_path_addressing.readPath!(property_tree_path_addressing.KRoot)(ref property_tree_path_addressing.KRoot subject, string path) pure @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.KRoot kk, "stops[#9].name"));
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" absent key stops[#3] → %s", string property_tree_path_addressing.readPath!(property_tree_path_addressing.KRoot)(ref property_tree_path_addressing.KRoot subject, string path) pure @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.KRoot kk, "stops[#3].name"));
(local variable) property_tree_path_addressing.KRoot kk.(field) property_tree_path_addressing.KStop[] property_tree_path_addressing.KRoot.stopsstops = [(struct) property_tree_path_addressing.KStopC25's subject: an element that OWNS its identity by opting in.
KStop(7, "x"), (struct) property_tree_path_addressing.KStopC25's subject: an element that OWNS its identity by opting in.
KStop(7, "y")]; // a duplicate key
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" duplicate key stops[#7] → %s (refused, never positional)",
string property_tree_path_addressing.readPath!(property_tree_path_addressing.KRoot)(ref property_tree_path_addressing.KRoot subject, string path) pure @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.KRoot kk, "stops[#7].name"));
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" unkeyed fill.stops[#7] → %s (identity is opt-in)",
string property_tree_path_addressing.readPath!(property_tree_path_addressing.Layer)(ref property_tree_path_addressing.Layer subject, string path) @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.Layer ll, "fill.stops[#7].name"));
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" the emitter mints it: keyedPath(\"stops\", 9) = %s",
string property_tree_path_addressing.keyedPath(string parent, ulong key) pure @safeditto — stable identity for an opted-in element (C25)
keyedPath("stops", 9));
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\nC26 — quoted segments carry names the bare grammar cannot:");
void std.stdio.writefln!(char, string, string)(in char[] fmt, string __param_1, string __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" [\"fill\"].tint ≡ fill.tint → %s ≡ %s",
string property_tree_path_addressing.readPath!(property_tree_path_addressing.Layer)(ref property_tree_path_addressing.Layer subject, string path) @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.Layer ll, `["fill"].tint`), string property_tree_path_addressing.readPath!(property_tree_path_addressing.Layer)(ref property_tree_path_addressing.Layer subject, string path) @safeRead a leaf as text through the runtime path.
readPath((local variable) property_tree_path_addressing.Layer ll, "fill.tint"));
const (local variable) const(string) weirdweird = `a.b [x] "q"`;
const (local variable) const(string) mintedminted = string property_tree_path_addressing.childPath(string parent, string member) pure nothrow @safeThe inverse: how the walk mints a child's path. A name outside the bare
identifier subset is emitted as a quoted segment, so every emitted path
re-parses to the same segments (C26).
childPath("", (local variable) const(string) weirdweird);
const (local variable) const(property_tree_path_addressing.Seg[]) backback = property_tree_path_addressing.Seg[] property_tree_path_addressing.segments(const(char)[] path) pure @safeParses at CTFE and at run time — the same function.
segments((local variable) const(string) mintedminted);
void std.stdio.writefln!(char, const(string)[], string)(in char[] fmt, const(string)[] __param_1, string __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" childPath of %(%s%) mints %s", [(local variable) const(string) weirdweird], (local variable) const(string) mintedminted);
void std.stdio.writefln!(char, bool)(in char[] fmt, bool __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" …which re-parses to one name segment (round-trip: %s)",
(local variable) const(property_tree_path_addressing.Seg[]) backback.(field) ulong const(property_tree_path_addressing.Seg[]).lengthlength == 1 && (local variable) const(property_tree_path_addressing.Seg[]) backback[0].(field) string property_tree_path_addressing.Seg.namename == (local variable) const(string) weirdweird);
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln(" identifier-shaped names stay bare: childPath(\"fill\", \"tint\") = %s",
string property_tree_path_addressing.childPath(string parent, string member) pure nothrow @safeThe inverse: how the walk mints a child's path. A name outside the bare
identifier subset is emitted as a quoted segment, so every emitted path
re-parses to the same segments (C26).
childPath("fill", "tint"));
}