wlang 0.8.4
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.
- data/LICENCE.rdoc +25 -0
- data/README.rdoc +111 -0
- data/bin/wlang +24 -0
- data/doc/specification/about.rdoc +61 -0
- data/doc/specification/dialects.wtpl +0 -0
- data/doc/specification/examples.rb +3 -0
- data/doc/specification/glossary.wtpl +14 -0
- data/doc/specification/hosting.rdoc +0 -0
- data/doc/specification/overview.rdoc +116 -0
- data/doc/specification/rulesets.wtpl +87 -0
- data/doc/specification/specification.css +52 -0
- data/doc/specification/specification.html +1361 -0
- data/doc/specification/specification.js +8 -0
- data/doc/specification/specification.wtpl +41 -0
- data/doc/specification/specification.yml +430 -0
- data/doc/specification/symbols.wtpl +16 -0
- data/lib/wlang.rb +186 -0
- data/lib/wlang/basic_object.rb +19 -0
- data/lib/wlang/dialect.rb +230 -0
- data/lib/wlang/dialect_dsl.rb +136 -0
- data/lib/wlang/dialect_loader.rb +69 -0
- data/lib/wlang/dialects/coderay_dialect.rb +35 -0
- data/lib/wlang/dialects/plain_text_dialect.rb +75 -0
- data/lib/wlang/dialects/rdoc_dialect.rb +33 -0
- data/lib/wlang/dialects/ruby_dialect.rb +35 -0
- data/lib/wlang/dialects/sql_dialect.rb +38 -0
- data/lib/wlang/dialects/standard_dialects.rb +113 -0
- data/lib/wlang/dialects/xhtml_dialect.rb +40 -0
- data/lib/wlang/encoder.rb +66 -0
- data/lib/wlang/encoder_set.rb +117 -0
- data/lib/wlang/errors.rb +37 -0
- data/lib/wlang/intelligent_buffer.rb +94 -0
- data/lib/wlang/parser.rb +251 -0
- data/lib/wlang/parser_context.rb +146 -0
- data/lib/wlang/ruby_extensions.rb +21 -0
- data/lib/wlang/rule.rb +66 -0
- data/lib/wlang/rule_set.rb +93 -0
- data/lib/wlang/rulesets/basic_ruleset.rb +75 -0
- data/lib/wlang/rulesets/buffering_ruleset.rb +103 -0
- data/lib/wlang/rulesets/context_ruleset.rb +115 -0
- data/lib/wlang/rulesets/encoding_ruleset.rb +73 -0
- data/lib/wlang/rulesets/imperative_ruleset.rb +132 -0
- data/lib/wlang/rulesets/ruleset_utils.rb +296 -0
- data/lib/wlang/template.rb +79 -0
- data/lib/wlang/wlang_command.rb +54 -0
- data/lib/wlang/wlang_command_options.rb +158 -0
- data/test/sandbox.rb +1 -0
- data/test/test_all.rb +8 -0
- data/test/wlang/anagram_bugs_test.rb +111 -0
- data/test/wlang/basic_ruleset_test.rb +52 -0
- data/test/wlang/buffering_ruleset_test.rb +102 -0
- data/test/wlang/buffering_template1.wtpl +1 -0
- data/test/wlang/buffering_template2.wtpl +1 -0
- data/test/wlang/buffering_template3.wtpl +1 -0
- data/test/wlang/buffering_template4.wtpl +1 -0
- data/test/wlang/buffering_template5.wtpl +1 -0
- data/test/wlang/context_ruleset_test.rb +32 -0
- data/test/wlang/data.rb +3 -0
- data/test/wlang/encoder_set_test.rb +42 -0
- data/test/wlang/imperative_ruleset_test.rb +107 -0
- data/test/wlang/intelligent_buffer_test.rb +194 -0
- data/test/wlang/othersymbols_test.rb +16 -0
- data/test/wlang/parser_context_test.rb +29 -0
- data/test/wlang/parser_test.rb +89 -0
- data/test/wlang/plain_text_dialect_test.rb +21 -0
- data/test/wlang/ruby_dialect_test.rb +100 -0
- data/test/wlang/ruby_expected.rb +3 -0
- data/test/wlang/ruby_template.wrb +3 -0
- data/test/wlang/ruleset_utils_test.rb +245 -0
- data/test/wlang/specification_examples_test.rb +52 -0
- data/test/wlang/test_utils.rb +25 -0
- data/test/wlang/wlang_test.rb +80 -0
- metadata +136 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'wlang/rulesets/ruleset_utils'
|
2
|
+
module WLang
|
3
|
+
class RuleSet
|
4
|
+
|
5
|
+
#
|
6
|
+
# Provides a ruleset for managing the context/scoping during instantitation.
|
7
|
+
#
|
8
|
+
# For an overview of this ruleset, see the wlang {specification file}[link://files/specification.html].
|
9
|
+
#
|
10
|
+
module Context
|
11
|
+
U=WLang::RuleSet::Utils
|
12
|
+
|
13
|
+
# Default mapping between tag symbols and methods
|
14
|
+
DEFAULT_RULESET = {'=' => :assignment,
|
15
|
+
'#=' => :block_assignment,
|
16
|
+
'%=' => :modulo_assignment,
|
17
|
+
'^=' => :encoding_assignment}
|
18
|
+
|
19
|
+
# Rule implementation of <tt>={wlang/hosted as x}{...}</tt>
|
20
|
+
def self.assignment(parser, offset)
|
21
|
+
expr, reached = parser.parse(offset, "wlang/ruby")
|
22
|
+
|
23
|
+
# decode expression
|
24
|
+
decoded = U.decode_expr_as(expr)
|
25
|
+
parser.syntax_error(offset) if decoded.nil?
|
26
|
+
|
27
|
+
# evaluate it
|
28
|
+
value = parser.evaluate(decoded[:expr])
|
29
|
+
|
30
|
+
# handle two different cases
|
31
|
+
if parser.has_block?(reached)
|
32
|
+
parser.context_push(decoded[:variable] => value)
|
33
|
+
text, reached = parser.parse_block(reached)
|
34
|
+
parser.context_pop
|
35
|
+
[text, reached]
|
36
|
+
else
|
37
|
+
parser.context_define(decoded[:variable], value)
|
38
|
+
["", reached]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Rule implementation of <tt>#={wlang/active-string}{...}{...}</tt>
|
43
|
+
def self.block_assignment(parser, offset)
|
44
|
+
variable, reached = parser.parse(offset, "wlang/active-string")
|
45
|
+
|
46
|
+
# decode expression
|
47
|
+
decoded = U.decode_var(variable)
|
48
|
+
parser.syntax_error(offset) if decoded.nil?
|
49
|
+
|
50
|
+
# parse second block in that dialect
|
51
|
+
value, reached = parser.parse_block(reached)
|
52
|
+
|
53
|
+
# handle two different cases
|
54
|
+
if parser.has_block?(reached)
|
55
|
+
parser.context_push(decoded[:variable] => value)
|
56
|
+
text, reached = parser.parse_block(reached)
|
57
|
+
parser.context_pop
|
58
|
+
[text, reached]
|
59
|
+
else
|
60
|
+
parser.context_define(decoded[:variable], value)
|
61
|
+
["", reached]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Rule implementation of <tt>%={wlang/active-string as x}{...}{...}</tt>
|
66
|
+
def self.modulo_assignment(parser, offset)
|
67
|
+
dialect_as, reached = parser.parse(offset, "wlang/ruby")
|
68
|
+
|
69
|
+
# decode expression
|
70
|
+
decoded = U.decode_qdialect_as(dialect_as)
|
71
|
+
parser.syntax_error(offset) if decoded.nil?
|
72
|
+
|
73
|
+
# parse second block in that dialect
|
74
|
+
value, reached = parser.parse_block(reached, decoded[:dialect])
|
75
|
+
|
76
|
+
# handle two different cases
|
77
|
+
if parser.has_block?(reached)
|
78
|
+
parser.context_push(decoded[:variable] => value)
|
79
|
+
text, reached = parser.parse_block(reached)
|
80
|
+
parser.context_pop
|
81
|
+
[text, reached]
|
82
|
+
else
|
83
|
+
parser.context_define(decoded[:variable], value)
|
84
|
+
["", reached]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Rule implementation of <tt>^={wlang/active-string as x}{...}{...}</tt>
|
89
|
+
def self.encoding_assignment(parser, offset)
|
90
|
+
encoding_as, reached = parser.parse(offset, "wlang/ruby")
|
91
|
+
|
92
|
+
# decode expression
|
93
|
+
decoded = U.decode_qencoder_as(encoding_as)
|
94
|
+
parser.syntax_error(offset) if decoded.nil?
|
95
|
+
|
96
|
+
# parse second block in that dialect
|
97
|
+
value, reached = parser.parse_block(reached)
|
98
|
+
value = parser.encode(value, decoded[:encoder])
|
99
|
+
|
100
|
+
# handle two different cases
|
101
|
+
if parser.has_block?(reached)
|
102
|
+
parser.context_push(decoded[:variable] => value)
|
103
|
+
text, reached = parser.parse_block(reached)
|
104
|
+
parser.context_pop
|
105
|
+
[text, reached]
|
106
|
+
else
|
107
|
+
parser.context_define(decoded[:variable], value)
|
108
|
+
["", reached]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end # module Context
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module WLang
|
2
|
+
class RuleSet
|
3
|
+
|
4
|
+
#
|
5
|
+
# Encoding RuleSet, commonly installed in any wlang dialect. Note that this
|
6
|
+
# ruleset overrides <tt>${...}</tt> implemented in WLang::RuleSet::Basic.
|
7
|
+
#
|
8
|
+
# For an overview of this ruleset, see the wlang {specification file}[link://files/specification.html].
|
9
|
+
#
|
10
|
+
module Encoding
|
11
|
+
|
12
|
+
# Default mapping between tag symbols and methods
|
13
|
+
DEFAULT_RULESET = {'&' => :main_encoding, '&;' => :entities_encoding,
|
14
|
+
"&'" => :single_quoting, '&"' => :double_quoting,
|
15
|
+
"$" => :injection, "'" => :single_quoted,
|
16
|
+
'"' => :double_quoted}
|
17
|
+
|
18
|
+
# Main encoding as <tt>&{...}</tt>
|
19
|
+
def self.main_encoding(parser, offset)
|
20
|
+
parsed, reached = parser.parse(offset)
|
21
|
+
result = parser.encode(parsed, "main-encoding")
|
22
|
+
[result, reached]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Entities-encoding as <tt>&;{...}</tt>
|
26
|
+
def self.entities_encoding(parser, offset)
|
27
|
+
parsed, reached = parser.parse(offset)
|
28
|
+
result = parser.encode(parsed, "entities-encoding")
|
29
|
+
[result, reached]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Single-quoting as <tt>&'{...}</tt>
|
33
|
+
def self.single_quoting(parser, offset)
|
34
|
+
parsed, reached = parser.parse(offset)
|
35
|
+
result = parser.encode(parsed, "single-quoting")
|
36
|
+
[result, reached]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Double-quoting as <tt>&"{...}</tt>
|
40
|
+
def self.double_quoting(parser, offset)
|
41
|
+
parsed, reached = parser.parse(offset)
|
42
|
+
result = parser.encode(parsed, "double-quoting")
|
43
|
+
[result, reached]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Injection as <tt>${wlang/ruby}</tt>
|
47
|
+
def self.injection(parser, offset)
|
48
|
+
expression, reached = parser.parse(offset, "wlang/ruby")
|
49
|
+
result = parser.evaluate(expression)
|
50
|
+
encoded = parser.encode(result.to_s, "main-encoding")
|
51
|
+
[encoded, reached]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Single-quoted as <tt>'{wlang/ruby}</tt>
|
55
|
+
def self.single_quoted(parser, offset)
|
56
|
+
expression, reached = parser.parse(offset, "wlang/ruby")
|
57
|
+
result = parser.evaluate(expression)
|
58
|
+
encoded = parser.encode(result.to_s, "single-quoting")
|
59
|
+
["'" << encoded, reached]
|
60
|
+
end
|
61
|
+
|
62
|
+
# Double-quoted as <tt>"{wlang/ruby}</tt>
|
63
|
+
def self.double_quoted(parser, offset)
|
64
|
+
expression, reached = parser.parse(offset, "wlang/ruby")
|
65
|
+
result = parser.evaluate(expression)
|
66
|
+
encoded = parser.encode(result.to_s, "double-quoting")
|
67
|
+
['"' << encoded, reached]
|
68
|
+
end
|
69
|
+
|
70
|
+
end # module Encoding
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module WLang
|
2
|
+
class RuleSet
|
3
|
+
|
4
|
+
#
|
5
|
+
# Imperative ruleset, providing special tags to iterate and instantiate
|
6
|
+
# conditionaly.
|
7
|
+
#
|
8
|
+
# For an overview of this ruleset, see the wlang {specification file}[link://files/specification.html].
|
9
|
+
#
|
10
|
+
module Imperative
|
11
|
+
U=WLang::RuleSet::Utils
|
12
|
+
|
13
|
+
# Regular expression for #1 in <tt>*{wlang/hosted}{...}</tt>
|
14
|
+
EACH_REGEXP = /^([^\s]+)((\s+(using\s+([_a-z]+)))?(\s+(as\s+([a-z]+(,\s+[a-z]+)*)))?)?$/
|
15
|
+
# 1 23 4 5 6 7 8
|
16
|
+
# 1 2 3 4 5
|
17
|
+
|
18
|
+
# Default mapping between tag symbols and methods
|
19
|
+
DEFAULT_RULESET = {'?' => :conditional, '*' => :enumeration}
|
20
|
+
|
21
|
+
#
|
22
|
+
# Conditional as <tt>?{wlang/hosted}{...}{...}</tt>
|
23
|
+
#
|
24
|
+
# (third block is optional) Instantiates #1, looking for an expression in the
|
25
|
+
# hosting language. Evaluates it, looking for a boolean value (according to
|
26
|
+
# boolean semantics of the hosting language). If true, instantiates #2,
|
27
|
+
# otherwise instantiates #3 if present.
|
28
|
+
#
|
29
|
+
def self.conditional(parser, offset)
|
30
|
+
expression, reached = parser.parse(offset, "wlang/ruby")
|
31
|
+
value = parser.evaluate(expression)
|
32
|
+
if value
|
33
|
+
then_block, reached = parser.parse_block(reached)
|
34
|
+
if parser.has_block?(reached)
|
35
|
+
else_block, reached = parser.parse_block(reached, "wlang/dummy")
|
36
|
+
end
|
37
|
+
[then_block, reached]
|
38
|
+
else
|
39
|
+
then_block, reached = parser.parse_block(reached, "wlang/dummy")
|
40
|
+
else_block = ""
|
41
|
+
if parser.has_block?(reached)
|
42
|
+
else_block, reached = parser.parse_block(reached)
|
43
|
+
end
|
44
|
+
[else_block, reached]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Install args on the parser
|
49
|
+
def self.merge_each_args(names, args)
|
50
|
+
hash = {}
|
51
|
+
size = names.length>args.length ? args.length : names.length
|
52
|
+
0.upto(size-1) do |i|
|
53
|
+
hash[names[i]] = args[i]
|
54
|
+
end
|
55
|
+
hash
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Enumeration as <tt>*{wlang/hosted using each as x}{...}{...}</tt>
|
60
|
+
#
|
61
|
+
# (third block is optional) Instantiates #1, looking for an expression in the
|
62
|
+
# hosting language. Evaluates it, looking for an enumerable. Iterates all its
|
63
|
+
# elements, instanciating #2 for each of them (the iterated value is set under
|
64
|
+
# name x in the scope). If #3 is present, it is instantiated between elements.
|
65
|
+
#
|
66
|
+
def self.enumeration(parser, offset)
|
67
|
+
expression, reached = parser.parse(offset, "wlang/ruby")
|
68
|
+
|
69
|
+
# decode 'wlang/hosted using each as x' expression
|
70
|
+
hash = U.expr(:expr,
|
71
|
+
["using", :var, false],
|
72
|
+
["as", :multi_as, false]).decode(expression, parser)
|
73
|
+
hash[:using] = "each" unless hash[:using]
|
74
|
+
hash[:as] = [] unless hash[:as]
|
75
|
+
parser.syntax_error(offset, "invalid loop expression '#{expression}'") if hash.nil?
|
76
|
+
|
77
|
+
# evaluate 'wlang/hosted' sub-expression
|
78
|
+
value = hash[:expr]
|
79
|
+
if value.nil?
|
80
|
+
expression, reached = parser.parse_block(reached, "wlang/dummy")
|
81
|
+
expression, reached = parser.parse_block(reached, "wlang/dummy") if parser.has_block?(reached)
|
82
|
+
["",reached]
|
83
|
+
else
|
84
|
+
raise "Enumerated value #{value} does not respond to #{hash[:using]}"\
|
85
|
+
unless value.respond_to?(hash[:using])
|
86
|
+
|
87
|
+
# some variables
|
88
|
+
iterator, names = hash[:using].to_sym, hash[:as]
|
89
|
+
buffer = parser.factor_buffer
|
90
|
+
|
91
|
+
# wlang start index of each block
|
92
|
+
block2, block3, theend = reached, nil, nil # *{}{block2}{block3}theend
|
93
|
+
first = true
|
94
|
+
|
95
|
+
# iterate now
|
96
|
+
value.send iterator do |*args|
|
97
|
+
if not(first) and parser.has_block?(block3)
|
98
|
+
# parse #3, positioned at reached after that
|
99
|
+
parsed, theend = parser.parse_block(block3)
|
100
|
+
parser.append_buffer(buffer, parsed, true)
|
101
|
+
end
|
102
|
+
|
103
|
+
# install arguments and parse block2, positioned at block3
|
104
|
+
parser.context_push(merge_each_args(names, args))
|
105
|
+
parsed, block3 = parser.parse_block(block2)
|
106
|
+
parser.append_buffer(buffer, parsed, true)
|
107
|
+
parser.context_pop
|
108
|
+
first = false
|
109
|
+
end
|
110
|
+
|
111
|
+
# Empty array special case
|
112
|
+
unless block3
|
113
|
+
parsed, block3 = parser.parse_block(block2, "wlang/dummy")
|
114
|
+
end
|
115
|
+
|
116
|
+
# Singleton array special case
|
117
|
+
unless theend
|
118
|
+
if parser.has_block?(block3)
|
119
|
+
parsed, theend = parser.parse_block(block3, "wlang/dummy")
|
120
|
+
else
|
121
|
+
theend = block3
|
122
|
+
end
|
123
|
+
end
|
124
|
+
[buffer, theend]
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end # module Imperative
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
module WLang
|
2
|
+
class RuleSet
|
3
|
+
module Utils
|
4
|
+
|
5
|
+
# Regexp string for 'dialect'
|
6
|
+
DIALECT = '[-a-z]+'
|
7
|
+
|
8
|
+
# Regexp string for 'encoder'
|
9
|
+
ENCODER = '[-a-z]+'
|
10
|
+
|
11
|
+
# Regexp string for 'qualified/dialect'
|
12
|
+
QDIALECT = DIALECT + '([\/]' + DIALECT + ')*'
|
13
|
+
|
14
|
+
# Regexp string for 'qualified/encoder'
|
15
|
+
QENCODER = ENCODER + '([\/]' + ENCODER + ')*'
|
16
|
+
|
17
|
+
# Regexp string for 'user_variable'
|
18
|
+
VAR = '[a-z][a-z0-9_]*'
|
19
|
+
|
20
|
+
# Regexp string for expression in the hosting language
|
21
|
+
EXPR = '.*?'
|
22
|
+
|
23
|
+
# Regexp string for URI expresion
|
24
|
+
URI = '[^\s]+'
|
25
|
+
|
26
|
+
# Part of a with expression
|
27
|
+
WITH_PART = '(' + VAR + ')' + '\s*:\s*(' + EXPR + ')'
|
28
|
+
|
29
|
+
# Regexp string for with expression
|
30
|
+
WITH = WITH_PART + '(\s*,\s*(' + WITH_PART + '))*'
|
31
|
+
|
32
|
+
# Regexp string for as expression
|
33
|
+
MULTI_AS = VAR + '(' + '\s*,\s*' + VAR + ')*'
|
34
|
+
|
35
|
+
# USING string for as expression
|
36
|
+
USING = VAR + '(' + '\s*,\s*' + VAR + ')*'
|
37
|
+
|
38
|
+
# Basic blocks for building expressions
|
39
|
+
BASIC_BLOCKS = {
|
40
|
+
:dialect => {:str => DIALECT, :groups => 0, :decoder => nil},
|
41
|
+
:encoder => {:str => ENCODER, :groups => 0, :decoder => nil},
|
42
|
+
:qdialect => {:str => QDIALECT, :groups => 1, :decoder => nil},
|
43
|
+
:qencoder => {:str => QENCODER, :groups => 1, :decoder => nil},
|
44
|
+
:var => {:str => VAR, :groups => 0, :decoder => nil},
|
45
|
+
:expr => {:str => EXPR, :groups => 0, :decoder => :decode_expr},
|
46
|
+
:uri => {:str => URI, :groups => 0, :decoder => nil},
|
47
|
+
:with => {:str => WITH, :groups => 6, :decoder => :decode_with},
|
48
|
+
:multi_as => {:str => MULTI_AS, :groups => 1, :decoder => :decode_multi_as},
|
49
|
+
:using => {:str => USING, :groups => 1, :decoder => :decode_using}
|
50
|
+
}
|
51
|
+
|
52
|
+
# Regular expressions of built expressions
|
53
|
+
REGEXPS = {}
|
54
|
+
|
55
|
+
# Builds an expression.
|
56
|
+
def self.expr(*args)
|
57
|
+
expr = REGEXPS[args]
|
58
|
+
if expr.nil?
|
59
|
+
# 1) we simply create an equivalent regular expression in _str_
|
60
|
+
str, hash, count = '^\s*', {}, 0
|
61
|
+
args.each do |arg|
|
62
|
+
case arg
|
63
|
+
when Symbol
|
64
|
+
raise ArgumentError, "No reusable RuleSet::Utils block called #{arg}"\
|
65
|
+
unless BASIC_BLOCKS[arg]
|
66
|
+
str << '(' << BASIC_BLOCKS[arg][:str] << ')'
|
67
|
+
hash[arg] = [(count+=1), BASIC_BLOCKS[arg][:decoder]]
|
68
|
+
count += BASIC_BLOCKS[arg][:groups]
|
69
|
+
when Array
|
70
|
+
keyword, what, mandatory = arg
|
71
|
+
raise ArgumentError, "No reusable RuleSet::Utils block called #{what}"\
|
72
|
+
unless BASIC_BLOCKS[what]
|
73
|
+
mandatory = true if mandatory.nil?
|
74
|
+
unless mandatory
|
75
|
+
str << '('
|
76
|
+
count += 1
|
77
|
+
end
|
78
|
+
str << '\s+' << keyword << '\s+'
|
79
|
+
str << '(' << BASIC_BLOCKS[what][:str] << ')'
|
80
|
+
str << ')?' unless mandatory
|
81
|
+
hash[keyword.to_sym] = [(count+=1), BASIC_BLOCKS[what][:decoder]]
|
82
|
+
count += BASIC_BLOCKS[what][:groups]
|
83
|
+
else
|
84
|
+
raise ArgumentError, "Symbol or Array expected"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
str << '\s*$'
|
88
|
+
|
89
|
+
# here is the expression, on which we put a decode method
|
90
|
+
expr = {:regexp => Regexp.new(str), :places => hash}
|
91
|
+
def expr.decode(str, parser=nil)
|
92
|
+
matchdata = self[:regexp].match(str)
|
93
|
+
return nil if matchdata.nil?
|
94
|
+
decoded = {}
|
95
|
+
self[:places].each_pair do |k,v|
|
96
|
+
value = matchdata[v[0]]
|
97
|
+
value = Utils.send(v[1], value, parser)\
|
98
|
+
if v[1] and not(value.nil?)
|
99
|
+
decoded[k] = value
|
100
|
+
end
|
101
|
+
return decoded
|
102
|
+
end
|
103
|
+
|
104
|
+
# and we save it!
|
105
|
+
REGEXPS[args] = expr
|
106
|
+
end
|
107
|
+
expr
|
108
|
+
end
|
109
|
+
|
110
|
+
# Decodes an expression using the parser
|
111
|
+
def self.decode_expr(expr, parser)
|
112
|
+
return expr if parser.nil?
|
113
|
+
parser.evaluate(expr)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Regular expression for WITH
|
117
|
+
RG_WITH = Regexp.new('^' + WITH + '$')
|
118
|
+
|
119
|
+
# Decodes a with expression
|
120
|
+
def self.decode_with(expr, parser, hash={})
|
121
|
+
matchdata = RG_WITH.match(expr)
|
122
|
+
return hash if matchdata.nil?
|
123
|
+
hash[matchdata[1]] = decode_expr(matchdata[2], parser)
|
124
|
+
decode_with(matchdata[4], parser, hash)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Decodes a multi as expression
|
128
|
+
def self.decode_multi_as(expr, parser)
|
129
|
+
expr.split(/\s*,\s*/)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Decodes a multi as expression
|
133
|
+
def self.decode_using(expr, parser)
|
134
|
+
expr.split(/\s*,\s*/).collect{|s| decode_expr(s, parser)}
|
135
|
+
end
|
136
|
+
|
137
|
+
# Builds a hash for 'using ... with ...' rules from a decoded expression
|
138
|
+
def self.context_from_using_and_with(decoded)
|
139
|
+
context = decoded[:using]
|
140
|
+
context = context.dup unless context.nil?
|
141
|
+
context = {} if context.nil?
|
142
|
+
context.merge!(decoded[:with]) unless decoded[:with].nil?
|
143
|
+
context
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
### DEPRECATED API ####################################################
|
148
|
+
|
149
|
+
# Regular expression for 'user_variable'
|
150
|
+
RG_VAR = Regexp.new('^\s*(' + VAR + ')\s*$')
|
151
|
+
|
152
|
+
# Regular expression for expression in the hosting language
|
153
|
+
RG_EXPR = Regexp.new('^\s*(' + EXPR + ')\s*$')
|
154
|
+
|
155
|
+
# Regular expression for expression in the hosting language
|
156
|
+
RG_URI = Regexp.new('^\s*(' + URI + ')\s*$')
|
157
|
+
|
158
|
+
# Part of a with expression
|
159
|
+
RG_WITH_PART = Regexp.new('(' + VAR + ')' + '\s*:\s*([^,]+)')
|
160
|
+
|
161
|
+
# Regular expression for 'dialect'
|
162
|
+
RG_DIALECT = Regexp.new('^\s*(' + DIALECT + ')\s*$')
|
163
|
+
|
164
|
+
# Regular expression for 'encoder'
|
165
|
+
RG_ENCODER = Regexp.new('^\s*(' + ENCODER + ')\s*$')
|
166
|
+
|
167
|
+
# Regular expression for 'qualified/dialect'
|
168
|
+
RG_QDIALECT = Regexp.new('^\s*(' + QDIALECT + ')\s*$')
|
169
|
+
|
170
|
+
# Regular expression for 'qualified/encoder'
|
171
|
+
RG_QENCODER = Regexp.new('^\s*(' + QENCODER + ')\s*$')
|
172
|
+
|
173
|
+
# Regexp string for 'qualified/dialect as var'
|
174
|
+
QDIALECT_AS = '(' + QDIALECT + ')\s+as\s+(' + VAR + ')'
|
175
|
+
|
176
|
+
# Regular expression for 'qualified/dialect as var'
|
177
|
+
RG_QDIALECT_AS = Regexp.new('^\s*(' + QDIALECT_AS + ')\s*$')
|
178
|
+
|
179
|
+
# Regexp string for 'qualified/dialect with ...'
|
180
|
+
QDIALECT_WITH = '(' + QDIALECT + ')\s+as\s+(' + WITH + ')'
|
181
|
+
|
182
|
+
# Regular expression for 'qualified/dialect with ...'
|
183
|
+
RG_QDIALECT_WITH = Regexp.new('^\s*(' + QDIALECT_WITH + ')\s*$')
|
184
|
+
|
185
|
+
# Regexp string for 'qualified/encoder as var'
|
186
|
+
QENCODER_AS = '(' + QENCODER + ')\s+as\s+(' + VAR + ')'
|
187
|
+
|
188
|
+
# Regular expression for 'qualified/encoder as var'
|
189
|
+
RG_QENCODER_AS = Regexp.new('^\s*(' + QENCODER_AS + ')\s*$')
|
190
|
+
|
191
|
+
# Regexp string for 'wlang/hosted as var'
|
192
|
+
EXPR_AS = '(' + EXPR + ')\s+as\s+(' + VAR + ')'
|
193
|
+
|
194
|
+
# Regular expression for 'wlang/hosted as var'
|
195
|
+
RG_EXPR_AS = Regexp.new('^\s*(' + EXPR_AS + ')\s*$')
|
196
|
+
|
197
|
+
# Regexp string for 'wlang/uri as var'
|
198
|
+
URI_AS = '(' + URI + ')\s+as\s+(' + VAR + ')'
|
199
|
+
|
200
|
+
# Regular expression for 'wlang/uri as var'
|
201
|
+
RG_URI_AS = Regexp.new('^\s*(' + URI_AS + ')\s*$')
|
202
|
+
|
203
|
+
# Regespc string for 'wlang/uri with ...'
|
204
|
+
URI_WITH = '(' + URI + ')\s+with\s+(' + WITH + ')'
|
205
|
+
|
206
|
+
# Regular expression for 'wlang/uri as var'
|
207
|
+
RG_URI_WITH = Regexp.new('^\s*(' + URI_WITH + ')\s*$')
|
208
|
+
|
209
|
+
# Decodes a simple var expression
|
210
|
+
def self.decode_var(src)
|
211
|
+
match = RG_VAR.match(src)
|
212
|
+
return nil unless match
|
213
|
+
{:variable => match[1]}
|
214
|
+
end
|
215
|
+
|
216
|
+
# Decodes a 'qualified/dialect as var'. Returns a hash
|
217
|
+
# with :dialect and :variable keys, or nil if _str_ does
|
218
|
+
# not match.
|
219
|
+
def self.decode_qdialect_as(str)
|
220
|
+
match = RG_QDIALECT_AS.match(str)
|
221
|
+
return nil unless match
|
222
|
+
{:dialect => match[2], :variable => match[4]}
|
223
|
+
end
|
224
|
+
|
225
|
+
# Decodes a 'qualified/encoder as var'. Returns a hash
|
226
|
+
# with :encoder and :variable keys, or nil if _str_ does
|
227
|
+
# not match.
|
228
|
+
def self.decode_qencoder_as(str)
|
229
|
+
match = RG_QENCODER_AS.match(str)
|
230
|
+
return nil unless match
|
231
|
+
{:encoder => match[2], :variable => match[4]}
|
232
|
+
end
|
233
|
+
|
234
|
+
# Decodes a 'wlang/hosted as var'. Returns a hash
|
235
|
+
# with :expression and :variable keys, or nil if _str_ does
|
236
|
+
# not match.
|
237
|
+
def self.decode_expr_as(str)
|
238
|
+
match = RG_EXPR_AS.match(str)
|
239
|
+
return nil unless match
|
240
|
+
{:expr => match[2], :variable => match[3]}
|
241
|
+
end
|
242
|
+
|
243
|
+
# Decodes a 'wlang/uri as var'. Returns a hash
|
244
|
+
# with :uri and :variable keys, or nil if _str_ does
|
245
|
+
# not match.
|
246
|
+
def self.decode_uri_as(str)
|
247
|
+
match = RG_URI_AS.match(str)
|
248
|
+
return nil unless match
|
249
|
+
{:uri => match[2], :variable => match[3]}
|
250
|
+
end
|
251
|
+
|
252
|
+
# Decodes a 'wlang/uri with ...'. Returns a hash
|
253
|
+
# with :uri and :with keys (with being another hash), or nil if _str_ does
|
254
|
+
# not match.
|
255
|
+
def self.decode_uri_with(str, parser=nil, optional=false)
|
256
|
+
match = RG_URI_WITH.match(str)
|
257
|
+
if match.nil?
|
258
|
+
return nil unless optional
|
259
|
+
match = RG_URI.match(str)
|
260
|
+
return nil if match.nil?
|
261
|
+
return {:uri => match[1], :with => nil}
|
262
|
+
else
|
263
|
+
with_expr, with = match[3], {}
|
264
|
+
exprs = with_expr.split(/\s*,\s*/)
|
265
|
+
exprs.each do |expr|
|
266
|
+
RG_WITH_PART =~ expr
|
267
|
+
with[$1] = parser.nil? ? $2 : parser.evaluate($2)
|
268
|
+
end
|
269
|
+
{:uri => match[2], :with => with}
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# Decodes a 'wlang/dialect with ...'. Returns a hash
|
274
|
+
# with :dialect and :with keys (with being another hash),
|
275
|
+
# or nil if _str_ does not match.
|
276
|
+
def self.decode_qdialect_with(str, parser=nil, optional=false)
|
277
|
+
match = RG_QDIALECT_WITH.match(str)
|
278
|
+
if match.nil?
|
279
|
+
return nil unless optional
|
280
|
+
match = RG_QDIALECT.match(str)
|
281
|
+
return nil if match.nil?
|
282
|
+
return {:dialect => match[1], :with => nil}
|
283
|
+
else
|
284
|
+
with_expr, with = match[3], {}
|
285
|
+
exprs = with_expr.split(/\s*,\s*/)
|
286
|
+
exprs.each do |expr|
|
287
|
+
RG_WITH_PART =~ expr
|
288
|
+
with[$1] = parser.nil? ? $2 : parser.evaluate($2)
|
289
|
+
end
|
290
|
+
{:dialect => match[2], :with => with}
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|