mbt-gen 0.0.6 → 0.0.8

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 (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mbt-gen.rb +168 -38
  3. data/lib/solver-lib.rb +5 -1
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d73853b99bf4d38c5c621765537531713eca02dcf357d2b2c004514c66fa78f7
4
- data.tar.gz: 99b8a8490a1a33662b5a044cb466fdb95875dfe2e1ba8f031065d6a58dc170c7
3
+ metadata.gz: 763d26c653134a8a5432753091e286e1df8aecf8c603a5eba5bc6980ed5c3e6f
4
+ data.tar.gz: e5e86c5368af05ff682fbd2f7bf7fed6a9e7909d7d92523728956ee3fee71946
5
5
  SHA512:
6
- metadata.gz: 11787320c7d53d3f0d14686f0d1224daee62522e6298175a02c3c1ad11f30f8ed564526fa773599125b2ea9c5cffd647298ac50449495661ef5d5f3d078ba5ce
7
- data.tar.gz: 45aef9d1c6c316aa145c1cc50a88319ae50390d676df64bb5abf7b86a5baf0612f19b27c347e44efeba1bf5c1f15c3f76e1e5fae0779f84df4145b13561ee92b
6
+ metadata.gz: 5c32f100cae06aa9f7e97d7e1b214f27a9d0747948feafddd01530392f45df0a9966e4f6ad3ef5be9a719b87f9e0bd91f966523415231b539026503aa1659064
7
+ data.tar.gz: 485bd1dfe0f01f7e553087603c90e73c582b7dd29de72cd79032f63f1b35df32af15c66369c540507ae4ed8427244159710076221dcf033696e7ce8732191ff7
data/lib/mbt-gen.rb CHANGED
@@ -596,6 +596,15 @@ def translate_validation_rule_to_SMTLIB(rule, context_struct, prefix, config, op
596
596
  field = resolve_field_path($1, context_struct, prefix)
597
597
  filled_var_for_field(field)
598
598
  end
599
+ .gsub(/LIST_VALUE\[[^\]]*,[^\]]*\]/) do |m|
600
+ unless m =~ /LIST_VALUE\[([^\]]*),([^\]]*)\]/ then
601
+ raise RuntimeError.new("could not parse LIST_VALUE_FIRST_ELEMENT[...]: #{m.inspect} in validation rule #{rule_name}")
602
+ end
603
+ xpath = $1
604
+ index = Integer($2)
605
+ l = resolve_field_path(xpath, context_struct, prefix)
606
+ value_var_for_list(l, index)
607
+ end
599
608
  .gsub(/VALUE\[[^\]]*\]/) do |m|
600
609
  unless m =~ /VALUE\[([^\]]*)\]/ then
601
610
  raise RuntimeError.new("could not parse VALUE[...]: #{m.inspect} in validation rule #{rule_name}")
@@ -630,15 +639,6 @@ def translate_validation_rule_to_SMTLIB(rule, context_struct, prefix, config, op
630
639
  end
631
640
  "(or #{terms.join(" ")})"
632
641
  end
633
- .gsub(/LIST_VALUE\[[^\]]*,[^\]]*\]/) do |m|
634
- unless m =~ /LIST_VALUE\[([^\]]*),([^\]]*)\]/ then
635
- raise RuntimeError.new("could not parse LIST_VALUE_FIRST_ELEMENT[...]: #{m.inspect} in validation rule #{rule_name}")
636
- end
637
- xpath = $1
638
- index = Integer($2)
639
- l = resolve_field_path(xpath, context_struct, prefix)
640
- value_var_for_list(l, index)
641
- end
642
642
  .gsub(/LIST_VALUE_FIRST_ELEMENT\[[^\]]*\]/) do |m| # DEPRECATED! Please use LIST_VALUE[...] instead.
643
643
  unless m =~ /LIST_VALUE_FIRST_ELEMENT\[([^\]]*)\]/ then
644
644
  raise RuntimeError.new("could not parse LIST_VALUE_FIRST_ELEMENT[...]: #{m.inspect} in validation rule #{rule_name}")
@@ -768,9 +768,9 @@ def parse_field_value(fielddef, value)
768
768
  when :int
769
769
  return Integer(value)
770
770
  when :string
771
- return value
771
+ return solver_value_to_string(value)
772
772
  when :enum
773
- return value
773
+ return solver_value_to_string(value)
774
774
  when :date
775
775
  return Date.parse(value)
776
776
  when :timestamp
@@ -778,6 +778,10 @@ def parse_field_value(fielddef, value)
778
778
  end
779
779
  end
780
780
 
