rubocop-boochtek 0.2.0 → 0.2.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +115 -5
  3. data/LICENSE.md +2 -2
  4. data/README.md +53 -106
  5. data/lib/disable_ssl_verify.rb +5 -0
  6. data/lib/extensions/argf.rb +8 -0
  7. data/lib/extensions/boolean.rb +48 -0
  8. data/lib/extensions/class.rb +10 -0
  9. data/lib/extensions/enumerable.rb +42 -0
  10. data/lib/extensions/integer.rb +7 -0
  11. data/lib/extensions/llvm_module.rb +101 -0
  12. data/lib/extensions/module.rb +34 -0
  13. data/lib/extensions/string.rb +29 -0
  14. data/lib/stone/ast/block.rb +88 -0
  15. data/lib/stone/ast/boolean_literal.rb +43 -0
  16. data/lib/stone/ast/computed_property_definition.rb +32 -0
  17. data/lib/stone/ast/constant_definition.rb +124 -0
  18. data/lib/stone/ast/expression.rb +12 -0
  19. data/lib/stone/ast/function_call.rb +291 -0
  20. data/lib/stone/ast/function_type_annotation.rb +31 -0
  21. data/lib/stone/ast/integer_literal.rb +46 -0
  22. data/lib/stone/ast/lambda.rb +126 -0
  23. data/lib/stone/ast/null_literal.rb +29 -0
  24. data/lib/stone/ast/program_unit/top_function.rb +209 -0
  25. data/lib/stone/ast/program_unit.rb +412 -0
  26. data/lib/stone/ast/property_access.rb +557 -0
  27. data/lib/stone/ast/record_definition.rb +180 -0
  28. data/lib/stone/ast/record_instantiation.rb +141 -0
  29. data/lib/stone/ast/reference.rb +145 -0
  30. data/lib/stone/ast/string_literal.rb +79 -0
  31. data/lib/stone/ast/two_phase_processing.rb +36 -0
  32. data/lib/stone/ast/type_annotation.rb +26 -0
  33. data/lib/stone/ast/type_declaration.rb +28 -0
  34. data/lib/stone/ast/type_of_expression.rb +41 -0
  35. data/lib/stone/ast/type_reference.rb +28 -0
  36. data/lib/stone/ast/union_type_annotation.rb +26 -0
  37. data/lib/stone/ast.rb +64 -0
  38. data/lib/stone/built_ins.rb +245 -0
  39. data/lib/stone/error/argument_error.rb +7 -0
  40. data/lib/stone/error/arity_error.rb +7 -0
  41. data/lib/stone/error/overflow.rb +18 -0
  42. data/lib/stone/error/property_error.rb +7 -0
  43. data/lib/stone/error/reference_error.rb +4 -0
  44. data/lib/stone/error/type_error.rb +7 -0
  45. data/lib/stone/error.rb +12 -0
  46. data/lib/stone/grammar.rb +124 -0
  47. data/lib/stone/libc.rb +27 -0
  48. data/lib/stone/prelude.stone +11 -0
  49. data/lib/stone/rtti.rb +182 -0
  50. data/lib/stone/scope.rb +85 -0
  51. data/lib/stone/transform.rb +410 -0
  52. data/lib/stone/type.rb +323 -0
  53. data/lib/stone/type_context.rb +38 -0
  54. data/lib/stone/type_registry.rb +73 -0
  55. data/lib/stone/types.rb +104 -0
  56. data/lib/stone.rb +70 -0
  57. metadata +54 -24
  58. data/CHANGELOG.md +0 -28
  59. data/Rakefile +0 -5
  60. data/config/default.yml +0 -277
  61. data/lib/rubocop/boochtek/plugin.rb +0 -43
  62. data/lib/rubocop/boochtek/version.rb +0 -7
  63. data/lib/rubocop/boochtek.rb +0 -18
  64. data/lib/rubocop/cop/boochtek/compact_endless_methods.rb +0 -103
  65. data/lib/rubocop-boochtek.rb +0 -3
@@ -0,0 +1,28 @@
1
+ require "stone/ast/expression"
2
+ require "stone/rtti"
3
+
4
+
5
+ module Stone
6
+ class AST
7
+ class TypeReference < Stone::AST::Expression
8
+
9
+ def initialize
10
+ @name = :type_reference
11
+ end
12
+
13
+ def type(_context = nil)
14
+ Stone::Type::Type
15
+ end
16
+
17
+ def to_llir(_builder, mod, _scope = Stone::Scope.top_level)
18
+ # Return pointer to the Type type constant
19
+ Stone::RTTI.type_constant_for(mod, Stone::Type::Type)
20
+ end
21
+
22
+ def to_s
23
+ "Type"
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ require "stone/ast"
2
+
3
+
4
+ module Stone
5
+ class AST
6
+ class UnionTypeAnnotation < Stone::AST
7
+
8
+ attr_reader :alternatives
9
+
10
+ def initialize(alternatives)
11
+ @alternatives = alternatives
12
+ @name = :union_type_annotation
13
+ end
14
+
15
+ def to_s
16
+ @alternatives.map(&:to_s).join(" | ")
17
+ end
18
+
19
+ def to_type(registry)
20
+ alternative_types = @alternatives.map { |t| t.to_type(registry) }.compact
21
+ Stone::Type.union(alternatives: alternative_types)
22
+ end
23
+
24
+ end
25
+ end
26
+ end
data/lib/stone/ast.rb ADDED
@@ -0,0 +1,64 @@
1
+ module Stone
2
+ class AST
3
+
4
+ attr_reader :name, :children
5
+
6
+ def initialize(name, children = nil)
7
+ @name = name
8
+ @children = children
9
+ end
10
+
11
+ # Helper module for checking if a node represents a record instance
12
+ module RecordHelpers
13
+ # Check if a node is a Reference to a record instance
14
+ module_function def record_instance?(node, mod)
15
+ node.is_a?(Stone::AST::Reference) && mod.record_instance?(node.identifier)
16
+ end
17
+
18
+ end
19
+
20
+ # Shared type resolution logic for AST nodes
21
+ module TypeResolver
22
+ module_function def resolve_node_type(node, mod)
23
+ case node
24
+ when Stone::AST::Reference then node.type(mod)
25
+ when Stone::AST::FunctionCall then node.type(mod)
26
+ when Stone::AST::PropertyAccess then resolve_property_type(node, mod)
27
+ else node.type
28
+ end
29
+ end
30
+
31
+ module_function def resolve_property_type(node, mod)
32
+ receiver_type = resolve_node_type(node.receiver, mod)
33
+ return nil unless receiver_type
34
+
35
+ receiver_type.property_return_type(node.property)
36
+ end
37
+ end
38
+
39
+ # Shared utilities for working with field annotations
40
+ module FieldHelpers
41
+ # Check if an annotation represents a union type
42
+ module_function def union_annotation?(annotation)
43
+ annotation.is_a?(Stone::AST::UnionTypeAnnotation)
44
+ end
45
+
46
+ # Resolve a field's annotation to a Stone::Type
47
+ # Returns nil if the type cannot be resolved
48
+ module_function def resolve_field_type(field, registry = Stone::Type::Registry)
49
+ annotation = field[:type]
50
+ if annotation.respond_to?(:to_type)
51
+ annotation.to_type(registry)
52
+ else
53
+ registry.lookup(field[:type_name] || field[:type])
54
+ end
55
+ end
56
+
57
+ # Get the string representation of a field's type
58
+ module_function def field_type_name(field)
59
+ field[:type_name] || field[:type].to_s
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,245 @@
1
+ require "llvm/core"
2
+ require "stone/types"
3
+ require "stone/rtti"
4
+
5
+
6
+ module Stone
7
+ # Handles registration of built-in constants (including functions and operators).
8
+ class BuiltIns
9
+
10
+ def initialize(mod)
11
+ @mod = mod
12
+ end
13
+
14
+ def setup
15
+ setup_rtti
16
+ define_predefined_constants
17
+ define_builtin_functions
18
+ register_builtin_function_types
19
+ end
20
+
21
+ private def setup_rtti
22
+ Stone::RTTI.new(@mod).setup
23
+ end
24
+
25
+ private def define_predefined_constants
26
+ define_constant("ZERO", 0)
27
+ define_constant("ONE", 1)
28
+ end
29
+
30
+ private def define_builtin_functions
31
+ define_if_function
32
+ define_sum_function
33
+ define_comparison_operators
34
+ define_boolean_operators
35
+ end
36
+
37
+ private def define_constant(name, value)
38
+ @mod.globals.add(LLVM::Int64, name).tap do |global|
39
+ global.initializer = LLVM::Int64.from_i(value)
40
+ global.linkage = :internal
41
+ global.global_constant = true
42
+ end
43
+ end
44
+
45
+ private def define_comparison_operators
46
+ define_comparison("==", :eq)
47
+ define_comparison("!=", :ne)
48
+ define_comparison("≠", :ne)
49
+ define_comparison("<", :slt)
50
+ define_comparison("<=", :sle)
51
+ define_comparison("≤", :sle)
52
+ define_comparison(">", :sgt)
53
+ define_comparison(">=", :sge)
54
+ define_comparison("≥", :sge)
55
+ end
56
+
57
+ private def define_comparison(name, predicate)
58
+ # Define binary comparison function
59
+ # <(a, b) checks a < b
60
+ # Chained comparisons like <(a, b, c) are handled inline in FunctionCall#to_llir
61
+ @mod.functions.add(name, comparison_function_type).tap { |func| build_icmp_body(func, predicate) }
62
+ end
63
+
64
+ private def comparison_function_type
65
+ @comparison_function_type ||= LLVM::Type.function([LLVM::Int64.type, LLVM::Int64.type], LLVM::Int1.type)
66
+ end
67
+
68
+ private def build_icmp_body(func, predicate)
69
+ func.basic_blocks.append("entry").build do |builder|
70
+ result = builder.icmp(predicate, func.params[0], func.params[1], "cmp_result")
71
+ builder.ret(result)
72
+ end
73
+ end
74
+
75
+ private def define_boolean_operators
76
+ define_boolean_binary("and", :and)
77
+ define_boolean_binary("or", :or)
78
+ define_boolean_binary("xor", :xor)
79
+ define_boolean_unary("not", :xor)
80
+
81
+ define_boolean_binary("∧", :and)
82
+ define_boolean_binary("∨", :or)
83
+ define_boolean_binary("⊻", :xor)
84
+ define_boolean_unary("¬", :xor)
85
+
86
+ # TODO: Overload `==` and `!=` for Bool type once we support function overloading.
87
+ # Currently these operators only work for Int. For Booleans, use `xor` for XOR/inequality,
88
+ # or the integer comparison operators will work since TRUE=1 and FALSE=0.
89
+ end
90
+
91
+ private def define_boolean_binary(name, instruction)
92
+ @mod.functions.add(name, boolean_binary_function_type).tap do |func|
93
+ build_boolean_binary_body(func, instruction)
94
+ end
95
+ end
96
+
97
+ private def define_boolean_unary(name, instruction)
98
+ @mod.functions.add(name, boolean_unary_function_type).tap do |func|
99
+ build_boolean_unary_body(func, instruction)
100
+ end
101
+ end
102
+
103
+ private def boolean_binary_function_type
104
+ @boolean_binary_function_type ||= LLVM::Type.function([LLVM::Int1.type, LLVM::Int1.type], LLVM::Int1.type)
105
+ end
106
+
107
+ private def boolean_unary_function_type
108
+ @boolean_unary_function_type ||= LLVM::Type.function([LLVM::Int1.type], LLVM::Int1.type)
109
+ end
110
+
111
+ private def build_boolean_binary_body(func, instruction)
112
+ func.basic_blocks.append("entry").build do |builder|
113
+ result = builder.send(instruction, func.params[0], func.params[1], "bool_result") # rubocop:disable Style/Send
114
+ builder.ret(result)
115
+ end
116
+ end
117
+
118
+ private def build_boolean_unary_body(func, instruction)
119
+ func.basic_blocks.append("entry").build do |builder|
120
+ # NOT is implemented as XOR with TRUE (i.e., not(a) = a xor TRUE)
121
+ # The instruction parameter should always be :xor for unary boolean operations
122
+ result = builder.send(instruction, func.params[0], LLVM::TRUE, "not_result") # rubocop:disable Style/Send
123
+ builder.ret(result)
124
+ end
125
+ end
126
+
127
+ # Define the sum function: sum(i64, i64) -> i64.
128
+ # Uses LLVM's sadd.with.overflow intrinsic for overflow detection.
129
+ private def define_sum_function
130
+ i64 = LLVM::Int64.type
131
+ function_type = LLVM::Type.function([i64, i64], i64)
132
+ @mod.functions.add("sum", function_type).tap { |func| build_sum_body(func) }
133
+ end
134
+
135
+ private def build_sum_body(func)
136
+ func.basic_blocks.append("entry").build do |builder|
137
+ intrinsic = sadd_with_overflow_intrinsic
138
+ result = builder.call(intrinsic, func.params[0], func.params[1], "sadd_result")
139
+ # TODO: Check for overflow (something like `builder.extract_value(result, 0) == 1`) and branch to return a Stone::Type::Error if true.
140
+ # overflowed = builder.extract_value(result, 1)
141
+ sum_value = builder.extract_value(result, 0, "sum")
142
+ builder.ret(sum_value)
143
+ end
144
+ end
145
+
146
+ private def sadd_with_overflow_intrinsic
147
+ intrinsic_name = "llvm.sadd.with.overflow.i64"
148
+ return @mod.functions[intrinsic_name] if @mod.functions[intrinsic_name]
149
+
150
+ i64 = LLVM::Int64.type
151
+ i1 = LLVM::Int1.type
152
+ result_type = LLVM::Type.struct([i64, i1], false)
153
+ function_type = LLVM::Type.function([i64, i64], result_type)
154
+ @mod.functions.add(intrinsic_name, function_type)
155
+ end
156
+
157
+ # Define the if function: if(i1 condition, block* then_block, block* else_block) -> i64.
158
+ # Takes a boolean condition and two block pointers, executes the appropriate block.
159
+ private def define_if_function
160
+ i1 = LLVM::Int1.type
161
+ i64 = LLVM::Int64.type
162
+ # Block type: () -> i64
163
+ block_type = LLVM::Type.function([], i64)
164
+ block_ptr = LLVM::Type.pointer(block_type)
165
+ # if takes: (i1 condition, block* then_block, block* else_block) -> i64
166
+ function_type = LLVM::Type.function([i1, block_ptr, block_ptr], i64)
167
+ @mod.functions.add("if", function_type).tap { |func| build_if_body(func) }
168
+ end
169
+
170
+ private def build_if_body(func)
171
+ blocks = create_if_basic_blocks(func)
172
+ build_if_entry(blocks, func.params[0])
173
+ then_result = build_if_branch(blocks[:then_bb], blocks[:merge_bb], func.params[1], "then_result")
174
+ else_result = build_if_branch(blocks[:else_bb], blocks[:merge_bb], func.params[2], "else_result")
175
+ build_if_merge(blocks, then_result, else_result)
176
+ end
177
+
178
+ private def create_if_basic_blocks(func)
179
+ {
180
+ entry_bb: func.basic_blocks.append("entry"),
181
+ then_bb: func.basic_blocks.append("then"),
182
+ else_bb: func.basic_blocks.append("else"),
183
+ merge_bb: func.basic_blocks.append("merge")
184
+ }
185
+ end
186
+
187
+ private def build_if_entry(blocks, condition)
188
+ blocks[:entry_bb].build { |builder| builder.cond(condition, blocks[:then_bb], blocks[:else_bb]) }
189
+ end
190
+
191
+ private def build_if_branch(branch_bb, merge_bb, block_ptr, result_name)
192
+ result = nil
193
+ branch_bb.build do |builder|
194
+ result = builder.call2(block_func_type, block_ptr, result_name)
195
+ builder.br(merge_bb)
196
+ end
197
+ result
198
+ end
199
+
200
+ private def build_if_merge(blocks, then_result, else_result)
201
+ blocks[:merge_bb].build do |builder|
202
+ phi = builder.phi(LLVM::Int64.type, {blocks[:then_bb] => then_result, blocks[:else_bb] => else_result}, "if_result")
203
+ builder.ret(phi)
204
+ end
205
+ end
206
+
207
+ private def block_func_type
208
+ @block_func_type ||= LLVM::Type.function([], LLVM::Int64.type)
209
+ end
210
+
211
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
212
+ private def register_builtin_function_types
213
+ int = Stone::Type::Int
214
+ bool = Stone::Type::Bool
215
+
216
+ # sum(Int, Int) -> Int
217
+ Stone::Type::Registry.register_as("sum", Stone::Type.function(param_types: [int, int], return_type: int))
218
+
219
+ # if(Bool, Block, Block) -> Int
220
+ # Note: Block type not yet in type system, so we just record param count conceptually
221
+ Stone::Type::Registry.register_as("if", Stone::Type.function(param_types: [bool], return_type: int))
222
+
223
+ # Boolean operators
224
+ # and(Bool, Bool) -> Bool
225
+ Stone::Type::Registry.register_as("and", Stone::Type.function(param_types: [bool, bool], return_type: bool))
226
+
227
+ # or(Bool, Bool) -> Bool
228
+ Stone::Type::Registry.register_as("or", Stone::Type.function(param_types: [bool, bool], return_type: bool))
229
+
230
+ # xor(Bool, Bool) -> Bool
231
+ Stone::Type::Registry.register_as("xor", Stone::Type.function(param_types: [bool, bool], return_type: bool))
232
+
233
+ # not(Bool) -> Bool
234
+ Stone::Type::Registry.register_as("not", Stone::Type.function(param_types: [bool], return_type: bool))
235
+
236
+ # Unicode operator aliases
237
+ Stone::Type::Registry.register_as("∧", Stone::Type.function(param_types: [bool, bool], return_type: bool))
238
+ Stone::Type::Registry.register_as("∨", Stone::Type.function(param_types: [bool, bool], return_type: bool))
239
+ Stone::Type::Registry.register_as("⊻", Stone::Type.function(param_types: [bool, bool], return_type: bool))
240
+ Stone::Type::Registry.register_as("¬", Stone::Type.function(param_types: [bool], return_type: bool))
241
+ end
242
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
243
+
244
+ end
245
+ end
@@ -0,0 +1,7 @@
1
+ require "stone/error"
2
+
3
+
4
+ module Stone
5
+ class ArgumentError < Stone::Error
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require "stone/error/argument_error"
2
+
3
+
4
+ module Stone
5
+ class ArityError < Stone::ArgumentError
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ require "stone/error"
2
+
3
+
4
+ module Stone
5
+ class Error
6
+ class Overflow < Stone::Error
7
+
8
+ attr_reader :literal
9
+
10
+ def initialize(message = nil, location:, literal:)
11
+ @literal = literal
12
+ message ||= "Overflow Error: #{literal} falls outside 64-bit Int range at line #{location.line}, column #{location.column}"
13
+ super(message, location: location)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ require "stone/error"
2
+
3
+
4
+ module Stone
5
+ class PropertyError < Stone::Error
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Stone
2
+ class ReferenceError < Stone::Error
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+ require "stone/error"
2
+
3
+
4
+ module Stone
5
+ class TypeError < Stone::Error
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Stone
2
+ class Error < StandardError
3
+
4
+ attr_reader :location
5
+
6
+ def initialize(message, location: nil)
7
+ super(message)
8
+ @location = location
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,124 @@
1
+ require "grammy/grammar"
2
+
3
+
4
+ module Stone
5
+ class Grammar < Grammy::Grammar
6
+
7
+ # Identifier and operator patterns
8
+ ALPHA_IDENTIFIER = /[a-zA-Z_][a-zA-Z0-9_]*[!?]?/
9
+ COMPUTED_PROPERTY_ID = /[A-Z][a-zA-Z0-9_]*@[a-zA-Z_][a-zA-Z0-9_]*[!?]?/
10
+ COMPARISON_OPERATOR = /(==|!=|<=|>=|≠|≤|≥|<|>)/
11
+ BOOLEAN_OPERATOR = /(∧|∨|⊻|¬)/
12
+
13
+ start :program_unit
14
+
15
+ # Program structure
16
+ rule(:program_unit) { statement_list + eof }
17
+ rule(:statement_list) { list(statement, separated_by: statement_separator, allow_repeated_separator: true) }
18
+ rule(:statement_separator) { newline | semi }
19
+ rule(:statement) { (definition | expression | comment) + (ws_no_nl? + comment)[0..1] }
20
+ rule(:definition) { (computed_property_id | identifier) + ws! + define_op + ws! + expression }
21
+
22
+ # Expressions
23
+ # Expression hierarchy (from lowest to highest precedence):
24
+ # 1. comparison_operation (lowest - binary/variadic operators)
25
+ # 2. boolean_operation (binary/variadic boolean operators)
26
+ # 3. postfix (function_call, property_access)
27
+ # 4. primary (highest - atoms)
28
+ #
29
+ # Comparisons operate on boolean operations, which operate on postfix expressions.
30
+ # boolean_operation with zero operators is equivalent to postfix_expression.
31
+ # This allows mixing with parentheses: (5 < 3) ∧ TRUE
32
+ # Both can chain: 1 < 2 < 3 desugars to <(1, 2, 3)
33
+ rule(:expression) { type_declaration | comparison_operation | boolean_operation }
34
+ rule(:type_declaration) { identifier + ws! + str("::") + ws! + type_annotation }
35
+ rule(:type_annotation) { type_union }
36
+ rule(:type_union) { type_term + (ws? + str("|") + ws? + type_term)[0..] }
37
+ rule(:type_term) { type_function | parens(type_annotation) | type_name }
38
+ rule(:type_function) { type_params + ws? + str("->") + ws? + type_return }
39
+ rule(:type_params) { parens(comma_separated(type_annotation, allow_trailing: false)) }
40
+ rule(:type_return) { type_return_union | parens(type_annotation) }
41
+ rule(:type_return_union) { type_name + (ws? + str("|") + ws? + type_name)[0..] }
42
+ rule(:type_name) { identifier }
43
+ rule(:comparison_operation) { boolean_operation + (ws! + comparison_operator + ws! + boolean_operation)[1..] }
44
+ rule(:boolean_operation) { postfix_expression + (ws! + boolean_operator + ws! + postfix_expression)[0..] }
45
+ rule(:postfix_expression) { primary + (argument_list | property_accessor)[0..] }
46
+ rule(:property_accessor) { str(".") + identifier }
47
+ rule(:primary) { parens(expression) | type_of_expression | record_definition | literal | type_reference | reference | lambda | block }
48
+ rule(:type_of_expression) { str("Type.of") + parens(expression) }
49
+ rule(:type_reference) { str("Type") }
50
+ rule(:argument_list) { parens(comma_separated(argument)) }
51
+ rule(:argument) { expression }
52
+ rule(:literal) { literal_null | literal_boolean | literal_string | literal_i64 }
53
+ rule(:reference) { identifier }
54
+ rule(:lambda) { lambda_op + parameter_list + ws? + block }
55
+ rule(:parameter_list) { parens(comma_separated(parameter, allow_trailing: false)) }
56
+ rule(:parameter) { identifier }
57
+ rule(:block) { braces(statement_list) }
58
+ rule(:record_definition) { str("Record") + parens(comma_separated(type_declaration, allow_trailing: false)) }
59
+
60
+ # Custom Matchers/Combinators
61
+ # Match the passed-in matchers/combinators within parentheses, with whitespace allowed.
62
+ def parens(submatchers)
63
+ str("(") + ws? + submatchers + ws? + str(")")
64
+ end
65
+
66
+ # Match the passed-in matchers/combinators within curly braces, with whitespace allowed.
67
+ def braces(submatchers)
68
+ str("{") + ws? + submatchers + ws? + str("}")
69
+ end
70
+
71
+ # Match a comma-separated list, optionally allowing a trailing comma.
72
+ def comma_separated(submatchers, allow_trailing: true)
73
+ list(submatchers, separated_by: str(","), allow_trailing: allow_trailing)
74
+ end
75
+
76
+ # Match a list of matchers/combinators, separated by a specified separator.
77
+ # - Supports allowing trailing separator (default: true)
78
+ # - Supports allowing repeated consecutive separators (default: false)
79
+ # - Supports minimum item count (default: 0)
80
+ def list(items, separated_by:, allow_trailing: true, allow_repeated_separator: false, min: 0)
81
+ list_repetition = min.zero? ? [0..] : [min..]
82
+ items_with_separators = ws? + items + (separated_by + ws? + items)[0..]
83
+ if allow_trailing
84
+ trailing_repetition = allow_repeated_separator ? [0..] : [0..1]
85
+ (items_with_separators + (separated_by + ws?)[*trailing_repetition])[*list_repetition]
86
+ else
87
+ items_with_separators[*list_repetition]
88
+ end
89
+ end
90
+
91
+ # Identifiers
92
+ terminal(:computed_property_id) { COMPUTED_PROPERTY_ID }
93
+ terminal(:identifier) { Regexp.union(ALPHA_IDENTIFIER, BOOLEAN_OPERATOR, COMPARISON_OPERATOR) }
94
+
95
+ # Operators
96
+ terminal(:comparison_operator) { COMPARISON_OPERATOR }
97
+ terminal(:boolean_operator) { BOOLEAN_OPERATOR }
98
+ terminal(:lambda_op) { "λ" }
99
+ terminal(:define_op) { ":=" }
100
+
101
+ # Literals
102
+ rule(:literal_null) { str("NULL") }
103
+ rule(:literal_boolean) { reg(/(TRUE|FALSE)/) }
104
+ # WARNING: String literal rule must come before comment rule to handle `#` inside strings correctly.
105
+ rule(:literal_string) { reg(/"(?:[^"\\]|\\.)*"/) }
106
+ # NOTE: Decimal must be last to avoid consuming `0` from the prefixes.
107
+ rule(:literal_i64) { literal_i64_binary | literal_i64_octal | literal_i64_hex | literal_i64_decimal }
108
+ terminal(:literal_i64_decimal) { /[+-]?\d+/ }
109
+ terminal(:literal_i64_binary) { /[+-]?0b[01]+/ }
110
+ terminal(:literal_i64_octal) { /[+-]?0o[0-7]+/ }
111
+ terminal(:literal_i64_hex) { /[+-]?0x[0-9a-fA-F]+/ }
112
+
113
+ # Whitespace and separators
114
+ terminal(:comment) { /#[^\n\r]*/ } # Does **NOT** include trailing EOL.
115
+ terminal(:newline) { /(\n|\r\n)/ }
116
+ terminal(:semi) { ";" }
117
+ terminal(:ws) { /[ \t\n\r]+/ }
118
+ terminal(:ws_no_nl) { /[ \t]+/ }
119
+ rule(:ws?) { ws[0..] } # White space is **allowed**.
120
+ rule(:ws!) { ws[1..] } # White space is **required**.
121
+ rule(:ws_no_nl?) { ws_no_nl[0..] }
122
+
123
+ end
124
+ end
data/lib/stone/libc.rb ADDED
@@ -0,0 +1,27 @@
1
+ require "llvm/core"
2
+
3
+
4
+ module Stone
5
+ # Module for declaring C standard library functions in LLVM
6
+ module LibC
7
+
8
+ module_function
9
+
10
+ def get_or_declare_strcmp(mod)
11
+ return mod.functions["strcmp"] if mod.functions["strcmp"]
12
+
13
+ # Declare strcmp: i32 strcmp(i8*, i8*)
14
+ strcmp_type = LLVM::Type.function([LLVM::Type.pointer(LLVM::Int8), LLVM::Type.pointer(LLVM::Int8)], LLVM::Int32)
15
+ mod.functions.add("strcmp", strcmp_type)
16
+ end
17
+
18
+ def get_or_declare_strlen(mod)
19
+ return mod.functions["strlen"] if mod.functions["strlen"]
20
+
21
+ # Declare strlen: i64 strlen(i8*)
22
+ strlen_type = LLVM::Type.function([LLVM::Type.pointer(LLVM::Int8)], LLVM::Int64)
23
+ mod.functions.add("strlen", strlen_type)
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ # Stone Standard Library Prelude
2
+ # This file is automatically loaded before user code.
3
+ # It defines standard properties for built-in types.
4
+
5
+ # Boolean properties
6
+ Bool@not := λ(this) { if(this, { FALSE }, { TRUE }) }
7
+
8
+ # Integer properties
9
+ Int@positive? := λ(this) { this > 0 }
10
+ Int@negative? := λ(this) { this < 0 }
11
+ Int@zero? := λ(this) { this == 0 }