expressir 2.4.35-arm-linux → 2.4.37-arm-linux

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/conformance.yml +34 -0
  3. data/.gitignore +3 -0
  4. data/Gemfile +1 -1
  5. data/TODO.sota/00-overview.md +57 -0
  6. data/TODO.sota/01-type-checking.md +115 -0
  7. data/TODO.sota/02-evaluator.md +86 -0
  8. data/TODO.sota/03-conformance.md +53 -0
  9. data/TODO.sota/04-lsp.md +70 -0
  10. data/TODO.sota/05-interop.md +55 -0
  11. data/TODO.sota/06-dialects.md +57 -0
  12. data/TODO.sota/07-reference-artifacts.md +55 -0
  13. data/docs/_guides/interop/index.adoc +25 -0
  14. data/docs/_guides/interop/json-schema.adoc +88 -0
  15. data/docs/_guides/interop/owl-rdf.adoc +87 -0
  16. data/docs/_guides/reference/checker-notes.adoc +55 -0
  17. data/docs/_guides/reference/checker.adoc +36 -0
  18. data/docs/_guides/reference/evaluator.adoc +40 -0
  19. data/docs/_guides/reference/grammar-crosswalk.adoc +299 -0
  20. data/docs/_guides/reference/index.adoc +22 -0
  21. data/docs/_guides/reference/lsp.adoc +41 -0
  22. data/docs/_guides/reference/p21.adoc +30 -0
  23. data/docs/_guides/reference/shtolo.adoc +36 -0
  24. data/exe/expressir-lsp +6 -0
  25. data/expressir.gemspec +2 -2
  26. data/lib/expressir/3.2/expressir_core.so +0 -0
  27. data/lib/expressir/3.3/expressir_core.so +0 -0
  28. data/lib/expressir/3.4/expressir_core.so +0 -0
  29. data/lib/expressir/4.0/expressir_core.so +0 -0
  30. data/lib/expressir/cli.rb +18 -0
  31. data/lib/expressir/commands/check.rb +3 -1
  32. data/lib/expressir/commands/export_json_schema.rb +29 -0
  33. data/lib/expressir/commands/export_owl.rb +26 -0
  34. data/lib/expressir/commands/validate.rb +3 -0
  35. data/lib/expressir/commands.rb +2 -0
  36. data/lib/expressir/conformance/trend.rb +100 -0
  37. data/lib/expressir/conformance.rb +8 -0
  38. data/lib/expressir/eval/environment.rb +83 -0
  39. data/lib/expressir/eval/expression.rb +610 -0
  40. data/lib/expressir/eval/interpreter.rb +316 -0
  41. data/lib/expressir/eval/rules.rb +88 -0
  42. data/lib/expressir/eval/values.rb +74 -0
  43. data/lib/expressir/eval.rb +17 -0
  44. data/lib/expressir/express/builders/expression_builder.rb +9 -1
  45. data/lib/expressir/express/checker.rb +779 -2
  46. data/lib/expressir/express/dialect.rb +44 -0
  47. data/lib/expressir/express/formatters/expressions_formatter.rb +4 -1
  48. data/lib/expressir/express/parser.rb +9 -4
  49. data/lib/expressir/express.rb +1 -0
  50. data/lib/expressir/interop/json_schema.rb +195 -0
  51. data/lib/expressir/interop/owl.rb +216 -0
  52. data/lib/expressir/interop.rb +13 -0
  53. data/lib/expressir/lsp/document.rb +226 -0
  54. data/lib/expressir/lsp/server.rb +184 -0
  55. data/lib/expressir/lsp/workspace.rb +402 -0
  56. data/lib/expressir/lsp.rb +11 -0
  57. data/lib/expressir/mapping/refpath.rb +65 -4
  58. data/lib/expressir/reference/grammar_crosswalk.rb +122 -0
  59. data/lib/expressir/reference/grammar_crosswalk.yml +800 -0
  60. data/lib/expressir/reference/note_catalog.rb +54 -0
  61. data/lib/expressir/reference.rb +10 -0
  62. data/lib/expressir/version.rb +1 -1
  63. data/lib/expressir.rb +4 -0
  64. data/lib/tasks/conformance.rake +27 -0
  65. data/lib/tasks/reference.rake +25 -0
  66. metadata +50 -4
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: Part 21
3
+ parent: Reference
4
+ grand_parent: Guides
5
+ nav_order: 4
6
+ ---
7
+
8
+ = Part 21
9
+
10
+ == Purpose
11
+
12
+ `Expressir::P21` reads ISO 10303-21 exchange structures: header
13
+ entities, simple and complex instances, nested lists, references,
14
+ typed parameters, enumerations and binaries.
15
+
16
+ == Validation
17
+
18
+ `Expressir::P21::Validator.validate(population, schema, short_names:)`
19
+ maps Part 21 records to EXPRESS attributes in clause 12 order —
20
+ supertype attributes first, derived slots valued `*`, inverse
21
+ attributes unmapped — and reports arity, unknown-entity,
22
+ unknown-reference, enum-value and derived-slot notes. Short names from
23
+ clause 12.3 are passed as a `{"CPT" => "cartesian_point"}` map.
24
+
25
+ == Conformance corpus
26
+
27
+ The annex examples from the ISO 10303-21 detachment live under
28
+ `spec/fixtures/p21/conformance/`; the gate spec parses each positive
29
+ fixture with zero notes and asserts the intended loud failure for
30
+ ladder-excluded forms.
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: SHTOLO and Annex G
3
+ parent: Reference
4
+ grand_parent: Guides
5
+ nav_order: 5
6
+ ---
7
+
8
+ = SHTOLO and Annex G
9
+
10
+ == Purpose
11
+
12
+ `Expressir::Express::Shtolo` converts a multi-schema shortform EXPRESS
13
+ model into an ISO 10303-11:1994 longform. It implements the two stages
14
+ of Annex G: multi-schema to an intermediate single artifact schema
15
+ (stage 1), then artifact to 1994-only constructs (stage 2).
16
+
17
+ == Rule coverage
18
+
19
+ Every Annex G rule has a live worked example in
20
+ `spec/expressir/express/shtolo_annex_g_rule_audit_spec.rb`. The
21
+ extracted rule list, the fixture that exercises each rule and the
22
+ eengine coverage verdict are in
23
+ `spec/fixtures/shtolo/annex-g-rules.md`.
24
+
25
+ Key rules: name munging (G.NM.1), USE wins over REFERENCE (G.1.2),
26
+ original-name copying for aliases (G.1.3), dependently-instantiable
27
+ rules (G.1.6), implicit interface closure (G.1.7), pruning (G.1.9),
28
+ extensible enumeration/select folding (G.2.3/G.2.4) and subtype
29
+ constraint removal (G.2.5). Where eeng and the annex disagree, the
30
+ annex wins; eeng output is a comparison input, not the contract.
31
+
32
+ == Structural safety
33
+
34
+ `Expressir::Express::SchemaCompare`/`SchemaPatch` provide the
35
+ declaration-level diff used to verify that a generated longform has
36
+ exactly the expected structural deltas.
data/exe/expressir-lsp ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require_relative "../lib/expressir"
5
+
6
+ exit Expressir::Lsp::Server.new.run
data/expressir.gemspec CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  spec.license = "BSD-2-Clause"
16
16
 