781
+ def solver_value_to_ruby_value(fielddef, solver_value)
782
+ parse_field_value(fielddef, solver_value)
783
+ end
784
+
781
785
  def solver_value_to_string(solver_value)
782
786
  if solver_value =~ /\"(.*)\"/ then
783
787
  return $1
@@ -786,6 +790,15 @@ def solver_value_to_string(solver_value)
786
790
  end
787
791
  end
788
792
 
793
+ def format_solver_value(fielddef, solver_value)
794
+ if fielddef[:formatter] then
795
+ fvalue_ruby = solver_value_to_ruby_value(fielddef, solver_value)
796
+ fvalue_str = fielddef[:formatter].call(fvalue_ruby)
797
+ else
798
+ fvalue_str = solver_value_to_string(solver_value)
799
+ end
800
+ end
801
+
789
802
  def build_document_from_model(progress, struct, model)
790
803
  doc = Nokogiri::XML(XML_TEMPLATE)
791
804
  add_model_struct_to_doc(progress, struct, doc, model)
@@ -806,7 +819,8 @@ def add_model_struct_to_doc(progress, struct, doc, model, prefix = nil)
806
819
  raise RuntimeError.new("xpath '#{xpath}' not contained in model.")
807
820
  end
808
821
  if model_value[:filled] == true || model_value[:filled] == "true" then
809
- doc.add_child("<#{key}>#{solver_value_to_string(model_value[:value])}</#{key}>")
822
+ fvalue_str = format_solver_value(value, model_value[:value])
823
+ doc.add_child("<#{key}>#{fvalue_str}</#{key}>")
810
824
  end
811
825
  elsif value[:type] == :list then
812
826
  node = doc.add_child("<#{key}></#{key}>")
@@ -1171,17 +1185,43 @@ def list_validation_rules(message)
1171
1185
  rules.each { |r| puts " #{r}" }
1172
1186
  end
1173
1187
 
1174
- def value_of_type(value, type)
1175
- if type == :string or type == :enum then
1188
+ def default_parser_for_type(value_str, datatype)
1189
+ if datatype == :string or datatype == :enum then
1190
+ value_str
1191
+ elsif datatype == :int then
1192
+ Integer(value_str)
1193
+ elsif datatype == :date then
1194
+ Date.parse(value_str)
1195
+ elsif datatype == :timestamp then
1196
+ Time.new(value_str)
1197
+ else
1198
+ raise RuntimeError.new("encountered unknown :datatype #{datatype.inspect}")
1199
+ end
1200
+ end
1201
+
1202
+ def value_of_type_to_solver(value, datatype)
1203
+ if datatype == :string || datatype == :enum then
1176
1204
  "\"#{value}\""
1177
- elsif type == :int then
1205
+ elsif datatype == :int then
1178
1206
  value.to_s
1179
- elsif type == :date then
1180
- (Date.parse(value) - Time.at(0).to_date).to_i.to_s
1181
- elsif type == :timestamp then
1182
- (Time.new(value) - Time.at(0)).to_i.to_s
1207
+ elsif datatype == :date then
1208
+ (value - Time.at(0).to_date).to_i.to_s
1209
+ elsif datatype == :timestamp then
1210
+ (value - Time.at(0)).to_i.to_s
1183
1211
  else
1184
- raise RuntimeError.new("encountered unknown :datatype #{type.inspect}")
1212
+ raise RuntimeError.new("encountered value of unknown type: #{datatype.inspect}")
1213
+ end
1214
+ end
1215
+
1216
+ def value_of_type(value, type)
1217
+ value_of_type_to_solver(default_parser_for_type(value, type), type)
1218
+ end
1219
+
1220
+ def parse_field_value_to_ruby(fielddef, fieldvalue)
1221
+ if fielddef[:parser] then
1222
+ fielddef[:parser].call(fieldvalue)
1223
+ else
1224
+ default_parser_for_type(fieldvalue, fielddef[:datatype])
1185
1225
  end
1186
1226
  end
1187
1227
 
@@ -1716,6 +1756,16 @@ def validate_model(message, prefix=nil)
1716
1756
  unless value[:datatype] then
1717
1757
  raise RuntimeError.new("Model Problem: encountered field without a :datatype (key=#{key.inspect}, value=#{value.inspect})")
1718
1758
  end
1759
+ if value[:parser] then
1760
+ unless value[:formatter] then
1761
+ raise RuntimeError.new("Model Problem: encountered field has a :parser, but no :formatter (key=#{key.inspect}, value=#{value.inspect})")
1762
+ end
1763
+ end
1764
+ if value[:formatter] then
1765
+ unless value[:parser] then
1766
+ raise RuntimeError.new("Model Problem: encountered field has a :formatter, but no :parser (key=#{key.inspect}, value=#{value.inspect})")
1767
+ end
1768
+ end
1719
1769
  if value[:datatype] == :enum then
