calyx 0.14.2 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 129a9b8ddec5530b5f427780572e1e0a3a667bd4
4
- data.tar.gz: 445ce73db118256bbf9ad748b82421235bb8573d
3
+ metadata.gz: ba09252aa5dedcc03c190a14c68be4eb446fc01c
4
+ data.tar.gz: cf7c1287f4dcfbc35682d74a1f83d3f7eeb8b2a4
5
5
  SHA512:
6
- metadata.gz: 5b31b154440e67f0bde8ba9cd49784aece4ffa971a3b15731f83b05696f44e4db3fc43cdf78ffba990c962c46ac7d71c4964339133a43c202bd651a9066113bd
7
- data.tar.gz: 3aded7ee2f7104244b0d47dc0a06a98607f32bdc05e17a1eabf153b1595cca60e17f708cbc2873eb09bb27b16ec6bb445980f23c3b679a76feb669ecb1bcd601
6
+ metadata.gz: 670da35d3148a5aaaba3eb81edd7ba9635f3667cc2c1f6288b64cf2f79cafbc2649f752799911e8156609887f9fa260a95bb29765e9342b23bf5ca4f1fa87bca
7
+ data.tar.gz: 013ab7c2761b8db39e3748fd80602cecbcefe547c1802ccf703a4e459f880d54c0104b502c0798bb9048bcee63e7cf380bf1e6f4d434a04c2bf422e72bfd4200
data/README.md CHANGED
@@ -214,11 +214,22 @@ end
214
214
  By default, the outcomes of generated rules are selected with Ruby’s built-in pseudorandom number generator (as seen in methods like `Kernel.rand` and `Array.sample`). To seed the random number generator, pass in an integer seed value as the first argument to the constructor:
215
215
 
216
216
  ```ruby
217
- MyGrammar.new(12345)
218
- Calyx::Grammar.new(12345, &rules)
217
+ grammar = Calyx::Grammar.new(seed: 12345) do
218
+ # rules...
219
+ end
220
+ ```
221
+
222
+ Alternatively, you can pass a preconfigured instance of Ruby’s stdlib `Random` class:
223
+
224
+ ```ruby
225
+ random = Random.new(12345)
226
+
227
+ grammar = Calyx::Grammar.new(rng: random) do
228
+ # rules...
229
+ end
219
230
  ```
220
231
 
221
- When a seed value isn’t supplied, `Time.new.to_i` is used as the default seed, which makes each run of the generator relatively unique.
232
+ When a random seed isn’t supplied, `Time.new.to_i` is used as the default seed, which makes each run of the generator relatively unique.
222
233
 
223
234
  ### Weighted Selection
224
235
 
@@ -1,4 +1,5 @@
1
1
  require 'calyx/version'
2
+ require 'calyx/rule'
2
3
  require 'calyx/grammar'
3
4
  require 'calyx/errors'
4
5
  require 'calyx/format'
@@ -9,10 +9,31 @@ module Calyx
9
9
  # end
10
10
  #
11
11
  # grammar.evaluate
12
- # # => Calyx::Errors::MissingRule: :blank is not registered
13
- class MissingRule < RuntimeError
14
- def initialize(msg)
15
- super(":#{msg} is not registered")
12
+ # # => Calyx::Errors::UndefinedRule: :blank is not defined
13
+ class UndefinedRule < RuntimeError
14
+ def initialize(rule, symbol)
15
+ @trace = if rule
16
+ rule.trace
17
+ else
18
+ trace_api_boundary(caller_locations)
19
+ end
20
+
21
+ super("undefined rule :#{symbol} in #{@trace.path}:#{@trace.lineno}:`#{source_line}`")
22
+ end
23
+
24
+ def trace_api_boundary(trace)
25
+ (trace.count - 1).downto(0) do |index|
26
+ if trace[index].to_s.include?('lib/calyx/')
27
+ return trace[index + 1]
28
+ end
29
+ end
30
+ end
31
+
32
+ def source_line
33
+ File.open(@trace.absolute_path) do |source|
34
+ (@trace.lineno-1).times { source.gets }
35
+ source.gets
36
+ end.strip
16
37
  end
17
38
  end
18
39
 
@@ -1,7 +1,63 @@
1
+ require 'pathname'
2
+
1
3
  module Calyx
2
4
  # Helper methods for loading and initializing grammars from static files
3
5
  # on disk.
4
6
  module Format
7
+ class Trace
8
+ def initialize(match_symbol, filename, contents)
9
+ @match_symbol = match_symbol
10
+ @filename = Pathname.new(filename)
11
+ @contents = contents
12
+ end
13
+
14
+ def path
15
+ @filename.basename
16
+ end
17
+
18
+ def absolute_path
19
+ @filename.expand_path
20
+ end
21
+
22
+ def lineno
23
+ line_number = 0
24
+ @contents.each_line do |line|
25
+ line_number += 1
26
+ return line_number if line =~ @match_symbol
27
+ end
28
+ end
29
+ end
30
+
31
+ class JSONGrammar
32
+ def initialize(filename)
33
+ require 'json'
34
+ @filename = filename
35
+ @contents = File.read(@filename)
36
+ @rules = JSON.parse(@contents)
37
+ end
38
+
39
+ def each_rule(&block)
40
+ @rules.each do |rule, productions|
41
+ yield rule, productions, Trace.new(/("#{rule}")(\s*)(:)/, @filename, @contents)
42
+ end
43
+ end
44
+ end
45
+
46
+ class YAMLGrammar
47
+ def initialize(filename)
48
+ require 'yaml'
49
+ @filename = filename
50
+ @contents = File.read(@filename)
51
+ @rules = YAML.load(@contents)
52
+ end
53
+
54
+ def each_rule(&block)
55
+ @rules.each do |rule, productions|
56
+ yield rule, productions, Trace.new(/#{rule}:/, @filename, @contents)
57
+ end
58
+ end
59
+ end
60
+
5
61
  # Reads a file and parses its format, based on the given extension.
6
62
  #
7
63
  # Accepts a JSON or YAML file path, identified by its extension (`.json`
@@ -10,12 +66,12 @@ module Calyx
10
66
  # @param [String] filename
11
67
  # @return [Calyx::Grammar]
12
68
  def self.load(filename)
13
- file = File.read(filename)
14
69
  extension = File.extname(filename)
70
+
15
71
  if extension == ".yml"
16
- self.load_yml(file)
72
+ self.load_yml(filename)
17
73
  elsif extension == ".json"
18
- self.load_json(file)
74
+ self.load_json(filename)
19
75
  else
20
76
  raise Errors::UnsupportedFormat.new(filename)
21
77
  end
@@ -23,28 +79,27 @@ module Calyx
23
79
 
24
80
  # Converts the given string of YAML data to a grammar instance.
25
81
  #
26
- # @param [String] data
27
- # @return [Calyx::Grammar]
28
- def self.load_yml(data)
29
- require 'yaml'
30
- self.build_grammar(YAML.load(data))
82
+ # @param [String] filename
83
+ # @return [Calyx::Format::YAMLGrammar]
84
+ def self.load_yml(filename)
85
+ self.build_grammar(YAMLGrammar.new(filename))
31
86
  end
32
87
 
33
88
  # Converts the given string of JSON data to a grammar instance.
34
89
  #
35
- # @param [String] data
36
- # @return [Calyx::Grammar]
37
- def self.load_json(data)
38
- require 'json'
39
- self.build_grammar(JSON.parse(data))
90
+ # @param [String] filename
91
+ # @return [Calyx::Format::JSONGrammar]
92
+ def self.load_json(filename)
93
+ self.build_grammar(JSONGrammar.new(filename))
40
94
  end
41
95
 
42
96
  private
43
97
 
44
- def self.build_grammar(rules)
98
+ def self.build_grammar(grammar_format)
45
99
  Calyx::Grammar.new do
46
- rules.each do |label, productions|
47
- rule(label, *productions)
100
+ grammar_format.each_rule do |label, productions, trace|
101
+ productions = [productions] unless productions.is_a?(Enumerable)
102
+ define_rule(label, trace, productions)
48
103
  end
49
104
  end
50
105
  end
@@ -59,7 +59,7 @@ module Calyx
59
59
  # @param [Symbol] name
60
60
  # @param [Array] productions
61
61
  def rule(name, *productions)
62
- registry.rule(name, *productions)
62
+ registry.define_rule(name, caller_locations.first, productions)
63
63
  end
64
64
 
65
65
  # Augument the grammar with a method missing hook that treats class
@@ -71,7 +71,7 @@ module Calyx
71
71
  # @param [Symbol] name
72
72
  # @param [Array] productions
73
73
  def method_missing(name, *productions)
74
- registry.rule(name, *productions)
74
+ registry.define_rule(name, caller_locations.first, productions)
75
75
  end
76
76
 
77
77
  # Hook for combining the registry of a parent grammar into the child that
@@ -103,8 +103,16 @@ module Calyx
103
103
  unless options.is_a?(Hash)
104
104
  config_opts = {}
105
105
  if options.is_a?(Numeric)
106
+ warn [
107
+ "NOTE: Passing a numeric seed arg directly is deprecated. ",
108
+ "Use the options hash instead: `Calyx::Grammar.new(seed: 1234)`"
109
+ ].join
106
110
  config_opts[:seed] = options
107
111
  elsif options.is_a?(Random)
112
+ warn [
113
+ "NOTE: Passing a Random object directly is deprecated. ",
114
+ "Use the options hash instead: `Calyx::Grammar.new(rng: Random.new)`"
115
+ ].join
108
116
  config_opts[:rng] = options
109
117
  end
110
118
  else
@@ -18,12 +18,7 @@ module Calyx
18
18
  # @param [Random] random
19
19
  # @return [Array]
20
20
  def evaluate(random)
21
- expansion = @registry.expand(@symbol)
22
- if expansion.nil?
23
- raise Errors::MissingRule.new(@symbol)
24
- else
25
- [@symbol, expansion.evaluate(random)]
26
- end
21
+ [@symbol, @registry.expand(@symbol).evaluate(random)]
27
22
  end
28
23
  end
29
24
  end
@@ -11,14 +11,6 @@ module Calyx
11
11
  @modifiers = Modifiers.new
12
12
  end
13
13
 
14
- # Hook for defining rules without explicitly calling the `#rule` method.
15
- #
16
- # @param [Symbol] name
17
- # @param [Array] productions
18
- def method_missing(name, *arguments)
19
- rule(name, *arguments)
20
- end
21
-
22
14
  # Attaches a modifier module to this instance.
23
15
  #
24
16
  # @param [Module] module_name
@@ -46,19 +38,48 @@ module Calyx
46
38
  end
47
39
  end
48
40
 
41
+ # Registers a new grammar rule without explicitly calling the `#rule` method.
42
+ #
43
+ # @param [Symbol] name
44
+ # @param [Array] productions
45
+ def method_missing(name, *productions)
46
+ define_rule(name, caller_locations.first, productions)
47
+ end
48
+
49
49
  # Registers a new grammar rule.
50
50
  #
51
51
  # @param [Symbol] name
52
52
  # @param [Array] productions
53
53
  def rule(name, *productions)
54
- rules[name.to_sym] = construct_rule(productions)
54
+ define_rule(name, caller_locations.first, productions)
55
55
  end
56
56
 
57
- # Expands the given rule symbol to its production.
57
+ # Defines a static rule in the grammar.
58
+ #
59
+ # @param [Symbol] name
60
+ # @param [Array] productions
61
+ def define_rule(name, trace, productions)
62
+ rules[name.to_sym] = Rule.new(name.to_sym, construct_rule(productions), trace)
63
+ end
64
+
65
+ # Defines a rule in the temporary evaluation context.
66
+ #
67
+ # @param [Symbol] name
68
+ # @param [Array] productions
69
+ def define_context_rule(name, trace, productions)
70
+ productions = [productions] unless productions.is_a?(Enumerable)
71
+ context[name.to_sym] = Rule.new(name.to_sym, construct_rule(productions), trace)
72
+ end
73
+
74
+ # Expands the given symbol to its rule.
58
75
  #
59
76
  # @param [Symbol] symbol
77
+ # @return [Calyx::Rule]
60
78
  def expand(symbol)
61
- rules[symbol] || context[symbol]
79
+ expansion = rules[symbol] || context[symbol]
80
+ raise Errors::UndefinedRule.new(@last_expansion, symbol) if expansion.nil?
81
+ @last_expansion = expansion
82
+ expansion
62
83
  end
63
84
 
64
85
  # Applies the given modifier function to the given value to transform it.
@@ -129,20 +150,10 @@ module Calyx
129
150
  raise Errors::DuplicateRule.new(key)
130
151
  end
131
152
 
132
- context[key.to_sym] = if value.is_a?(Array)
133
- Production::Choices.parse(value, self)
134
- else
135
- Production::Concat.parse(value.to_s, self)
136
- end
153
+ define_context_rule(key, caller_locations.last, value)
137
154
  end
138
155
 
139
- expansion = expand(start_symbol)
140
-
141
- if expansion.respond_to?(:evaluate)
142
- [start_symbol, expansion.evaluate(random)]
143
- else
144
- raise Errors::MissingRule.new(start_symbol)
145
- end
156
+ [start_symbol, expand(start_symbol).evaluate(random)]
146
157
  end
147
158
 
148
159
  private
@@ -0,0 +1,17 @@
1
+ module Calyx
2
+ # Represents a named rule connected to a tree of productions that can be
3
+ # evaluated and a trace which represents where the rule was declared.
4
+ class Rule
5
+ attr_reader :name, :productions, :trace
6
+
7
+ def initialize(name, productions, trace)
8
+ @name = name.to_sym
9
+ @productions = productions
10
+ @trace = trace
11
+ end
12
+
13
+ def evaluate(random)
14
+ productions.evaluate(random)
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module Calyx
2
- VERSION = '0.14.2'.freeze
2
+ VERSION = '0.15.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: calyx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.2
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Rickerby
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-21 00:00:00.000000000 Z
11
+ date: 2017-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -85,6 +85,7 @@ files:
85
85
  - lib/calyx/production/unique.rb
86
86
  - lib/calyx/production/weighted_choices.rb
87
87
  - lib/calyx/registry.rb
88
+ - lib/calyx/rule.rb
88
89
  - lib/calyx/version.rb
89
90
  homepage: https://github.com/maetl/calyx
90
91
  licenses: