code-ruby 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -4
  3. data/Gemfile.lock +10 -22
  4. data/code-ruby.gemspec +4 -3
  5. data/lib/code/error.rb +16 -5
  6. data/lib/code/node/base_10.rb +4 -2
  7. data/lib/code/node/base_16.rb +3 -1
  8. data/lib/code/node/base_2.rb +3 -1
  9. data/lib/code/node/base_8.rb +3 -1
  10. data/lib/code/node/boolean.rb +4 -2
  11. data/lib/code/node/call.rb +35 -12
  12. data/lib/code/node/call_argument.rb +16 -5
  13. data/lib/code/node/code.rb +5 -4
  14. data/lib/code/node/decimal.rb +2 -0
  15. data/lib/code/node/{dictionnary.rb → dictionary.rb} +14 -5
  16. data/lib/code/node/function.rb +7 -4
  17. data/lib/code/node/function_parameter.rb +3 -1
  18. data/lib/code/node/if.rb +5 -3
  19. data/lib/code/node/left_operation.rb +63 -0
  20. data/lib/code/node/list.rb +2 -0
  21. data/lib/code/node/negation.rb +2 -0
  22. data/lib/code/node/not.rb +2 -0
  23. data/lib/code/node/nothing.rb +3 -1
  24. data/lib/code/node/number.rb +2 -0
  25. data/lib/code/node/right_operation.rb +70 -0
  26. data/lib/code/node/splat.rb +2 -0
  27. data/lib/code/node/square_bracket.rb +37 -0
  28. data/lib/code/node/statement.rb +13 -5
  29. data/lib/code/node/string.rb +3 -1
  30. data/lib/code/node/ternary.rb +5 -3
  31. data/lib/code/node/unary_minus.rb +2 -0
  32. data/lib/code/node/while.rb +6 -4
  33. data/lib/code/node.rb +11 -5
  34. data/lib/code/object/argument.rb +13 -7
  35. data/lib/code/object/boolean.rb +43 -5
  36. data/lib/code/object/class.rb +17 -0
  37. data/lib/code/object/context.rb +36 -0
  38. data/lib/code/object/decimal.rb +252 -100
  39. data/lib/code/object/dictionary.rb +641 -0
  40. data/lib/code/object/function.rb +54 -27
  41. data/lib/code/object/global.rb +65 -19
  42. data/lib/code/object/identifier_list.rb +47 -0
  43. data/lib/code/object/integer.rb +320 -137
  44. data/lib/code/object/list.rb +140 -138
  45. data/lib/code/object/nothing.rb +10 -4
  46. data/lib/code/object/number.rb +6 -1
  47. data/lib/code/object/range.rb +85 -88
  48. data/lib/code/object/ruby_function.rb +11 -6
  49. data/lib/code/object/string.rb +51 -48
  50. data/lib/code/object.rb +117 -139
  51. data/lib/code/parser/addition.rb +4 -2
  52. data/lib/code/parser/and_operator.rb +4 -2
  53. data/lib/code/parser/bitwise_and.rb +4 -2
  54. data/lib/code/parser/bitwise_or.rb +4 -2
  55. data/lib/code/parser/boolean.rb +3 -1
  56. data/lib/code/parser/call.rb +17 -11
  57. data/lib/code/parser/chained_call.rb +10 -22
  58. data/lib/code/parser/class.rb +9 -6
  59. data/lib/code/parser/code.rb +6 -4
  60. data/lib/code/parser/{dictionnary.rb → dictionary.rb} +16 -13
  61. data/lib/code/parser/equal.rb +9 -36
  62. data/lib/code/parser/equality.rb +4 -2
  63. data/lib/code/parser/function.rb +24 -9
  64. data/lib/code/parser/greater.rb +6 -3
  65. data/lib/code/parser/group.rb +4 -2
  66. data/lib/code/parser/if.rb +6 -4
  67. data/lib/code/parser/if_modifier.rb +5 -25
  68. data/lib/code/parser/left_operation.rb +40 -0
  69. data/lib/code/parser/list.rb +6 -5
  70. data/lib/code/parser/multiplication.rb +4 -2
  71. data/lib/code/parser/name.rb +19 -4
  72. data/lib/code/parser/negation.rb +4 -2
  73. data/lib/code/parser/not_keyword.rb +5 -3
  74. data/lib/code/parser/nothing.rb +3 -10
  75. data/lib/code/parser/number.rb +4 -2
  76. data/lib/code/parser/or_keyword.rb +4 -2
  77. data/lib/code/parser/or_operator.rb +4 -2
  78. data/lib/code/parser/power.rb +4 -28
  79. data/lib/code/parser/range.rb +4 -2
  80. data/lib/code/parser/rescue.rb +6 -26
  81. data/lib/code/parser/right_operation.rb +40 -0
  82. data/lib/code/parser/shift.rb +4 -2
  83. data/lib/code/parser/splat.rb +5 -3
  84. data/lib/code/parser/square_bracket.rb +48 -0
  85. data/lib/code/parser/statement.rb +3 -1
  86. data/lib/code/parser/string.rb +12 -10
  87. data/lib/code/parser/ternary.rb +10 -11
  88. data/lib/code/parser/unary_minus.rb +5 -3
  89. data/lib/code/parser/while.rb +5 -3
  90. data/lib/code/parser/whitespace.rb +2 -0
  91. data/lib/code/parser.rb +10 -3
  92. data/lib/code/ruby.rb +4 -2
  93. data/lib/code/type/hash.rb +38 -0
  94. data/lib/code/type/maybe.rb +29 -0
  95. data/lib/code/type/or.rb +38 -0
  96. data/lib/code/type/repeat.rb +38 -0
  97. data/lib/code/type/sig.rb +130 -0
  98. data/lib/code/type.rb +25 -0
  99. data/lib/code/version.rb +3 -0
  100. data/lib/code-ruby.rb +1 -2
  101. data/lib/code.rb +15 -16
  102. data/spec/code/node/call_spec.rb +39 -0
  103. data/spec/code/object/boolean_spec.rb +18 -0
  104. data/spec/code/object/decimal_spec.rb +51 -0
  105. data/spec/code/object/dictionary_spec.rb +98 -0
  106. data/spec/code/object/function_spec.rb +42 -0
  107. data/spec/code/object/integer_spec.rb +43 -0
  108. data/spec/code/object/nothing_spec.rb +14 -0
  109. data/spec/code/object/range_spec.rb +23 -0
  110. data/spec/code/parser/boolean_spec.rb +5 -10
  111. data/spec/code/parser/chained_call.rb +4 -5
  112. data/spec/code/parser/{dictionnary_spec.rb → dictionary_spec.rb} +5 -6
  113. data/spec/code/parser/function_spec.rb +4 -5
  114. data/spec/code/parser/group_spec.rb +5 -12
  115. data/spec/code/parser/if_modifier_spec.rb +18 -0
  116. data/spec/code/parser/list_spec.rb +4 -5
  117. data/spec/code/parser/number_spec.rb +4 -5
  118. data/spec/code/parser/string_spec.rb +4 -5
  119. data/spec/code/parser_spec.rb +22 -16
  120. data/spec/code/type_spec.rb +21 -0
  121. data/spec/code_spec.rb +171 -0
  122. data/spec/spec_helper.rb +1 -6
  123. metadata +61 -137
  124. data/.cherry.js +0 -21
  125. data/.editorconfig +0 -9
  126. data/.github/workflows/rspec.yml +0 -14
  127. data/.gitignore +0 -2
  128. data/.prettierrc +0 -3
  129. data/.tool-versions +0 -1
  130. data/CHANGELOG.md +0 -55
  131. data/LICENSE +0 -7
  132. data/README.md +0 -103
  133. data/TODO +0 -17
  134. data/bin/code +0 -76
  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 -11
  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