1720
1770
  unless value[:values] then
1721
1771
  raise RuntimeError.new("Model Problem: encountered enum field without a :values (key=#{key.inspect}, value=#{value.inspect})")
@@ -1723,7 +1773,13 @@ def validate_model(message, prefix=nil)
1723
1773
  end
1724
1774
  elsif value[:type] == :list then
1725
1775
  unless value[:datatype] then
1726
- raise RuntimeError.new("Model Problem: encountered list without a :datatype (key=#{key.inspect}, value=#{value.inspect})")
1776
+ raise RuntimeError.new("Model Problem: encountered list without :datatype property (key=#{key.inspect}, value=#{value.inspect})")
1777
+ end
1778
+ unless value[:model_maxLength] then
1779
+ raise RuntimeError.new("Model Problem: encountered list without :model_maxLength property (key=#{key.inspect}, value=#{value.inspect})")
1780
+ end
1781
+ unless value[:xpath_element] then
1782
+ raise RuntimeError.new("Model Problem: encountered list without :xpath_element property (key=#{key.inspect}, value=#{value.inspect})")
1727
1783
  end
1728
1784
  if value[:datatype] == :enum then
1729
1785
  unless value[:values] then
@@ -1797,11 +1853,11 @@ end
1797
1853
  def validate_schema_against_doc_struct(struct, doc, prefix = nil)
1798
1854
  struct.each do |key, value|
1799
1855
  next if key == :additional_smtlib || key == :validation_rules || key == :predicates || key == :parent_link || key == :struct_xpath_prefix
1800
- if prefix then
1801
- xpath = "#{prefix}/#{key}"
1802
- else
1803
- xpath = key
1804
- end
1856
+ if prefix then
1857
+ xpath = "#{prefix}/#{key}"
1858
+ else
1859
+ xpath = key
1860
+ end
1805
1861
  if value[:type] == :structure then
1806
1862
  node = doc.at_xpath(key.to_s)
1807
1863
  if node then
@@ -1832,6 +1888,26 @@ def validate_schema_against_doc_struct(struct, doc, prefix = nil)
1832
1888
  end
1833
1889
  end
1834
1890
  end
1891
+ elsif value[:type] == :objlist then
1892
+ if value[:optional] == false then
1893
+ unless doc.at_xpath(key.to_s) then
1894
+ raise FatalError.new("ERROR: schema validation failed: non-optional objlist #{xpath} not found in doc.")
1895
+ end
1896
+ end
1897
+ list = doc.at_xpath(key.to_s)
1898
+ if list then
1899
+ index = 0
1900
+ list.children.to_a.each_with_index do |c|
1901
+ next unless c.is_a?(Nokogiri::XML::Element)
1902
+ index += 1
1903
+ unless c.name == value[:xpath_element] then
1904
+ raise FatalError.new("ERROR: schema validation failed: encountered element #{c.name} in objlist #{xpath}, which should only contain #{value[:xpath_element]} elements.")
1905
+ end
1906
+ if c then
1907
+ validate_schema_against_doc_struct(value[:ref], c, "#{xpath}/#{value[:xpath_element]}[#{index}]" )
1908
+ end
1909
+ end
1910
+ end
1835
1911
  else
1836
1912
  raise RuntimeError.new("Model error: encountered element #{xpath} of unknown type: #{value[:type]}.")
1837
1913
  end
@@ -1843,10 +1919,10 @@ def translate_doc_to_SMTLIB(progress, solver, struct, doc, hash = {})
1843
1919
  config = hash[:config]
1844
1920
 
1845
1921
  solver.to_solver(progress, "; --- Document: ---")
1846
- translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, nil, hash)
1922
+ translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, nil, nil, hash)
1847
1923
  end
1848
1924
 
1849
- def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, hash = {})
1925
+ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, var_prefix, hash = {})
1850
1926
  options = hash[:options]
1851
1927
  config = hash[:config]
1852
1928
 
@@ -1857,6 +1933,11 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1857
1933
  else
1858
1934
  xpath = key.to_s
1859
1935
  end
1936
+ if var_prefix then
1937
+ var_xpath = "#{var_prefix}/#{key.to_s}"
1938
+ else
1939
+ var_xpath = key.to_s
1940
+ end
1860
1941
  if value[:type] == :structure then
1861
1942
  node = doc.at_xpath(xpath)
1862
1943
  if node then
@@ -1866,9 +1947,9 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1866
1947
  }
