code-ruby 0.5.6 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -3
  3. data/Gemfile.lock +11 -21
  4. data/README.md +2 -102
  5. data/bin/code +34 -16
  6. data/code-ruby.gemspec +5 -3
  7. data/lib/code/error.rb +16 -5
  8. data/lib/code/node/base_10.rb +4 -2
  9. data/lib/code/node/base_16.rb +3 -1
  10. data/lib/code/node/base_2.rb +3 -1
  11. data/lib/code/node/base_8.rb +3 -1
  12. data/lib/code/node/boolean.rb +4 -2
  13. data/lib/code/node/call.rb +35 -12
  14. data/lib/code/node/call_argument.rb +16 -5
  15. data/lib/code/node/code.rb +5 -4
  16. data/lib/code/node/decimal.rb +2 -0
  17. data/lib/code/node/{dictionnary.rb → dictionary.rb} +14 -5
  18. data/lib/code/node/function.rb +7 -4
  19. data/lib/code/node/function_parameter.rb +3 -1
  20. data/lib/code/node/if.rb +5 -3
  21. data/lib/code/node/left_operation.rb +63 -0
  22. data/lib/code/node/list.rb +2 -0
  23. data/lib/code/node/negation.rb +2 -0
  24. data/lib/code/node/not.rb +2 -0
  25. data/lib/code/node/nothing.rb +3 -1
  26. data/lib/code/node/number.rb +2 -0
  27. data/lib/code/node/right_operation.rb +70 -0
  28. data/lib/code/node/splat.rb +2 -0
  29. data/lib/code/node/square_bracket.rb +37 -0
  30. data/lib/code/node/statement.rb +13 -5
  31. data/lib/code/node/string.rb +3 -1
  32. data/lib/code/node/ternary.rb +5 -3
  33. data/lib/code/node/unary_minus.rb +2 -0
  34. data/lib/code/node/while.rb +6 -4
  35. data/lib/code/node.rb +11 -5
  36. data/lib/code/object/argument.rb +13 -7
  37. data/lib/code/object/boolean.rb +43 -5
  38. data/lib/code/object/class.rb +17 -0
  39. data/lib/code/object/context.rb +36 -0
  40. data/lib/code/object/decimal.rb +252 -100
  41. data/lib/code/object/dictionary.rb +641 -0
  42. data/lib/code/object/function.rb +54 -27
  43. data/lib/code/object/global.rb +65 -19
  44. data/lib/code/object/identifier_list.rb +47 -0
  45. data/lib/code/object/integer.rb +320 -137
  46. data/lib/code/object/list.rb +140 -138
  47. data/lib/code/object/nothing.rb +10 -4
  48. data/lib/code/object/number.rb +6 -1
  49. data/lib/code/object/range.rb +85 -88
  50. data/lib/code/object/ruby_function.rb +11 -6
  51. data/lib/code/object/string.rb +51 -48
  52. data/lib/code/object.rb +117 -139
  53. data/lib/code/parser/addition.rb +4 -2
  54. data/lib/code/parser/and_operator.rb +4 -2
  55. data/lib/code/parser/bitwise_and.rb +4 -2
  56. data/lib/code/parser/bitwise_or.rb +4 -2
  57. data/lib/code/parser/boolean.rb +3 -1
  58. data/lib/code/parser/call.rb +17 -11
  59. data/lib/code/parser/chained_call.rb +10 -22
  60. data/lib/code/parser/class.rb +9 -6
  61. data/lib/code/parser/code.rb +6 -4
  62. data/lib/code/parser/{dictionnary.rb → dictionary.rb} +16 -13
  63. data/lib/code/parser/equal.rb +9 -36
  64. data/lib/code/parser/equality.rb +4 -2
  65. data/lib/code/parser/function.rb +24 -9
  66. data/lib/code/parser/greater.rb +6 -3
  67. data/lib/code/parser/group.rb +4 -2
  68. data/lib/code/parser/if.rb +6 -4
  69. data/lib/code/parser/if_modifier.rb +5 -25
  70. data/lib/code/parser/left_operation.rb +40 -0
  71. data/lib/code/parser/list.rb +6 -5
  72. data/lib/code/parser/multiplication.rb +4 -2
  73. data/lib/code/parser/name.rb +19 -4
  74. data/lib/code/parser/negation.rb +4 -2
  75. data/lib/code/parser/not_keyword.rb +5 -3
  76. data/lib/code/parser/nothing.rb +3 -10
  77. data/lib/code/parser/number.rb +4 -2
  78. data/lib/code/parser/or_keyword.rb +4 -2
  79. data/lib/code/parser/or_operator.rb +4 -2
  80. data/lib/code/parser/power.rb +4 -28
  81. data/lib/code/parser/range.rb +4 -2
  82. data/lib/code/parser/rescue.rb +6 -26
  83. data/lib/code/parser/right_operation.rb +40 -0
  84. data/lib/code/parser/shift.rb +4 -2
  85. data/lib/code/parser/splat.rb +5 -3
  86. data/lib/code/parser/square_bracket.rb +48 -0
  87. data/lib/code/parser/statement.rb +3 -1
  88. data/lib/code/parser/string.rb +12 -10
  89. data/lib/code/parser/ternary.rb +10 -11
  90. data/lib/code/parser/unary_minus.rb +5 -3
  91. data/lib/code/parser/while.rb +5 -3
  92. data/lib/code/parser/whitespace.rb +2 -0
  93. data/lib/code/parser.rb +10 -3
  94. data/lib/code/ruby.rb +4 -2
  95. data/lib/code/type/hash.rb +38 -0
  96. data/lib/code/type/maybe.rb +29 -0
  97. data/lib/code/type/or.rb +38 -0
  98. data/lib/code/type/repeat.rb +38 -0
  99. data/lib/code/type/sig.rb +130 -0
  100. data/lib/code/type.rb +25 -0
  101. data/lib/code/version.rb +3 -0
  102. data/lib/code-ruby.rb +1 -2
  103. data/lib/code.rb +15 -16
  104. data/spec/code/node/call_spec.rb +39 -0
  105. data/spec/code/object/boolean_spec.rb +18 -0
  106. data/spec/code/object/decimal_spec.rb +51 -0
  107. data/spec/code/object/dictionary_spec.rb +98 -0
  108. data/spec/code/object/function_spec.rb +42 -0
  109. data/spec/code/object/integer_spec.rb +43 -0
  110. data/spec/code/object/nothing_spec.rb +14 -0
  111. data/spec/code/object/range_spec.rb +23 -0
  112. data/spec/code/parser/boolean_spec.rb +5 -10
  113. data/spec/code/parser/chained_call.rb +4 -5
  114. data/spec/code/parser/{dictionnary_spec.rb → dictionary_spec.rb} +5 -6
  115. data/spec/code/parser/function_spec.rb +4 -5
  116. data/spec/code/parser/group_spec.rb +5 -12
  117. data/spec/code/parser/if_modifier_spec.rb +18 -0
  118. data/spec/code/parser/list_spec.rb +4 -5
  119. data/spec/code/parser/number_spec.rb +4 -5
  120. data/spec/code/parser/string_spec.rb +4 -5
  121. data/spec/code/parser_spec.rb +22 -16
  122. data/spec/code/type_spec.rb +21 -0
  123. data/spec/code_spec.rb +171 -0
  124. data/spec/spec_helper.rb +1 -6
  125. metadata +63 -136
  126. data/.cherry.js +0 -21
  127. data/.editorconfig +0 -9
  128. data/.github/workflows/rspec.yml +0 -14
  129. data/.gitignore +0 -2
  130. data/.prettierrc +0 -3
  131. data/.tool-versions +0 -1
  132. data/CHANGELOG.md +0 -55
  133. data/LICENSE +0 -7
  134. data/TODO +0 -17
  135. data/bin/format +0 -3
  136. data/bin/publish +0 -19
  137. data/bin/template +0 -85
  138. data/bin/test +0 -17
  139. data/docs/class.code +0 -9
  140. data/docs/euler/1.template +0 -10
  141. data/docs/euler/2.template +0 -16
  142. data/docs/euler/3.template +0 -16
  143. data/docs/euler/4.template +0 -10
  144. data/docs/euler/5.template +0 -13
  145. data/docs/fibonnaci.template +0 -14
  146. data/docs/meetup.code +0 -12
  147. data/docs/precedence.template +0 -36
  148. data/docs/rain.code +0 -22
  149. data/docs/slack.code +0 -17
  150. data/docs/stripe.code +0 -7
  151. data/docs/twitter.code +0 -9
  152. data/language-ruby.gemspec +0 -17
  153. data/lib/code/node/chained_call.rb +0 -23
  154. data/lib/code/node/equal.rb +0 -34
  155. data/lib/code/node/if_modifier.rb +0 -47
  156. data/lib/code/node/operation.rb +0 -38
  157. data/lib/code/node/power.rb +0 -20
  158. data/lib/code/node/rescue.rb +0 -17
  159. data/lib/code/object/dictionnary.rb +0 -96
  160. data/lib/code/parser/equality_lower.rb +0 -9
  161. data/lib/code/parser/operation.rb +0 -35
  162. data/lib/language/atom.rb +0 -342
  163. data/lib/language/output.rb +0 -130
  164. data/lib/language/parser/absent/present.rb +0 -8
  165. data/lib/language/parser/absent.rb +0 -6
  166. data/lib/language/parser/end_of_input.rb +0 -6
  167. data/lib/language/parser/interuption.rb +0 -38
  168. data/lib/language/parser/not_end_of_input.rb +0 -6
  169. data/lib/language/parser/str/not_found.rb +0 -16
  170. data/lib/language/parser/str.rb +0 -6
  171. data/lib/language/parser.rb +0 -53
  172. data/lib/language-ruby.rb +0 -10
  173. data/lib/language.rb +0 -80
  174. data/lib/template/node/code_part.rb +0 -13
  175. data/lib/template/node/part.rb +0 -19
  176. data/lib/template/node/template.rb +0 -15
  177. data/lib/template/node/text_part.rb +0 -13
  178. data/lib/template/node.rb +0 -4
  179. data/lib/template/parser/template.rb +0 -39
  180. data/lib/template/parser.rb +0 -19
  181. data/lib/template/version.rb +0 -3
  182. data/lib/template-ruby.rb +0 -10
  183. data/lib/template.rb +0 -50
  184. data/spec/code/addition_spec.rb +0 -13
  185. data/spec/code/and_operator_spec.rb +0 -13
  186. data/spec/code/bitwise_and_spec.rb +0 -13
  187. data/spec/code/bitwise_or_spec.rb +0 -13
  188. data/spec/code/boolean_spec.rb +0 -13
  189. data/spec/code/call_spec.rb +0 -21
  190. data/spec/code/chained_call_spec.rb +0 -16
  191. data/spec/code/code_spec.rb +0 -29
  192. data/spec/code/dictionnary_spec.rb +0 -17
  193. data/spec/code/equal_spec.rb +0 -26
  194. data/spec/code/equality_spec.rb +0 -13
  195. data/spec/code/function_spec.rb +0 -18
  196. data/spec/code/greater_spec.rb +0 -18
  197. data/spec/code/group_spec.rb +0 -12
  198. data/spec/code/if_modifier_spec.rb +0 -20
  199. data/spec/code/if_spec.rb +0 -25
  200. data/spec/code/list_spec.rb +0 -19
  201. data/spec/code/multiplication_spec.rb +0 -18
  202. data/spec/code/negation_spec.rb +0 -20
  203. data/spec/code/not_keyword_spec.rb +0 -13
  204. data/spec/code/nothing_spec.rb +0 -17
  205. data/spec/code/number_spec.rb +0 -22
  206. data/spec/code/or_keyword_spec.rb +0 -17
  207. data/spec/code/or_operator_spec.rb +0 -16
  208. data/spec/code/parser/call_spec.rb +0 -26
  209. data/spec/code/power_spec.rb +0 -13
  210. data/spec/code/range_spec.rb +0 -16
  211. data/spec/code/rescue_spec.rb +0 -13
  212. data/spec/code/shift_spec.rb +0 -13
  213. data/spec/code/splat_spec.rb +0 -13
  214. data/spec/code/string_spec.rb +0 -27
  215. data/spec/code/ternary_spec.rb +0 -18
  216. data/spec/code/unary_minus_spec.rb +0 -13
  217. data/spec/code/while_spec.rb +0 -18
  218. data/template-ruby.gemspec +0 -19
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
3
- class Range < Operation
5
+ class Range < LeftOperation
4
6
  def statement
