expressir 2.1.31 → 2.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +3 -2
  3. data/.github/workflows/release.yml +6 -0
  4. data/.rubocop_todo.yml +106 -92
  5. data/Gemfile +1 -1
  6. data/README.adoc +372 -1
  7. data/docs/_guides/formatter/formatter-architecture.adoc +401 -0
  8. data/docs/_guides/ruby-api/parsing-files.adoc +1 -1
  9. data/docs/_pages/parsers.adoc +31 -5
  10. data/docs/lychee.toml +3 -0
  11. data/expressir.gemspec +3 -2
  12. data/lib/expressir/benchmark.rb +6 -6
  13. data/lib/expressir/cli.rb +9 -0
  14. data/lib/expressir/commands/base.rb +2 -9
  15. data/lib/expressir/commands/format.rb +30 -0
  16. data/lib/expressir/commands/package.rb +92 -87
  17. data/lib/expressir/commands/validate_ascii.rb +2 -4
  18. data/lib/expressir/commands/validate_load.rb +8 -5
  19. data/lib/expressir/coverage.rb +15 -11
  20. data/lib/expressir/errors.rb +115 -0
  21. data/lib/expressir/express/builder.rb +350 -0
  22. data/lib/expressir/express/builders/attribute_decl_builder.rb +38 -0
  23. data/lib/expressir/express/builders/built_in_builder.rb +88 -0
  24. data/lib/expressir/express/builders/constant_builder.rb +115 -0
  25. data/lib/expressir/express/builders/declaration_builder.rb +24 -0
  26. data/lib/expressir/express/builders/derive_clause_builder.rb +16 -0
  27. data/lib/expressir/express/builders/derived_attr_builder.rb +28 -0
  28. data/lib/expressir/express/builders/domain_rule_builder.rb +21 -0
  29. data/lib/expressir/express/builders/entity_decl_builder.rb +108 -0
  30. data/lib/expressir/express/builders/explicit_attr_builder.rb +52 -0
  31. data/lib/expressir/express/builders/expression_builder.rb +453 -0
  32. data/lib/expressir/express/builders/function_decl_builder.rb +84 -0
  33. data/lib/expressir/express/builders/helpers.rb +148 -0
  34. data/lib/expressir/express/builders/interface_builder.rb +171 -0
  35. data/lib/expressir/express/builders/inverse_attr_builder.rb +45 -0
  36. data/lib/expressir/express/builders/inverse_attr_type_builder.rb +36 -0
  37. data/lib/expressir/express/builders/inverse_clause_builder.rb +16 -0
  38. data/lib/expressir/express/builders/literal_builder.rb +107 -0
  39. data/lib/expressir/express/builders/procedure_decl_builder.rb +80 -0
  40. data/lib/expressir/express/builders/qualifier_builder.rb +128 -0
  41. data/lib/expressir/express/builders/reference_builder.rb +27 -0
  42. data/lib/expressir/express/builders/rule_decl_builder.rb +95 -0
  43. data/lib/expressir/express/builders/schema_body_decl_builder.rb +22 -0
  44. data/lib/expressir/express/builders/schema_decl_builder.rb +62 -0
  45. data/lib/expressir/express/builders/schema_version_builder.rb +40 -0
  46. data/lib/expressir/express/builders/simple_id_builder.rb +26 -0
  47. data/lib/expressir/express/builders/statement_builder.rb +250 -0
  48. data/lib/expressir/express/builders/subtype_constraint_builder.rb +188 -0
  49. data/lib/expressir/express/builders/syntax_builder.rb +19 -0
  50. data/lib/expressir/express/builders/token_builder.rb +15 -0
  51. data/lib/expressir/express/builders/type_builder.rb +264 -0
  52. data/lib/expressir/express/builders/type_decl_builder.rb +32 -0
  53. data/lib/expressir/express/builders/unique_clause_builder.rb +22 -0
  54. data/lib/expressir/express/builders/unique_rule_builder.rb +36 -0
  55. data/lib/expressir/express/builders/where_clause_builder.rb +22 -0
  56. data/lib/expressir/express/builders.rb +43 -0
  57. data/lib/expressir/express/error.rb +18 -2
  58. data/lib/expressir/express/formatter.rb +18 -1508
  59. data/lib/expressir/express/formatters/data_types_formatter.rb +317 -0
  60. data/lib/expressir/express/formatters/declarations_formatter.rb +689 -0
  61. data/lib/expressir/express/formatters/expressions_formatter.rb +160 -0
  62. data/lib/expressir/express/formatters/literals_formatter.rb +46 -0
  63. data/lib/expressir/express/formatters/references_formatter.rb +42 -0
  64. data/lib/expressir/express/formatters/remark_formatter.rb +296 -0
  65. data/lib/expressir/express/formatters/statements_formatter.rb +224 -0
  66. data/lib/expressir/express/formatters/supertype_expressions_formatter.rb +48 -0
  67. data/lib/expressir/express/parser.rb +129 -14
  68. data/lib/expressir/express/pretty_formatter.rb +624 -0
  69. data/lib/expressir/express/remark_attacher.rb +1155 -0
  70. data/lib/expressir/express/resolve_references_model_visitor.rb +1 -0
  71. data/lib/expressir/express/streaming_builder.rb +467 -0
  72. data/lib/expressir/express/transformer/remark_handling.rb +196 -0
  73. data/lib/expressir/model/identifier.rb +1 -1
  74. data/lib/expressir/model/model_element.rb +30 -2
  75. data/lib/expressir/model/remark_info.rb +51 -0
  76. data/lib/expressir/model/search_engine.rb +58 -9
  77. data/lib/expressir/version.rb +1 -1
  78. data/lib/expressir.rb +6 -4
  79. metadata +71 -7
  80. data/lib/expressir/express/visitor.rb +0 -2815
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers"
4
+
5
+ module Expressir
6
+ module Express
7
+ module Builders
8
+ # Builds qualifier nodes (attribute, group, index qualifiers).
9
+ class QualifierBuilder
10
+ include Helpers
11
+
12
+ def build_redeclared_attribute(ast_data)
13
+ qualified_attr = ast_data[:qualified_attribute]
14
+ if qualified_attr.is_a?(Hash)
15
+ Builder.build({ qualified_attribute: qualified_attr })
16
+ end
17
+ end
18
+
19
+ def build_referenced_attribute(ast_data)
20
+ if ast_data[:attribute_ref]
21
+ Builder.build({ attribute_ref: ast_data[:attribute_ref] })
22
+ elsif ast_data[:qualified_attribute]
23
+ Builder.build({ qualified_attribute: ast_data[:qualified_attribute] })
24
+ end
25
+ end
26
+
27
+ def build_qualified_attribute(ast_data)
28
+ # t_self is part of qualified_attribute grammar
29
+ self_ref = if ast_data[:t_self]
30
+ Expressir::Model::References::SimpleReference.new(id: "SELF")
31
+ end
32
+
33
+ # group_qualifier contains the entity reference
34
+ entity_ref = if ast_data[:group_qualifier]
35
+ Builder.build({ entity_ref: ast_data[:group_qualifier][:entity_ref] })
36
+ end
37
+
38
+ # Create GroupReference with ref (SELF) and entity
39
+ group_qual = if self_ref || entity_ref
40
+ Expressir::Model::References::GroupReference.new(
41
+ ref: self_ref, entity: entity_ref,
42
+ )
43
+ end
44
+
45
+ attr_qual = if ast_data[:attribute_qualifier]
46
+ Builder.build({ attribute_qualifier: ast_data[:attribute_qualifier] })
47
+ end
48
+
49
+ if group_qual && attr_qual
50
+ Expressir::Model::References::AttributeReference.new(
51
+ ref: group_qual, attribute: attr_qual,
52
+ )
53
+ else
54
+ attr_qual || group_qual
55
+ end
56
+ end
57
+
58
+ def build_group_qualifier(ast_data)
59
+ entity = if ast_data[:entity_ref]
60
+ Builder.build({ entity_ref: ast_data[:entity_ref] })
61
+ end
62
+ Expressir::Model::References::GroupReference.new(entity: entity)
63
+ end
64
+
65
+ def build_attribute_qualifier(ast_data)
66
+ Builder.build({ attribute_ref: ast_data[:attribute_ref] })
67
+ end
68
+
69
+ def build_index_qualifier(ast_data)
70
+ index1 = Builder.build_optional(ast_data[:index1])
71
+ index2 = Builder.build_optional(ast_data[:index2])
72
+ { index1: index1, index2: index2 }
73
+ end
74
+
75
+ def build_index1(ast_data)
76
+ Builder.build_optional(ast_data[:numeric_expression])
77
+ end
78
+
79
+ def build_index2(ast_data)
80
+ Builder.build_optional(ast_data[:numeric_expression])
81
+ end
82
+
83
+ def build_index(ast_data)
84
+ Builder.build_optional(ast_data[:numeric_expression])
85
+ end
86
+
87
+ def build_enumeration_reference(ast_data)
88
+ type_ref = Builder.build_optional(ast_data[:type_ref])
89
+ enum_ref = if ast_data[:enumeration_ref]
90
+ Builder.build({ enumeration_ref: ast_data[:enumeration_ref] })
91
+ end
92
+
93
+ if type_ref && enum_ref
94
+ Expressir::Model::References::AttributeReference.new(
95
+ ref: type_ref,
96
+ attribute: enum_ref,
97
+ )
98
+ elsif enum_ref
99
+ enum_ref
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ builder = Expressir::Express::Builders::QualifierBuilder.new
108
+
109
+ Builder.register(:redeclared_attribute) do |d|
110
+ builder.build_redeclared_attribute(d)
111
+ end
112
+ Builder.register(:referenced_attribute) do |d|
113
+ builder.build_referenced_attribute(d)
114
+ end
115
+ Builder.register(:qualified_attribute) do |d|
116
+ builder.build_qualified_attribute(d)
117
+ end
118
+ Builder.register(:group_qualifier) { |d| builder.build_group_qualifier(d) }
119
+ Builder.register(:attribute_qualifier) do |d|
120
+ builder.build_attribute_qualifier(d)
121
+ end
122
+ Builder.register(:index_qualifier) { |d| builder.build_index_qualifier(d) }
123
+ Builder.register(:index1) { |d| builder.build_index1(d) }
124
+ Builder.register(:index2) { |d| builder.build_index2(d) }
125
+ Builder.register(:index) { |d| builder.build_index(d) }
126
+ Builder.register(:enumeration_reference) do |d|
127
+ builder.build_enumeration_reference(d)
128
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers"
4
+
5
+ module Expressir
6
+ module Express
7
+ module Builders
8
+ # Builds reference nodes (attribute_ref, entity_ref, etc.).
9
+ # Returns SimpleReference for all reference types.
10
+ class ReferenceBuilder
11
+ include Helpers
12
+
13
+ def call(ast_data)
14
+ id = extract_id_ref(ast_data)
15
+ Expressir::Model::References::SimpleReference.new(id: id)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ # Register for all reference types
23
+ %i[attribute_ref constant_ref entity_ref enumeration_ref function_ref
24
+ parameter_ref procedure_ref rule_ref rule_label_ref schema_ref
25
+ subtype_constraint_ref type_label_ref type_ref variable_ref].each do |ref_type|
26
+ Builder.register(ref_type, Expressir::Express::Builders::ReferenceBuilder.new)
27
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers"
4
+
5
+ module Expressir
6
+ module Express
7
+ module Builders
8
+ # Builds rule declaration nodes.
9
+ class RuleDeclBuilder
10
+ include Helpers
11
+
12
+ def call(ast_data)
13
+ head = ast_data[:rule_head]
14
+ algorithm_head = ast_data[:algorithm_head]
15
+ stmts = ast_data[:stmt]
16
+ where_clause = ast_data[:where_clause]
17
+
18
+ id = Builder.build_optional(head[:rule_id]) if head
19
+ applies_to = build_applies_to(head)
20
+
21
+ declarations = []
22
+ if algorithm_head.is_a?(Hash)
23
+ declarations = Builder.build_children(algorithm_head[:declaration])
24
+ end
25
+
26
+ types = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::Type) }
27
+ entities = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::Entity) }
28
+ subtype_constraints = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::SubtypeConstraint) }
29
+ functions = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::Function) }
30
+ procedures = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::Procedure) }
31
+ constants = build_constant_decl(algorithm_head[:constant_decl]) if algorithm_head.is_a?(Hash) && algorithm_head[:constant_decl]
32
+ variables = build_local_decl(algorithm_head[:local_decl]) if algorithm_head.is_a?(Hash) && algorithm_head[:local_decl]
33
+ statements = Builder.build_children(stmts)
34
+ where_rules = where_clause ? Builder.build({ where_clause: where_clause }) : []
35
+
36
+ Expressir::Model::Declarations::Rule.new(
37
+ id: id,
38
+ applies_to: [applies_to].flatten.compact,
39
+ types: types,
40
+ entities: entities,
41
+ subtype_constraints: subtype_constraints,
42
+ functions: functions,
43
+ procedures: procedures,
44
+ constants: [constants].flatten.compact,
45
+ variables: [variables].flatten.compact,
46
+ statements: statements.compact,
47
+ where_rules: [where_rules].flatten.compact,
48
+ )
49
+ end
50
+
51
+ private
52
+
53
+ def build_applies_to(head)
54
+ return [] unless head && head[:list_of_entity_ref]
55
+
56
+ entity_refs = head[:list_of_entity_ref]
57
+ refs = []
58
+
59
+ # entity_refs can be:
60
+ # - A Hash with entity_ref key (single entity)
61
+ # - An Array of Hashes (multiple entities)
62
+ if entity_refs.is_a?(Array)
63
+ entity_refs.each do |item|
64
+ if item[:entity_ref]
65
+ refs << Builder.build({ entity_ref: item[:entity_ref] })
66
+ end
67
+ end
68
+ elsif entity_refs.is_a?(Hash)
69
+ if entity_refs[:entity_ref]
70
+ refs << Builder.build({ entity_ref: entity_refs[:entity_ref] })
71
+ end
72
+ if entity_refs[:item]
73
+ [entity_refs[:item]].flatten.each do |item|
74
+ if item[:entity_ref]
75
+ refs << Builder.build({ entity_ref: item[:entity_ref] })
76
+ end
77
+ end
78
+ end
79
+ end
80
+ refs.compact
81
+ end
82
+
83
+ def build_constant_decl(data)
84
+ Builder.build_children(data[:constant_body])
85
+ end
86
+
87
+ def build_local_decl(data)
88
+ Builder.build_children(data[:local_variable]).flatten.compact
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ Builder.register(:rule_decl, Expressir::Express::Builders::RuleDeclBuilder.new)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers"
4
+
5
+ module Expressir
6
+ module Express
7
+ module Builders
8
+ # Builds schema_body_declaration nodes - dispatches to declaration or rule.
9
+ class SchemaBodyDeclBuilder
10
+ def call(ast_data)
11
+ if ast_data[:declaration]
12
+ Builder.build({ declaration: ast_data[:declaration] })
13
+ elsif ast_data[:rule_decl]
14
+ Builder.build({ rule_decl: ast_data[:rule_decl] })
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ Builder.register(:schema_body_declaration, Expressir::Express::Builders::SchemaBodyDeclBuilder.new)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers"
4
+
5
+ module Expressir
6
+ module Express
7
+ module Builders
8
+ # Builds schema_decl nodes into Schema objects.
9
+ class SchemaDeclBuilder
10
+ include Helpers
11
+
12
+ def call(ast_data)
13
+ id = Builder.build_optional(ast_data[:schema_id])
14
+ version = if ast_data[:schema_version_id]
15
+ Builder.build({ schema_version_id: ast_data[:schema_version_id] })
16
+ end
17
+
18
+ interfaces = []
19
+ constants = []
20
+ declarations = []
21
+
22
+ if ast_data[:schema_body]
23
+ interfaces = Builder.build_children(ast_data[:schema_body][:interface_specification])
24
+ constants = build_constants(ast_data[:schema_body][:constant_decl])
25
+ body_decls = ast_data[:schema_body][:schema_body_declaration]
26
+ declarations = Builder.build_children(body_decls) if body_decls
27
+ end
28
+
29
+ types = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::Type) }
30
+ entities = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::Entity) }
31
+ subtype_constraints = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::SubtypeConstraint) }
32
+ functions = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::Function) }
33
+ rules = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::Rule) }
34
+ procedures = declarations.select { |x| x.is_a?(Expressir::Model::Declarations::Procedure) }
35
+
36
+ Expressir::Model::Declarations::Schema.new(
37
+ id: id,
38
+ version: version,
39
+ interfaces: interfaces.compact,
40
+ constants: [constants].flatten.compact,
41
+ types: types,
42
+ entities: entities,
43
+ subtype_constraints: subtype_constraints,
44
+ functions: functions,
45
+ rules: rules,
46
+ procedures: procedures,
47
+ )
48
+ end
49
+
50
+ private
51
+
52
+ def build_constants(data)
53
+ return [] unless data
54
+
55
+ Builder.build_children(data[:constant_body])
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ Builder.register(:schema_decl, Expressir::Express::Builders::SchemaDeclBuilder.new)
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers"
4
+
5
+ module Expressir
6
+ module Express
7
+ module Builders
8
+ # Builds schema_version_id nodes into SchemaVersion objects.
9
+ class SchemaVersionBuilder
10
+ include Helpers
11
+
12
+ def call(ast_data)
13
+ string_val = Builder.build_optional(ast_data[:string_literal])
14
+ value = string_val.is_a?(Expressir::Model::Literals::String) ? string_val.value : string_val.to_s
15
+
16
+ items = nil
17
+ if value.start_with?("{") && value.end_with?("}")
18
+ parts = value[1..-2].split
19
+ items = parts.map do |part|
20
+ if (match = part.match(/^(.+)\((\d+)\)$/))
21
+ Expressir::Model::Declarations::SchemaVersionItem.new(
22
+ name: match[1], value: match[2],
23
+ )
24
+ elsif /^\d+$/.match?(part)
25
+ Expressir::Model::Declarations::SchemaVersionItem.new(value: part)
26
+ else
27
+ Expressir::Model::Declarations::SchemaVersionItem.new(name: part)
28
+ end
29
+ end
30
+ end
31
+
32
+ Expressir::Model::Declarations::SchemaVersion.new(value: value,
33
+ items: items)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ Builder.register(:schema_version_id, Expressir::Express::Builders::SchemaVersionBuilder.new)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers"
4
+
5
+ module Expressir
6
+ module Express
7
+ module Builders
8
+ # Builds simple ID nodes (simple_id, schema_id, entity_id, etc.)
9
+ # These return a string, not a Model object.
10
+ class SimpleIdBuilder
11
+ include Helpers
12
+
13
+ def call(ast_data)
14
+ extract_text(ast_data[:str]) || extract_text(ast_data[:simple_id]&.dig(:str))
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ # Register for all ID types
22
+ %i[simple_id schema_id entity_id type_id function_id procedure_id
23
+ rule_id rule_label_id constant_id parameter_id variable_id
24
+ enumeration_id subtype_constraint_id type_label_id attribute_id].each do |id_type|
25
+ Builder.register(id_type, Expressir::Express::Builders::SimpleIdBuilder.new)
26
+ end
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers"
4
+
5
+ module Expressir
6
+ module Express
7
+ module Builders
8
+ # Builds statement nodes (assignment, if, case, repeat, etc.).
9
+ class StatementBuilder
10
+ include Helpers
11
+
12
+ # stmt - choice dispatcher
13
+ def build_stmt(ast_data)
14
+ if ast_data[:assignment_stmt]
15
+ Builder.build({ assignment_stmt: ast_data[:assignment_stmt] })
16
+ elsif ast_data[:alias_stmt]
17
+ Builder.build({ alias_stmt: ast_data[:alias_stmt] })
18
+ elsif ast_data[:if_stmt]
19
+ Builder.build({ if_stmt: ast_data[:if_stmt] })
20
+ elsif ast_data[:case_stmt]
21
+ Builder.build({ case_stmt: ast_data[:case_stmt] })
22
+ elsif ast_data[:compound_stmt]
23
+ Builder.build({ compound_stmt: ast_data[:compound_stmt] })
24
+ elsif ast_data[:repeat_stmt]
25
+ Builder.build({ repeat_stmt: ast_data[:repeat_stmt] })
26
+ elsif ast_data[:return_stmt]
27
+ Builder.build({ return_stmt: ast_data[:return_stmt] })
28
+ elsif ast_data[:escape_stmt]
29
+ Builder.build({ escape_stmt: ast_data[:escape_stmt] })
30
+ elsif ast_data[:skip_stmt]
31
+ Builder.build({ skip_stmt: ast_data[:skip_stmt] })
32
+ elsif ast_data[:null_stmt]
33
+ Builder.build({ null_stmt: ast_data[:null_stmt] })
34
+ elsif ast_data[:procedure_call_stmt]
35
+ Builder.build({ procedure_call_stmt: ast_data[:procedure_call_stmt] })
36
+ end
37
+ end
38
+
39
+ def build_assignment_stmt(ast_data)
40
+ ref = Builder.build_optional(ast_data[:general_ref])
41
+ expression = Builder.build_optional(ast_data[:expression])
42
+
43
+ if ast_data[:qualifier]
44
+ [ast_data[:qualifier]].flatten.compact.each do |qual|
45
+ qual_result = Builder.build({ qualifier: qual })
46
+ ref = apply_qualifier(ref, qual_result)
47
+ end
48
+ end
49
+
50
+ Expressir::Model::Statements::Assignment.new(ref: ref,
51
+ expression: expression)
52
+ end
53
+
54
+ def build_alias_stmt(ast_data)
55
+ var_id = Builder.build_optional(ast_data[:variable_id])
56
+ expression = Builder.build_optional(ast_data[:general_ref])
57
+ statements = Builder.build_children(ast_data[:stmt])
58
+
59
+ if ast_data[:qualifier] && expression
60
+ [ast_data[:qualifier]].flatten.compact.each do |qual|
61
+ qual_result = Builder.build({ qualifier: qual })
62
+ expression = apply_qualifier(expression, qual_result)
63
+ end
64
+ end
65
+
66
+ Expressir::Model::Statements::Alias.new(
67
+ id: var_id,
68
+ expression: expression,
69
+ statements: statements.compact,
70
+ )
71
+ end
72
+
73
+ def build_if_stmt(ast_data)
74
+ expression = Builder.build_optional(ast_data[:logical_expression])
75
+ then_stmts = Builder.build_children(ast_data[:if_stmt_statements]&.dig(:stmt))
76
+ else_stmts = Builder.build_children(ast_data[:if_stmt_else_statements]&.dig(:stmt))
77
+
78
+ Expressir::Model::Statements::If.new(
79
+ expression: expression,
80
+ statements: then_stmts.compact,
81
+ else_statements: else_stmts.compact,
82
+ )
83
+ end
84
+
85
+ def build_case_stmt(ast_data)
86
+ selector = Builder.build_optional(ast_data[:selector])
87
+ actions = Builder.build_children(ast_data[:case_action])
88
+ otherwise = Builder.build_children(ast_data[:otherwise]&.dig(:stmt))
89
+
90
+ Expressir::Model::Statements::Case.new(
91
+ expression: selector,
92
+ actions: actions.compact,
93
+ otherwise: otherwise.compact,
94
+ )
95
+ end
96
+
97
+ def build_selector(ast_data)
98
+ Builder.build_optional(ast_data[:expression])
99
+ end
100
+
101
+ def build_case_action(ast_data)
102
+ labels = Builder.build_children(ast_data[:case_label])
103
+ statements = Builder.build_children(ast_data[:stmt])
104
+
105
+ Expressir::Model::Statements::CaseAction.new(
106
+ labels: labels.compact,
107
+ statements: statements.compact,
108
+ )
109
+ end
110
+
111
+ def build_case_label(ast_data)
112
+ Builder.build_optional(ast_data[:expression])
113
+ end
114
+
115
+ def build_compound_stmt(ast_data)
116
+ statements = Builder.build_children(ast_data[:stmt])
117
+ Expressir::Model::Statements::Compound.new(statements: statements.compact)
118
+ end
119
+
120
+ def build_repeat_stmt(ast_data)
121
+ control = ast_data[:repeat_control]
122
+ statements = Builder.build_children(ast_data[:stmt])
123
+
124
+ var_id = nil
125
+ bound1 = nil
126
+ bound2 = nil
127
+ increment = nil
128
+
129
+ increment_control = control[:increment_control] if control.is_a?(Hash)
130
+ if increment_control.is_a?(Hash)
131
+ var_id = Builder.build_optional(increment_control[:variable_id])
132
+ bound1 = Builder.build_optional(increment_control[:bound1])
133
+ bound2 = Builder.build_optional(increment_control[:bound2])
134
+ increment = Builder.build_optional(increment_control[:increment])
135
+ end
136
+
137
+ while_ctrl = control.is_a?(Hash) ? control[:while_control] : nil
138
+ until_ctrl = control.is_a?(Hash) ? control[:until_control] : nil
139
+
140
+ while_expression = if while_ctrl.is_a?(Hash)
141
+ Builder.build_optional(while_ctrl[:logical_expression])
142
+ end
143
+ until_expression = if until_ctrl.is_a?(Hash)
144
+ Builder.build_optional(until_ctrl[:logical_expression])
145
+ end
146
+
147
+ Expressir::Model::Statements::Repeat.new(
148
+ id: var_id,
149
+ bound1: bound1,
150
+ bound2: bound2,
151
+ increment: increment,
152
+ while_expression: while_expression,
153
+ until_expression: until_expression,
154
+ statements: statements.compact,
155
+ )
156
+ end
157
+
158
+ def build_increment_control(_ast_data)
159
+ nil
160
+ end
161
+
162
+ def build_increment(ast_data)
163
+ Builder.build_optional(ast_data[:numeric_expression])
164
+ end
165
+
166
+ def build_while_control(ast_data)
167
+ Builder.build_optional(ast_data[:logical_expression])
168
+ end
169
+
170
+ def build_until_control(ast_data)
171
+ Builder.build_optional(ast_data[:logical_expression])
172
+ end
173
+
174
+ def build_return_stmt(ast_data)
175
+ expression = Builder.build_optional(ast_data[:expression])
176
+ Expressir::Model::Statements::Return.new(expression: expression)
177
+ end
178
+
179
+ def build_escape_stmt(_ast_data)
180
+ Expressir::Model::Statements::Escape.new
181
+ end
182
+
183
+ def build_skip_stmt(_ast_data)
184
+ Expressir::Model::Statements::Skip.new
185
+ end
186
+
187
+ def build_null_stmt(_ast_data)
188
+ Expressir::Model::Statements::Null.new
189
+ end
190
+
191
+ def build_procedure_call_stmt(ast_data)
192
+ proc_ref = if ast_data[:procedure_ref]
193
+ Builder.build({ procedure_ref: ast_data[:procedure_ref] })
194
+ elsif ast_data[:built_in_procedure]
195
+ Builder.build({ built_in_procedure: ast_data[:built_in_procedure] })
196
+ end
197
+ params = if ast_data[:actual_parameter_list]
198
+ Builder.build({ actual_parameter_list: ast_data[:actual_parameter_list] })
199
+ else
200
+ []
201
+ end
202
+
203
+ Expressir::Model::Statements::ProcedureCall.new(
204
+ procedure: proc_ref,
205
+ parameters: [params].flatten.compact,
206
+ )
207
+ end
208
+
209
+ def build_type_label(ast_data)
210
+ if ast_data[:type_label_id]
211
+ Builder.build({ type_label_id: ast_data[:type_label_id] })
212
+ elsif ast_data[:type_label_ref]
213
+ Builder.build({ type_label_ref: ast_data[:type_label_ref] })
214
+ end
215
+ end
216
+
217
+ def build_type_label_ref(ast_data)
218
+ id = extract_text(ast_data[:str] || ast_data[:type_label_id]&.dig(:str))
219
+ Expressir::Model::References::SimpleReference.new(id: id)
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ builder = Expressir::Express::Builders::StatementBuilder.new
227
+
228
+ Builder.register(:stmt) { |d| builder.build_stmt(d) }
229
+ Builder.register(:assignment_stmt) { |d| builder.build_assignment_stmt(d) }
230
+ Builder.register(:alias_stmt) { |d| builder.build_alias_stmt(d) }
231
+ Builder.register(:if_stmt) { |d| builder.build_if_stmt(d) }
232
+ Builder.register(:case_stmt) { |d| builder.build_case_stmt(d) }
233
+ Builder.register(:selector) { |d| builder.build_selector(d) }
234
+ Builder.register(:case_action) { |d| builder.build_case_action(d) }
235
+ Builder.register(:case_label) { |d| builder.build_case_label(d) }
236
+ Builder.register(:compound_stmt) { |d| builder.build_compound_stmt(d) }
237
+ Builder.register(:repeat_stmt) { |d| builder.build_repeat_stmt(d) }
238
+ Builder.register(:increment_control) { |d| builder.build_increment_control(d) }
239
+ Builder.register(:increment) { |d| builder.build_increment(d) }
240
+ Builder.register(:while_control) { |d| builder.build_while_control(d) }
241
+ Builder.register(:until_control) { |d| builder.build_until_control(d) }
242
+ Builder.register(:return_stmt) { |d| builder.build_return_stmt(d) }
243
+ Builder.register(:escape_stmt) { |d| builder.build_escape_stmt(d) }
244
+ Builder.register(:skip_stmt) { |d| builder.build_skip_stmt(d) }
245
+ Builder.register(:null_stmt) { |d| builder.build_null_stmt(d) }
246
+ Builder.register(:procedure_call_stmt) do |d|
247
+ builder.build_procedure_call_stmt(d)
248
+ end
249
+ Builder.register(:type_label) { |d| builder.build_type_label(d) }
250
+ Builder.register(:type_label_ref) { |d| builder.build_type_label_ref(d) }