17
17
  spec.bindir = "exe"
18
- spec.executables = %w[expressir]
18
+ spec.executables = %w[expressir expressir-lsp]
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.metadata["homepage_uri"] = spec.homepage
@@ -47,7 +47,7 @@ Gem::Specification.new do |spec|
47
47
  spec.add_dependency "benchmark-ips"
48
48
  spec.add_dependency "csv"
49
49
  spec.add_dependency "liquid", "~> 5"
50
- spec.add_dependency "lutaml-model", "~> 0.8.76"
50
+ spec.add_dependency "lutaml-model", "~> 0.8.114"
51
51
  spec.add_dependency "moxml"
52
52
  spec.add_dependency "nokogiri", "~> 1.19"
53
53
  spec.add_dependency "paint"
Binary file
Binary file
Binary file
Binary file
data/lib/expressir/cli.rb CHANGED
@@ -111,6 +111,24 @@ module Expressir
111
111
  Expressir::Commands::Xsd.new(options).run(path)
112
112
  end
113
113
 
114
+ desc "export-json-schema PATH",
115
+ "Write a JSON Schema rendering of the first EXPRESS schema at PATH"
116
+ method_option :output, type: :string, aliases: "-o",
117
+ desc: "Output file path (defaults to stdout)"
118
+ def export_json_schema(path)
119
+ Expressir::Commands::ExportJsonSchema.new(options).run(path)
120
+ end
121
+
122
+ desc "export-owl PATH",
123
+ "Write an OWL/Turtle rendering of the first EXPRESS schema at PATH"
124
+ method_option :output, type: :string, aliases: "-o",
125
+ desc: "Output file path (defaults to stdout)"
126
+ method_option :base_iri, type: :string,
127
+ desc: "Ontology IRI base (default: expressir.org/owl/<schema>#)"
128
+ def export_owl(path)
129
+ Expressir::Commands::ExportOwl.new(options).run(path)
130
+ end
131
+
114
132
  desc "xml-index DIR", "Write the SMRL index XML for every EXPRESS schema under DIR"