5
- ::Code::Parser::OrOperator
7
+ OrOperator
6
8
  end
7
9
 
8
10
  def dot
@@ -1,38 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
3
- class Rescue < Language
5
+ class Rescue < RightOperation
4
6
  def statement
5
- ::Code::Parser::Ternary
6
- end
7
-
8
- def rescue_class
9
- ::Code::Parser::Rescue
10
- end
11
-
12
- def whitespace
13
- ::Code::Parser::Whitespace
14
- end
15
-
16
- def whitespace?
17
- whitespace.maybe
7
+ Ternary
18
8
  end
19
9
 
20
10
  def rescue_keyword
21
11
  str("rescue")
22
12
  end
23
13
 
24
- def root
25
- (
26
- statement.aka(:left) <<
27
- (
28
- whitespace? << rescue_keyword << whitespace? <<
29
- rescue_class.aka(:right)
30
- ).maybe
31
- )
32
- .aka(:rescue)
33
- .then do |output|
34
- output[:rescue][:right] ? output : output[:rescue][:left]
35
- end
14
+ def operator
15
+ rescue_keyword
36
16
  end
37
17
  end
38
18
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Code
4
+ class Parser
5
+ class RightOperation < Language
6
+ def statement
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def whitespace
11
+ Whitespace
12
+ end
13
+
14
+ def whitespace?
15
+ whitespace.maybe
16
+ end
17
+
18
+ def operator
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def root
23
+ (
24
+ statement.aka(:left) << (
25
+ whitespace? << operator.aka(:operator) << whitespace? <<
26
+ self.class.aka(:right)
27
+ ).maybe
28
+ )
29
+ .aka(:right_operation)
30
+ .then do |output|
31
+ if output[:right_operation][:right]
32
+ output
33
+ else
34
+ output[:right_operation][:left]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
3
- class Shift < Operation
5
+ class Shift < LeftOperation
4
6
  def statement
