Sign in

    moonrockz/krueger/dialect does not have a README file

    AttributeSyntax

    pub(all) enum AttributeSyntax {
    Off
    DocComment
    } derive(Eq,
    Debug
    )

    Whether attributes in doc comments are parsed.

    Dialect

    pub(all) struct Dialect {
    name : String
    rules :
    HashSet
    [Rule]
    operators : Array[OperatorDef]
    reserved_words : Array[String]
    operator_symbols : Array[String]
    attributes : AttributeSyntax
    core_package : Bool
    }

    A variant of Elm: which rejection rules run, and the extension data (operator table, extra reserved words, extra operator symbols) the scanner and the parser read. The AST shape is elm-syntax 7.3.9 in every dialect.

    • An extra operator symbol must start with a character the lexer already treats as a symbol (+ - / * = . < > : & | ^ ?) and must not start with -- (a line comment).
    • An extra reserved word must not be alias, infix or effect: the parser reads those by name.
    • { ..d, field: value } shares rules and operators with d; copy them before changing either.

    Build the scanner and the parser with the same dialect. To make a custom dialect, start from a built-in one. This one adds an operator <=>. The operator needs an entry in operators (for the parser) and in operator_symbols (for the lexer, which otherwise reads <= and >):

    test {
    let base = @dialect.Dialect::elm_0_19_1()
    let dialect = {
    ..base,
    name: "my-elm",
    operators: [
    ..base.operators,
    { symbol: "<=>", precedence: 4, direction: Non, },
    ],
    operator_symbols: ["<=>"],
    }
    inspect(dialect.validate().length(), content="0")
    let source = @scanner.SourceText::new(
    "module Main exposing (..)\n\nx =\n a <=> b\n",
    )
    let plain = @parser.parse_module(
    source,
    @scanner.DefaultScanner::new(dialect=base),
    dialect=base,
    )
    inspect(plain.diagnostics[0].title, content="UNKNOWN OPERATOR")
    let custom = @parser.parse_module(
    source,
    @scanner.DefaultScanner::new(dialect~),
    dialect~,
    )
    inspect(custom.diagnostics.length(), content="0")
    }

    An extra reserved word stops a name from being used. To turn a rule off, give the dialect a new rule set; do not change the set of the base:

    test {
    let base = @dialect.Dialect::elm_0_19_1()
    let rules = base.rules.copy()
    rules.remove(LeadingZero)
    let dialect = { ..base, rules, reserved_words: ["forall"], }
    inspect(base.has(LeadingZero), content="true")
    inspect(dialect.has(LeadingZero), content="false")
    let parse = (text : String) => {
    @parser.parse_module(
    @scanner.SourceText::new(text),
    @scanner.DefaultScanner::new(dialect~),
    dialect~,
    ).diagnostics
    }
    inspect(parse("module Main exposing (..)\n\nx = 007\n").length(), content="0")
    let errors = parse("module Main exposing (..)\n\nforall = 1\n")
    inspect(
    errors[0].message,
    content="Malformed function declaration: expected a function name, found `forall`",
    )
    }

    Dialect::elm_0_19_1

    fn Dialect::elm_0_19_1() -> Dialect

    The default dialect: rejects what elm make 0.19.1 rejects as syntax.

    It enables every rule except TitlecaseNameStart, has the standard operator table, and reads doc-comment attributes. core_package is false, so effect modules and infix declarations are errors.

    test {
    let dialect = @dialect.Dialect::elm_0_19_1()
    inspect(dialect.name, content="elm-0.19.1")
    inspect(dialect.has(PortInNormalModule), content="true")
    debug_inspect(
    dialect.operator("|>"),
    content=(
    #|Some({ symbol: "|>", precedence: 1, direction: Left })
    ),
    )
    }

    Dialect::elm_syntax_7_3_9

    fn Dialect::elm_syntax_7_3_9() -> Dialect

    Rejects exactly what elm-syntax 7.3.9 rejects.

    Use it to compare output with elm-syntax: the parity harness scores in this dialect. It accepts some syntax that elm make rejects, for example 007, 0X1F and 1e.

    test {
    let source = @scanner.SourceText::new(
    "module Main exposing (..)\n\nx =\n 0X1F\n",
    )
    let parse = (dialect : @dialect.Dialect) => {
    @parser.parse_module(
    source,
    @scanner.DefaultScanner::new(dialect~),
    dialect~,
    ).diagnostics.length()
    }
    inspect(parse(@dialect.Dialect::elm_syntax_7_3_9()), content="0")
    inspect(parse(@dialect.Dialect::elm_0_19_1()), content="1")
    }

    Dialect::has

    fn Dialect::has(self : Dialect, rule : Rule) -> Bool

    Whether the dialect enables rule.

    Dialect::operator

    fn Dialect::operator(self : Dialect, symbol : String) -> OperatorDef?

    The operator table entry for symbol, if the dialect has one.

    Dialect::validate

    fn Dialect::validate(self : Dialect) -> Array[String]

    Problems with the dialect's extension data, as messages (empty when it is valid). The scanner and parser do not check these; call this when a dialect comes from outside (a config file, a CLI flag).

    OperatorDef

    pub(all) struct OperatorDef {
    symbol : String
    precedence : Int
    direction :
    InfixDirection

    } derive(Eq,
    Debug
    )

    An infix operator: its symbol, precedence (1 to 9) and direction.

    Rule

    pub(all) enum Rule {
    LeadingZero
    EmptyHex
    BadUnicodeEscape
    CharLength
    IndentedContinuation
    LetDeclarationColumn
    ModuleLevelColumn
    SpacedOperatorName
    UppercaseHexPrefix
    ExponentWithoutDigits
    ImportColumn
    NonAsciiDigitInName
    TitlecaseNameStart
    EffectModule
    InfixDeclaration
    DuplicateEffectKey
    PortInNormalModule
    } derive(Eq, Hash,
    Debug
    )

    A rejection rule: one class of syntax that a dialect rejects. A rule only runs when the dialect enables it.

    A diagnostic from a rule names it as [rule: <name>] (see Rule::name). Dialect::has tells whether a dialect enables a rule.

    test {
    let elm = @dialect.Dialect::elm_0_19_1()
    let oracle = @dialect.Dialect::elm_syntax_7_3_9()
    // Both reject a char literal with two characters.
    inspect(elm.has(CharLength) && oracle.has(CharLength), content="true")
    // Only `elm make` rejects `007`.
    inspect(elm.has(LeadingZero), content="true")
    inspect(oracle.has(LeadingZero), content="false")
    inspect(@dialect.Rule::LeadingZero.name(), content="leading-zero")
    }

    Rule::name

    fn Rule::name(self : Rule) -> String

    Kebab-case name, shown in diagnostics as [rule: <name>].

    DEFAULT_FUEL

    let DEFAULT_FUEL : Int

    The default fuel of the parser: how deep it goes before it refuses with TOO MUCH NESTING (KR-PARSE-010).

    Fuel is a nesting counter, as the Scala compiler's -Xmax-fuel: a walk spends units when it enters a nested construct and gets them back when it leaves. The costs are internal; the caller sees one number. With the default, the parser accepts 150 nesting levels: a declaration body and 149 nested parentheses, lists, records, lambdas, lets or cases, or 74 nested constructor patterns or type applications (they count two levels each). More fuel needs more stack, and the stack is the host's setting, not krueger's: see the cookbook article "Set the nesting budget".

    standard_operators

    fn standard_operators() -> Array[OperatorDef]

    The infix operators of Elm 0.19.1 as elm-syntax 7.3.9 knows them, including </> and <?> from elm/url.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io