115
133
  method_option :output, type: :string, aliases: "-o",
116
134
  desc: "Output file path (defaults to stdout)"
@@ -25,7 +25,9 @@ module Expressir
25
25
  raise Thor::Error, "#{failed} file(s) failed to parse"
26
26
  end
27
27
 
28
- result = Expressir::Express::Checker.new(repository).check
28
+ result = Expressir::Express::Checker.new(
29
+ repository, dialect: options[:dialect]
30
+ ).check
29
31
 
30
32
  if options[:json]
31
33
  say JSON.generate(
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Commands
5
+ # `expressir export-json-schema`: write the JSON Schema rendering of
6
+ # the first schema in PATH.
7
+ class ExportJsonSchema < Base
8
+ def run(path)
9
+ repository = Expressir::Express::Parser.from_file(path)
10
+ schema = repository.schemas.first
11
+ raise Thor::Error, "no schema in #{path}" unless schema
12
+
13
+ document = Expressir::Interop::JsonSchema.new(schema).to_json
14
+ write(document)
15
+ end
16
+
17
+ private
18
+
19
+ def write(document)
20
+ if options[:output]
21
+ File.write(options[:output], document)
22
+ say "written to #{options[:output]}"
23
+ else
24
+ $stdout.write(document)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Commands
5
+ # `expressir export-owl`: write the OWL/Turtle rendering of the
6
+ # first schema in PATH.
7
+ class ExportOwl < Base
8
+ def run(path)
9
+ repository = Expressir::Express::Parser.from_file(path)
10
+ schema = repository.schemas.first
11
+ raise Thor::Error, "no schema in #{path}" unless schema
12
+
13
+ document = Expressir::Interop::Owl.new(
14
+ schema,
15
+ base_iri: options[:base_iri],
16
+ ).export
17
+ if options[:output]
18
+ File.write(options[:output], document)
19
+ say "written to #{options[:output]}"
20
+ else
21
+ $stdout.write(document)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -98,6 +98,9 @@ module Expressir
98
98
  method_option :stepmod, type: :string,
99
99
  desc: "STEPmod checkout root — explicit opt-in to directory-" \
100
100
  "convention resolution as the fallback"
101
+ method_option :dialect, type: :string,
102
+ desc: "ISO 10303-11 edition for dialect diagnostics: " \
103
+ "'1994' or '2004' (default: no edition notes)"
101
104
  def check(*paths)
102
105
  Check.new(options).run(*paths)
103
106
  end
@@ -26,6 +26,8 @@ module Expressir
26
26
  autoload :Manifest, "#{__dir__}/commands/manifest"
27
27
  autoload :MappingValidate, "#{__dir__}/commands/mapping_validate"
28
28
  autoload :Xsd, "#{__dir__}/commands/xsd"
29
+ autoload :ExportJsonSchema, "#{__dir__}/commands/export_json_schema"
30
+ autoload :ExportOwl, "#{__dir__}/commands/export_owl"
29
31
  autoload :SmrlXmlIndex, "#{__dir__}/commands/smrl_xml_index"
30
32
  end
31
33
  end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Expressir
6
+ module Conformance
7
+ # Trended property metrics over a corpus slice: parse rate and
8
+ # format idempotence rate are fractions; the diagnostic rate is the
9
+ # number of Checker notes per parsed file. Per-note-id counts make
10
+ # the report actionable. Published by the nightly conformance
11
+ # workflow as a JSON artifact so the numbers can be trended.
12
+ class Trend
13
+ def self.measure(files)
14
+ report = {
15
+ "files" => files.size,
16
+ "parsed" => 0,
17
+ "parse_failed" => 0,
18
+ "measurement_failed" => 0,
19
+ "idempotent" => 0,
20
+ "not_idempotent" => 0,
21
+ "notes" => 0,
22
+ "errors" => 0,
23
+ "warnings" => 0,
24
+ "note_ids" => {},
25
+ }
26
+ files.each do |path|
27
+ measure_file(path, report)
28
+ end
29
+ report.merge!(
30
+ "parse_rate" => rate(report["parsed"], report["files"]),
31
+ "idempotence_rate" => rate(report["idempotent"], report["parsed"]),
32
+ "diagnostic_rate" => rate(report["notes"], report["parsed"]),
33
+ )
34
+ report
35
+ end
36
+
37
+ def self.run(files, output:)
38
+ report = measure(files)
39
+ File.write(output, "#{JSON.pretty_generate(report)}\n")
40
+ report
41
+ end
42
+
43
+ def self.measure_file(path, report)
44
+ repository = parse_file(path)
45
+ unless repository
46
+ report["parse_failed"] += 1
47
+ return
48
+ end
49
+ report["parsed"] += 1
50
+ measure_properties(repository, report)
51
+ end
52
+ private_class_method :measure_file
53
+
54
+ def self.parse_file(path)
55
+ Expressir::Express::Parser.from_exp(File.read(path))
56
+ rescue StandardError
57
+ nil
58
+ end
59
+ private_class_method :parse_file
60
+
61
+ def self.measure_properties(repository, report)
62
+ schema = Array(repository.schemas).first
63
+ if schema && idempotent?(schema)
64
+ report["idempotent"] += 1
65
+ else
66
+ report["not_idempotent"] += 1
67
+ end
68
+ notes = Expressir::Express::Checker.new(
69
+ Expressir::Model::Repository.new(files: [repository]),
70
+ ).check.notes
71
+ report["notes"] += notes.size
72
+ notes.each do |note|
73
+ report[note.severity == :error ? "errors" : "warnings"] += 1
74
+ report["note_ids"][note.id.to_s] =
75
+ report["note_ids"].fetch(note.id.to_s, 0) + 1
76
+ end
77
+ rescue StandardError
78
+ report["measurement_failed"] += 1
79
+ end
80
+ private_class_method :measure_properties
81
+
82
+ def self.idempotent?(schema)
83
+ once = Expressir::Express::Formatter.format(schema)
84
+ reparsed = Expressir::Express::Parser.from_exp(once)
85
+ twice = Expressir::Express::Formatter.format(reparsed.schemas.first)
86
+ once == twice
87
+ rescue StandardError
88
+ false
89
+ end
90
+ private_class_method :idempotent?
91
+
92
+ def self.rate(numerator, denominator)
93
+ return 0.0 if denominator.zero?
94
+
95
+ (numerator.to_f / denominator).round(4)
96
+ end
97
+ private_class_method :rate
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ # Measurement tiers over the standards corpus (TODO.sota/07 part 2).
5
+ module Conformance
6
+ autoload :Trend, "#{__dir__}/conformance/trend"
7
+ end
8
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Expressir
4
+ module Eval
5
+ # Lexical scope chain: a name resolves to a value through the
6
+ # innermost scope that declares it. The schema provides type
7
+ # context; the population (step 3) provides instances for the
8
+ # instance-aware builtins.
9
+ class Environment
10
+ attr_reader :schema, :population
11
+
12
+ def initialize(schema:, population: nil, frames: [{}])
13
+ @schema = schema
14
+ @population = population
15
+ @frames = frames
16
+ end
17
+
18
+ def push(frame)
19
+ Environment.new(schema: @schema, population: @population,
20
+ frames: @frames + [frame])
21
+ end
22
+
23
+ def define(name, value)
24
+ frame = @frames.last.dup
25
+ frame[name.safe_downcase] = value
26
+ Environment.new(schema: @schema, population: @population,
27
+ frames: @frames[0..-2] + [frame])
28
+ end
29
+
30
+ def lookup(name)
31
+ key = name.safe_downcase
32
+ @frames.reverse_each do |frame|
33
+ return frame[key] if frame.key?(key)
34
+ end
35
+ nil
36
+ end
37
+
38
+ def bound?(name)
39
+ !lookup(name).nil? || @frames.any? { |f| f.key?(name.safe_downcase) }
40
+ end
41
+
42
+ def interpreter
43
+ @interpreter ||= Interpreter.new(schema: @schema,
44
+ population: @population)
45
+ end
46
+
47
+ # Part 21 attribute slots for an entity: inherited explicit
48
+ # attributes (supertype closure order) then the entity's own —
49
+ # the same order the P21 validator maps values by.
50
+ def slots_for(entity)
51
+ @slots ||= {}
52
+ @slots[entity] ||= begin
53
+ inherited = entity.supertype_closure.flat_map do |supertype|
54
+ explicit_attributes(supertype)
55
+ end
56
+ (inherited + explicit_attributes(entity)).each_with_index
57
+ .to_h do |attribute, index|
58
+ [attribute.id&.safe_downcase, index]
59
+ end
60
+ end
61
+ end
62
+
63
+ def explicit_attributes(entity)
64
+ Array(entity.attributes)
65
+ .select { |a| a.instance_of?(Model::Declarations::Attribute) }
66
+ end
67
+
68
+ def entity_for_keyword(keyword)
69
+ Array(@schema&.entities).find do |entity|
70
+ entity.id&.safe_downcase == keyword&.safe_downcase
71
+ end
72
+ end
73
+
74
+ def instance_for(reference)
75
+ return nil unless @population && reference.is_a?(String)
76
+ return nil unless reference.start_with?("#")
77
+
78
+ record = @population.instances[reference]
79
+ record && Values::Instance.new(id: reference[1..], record: record)
80
+ end
81
+ end
82
+ end
83
+ end