simply_stored 0.3.9 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +38 -0
- data/README.md +45 -24
- data/lib/simply_stored/couch/association_property.rb +27 -0
- data/lib/simply_stored/couch/belongs_to.rb +5 -7
- data/lib/simply_stored/couch/finders.rb +5 -1
- data/lib/simply_stored/couch/has_and_belongs_to_many.rb +202 -0
- data/lib/simply_stored/couch/has_many.rb +6 -51
- data/lib/simply_stored/couch/has_one.rb +4 -29
- data/lib/simply_stored/couch/properties.rb +11 -0
- data/lib/simply_stored/couch.rb +38 -2
- data/lib/simply_stored/instance_methods.rb +68 -29
- data/lib/simply_stored.rb +3 -1
- data/test/{couchdb/couch_active_model_compatibility_test.rb → active_model_compatibility_test.rb} +2 -2
- data/test/{couchdb/couch_belongs_to_test.rb → belongs_to_test.rb} +13 -3
- data/test/{couchdb/couch_conflict_handling_test.rb → conflict_handling_test.rb} +3 -3
- data/test/{couchdb/couch_finder_test.rb → finder_test.rb} +8 -3
- data/test/fixtures/couch.rb +55 -0
- data/test/has_and_belongs_to_many_test.rb +639 -0
- data/test/{couchdb/couch_has_many_test.rb → has_many_test.rb} +13 -3
- data/test/{couchdb/couch_has_one_test.rb → has_one_test.rb} +13 -3
- data/test/{couchdb/couch_instance_lifecycle_test.rb → instance_lifecycle_test.rb} +3 -3
- data/test/{couchdb/couch_mass_assignment_protection_test.rb → mass_assignment_protection_test.rb} +3 -3
- data/test/{couchdb/couch_s3_test.rb → s3_test.rb} +3 -3
- data/test/{couchdb/couch_soft_deletable_test.rb → soft_deletable_test.rb} +3 -3
- data/test/test_helper.rb +1 -5
- data/test/{couchdb/couch_validations_test.rb → validations_test.rb} +3 -3
- data/test/{couchdb/custom_views_test.rb → views_test.rb} +3 -3
- metadata +36 -234
- data/lib/simply_stored/simpledb/associations.rb +0 -215
- data/lib/simply_stored/simpledb/attributes.rb +0 -173
- data/lib/simply_stored/simpledb/storag.rb +0 -85
- data/lib/simply_stored/simpledb/validations.rb +0 -88
- data/lib/simply_stored/simpledb.rb +0 -216
- data/test/fixtures/simpledb/item.rb +0 -11
- data/test/fixtures/simpledb/item_daddy.rb +0 -8
- data/test/fixtures/simpledb/log_item.rb +0 -3
- data/test/fixtures/simpledb/namespace_bar.rb +0 -5
- data/test/fixtures/simpledb/namespace_foo.rb +0 -7
- data/test/fixtures/simpledb/protected_item.rb +0 -3
- data/test/simply_stored_simpledb_test.rb +0 -1376
- data/test/vendor/dhaka-2.2.1/lib/dhaka/dot/dot.rb +0 -29
- data/test/vendor/dhaka-2.2.1/lib/dhaka/evaluator/evaluator.rb +0 -133
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/closure_hash.rb +0 -15
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/grammar.rb +0 -240
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/grammar_symbol.rb +0 -27
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/precedence.rb +0 -19
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/production.rb +0 -36
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/accept_actions.rb +0 -36
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/alphabet.rb +0 -21
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/compiled_lexer.rb +0 -46
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/dfa.rb +0 -121
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexeme.rb +0 -32
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexer.rb +0 -70
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexer_run.rb +0 -78
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_grammar.rb +0 -392
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_parser.rb +0 -2010
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_tokenizer.rb +0 -14
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/specification.rb +0 -96
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/state.rb +0 -68
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/state_machine.rb +0 -37
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/action.rb +0 -55
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/channel.rb +0 -58
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/compiled_parser.rb +0 -51
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/conflict.rb +0 -54
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/item.rb +0 -42
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parse_result.rb +0 -50
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parse_tree.rb +0 -66
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser.rb +0 -165
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_methods.rb +0 -11
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_run.rb +0 -39
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_state.rb +0 -74
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/token.rb +0 -22
- data/test/vendor/dhaka-2.2.1/lib/dhaka/runtime.rb +0 -51
- data/test/vendor/dhaka-2.2.1/lib/dhaka/tokenizer/tokenizer.rb +0 -190
- data/test/vendor/dhaka-2.2.1/lib/dhaka.rb +0 -62
- data/test/vendor/dhaka-2.2.1/test/all_tests.rb +0 -5
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_evaluator.rb +0 -64
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_evaluator_test.rb +0 -43
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_grammar.rb +0 -41
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_grammar_test.rb +0 -9
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_test_methods.rb +0 -9
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_tokenizer.rb +0 -39
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_tokenizer_test.rb +0 -38
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_evaluator.rb +0 -43
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_grammar.rb +0 -24
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_grammar_test.rb +0 -30
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_lexer_specification.rb +0 -23
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_parser_test.rb +0 -33
- data/test/vendor/dhaka-2.2.1/test/brackets/bracket_grammar.rb +0 -23
- data/test/vendor/dhaka-2.2.1/test/brackets/bracket_tokenizer.rb +0 -22
- data/test/vendor/dhaka-2.2.1/test/brackets/brackets_test.rb +0 -28
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_driver.rb +0 -46
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_driver_test.rb +0 -276
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_evaluator.rb +0 -284
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_evaluator_test.rb +0 -38
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_grammar.rb +0 -104
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer.rb +0 -109
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer_specification.rb +0 -37
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer_test.rb +0 -58
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_parser.rb +0 -879
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_parser_test.rb +0 -55
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_test.rb +0 -170
- data/test/vendor/dhaka-2.2.1/test/core/another_lalr_but_not_slr_grammar.rb +0 -20
- data/test/vendor/dhaka-2.2.1/test/core/compiled_parser_test.rb +0 -44
- data/test/vendor/dhaka-2.2.1/test/core/dfa_test.rb +0 -170
- data/test/vendor/dhaka-2.2.1/test/core/evaluator_test.rb +0 -22
- data/test/vendor/dhaka-2.2.1/test/core/grammar_test.rb +0 -83
- data/test/vendor/dhaka-2.2.1/test/core/lalr_but_not_slr_grammar.rb +0 -19
- data/test/vendor/dhaka-2.2.1/test/core/lexer_test.rb +0 -139
- data/test/vendor/dhaka-2.2.1/test/core/malformed_grammar.rb +0 -7
- data/test/vendor/dhaka-2.2.1/test/core/malformed_grammar_test.rb +0 -8
- data/test/vendor/dhaka-2.2.1/test/core/nullable_grammar.rb +0 -21
- data/test/vendor/dhaka-2.2.1/test/core/parse_result_test.rb +0 -44
- data/test/vendor/dhaka-2.2.1/test/core/parser_state_test.rb +0 -24
- data/test/vendor/dhaka-2.2.1/test/core/parser_test.rb +0 -131
- data/test/vendor/dhaka-2.2.1/test/core/precedence_grammar.rb +0 -17
- data/test/vendor/dhaka-2.2.1/test/core/precedence_grammar_test.rb +0 -9
- data/test/vendor/dhaka-2.2.1/test/core/rr_conflict_grammar.rb +0 -21
- data/test/vendor/dhaka-2.2.1/test/core/simple_grammar.rb +0 -22
- data/test/vendor/dhaka-2.2.1/test/core/sr_conflict_grammar.rb +0 -16
- data/test/vendor/dhaka-2.2.1/test/dhaka_test_helper.rb +0 -17
- data/test/vendor/dhaka-2.2.1/test/fake_logger.rb +0 -17
- data/test/vendor/simplerdb-0.2/lib/simplerdb/client_exception.rb +0 -10
- data/test/vendor/simplerdb-0.2/lib/simplerdb/db.rb +0 -146
- data/test/vendor/simplerdb-0.2/lib/simplerdb/query_language.rb +0 -266
- data/test/vendor/simplerdb-0.2/lib/simplerdb/server.rb +0 -33
- data/test/vendor/simplerdb-0.2/lib/simplerdb/servlet.rb +0 -191
- data/test/vendor/simplerdb-0.2/lib/simplerdb.rb +0 -3
- data/test/vendor/simplerdb-0.2/test/functional_test.rb +0 -81
- data/test/vendor/simplerdb-0.2/test/query_evaluator_test.rb +0 -73
- data/test/vendor/simplerdb-0.2/test/query_parser_test.rb +0 -64
- data/test/vendor/simplerdb-0.2/test/simplerdb_test.rb +0 -80
@@ -1,29 +0,0 @@
|
|
1
|
-
module Dhaka
|
2
|
-
module Dot #:nodoc:
|
3
|
-
class Digraph #:nodoc:
|
4
|
-
def initialize(node_attributes = {})
|
5
|
-
@result = ["digraph x {"]
|
6
|
-
@result << %(node #{dotify_hash(node_attributes)})
|
7
|
-
yield(self)
|
8
|
-
@result << '}'
|
9
|
-
end
|
10
|
-
|
11
|
-
def node(obj, attributes = {})
|
12
|
-
@result << "#{obj.object_id} #{dotify_hash(attributes)}"
|
13
|
-
end
|
14
|
-
|
15
|
-
def edge(src, dest, attributes = {})
|
16
|
-
@result << "#{src.object_id} -> #{dest.object_id} #{dotify_hash(attributes)}"
|
17
|
-
end
|
18
|
-
|
19
|
-
def dotify_hash hash
|
20
|
-
sorted_key_value_pairs = hash.collect {|key, value| [key.to_s, value.to_s]}.sort
|
21
|
-
hash.empty? ? "" : '[' + sorted_key_value_pairs.collect {|key, value| "#{key}=#{value.to_s.inspect}"}.join(' ') + ']'
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_dot
|
25
|
-
@result.join("\n")
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,133 +0,0 @@
|
|
1
|
-
module Dhaka
|
2
|
-
# Abstract base class for evaluators.
|
3
|
-
#
|
4
|
-
# Defining an evaluator is an easy way to perform syntax-directed evaluation without having to generate an abstract
|
5
|
-
# syntax tree representation of the input.
|
6
|
-
#
|
7
|
-
# An evaluation rule for a given production named +bar+ is defined by calling +for_bar+ with
|
8
|
-
# a block that performs the evaluation. For detailed examples, see the evaluators in the
|
9
|
-
# test suite.
|
10
|
-
#
|
11
|
-
# The following is an evaluator for arithmetic expressions. When a parse tree node is encountered that
|
12
|
-
# corresponds to the production named +addition+, the block passed to +for_addition+ is invoked. The +evaluate+
|
13
|
-
# method is then recursively called on the child nodes, in this case the operands to the addition operation. The
|
14
|
-
# result is obtained by adding the evaluation results of the child nodes.
|
15
|
-
#
|
16
|
-
# class ArithmeticPrecedenceEvaluator < Dhaka::Evaluator
|
17
|
-
#
|
18
|
-
# self.grammar = ArithmeticPrecedenceGrammar
|
19
|
-
#
|
20
|
-
# define_evaluation_rules do
|
21
|
-
#
|
22
|
-
# for_subtraction do
|
23
|
-
# evaluate(child_nodes[0]) - evaluate(child_nodes[2])
|
24
|
-
# end
|
25
|
-
#
|
26
|
-
# for_addition do
|
27
|
-
# evaluate(child_nodes[0]) + evaluate(child_nodes[2])
|
28
|
-
# end
|
29
|
-
#
|
30
|
-
# for_division do
|
31
|
-
# evaluate(child_nodes[0]).to_f/evaluate(child_nodes[2])
|
32
|
-
# end
|
33
|
-
#
|
34
|
-
# for_multiplication do
|
35
|
-
# evaluate(child_nodes[0]) * evaluate(child_nodes[2])
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
# for_literal do
|
39
|
-
# child_nodes[0].token.value.to_i
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# for_parenthetized_expression do
|
43
|
-
# evaluate(child_nodes[1])
|
44
|
-
# end
|
45
|
-
#
|
46
|
-
# for_negated_expression do
|
47
|
-
# -evaluate(child_nodes[1])
|
48
|
-
# end
|
49
|
-
#
|
50
|
-
# for_power do
|
51
|
-
# evaluate(child_nodes[0])**evaluate(child_nodes[2])
|
52
|
-
# end
|
53
|
-
#
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
# end
|
57
|
-
|
58
|
-
class Evaluator < SimpleDelegator
|
59
|
-
class << self
|
60
|
-
# Define evaluation rules within a block passed to this method. The evaluator will define
|
61
|
-
# default evaluation rules for pass-through productions (i.e. productions with expansions
|
62
|
-
# consisting of exactly one grammar symbol). The default evaluation rule for such productions
|
63
|
-
# is to simply return the result of calling +evaluate+ on the unique child node. Setting the
|
64
|
-
# <tt>:raise_error</tt> option to true tells the evaluator to throw an exception if you neglect
|
65
|
-
# to define a rule for a non-pass-through production (one where the expansion consists of
|
66
|
-
# multiple symbols), listing all the productions that absolutely need to be defined before you
|
67
|
-
# can continue.
|
68
|
-
def define_evaluation_rules(options = {})
|
69
|
-
yield
|
70
|
-
check_definitions(options)
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def check_definitions(options)
|
76
|
-
filter = lambda {|productions| productions.map {|production| production.name} - actions}
|
77
|
-
pass_through_productions_without_rules = filter[grammar.productions.select {|production| production.expansion.size == 1}]
|
78
|
-
pass_through_productions_without_rules.each do |rule_name|
|
79
|
-
send(:define_method, rule_name) do
|
80
|
-
evaluate(child_nodes.first)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
non_trivial_productions_with_rules_undefined = filter[grammar.productions.select {|production| production.expansion.size != 1}]
|
84
|
-
raise EvaluatorDefinitionError.new(non_trivial_productions_with_rules_undefined) unless non_trivial_productions_with_rules_undefined.empty? || !options[:raise_error]
|
85
|
-
end
|
86
|
-
|
87
|
-
def inherited(evaluator)
|
88
|
-
class << evaluator
|
89
|
-
attr_accessor :grammar, :actions
|
90
|
-
end
|
91
|
-
evaluator.actions = []
|
92
|
-
end
|
93
|
-
|
94
|
-
def method_missing(method_name, *args, &blk)
|
95
|
-
name = method_name.to_s
|
96
|
-
if name =~ /^for_(.+)$/
|
97
|
-
rule_name = $1
|
98
|
-
raise "Attempted to define evaluation rule for non-existent production '#{rule_name}'" unless grammar.production_named(rule_name)
|
99
|
-
actions << rule_name
|
100
|
-
send(:define_method, rule_name, &blk)
|
101
|
-
else
|
102
|
-
super
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
# Evaluate a parse tree node.
|
108
|
-
def evaluate node
|
109
|
-
@node_stack ||= []
|
110
|
-
@node_stack << node
|
111
|
-
__setobj__(@node_stack.last)
|
112
|
-
result = send(node.production.name)
|
113
|
-
@node_stack.pop
|
114
|
-
__setobj__(@node_stack.last)
|
115
|
-
result
|
116
|
-
end
|
117
|
-
|
118
|
-
def initialize
|
119
|
-
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
class EvaluatorDefinitionError < StandardError #:nodoc:
|
124
|
-
def initialize(non_trivial_productions_with_rules_undefined)
|
125
|
-
@non_trivial_productions_with_rules_undefined = non_trivial_productions_with_rules_undefined
|
126
|
-
end
|
127
|
-
|
128
|
-
def to_s
|
129
|
-
result = "The following non-trivial productions do not have any evaluation rules defined:\n"
|
130
|
-
result << @non_trivial_productions_with_rules_undefined.join("\n")
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
@@ -1,240 +0,0 @@
|
|
1
|
-
module Dhaka
|
2
|
-
|
3
|
-
# Reserved name for the start symbol for all grammars.
|
4
|
-
START_SYMBOL_NAME = "_Start_"
|
5
|
-
END_SYMBOL_NAME = "_End_" #:nodoc:
|
6
|
-
|
7
|
-
# Productions for specific grammar symbols are defined in the context of this class.
|
8
|
-
class ProductionBuilder
|
9
|
-
# +symbol+ is the grammar symbol that productions are being defined for.
|
10
|
-
def initialize(grammar, symbol)
|
11
|
-
@grammar = grammar
|
12
|
-
@symbol = symbol
|
13
|
-
end
|
14
|
-
|
15
|
-
def to_byte(char)
|
16
|
-
char.respond_to?(:bytes) ? char.bytes.first : char[0]
|
17
|
-
end
|
18
|
-
|
19
|
-
# Creates a new production for +symbol+ with an expansion of +expansion+. The options hash can include
|
20
|
-
# a directive <tt>:prec</tt>, the value of which is a grammar symbol name. The precedence of the production is then
|
21
|
-
# set to the precedence of the grammar symbol corresponding to that name.
|
22
|
-
#
|
23
|
-
# See the arithmetic precedence grammar in the test suites for an example.
|
24
|
-
def method_missing(production_name, expansion, options = {}, &blk)
|
25
|
-
expansion_symbols = Array(expansion).collect {|name| @grammar.symbols[name]}
|
26
|
-
production_args = [@symbol, expansion_symbols, production_name.to_s, blk, @grammar.production_index]
|
27
|
-
if precedence_symbol_name = options[:prec]
|
28
|
-
production_args << @grammar.symbol_for_name(precedence_symbol_name).precedence
|
29
|
-
end
|
30
|
-
|
31
|
-
production = Production.new(*production_args)
|
32
|
-
@grammar.production_index += 1
|
33
|
-
|
34
|
-
@symbol.nullable = true if expansion_symbols.empty?
|
35
|
-
@grammar.productions_by_symbol[production.symbol] << production
|
36
|
-
raise "Duplicate production named #{production.name}" if @grammar.productions_by_name[production.name]
|
37
|
-
@grammar.productions_by_name[production.name] = production
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# The precedence builder defines three methods, +left+, +right+ and +nonassoc+. These accept arrays of grammar
|
42
|
-
# symbols all of which have the same precedence level and associativity.
|
43
|
-
#
|
44
|
-
# See the arithmetic precedence grammar in the test suites for an example of how this works.
|
45
|
-
class PrecedenceBuilder
|
46
|
-
def initialize(grammar) #:nodoc:
|
47
|
-
@grammar = grammar
|
48
|
-
@precedence_level = 0
|
49
|
-
end
|
50
|
-
|
51
|
-
[:left, :right, :nonassoc].each do |associativity|
|
52
|
-
define_method(associativity) do |symbols|
|
53
|
-
assign_precedences associativity, symbols
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
def assign_precedences(associativity, symbol_names)
|
59
|
-
symbol_names.each do |symbol_name|
|
60
|
-
symbol = @grammar.symbols[symbol_name]
|
61
|
-
symbol.precedence = Precedence.new(@precedence_level, associativity)
|
62
|
-
end
|
63
|
-
@precedence_level += 1
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
# Abstract base class for grammar specifications.
|
68
|
-
#
|
69
|
-
# The following is a grammar specification for simple arithmetic. Precedences are specified as in Yacc -
|
70
|
-
# in ascending order of binding strength, with equal-strength symbols on the same level.
|
71
|
-
# Production rules are specified for each symbol by specifying the name of the production (used when
|
72
|
-
# encoding the Evaluator) and the expansion for that particular production. For example, the production named
|
73
|
-
# +addition+ expands the symbol <tt>'E'</tt> to the list of symbols <tt>['E', '+', 'E']</tt>.
|
74
|
-
#
|
75
|
-
# class ArithmeticPrecedenceGrammar < Dhaka::Grammar
|
76
|
-
# precedences do
|
77
|
-
# left ['+', '-']
|
78
|
-
# left ['*', '/']
|
79
|
-
# nonassoc ['^']
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# for_symbol(Dhaka::START_SYMBOL_NAME) do
|
83
|
-
# expression ['E']
|
84
|
-
# end
|
85
|
-
#
|
86
|
-
# for_symbol('E') do
|
87
|
-
# addition ['E', '+', 'E']
|
88
|
-
# subtraction ['E', '-', 'E']
|
89
|
-
# multiplication ['E', '*', 'E']
|
90
|
-
# division ['E', '/', 'E']
|
91
|
-
# power ['E', '^', 'E']
|
92
|
-
# literal ['n']
|
93
|
-
# parenthetized_expression ['(', 'E', ')']
|
94
|
-
# negated_expression ['-', 'E'], :prec => '*'
|
95
|
-
# end
|
96
|
-
# end
|
97
|
-
#
|
98
|
-
# In the above grammar, the symbols <tt>+</tt> and <tt>-</tt> are declared as being +left+-associative, meaning that
|
99
|
-
# 1 + 2 + 3 is parsed as (1 + 2) + 3 as opposed to 1 + (2 + 3) (+right+-associativity). The symbol <tt>^</tt> is declared
|
100
|
-
# +nonassoc+ which means that expressions such as 2 ^ 3 ^ 4 are not allowed (non-associative). <tt>+</tt> and <tt>-</tt> are listed
|
101
|
-
# before <tt>^</tt> which means that they bind lower, and an expression such as 2 + 3 ^ 5 will be always be parsed as
|
102
|
-
# 2 + (3 ^ 5) and not (2 + 3) ^ 5.
|
103
|
-
class Grammar
|
104
|
-
class << self
|
105
|
-
# Used for defining the Production-s for the symbol with name +symbol+. The block +blk+ is
|
106
|
-
# evaluated in the context of a ProductionBuilder.
|
107
|
-
def for_symbol symbol, &blk
|
108
|
-
symbol = symbols[symbol]
|
109
|
-
symbol.non_terminal = true
|
110
|
-
ProductionBuilder.new(self, symbol).instance_eval(&blk)
|
111
|
-
end
|
112
|
-
|
113
|
-
# Used for defining the precedences and associativities of symbols. The block +blk+ is
|
114
|
-
# evaluated in the context of a PrecedenceBuilder.
|
115
|
-
def precedences &blk
|
116
|
-
PrecedenceBuilder.new(self).instance_eval(&blk)
|
117
|
-
end
|
118
|
-
|
119
|
-
# Returns the grammar symbol identified by +name+
|
120
|
-
def symbol_for_name(name)
|
121
|
-
if symbols.has_key? name
|
122
|
-
symbols[name]
|
123
|
-
else
|
124
|
-
raise "No symbol with name #{name} found"
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
# Returns a list of all the Production-s in this grammar.
|
129
|
-
def productions
|
130
|
-
productions_by_name.values
|
131
|
-
end
|
132
|
-
|
133
|
-
def productions_for_symbol(symbol) #:nodoc:
|
134
|
-
productions_by_symbol[symbol]
|
135
|
-
end
|
136
|
-
|
137
|
-
def closure(kernel) #:nodoc:
|
138
|
-
channels = Hash.new {|hash, start_item| hash[start_item] = Set.new}
|
139
|
-
result = compute_closure(kernel) do |hash, item|
|
140
|
-
if item.next_symbol and item.next_symbol.non_terminal
|
141
|
-
productions_by_symbol[item.next_symbol].each do |production|
|
142
|
-
new_channel = spontaneous_channel(item, hash[Item.new(production, 0)])
|
143
|
-
channels[item] << new_channel
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
[result, channels]
|
148
|
-
end
|
149
|
-
|
150
|
-
def passive_channel(start_item, end_item) #:nodoc:
|
151
|
-
PassiveChannel.new(self, start_item, end_item)
|
152
|
-
end
|
153
|
-
|
154
|
-
def first(given_symbol) #:nodoc:
|
155
|
-
cached_result = __first_cache[given_symbol]
|
156
|
-
return cached_result if cached_result
|
157
|
-
result = compute_closure([given_symbol]) do |hash, symbol|
|
158
|
-
productions_by_symbol[symbol].each do |production|
|
159
|
-
symbol_index = 0
|
160
|
-
while next_symbol = production.expansion[symbol_index]
|
161
|
-
hash[next_symbol]
|
162
|
-
break unless next_symbol.nullable
|
163
|
-
symbol_index += 1
|
164
|
-
end
|
165
|
-
end if symbol.non_terminal
|
166
|
-
end.values.select {|symbol| symbol.terminal}.to_set
|
167
|
-
__first_cache[given_symbol] = result
|
168
|
-
result
|
169
|
-
end
|
170
|
-
|
171
|
-
# Returns the Production identified by +name+.
|
172
|
-
def production_named(name)
|
173
|
-
productions_by_name[name]
|
174
|
-
end
|
175
|
-
|
176
|
-
# Returns the set of terminal symbols in the grammar.
|
177
|
-
def terminal_symbols
|
178
|
-
symbols.values.select {|symbol| symbol.terminal}
|
179
|
-
end
|
180
|
-
|
181
|
-
# Returns the set of non-terminal symbols in the grammar.
|
182
|
-
def non_terminal_symbols
|
183
|
-
symbols.values.select {|symbol| symbol.non_terminal}
|
184
|
-
end
|
185
|
-
|
186
|
-
# Export the grammar to a BNF-like format
|
187
|
-
def to_bnf
|
188
|
-
result = []
|
189
|
-
last_symbol = nil
|
190
|
-
productions.sort.each do |production|
|
191
|
-
if production.symbol != last_symbol
|
192
|
-
result << ""
|
193
|
-
result << "#{production.symbol.name.inspect} :"
|
194
|
-
last_symbol = production.symbol
|
195
|
-
end
|
196
|
-
result << " | #{production.expansion.collect{|symbol| symbol.name.inspect}.join(' ')}"
|
197
|
-
end
|
198
|
-
result.join("\n")
|
199
|
-
end
|
200
|
-
|
201
|
-
private
|
202
|
-
|
203
|
-
def inherited(grammar)
|
204
|
-
class << grammar
|
205
|
-
attr_accessor :symbols, :productions_by_symbol, :productions_by_name, :start_symbol, :end_symbol, :__first_cache, :production_index
|
206
|
-
end
|
207
|
-
grammar.symbols = Hash.new {|hash, name| hash[name] = GrammarSymbol.new(name)}
|
208
|
-
grammar.productions_by_symbol = Hash.new {|hash, name| hash[name] = Set.new([])}
|
209
|
-
grammar.productions_by_name = {}
|
210
|
-
grammar.end_symbol = grammar.symbols[END_SYMBOL_NAME]
|
211
|
-
grammar.start_symbol = grammar.symbols[START_SYMBOL_NAME]
|
212
|
-
grammar.__first_cache = {}
|
213
|
-
grammar.production_index = 0
|
214
|
-
end
|
215
|
-
|
216
|
-
def spontaneous_channel(start_item, end_item)
|
217
|
-
SpontaneousChannel.new(self, start_item, end_item)
|
218
|
-
end
|
219
|
-
|
220
|
-
def compute_closure(initial)
|
221
|
-
closure_hash = ClosureHash.new do |hash, item|
|
222
|
-
hash.dirty = true
|
223
|
-
hash[item] = item
|
224
|
-
end
|
225
|
-
|
226
|
-
closure_hash.load_set(initial)
|
227
|
-
|
228
|
-
loop do
|
229
|
-
closure_hash.keys.each do |element|
|
230
|
-
yield closure_hash, element
|
231
|
-
end
|
232
|
-
break unless closure_hash.dirty
|
233
|
-
closure_hash.dirty = false
|
234
|
-
end
|
235
|
-
closure_hash
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
module Dhaka
|
2
|
-
# Each grammar symbol is uniquely identified by a string name. The name of a symbol can
|
3
|
-
# be anything (except the two reserved names <tt>'\_Start_'</tt> and <tt>'\_End_'</tt>) and need not
|
4
|
-
# correspond to its character representation. For example, an ampersand in the input string could
|
5
|
-
# be tokenized as a symbol with a name 'AND_OP'. You never have to directly instantiate a
|
6
|
-
# GrammarSymbol. It is done implicitly for you when you define a Grammar.
|
7
|
-
class GrammarSymbol
|
8
|
-
attr_reader :name
|
9
|
-
attr_accessor :non_terminal, :nullable, :precedence, :associativity
|
10
|
-
|
11
|
-
def initialize(name)
|
12
|
-
@name = name
|
13
|
-
end
|
14
|
-
|
15
|
-
def terminal
|
16
|
-
!non_terminal
|
17
|
-
end
|
18
|
-
|
19
|
-
def to_s #:nodoc:
|
20
|
-
name.dup
|
21
|
-
end
|
22
|
-
|
23
|
-
def <=> other
|
24
|
-
name <=> other.name
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Dhaka
|
2
|
-
class Precedence #:nodoc:
|
3
|
-
include Comparable
|
4
|
-
attr_reader :precedence_level, :associativity
|
5
|
-
|
6
|
-
def initialize(precedence_level, associativity)
|
7
|
-
@precedence_level = precedence_level
|
8
|
-
@associativity = associativity
|
9
|
-
end
|
10
|
-
|
11
|
-
def <=> other
|
12
|
-
precedence_level <=> other.precedence_level
|
13
|
-
end
|
14
|
-
|
15
|
-
def to_s
|
16
|
-
"Precedence: #{precedence_level} Associativity: #{associativity}"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module Dhaka
|
2
|
-
class Production
|
3
|
-
include Comparable
|
4
|
-
|
5
|
-
attr_reader :symbol, :expansion, :name, :action, :priority
|
6
|
-
|
7
|
-
def initialize(symbol, expansion, name, action, priority, precedence = nil)
|
8
|
-
@symbol = symbol
|
9
|
-
@expansion = expansion
|
10
|
-
@name = name
|
11
|
-
@precedence = precedence
|
12
|
-
@action = action || proc { self }
|
13
|
-
@priority = priority
|
14
|
-
end
|
15
|
-
|
16
|
-
def precedence
|
17
|
-
unless @precedence
|
18
|
-
expansion.reverse_each do |symbol|
|
19
|
-
if symbol.terminal
|
20
|
-
@precedence = symbol.precedence
|
21
|
-
break
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
@precedence
|
26
|
-
end
|
27
|
-
|
28
|
-
def to_s #:nodoc:
|
29
|
-
"#{name} #{symbol} ::= #{expansion.join(' ')}"
|
30
|
-
end
|
31
|
-
|
32
|
-
def <=> other
|
33
|
-
priority <=> other.priority
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module Dhaka
|
2
|
-
module LexerSupport
|
3
|
-
class AcceptAction
|
4
|
-
attr_reader :pattern
|
5
|
-
def initialize(pattern)
|
6
|
-
@pattern = pattern
|
7
|
-
end
|
8
|
-
|
9
|
-
def call(lexer_run)
|
10
|
-
lexer_run.accept(pattern)
|
11
|
-
end
|
12
|
-
|
13
|
-
def compile_to_ruby_source
|
14
|
-
"accept(#{pattern.inspect})"
|
15
|
-
end
|
16
|
-
|
17
|
-
def to_dot
|
18
|
-
"Accept #{pattern.inspect}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class LookaheadAcceptAction < AcceptAction
|
23
|
-
def call(lexer_run)
|
24
|
-
lexer_run.accept_last_saved_checkpoint(pattern)
|
25
|
-
end
|
26
|
-
|
27
|
-
def compile_to_ruby_source
|
28
|
-
"accept_with_lookahead(#{pattern.inspect})"
|
29
|
-
end
|
30
|
-
|
31
|
-
def to_dot
|
32
|
-
"Accept With Lookahead #{pattern.inspect}"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Dhaka
|
2
|
-
module LexerSupport
|
3
|
-
DIGITS = ('0'..'9').to_a
|
4
|
-
LOWERCASE_LETTERS = ('a'..'z').to_a
|
5
|
-
UPPERCASE_LETTERS = ('A'..'Z').to_a
|
6
|
-
LETTERS = LOWERCASE_LETTERS + UPPERCASE_LETTERS
|
7
|
-
WHITESPACE = [" ", "\r", "\n", "\t"]
|
8
|
-
SYMBOLS = %w| ~ ` ! @ # % & _ = : ; " ' < , > - |
|
9
|
-
CLASSES = {'d' => DIGITS, 'w' => LETTERS, 's' => WHITESPACE}
|
10
|
-
|
11
|
-
OPERATOR_CHARACTERS = {'(' => 'open_parenth', ')' => 'close_parenth', '[' => 'open_square_bracket',
|
12
|
-
']' => 'close_square_bracket', '+' => 'plus', '*' => 'asterisk',
|
13
|
-
'?' => 'question_mark', '.' => 'period', '\\' => 'back_slash',
|
14
|
-
'|' => 'pipe', '{' => 'left_curly_brace', '}' => 'right_curly_brace',
|
15
|
-
'/' => 'forward_slash', '^' => 'caret', '$' => 'dollar'}
|
16
|
-
|
17
|
-
SET_OPERATOR_CHARACTERS = %w| - ^ [ ] \\ |
|
18
|
-
|
19
|
-
ALL_CHARACTERS = DIGITS + LETTERS + SYMBOLS + WHITESPACE + OPERATOR_CHARACTERS.keys
|
20
|
-
end
|
21
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
module Dhaka
|
2
|
-
# Abstract base class of all compiled Lexers. It is only used by generated code.
|
3
|
-
class CompiledLexer
|
4
|
-
|
5
|
-
class << self
|
6
|
-
# Returns a LexerRun that tokenizes +input+.
|
7
|
-
def lex input
|
8
|
-
LexerRun.new(self, input)
|
9
|
-
end
|
10
|
-
|
11
|
-
def start_state #:nodoc:
|
12
|
-
states[start_state_id]
|
13
|
-
end
|
14
|
-
|
15
|
-
def action_for_pattern pattern #:nodoc:
|
16
|
-
specification.items[pattern].action
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
def inherited(lexer)
|
21
|
-
class << lexer
|
22
|
-
attr_accessor :states, :specification, :start_state_id
|
23
|
-
end
|
24
|
-
lexer.states = Hash.new do |hash, state_id|
|
25
|
-
hash[state_id] = LexerSupport::State.new(lexer, nil)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def at_state x, &blk
|
30
|
-
states[x].instance_eval(&blk)
|
31
|
-
end
|
32
|
-
|
33
|
-
def start_with start_state_id
|
34
|
-
self.start_state_id = start_state_id
|
35
|
-
end
|
36
|
-
|
37
|
-
def switch_to dest_state_id
|
38
|
-
states[dest_state_id]
|
39
|
-
end
|
40
|
-
|
41
|
-
def inspect
|
42
|
-
"<Dhaka::CompiledLexer specification : #{specification}>"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|