5
- ::Code::Parser::Addition
7
+ Addition
6
8
  end
7
9
 
8
10
  def greater
@@ -1,16 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
3
5
  class Splat < Language
4
6
  def statement
5
- ::Code::Parser::Class
7
+ Class
6
8
  end
7
9
 
8
10
  def splat
9
- ::Code::Parser::Splat
11
+ Splat
10
12
  end
11
13
 
12
14
  def whitespace
13
- ::Code::Parser::Whitespace
15
+ Whitespace
14
16
  end
15
17
 
16
18
  def whitespace?
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Code
4
+ class Parser
5
+ class SquareBracket < Language
6
+ def statement
7
+ UnaryMinus
8
+ end
9
+
10
+ def square_bracket
11
+ SquareBracket
12
+ end
13
+
14
+ def whitespace
15
+ Whitespace
16
+ end
17
+
18
+ def whitespace?
19
+ whitespace.maybe
20
+ end
21
+
22
+ def left_square_bracket
23
+ str("[")
24
+ end
25
+
26
+ def right_square_bracket
27
+ str("]")
28
+ end
29
+
30
+ def root
31
+ (
32
+ statement.aka(:left) << (
33
+ left_square_bracket << whitespace? << square_bracket <<
34
+ (whitespace? << right_square_bracket).maybe
35
+ ).repeat(1).aka(:statements).maybe
36
+ )
37
+ .aka(:square_bracket)
38
+ .then do |output|
39
+ if output[:square_bracket][:statements]
40
+ output
41
+ else
42
+ output[:square_bracket][:left]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
3
5
  class Statement < Language
4
6
  def root
5
- ::Code::Parser::Equality
7
+ Splat
6
8
  end
7
9
  end
8
10
  end
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
3
5
  class String < Language
4
6
  def code
5
- ::Code::Parser::Code
7
+ Code
6
8
  end
7
9
 
8
10
  def name
9
- ::Code::Parser::Name
11
+ Name
10
12
  end
11
13
 
12
14
  def single_quote
@@ -18,7 +20,7 @@ class Code
18
20
  end
19
21
 
20
22
  def backslash
21
- str("\\")
23
+ str('\\')
22
24
  end
23
25
 
24
26
  def opening_curly_bracket
@@ -54,15 +56,15 @@ class Code
54
56
  end
55
57
 
56
58
  def single_quoted_string
57
- single_quote.ignore <<
58
- (code_part.aka(:code) | single_quoted_text_part.aka(:text)).repeat <<
59
- single_quote.ignore.maybe
59
+ single_quote.ignore << (
60
+ code_part.aka(:code) | single_quoted_text_part.aka(:text)
61
+ ).repeat << single_quote.ignore.maybe
60
62
  end
61
63
 
62
64
  def double_quoted_string
63
- double_quote.ignore <<
64
- (code_part.aka(:code) | double_quoted_text_part.aka(:text)).repeat <<
65
- double_quote.ignore.maybe
65
+ double_quote.ignore << (
66
+ code_part.aka(:code) | double_quoted_text_part.aka(:text)
67
+ ).repeat << double_quote.ignore.maybe
66
68
  end