1867
1948
  assertionID = "doc-#{xpath.gsub('/', '-')}"
1868
1949
  solver.associateAssertionIDWith(assertionID, info)
1869
- solver.to_solver(progress, "(assert (! (= #{exists_var_for_structure(xpath)} true) :named #{assertionID}))")
1950
+ solver.to_solver(progress, "(assert (! (= #{exists_var_for_structure(var_xpath)} true) :named #{assertionID}))")
1870
1951
 
1871
- translate_struct_in_doc_to_SMTLIB(progress, solver, value[:ref], doc, xpath, hash)
1952
+ translate_struct_in_doc_to_SMTLIB(progress, solver, value[:ref], doc, xpath, var_xpath, hash)
1872
1953
  else
1873
1954
  info = {
1874
1955
  :assertion_type => :doc_structure_not_exists,
@@ -1876,7 +1957,7 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1876
1957
  }
1877
1958
  assertionID = "doc-#{xpath.gsub('/', '-')}"
1878
1959
  solver.associateAssertionIDWith(assertionID, info)
1879
- solver.to_solver(progress, "(assert (! (= #{exists_var_for_structure(xpath)} false) :named #{assertionID}))")
1960
+ solver.to_solver(progress, "(assert (! (= #{exists_var_for_structure(var_xpath)} false) :named #{assertionID}))")
1880
1961
  end
1881
1962
  elsif value[:type] == :field then
1882
1963
  node = doc.at_xpath(xpath)
@@ -1887,10 +1968,11 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1887
1968
  :xpath => xpath,
1888
1969
  :value => fvalue
1889
1970
  }
1890
- fvalue_str = value_of_type(fvalue, value[:datatype])
1891
- assertionID = "doc-#{xpath.gsub('/', '-')}"
1971
+ fvalue_ruby = parse_field_value_to_ruby(value, fvalue)
1972
+ fvalue_str = value_of_type_to_solver(fvalue_ruby, value[:datatype])
1973
+ assertionID = "doc-#{var_xpath.gsub('/', '-')}"
1892
1974
  solver.associateAssertionIDWith(assertionID, info)
1893
- solver.to_solver(progress, "(assert (! (and (= #{filled_var_for_field(xpath)} true) (= #{value_var_for_field(xpath)} #{fvalue_str})) :named #{assertionID}))")
1975
+ solver.to_solver(progress, "(assert (! (and (= #{filled_var_for_field(var_xpath)} true) (= #{value_var_for_field(var_xpath)} #{fvalue_str})) :named #{assertionID}))")
1894
1976
  else
1895
1977
  info = {
1896
1978
  :assertion_type => :doc_field_empty,
@@ -1898,7 +1980,7 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1898
1980
  }
1899
1981
  assertionID = "doc-#{xpath.gsub('/', '-')}"
1900
1982
  solver.associateAssertionIDWith(assertionID, info)
1901
- solver.to_solver(progress, "(assert (! (= #{filled_var_for_field(xpath)} false) :named #{assertionID}))")
1983
+ solver.to_solver(progress, "(assert (! (= #{filled_var_for_field(var_xpath)} false) :named #{assertionID}))")
1902
1984
  end
1903
1985
  elsif value[:type] == :list then
1904
1986
  node = doc.at_xpath(xpath)
@@ -1913,14 +1995,62 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1913
1995
  :xpath => xpath,
1914
1996
  :values => fvalues
1915
1997
  }
1916
- fvalue_str = value_of_type(fvalues, value[:datatype])
1917
1998
  assertionID = "doc-#{xpath.gsub('/', '-')}"
1918
1999
  solver.associateAssertionIDWith(assertionID, info)
1919
2000
  clauses = []
1920
2001
  fvalues.each_with_index do |fvalue, index|
1921
2002
  clauses << "(= #{value_var_for_list(xpath,index)} #{value_of_type(fvalue, value[:datatype])})"
1922
2003
  end
