mbt-gen 0.0.7 → 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 +152 -28
  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: 60f33b703c44710c8f3fd4ac743eda550f72af721e6480beb9c62d4e7d1258c7
4
- data.tar.gz: 53f6012841f8572350c02cffa8fb841ae7de411985c15c21f7c7e02465e191f5
3
+ metadata.gz: 763d26c653134a8a5432753091e286e1df8aecf8c603a5eba5bc6980ed5c3e6f
4
+ data.tar.gz: e5e86c5368af05ff682fbd2f7bf7fed6a9e7909d7d92523728956ee3fee71946
5
5
  SHA512:
6
- metadata.gz: 9143a6315fa8e359420862a2e0d8d6dbaf08efb2ae9f749d3f51a178f55ffc619602d098dba6e518cfb606a92fb5f730d2fb627216320817d37aa38a2dc706fd
7
- data.tar.gz: 292e6fa9e9dc391c906b6d3ee305edc8c7c8a74b39e345380813014177c4d7c68753dfe46eb37ce64ff6ec8aa24f8da261ab97f797d44430fe72aa0ba363934d
6
+ metadata.gz: 5c32f100cae06aa9f7e97d7e1b214f27a9d0747948feafddd01530392f45df0a9966e4f6ad3ef5be9a719b87f9e0bd91f966523415231b539026503aa1659064
7
+ data.tar.gz: 485bd1dfe0f01f7e553087603c90e73c582b7dd29de72cd79032f63f1b35df32af15c66369c540507ae4ed8427244159710076221dcf033696e7ce8732191ff7
data/lib/mbt-gen.rb CHANGED
@@ -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
1211
+ else
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)
1183
1223
  else
1184
- raise RuntimeError.new("encountered unknown :datatype #{type.inspect}")
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})")
@@ -1803,11 +1853,11 @@ end
1803
1853
  def validate_schema_against_doc_struct(struct, doc, prefix = nil)
1804
1854
  struct.each do |key, value|
1805
1855
  next if key == :additional_smtlib || key == :validation_rules || key == :predicates || key == :parent_link || key == :struct_xpath_prefix
1806
- if prefix then
1807
- xpath = "#{prefix}/#{key}"
1808
- else
1809
- xpath = key
1810
- end
1856
+ if prefix then
1857
+ xpath = "#{prefix}/#{key}"
1858
+ else
1859
+ xpath = key
1860
+ end
1811
1861
  if value[:type] == :structure then
1812
1862
  node = doc.at_xpath(key.to_s)
1813
1863
  if node then
@@ -1838,6 +1888,26 @@ def validate_schema_against_doc_struct(struct, doc, prefix = nil)
1838
1888
  end
1839
1889
  end
1840
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
1841
1911
  else
1842
1912
  raise RuntimeError.new("Model error: encountered element #{xpath} of unknown type: #{value[:type]}.")
1843
1913
  end
@@ -1849,10 +1919,10 @@ def translate_doc_to_SMTLIB(progress, solver, struct, doc, hash = {})
1849
1919
  config = hash[:config]
1850
1920
 
1851
1921
  solver.to_solver(progress, "; --- Document: ---")
1852
- 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)
1853
1923
  end
1854
1924
 
1855
- 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 = {})
1856
1926
  options = hash[:options]
1857
1927
  config = hash[:config]
1858
1928
 
@@ -1863,6 +1933,11 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1863
1933
  else
1864
1934
  xpath = key.to_s
1865
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
1866
1941
  if value[:type] == :structure then
1867
1942
  node = doc.at_xpath(xpath)
1868
1943
  if node then
@@ -1872,9 +1947,9 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1872
1947
  }
1873
1948
  assertionID = "doc-#{xpath.gsub('/', '-')}"
1874
1949
  solver.associateAssertionIDWith(assertionID, info)
1875
- 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}))")
1876
1951
 
1877
- 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)
1878
1953
  else
1879
1954
  info = {
1880
1955
  :assertion_type => :doc_structure_not_exists,
@@ -1882,7 +1957,7 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1882
1957
  }
1883
1958
  assertionID = "doc-#{xpath.gsub('/', '-')}"
1884
1959
  solver.associateAssertionIDWith(assertionID, info)
1885
- 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}))")
1886
1961
  end
1887
1962
  elsif value[:type] == :field then
1888
1963
  node = doc.at_xpath(xpath)
@@ -1893,10 +1968,11 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1893
1968
  :xpath => xpath,
1894
1969
  :value => fvalue
1895
1970
  }
1896
- fvalue_str = value_of_type(fvalue, value[:datatype])
1897
- 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('/', '-')}"
1898
1974
  solver.associateAssertionIDWith(assertionID, info)
1899
- 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}))")
1900
1976
  else
1901
1977
  info = {
1902
1978
  :assertion_type => :doc_field_empty,
@@ -1904,7 +1980,7 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1904
1980
  }
1905
1981
  assertionID = "doc-#{xpath.gsub('/', '-')}"
1906
1982
  solver.associateAssertionIDWith(assertionID, info)
1907
- 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}))")
1908
1984
  end
1909
1985
  elsif value[:type] == :list then
1910
1986
  node = doc.at_xpath(xpath)
@@ -1919,14 +1995,62 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1919
1995
  :xpath => xpath,
1920
1996
  :values => fvalues
1921
1997
  }
1922
- fvalue_str = value_of_type(fvalues, value[:datatype])
1923
1998
  assertionID = "doc-#{xpath.gsub('/', '-')}"
1924
1999
  solver.associateAssertionIDWith(assertionID, info)
1925
2000
  clauses = []
1926
2001
  fvalues.each_with_index do |fvalue, index|
1927
2002
  clauses << "(= #{value_var_for_list(xpath,index)} #{value_of_type(fvalue, value[:datatype])})"
1928
2003
  end
1929
- 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}))")
1930
2054
  else
1931
2055
  info = {
1932
2056
  :assertion_type => :doc_field_empty,
@@ -1934,7 +2058,7 @@ def translate_struct_in_doc_to_SMTLIB(progress, solver, struct, doc, prefix, has
1934
2058
  }
1935
2059
  assertionID = "doc-#{xpath.gsub('/', '-')}"
1936
2060
  solver.associateAssertionIDWith(assertionID, info)
1937
- 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}))")
1938
2062
  end
1939
2063
  else
1940
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.7
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