expressir 2.1.15 → 2.1.16

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.
@@ -0,0 +1,307 @@
1
+ module Expressir
2
+ module Benchmark
3
+ require "benchmark"
4
+ require "json"
5
+ require "csv"
6
+
7
+ class << self
8
+ # Measures the execution time of schema loading and optionally reports it
9
+ # @param file [String] File being processed
10
+ # @param options [Hash] Options for benchmarking
11
+ # @return The result of the block
12
+ def measure_file(file)
13
+ return yield unless Expressir.configuration.benchmark_enabled?
14
+
15
+ if Expressir.configuration.benchmark_ips?
16
+ require "benchmark/ips"
17
+
18
+ result = nil
19
+ ::Benchmark.ips do |x|
20
+ x.config(time: Expressir.configuration.benchmark_iterations,
21
+ warmup: Expressir.configuration.benchmark_warmup)
22
+
23
+ x.report("Loading #{file}") do
24
+ result = yield
25
+ end
26
+
27
+ if Expressir.configuration.benchmark_save?
28
+ x.save! "expressir_benchmark_#{File.basename(file)}"
29
+ end
30
+ end
31
+
32
+ # Calculate objects per second
33
+ if result.respond_to?(:schemas)
34
+ objects_per_second = calculate_objects_per_second(result)
35
+ puts "Objects per second: #{objects_per_second}" if Expressir.configuration.benchmark_verbose?
36
+ end
37
+
38
+ else
39
+ # Standard benchmarking
40
+ result = nil
41
+ time = ::Benchmark.measure do
42
+ result = yield
43
+ end
44
+
45
+ if result.respond_to?(:schemas)
46
+ objects_per_second = calculate_objects_per_second(result, time.real)
47
+ output_benchmark_result(file, time.real, objects_per_second)
48
+ else
49
+ output_benchmark_result(file, time.real)
50
+ end
51
+
52
+ end
53
+ result
54
+ end
55
+
56
+ # Benchmarks file collection processing
57
+ # @param files [Array<String>] List of files to process
58
+ # @return [Array] Results from processing files
59
+ def measure_collection(files)
60
+ unless Expressir.configuration.benchmark_enabled?
61
+ # Process each file individually even when benchmarking is disabled
62
+ results = []
63
+ files.each do |file|
64
+ file_results = yield(file)
65
+ results.concat(Array(file_results))
66
+ end
67
+ return results
68
+ end
69
+
70
+ results = []
71
+ total_time = ::Benchmark.measure do
72
+ files.each_with_index do |file, i|
73
+ if Expressir.configuration.benchmark_verbose?
74
+ puts "#{i + 1}/#{files.length} Processing #{file}"
75
+ end
76
+
77
+ file_results = measure_file(file) do
78
+ yield(file)
79
+ end
80
+
81
+ results.concat(Array(file_results))
82
+ end
83
+ end
84
+
85
+ output_collection_result(files.length, total_time.real, results)
86
+ results
87
+ end
88
+
89
+ # Measures reference resolution
90
+ # @return The result of the block
91
+ def measure_references
92
+ return yield unless Expressir.configuration.benchmark_enabled?
93
+
94
+ if Expressir.configuration.benchmark_ips?
95
+ require "benchmark/ips"
96
+
97
+ result = nil
98
+ ::Benchmark.ips do |x|
99
+ x.config(time: Expressir.configuration.benchmark_iterations,
100
+ warmup: Expressir.configuration.benchmark_warmup)
101
+
102
+ x.report("Reference resolution") do
103
+ result = yield
104
+ end
105
+ end
106
+
107
+ else
108
+ result = nil
109
+ time = ::Benchmark.measure do
110
+ result = yield
111
+ end
112
+
113
+ output_benchmark_result("Reference resolution", time.real)
114
+ end
115
+ result
116
+ end
117
+
118
+ # Benchmark with caching
119
+ # @param path [String] Path to the file
120
+ # @param cache_path [String] Path for the cache file
121
+ # @return [Hash] Results with timing information
122
+ def measure_with_cache(path, cache_path = nil)
123
+ return yield(path) unless Expressir.configuration.benchmark_enabled?
124
+
125
+ require "tempfile"
126
+ temp_file = nil
127
+
128
+ unless cache_path
129
+ temp_file = Tempfile.new(["expressir_cache", ".bin"])
130
+ cache_path = temp_file.path
131
+ end
132
+
133
+ results = {}
134
+
135
+ # Measure parsing time
136
+ parsing_time = ::Benchmark.measure do
137
+ results[:repository] = yield(path)
138
+ end
139
+ results[:parsing_time] = parsing_time.real
140
+
141
+ # Measure cache writing time
142
+ cache_write_time = ::Benchmark.measure do
143
+ Expressir::Express::Cache.to_file(cache_path, results[:repository])
144
+ end
145
+ results[:cache_write_time] = cache_write_time.real
146
+
147
+ # Measure cache reading time
148
+ cache_read_time = ::Benchmark.measure do
149
+ results[:cached_repository] = Expressir::Express::Cache.from_file(cache_path)
150
+ end
151
+ results[:cache_read_time] = cache_read_time.real
152
+
153
+ # Output results
154
+ if Expressir.configuration.benchmark_verbose?
155
+ puts "Parsing time: #{results[:parsing_time].round(4)}s"
156
+ puts "Cache write time: #{results[:cache_write_time].round(4)}s"
157
+ puts "Cache read time: #{results[:cache_read_time].round(4)}s"
158
+
159
+ if results[:repository].respond_to?(:schemas)
160
+ objects = count_objects(results[:repository])
161
+ puts "Total objects: #{objects}"
162
+ puts "Objects per second (parsing): #{(objects / results[:parsing_time]).round(2)}"
163
+ puts "Objects per second (reading): #{(objects / results[:cache_read_time]).round(2)}"
164
+ end
165
+ end
166
+
167
+ # Clean up temp file if we created one
168
+ if temp_file
169
+ temp_file.close
170
+ temp_file.unlink
171
+ end
172
+
173
+ results
174
+ end
175
+
176
+ private
177
+
178
+ # Calculate objects per second
179
+ # @param repository [Object] The repository object
180
+ # @param time [Float] The time taken in seconds
181
+ # @return [Float] Objects per second
182
+ def calculate_objects_per_second(repository, time = 1.0)
183
+ objects = count_objects(repository)
184
+ (objects / time).round(2)
185
+ end
186
+
187
+ # Count objects in a repository
188
+ # @param repository [Object] The repository object
189
+ # @return [Integer] Number of objects
190
+ def count_objects(repository)
191
+ return 0 unless repository.respond_to?(:schemas)
192
+
193
+ count = repository.schemas.size
194
+
195
+ repository.schemas.each do |schema|
196
+ count += schema.entities.size if schema.entities
197
+ count += schema.types.size if schema.types
198
+ count += schema.functions.size if schema.functions
199
+ count += schema.rules.size if schema.rules
200
+ count += schema.procedures.size if schema.procedures
201
+ count += schema.constants.size if schema.constants
202
+
203
+ # Count attributes in entities
204
+ schema.entities&.each do |entity|
205
+ # Count explicit attributes
206
+ if entity.attributes
207
+ count += entity.attributes.size
208
+ end
209
+
210
+ # Count inheritance relationships
211
+ count += 1 if entity.subtype_of
212
+ end
213
+ end
214
+
215
+ count
216
+ end
217
+
218
+ # Output benchmark result in the appropriate format
219
+ # @param label [String] Label for the benchmark
220
+ # @param time [Float] Time in seconds
221
+ # @param objects_per_second [Float, nil] Objects per second metric (optional)
222
+ def output_benchmark_result(label, time, objects_per_second = nil)
223
+ time_rounded = time.round(4)
224
+
225
+ case Expressir.configuration.benchmark_format
226
+ when "json"
227
+ result = {
228
+ label: label,
229
+ time_seconds: time_rounded,
230
+ }
231
+ result[:objects_per_second] = objects_per_second if objects_per_second
232
+ puts JSON.generate(result)
233
+ when "csv"
234
+ headers = ["Label", "Time (s)"]
235
+ headers << "Objects/s" if objects_per_second
236
+
237
+ values = [label, time_rounded]
238
+ values << objects_per_second if objects_per_second
239
+
240
+ puts headers.join(",")
241
+ puts values.join(",")
242
+ else
243
+ # Default format
244
+ message = "#{label}: #{time_rounded}s"
245
+ message += " (#{objects_per_second} objects/s)" if objects_per_second
246
+ puts message if Expressir.configuration.benchmark_verbose?
247
+ end
248
+ end
249
+
250
+ # Output collection benchmark result
251
+ # @param count [Integer] Number of files processed
252
+ # @param total_time [Float] Total time in seconds
253
+ # @param results [Array] Array of results
254
+ def output_collection_result(count, total_time, results)
255
+ avg_time = total_time / count
256
+
257
+ # Get total object count if possible
258
+ total_objects = 0
259
+ schema_count = 0
260
+
261
+ # Try to count total objects and schemas
262
+ results.each do |result|
263
+ if result.respond_to?(:schemas)
264
+ schema_count += result.schemas.size
265
+ total_objects += count_objects(result)
266
+ elsif result.is_a?(Array)
267
+ result.each do |r|
268
+ if r.respond_to?(:id) # Likely a schema
269
+ schema_count += 1
270
+ total_objects += 1 # Count the schema itself
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
+ objects_per_second = (total_objects / total_time).round(2)
277
+
278
+ case Expressir.configuration.benchmark_format
279
+ when "json"
280
+ result = {
281
+ files_processed: count,
282
+ schemas_loaded: schema_count,
283
+ total_objects: total_objects,
284
+ total_time_seconds: total_time.round(4),
285
+ average_time_seconds: avg_time.round(4),
286
+ objects_per_second: objects_per_second,
287
+ }
288
+ puts JSON.generate(result)
289
+ when "csv"
290
+ headers = ["Files", "Schemas", "Objects", "Total Time (s)", "Avg Time (s)", "Objects/s"]
291
+ values = [count, schema_count, total_objects, total_time.round(4), avg_time.round(4), objects_per_second]
292
+
293
+ puts headers.join(",")
294
+ puts values.join(",")
295
+ else
296
+ # Default format
297
+ if Expressir.configuration.benchmark_verbose?
298
+ puts "\nProcessed #{count} files in #{total_time.round(4)}s"
299
+ puts "Loaded #{schema_count} schemas containing #{total_objects} objects"
300
+ puts "Average time per file: #{avg_time.round(4)}s"
301
+ puts "Performance: #{objects_per_second} objects/s"
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
data/lib/expressir/cli.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "thor"
2
+ require "yaml"
2
3
 
3
4
  module Expressir
4
5
  class Cli < Thor
@@ -11,6 +12,67 @@ module Expressir
11
12
  end
12
13
  end
13
14
 
15
+ desc "clean PATH", "Strip remarks and prettify EXPRESS schema at PATH"
16
+ method_option :output, type: :string, desc: "Output file path (defaults to stdout)"
17
+ def clean(path)
18
+ repository = Expressir::Express::Parser.from_file(path)
19
+ formatted_schemas = repository.schemas.map do |schema|
20
+ # Format schema without remarks
21
+ schema.to_s(no_remarks: true)
22
+ end.join("\n\n")
23
+
24
+ if options[:output]
25
+ File.write(options[:output], formatted_schemas)
26
+ puts "Cleaned schema written to #{options[:output]}"
27
+ else
28
+ puts formatted_schemas
29
+ end
30
+ end
31
+
32
+ desc "benchmark FILE_OR_YAML", "Benchmark schema loading performance for a file or list of files from YAML"
33
+ method_option :ips, type: :boolean, desc: "Use benchmark-ips for detailed statistics"
34
+ method_option :verbose, type: :boolean, desc: "Show verbose output"
35
+ method_option :save, type: :boolean, desc: "Save benchmark results to file"
36
+ method_option :format, type: :string, desc: "Output format (json, csv, default)"
37
+ def benchmark(path)
38
+ # Enable benchmarking via configuration
39
+ Expressir.configuration.benchmark_enabled = true
40
+ Expressir.configuration.benchmark_ips = options[:ips]
41
+ Expressir.configuration.benchmark_verbose = options[:verbose]
42
+ Expressir.configuration.benchmark_save = options[:save]
43
+ Expressir.configuration.benchmark_format = options[:format] if options[:format]
44
+
45
+ if [".yml", ".yaml"].include?(File.extname(path).downcase)
46
+ benchmark_from_yaml(path)
47
+ else
48
+ benchmark_file(path)
49
+ end
50
+ end
51
+
52
+ desc "benchmark-cache FILE_OR_YAML", "Benchmark schema loading with caching"
53
+ method_option :ips, type: :boolean, desc: "Use benchmark-ips for detailed statistics"
54
+ method_option :verbose, type: :boolean, desc: "Show verbose output"
55
+ method_option :save, type: :boolean, desc: "Save benchmark results to file"
56
+ method_option :format, type: :string, desc: "Output format (json, csv, default)"
57
+ method_option :cache_path, type: :string, desc: "Path to store the cache file"
58
+ def benchmark_cache(path)
59
+ # Same options as benchmark + cache_path
60
+ Expressir.configuration.benchmark_enabled = true
61
+ Expressir.configuration.benchmark_ips = options[:ips]
62
+ Expressir.configuration.benchmark_verbose = options[:verbose]
63
+ Expressir.configuration.benchmark_save = options[:save]
64
+ Expressir.configuration.benchmark_format = options[:format] if options[:format]
65
+
66
+ # Run benchmarks with cache
67
+ cache_path = options[:cache_path] || generate_temp_cache_path
68
+
69
+ if [".yml", ".yaml"].include?(File.extname(path).downcase)
70
+ benchmark_cache_from_yaml(path, cache_path)
71
+ else
72
+ benchmark_cache_file(path, cache_path)
73
+ end
74
+ end
75
+
14
76
  no_commands do
15
77
  def _validate_schema(path)
16
78
  repository = Expressir::Express::Parser.from_file(path)
@@ -30,6 +92,109 @@ module Expressir
30
92
  puts msg
31
93
  end
32
94
  end
95
+
96
+ def benchmark_file(path)
97
+ puts "Express Schema Loading Benchmark"
98
+ puts "--------------------------------"
99
+
100
+ repository = Expressir::Benchmark.measure_file(path) do
101
+ Expressir::Express::Parser.from_file(path)
102
+ end
103
+
104
+ if repository
105
+ puts "Loaded #{repository.schemas.size} schemas"
106
+ else
107
+ puts "Failed to load schema"
108
+ end
109
+ end
110
+
111
+ def benchmark_from_yaml(yaml_path)
112
+ puts "Express Schema Loading Benchmark from YAML"
113
+ puts "--------------------------------"
114
+
115
+ # Load schema list from YAML
116
+ schema_list = YAML.load_file(yaml_path)
117
+ if schema_list.is_a?(Hash) && schema_list["schemas"]
118
+ # Handle format: { "schemas": ["path1", "path2", ...] }
119
+ schema_files = schema_list["schemas"]
120
+ elsif schema_list.is_a?(Array)
121
+ # Handle format: ["path1", "path2", ...]
122
+ schema_files = schema_list
123
+ else
124
+ puts "Invalid YAML format. Expected an array of schema paths or a hash with a 'schemas' key."
125
+ return
126
+ end
127
+
128
+ puts "YAML File: #{yaml_path}"
129
+ puts "Number of schemas in list: #{schema_files.size}"
130
+ puts "--------------------------------"
131
+
132
+ # Load the schemas
133
+ Expressir::Benchmark.measure_collection(schema_files) do |file|
134
+ repository = Expressir::Express::Parser.from_file(file)
135
+ repository.schemas
136
+ end
137
+ end
138
+
139
+ def benchmark_cache_file(path, cache_path)
140
+ puts "Express Schema Loading Benchmark with Caching"
141
+ puts "--------------------------------"
142
+ puts "Schema: #{path}"
143
+ puts "Cache: #{cache_path}"
144
+ puts "--------------------------------"
145
+
146
+ # Benchmark with caching
147
+ results = Expressir::Benchmark.measure_with_cache(path, cache_path) do |file|
148
+ Expressir::Express::Parser.from_file(file)
149
+ end
150
+
151
+ if results[:repository]
152
+ schema_count = results[:repository].schemas.size
153
+ puts "Loaded #{schema_count} schemas"
154
+ else
155
+ puts "Failed to load schema"
156
+ end
157
+ end
158
+
159
+ def benchmark_cache_from_yaml(yaml_path, cache_path)
160
+ puts "Express Schema Loading Benchmark with Caching from YAML"
161
+ puts "--------------------------------"
162
+
163
+ # Load schema list from YAML
164
+ schema_list = YAML.load_file(yaml_path)
165
+ if schema_list.is_a?(Hash) && schema_list["schemas"]
166
+ # Handle format: { "schemas": ["path1", "path2", ...] }
167
+ schema_files = schema_list["schemas"]
168
+ elsif schema_list.is_a?(Array)
169
+ # Handle format: ["path1", "path2", ...]
170
+ schema_files = schema_list
171
+ else
172
+ puts "Invalid YAML format. Expected an array of schema paths or a hash with a 'schemas' key."
173
+ return
174
+ end
175
+
176
+ puts "YAML File: #{yaml_path}"
177
+ puts "Number of schemas in list: #{schema_files.size}"
178
+ puts "Cache: #{cache_path}"
179
+ puts "--------------------------------"
180
+
181
+ # Process each file with caching
182
+ schema_files.each_with_index do |file, index|
183
+ puts "Processing file #{index + 1}/#{schema_files.size}: #{file}"
184
+
185
+ # Benchmark with caching
186
+ Expressir::Benchmark.measure_with_cache(file, cache_path) do |path|
187
+ Expressir::Express::Parser.from_file(path)
188
+ end
189
+
190
+ puts "--------------------------------"
191
+ end
192
+ end
193
+
194
+ def generate_temp_cache_path
195
+ require "tempfile"
196
+ Tempfile.new(["expressir_cache", ".bin"]).path
197
+ end
33
198
  end
34
199
 
35
200
  desc "validate *PATH", "validate EXPRESS schema located at PATH"
@@ -12,10 +12,49 @@ module Expressir
12
12
  end
13
13
 
14
14
  class Configuration
15
- attr_accessor :logs
15
+ attr_accessor :logs, :benchmark_enabled, :benchmark_ips, :benchmark_verbose,
16
+ :benchmark_save, :benchmark_iterations, :benchmark_warmup, :benchmark_parallel,
17
+ :benchmark_format
16
18
 
17
19
  def initialize
18
20
  @logs = %i(error)
21
+ # Enable benchmarking via environment variable
22
+ @benchmark_enabled = ENV["EXPRESSIR_BENCHMARK"] == "true"
23
+ # Use benchmark-ips for detailed statistics
24
+ @benchmark_ips = ENV["EXPRESSIR_BENCHMARK_IPS"] == "true"
25
+ # Show verbose output for benchmarking
26
+ @benchmark_verbose = ENV["EXPRESSIR_BENCHMARK_VERBOSE"] == "true"
27
+ # Save benchmark results to file
28
+ @benchmark_save = ENV["EXPRESSIR_BENCHMARK_SAVE"] == "true"
29
+ # Number of iterations for benchmark-ips
30
+ @benchmark_iterations = (ENV["EXPRESSIR_BENCHMARK_ITERATIONS"] || "3").to_i
31
+ # Warmup time for benchmark-ips in seconds
32
+ @benchmark_warmup = (ENV["EXPRESSIR_BENCHMARK_WARMUP"] || "1").to_i
33
+ # Enable parallel processing (for future implementation)
34
+ @benchmark_parallel = ENV["EXPRESSIR_BENCHMARK_PARALLEL"] == "true"
35
+ # Output format for benchmark results (default, json, csv)
36
+ @benchmark_format = ENV["EXPRESSIR_BENCHMARK_FORMAT"] || "default"
37
+ end
38
+
39
+ # Helper methods for checking benchmark settings
40
+ def benchmark_enabled?
41
+ @benchmark_enabled
42
+ end
43
+
44
+ def benchmark_ips?
45
+ @benchmark_enabled && @benchmark_ips
46
+ end
47
+
48
+ def benchmark_verbose?
49
+ @benchmark_enabled && @benchmark_verbose
50
+ end
51
+
52
+ def benchmark_save?
53
+ @benchmark_enabled && @benchmark_save
54
+ end
55
+
56
+ def benchmark_parallel?
57
+ @benchmark_enabled && @benchmark_parallel
19
58
  end
20
59
  end
21
60
 
@@ -38,7 +38,7 @@ module Expressir
38
38
  cache = Model::Cache.from_yaml(yaml)
39
39
 
40
40
  if cache.version != version
41
- raise Error.new("Cache version mismatch, cache version is #{cache.version}, Expressir version is #{version}")
41
+ raise Error::CacheVersionMismatchError.new(cache.version, version)
42
42
  end
43
43
 
44
44
  cache
@@ -0,0 +1,87 @@
1
+ module Expressir
2
+ module Express
3
+ # Module containing all Express-related errors
4
+ module Error
5
+ # Base error class for Express errors
6
+ class ExpressError < StandardError; end
7
+
8
+ # Error raised when a schema file fails to parse
9
+ class SchemaParseFailure < ExpressError
10
+ attr_reader :filename, :original_error, :parse_failure_cause
11
+
12
+ # Initialize a new SchemaParseFailure error
13
+ # @param filename [String] The name of the file that failed to parse
14
+ # @param original_error [Parslet::ParseFailed] The original parsing error
15
+ def initialize(filename, original_error)
16
+ @filename = filename
17
+ @original_error = original_error
18
+ @parse_failure_cause = original_error.parse_failure_cause
19
+ super("Failed to parse schema in file '#{filename}': #{original_error.message}")
20
+ end
21
+ end
22
+
23
+ # Error raised when there's a version mismatch in the cache
24
+ class CacheVersionMismatchError < ExpressError
25
+ attr_reader :cache_version, :expressir_version
26
+
27
+ # Initialize a new CacheVersionMismatchError
28
+ # @param cache_version [String] The version of the cache file
29
+ # @param expressir_version [String] The current Expressir version
30
+ def initialize(cache_version, expressir_version)
31
+ @cache_version = cache_version
32
+ @expressir_version = expressir_version
33
+ super("Cache version mismatch, cache version is #{cache_version}, Expressir version is #{expressir_version}")
34
+ end
35
+ end
36
+
37
+ # Base class for visitor-related errors
38
+ class VisitorError < ExpressError; end
39
+
40
+ # Error raised when visitor encounters invalid state
41
+ class VisitorInvalidStateError < VisitorError
42
+ # Initialize a new VisitorInvalidStateError
43
+ # @param message [String] Additional error information
44
+ def initialize(message = "Invalid state")
45
+ super("Visitor error: #{message}")
46
+ end
47
+ end
48
+
49
+ # Error raised when visitor encounters invalid input
50
+ class VisitorInvalidInputError < VisitorError
51
+ # Initialize a new VisitorInvalidInputError
52
+ # @param message [String] Additional error information
53
+ def initialize(message)
54
+ super("Invalid input error: #{message}")
55
+ end
56
+ end
57
+
58
+ # Error raised when a required method is missing
59
+ class FormatterMethodMissingError < ExpressError
60
+ # Initialize a new FormatterMethodMissingError
61
+ # @param formatter [String] Name of the formatter
62
+ # @param method [String] Name of the missing method
63
+ def initialize(formatter, method = nil)
64
+ method_msg = method ? " Missing method: #{method}" : " Missing required method."
65
+ super("Formatter error in #{formatter}.#{method_msg}")
66
+ end
67
+ end
68
+
69
+ # Error raised when reference resolution fails
70
+ class VisitorReferenceResolutionError < VisitorError
71
+ attr_reader :source, :reference
72
+
73
+ # Initialize a new VisitorReferenceResolutionError
74
+ # @param source [String] Source of the reference
75
+ # @param reference [String] The reference that failed to resolve
76
+ def initialize(source, reference)
77
+ @source = source
78
+ @reference = reference
79
+ super("Failed to resolve reference '#{reference}' from '#{source}'")
80
+ end
81
+ end
82
+ end
83
+
84
+ # For backward compatibility
85
+ CacheVersionMismatchError = Error::CacheVersionMismatchError
86
+ end
87
+ end
@@ -9,7 +9,7 @@ module Expressir
9
9
  # @!visibility private
10
10
  def self.included(mod)
11
11
  if !mod.superclass.private_method_defined? :format_references_simple_reference
12
- raise "Missing method"
12
+ raise Error::FormatterMethodMissingError.new("HyperlinkFormatter", "format_references_simple_reference")
13
13
  end
14
14
  end
15
15