1923
- solver.to_solver(progress, "(assert (! (and (= #{filled_var_for_field(xpath)} true) (= #{size_var_for_list(xpath)} #{fvalues.size}) #{clauses.join(' ')}) :named #{assertionID}))")
2004
+ solver.to_solver(progress, "(assert (! (and (= #{filled_var_for_field(var_xpath)} true) (= #{size_var_for_list(xpath)} #{fvalues.size}) #{clauses.join(' ')}) :named #{assertionID}))")
2005
+ else
2006
+ info = {
2007
+ :assertion_type => :doc_field_empty,
2008
+ :xpath => xpath,
2009
+ }
2010
+ assertionID = "doc-#{xpath.gsub('/', '-')}"
2011
+ solver.associateAssertionIDWith(assertionID, info)
2012
+ solver.to_solver(progress, "(assert (! (= #{filled_var_for_field(var_xpath)} false) :named #{assertionID}))")
2013
+ end
2014
+ elsif value[:type] == :objlist then
2015
+ node = doc.at_xpath(xpath)
2016
+ if node then
2017
+ fvalues = []
2018
+ index = 0
2019
+ num_elements = 0
2020
+ value[:model_maxLength].times do |index|
2021
+ element_node = node.at_xpath("#{value[:xpath_element]}[#{index+1}]")
2022
+ element_xpath = "#{xpath}/#{value[:xpath_element]}[#{index+1}]"
2023
+ element_var_xpath = "#{xpath}/#{index}"
2024
+ if element_node then
2025
+ num_elements += 1
2026
+ info = {
2027
+ :assertion_type => :doc_structure_exists,
2028
+ :xpath => element_xpath,
2029
+ }
2030
+ assertionID = "doc-#{element_var_xpath.gsub('/', '-')}"
2031
+ solver.associateAssertionIDWith(assertionID, info)
2032
+ solver.to_solver(progress, "(assert (! (= #{exists_var_for_structure(element_var_xpath)} true) :named #{assertionID}))")
2033
+
2034
+ translate_struct_in_doc_to_SMTLIB(progress, solver, value[:ref], doc, element_xpath, element_var_xpath, hash)
2035
+ else
2036
+ info = {
2037
+ :assertion_type => :doc_structure_not_exists,
2038
+ :xpath => element_xpath,
2039
+ }
2040
+ assertionID = "doc-#{element_var_xpath.gsub('/', '-')}"
2041
+ solver.associateAssertionIDWith(assertionID, info)
2042
+ solver.to_solver(progress, "(assert (! (= #{exists_var_for_structure(element_var_xpath)} false) :named #{assertionID}))")
2043
+ end
2044
+ end
2045
+ info = {
2046
+ :assertion_type => :doc_objlist_filled,
2047
+ :size => num_elements,
2048
+ :xpath => xpath
2049
+ }
2050
+ assertionID = "doc-#{xpath.gsub('/', '-')}"
2051
+ solver.associateAssertionIDWith(assertionID, info)
2052
+ clauses = []
2053
+ solver.to_solver(progress, "(assert (! (and (= #{filled_var_for_field(var_xpath)} true) (= #{size_var_for_list(var_xpath)} #{num_elements})) :named #{assertionID}))")
1924
2054
  else
1925
2055
  info = {
1926
2056
  :assertion_type => :doc_field_empty,
@@ -1928,7 +2058,7 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1928
2058
  }
1929
2059
  assertionID = "doc-#{xpath.gsub('/', '-')}"
1930
2060
  solver.associateAssertionIDWith(assertionID, info)
1931
- solver.to_solver(progress, "(assert (! (= #{filled_var_for_field(xpath)} false) :named #{assertionID}))")
2061
+ solver.to_solver(progress, "(assert (! (= #{filled_var_for_field(var_xpath)} false) :named #{assertionID}))")
1932
2062
  end
1933
2063
  else
1934
2064
  raise RuntimeError.new("Model Error: encountered unknown type #{value[:type].inspect}")
data/lib/solver-lib.rb CHANGED
@@ -225,7 +225,7 @@ class SolverFactory
225
225
  unsat_core = session.from_solver(@progress)
226
226
  explanation = session.parse_unsat_core(unsat_core)
227
227
  else
228
- ModelUtils.handleSolverError(@progress, options, result)
228
+ ModelUtils.handleSolverError(@progress, session, options, result)
229
229
  end
230
230
 
231
231
  return explanation
@@ -469,6 +469,10 @@ class Z3Solver
469
469
  return "Structure #{info[:xpath]} exists in document"
470
470
  elsif info[:assertion_type] == :doc_structure_not_exists then
471
471
  return "Structure #{info[:xpath]} does not exist in document"
472
+ elsif info[:assertion_type] == :doc_list_filled then
473
+ return "List #{info[:xpath]} is filled in document and contains the values #{info[:values].inspect}"
474
+ elsif info[:assertion_type] == :doc_objlist_filled then
475
+ return "ObjList #{info[:xpath]} is filled in document and contains #{info[:size]} elements"
472
476
  elsif info[:assertion_type] == :constraint then
473
477
  if info[:constraint_type] == :min then
474
478
  return "Field #{info[:xpath]} has a minimum value of #{info[:value]}."
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mbt-gen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - FM-enthusiast
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-31 00:00:00.000000000 Z
11
+ date: 2026-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri