• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

type-ruby / t-ruby / 20878882620

10 Jan 2026 01:16PM UTC coverage: 92.105% (-0.2%) from 92.341%
20878882620

Pull #36

github

web-flow
Merge 99e48fa63 into 2df43cad9
Pull Request #36: feat: add TypeScript-style array shorthand syntax `T[]`

35 of 36 new or added lines in 3 files covered. (97.22%)

29 existing lines in 1 file now uncovered.

8213 of 8917 relevant lines covered (92.1%)

1797.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.83
/lib/t_ruby/parser_combinator/type_parser.rb
1
# frozen_string_literal: true
2

3
module TRuby
1✔
4
  module ParserCombinator
1✔
5
    # Type Parser - Parse T-Ruby type expressions
6
    class TypeParser
1✔
7
      include DSL
1✔
8

9
      def initialize
1✔
10
        build_parsers
944✔
11
      end
12

13
      def parse(input)
1✔
14
        result = @type_expr.parse(input.strip)
17,789✔
15
        if result.success?
17,789✔
16
          { success: true, type: result.value, remaining: input[result.position..] }
17,775✔
17
        else
18
          { success: false, error: result.error, position: result.position }
14✔
19
        end
20
      end
21

22
      private
1✔
23

24
      def build_parsers
1✔
25
        # Identifier (type name)
26
        type_name = identifier.label("type name")
944✔
27

28
        # Simple type
29
        type_name.map { |name| IR::SimpleType.new(name: name) }
944✔
30

31
        # Lazy reference for recursive types
32
        type_expr = lazy { @type_expr }
1,010✔
33

34
        # Generic type arguments: <Type, Type, ...>
35
        generic_args = (
36
          lexeme(char("<")) >>
944✔
37
          type_expr.sep_by1(lexeme(char(","))) <<
38
          lexeme(char(">"))
39
        ).map { |(_, types)| types }
124✔
40

41
        # Generic type: Base<Args>
42
        generic_type = (type_name >> generic_args.optional).map do |(name, args)|
944✔
43
          if args && !args.empty?
18,063✔
44
            IR::GenericType.new(base: name, type_args: args)
124✔
45
          else
46
            IR::SimpleType.new(name: name)
17,939✔
47
          end
48
        end
49

50
        # Nullable type: Type?
51
        nullable_suffix = char("?")
944✔
52

53
        # Parenthesized type
54
        paren_type = (lexeme(char("(")) >> type_expr << lexeme(char(")"))).map { |(_, t)| t }
947✔
55

56
        # Function type: (Params) -> ReturnType
57
        param_list = (
58
          lexeme(char("(")) >>
944✔
59
          type_expr.sep_by(lexeme(char(","))) <<
60
          lexeme(char(")"))
61
        ).map { |(_, params)| params }
8✔
62

63
        arrow = lexeme(string("->"))
944✔
64

65
        function_type = (param_list >> arrow >> type_expr).map do |((params, _arrow), ret)|
944✔
66
          IR::FunctionType.new(param_types: params, return_type: ret)
5✔
67
        end
68

69
        # Tuple type: [Type, Type, ...]
70
        tuple_type = (
71
          lexeme(char("[")) >>
944✔
72
          type_expr.sep_by1(lexeme(char(","))) <<
73
          lexeme(char("]"))
74
        ).map { |(_, types)| IR::TupleType.new(element_types: types) }
1✔
75

76
        # Primary type (before operators)
77
        primary_type = choice(
944✔
78
          function_type,
79
          tuple_type,
80
          paren_type,
81
          generic_type
82
        )
83

84
        # Array shorthand suffix: [] (can be repeated for nested arrays)
85
        array_suffix = string("[]")
944✔
86

87
        # Postfix operators: ([] | ?)*
88
        # Handles: String[], Integer[][], String[]?, String?[], etc.
89
        postfix_op = array_suffix | nullable_suffix
944✔
90

91
        base_type = (primary_type >> postfix_op.many).map do |(initial_type, ops)|
944✔
92
          ops.reduce(initial_type) do |type, op|
18,072✔
93
            case op
61✔
94
            when "[]"
95
              IR::GenericType.new(base: "Array", type_args: [type])
48✔
96
            when "?"
97
              IR::NullableType.new(inner_type: type)
13✔
98
            else
NEW
99
              type
×
100
            end
101
          end
102
        end
103

104
        # Union type: Type | Type | ...
105
        union_op = lexeme(char("|"))
944✔
106
        union_type = base_type.sep_by1(union_op).map do |types|
944✔
107
          types.length == 1 ? types.first : IR::UnionType.new(types: types)
17,946✔
108
        end
109

110
        # Intersection type: Type & Type & ...
111
        intersection_op = lexeme(char("&"))
944✔
112
        @type_expr = union_type.sep_by1(intersection_op).map do |types|
944✔
113
          types.length == 1 ? types.first : IR::IntersectionType.new(types: types)
17,941✔
114
        end
115
      end
116
    end
117
  end
118
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc