tree-sitter-wit 0.2.0

Wit grammar for tree-sitter
Documentation
function punctuated(rule, separator) {
    return seq(rule, repeat(seq(separator, rule)));
}

function cases(rule) {
    return optional(
        seq(
            punctuated(rule, ","),
            optional(","),
        ),
    );
}

module.exports = grammar({
    name: "wit",
    word: $ => $.identifier,
    extras: $ => [/[\s\n\t]+/, $.slash_comment, $.block_comment],
    conficts: $ => [
        [$.exported_item, $.exported_path],
        [$.imported_item, $.imported_path],
        [$.doc_comment, $.slash_comment],
    ],

    rules: {
        source_file: $ => seq(
            optional(field("package", $.package_decl)),
            repeat($.top_level_item),
        ),

        package_decl: $ => seq(
            field("attributes", repeat($.attribute)),
            "package",
            $.fully_qualified_package_name,
            ";",
        ),
        fully_qualified_package_name: $ => seq(
            field("package", $.package_name),
            token.immediate(":"),
            field("path", $.package_path),
            optional(seq(token.immediate("@"), field("version", $.semver))),
        ),
        // FIXME: Allow package names with multiple segments separated by ":"
        // without causing an ambiguity in "package really:nested:module;", where
        // that last "module" is actually the $.package_path
        package_name: $ => $.identifier,
        package_path: $ => prec.left(punctuated($.identifier, "/")),

        top_level_item: $ => choice($.top_level_use_item, $.world_item, $.interface_item),

        top_level_use_item: $ => seq(
            "use",
            $.use_path,
            optional(seq("as", field("alias", $.identifier))),
            ";",
        ),
        use_path: $ => choice($.local_use_path, $.fully_qualified_use_path),
        local_use_path: $ => $.identifier,
        fully_qualified_use_path: $ => seq(
            field("package", $.package_name),
            token.immediate(":"),
            field("path", $.package_path),
            optional(seq(token.immediate("@"), field("version", $.semver))),
        ),

        world_item: $ => seq(
            field("attributes", repeat($.attribute)),
            "world",
            field("name", $.identifier),
            "{",
            field("items", repeat($.world_items)),
            "}",
        ),
        world_items: $ => choice(
            $.export_item, $.import_item, $.use_item, $.typedef_item, $.include_item,
        ),
        export_item: $ => choice(
            prec(1, $.exported_item),
            $.exported_path,
        ),
        exported_item: $ => seq("export", field("name", $.identifier), ":", $.extern_type),
        exported_path: $ => seq("export", $.fully_qualified_use_path, ";"),
        import_item: $ => choice($.imported_item, $.imported_path),
        imported_item: $ => seq("import", field("name", $.identifier), ":", $.extern_type),
        imported_path: $ => seq("import", $.use_path, ";"),

        extern_type: $ => choice(
            seq($.func_type, ";"),
            seq("interface", "{", repeat($.interface_items), "}"),
        ),

        use_item: $ => seq(
            "use",
            field("path", $.use_path),
            token.immediate("."),
            token.immediate("{"),
            $._use_names_list,
            token.immediate("}"),
            ";",
        ),
        _use_names_list: $ => punctuated($.use_names_item, ","),
        use_names_item: $ => seq(
            field("name", $.identifier),
            optional(seq("as", field("alias", $.identifier))),
        ),

        interface_item: $ => seq(
            field("attributes", repeat($.attribute)),
            "interface",
            field("name", $.identifier),
            "{",
            field("items", repeat($.interface_items)),
            "}",
        ),
        interface_items: $ => choice($.typedef_item, $.use_item, $.func_item),
        typedef_item: $ => choice(
            $.resource_item,
            $.variant_item,
            $.record_item,
            $.flags_item,
            $.enum_item,
            $.type_item,
        ),
        func_item: $ => seq(
            field("attributes", repeat($.attribute)),
            field("name", $.identifier),
            ":",
            field("ty", $.func_type),
            ";",
        ),
        func_type: $ => seq(
            "func",
            field("params", $.param_list),
            optional(
                seq(
                    "->",
                    field("result", $.result_list),
                )
            ),
        ),
        param_list: $ => seq("(", field("params", optional($._param_list_inner)), ")"),
        _param_list_inner: $ => seq(punctuated($._named_type_list, ","), optional(",")),
        result_list: $ => choice($.ty, seq("(", optional($.named_result_list), ")")),
        named_result_list: $ => $._named_type_list,
        _named_type_list: $ => prec.left(punctuated($.named_type, ",")),
        named_type: $ => seq(
            field("name", $.identifier),
            ":",
            field("ty", $.ty),
        ),

        include_item: $ => seq(
            "include",
            field("path", $.use_path),
            choice(
                ";",
                seq("with", "{", field("names", $.include_names_list), "}")
            ),
        ),
        include_names_list: $ => punctuated($.include_names_item, ","),
        include_names_item: $ => seq(
            field("name", $.identifier),
            "as",
            field("alias", $.identifier),
        ),

        resource_item: $ => seq(
            field("attributes", repeat($.attribute)),
            "resource",
            field("name", $.identifier),
            choice(
                ";",
                seq("{", field("methods", repeat($.resource_method)), "}")
            ),
        ),
        resource_method: $ => choice(
            $.func_item,
            $.static_method,
            $.resource_constructor,
        ),
        static_method: $ => seq(
            field("attributes", repeat($.attribute)),
            field("name", $.identifier),
            ":",
            "static",
            $.func_type,
            ";",
        ),
        resource_constructor: $ => seq(
            field("attributes", repeat($.attribute)),
            "constructor",
            field("params", $.param_list),
            ";",
        ),

        variant_item: $ => seq(
            field("attributes", repeat($.attribute)),
            "variant",
            field("name", $.identifier),
            "{",
            field("cases", cases($.variant_case)),
            "}",
        ),
        variant_case: $ => seq(
            field("attributes", repeat($.attribute)),
            field("name", $.identifier),
            optional(
                seq(
                    "(",
                    field("ty", $.ty),
                    ")",
                ),
            ),
        ),

        record_item: $ => seq(
            field("attributes", repeat($.attribute)),
            "record",
            field("name", $.identifier),
            "{",
            field("fields", cases($.record_field)),
            "}",
        ),
        record_field: $ => seq(
            field("attributes", repeat($.attribute)),
            field("name", $.identifier),
            ":",
            field("ty", $.ty),
        ),

        flags_item: $ => seq(
            field("attributes", repeat($.attribute)),
            "flags",
            field("name", $.identifier),
            "{",
            field("cases", cases($.flags_case)),
            "}",
        ),
        flags_case: $ => seq(
            field("attributes", repeat($.attribute)),
            field("name", $.identifier),
        ),

        enum_item: $ => seq(
            field("attributes", repeat($.attribute)),
            "enum",
            field("name", $.identifier),
            "{",
            field("cases", cases($.enum_case)),
            "}",
        ),
        enum_case: $ => seq(
            field("attributes", repeat($.attribute)),
            field("name", $.identifier),
        ),

        type_item: $ => seq(
            field("attributes", repeat($.attribute)),
            "type",
            field("name", $.identifier),
            "=",
            field("ty", $.ty),
            ";",
        ),

        ty: $ => choice($.builtins, $.tuple, $.list, $.option, $.result, $.user_defined_type, $.handle),
        builtins: $ => choice(
            "u8", "u16", "u32", "u64", "s8", "s16", "s32", "s64", "float32", "float64", "char", "bool", "string"
        ),
        tuple: $ => seq("tuple", "<", cases($.ty), ">"),
        list: $ => seq("list", "<", $.ty, ">"),
        option: $ => seq("option", "<", $.ty, ">"),
        result: $ => seq("result", optional($._result_list)),
        _result_list: $ => seq(
            "<",
            choice("_", field("ok", $.ty)),
            optional(
                seq(",", field("err", $.ty)),
            ),
            ">",
        ),
        user_defined_type: $ => field("name", $.identifier),

        handle: $ => choice($.borrowed_handle, $.owned_handle),
        borrowed_handle: $ => seq("borrow", "<", $.user_defined_type, ">"),
        owned_handle: $ => seq("own", "<", $.user_defined_type, ">"),

        attribute: $ => $.doc_comment,

        semver: $ => /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/,
        identifier: $ => /%?[a-zA-Z][a-zA-Z0-9-]*/,
        doc_comment: $ => seq("///", /[ ]*/, $.docs),
        docs: $ => /[^\n]*/,
        block_comment: $ => seq(
            '/*',
            /[^*]*\*+([^/*][^*]*\*+)*/,
            '/'
        ),
        slash_comment: $ => seq("//", /[^/\n][^\n]*/),
    },
});