Scope and ownership
LinkLiterals write values directly in source. This chapter covers their spellings, type families, escapes, interpolation, and template tags.
Edition: Topaz 5.21 language mode. Source authority: SELF compiler revision af88afbc1d99a8c0220b5984f9d74f895f8156f2. Each executable example records its independent expectation and verification result.
Lexical structure owns identifiers, token separation, comments, and source positions. Expressions and operators owns unary signs and grouping. Types owns general compatibility and inference. Concurrency owns the behavior of concurrent and its timeout. Library entries own conversion functions and operations on template values.
Integers and floats
LinkDecimalDigit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;BinaryDigit = "0" | "1" ;HexDigit = DecimalDigit | "a" | "b" | "c" | "d" | "e" | "f" ;DecimalDigits = DecimalDigit ("_"? DecimalDigit)* ;BinaryDigits = BinaryDigit ("_"? BinaryDigit)* ;HexDigits = HexDigit ("_"? HexDigit)* ;IntegerLiteral = DecimalDigits| "0x" HexDigits| "0b" BinaryDigits ;FloatLiteral = DecimalDigits "." DecimalDigits ;
This manually transcribed excerpt describes numeric tokens in language mode 5.21. Adjacent elements belong to the same token. It omits operator grammar and contextual type rules.
Source evidence
- validDigitRun, integerRadix, validIntegerLiteral, validDecimalLiteral at
af88afbc1d99· 14-46 - scanNumber at
af88afbc1d99· 467-514 - validateNumericLiteral at
af88afbc1d99· 322-330
Integer spellings
An integer literal uses one of these radix forms.
| Radix | Prefix | Digits |
|---|---|---|
| 10 | none | 0123456789 |
| 16 | 0x | 0123456789abcdef |
| 2 | 0b | 01 |
Radix prefixes and hexadecimal letters are lowercase. An uppercase spelling produces TPZ2001 during parsing.
A leading - is a unary expression operator applied to a literal.
Why
Decimal notation expresses magnitudes while binary and hexadecimal notation expose groups of bits. Changing the case of a radix prefix or hexadecimal digit changes neither the value nor the chosen radix. Lowercase removes that spelling choice while preserving the different purposes of the radix forms. A shared integer type keeps arithmetic independent of the chosen radix.
Source evidence
- validDigitRun, integerRadix, validIntegerLiteral, validDecimalLiteral at
af88afbc1d99· 14-46 - scanNumber at
af88afbc1d99· 467-514 - digit, integerRadix and validIntegerLiteral at
9b1bbdeceba9· 8-39 - validateNumericLiteral uses the malformed numeric literal diagnostic at
9b1bbdeceba9· 322-330 - Bytes.toHex emits lowercase hexadecimal at
9b1bbdeceba9· 122-128 - parseUnary still admits the plus prefix at
9b1bbdeceba9· 1436-1459 - issues/31 at
af88afbc1d99
Numeric digit separators
In a numeric digit run, _ separates two digits of the selected radix. The run starts and ends with a digit and cannot contain repeated separators. Parsing a malformed numeric token produces TPZ2001.
Why
Digit separators make long magnitudes easier to read. Requiring a digit on each side prevents separators from becoming prefixes or suffixes.
Source evidence
- validDigitRun, integerRadix, validIntegerLiteral, validDecimalLiteral at
af88afbc1d99· 14-46 - validateNumericLiteral at
af88afbc1d99· 322-330
Integer values and the signed range
An integer literal has a literal type compatible with int. Values range from - through 9223372036854775807. The signed minimum is accepted as -. An out-of-range integer value produces TPZ5001 during checking.
Why
A fixed signed range gives integer values the same bounds across hosts. Rejecting an unrepresentable literal prevents silent truncation before the program runs.
Source evidence
- inferUnaryExpression at
af88afbc1d99· 1044-1068 - builtin_bigint_to_int at
af88afbc1d99· 66-72 - literal expression inference at
af88afbc1d99· 1336-1362 - integerMagnitude, decodeIntegerLiteral at
af88afbc1d99· 47-69
Decimal float spellings and types
A float literal has decimal digits on both sides of .. Exponent notation is rejected as a malformed numeric token with TPZ2001 during parsing. A float literal has a literal type compatible with float.
Why
A decimal point distinguishes a floating literal from an integer literal. Digits on both sides separate it from member and range punctuation. Positional decimal spelling matches finite-float rendering, which expands the shortest decimal representation into positional notation. Excluding exponent notation keeps source and display on that shared convention. The values use binary64.
Source evidence
- validDigitRun, integerRadix, validIntegerLiteral, validDecimalLiteral at
af88afbc1d99· 14-46 - scanNumber at
af88afbc1d99· 467-514 - validateNumericLiteral at
af88afbc1d99· 322-330 - decodeDecimalFloatLiteral at
af88afbc1d99· 84-88 - literal expression inference at
af88afbc1d99· 1336-1362 - FLOAT_RENDER_GOLDENS at
271329b482bc· 21-32
Decimal float rounding, overflow, and signed zero
Decimal float literals round to the nearest binary64 value. An exact tie selects the value with an even final significand bit. Overflow produces infinity. Underflow can produce a subnormal value or zero. Unary minus retains the sign of zero. Interpolation writes the infinities as inf and -, and the two zeros as 0. and -. Decimal spelling still requires a point with digits on both sides; exponent spelling is rejected.
Why
Binary64 gives floating literals a common numeric representation. Defined rounding and signed zero make boundary values reproducible across the literal and arithmetic paths.
Source evidence
- decodeDecimalFloatLiteral and floatValueBits at
151141d7bb44· 84-116 - checked float bits in lowered detail at
151141d7bb44· 282-299 - floatLiteral emits f64::from_bits at
151141d7bb44· 308-316 - builtin_math_parse_float binary64 conversion at
151141d7bb44· 145-176 - render_float at
151141d7bb44· 38-46
Booleans, null, and unit
LinkBooleanLiteral = "true" | "false" ;NullLiteral = "null" ;UnitLiteral = "(" ")" ;
Parentheses containing an expression belong to the expression grammar.
Source evidence
- parseLiteralAtom at
af88afbc1d99· 1190-1232 - parseParenthesizedExpression at
af88afbc1d99· 1040-1053 - literal expression inference at
af88afbc1d99· 1336-1362
Boolean, null, and unit types
| Spelling | Type |
|---|---|
true | boolean literal type compatible with bool |
false | boolean literal type compatible with bool |
null | null literal type |
() | unit type () |
Why
Boolean values, absence, and unit represent distinct concepts. Their separate types prevent an absent value or a completed action from becoming an implicit Boolean.
Source evidence
- parseLiteralAtom at
af88afbc1d99· 1190-1232 - parseParenthesizedExpression at
af88afbc1d99· 1040-1053 - literal expression inference at
af88afbc1d99· 1336-1362 - parseParenthesizedType at
af88afbc1d99· 219-251 - formType at
af88afbc1d99· 621-643
For null compatibility, the contextual typing of None(), and restrictions on the binding name None, see Types and Bindings and scope.
Duration literals
LinkDurationLiteral = DecimalDigits DurationUnit ;DurationUnit = "ms" | "s" | "m" ;
DecimalDigits is defined in the numeric grammar above.
Concurrency owns the complete concurrent expression and timeout behavior.
Source evidence
- scanNumber at
af88afbc1d99· 467-514 - parseConcurrentTimeout at
af88afbc1d99· 722-779 - parseAtom at
af88afbc1d99· 1245-1255
Duration literals are ordinary values
A duration literal attaches a lowercase ms, s, m suffix to a decimal integer magnitude. It denotes an ordinary Duration value, including in bindings, function arguments and results, parenthesized expressions, and concurrent(timeout: .. Duration has no type arguments and does not implicitly convert to or from int or float. The same literal conversion and range checks apply in every expression position. Operations follow the Duration values rule.
Arithmetic, comparison, display, and toMillis follow Duration operations.
Why
A unit suffix states the time scale beside the magnitude. The same literal denotes Duration in an ordinary expression and in a timeout; binding or parenthesizing that value does not change its admission. Duration remains distinct from numeric types so an integer cannot silently acquire a time unit.
Source evidence
- scanNumber at
af88afbc1d99· 467-514 - parseConcurrentTimeout at
af88afbc1d99· 722-779 - parseAtom at
af88afbc1d99· 1245-1255 - pull/41 at
271329b482bc - issues/31 at
1cbf38d3d422
Duration values and operations
| Suffix | Unit | Milliseconds per unit |
|---|---|---|
ms | millisecond | 1 |
s | second | 1000 |
m | minute | 60000 |
A Duration stores from 0 through 18446744073709551615 milliseconds. An out-of-range duration literal produces TPZ5001 during checking.
Two Duration values support addition and subtraction. Multiplication takes a Duration and an int in either order, as in d * n and n * d. Construct a duration from an integer with 1ms * n. Duration has no division and no function for converting a number to Duration.
Eq compares millisecond counts for equality. Order compares those counts numerically. Display produces a decimal millisecond literal such as 1500ms, which reads back as the same value. to returns the millisecond count. Duration has no JSON capability in 6.0.
An overflowing sum or product, a subtraction below zero, any negative integer multiplier, or a millisecond count outside the int range stops with runtime TPZ4004. These faults follow the fault cleanup rule.
Runtime range failures use TPZ4004. Timeout evaluation follows concurrent deadlines.
Why
Every Duration stores milliseconds, so operations use one unit. A literal and integer multiplication provide one way to construct a duration from a number. The millisecond display reads back as the same value.
Source evidence
- decodeDurationMilliseconds at
af88afbc1d99· 70-83 - inferConcurrentExpression at
af88afbc1d99· 608-619 - issues/31 at
1cbf38d3d422
Length literals
LinkLengthLiteral = DecimalMagnitude LengthUnit ;DecimalMagnitude = DecimalDigits ("." DecimalDigits)? ;LengthUnit = "pt" | "mm" | "cm" | "in" | "em" ;
DecimalDigits is defined in the numeric grammar above.
Source evidence
- scanNumber at
af88afbc1d99· 467-514 - unitName, validLiteral, decodeLength at
af88afbc1d99· 8-59
Length spellings and types
A length literal attaches its lowercase unit directly to a decimal integer or decimal fraction. Radix and exponent notation are not length-literal forms.
| Suffix | Literal type |
|---|---|
pt | Length<Pt> |
mm | Length<Mm> |
cm | Length<Cm> |
in | Length<In> |
em | Length<Em> |
The suffix determines the compiler-owned length type. Literal formation rejects an unrepresentable result with TPZ5001 during checking.
Why
Typed units let page dimensions and font-relative dimensions enter layout APIs directly. Keeping em distinct from physical lengths prevents a font size from being assumed implicitly.
Source evidence
- scanNumber at
af88afbc1d99· 467-514 - unitName, validLiteral, decodeLength at
af88afbc1d99· 8-59 - lengthType, lengthUnit at
af88afbc1d99· 694-701 - literal expression inference at
af88afbc1d99· 1336-1362
Length conversions and arithmetic belong to their API entries. See Pending contracts for precision and exact literal endpoints.
Length rounding and literal boundaries
A Length literal is converted from its exact decimal value, including its sign. It rounds to the nearest multiple of the unit step below. An exact midpoint selects the even multiple.
| Unit | Step in that unit | Minimum input, included | Upper input limit, excluded |
|---|---|---|---|
pt | 1/65536 | -18446744073709551617/131072 | 18446744073709551615/131072 |
mm | 127/23592960 | -2342736497361113055359/47185920 | 156182433157407537007/3145728 |
cm | 127/235929600 | -2342736497361113055359/471859200 | 156182433157407537007/31457280 |
in | 1/4718592 | -18446744073709551617/9437184 | 6148914691236517205/3145728 |
em | 1/65536 | -18446744073709551617/131072 | 18446744073709551615/131072 |
The fractions are exact mathematical values. Write a decimal value followed immediately by its unit in source. A literal outside this interval produces TPZ5001 during checking. Equality and the public unit-conversion methods observe the rounded result.
Why
Exact decimal conversion fixes a document dimension before layout. A shared point grid aligns physical unit conversions, and ties-to-even rounding avoids directional bias at exact midpoints.
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Strings and interpolation
LinkStringLiteral = SingleLineString | MultilineString ;SingleLineString = '"' SingleLinePart* '"' ;MultilineString = '"""' MultilinePart* '"""' ;SingleLinePart = SingleLineText | Escape | Interpolation ;MultilinePart = MultilineText | Escape | Interpolation ;Escape = '\' ("n" | "r" | "t" | '\' | '"' | "{" | "}") ;Interpolation = "{" Expression "}" ;
SingleLineText and MultilineText are scanner-delimited text runs. A physical LF, CRLF, or CR is a line break. Inside a single-line string, text and interpolation reject physical line breaks. Multiline text uses them for opening removal and indentation, then stores LF. Escapes and inserted string values retain their carriage returns.
Source evidence
- isValidEscape, startsTripleQuote at
af88afbc1d99· 166-181 - scanString at
af88afbc1d99· 343-434 - parseString at
af88afbc1d99· 332-410
String delimiters and interpolation
| String form | Opening and closing delimiter |
|---|---|
| single-line | " |
| multiline | """ |
An unescaped { begins interpolation containing an expression followed by }. An empty interpolation produces TPZ2001 during parsing.
Why
Interpolation joins evaluated values with literal text through one string grammar. Multiline delimiters allow document text to retain visible line structure in source.
Source evidence
- scanString at
af88afbc1d99· 343-434 - parseString at
af88afbc1d99· 332-410
String escape values
Single-line and multiline strings use these escapes, including strings tagged r, sh, sql.
| Escape | Value | Code point |
|---|---|---|
\n | line feed | U+000A |
\r | carriage return | U+000D |
\t | tab | U+0009 |
\\ | backslash | U+005C |
\" | quotation mark | U+0022 |
\{ | opening brace | U+007B |
\} | closing brace | U+007D |
An unknown escape produces TPZ0004 during lexing for every tag. Write an escaped backslash when a literal backslash is intended.
Why
One escape grammar gives a backslash the same meaning in strings and every template tag. Explicit escapes distinguish delimiters from text.
Source evidence
- isValidEscape, startsTripleQuote at
af88afbc1d99· 166-181 - escapeValue at
af88afbc1d99· 118-123 - scanStringEscape, scanStringInterpolation at
af88afbc1d99· 247-318 - issues/31 at
570530e69141 - scanStringEscape at
570530e69141· 247-273 - escapeValue at
570530e69141· 118-123 - template parts accessor at
570530e69141· 506-519 - pull/41 at
271329b482bc
Single-line string boundaries
A single-line string cannot cross a physical LF, CRLF, or CR line break, including inside interpolation. Crossing one produces TPZ0003 during lexing. An escaped carriage return or a carriage return in an inserted string remains character data.
Why
A physical line break ends an unfinished single-line string before later source is consumed as text. Equivalent file line endings must produce the same rejection.
Source evidence
- scanString at
af88afbc1d99· 343-434 - scanStringEscape, scanStringInterpolation at
af88afbc1d99· 247-318 - scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - scanCode propagates LF and CRLF from single-line interpolation at
9b1bbdeceba9· 572-628 - interruptedString reports TPZ0003 at the opening delimiter at
9b1bbdeceba9· 303-325
String literal and interpolation types
Ordinary strings use the following type selection.
| Interpolation | Type |
|---|---|
| absent | string literal type |
| present | string |
Why
A fixed literal has a known value that type checking can retain. Interpolation depends on evaluated expressions, so its result uses the general string type.
Source evidence
- inferTextExpression at
af88afbc1d99· 499-537
ByteBuffer interpolation
Interpolating a value whose type contains Byte in a string or tagged template produces TPZ5001 during checking.
Why
ByteBuffer is mutable storage rather than text. Rejecting its implicit display keeps byte decoding and the choice of character encoding explicit.
Source evidence
- inferTextExpression at
af88afbc1d99· 499-537
See Pending contracts for value formatting, evaluation order, and the number of evaluations.
Ordinary string interpolation order and conversion
In an ordinary string, each interpolation expression is evaluated once in source order. A nested interpolation completes before the next outer expression. The value then uses the standard renderer, with no automatic call to a user-defined show method. Inserted spaces and newlines remain unchanged.
| Value family | Conversion |
|---|---|
| Scalars | Strings insert their text. Integers and floats use numeric text. Booleans, null, and unit use their literal spellings. |
| Arrays and structural records | Elements and named fields render recursively, separated by commas. |
| Option and Result | The constructor name surrounds the rendered payload. None has no payload. |
| Nominal values | Records, enum variants, and newtypes retain their nominal identity and recursively render their contents. |
| Collections and ranges | Map and Set use their standard collection displays. Ranges show their endpoints and optional step. |
| Functions and resources | Functions, files, namespaces, and opaque values use their standard display labels. |
| Library values | Bytes use hexadecimal text. Path, Regex, Date, URL, BigInt, Decimal, and other built-in values use their standard representations. |
| Templates | Display a summary with the tag and counts of parts and values. |
A value whose type contains Byte produces TPZ5001 during checking, including inside arrays, records, unions, and nominal fields.
Why
Evaluating each expression once in source order makes effects predictable. Preserving inserted whitespace lets document content determine its own line and spacing structure.
Source evidence
- ordered string parts and nested expressions at
151141d7bb44· 358-409 - emitString sequential render steps at
151141d7bb44· 337-385 - inferTextExpression at
151141d7bb44· 499-537 - containsByteBuffer recursion at
151141d7bb44· 226-286 - render_into value conversion cases at
151141d7bb44· 169-409 - render_aggregate_into at
151141d7bb44· 90-116
Preserved Unicode string data
Literal text, inserted strings, and template text preserve their Unicode scalar sequence after the specified escape, physical-line-break, indentation, and blank-line processing. They do not apply Unicode normalization or remove Default_Ignorable_Code_Point scalars. Composed and decomposed text can therefore produce different UTF-8 bytes. A name inside an interpolation follows the identifier rules because it is part of the expression.
Why
Text values may carry externally supplied spelling, file contents, or protocol bytes. Implicit Unicode normalization or removal of invisible scalars would lose that data. Names use NFC to give bindings a canonical source spelling. A string preserves its scalar sequence after the separately specified source-text transformations.
Source evidence
- LL-3 recommendation: NFC source names, combining continuations, and preserved string data at
72a59a31125f - Canonical normalization forms at
Unicode-16.0· 3.11.7 - escapeValue at
af88afbc1d99· 118-123 - scanStringEscape, scanStringInterpolation at
af88afbc1d99· 247-318 - issues/31 at
a545e8811f8f
Multiline strings
LinkMultiline opening line break
In a multiline string, one physical LF, CRLF, or CR line break immediately after the opening delimiter is removed from the value. CRLF is one line break.
Why
Removing the opening line break lets a document text block start on the line after its delimiter without adding an empty first line.
Source evidence
- stringLayout, cookStringParts at
af88afbc1d99· 201-332 - scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315 - issues/31 at
af88afbc1d99
Multiline closing indentation
When the closing delimiter follows spaces and tabs on its own line, that exact prefix is removed from each nonblank text line. Physical LF, CRLF, and CR each start a new line for this check. A nonblank line without that prefix produces TPZ0006 during lexing. The closing indentation is also removed.
Why
Removing the closing delimiter prefix separates source indentation from document indentation. Checking every physical line-ending form prevents file conversion from bypassing the indentation check.
Source evidence
- stringLayout, cookStringParts at
af88afbc1d99· 201-332 - scanString at
af88afbc1d99· 343-434 - multilineIndentDiagnostics at
af88afbc1d99· 182-191 - scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315 - multilineIndentDiagnostics reports TPZ0006 for a missing source prefix at
9b1bbdeceba9· 182-191 - issues/31 at
af88afbc1d99
Multiline newline values
Physical LF, CRLF, and CR line breaks in multiline text become LF. CRLF becomes one LF. A newline before the closing indentation remains in the value. Normalization applies to source text before escape decoding and value insertion. An escaped carriage return or a carriage return in an inserted string remains character data. Interpolation expressions use the source expression grammar.
Why
Treating physical LF, CRLF, and CR alike keeps document text stable when a file changes line-ending format. Escapes and inserted values preserve intentional carriage-return data.
Source evidence
- stringLayout, cookStringParts at
af88afbc1d99· 201-332 - scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315 - issues/31 at
af88afbc1d99
Whitespace and interpolation in multiline strings
A multiline source text line containing just spaces (U+0020) and tabs (U+0009) contributes an empty line. Its line break remains. This applies with or without closing indentation, in ordinary and tagged strings. Removal happens before escape decoding and value insertion. Escaped and inserted whitespace remains data. Nonblank indentation compares the exact source prefix, without expanding tabs. An interpolated value cannot supply missing indentation. A prefix mismatch produces TPZ0006 during lexing. Physical LF, CRLF, and CR start new text lines and become LF in the string value.
Why
Blank source lines remain empty when an editor changes their indentation. This keeps document text independent of formatting. Source whitespace is removed before escapes and interpolation, so explicit character data remains intact. Exact nonblank prefixes avoid dependence on an editor tab width.
Source evidence
- stringLayout at
151141d7bb44· 201-246 - cookStringParts at
151141d7bb44· 250-332 - multilineIndentDiagnostics at
151141d7bb44· 182-191 - scanString at
151141d7bb44· 343-426 - scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315 - multilineIndentDiagnostics reports TPZ0006 for a missing source prefix at
9b1bbdeceba9· 182-191 - issues/31 at
af88afbc1d99 - issues/31 at
23582e1d56db
Tagged templates
LinkTemplateLiteral = TemplateTag StringLiteral ;TemplateTag = "r" | "sh" | "sql" ;
String is defined above. Every tag uses its complete escape grammar, including the same delimiter and interpolation-brace escapes.
Tag-specific processing and operations on the resulting template value belong to their library entries.
Source evidence
- parseString at
af88afbc1d99· 332-410 - issues/31 at
570530e69141 - pull/41 at
271329b482bc
Registered template tags
The template tags are r, sh, sql. Write the tag immediately before a single-line or triple-quoted string. A different immediately attached identifier produces TPZ2002 during parsing.
Why
The registry keeps template consumers explicit and catches misspelled tags. Paths use the ordinary Path.from call, which accepts both literal and computed strings and returns the same validated Path type. The former p tag produced a template with slash-only display conversion, so it is removed rather than retain a second path-looking spelling with different validation. This follows the Path.from-only owner decision and the one-way-to-say-it criterion. Joining path parts safely is expressed by Path.join. Reading text as a path is expressed by Path.from. A path template tag adds no distinct intent to either operation.
Source evidence
- parseString at
af88afbc1d99· 332-410 - identifierHeadKind at
af88afbc1d99· 103-110 - pull/41 at
271329b482bc - issues/31 at
23582e1d56db
Template value types
A registered tagged template is checked as a template value.
Why
A distinct template type preserves the boundary between structured text and an ordinary string. Consumers can inspect the tag and literal parts before interpreting them.
Source evidence
- inferTextExpression at
af88afbc1d99· 499-537
Each registered tag uses the string escape grammar above. Construction, accessors, and display are defined below. Construct a path with Path.from applied to an ordinary string; p is not a registered template tag.
Tagged-template construction and display
Registered tags construct template values from cooked text parts and expressions evaluated once in source order. There is one more text part than interpolation values. tag returns the tag as a string. parts returns an array value of the literal text segments under value semantics. Interpolated values remain separate and have no public accessor.
| Tag | Construction and string-interpolation display |
|---|---|
r | Keeps the pattern structure without compiling or validating a regular expression. Display reports the tag and counts of parts and values. |
sh | Keeps text and values separate without executing a command. Display reports the tag and counts. |
sql | Keeps query text and values separate without executing a query or inserting values into query text. Display reports the tag and counts. |
A template value cannot be passed where string is required. Use ordinary string interpolation to obtain its display. All tags use the shared string escape grammar. For example, r"\\d" preserves a regex backslash, and \{ and \} preserve literal braces.
Why
Keeping inserted values separate from text lets a consumer interpret their roles without reconstructing them from a string. Construction does not execute the embedded command or query. Joining path parts safely is expressed by Path.join. Reading text as a path is expressed by Path.from. A path template tag adds no distinct intent to either operation.
Source evidence
- TemplateData, render_into and make_template at
151141d7bb44· 1728-1788 - tag and fresh parts accessors at
151141d7bb44· 506-519 - emitString uses make_template with evaluated values at
151141d7bb44· 337-385 - template classification and ByteBuffer rejection at
151141d7bb44· 499-537 - templateMemberType accessors at
151141d7bb44· 1226-1230 - scanStringEscape shared by tagged strings at
151141d7bb44· 247-273 - issues/31 at
570530e69141 - scanStringEscape at
570530e69141· 247-273 - escapeValue at
570530e69141· 118-123 - template parts accessor at
570530e69141· 506-519 - pull/41 at
271329b482bc - issues/31 at
23582e1d56db - issues/31 at
1162031127a0
Constructing paths
LinkConstruct a normalized Path with Path.from
Path. follows the Path construction contract. That rule defines normalization, input failures and removed construction members.
Why
Path. gives literal, interpolated and computed text one conversion to a relative path. Its construction contract owns normalization and input failures. The old cwd and project factories performed the same pure normalization. Their different error prefixes did not express a distinct operation. The old normalize member returned its receiver. Path. safely combines path parts, while Path. reads text as a path. The removed p tag added no distinct intent to either operation.
Source evidence
- issues/31 at
271329b482bc - pull/41 at
271329b482bc - normalize_path_text and the three text factories at
271329b482bc· 261-341 - builtin_path_normalize returns the existing normalized Path at
271329b482bc· 424-427 - Path static member signatures at
271329b482bc· 1733-1744 - Path receiver member signatures at
271329b482bc· 1040-1051 - reportMissingMember uses TPZ5006 at
271329b482bc· 1364-1402 - p constructs TemplateData and replaces backslashes only in display at
271329b482bc· 1728-1788 - issues/31 at
23582e1d56db
Examples and counterexamples
LinkInteger bases and decimal floats
Linklet decimal: int = 1_000let hexadecimal: int = 0xca_felet binary: int = 0b1010_0101let fraction: float = 1_234.5_6let minimum: int = -9223372036854775808let maximum: int = 9223372036854775807let value: int = 1_0
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- validDigitRun, integerRadix, validIntegerLiteral, validDecimalLiteral at
af88afbc1d99· 14-46 - inferUnaryExpression at
af88afbc1d99· 1044-1068 - builtin_bigint_to_int at
af88afbc1d99· 66-72 - literal expression inference at
af88afbc1d99· 1336-1362
Repeated digit separator
Linklet value = 1__0
No stdout.
error[TPZ2001]: malformed numeric literal --> example.tpz:1:13 | 1 | let value = 1__0 | ^^^^ example.tpz: 1 diagnostic
Expected result
Expected result
Rejection during parse. Nonzero exit.
TPZ2001malformed-numeric-literal atexample.tpz:1:13
Observed assertions · passed · 5.21.3
Exit code 1.
No stdout.
error[TPZ2001]: malformed numeric literal--> example.tpz:1:13|1 | let value = 1__0| ^^^^example.tpz: 1 diagnostic
- Passed · checking TPZ2001; cause malformed-numeric-literal; primary location asserted; additional diagnostics allow
- Passed · expected nonzero; observed 1
The intended cause is the repeated underscore inside the numeric token. Replace it with one underscore or remove it.
Source evidence
- validDigitRun, integerRadix, validIntegerLiteral, validDecimalLiteral at
af88afbc1d99· 14-46 - validateNumericLiteral at
af88afbc1d99· 322-330
Exponent spelling
Linklet value = 1e3
No stdout.
error[TPZ2001]: malformed numeric literal --> example.tpz:1:13 | 1 | let value = 1e3 | ^^^ example.tpz: 1 diagnostic
Expected result
Expected result
Rejection during parse. Nonzero exit.
TPZ2001malformed-numeric-literal atexample.tpz:1:13
Observed assertions · passed · 5.21.3
Exit code 1.
No stdout.
error[TPZ2001]: malformed numeric literal--> example.tpz:1:13|1 | let value = 1e3| ^^^example.tpz: 1 diagnostic
- Passed · checking TPZ2001; cause malformed-numeric-literal; primary location asserted; additional diagnostics allow
- Passed · expected nonzero; observed 1
Use 1000. when the intended value is one thousand as a float.
Source evidence
- scanNumber at
af88afbc1d99· 467-514 - validateNumericLiteral at
af88afbc1d99· 322-330
Integer magnitude above the signed range
Linklet value = 9223372036854775808
No stdout.
error[TPZ5001]: integer literal `9223372036854775808` is out of range --> example.tpz:1:13 | 1 | let value = 9223372036854775808 | ^^^^^^^^^^^^^^^^^^^ example.tpz: 1 type diagnostic
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001integer-literal-out-of-range atexample.tpz:1:13
Observed assertions · passed · 5.21.3
Exit code 1.
No stdout.
error[TPZ5001]: integer literal `9223372036854775808` is out of range--> example.tpz:1:13|1 | let value = 9223372036854775808| ^^^^^^^^^^^^^^^^^^^example.tpz: 1 type diagnostic
- Passed · checking TPZ5001; cause integer-literal-out-of-range; primary location asserted; additional diagnostics allow
- Passed · expected nonzero; observed 1
Source evidence
- literal expression inference at
af88afbc1d99· 1336-1362 - builtin_bigint_to_int at
af88afbc1d99· 66-72
Boolean, null, and unit values
Linklet enabled: bool = truelet disabled: bool = falselet absent = nulllet completed: () = ()
example.tpz: types-ok (1 module) example.tpz: resolve-ok (1 module)
Expected result
Expected result
Checking accepts the program. Exit code 0.
Observed assertions · passed · 5.21.3
Exit code 0.
example.tpz: types-ok (1 module)example.tpz: resolve-ok (1 module)
No stderr.
- Passed · expected 0; observed 0
Source evidence
- parseLiteralAtom at
af88afbc1d99· 1190-1232 - parseParenthesizedExpression at
af88afbc1d99· 1040-1053 - literal expression inference at
af88afbc1d99· 1336-1362
Duration in its timeout context
Linklet result = concurrent(timeout: 1_500ms) {value: 1} else {{ value: 0 }}
example.tpz: types-ok (1 module) example.tpz: resolve-ok (1 module)
Expected result
Expected result
Checking accepts the program. Exit code 0.
Observed assertions · passed · 5.21.3
Exit code 0.
example.tpz: types-ok (1 module)example.tpz: resolve-ok (1 module)
No stderr.
- Passed · expected 0; observed 0
Source evidence
- parseConcurrentTimeout at
af88afbc1d99· 722-779 - inferConcurrentExpression at
af88afbc1d99· 608-619
A Duration value outside a timeout
Linklet delay = 3slet saved: Duration = (delay)
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
The stable example identity formerly tested the SELF-only context restriction. Its old fixture, assertion plan and receipt remain historical evidence. The current version expects an ordinary Duration binding to succeed; the original receipt is not evidence of this revised contract.
Length suffixes and types
Linklet points: Length<Pt> = 12ptlet millimeters: Length<Mm> = 2.5mmlet centimeters: Length<Cm> = 1cmlet inches: Length<In> = 0.5inlet relative: Length<Em> = 1.25em
example.tpz: types-ok (1 module) example.tpz: resolve-ok (1 module)
Expected result
Expected result
Checking accepts the program. Exit code 0.
Observed assertions · passed · 5.21.3
Exit code 0.
example.tpz: types-ok (1 module)example.tpz: resolve-ok (1 module)
No stderr.
- Passed · expected 0; observed 0
Source evidence
- unitName, validLiteral, decodeLength at
af88afbc1d99· 8-59 - lengthType, lengthUnit at
af88afbc1d99· 694-701 - literal expression inference at
af88afbc1d99· 1336-1362
Escapes and an interpolated value
Linkimport std.iolet count = 3io.writeLine("Topaz {count}: \"\{ok\}\"\\")io.writeLine("first\nsecond\tend")
Topaz 3: "{ok}"\
first
second end
Expected result
Expected result
Execution completes. Exit code 0.
Topaz 3: "{ok}"\firstsecond end
Observed assertions · passed · 5.21.3
Exit code 0.
Topaz 3: "{ok}"\firstsecond end
No stderr.
- Passed · expected 0; observed 0
- Passed · stdout matches the authored expectation
- Passed · stderr matches the authored expectation
Source evidence
- isValidEscape, startsTripleQuote at
af88afbc1d99· 166-181 - parseString at
af88afbc1d99· 332-410 - inferTextExpression at
af88afbc1d99· 499-537
Unknown escape
Linklet text = "bad\q"
No stdout.
error[TPZ0004]: invalid escape `\q` in a string literal --> example.tpz:1:16 | 1 | let text = "bad\q" | ^^ example.tpz: 1 diagnostic
Expected result
Observed assertions · passed · 5.21.3
Exit code 1.
No stdout.
error[TPZ0004]: invalid escape `\q` in a string literal--> example.tpz:1:16|1 | let text = "bad\q"| ^^example.tpz: 1 diagnostic
- Passed · checking TPZ0004; cause invalid-string-escape; primary location asserted; additional diagnostics allow
- Passed · expected nonzero; observed 1
Replace the unknown escape with a supported escape or write an escaped backslash when a literal backslash is intended.
Source evidence
- isValidEscape, startsTripleQuote at
af88afbc1d99· 166-181 - scanStringEscape, scanStringInterpolation at
af88afbc1d99· 247-318
Multiline indentation and final newline
Linkimport std.ioio.writeLine("""firstsecond""")
first second
Expected result
Expected result
Execution completes. Exit code 0.
firstsecond
Observed assertions · passed · 5.21.3
Exit code 0.
firstsecond
No stderr.
- Passed · expected 0; observed 0
- Passed · stdout matches the authored expectation
- Passed · stdout matches the authored expectation
- Passed · stdout matches the authored expectation
- Passed · stderr matches the authored expectation
Source evidence
- stringLayout, cookStringParts at
af88afbc1d99· 201-332 - scanString at
af88afbc1d99· 343-434
The four registered template tags
Linklet regexTemplate = r"a+"let shellTemplate = sh"echo hello"let sqlTemplate = sql"select {1}"
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- parseString at
af88afbc1d99· 332-410 - inferTextExpression at
af88afbc1d99· 499-537 - pull/41 at
271329b482bc
Tag outside the registry
Linklet value = html"hello"
No stdout.
error[TPZ2002]: unknown template tag `html`; the registry is `p`, `r`, `sh`, `sql` --> example.tpz:1:13 | 1 | let value = html"hello" | ^^^^ example.tpz: 1 diagnostic
Expected result
Expected result
Rejection during parse. Nonzero exit.
TPZ2002unknown-template-tag atexample.tpz:1:13
Observed assertions · passed · 5.21.3
Exit code 1.
No stdout.
error[TPZ2002]: unknown template tag `html`; the registry is `p`, `r`, `sh`, `sql`--> example.tpz:1:13|1 | let value = html"hello"| ^^^^example.tpz: 1 diagnostic
- Passed · checking TPZ2002; cause unknown-template-tag; primary location asserted; additional diagnostics allow
- Passed · expected nonzero; observed 1
The counterexample uses the tag html. The program does not invoke an HTML operation.
Source evidence
- parseString at
af88afbc1d99· 332-410
Blank source lines stay empty while CRLF and CR become LF
Linkimport std.iolet part = "x"let value = """A{part}B C"""io.writeLine(value.encodeUtf8().toHex())
41780a0a420a430a
Expected result
Expected result
Execution completes. Exit code 0.
41780a0a420a430a
Source evidence
- stringLayout at
151141d7bb44· 201-246 - cookStringParts at
151141d7bb44· 250-332 - multilineIndentDiagnostics at
151141d7bb44· 182-191 - scanString at
151141d7bb44· 343-426 - scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315
A tab prefix is not two spaces
Linklet value = """first"""
Expected result
Expected result
Rejection during lex. Nonzero exit.
TPZ0006multiline-closing-indent-mismatch atexample.tpz:2:1
Source evidence
- stringLayout at
151141d7bb44· 201-246 - cookStringParts at
151141d7bb44· 250-332 - multilineIndentDiagnostics at
151141d7bb44· 182-191 - scanString at
151141d7bb44· 343-426
Interpolation cannot supply source indentation
Linklet padding = " "let value = """{padding}body"""
Expected result
Expected result
Rejection during lex. Nonzero exit.
TPZ0006multiline-closing-indent-mismatch atexample.tpz:3:1
Source evidence
- stringLayout at
151141d7bb44· 201-246 - cookStringParts at
151141d7bb44· 250-332 - multilineIndentDiagnostics at
151141d7bb44· 182-191 - scanString at
151141d7bb44· 343-426
Observe ties-to-even through equality and unit conversion
Linkimport std.iolet half = 0.00000762939453125ptlet oneAndHalf = 0.00002288818359375ptlet two = 0.000030517578125ptio.writeLine("{half == 0pt}|{oneAndHalf == two}|{-half == 0pt}")io.writeLine("{(25.4mm).toPt() == 72pt}|{(2.54cm).toPt() == 72pt}|{(1in).toPt() == 72pt}")io.writeLine("{0.00000762939453125em == 0em}|{0.00002288818359375em == 0.000030517578125em}")
true|true|true true|true|true true|true
Expected result
Expected result
Execution completes. Exit code 0.
true|true|truetrue|true|truetrue|true
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Decimal values inside both limits for every unit
Linklet good0 = -140737488355328.000007629394531250000000000000ptlet good1 = 140737488355327.999992370605468749999999999999ptlet good2 = -49649058392018.488891580369737413194444444444mmlet good3 = 49649058392018.488886197408040364583333333333mmlet good4 = -4964905839201.848889158036973741319444444444cmlet good5 = 4964905839201.848888619740804036458333333333cmlet good6 = -1954687338268.444444550408257378472222222222inlet good7 = 1954687338268.444444338480631510416666666666inlet good8 = -140737488355328.000007629394531250000000000000emlet good9 = 140737488355327.999992370605468749999999999999em
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Observe rounding, infinity, subnormals, and signed zero
Linkimport std.iolet tieEven = 1.00000000000000011102230246251565404236316680908203125let tieNext = 1.00000000000000033306690738754696212708950042724609375let overflow = 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0let tiny = 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001let subnormal = 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005let twiceSubnormal = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001io.writeLine("{tieEven == 1.0}|{tieNext == 1.0000000000000004}")io.writeLine("{overflow}|{-overflow}")io.writeLine("{tiny}|{-tiny}|{1.0 / tiny}|{1.0 / -tiny}")io.writeLine("{subnormal > 0.0}|{subnormal < twiceSubnormal}|{subnormal + subnormal == twiceSubnormal}")
true|true inf|-inf 0.0|-0.0|inf|-inf true|true|true
Expected result
Expected result
Execution completes. Exit code 0.
true|trueinf|-inf0.0|-0.0|inf|-inftrue|true|true
Source evidence
- decodeDecimalFloatLiteral and floatValueBits at
151141d7bb44· 84-116 - checked float bits in lowered detail at
151141d7bb44· 282-299 - floatLiteral emits f64::from_bits at
151141d7bb44· 308-316 - builtin_math_parse_float binary64 conversion at
151141d7bb44· 145-176 - render_float at
151141d7bb44· 38-46
Exponent spelling is not a decimal float literal
Linklet value = 1.0e3
Expected result
Expected result
Rejection during parse. Nonzero exit.
TPZ2001malformed-numeric-literal atexample.tpz:1:13
Source evidence
- decodeDecimalFloatLiteral and floatValueBits at
151141d7bb44· 84-116 - checked float bits in lowered detail at
151141d7bb44· 282-299 - floatLiteral emits f64::from_bits at
151141d7bb44· 308-316 - builtin_math_parse_float binary64 conversion at
151141d7bb44· 145-176 - render_float at
151141d7bb44· 38-46 - scanNumber at
af88afbc1d99· 467-514 - validateNumericLiteral at
af88afbc1d99· 322-330
Nested interpolations evaluate each occurrence once
Linkimport std.iofunction mark(n: int) -> int { io.writeLine("mark {n}")n }let value = "outer {mark(1)}|{"inner {mark(2)}"}|{mark(3)}"io.writeLine(value)
mark 1 mark 2 mark 3 outer 1|inner 2|3
Expected result
Expected result
Execution completes. Exit code 0.
mark 1mark 2mark 3outer 1|inner 2|3
Source evidence
- ordered string parts and nested expressions at
151141d7bb44· 358-409 - emitString sequential render steps at
151141d7bb44· 337-385 - inferTextExpression at
151141d7bb44· 499-537 - containsByteBuffer recursion at
151141d7bb44· 226-286 - render_into value conversion cases at
151141d7bb44· 169-409 - render_aggregate_into at
151141d7bb44· 90-116
Display scalars and nested values with the standard renderer
Linkimport std.iolet row = { count: 3 }let selected: Option<int> = Some(3)let missing: Option<int> = None()let ok: Result<int, string> = Ok(4)let err: Result<int, string> = Err("no")io.writeLine("{7}|{2.5}|{true}|{()}|{null}")io.writeLine("{[1, 2]}|{row}|{selected}|{missing}|{ok}|{err}")
7|2.5|true|()|null
[1, 2]|{ count: 3 }|Some(3)|None|Ok(4)|Err(no)
Expected result
Expected result
Execution completes. Exit code 0.
7|2.5|true|()|null[1, 2]|{ count: 3 }|Some(3)|None|Ok(4)|Err(no)
Source evidence
- ordered string parts and nested expressions at
151141d7bb44· 358-409 - emitString sequential render steps at
151141d7bb44· 337-385 - inferTextExpression at
151141d7bb44· 499-537 - containsByteBuffer recursion at
151141d7bb44· 226-286 - render_into value conversion cases at
151141d7bb44· 169-409 - render_aggregate_into at
151141d7bb44· 90-116
An aggregate containing ByteBuffer cannot be interpolated
Linklet buffer = ByteBuffer.allocate(1)let nested = [buffer]let value = "{nested}"
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001byte-buffer-interpolation atexample.tpz:3:15
Source evidence
- ordered string parts and nested expressions at
151141d7bb44· 358-409 - emitString sequential render steps at
151141d7bb44· 337-385 - inferTextExpression at
151141d7bb44· 499-537 - containsByteBuffer recursion at
151141d7bb44· 226-286 - render_into value conversion cases at
151141d7bb44· 169-409 - render_aggregate_into at
151141d7bb44· 90-116
Inspect templates without executing or validating their contents
Linkimport std.ioimport std.path { Path }function mark(n: int) -> int { io.writeLine("mark {n}")n }let segment = "a\\b"let path = match (Path.from("dir\\{segment}/{mark(1)}")) {case Ok(value) => value.toString()case Err(error) => error}let pattern = r"[{mark(2)}"let command = sh"echo {mark(3)}"let query = sql"SELECT {mark(4)}"io.writeLine("{path}")io.writeLine("{pattern}|{command}|{query}")io.writeLine("{query.tag}|{query.parts.length}|{query.parts[0]}|{query.parts[1]}")let mut parts = query.partsparts[0] = "changed"io.writeLine("{query.parts[0]}")
mark 1 mark 2 mark 3 mark 4 dir/a/b/1 <r template, 2 part(s), 1 interpolation(s)>|<sh template, 2 part(s), 1 interpolation(s)>|<sql template, 2 part(s), 1 interpolation(s)> sql|2|SELECT | SELECT
Expected result
Expected result
Execution completes. Exit code 0.
mark 1mark 2mark 3mark 4dir/a/b/1<r template, 2 part(s), 1 interpolation(s)>|<sh template, 2 part(s), 1 interpolation(s)>|<sql template, 2 part(s), 1 interpolation(s)>sql|2|SELECT |SELECT
The parts accessor returns an array value under value semantics. Changing parts leaves query. unchanged.
Source evidence
- TemplateData, render_into and make_template at
151141d7bb44· 1728-1788 - tag and fresh parts accessors at
151141d7bb44· 506-519 - emitString uses make_template with evaluated values at
151141d7bb44· 337-385 - template classification and ByteBuffer rejection at
151141d7bb44· 499-537 - templateMemberType accessors at
151141d7bb44· 1226-1230 - scanStringEscape shared by tagged strings at
151141d7bb44· 247-273
A template value cannot initialize string
Linklet value: string = sql"SELECT 1"
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001template-is-not-string atexample.tpz:1:21
Source evidence
- TemplateData, render_into and make_template at
151141d7bb44· 1728-1788 - tag and fresh parts accessors at
151141d7bb44· 506-519 - emitString uses make_template with evaluated values at
151141d7bb44· 337-385 - template classification and ByteBuffer rejection at
151141d7bb44· 499-537 - templateMemberType accessors at
151141d7bb44· 1226-1230 - scanStringEscape shared by tagged strings at
151141d7bb44· 247-273
Tagged interpolation rejects ByteBuffer too
Linklet buffer = ByteBuffer.allocate(1)let value = sql"SELECT {buffer}"
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001byte-buffer-interpolation atexample.tpz:2:25
Source evidence
- TemplateData, render_into and make_template at
151141d7bb44· 1728-1788 - tag and fresh parts accessors at
151141d7bb44· 506-519 - emitString uses make_template with evaluated values at
151141d7bb44· 337-385 - template classification and ByteBuffer rejection at
151141d7bb44· 499-537 - templateMemberType accessors at
151141d7bb44· 1226-1230 - scanStringEscape shared by tagged strings at
151141d7bb44· 247-273
Reject a value past the negative pt limit
Linklet bad0 = -140737488355328.000007629394531250000000000001pt
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001length-literal-not-representable atexample.tpz:1:12
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Reject a value past the positive pt limit
Linklet bad1 = 140737488355327.999992370605468750000000000000pt
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001length-literal-not-representable atexample.tpz:1:12
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Reject a value past the negative mm limit
Linklet bad2 = -49649058392018.488891580369737413194444444445mm
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001length-literal-not-representable atexample.tpz:1:12
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Reject a value past the positive mm limit
Linklet bad3 = 49649058392018.488886197408040364583333333334mm
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001length-literal-not-representable atexample.tpz:1:12
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Reject a value past the negative cm limit
Linklet bad4 = -4964905839201.848889158036973741319444444445cm
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001length-literal-not-representable atexample.tpz:1:12
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Reject a value past the positive cm limit
Linklet bad5 = 4964905839201.848888619740804036458333333334cm
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001length-literal-not-representable atexample.tpz:1:12
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Reject a value past the negative in limit
Linklet bad6 = -1954687338268.444444550408257378472222222223in
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001length-literal-not-representable atexample.tpz:1:12
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Reject a value past the positive in limit
Linklet bad7 = 1954687338268.444444338480631510416666666667in
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001length-literal-not-representable atexample.tpz:1:12
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Reject a value past the negative em limit
Linklet bad8 = -140737488355328.000007629394531250000000000001em
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001length-literal-not-representable atexample.tpz:1:12
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Reject a value past the positive em limit
Linklet bad9 = 140737488355327.999992370605468750000000000000em
Expected result
Expected result
Rejection during check. Nonzero exit.
TPZ5001length-literal-not-representable atexample.tpz:1:12
Source evidence
- roundFraction and decodeLength at
151141d7bb44· 19-59 - signedLengthInput at
151141d7bb44· 101-128 - signed Length literal validation at
151141d7bb44· 1069-1085 - unsigned Length literal validation at
151141d7bb44· 1353-1358 - public Length conversion and milliPoints methods at
151141d7bb44· 100-141
Every tag cooks the same single-line escapes
Linkimport std.iolet pattern = r"\n\r\t\\\"\{\}"io.writeLine(pattern.parts[0])let command = sh"\n\r\t\\\"\{\}"io.writeLine(command.parts[0])let query = sql"\n\r\t\\\"\{\}"io.writeLine(query.parts[0])
\"{}
\"{}
\"{}
Expected result
Expected result
Execution completes. Exit code 0.
\"{}\"{}\"{}
The standard-output expectation checks the actual LF, CR, tab, backslash, quotation-mark, and brace characters in each cooked text part.
Source evidence
- issues/31 at
570530e69141 - scanStringEscape at
570530e69141· 247-273 - escapeValue at
570530e69141· 118-123 - template parts accessor at
570530e69141· 506-519
Every tag cooks the same multiline escapes
Linkimport std.iolet pattern = r"""\n\r\t\\\"\{\}"""io.writeLine(pattern.parts[0])let command = sh"""\n\r\t\\\"\{\}"""io.writeLine(command.parts[0])let query = sql"""\n\r\t\\\"\{\}"""io.writeLine(query.parts[0])
\"{}
\"{}
\"{}
Expected result
Expected result
Execution completes. Exit code 0.
\"{}\"{}\"{}
The standard-output expectation checks the actual LF, CR, tab, backslash, quotation-mark, and brace characters in each cooked text part.
Source evidence
- issues/31 at
570530e69141 - scanStringEscape at
570530e69141· 247-273 - escapeValue at
570530e69141· 118-123 - template parts accessor at
570530e69141· 506-519
Unknown escape in a r template
Linklet value = r"\d"
Expected result
Use a supported escape or double the backslash to keep it as literal text.
Source evidence
- issues/31 at
570530e69141 - scanStringEscape at
570530e69141· 247-273 - escapeValue at
570530e69141· 118-123 - template parts accessor at
570530e69141· 506-519
Unknown escape in a sh template
Linklet value = sh"\q"
Expected result
Use a supported escape or double the backslash to keep it as literal text.
Source evidence
- issues/31 at
570530e69141 - scanStringEscape at
570530e69141· 247-273 - escapeValue at
570530e69141· 118-123 - template parts accessor at
570530e69141· 506-519
Unknown escape in a sql template
Linklet value = sql"\q"
Expected result
Use a supported escape or double the backslash to keep it as literal text.
Source evidence
- issues/31 at
570530e69141 - scanStringEscape at
570530e69141· 247-273 - escapeValue at
570530e69141· 118-123 - template parts accessor at
570530e69141· 506-519
Preserve a regex backslash and literal braces
Linkimport std.iolet pattern = r"\\d+\{n\}"io.writeLine(pattern.parts[0])
\d+{n}
Expected result
Expected result
Execution completes. Exit code 0.
\d+{n}
Source evidence
- issues/31 at
570530e69141 - scanStringEscape at
570530e69141· 247-273 - escapeValue at
570530e69141· 118-123 - template parts accessor at
570530e69141· 506-519
Unknown escape in a multiline r template
Linklet pattern = r"""\d"""
Expected result
Source evidence
- issues/31 at
570530e69141 - scanStringEscape at
570530e69141· 247-273 - escapeValue at
570530e69141· 118-123 - template parts accessor at
570530e69141· 506-519
Reject uppercase hex prefix
Linklet value = 0Xff
Expected result
Expected result
Rejection during parse. Nonzero exit.
TPZ2001malformed-numeric-literal atexample.tpz:1:13
Source evidence
- digit, integerRadix and validIntegerLiteral at
9b1bbdeceba9· 8-39 - validateNumericLiteral uses the malformed numeric literal diagnostic at
9b1bbdeceba9· 322-330
Reject uppercase binary prefix
Linklet value = 0B1010
Expected result
Expected result
Rejection during parse. Nonzero exit.
TPZ2001malformed-numeric-literal atexample.tpz:1:13
Source evidence
- digit, integerRadix and validIntegerLiteral at
9b1bbdeceba9· 8-39 - validateNumericLiteral uses the malformed numeric literal diagnostic at
9b1bbdeceba9· 322-330
Reject uppercase hex digit
Linklet value = 0xA
Expected result
Expected result
Rejection during parse. Nonzero exit.
TPZ2001malformed-numeric-literal atexample.tpz:1:13
Source evidence
- digit, integerRadix and validIntegerLiteral at
9b1bbdeceba9· 8-39 - validateNumericLiteral uses the malformed numeric literal diagnostic at
9b1bbdeceba9· 322-330
Reject mixed case hex digit
Linklet value = 0xaF
Expected result
Expected result
Rejection during parse. Nonzero exit.
TPZ2001malformed-numeric-literal atexample.tpz:1:13
Source evidence
- digit, integerRadix and validIntegerLiteral at
9b1bbdeceba9· 8-39 - validateNumericLiteral uses the malformed numeric literal diagnostic at
9b1bbdeceba9· 322-330
Read lowercase radix literals
Linkimport std.io { writeLine }writeLine("{0xff}")writeLine("{0b1010}")writeLine("{0xabcdef}")
255 10 11259375
Expected result
Expected result
Execution completes. Exit code 0.
2551011259375
Source evidence
- digit, integerRadix and validIntegerLiteral at
9b1bbdeceba9· 8-39 - validateNumericLiteral uses the malformed numeric literal diagnostic at
9b1bbdeceba9· 322-330 - Bytes.toHex emits lowercase hexadecimal at
9b1bbdeceba9· 122-128
Reject physical LF in single-line text
Linklet value = "leftright"
Expected result
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - interruptedString reports TPZ0003 at the opening delimiter at
9b1bbdeceba9· 303-325
Reject physical LF in single-line interpolation
Linklet value = "{1+ 2}"
Expected result
Expected result
Rejection during lex. Nonzero exit.
TPZ0003single-line-interpolation-line-break atexample.tpz:1:13
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - interruptedString reports TPZ0003 at the opening delimiter at
9b1bbdeceba9· 303-325 - scanCode propagates LF and CRLF from single-line interpolation at
9b1bbdeceba9· 572-628
Reject physical CRLF in single-line text
Linklet value = "leftright"
Expected result
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - interruptedString reports TPZ0003 at the opening delimiter at
9b1bbdeceba9· 303-325
Reject physical CRLF in single-line interpolation
Linklet value = "{1+ 2}"
Expected result
Expected result
Rejection during lex. Nonzero exit.
TPZ0003single-line-interpolation-line-break atexample.tpz:1:13
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - interruptedString reports TPZ0003 at the opening delimiter at
9b1bbdeceba9· 303-325 - scanCode propagates LF and CRLF from single-line interpolation at
9b1bbdeceba9· 572-628
Reject physical CR in single-line text
Linklet value = "left right"
Expected result
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - interruptedString reports TPZ0003 at the opening delimiter at
9b1bbdeceba9· 303-325
Reject physical CR in single-line interpolation
Linklet value = "{1 + 2}"
Expected result
Expected result
Rejection during lex. Nonzero exit.
TPZ0003single-line-interpolation-line-break atexample.tpz:1:13
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - interruptedString reports TPZ0003 at the opening delimiter at
9b1bbdeceba9· 303-325 - scanCode propagates LF and CRLF from single-line interpolation at
9b1bbdeceba9· 572-628
Reject physical CR in a string nested inside single-line interpolation
Linklet value = "{""" inner"""}"
Expected result
Expected result
Rejection during lex. Nonzero exit.
TPZ0003single-line-interpolation-line-break atexample.tpz:1:13
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - interruptedString reports TPZ0003 at the opening delimiter at
9b1bbdeceba9· 303-325 - scanCode propagates LF and CRLF from single-line interpolation at
9b1bbdeceba9· 572-628
Physical CRLF uses the same multiline text and indentation
Linkimport std.iolet value = """firstsecond"""io.writeLine(value.encodeUtf8().toHex())
66697273740a7365636f6e640a
Expected result
Expected result
Execution completes. Exit code 0.
66697273740a7365636f6e640a
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315
Physical CR uses the same multiline text and indentation
Linkimport std.iolet value = """ first second """io.writeLine(value.encodeUtf8().toHex())
66697273740a7365636f6e640a
Expected result
Expected result
Execution completes. Exit code 0.
66697273740a7365636f6e640a
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315
Reject missing indentation after a physical CR
Linklet value = """first second"""
Expected result
Expected result
Rejection during lex. Nonzero exit.
TPZ0006multiline-closing-indent-mismatch atexample.tpz:3:1
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315 - multilineIndentDiagnostics reports TPZ0006 for a missing source prefix at
9b1bbdeceba9· 182-191
Escaped carriage returns remain character data
Linkimport std.iolet opening = """\rhead"""let single = "S\rE"let multi = """M\rN"""io.writeLine(opening.encodeUtf8().toHex())io.writeLine(single.encodeUtf8().toHex())io.writeLine(multi.encodeUtf8().toHex())
0d68656164 530d45 4d0d4e0a
Expected result
Expected result
Execution completes. Exit code 0.
0d68656164530d454d0d4e0a
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315
An inserted carriage return decoded from bytes remains character data
Linkimport std.iolet data = match (Bytes.fromHex("0d")) {case Ok(bytes) => match (bytes.decodeUtf8()) {case Ok(value) => valuecase Err(_) => ""}case Err(_) => ""}let opening = """{data}head"""let single = "L{data}R"let multi = """L{data}R"""io.writeLine(opening.encodeUtf8().toHex())io.writeLine(single.encodeUtf8().toHex())io.writeLine(multi.encodeUtf8().toHex())
0d68656164 4c0d52 4c0d520a
Expected result
Expected result
Execution completes. Exit code 0.
0d686561644c0d524c0d520a
Source evidence
- scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315 - emitString sequential render steps at
151141d7bb44· 337-385 - render_into value conversion cases at
151141d7bb44· 169-409
Blank source lines ignore indentation
Linkimport std.iolet indented = """AB"""let unindented = """AB"""let tagged = r"""AB"""io.writeLine(indented.encodeUtf8().toHex())io.writeLine(unindented.encodeUtf8().toHex())io.writeLine(tagged.parts[0].encodeUtf8().toHex())
410a0a0a420a 410a0a42 410a0a420a
Expected result
Expected result
Execution completes. Exit code 0.
410a0a0a420a410a0a42410a0a420a
Source evidence
- stringLayout at
151141d7bb44· 201-246 - cookStringParts at
151141d7bb44· 250-332 - multilineIndentDiagnostics at
151141d7bb44· 182-191 - scanString at
151141d7bb44· 343-426 - scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315
Escaped and inserted whitespace remains data
Linkimport std.iolet inserted = " \t"let value = """A\t{inserted}B"""io.writeLine(value.encodeUtf8().toHex())
410a090a20090a420a
Expected result
Expected result
Execution completes. Exit code 0.
410a090a20090a420a
Source evidence
- stringLayout at
151141d7bb44· 201-246 - cookStringParts at
151141d7bb44· 250-332 - multilineIndentDiagnostics at
151141d7bb44· 182-191 - scanString at
151141d7bb44· 343-426 - scanString recognizes LF and CRLF while preserving bare CR at
9b1bbdeceba9· 343-423 - stringLayout recognizes LF and CRLF for opening and indentation at
9b1bbdeceba9· 202-242 - cookStringParts converts physical CRLF before decoding escapes at
9b1bbdeceba9· 247-315
Path construction uses Path.from instead of the p tag
Linklet value = p"notes"
Expected result
Expected result
Rejection during parse. Nonzero exit.
TPZ2002unknown-template-tag atexample.tpz:1:13
Source evidence
- parseString still registers p and uses TPZ2002 for unknown tags at
271329b482bc· 332-355 - p constructs TemplateData and replaces backslashes only in display at
271329b482bc· 1728-1788 - pull/41 at
271329b482bc
Path construction uses Path.from instead of the p tag
Linklet value = p"""notes"""
Expected result
Expected result
Rejection during parse. Nonzero exit.
TPZ2002unknown-template-tag atexample.tpz:1:13
Source evidence
- parseString still registers p and uses TPZ2002 for unknown tags at
271329b482bc· 332-355 - p constructs TemplateData and replaces backslashes only in display at
271329b482bc· 1728-1788 - pull/41 at
271329b482bc
Path.from normalizes literal and interpolated text
Linkimport std.ioimport std.path { Path }function normalized(text: string) -> string {match (Path.from(text)) {case Ok(value) => value.toString()case Err(error) => error}}let directory = "notes"io.writeLine(normalized("./{directory}//draft/../chapter.tpz"))io.writeLine(normalized("notes\\draft\\..\\chapter.tpz"))io.writeLine(normalized("notes/.."))io.writeLine(normalized("Notes/가.TPZ"))
notes/chapter.tpz notes/chapter.tpz . Notes/가.TPZ
Expected result
Expected result
Execution completes. Exit code 0.
notes/chapter.tpznotes/chapter.tpz.Notes/가.TPZ
Source evidence
- issues/31 at
271329b482bc - pull/41 at
271329b482bc - normalize_path_text and the three text factories at
271329b482bc· 261-341 - builtin_path_normalize returns the existing normalized Path at
271329b482bc· 424-427 - Path static member signatures at
271329b482bc· 1733-1744 - Path receiver member signatures at
271329b482bc· 1040-1051 - reportMissingMember uses TPZ5006 at
271329b482bc· 1364-1402 - p constructs TemplateData and replaces backslashes only in display at
271329b482bc· 1728-1788
Invalid path text is an Err result
Linkimport std.ioimport std.path { Path }function outcome(text: string) -> string {match (Path.from(text)) {case Ok(_) => "Ok"case Err(_) => "Err"}}io.writeLine("empty: {outcome("")}")io.writeLine("absolute: {outcome("/notes")}")io.writeLine("drive: {outcome("C:notes")}")io.writeLine("escape: {outcome("../notes")}")
empty: Err absolute: Err drive: Err escape: Err
Expected result
Expected result
Execution completes. Exit code 0.
empty: Errabsolute: Errdrive: Errescape: Err
Source evidence
- issues/31 at
271329b482bc - pull/41 at
271329b482bc - normalize_path_text and the three text factories at
271329b482bc· 261-341 - builtin_path_normalize returns the existing normalized Path at
271329b482bc· 424-427 - Path static member signatures at
271329b482bc· 1733-1744 - Path receiver member signatures at
271329b482bc· 1040-1051 - reportMissingMember uses TPZ5006 at
271329b482bc· 1364-1402 - p constructs TemplateData and replaces backslashes only in display at
271329b482bc· 1728-1788
Use Path.from instead of Path.cwdRelative
Linkimport std.path { Path }let value = Path.cwdRelative("notes")
Expected result
Source evidence
- issues/31 at
271329b482bc - pull/41 at
271329b482bc - normalize_path_text and the three text factories at
271329b482bc· 261-341 - builtin_path_normalize returns the existing normalized Path at
271329b482bc· 424-427 - Path static member signatures at
271329b482bc· 1733-1744 - Path receiver member signatures at
271329b482bc· 1040-1051 - reportMissingMember uses TPZ5006 at
271329b482bc· 1364-1402 - p constructs TemplateData and replaces backslashes only in display at
271329b482bc· 1728-1788
Use Path.from instead of Path.project
Linkimport std.path { Path }let value = Path.project("notes")
Expected result
Source evidence
- issues/31 at
271329b482bc - pull/41 at
271329b482bc - normalize_path_text and the three text factories at
271329b482bc· 261-341 - builtin_path_normalize returns the existing normalized Path at
271329b482bc· 424-427 - Path static member signatures at
271329b482bc· 1733-1744 - Path receiver member signatures at
271329b482bc· 1040-1051 - reportMissingMember uses TPZ5006 at
271329b482bc· 1364-1402 - p constructs TemplateData and replaces backslashes only in display at
271329b482bc· 1728-1788
A Path is already normalized
Linkimport std.path { Path }function keep(value: Path) -> Path { value.normalize() }
Expected result
Source evidence
- issues/31 at
271329b482bc - pull/41 at
271329b482bc - normalize_path_text and the three text factories at
271329b482bc· 261-341 - builtin_path_normalize returns the existing normalized Path at
271329b482bc· 424-427 - Path static member signatures at
271329b482bc· 1733-1744 - Path receiver member signatures at
271329b482bc· 1040-1051 - reportMissingMember uses TPZ5006 at
271329b482bc· 1364-1402 - p constructs TemplateData and replaces backslashes only in display at
271329b482bc· 1728-1788
Use an existing Path directly
Linkimport std.path { Path }function keep(value: Path) -> Path { value }
No stdout.
Expected result
Expected result
Checking accepts the program. Exit code 0.
Source evidence
- issues/31 at
271329b482bc - pull/41 at
271329b482bc - normalize_path_text and the three text factories at
271329b482bc· 261-341 - builtin_path_normalize returns the existing normalized Path at
271329b482bc· 424-427 - Path static member signatures at
271329b482bc· 1733-1744 - Path receiver member signatures at
271329b482bc· 1040-1051 - reportMissingMember uses TPZ5006 at
271329b482bc· 1364-1402 - p constructs TemplateData and replaces backslashes only in display at
271329b482bc· 1728-1788
Preserve composed, decomposed, and invisible string data
Linkimport std.iolet composed = "é"let decomposed = "é"let hangul = "가"let invisible = "a͏️"let inserted = "{decomposed}"io.writeLine(composed.encodeUtf8().toHex())io.writeLine(decomposed.encodeUtf8().toHex())io.writeLine(hangul.encodeUtf8().toHex())io.writeLine(invisible.encodeUtf8().toHex())io.writeLine(inserted.encodeUtf8().toHex())
c3a9 65cc81 e18480e185a1 61cd8fe2808defb88f 65cc81
Expected result
Expected result
Execution completes. Exit code 0.
c3a965cc81e18480e185a161cd8fe2808defb88f65cc81
Source evidence
- LL-3 recommendation: NFC source names, combining continuations, and preserved string data at
72a59a31125f - Canonical normalization forms at
Unicode-16.0· 3.11.7 - escapeValue at
af88afbc1d99· 118-123 - scanStringEscape, scanStringInterpolation at
af88afbc1d99· 247-318
Add and subtract Duration values
Linkimport std.io { writeLine }let sum = 1s + 500mslet difference = 2s - 500mswriteLine("{sum}")writeLine("{difference}")
1500ms 1500ms
Expected result
Expected result
Execution completes. Exit code 0.
1500ms1500ms
Multiply a Duration by an int in either order
Linkimport std.io { writeLine }let count = 3let left = 500ms * countlet right = count * 500mslet constructed = 1ms * countwriteLine("{left}")writeLine("{right}")writeLine("{constructed}")
1500ms 1500ms 3ms
Expected result
Expected result
Execution completes. Exit code 0.
1500ms1500ms3ms
Compare Duration values by milliseconds
Linkimport std.io { writeLine }let first = 1slet same = 1000mslet later = 2swriteLine("{Eq.equals(first, same)}")writeLine("{Order.compare(first, later) < 0}")writeLine("{first == same}")writeLine("{first != later}")writeLine("{first < later}")writeLine("{first <= same}")writeLine("{later > first}")writeLine("{same >= first}")
true true true true true true true true
Expected result
Expected result
Execution completes. Exit code 0.
truetruetruetruetruetruetruetrue
Display millisecond literals and extract int milliseconds
Linkimport std.io { writeLine }let delay = 1500mslet minute = 1mlet second = 1slet zero = 0mslet endpoint = 18446744073709551615mslet intEndpoint = 9223372036854775807mswriteLine(Show.show(delay))writeLine("{delay.toMillis()}")writeLine("{minute.toMillis()}")writeLine("{second.toMillis()}")writeLine("{zero}")writeLine("{endpoint}")writeLine("{intEndpoint.toMillis()}")
1500ms 1500 60000 1000 0ms 18446744073709551615ms 9223372036854775807
Expected result
Expected result
Execution completes. Exit code 0.
1500ms15006000010000ms18446744073709551615ms9223372036854775807
Reject a Duration subtraction below zero
Linklet earlier = 1mslet later = 2mslet difference = earlier - later
No stdout.
Expected result
Expected result
Rejection during run. Nonzero exit.
TPZ4004duration-negative-difference atexample.tpz:3:18
Reject a Duration sum outside its millisecond range
Linklet largest = 18446744073709551615mslet result = largest + 1ms
No stdout.
Expected result
Reject a Duration product outside its millisecond range
Linklet large = 9223372036854775807mslet result = large * 3
No stdout.
Expected result
Reject multiplication by a negative int
Linklet delay = 1mslet count = -1let result = delay * count
No stdout.
Expected result
Expected result
Rejection during run. Nonzero exit.
TPZ4004duration-negative-multiplier atexample.tpz:3:14
Reject a millisecond count outside int
Linklet delay = 9223372036854775808mslet count = delay.toMillis()
No stdout.
Expected result
Expected result
Rejection during run. Nonzero exit.
TPZ4004duration-to-millis-overflow atexample.tpz:2:13
Pending contracts
LinkRelated reference
LinkLexical structure owns token boundaries and reserved spellings. General type compatibility belongs to Types; arithmetic and grouping belong to Expressions and operators. The enclosing timeout form belongs to Concurrency. Diagnostic entries own complete causes and remedies for each qualified diagnostic.