Skip to content
Topaz6.0 · Before release
Download

Search documentation

Specification 6.0 · Not released · Tested product 5.21.3

Use arrow keys to choose a result. Press Enter to open.

Type an API name, diagnostic number, rule, or project topic.

    Language · chapter 01 · 17 rules

    Lexical structure

    Source spellings, reserved and contextual words, comments, line breaks and diagnostic positions.

    Scope and ownership

    Link

    Lexical structure identifies source spellings and statement boundaries. Literal values, escapes and template forms belong to Literals and templates. Name lookup and binding restrictions belong to Bindings and scope. Operator grouping belongs to Expressions and operators. Contextual forms retain the owners listed below.

    Edition: Topaz 5.21 language mode.

    Rules cite owner decisions and reviewed SELF source. Each executable example keeps its independent expectation and verification result. The line-break decision was applied against 570530e69141ba3a2014870c84c6759916a10833; the documented implementation limitations identify the remaining scanner differences.

    Identifiers, underscore and labels

    Link
    EBNF
    1. IdentifierSpelling = IdentifierStart IdentifierContinue* ;
    2. IdentifierStart = ( "_" | UnicodeLetter | NonAsciiEmoji ) - DefaultIgnorable ;
    3. IdentifierContinue = ( IdentifierStart | UnicodeNumber | UnicodeMark ) - DefaultIgnorable ;
    4. Identifier = IdentifierSpelling - ReservedWord - "_" ;
    5. Underscore = "_" ;
    6. Label = "'" IdentifierSpelling ;

    In this EBNF excerpt, * denotes repetition and - excludes a scalar or spelling. UnicodeMark comprises Mn, Mc, and Me. DefaultIgnorable is Unicode 16.0.0 Default_Ignorable_Code_Point. The complete IdentifierSpelling must already be NFC. The NFC condition applies to the whole sequence, not to each scalar separately. ReservedWord is defined in the next section.

    Source evidence

    Identifier characters

    An identifier starts with _, a Unicode letter, or a non-ASCII scalar with the Unicode Emoji property. Later scalars may also be Unicode numbers and combining marks.

    The character classes use Unicode 16.0.0. Letters use Lu, Ll, Lt, Lm, Lo, numbers use Nd, Nl, No, and combining marks use Mn, Mc, Me. A combining mark cannot start a name.

    Exclude every scalar with Default_Ignorable_Code_Point from both positions, even when it is a letter or combining mark. This includes variation selectors, join controls, combining grapheme joiner, Hangul fillers, and emoji tag characters. No script or emoji sequence receives an exception. Each scalar in a multi-scalar emoji name must qualify independently. String data follows the string rules.

    Why

    Programs can name values in the languages of the documents they process. Combining marks continue a name so those spellings do not depend on precomposed letters. A scalar that is normally invisible must not distinguish two names. Excluding Default_Ignorable_Code_Point from both positions gives names one rule without script, font, or emoji-presentation exceptions.

    Source evidence

    Complete identifier spellings

    When no string delimiter immediately follows, the lexer classifies the complete identifier spelling. A spelling in ReservedWord becomes a keyword token. The complete spelling _ becomes an underscore token. Other spellings become identifier tokens.

    Why

    Classifying a complete word prevents a keyword prefix from splitting a longer name. A distinct wildcard token keeps discarded values separate from named bindings.

    Source evidence
    Ruletopaz.lexical-structure.rule.labels

    Label spellings

    ' followed by IdentifierSpelling forms a label token. Its body is not reclassified as a keyword.

    Why

    The label prefix distinguishes a control target from a value name. Its separate token class makes the target visible before name lookup.

    Source evidence
    • scanLabel at af88afbc1d99 · 519-544

    See Pattern matching and Expressions and operators for uses of _. Blocks and control flow describes label binding and jump targets.

    Canonical source spelling

    The complete name must already be in Unicode 16.0.0 NFC form in the source. This applies to declarations, references, members, fields, label bodies, and template tag names. Check the whole sequence before keyword or tag classification.

    Bindings and lookup compare the admitted scalar sequence exactly. They do not normalize a spelling silently or create a second alias. NFC uses canonical equivalence. It does not apply compatibility normalization or case folding.

    A non-NFC name produces TPZ0007 during lex. Mark the complete name span, excluding a label's apostrophe and a tag's string delimiter, and suggest its NFC spelling. Review the replacement for collisions before changing the source.

    Why

    Canonically equivalent name spellings denote the same written name to a reader. Requiring one NFC source spelling prevents hidden aliases during binding and lookup. An explicit rejection and suggested replacement let the programmer review any resulting collision before changing the source. String contents remain data because normalizing them would change bytes the program may need to preserve.

    Source evidence

    Reserved words

    Link
    EBNF
    1. ReservedWord =
    2. "let" | "mut" | "const" | "type" | "function" | "return" | "if"
    3. | "else" | "match" | "case" | "for" | "in" | "by" | "while"
    4. | "break" | "continue" | "defer" | "concurrent" | "true" | "false" | "null" ;

    The literal values of true, false and null are specified in Literals and templates.

    Source evidence
    Ruletopaz.lexical-structure.rule.identifier-names

    Identifier name positions

    A name position that requires an identifier rejects keyword tokens.

    Why

    Keeping keywords out of binding positions prevents a declaration from replacing the meaning of a language form.

    Source evidence
    Ruletopaz.lexical-structure.rule.field-names

    Field name positions

    A field-name position accepts an identifier token or a keyword token.

    Why

    A field position already identifies a data member. Accepting keyword spellings there preserves names from structured data without an escape syntax.

    Source evidence

    Bindings and scope owns binding restrictions, including the treatment of None. Types and Functions and generics own compiler-defined type names and type-parameter restrictions.

    Contextual spellings and their owners

    Link

    Contextual identifier spellings

    For identifier tokens with the spellings below, the parser assigns a contextual role when the surrounding form matches.

    SpellingRoleCanonical owner
    enum, record, newtypeNominal declaration headsRecords, enums and newtypes
    derivesDerivation clause after a nominal declaration headRecords, enums and newtypes; Protocols and implementations
    protocol, impl, usingProtocol, implementation and using formsProtocols and implementations
    import, exportProgram-level module formsModules and visibility
    asImport alias clauseModules and visibility
    useRecognized and rejected module-item spellingModules and visibility
    set, mapCollection forms before a braceCollections and comprehensions
    loopLoop head, optionally followed by a labelBlocks and control flow
    selfFirst-parameter recognitionFunctions and generics; receiver contexts in Protocols and implementations
    timeoutNamed timeout option in a concurrent formConcurrency

    Why

    Recognizing these words at their construct heads lets them remain ordinary names elsewhere. The surrounding grammar supplies the role.

    Source evidence

    The named chapters own these forms' grammar and meaning. Literals and templates owns tags and interpolation.

    Punctuation tokens

    Link
    EBNF
    1. Punctuation =
    2. "..." | "..<" | "??=" | "**" | ".." | "?." | "??" | "<="
    3. | ">=" | ">>" | "==" | "!=" | "+=" | "-=" | "*=" | "/="
    4. | "%=" | "&&" | "||" | "=>" | "->" | "(" | ")"
    5. | "[" | "]" | "{" | "}" | "+" | "-" | "*" | "/"
    6. | "%" | "." | "?" | "<" | ">" | "=" | "!" | "~"
    7. | "|" | "," | ":" | ";" ;

    See Expressions and operators, Types, and Pattern matching for the syntactic roles of these tokens. The adjacent >> token supplies two type-closing delimiters and has no expression-operator meaning. The retired |> spelling is not a punctuation token or an expression form.

    Source evidence
    • punctuation at af88afbc1d99 · 111-161
    Ruletopaz.lexical-structure.rule.punctuation

    Punctuation token boundaries

    The punctuation scanner chooses the longest matching punctuation spelling. Comments, string delimiters, and label prefixes have separate lexical forms. The adjacent >> token supplies two type-closing delimiters. It has no expression-operator meaning.

    Why

    Longest matching keeps a compound operator together. Its meaning does not depend on trying shorter operator spellings first.

    Source evidence
    • punctuation at af88afbc1d99 · 111-161

    Whitespace and comments

    Link
    EBNF
    1. HorizontalSpace = " " | HT ;
    2. Newline = LF | CR LF | CR ;
    3. LineComment = "//" NonNewlineScalar* ;

    NonNewlineScalar denotes a source scalar other than LF or CR. A comment starts with // and ends before LF, CRLF, CR, or the end of the source. Each physical break remains available to the normal layout rules.

    Source evidence
    Ruletopaz.lexical-structure.rule.whitespace

    Horizontal space and newlines

    Space (U+0020) and horizontal tab (U+0009) separate tokens. LF (U+000A), CRLF, and bare CR (U+000D) each supply one line break to layout processing. Each spelling preserves the same statement boundary. Indentation does not delimit blocks. Braces and the surrounding syntax determine block and list boundaries.

    Why

    Equivalent line endings preserve a program when a file moves between editors or operating systems. Braces keep scope independent of indentation.

    Source evidence
    Ruletopaz.lexical-structure.rule.line-comments

    Line comments

    A line comment begins with // and ends before the next LF, CRLF, or bare CR, or at the end of the source. Its terminating line break remains available to layout processing.

    Why

    One comment opener covers both explanatory lines and trailing comments. Preserving its terminating line break keeps comments from joining surrounding statements.

    Source evidence
    Ruletopaz.lexical-structure.rule.block-comments

    Block comment spellings are rejected

    Write every comment line with //. The /* ... */ spelling is rejected with TPZ2001 during parsing. Its opener is tokenized as ordinary / and * punctuation. Comment markers inside a string remain text.

    Why

    Line comments give ignored source text one spelling. Prefixing each line handles a paragraph of commentary without a closing delimiter or a nesting exception. Line comments also preserve the physical break that separates surrounding statements. A block comment and successive line comments express the same intent to set explanatory text aside. A short mid-line comment can move to a preceding line without changing that intent.

    Source evidence

    Line breaks and statement boundaries

    Link
    EBNF
    1. StatementSeparator = StatementNewline ;

    StatementNewline denotes a significant LF, CRLF, or CR line ending. A semicolon is rejected. Blocks and control flow defines final values and explicit discard.

    Source evidence

    Statement boundaries

    LF, CRLF, and bare CR separate completed items at program level and in statement blocks when no continuation condition applies, including the break that ends a line comment. A semicolon is rejected and never discards a value. Blank lines do not introduce an extra item. No implicit separator is inserted immediately before a closing delimiter or the end of the source. The final expression remains the block value across trailing line breaks and comments. To discard that value explicitly, write let _ = expression.

    Why

    A semicolon separator and a line boundary both separate completed statements. Newlines express that separation. Discarding a result is a different intent and is written explicitly with let _ = expression. Trailing whitespace and comments therefore leave the final expression value unchanged.

    Source evidence

    Line breaks inside delimiter forms

    A line break continues the surrounding form while its active context is parentheses, square brackets, interpolation, record fields, or import selections. An explicit ; in those contexts produces TPZ1001. Use , between list elements.

    Why

    The enclosing delimiter identifies an unfinished expression or list. Commas keep element boundaries explicit when a long form spans several lines.

    Source evidence
    Ruletopaz.lexical-structure.rule.token-continuation

    Continuation around tokens

    A newline continues the form at these token positions.

    Position of the line breakToken spellings
    Before>, >>, else
    After+, -, !, ~, ,, =>, (, [
    Either side**, *, /, %, .., ..<, <, <=, >=, ==, !=, in, &&, \|\|, ??, =, +=, -=, *=, /=, %=, ??=, ., ?., by

    Why

    Continuation keeps an unfinished operator expression together. The lexer records line breaks and the layout pass uses adjacent tokens to decide its boundaries.

    Source evidence

    Continuation around pattern alternatives

    A line break on either side of | continues a binding, case, or for pattern while that pattern is being read.

    Why

    The alternatives belong to one pattern. Keeping them together lets the same binding requirements apply across a multiline alternative list.

    Source evidence

    Collections and comprehensions, Modules and visibility, and Functions and generics own list grammars. Expressions and operators owns precedence. Pattern matching owns pattern grammar.

    Explicit call type arguments have a recorded implementation limitation involving the gap between > and (. Its intended contract and workaround remain in Functions and generics. That spacing-sensitive behavior does not add a lexical prohibition.

    Source locations

    Link
    Ruletopaz.lexical-structure.rule.unknown-character

    Unknown source characters

    An unrecognized source character produces TPZ0001 at that character's location.

    Why

    Rejecting an unknown character at its location prevents silent removal from changing a program.

    Source evidence
    • scanUnknown at af88afbc1d99 · 559-577
    Ruletopaz.lexical-structure.rule.source-positions

    Diagnostic source positions

    CLI diagnostic labels use 1-based line, col, endLine, and endCol. Columns count Unicode scalar values from the start of the line. The lo and hi fields use 0-based UTF-8 byte offsets. Both end positions are exclusive.

    Why

    Scalar columns locate characters without depending on UTF-8 byte width. Byte offsets address exact source slices, and exclusive ends compose adjacent spans.

    Source evidence

    Examples and non-examples

    Link

    Identifier spellings and contextual names

    Link
    example.tpzIdentifier spellings and contextual namesOpen in Playground
    1. let answer_2: int = 42
    2. let 값: int = answer_2
    3. let 🟦: int = 값
    4. let ifx: int = 🟦
    5. let record: int = ifx
    6. let loop: int = record
    Recorded outputexit 0
    No stdout.
    $ topaz check example.tpz --compiler self --format json · recorded with topaz 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Observed assertions · passed · 5.21.3

    Exit code 0.

    No stdout.

    No stderr.

    • Passed · expected 0; observed 0
    • Passed · expected 0; observed 0
    • Passed · expected 0; observed 0
    • Passed · expected 0; observed 0
    Source evidence

    Comments and continued expressions

    Link
    example.tpzComments and continued expressionsOpen in Playground
    1. // A comment occupies its prefixed line.
    2. let first: int = 1 // A line comment.
    3. let second: int =
    4. first +
    5. 2
    6. let third: int = (second
    7. * 2)
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    The retired block opener is rejected without a closer

    Link
    example.tpzThe retired block opener is rejected without a closerOpen in Playground
    1. let value: int = 1
    2. /* unfinished
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 diagnostic-tpz2001 at example.tpz:2:1
    Source evidence
    • scanComment at af88afbc1d99 · 446-465
    • parseAtom at 271329b482bc · 1291-1299

    A line comment needs no closing delimiter

    Link
    example.tpzA line comment needs no closing delimiterOpen in Playground
    1. let value: int = 1
    2. // A line comment also ends at the end of the source.
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence
    • scanComment at af88afbc1d99 · 446-465

    A semicolon in an array list

    Link
    example.tpzA semicolon in an array listOpen in Playground
    1. let values: Array<int> = [1; 2]
    Recorded outputexit 1
    No stdout.
    error[TPZ1001]: `;` does not separate items inside a delimiter list; use `,`
     --> example.tpz:1:28
      |
    1 | let values: Array<int> = [1; 2]
      |                            ^
    
    error[TPZ2001]: expected `]`
     --> example.tpz:1:30
      |
    1 | let values: Array<int> = [1; 2]
      |                              ^
    
    error[TPZ2001]: expected an expression
     --> example.tpz:1:31
      |
    1 | let values: Array<int> = [1; 2]
      |                               ^
    
    error[TPZ2001]: expected a statement
     --> example.tpz:1:31
      |
    1 | let values: Array<int> = [1; 2]
      |                               ^
    
    example.tpz: 4 diagnostics
    
    $ topaz check example.tpz --compiler self · recorded with topaz 5.21.3
    Expected result

    Expected result

    Rejection during layout. Nonzero exit.

    • TPZ1001 semicolon-in-delimiter-list at example.tpz:1:28
    Observed assertions · passed · 5.21.3

    Exit code 1.

    No stdout.

    Observed stderr
    1. error[TPZ1001]: `;` does not separate items inside a delimiter list; use `,`
    2. --> example.tpz:1:28
    3. |
    4. 1 | let values: Array<int> = [1; 2]
    5. | ^
    6. error[TPZ2001]: expected `]`
    7. --> example.tpz:1:30
    8. |
    9. 1 | let values: Array<int> = [1; 2]
    10. | ^
    11. error[TPZ2001]: expected an expression
    12. --> example.tpz:1:31
    13. |
    14. 1 | let values: Array<int> = [1; 2]
    15. | ^
    16. error[TPZ2001]: expected a statement
    17. --> example.tpz:1:31
    18. |
    19. 1 | let values: Array<int> = [1; 2]
    20. | ^
    21. example.tpz: 4 diagnostics
    • Passed · checking TPZ1001; cause semicolon-in-delimiter-list; primary location asserted; additional diagnostics allow
    • Passed · expected nonzero; observed 1
    Source evidence

    Separate array elements with a comma

    Link
    example.tpzSeparate array elements with a commaOpen in Playground
    1. let values: Array<int> = [1, 2]
    Recorded outputexit 0
    No stdout.
    $ topaz check example.tpz --compiler self --format json · recorded with topaz 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Observed assertions · passed · 5.21.3

    Exit code 0.

    No stdout.

    No stderr.

    • Passed · expected 0; observed 0
    Source evidence

    A keyword used as a function name

    Link
    example.tpzA keyword used as a function nameOpen in Playground
    1. function if() { }
    Recorded outputexit 1
    No stdout.
    error[TPZ2001]: expected a function name
     --> example.tpz:1:10
      |
    1 | function if() { }
      |          ^^
    
    example.tpz: 1 diagnostic
    
    $ topaz check example.tpz --compiler self · recorded with topaz 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 expected-function-name at example.tpz:1:10
    Observed assertions · passed · 5.21.3

    Exit code 1.

    No stdout.

    Observed stderr
    1. error[TPZ2001]: expected a function name
    2. --> example.tpz:1:10
    3. |
    4. 1 | function if() { }
    5. | ^^
    6. example.tpz: 1 diagnostic
    • Passed · checking TPZ2001; cause expected-function-name; primary location asserted; additional diagnostics allow
    • Passed · checking TPZ2001; cause expected-function-name; primary location asserted; additional diagnostics allow
    • Passed · expected nonzero; observed 1
    Source evidence

    Give the function an identifier

    Link
    example.tpzGive the function an identifierOpen in Playground
    1. function choose() { }
    Recorded outputexit 0
    No stdout.
    $ topaz check example.tpz --compiler self --format json · recorded with topaz 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Observed assertions · passed · 5.21.3

    Exit code 0.

    No stdout.

    No stderr.

    • Passed · expected 0; observed 0
    • Passed · expected 0; observed 0
    Source evidence

    An unknown character after a Unicode name

    Link
    example.tpzAn unknown character after a Unicode nameOpen in Playground
    1. let 이름 = @
    Recorded outputexit 1
    No stdout.
    error[TPZ0001]: unknown character `@`
     --> example.tpz:1:10
      |
    1 | let 이름 = @
      |          ^
    
    error[TPZ2001]: expected an expression
     --> example.tpz:2:1
      |
    2 | 
      | ^
    
    example.tpz: 2 diagnostics
    
    $ topaz check example.tpz --compiler self · recorded with topaz 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0001 unknown-source-character at example.tpz:1:10
    Observed assertions · passed · 5.21.3

    Exit code 1.

    No stdout.

    Observed stderr
    1. error[TPZ0001]: unknown character `@`
    2. --> example.tpz:1:10
    3. |
    4. 1 | let 이름 = @
    5. | ^
    6. error[TPZ2001]: expected an expression
    7. --> example.tpz:2:1
    8. |
    9. 2 |
    10. | ^
    11. example.tpz: 2 diagnostics
    • Passed · checking TPZ0001; cause unknown-source-character; primary location asserted; additional diagnostics allow
    • Passed · checking TPZ0001; cause unknown-source-character; primary location asserted; additional diagnostics allow
    • Passed · expected nonzero; observed 1

    This example exercises the primary line and scalar column. Byte offsets and end positions remain pending product verification.

    Source evidence

    LF separates completed statements

    Link
    example.tpzLF separates completed statementsOpen in Playground
    1. import std.io
    2. let value = 1
    3. -2
    4. io.writeLine("{value}")
    Expected outputexit 0
    1
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 1
    Source evidence

    A block comment with LF is rejected

    Link
    example.tpzA block comment with LF is rejectedOpen in Playground
    1. import std.io
    2. let value = 1 /* before
    3. after */ +2
    4. io.writeLine("{value}")
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 diagnostic-tpz2001 at example.tpz:2:16
    Source evidence

    LF ends a line comment

    Link
    example.tpzLF ends a line commentOpen in Playground
    1. import std.io
    2. // comment
    3. io.writeLine("after")
    4. io.writeLine("tail")
    Expected outputexit 0
    after
    tail
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. after
    2. tail
    Source evidence

    CRLF separates completed statements

    Link
    example.tpzCRLF separates completed statementsOpen in Playground
    1. import std.io
    2. let value = 1
    3. -2
    4. io.writeLine("{value}")
    Expected outputexit 0
    1
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 1
    Source evidence

    A block comment with CRLF is rejected

    Link
    example.tpzA block comment with CRLF is rejectedOpen in Playground
    1. import std.io
    2. let value = 1 /* before
    3. after */ +2
    4. io.writeLine("{value}")
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 diagnostic-tpz2001 at example.tpz:2:16
    Source evidence

    CRLF ends a line comment

    Link
    example.tpzCRLF ends a line commentOpen in Playground
    1. import std.io
    2. // comment
    3. io.writeLine("after")
    4. io.writeLine("tail")
    Expected outputexit 0
    after
    tail
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. after
    2. tail
    Source evidence

    Bare CR separates completed statements

    Link
    example.tpzBare CR separates completed statementsOpen in Playground
    1. import std.io
    2. let value = 1 -2
    3. io.writeLine("{value}")
    Expected outputexit 0
    1
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. 1
    Source evidence

    A block comment with CR is rejected

    Link
    example.tpzA block comment with CR is rejectedOpen in Playground
    1. import std.io
    2. let value = 1 /* before after */ +2
    3. io.writeLine("{value}")
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 diagnostic-tpz2001 at example.tpz:2:16
    Source evidence

    Bare CR ends a line comment

    Link
    example.tpzBare CR ends a line commentOpen in Playground
    1. import std.io
    2. // comment io.writeLine("after")
    3. io.writeLine("tail")
    Expected outputexit 0
    after
    tail
    
    $ topaz run example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Execution completes. Exit code 0.

    Expected stdout
    1. after
    2. tail
    Source evidence

    A block comment on one line is rejected

    Link
    example.tpzA block comment on one line is rejectedOpen in Playground
    1. import std.io
    2. let value = 1 /* no line break */ +2
    3. io.writeLine("{value}")
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 diagnostic-tpz2001 at example.tpz:2:16
    Source evidence

    Parentheses do not admit block comments

    Link
    example.tpzParentheses do not admit block commentsOpen in Playground
    1. import std.io
    2. let value = (1 /* before after */ +2)
    3. io.writeLine("{value}")
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 diagnostic-tpz2001 at example.tpz:2:17
    Source evidence

    Adjacent declarations need a statement boundary

    Link
    example.tpzAdjacent declarations need a statement boundaryOpen in Playground
    1. let first = 1 let second = 2
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 expected-statement-separator at example.tpz:1:15
    Source evidence
    • issues/31 at 570530e69141
    • scanComment at 570530e69141 · 446-465
    • itemBoundary at 570530e69141 · 443-456

    A newline separates commented items

    Link
    example.tpzA newline separates commented itemsOpen in Playground
    1. let first = 1
    2. let second = 2 // The newline separates the items.
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence
    • issues/31 at 570530e69141
    • scanComment at 570530e69141 · 446-465
    • itemBoundary at 570530e69141 · 443-456

    A semicolon cannot separate program items

    Link
    example.tpzA semicolon cannot separate program itemsOpen in Playground
    1. let first = 1; let second = 2
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 expected-statement-separator at example.tpz:1:14

    A semicolon cannot discard a block tail

    Link
    example.tpzA semicolon cannot discard a block tailOpen in Playground
    1. function value() { 7; }
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self · target product 5.21.3
    Expected result

    Expected result

    Rejection during parse. Nonzero exit.

    • TPZ2001 expected-statement-separator at example.tpz:1:21

    Combining marks continue NFC names

    Link
    example.tpzCombining marks continue NFC namesOpen in Playground
    1. let q́: int = 1
    2. let का: int = q́
    3. let x⃝: int = का
    4. loop 'q́ { break 'q́ }
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    q + U+0301 is Mn, U+093E in का is Mc, and U+20DD in x⃝ is Me. Each complete name is already NFC.

    Source evidence

    Use the NFC spelling throughout the source

    Link
    example.tpzUse the NFC spelling throughout the sourceOpen in Playground
    1. let café: int = 1
    2. let 가: int = 2
    3. let K: int = 3
    4. let value: int = café + 가 + K
    Expected outputexit 0
    No stdout.
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Checking accepts the program. Exit code 0.

    Source evidence

    A combining mark cannot start a name

    Link
    example.tpzA combining mark cannot start a nameOpen in Playground
    1. let ́name = 1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0001 unknown-source-character at example.tpz:1:5

    The leading scalar is U+0301 COMBINING ACUTE ACCENT.

    Source evidence

    Reject an invisible Hangul filler at the start

    Link
    example.tpzReject an invisible Hangul filler at the startOpen in Playground
    1. let ᅟname = 1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0001 unknown-source-character at example.tpz:1:5

    U+115F HANGUL CHOSEONG FILLER is a Letter and Default_Ignorable_Code_Point.

    Source evidence

    Reject an invisible Hangul filler in a name

    Link
    example.tpzReject an invisible Hangul filler in a nameOpen in Playground
    1. let aㅤ = 1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0001 unknown-source-character at example.tpz:1:6

    U+3164 HANGUL FILLER is excluded even though its General_Category is Lo.

    Source evidence

    Reject a variation selector in a name

    Link
    example.tpzReject a variation selector in a nameOpen in Playground
    1. let ✈️ = 1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0001 unknown-source-character at example.tpz:1:6

    The rejected scalar is U+FE0F VARIATION SELECTOR-16. Use the admitted name ✈ without the selector.

    Source evidence

    Reject a joiner in an emoji name

    Link
    example.tpzReject a joiner in an emoji nameOpen in Playground
    1. let 👩‍💻 = 1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0001 unknown-source-character at example.tpz:1:6

    The rejected scalar is U+200D ZERO WIDTH JOINER. A displayed emoji cluster does not override scalar admission.

    Source evidence

    Reject a combining grapheme joiner

    Link
    example.tpzReject a combining grapheme joinerOpen in Playground
    1. let a͏ = 1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0001 unknown-source-character at example.tpz:1:6

    U+034F COMBINING GRAPHEME JOINER is Mn and Default_Ignorable_Code_Point. The exclusion takes precedence over combining-mark continuation.

    Source evidence

    Reject decomposed source names

    Link
    example.tpzReject decomposed source namesOpen in Playground
    1. let café = 1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0007 identifier-not-nfc at example.tpz:1:5

    NFC spells this complete name café.

    Source evidence

    Reject composable Hangul jamo in a name

    Link
    example.tpzReject composable Hangul jamo in a nameOpen in Playground
    1. let 가 = 1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0007 identifier-not-nfc at example.tpz:1:5

    The name contains U+1100 and U+1161. NFC replaces the sequence with U+AC00 가.

    Source evidence

    Reject a scalar with a different NFC spelling

    Link
    example.tpzReject a scalar with a different NFC spellingOpen in Playground
    1. let K = 1
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0007 identifier-not-nfc at example.tpz:1:5

    U+212A KELVIN SIGN has the canonical NFC spelling U+004B K. This is a canonical normalization rule, not NFKC or case folding.

    Source evidence

    Apply NFC to label bodies

    Link
    example.tpzApply NFC to label bodiesOpen in Playground
    1. loop '가 { break }
    Expected outputNonzero exit
    $ topaz check example.tpz --compiler self --format json · target product 5.21.3
    Expected result

    Expected result

    Rejection during lex. Nonzero exit.

    • TPZ0007 identifier-not-nfc at example.tpz:1:7

    The primary name span excludes the opening apostrophe. Write the label body 가.

    Source evidence

    Related reference

    Link

    Literals and templates

    Functions and generics

    Unknown character

    Semicolon in delimiter list

    Unexpected token

    Source name is not NFC

    Menu

    Download