67
69
 
68
70
  def symbol
@@ -71,7 +73,7 @@ class Code
71
73
 
72
74
  def root
73
75
  (single_quoted_string | double_quoted_string | symbol).aka(:string) |
74
- ::Code::Parser::Number
76
+ Number
75
77
  end
76
78
  end
77
79
  end
@@ -1,16 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
3
5
  class Ternary < Language
4
6
  def statement
5
- ::Code::Parser::Range
7
+ Range
6
8
  end
7
9
 
8
10
  def ternary
9
- ::Code::Parser::Ternary
11
+ Ternary
10
12
  end
11
13
 
12
14
  def whitespace
13
- ::Code::Parser::Whitespace
15
+ Whitespace
14
16
  end
15
17
 
16
18
  def whitespace?
@@ -27,14 +29,11 @@ class Code
27
29
 
28
30
  def root
29
31
  (
30
- statement.aka(:left) <<
31
- (
32
- whitespace? << question_mark << whitespace? <<
33
- ternary.aka(:middle) <<
34
- (
35
- whitespace? << colon << whitespace? << ternary.aka(:right)
36
- ).maybe
37
- ).maybe
32
+ statement.aka(:left) << (
33
+ whitespace? << question_mark << whitespace? <<
34
+ ternary.aka(:middle) <<
35
+ (whitespace? << colon << whitespace? << ternary.aka(:right)).maybe
36
+ ).maybe
38
37
  )
39
38
  .aka(:ternary)
40
39
  .then do |output|
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
3
5
  class UnaryMinus < Language
4
6
  def unary_minus
5
- ::Code::Parser::UnaryMinus
7
+ UnaryMinus
6
8
  end
7
9
 
8
10
  def whitespace
9
- ::Code::Parser::Whitespace
11
+ Whitespace
10
12
  end
11
13
 
12
14
  def whitespace?
@@ -24,7 +26,7 @@ class Code
24
26
  def root
25
27
  (operator.aka(:operator) << whitespace? << unary_minus.aka(:right)).aka(
26
28
  :unary_minus
27
- ) | ::Code::Parser::Power
29
+ ) | Power
28
30
  end
29
31
  end
30
32
  end
@@ -1,16 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
3
5
  class While < Language
4
6
  def statement
5
- ::Code::Parser::If
7
+ If
6
8
  end
7
9
 
8
10
  def whitespace
9
- ::Code::Parser::Whitespace
11
+ Whitespace
10
12
  end
11
13
 
12
14
  def code
13
- ::Code::Parser::Code
15
+ Code
14
16
  end
15
17
 
16
18
  def while_keyword
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
3
5
  class Whitespace < Language
data/lib/code/parser.rb CHANGED
@@ -1,15 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Parser
5
+ class Error < StandardError
6
+ end
7
+
3
8
  def initialize(input)
4
9
  @input = input
5
10
  end
6
11
 
7
- def self.parse(input)
8
- new(input).parse
12
+ def self.parse(...)
13
+ new(...).parse
9
14
  end
10
15
 
11
16
  def parse
12
- ::Code::Parser::Code.parse(input)
17
+ Code.parse(input)
18
+ rescue Language::Parser::NotEndOfInput => e
19
+ raise Error, e.message
13
20
  end
14
21
 
15
22
  private
data/lib/code/ruby.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  class Ruby
3
5
  def initialize(raw = {})
@@ -32,7 +34,7 @@ class Code
32
34
  elsif big_decimal?
33
35
  ::Code::Object::Decimal.new(raw)
34
36
  elsif hash?
35
- ::Code::Object::Dictionnary.new(
37
+ ::Code::Object::Dictionary.new(
36
38
  raw
37
39
  .map do |key, value|
38
40
  [::Code::Ruby.to_code(key), ::Code::Ruby.to_code(value)]
@@ -164,7 +166,7 @@ class Code
164
166
  end
165
167
 
166
168
  def code_dictionnary?
167
- raw.is_a?(::Code::Object::Dictionnary)
169
+ raw.is_a?(::Code::Object::Dictionary)
168
170
  end
169
171
 
170
172
  def code_list?
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Code
4
+ class Type
5
+ class Hash < Type
6
+ attr_reader :hash
7
+
8
+ def initialize(hash)
9
+ @hash = hash
10
+ end
11
+
12
+ def valid?(argument)
13
+ return false unless argument.is_a?(Object::Dictionary)
14
+ argument = argument.raw
15
+ (argument.keys + hash.keys).uniq.all? do |key|
16
+ next false unless hash[key] && argument[key]
17
+ valid_for?(expected: hash[key], actual: argument[key])
18
+ end
19
+ end
20
+
21
+ def min_arguments
22
+ hash.sum do |_, value|
23
+ min_arguments_of(value)
24
+ end
25
+ end
26
+
27
+ def max_arguments
28
+ hash.sum do |_, value|
29
+ max_arguments_of(value)
30
+ end
31
+ end
32
+
33
+ def name
34
+ "{#{hash.map { |key, value| "#{key.inspect} => #{value.name}" }.join(", ")}}"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Code
4
+ class Type
5
+ class Maybe < Type
6
+ attr_reader :clazz
7
+
8
+ def initialize(clazz)
9
+ @clazz = clazz
10
+ end
11
+
12
+ def valid?(argument)
13
+ !argument || valid_for?(expected: clazz, actual: argument)
14
+ end
15
+
16
+ def min_arguments
17
+ 0
18
+ end
19
+
20
+ def max_arguments
21
+ max_arguments_of(clazz)
22
+ end
23
+
24
+ def name
25
+ "#{clazz.name}.maybe"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Code
4
+ class Type
5
+ class Or < Type
6
+ attr_reader :left, :right
7
+
8
+ def initialize(left, right)
9
+ @left = left
10
+ @right = right
11
+ end
12
+
13
+ def valid?(argument)
14
+ valid_for?(expected: left, actual: argument) ||
15
+ valid_for?(expected: right, actual: argument)
16
+ end
17
+
18
+ def min_arguments
19
+ [min_arguments_of(left), min_arguments_of(right)].min
20
+ end
21
+
22
+ def max_arguments
23
+ left_max_arguments = max_arguments_of(left)
24
+ right_max_arguments = max_arguments_of(right)
25
+
26
+ if left_max_arguments.nil? || right_max_arguments.nil?
27
+ nil
28
+ else
29
+ [left_max_arguments, right_max_arguments].max
30
+ end
31
+ end
32
+
33
+ def name
34
+ "(#{left.name} | #{right.name})"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Code
4
+ class Type
5
+ class Repeat < Type
6
+ attr_reader :clazz, :minimum, :maximum
7
+
8
+ def initialize(clazz, minimum: 0, maximum: nil)
9
+ @clazz = clazz
10
+ @minimum = minimum
11
+ @maximum = maximum
12
+ end
13
+
14
+ def valid?(argument)
15
+ valid_for?(expected: clazz, actual: argument)
16
+ end
17
+
18
+ def min_arguments
19
+ minimum * min_arguments_of(clazz)
20
+ end
21
+
22
+ def max_arguments
23
+ max_arguments = max_arguments_of(clazz)
24
+ max_arguments.nil? || maximum.nil? ? nil : maximum * max_arguments
25
+ end
26
+
27
+ def name
28
+ if minimum.zero? && maximum.nil?
29
+ "#{clazz.name}.repeat"
30
+ elsif maximum.nil?
31
+ "#{clazz.name}.repeat(#{minimum})"
32
+ else
33
+ "#{clazz.name}.repeaa(#{minimum}, #{maximum})"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Code
4
+ class Type
5
+ class Sig
6
+ def initialize(args, object:, &block)
7
+ @args = args
8
+ @block = block
9
+ @object = object
10
+ end
11
+
12
+ def self.sig(...)
13
+ new(...).sig
14
+ end
15
+
16
+ def sig
17
+ check_number_of_arguments!
18
+ check_types_of_arguments!
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :args, :block, :object
24
+
25
+ def expected_arguments
26
+ @expected_arguments ||= Array(block&.call || []).map do |clazz|
27
+ clazz.is_a?(::Hash) ? Hash.new(clazz) : clazz
28
+ end
29
+ end
30
+
31
+ def min_arguments_of(clazz)
32
+ clazz.is_a?(Type) ? clazz.min_arguments : 1
33
+ end
34
+
35
+ def max_arguments_of(clazz)
36
+ clazz.is_a?(Type) ? clazz.max_arguments : 1
37
+ end
38
+
39
+ def actual_arguments
40
+ args[:arguments].map(&:value)
41
+ end
42
+
43
+ def operator
44
+ args[:operator] || "call"
45
+ end
46
+
47
+ def function
48
+ "#{object.class.name}##{operator}"
49
+ end
50
+
51
+ def min_arguments
52
+ expected_arguments.sum { |clazz| min_arguments_of(clazz) }
53
+ end
54
+
55
+ def max_arguments
56
+ max_arguments = expected_arguments.map { |clazz| max_arguments_of(clazz) }
57
+ max_arguments.include?(nil) ? nil : max_arguments.sum
58
+ end
59
+
60
+ def actual_count
61
+ if actual_arguments.one?
62
+ "1 argument"
63
+ else
64
+ "#{actual_arguments.size} arguments"
65
+ end
66
+ end
67
+
68
+ def expected_count
69
+ if min_arguments == max_arguments
70
+ if min_arguments == 1
71
+ "1 argument"
72
+ else
73
+ "#{min_arguments} arguments"
74
+ end
75
+ elsif max_arguments.nil?
76
+ "#{min_arguments}+ arguments"
77
+ else
78
+ "#{min_arguments}-#{max_arguments} arguments"
79
+ end
80
+ end
81
+
82
+ def expected_range
83
+ max_arguments.nil? ? min_arguments.. : min_arguments..max_arguments
84
+ end
85
+
86
+ def check_number_of_arguments!
87
+ return if expected_range.include?(actual_arguments.size)
88
+
89
+ raise(
90
+ Error::ArityError,
91
+ "#{function}: Expected #{expected_count} but got #{actual_count}"
92
+ )
93
+ end
94
+
95
+ def valid_for?(expected:, actual:)
96
+ expected.is_a?(Type) ? expected.valid?(actual) : actual.is_a?(expected)
97
+ end
98
+
99
+ def check_types_of_arguments!
100
+ expected_index = 0
101
+ repeat_index = 0
102
+
103
+ actual_arguments.each do |actual|
104
+ expected = expected_arguments[expected_index]
105
+ if expected.is_a?(Repeat)
106
+ if valid_for?(expected:, actual:)
107
+ repeat_index += 1
108
+ elsif repeat_index >= expected.min_arguments
109
+ expected_index += 1
110
+ repeat_index = 0
111
+ else
112
+ raise(
113
+ Error::TypeError,
114
+ "#{function}: expected #{expected.name}, got #{actual.inspect}"
115
+ )
116
+ end
117
+ elsif valid_for?(expected:, actual:)
118
+ expected_index += 1
119
+ repeat_index = 0
120
+ else
121
+ raise(
122
+ Error::TypeError,
123
+ "#{function}: expected #{expected.name}, got #{actual.inspect}"
124
+ )
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end