wlang 0.8.5 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +65 -0
- data/README.rdoc +85 -31
- data/bin/wlang +5 -0
- data/doc/specification/dialect.wtpl +14 -0
- data/doc/specification/dialects.wtpl +3 -0
- data/doc/specification/glossary.wtpl +4 -4
- data/doc/specification/rulesets.wtpl +9 -9
- data/doc/specification/specification.css +1 -0
- data/doc/specification/specification.html +68 -5
- data/doc/specification/specification.wtpl +12 -12
- data/doc/specification/specification.yml +9 -9
- data/doc/specification/symbols.wtpl +8 -8
- data/lib/wlang.rb +297 -75
- data/lib/wlang/dialect.rb +7 -3
- data/lib/wlang/dialects/coderay_dialect.rb +4 -4
- data/lib/wlang/dialects/plain_text_dialect.rb +13 -19
- data/lib/wlang/dialects/redcloth_dialect.rb +16 -0
- data/lib/wlang/dialects/ruby_dialect.rb +16 -2
- data/lib/wlang/dialects/standard_dialects.rb +20 -0
- data/lib/wlang/dialects/xhtml_dialect.rb +24 -1
- data/lib/wlang/encoder.rb +1 -1
- data/lib/wlang/encoder_set.rb +5 -0
- data/lib/wlang/errors.rb +70 -6
- data/lib/wlang/ext/hash_methodize.rb +13 -0
- data/lib/wlang/{ruby_extensions.rb → ext/string.rb} +9 -5
- data/lib/wlang/hash_scope.rb +89 -0
- data/lib/wlang/hosted_language.rb +146 -0
- data/lib/wlang/parser.rb +189 -126
- data/lib/wlang/parser_state.rb +94 -0
- data/lib/wlang/rule_set.rb +16 -3
- data/lib/wlang/rulesets/basic_ruleset.rb +14 -6
- data/lib/wlang/rulesets/buffering_ruleset.rb +20 -29
- data/lib/wlang/rulesets/context_ruleset.rb +16 -20
- data/lib/wlang/rulesets/imperative_ruleset.rb +4 -4
- data/lib/wlang/rulesets/ruleset_utils.rb +26 -5
- data/lib/wlang/template.rb +16 -34
- data/lib/wlang/wlang_command.rb +4 -7
- data/lib/wlang/wlang_command_options.rb +5 -0
- data/test/blackbox/basic/execution_1.exp +1 -0
- data/test/blackbox/basic/execution_1.tpl +1 -0
- data/test/blackbox/basic/execution_2.exp +1 -0
- data/test/blackbox/basic/execution_2.tpl +1 -0
- data/test/blackbox/basic/execution_3.exp +1 -0
- data/test/blackbox/basic/execution_3.tpl +1 -0
- data/test/blackbox/basic/execution_4.exp +1 -0
- data/test/blackbox/basic/execution_4.tpl +1 -0
- data/test/blackbox/basic/inclusion_1.exp +1 -0
- data/test/blackbox/basic/inclusion_1.tpl +1 -0
- data/test/blackbox/basic/inclusion_2.exp +1 -0
- data/test/blackbox/basic/inclusion_2.tpl +1 -0
- data/test/blackbox/basic/injection_1.exp +1 -0
- data/test/blackbox/basic/injection_1.tpl +1 -0
- data/test/blackbox/basic/injection_2.exp +1 -0
- data/test/blackbox/basic/injection_2.tpl +1 -0
- data/test/blackbox/basic/modulation_1.exp +1 -0
- data/test/blackbox/basic/modulation_1.tpl +1 -0
- data/test/blackbox/basic/modulation_2.exp +1 -0
- data/test/blackbox/basic/modulation_2.tpl +1 -0
- data/test/blackbox/basic/recursive_app_1.exp +1 -0
- data/test/blackbox/basic/recursive_app_1.tpl +1 -0
- data/test/blackbox/basic/recursive_app_2.exp +1 -0
- data/test/blackbox/basic/recursive_app_2.tpl +1 -0
- data/test/blackbox/buffering/data_1.rb +1 -0
- data/test/blackbox/buffering/data_assignment_1.exp +1 -0
- data/test/blackbox/buffering/data_assignment_1.tpl +1 -0
- data/test/blackbox/buffering/data_assignment_2.exp +1 -0
- data/test/blackbox/buffering/data_assignment_2.tpl +1 -0
- data/test/blackbox/buffering/data_assignment_3.exp +1 -0
- data/test/blackbox/buffering/data_assignment_3.tpl +1 -0
- data/test/blackbox/buffering/data_assignment_4.exp +1 -0
- data/test/blackbox/buffering/data_assignment_4.tpl +1 -0
- data/test/blackbox/buffering/input_1.exp +1 -0
- data/test/blackbox/buffering/input_1.tpl +1 -0
- data/test/blackbox/buffering/input_2.exp +1 -0
- data/test/blackbox/buffering/input_2.tpl +1 -0
- data/test/blackbox/buffering/input_3.exp +1 -0
- data/test/blackbox/buffering/input_3.tpl +1 -0
- data/test/blackbox/buffering/input_inclusion.exp +1 -0
- data/test/blackbox/buffering/input_inclusion.tpl +1 -0
- data/test/blackbox/buffering/input_inclusion_1.exp +0 -0
- data/test/blackbox/buffering/input_inclusion_1.tpl +1 -0
- data/test/blackbox/buffering/input_inclusion_2.exp +1 -0
- data/test/blackbox/buffering/input_inclusion_2.tpl +1 -0
- data/test/blackbox/buffering/input_inclusion_3.exp +1 -0
- data/test/blackbox/buffering/input_inclusion_3.tpl +1 -0
- data/test/blackbox/buffering/input_inclusion_4.exp +0 -0
- data/test/blackbox/buffering/input_inclusion_4.tpl +1 -0
- data/test/blackbox/buffering/input_inclusion_5.exp +1 -0
- data/test/blackbox/buffering/input_inclusion_5.tpl +1 -0
- data/test/blackbox/buffering/input_inclusion_6.exp +1 -0
- data/test/blackbox/buffering/input_inclusion_6.tpl +1 -0
- data/test/blackbox/buffering/input_inclusion_7.exp +0 -0
- data/test/blackbox/buffering/input_inclusion_7.tpl +1 -0
- data/test/blackbox/buffering/text_1.txt +1 -0
- data/test/blackbox/buffering/wlang.txt +1 -0
- data/test/blackbox/context/assignment_1.exp +1 -0
- data/test/blackbox/context/assignment_1.tpl +1 -0
- data/test/blackbox/context/assignment_2.exp +1 -0
- data/test/blackbox/context/assignment_2.tpl +1 -0
- data/test/blackbox/context/assignment_3.exp +2 -0
- data/test/blackbox/context/assignment_3.tpl +2 -0
- data/test/blackbox/context/assignment_4.exp +1 -0
- data/test/blackbox/context/assignment_4.tpl +1 -0
- data/test/blackbox/context/block_assignment_1.exp +1 -0
- data/test/blackbox/context/block_assignment_1.tpl +1 -0
- data/test/blackbox/context/block_assignment_2.exp +1 -0
- data/test/blackbox/context/block_assignment_2.tpl +1 -0
- data/test/blackbox/context/modulo_assignment_1.exp +1 -0
- data/test/blackbox/context/modulo_assignment_1.tpl +1 -0
- data/test/blackbox/context/modulo_assignment_2.exp +1 -0
- data/test/blackbox/context/modulo_assignment_2.tpl +1 -0
- data/test/blackbox/data_1.rb +1 -0
- data/test/blackbox/test_all.rb +59 -0
- data/test/spec/basic_object.spec +40 -0
- data/test/spec/global_extensions.rb +2 -0
- data/test/spec/hash_scope.spec +76 -0
- data/test/spec/redcloth_dialect.spec +24 -0
- data/test/spec/test_all.rb +8 -0
- data/test/spec/wlang.spec +53 -0
- data/test/spec/xhtml_dialect.spec +23 -0
- data/test/{test_all.rb → unit/test_all.rb} +1 -1
- data/test/{wlang → unit/wlang}/anagram_bugs_test.rb +2 -2
- data/test/{wlang → unit/wlang}/basic_ruleset_test.rb +1 -1
- data/test/{wlang → unit/wlang}/buffering_ruleset_test.rb +4 -4
- data/test/{wlang → unit/wlang}/buffering_template1.wtpl +0 -0
- data/test/{wlang → unit/wlang}/buffering_template2.wtpl +0 -0
- data/test/{wlang → unit/wlang}/buffering_template3.wtpl +0 -0
- data/test/unit/wlang/buffering_template4.wtpl +1 -0
- data/test/unit/wlang/buffering_template5.wtpl +1 -0
- data/test/{wlang → unit/wlang}/context_ruleset_test.rb +0 -0
- data/test/{wlang → unit/wlang}/data.rb +0 -0
- data/test/{wlang → unit/wlang}/encoder_set_test.rb +0 -0
- data/test/{wlang → unit/wlang}/imperative_ruleset_test.rb +0 -0
- data/test/{wlang → unit/wlang}/intelligent_buffer_test.rb +0 -0
- data/test/{wlang → unit/wlang}/othersymbols_test.rb +0 -0
- data/test/{wlang → unit/wlang}/parser_test.rb +10 -11
- data/test/{wlang → unit/wlang}/plain_text_dialect_test.rb +0 -0
- data/test/{wlang → unit/wlang}/ruby_dialect_test.rb +0 -0
- data/test/{wlang → unit/wlang}/ruby_expected.rb +0 -0
- data/test/{wlang → unit/wlang}/ruby_template.wrb +0 -0
- data/test/{wlang → unit/wlang}/ruleset_utils_test.rb +0 -0
- data/test/{wlang → unit/wlang}/specification_examples_test.rb +2 -2
- data/test/{wlang → unit/wlang}/test_utils.rb +1 -1
- data/test/{wlang → unit/wlang}/wlang_test.rb +0 -0
- metadata +135 -42
- data/lib/wlang/basic_object.rb +0 -19
- data/lib/wlang/parser_context.rb +0 -139
- data/test/sandbox.rb +0 -1
- data/test/wlang/buffering_template4.wtpl +0 -1
- data/test/wlang/buffering_template5.wtpl +0 -1
- data/test/wlang/parser_context_test.rb +0 -29
@@ -0,0 +1,94 @@
|
|
1
|
+
module WLang
|
2
|
+
class Parser
|
3
|
+
#
|
4
|
+
# Encapsulates all state information of a WLang parser
|
5
|
+
#
|
6
|
+
class State
|
7
|
+
|
8
|
+
# The attached parser
|
9
|
+
attr_accessor :parser
|
10
|
+
|
11
|
+
# The parent state
|
12
|
+
attr_accessor :parent
|
13
|
+
|
14
|
+
# The current hosted language
|
15
|
+
attr_accessor :hosted
|
16
|
+
|
17
|
+
# The current template
|
18
|
+
attr_accessor :template
|
19
|
+
|
20
|
+
# The current dialect
|
21
|
+
attr_accessor :dialect
|
22
|
+
|
23
|
+
# The current offset in template's source code
|
24
|
+
attr_accessor :offset
|
25
|
+
|
26
|
+
# The current scope
|
27
|
+
attr_accessor :scope
|
28
|
+
|
29
|
+
# The current output buffer
|
30
|
+
attr_accessor :buffer
|
31
|
+
|
32
|
+
# Creates a new state instance for a given parser and optional
|
33
|
+
# parent.
|
34
|
+
def initialize(parser, parent = nil)
|
35
|
+
@parser, @parent = parser, parent
|
36
|
+
end
|
37
|
+
|
38
|
+
# Checks internals
|
39
|
+
def check
|
40
|
+
raise "WLang::Parser::State fatal: invalid parser #{parser}" unless ::WLang::Parser===parser
|
41
|
+
raise "WLang::Parser::State fatal: invalid hosted #{hosted}" unless ::WLang::HostedLanguage===hosted
|
42
|
+
raise "WLang::Parser::State fatal: missing template #{template}" unless ::WLang::Template===template
|
43
|
+
raise "WLang::Parser::State fatal: missing dialect #{dialect}" unless ::WLang::Dialect===dialect
|
44
|
+
raise "WLang::Parser::State fatal: missing offset #{offset}" unless Integer===offset
|
45
|
+
raise "WLang::Parser::State fatal: missing scope #{scope}" unless ::WLang::HashScope===scope
|
46
|
+
raise "WLang::Parser::State fatal: missing buffer #{buffer}" unless buffer.respond_to?(:<<)
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Branches this state.
|
52
|
+
#
|
53
|
+
# Branching allows creating a child parser state of this one. Options are:
|
54
|
+
# - :hosted => a new hosted language
|
55
|
+
# - :template => a new template
|
56
|
+
# - :dialect => a new dialect
|
57
|
+
# - :offset => a new offset in the template
|
58
|
+
# - :shared => :all, :root or :none (which scoping should be shared)
|
59
|
+
# - :scope => a Hash of new pairing to push on the new scope
|
60
|
+
# - :buffer => a new output buffer to use
|
61
|
+
#
|
62
|
+
def branch(opts = {})
|
63
|
+
child = State.new(parser, self)
|
64
|
+
child.hosted = opts[:hosted] || hosted
|
65
|
+
child.template = opts[:template] || template
|
66
|
+
child.dialect = opts[:dialect] || child.template.dialect
|
67
|
+
child.offset = opts[:offset] || offset
|
68
|
+
child.buffer = opts[:buffer] || child.dialect.factor_buffer
|
69
|
+
child.scope = case opts[:shared]
|
70
|
+
when :all, NilClass
|
71
|
+
scope.branch(opts[:scope])
|
72
|
+
when :root
|
73
|
+
scope.root.branch(opts[:scope])
|
74
|
+
when :none
|
75
|
+
::WLang::HashScope.new(opts[:scope])
|
76
|
+
else
|
77
|
+
raise ArgumentError, "Invalid ParserState.branch option :shared #{opts[:shared]}"
|
78
|
+
end
|
79
|
+
child.check
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns a friendly location for this parser state
|
83
|
+
def where
|
84
|
+
template ? template.where(offset) : "Source template tree"
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns a friendly wlang backtrace as an array
|
88
|
+
def backtrace
|
89
|
+
parent ? parent.backtrace.unshift(where) : [where]
|
90
|
+
end
|
91
|
+
|
92
|
+
end # class State
|
93
|
+
end # class Parser
|
94
|
+
end # module WLang
|
data/lib/wlang/rule_set.rb
CHANGED
@@ -18,10 +18,22 @@ module WLang
|
|
18
18
|
# == Detailed API
|
19
19
|
class RuleSet
|
20
20
|
|
21
|
+
# Which modules are reused
|
22
|
+
attr_reader :reuse
|
23
|
+
|
21
24
|
#
|
22
25
|
# Creates an new dialect rule set.
|
23
26
|
#
|
24
|
-
def initialize()
|
27
|
+
def initialize()
|
28
|
+
@rules = {}
|
29
|
+
@reuse = []
|
30
|
+
@patterns = Hash.new{|h, k| h[k] = build_pattern(k)}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Yields the block with name, rule pairs
|
34
|
+
def each
|
35
|
+
@rules.each_pair{|name,rule| yield(name, rule)}
|
36
|
+
end
|
25
37
|
|
26
38
|
#
|
27
39
|
# Adds a tag matching rule to this rule set. _tag_ must be a String with the
|
@@ -38,7 +50,7 @@ module WLang
|
|
38
50
|
end
|
39
51
|
raise(ArgumentError, "Rule expected") unless Rule===rule
|
40
52
|
@rules[tag] = rule
|
41
|
-
@
|
53
|
+
@patterns.clear
|
42
54
|
end
|
43
55
|
|
44
56
|
#
|
@@ -46,6 +58,7 @@ module WLang
|
|
46
58
|
#
|
47
59
|
def add_rules(mod, pairs=nil)
|
48
60
|
raise(ArgumentError,"Module expected") unless Module===mod
|
61
|
+
reuse << mod
|
49
62
|
pairs = mod::DEFAULT_RULESET if pairs.nil?
|
50
63
|
pairs.each_pair do |symbol,method|
|
51
64
|
meth = mod.method(method)
|
@@ -61,7 +74,7 @@ module WLang
|
|
61
74
|
# not intended to be used by users themselve.
|
62
75
|
#
|
63
76
|
def pattern(block_symbols)
|
64
|
-
|
77
|
+
@patterns[block_symbols]
|
65
78
|
end
|
66
79
|
|
67
80
|
#
|
@@ -50,23 +50,31 @@ module WLang
|
|
50
50
|
execution(parser, offset)
|
51
51
|
end
|
52
52
|
|
53
|
-
# Rule implementation of <tt>%!{wlang/ruby using ... with ...}</tt>
|
53
|
+
# Rule implementation of <tt>%!{wlang/ruby using ... with ...}{...}</tt>
|
54
54
|
def self.recursive_application(parser, offset)
|
55
55
|
dialect, reached = parser.parse(offset, "wlang/active-string")
|
56
56
|
text, reached = parser.parse_block(reached)
|
57
57
|
|
58
58
|
# decode expression
|
59
59
|
decoded = U.expr(:qdialect,
|
60
|
+
["share", :share, false],
|
60
61
|
["using", :expr, false],
|
61
62
|
["with", :with, false]).decode(dialect, parser)
|
62
63
|
parser.syntax_error(offset) if decoded.nil?
|
63
64
|
|
64
65
|
# build context
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
dialect2 = decoded[:qdialect]
|
67
|
+
shared = decoded[:share].nil? ? :root : decoded[:share]
|
68
|
+
context = U.context_from_using_and_with(decoded)
|
69
|
+
|
70
|
+
# TODO: refactor me!!
|
71
|
+
parser.branch(:template => WLang::template(text, dialect2),
|
72
|
+
:offset => 0,
|
73
|
+
:shared => shared,
|
74
|
+
:scope => context) {
|
75
|
+
instantiated, forget = parser.instantiate
|
76
|
+
[instantiated, reached]
|
77
|
+
}
|
70
78
|
end
|
71
79
|
|
72
80
|
end # module Basic
|
@@ -41,6 +41,7 @@ module WLang
|
|
41
41
|
end
|
42
42
|
["", reached]
|
43
43
|
else
|
44
|
+
text = parser.parse(offset, "wlang/dummy")[0]
|
44
45
|
parser.error(offset, "unable to apply output rule >>{#{text}}, not a writable directory (#{file})")
|
45
46
|
end
|
46
47
|
end
|
@@ -59,12 +60,11 @@ module WLang
|
|
59
60
|
|
60
61
|
# handle two different cases
|
61
62
|
if parser.has_block?(reached)
|
62
|
-
parser.
|
63
|
-
|
64
|
-
|
65
|
-
[text, reached]
|
63
|
+
parser.branch_scope(decoded[:variable] => data) {
|
64
|
+
parser.parse_block(reached)
|
65
|
+
}
|
66
66
|
else
|
67
|
-
parser.
|
67
|
+
parser.scope_define(decoded[:variable], data)
|
68
68
|
["", reached]
|
69
69
|
end
|
70
70
|
else
|
@@ -77,38 +77,29 @@ module WLang
|
|
77
77
|
def self.input_inclusion(parser, offset)
|
78
78
|
uri, reached = parser.parse(offset, "wlang/uri")
|
79
79
|
|
80
|
-
# decode expression
|
80
|
+
# decode the expression
|
81
81
|
decoded = U.expr(:uri,
|
82
|
+
["share", :share, false],
|
82
83
|
["using", :using, false],
|
83
84
|
["with", :with, false]).decode(uri, parser)
|
84
85
|
parser.syntax_error(offset) if decoded.nil?
|
85
86
|
|
86
|
-
#
|
87
|
-
|
88
|
-
|
89
|
-
raise "<<+ does not support multiple with for now." if decoded[:using].size != 1
|
90
|
-
context = decoded[:using][0]
|
91
|
-
raise "Unexpected nil context when duplicated" if context.nil?
|
92
|
-
else
|
93
|
-
context = {}
|
94
|
-
end
|
87
|
+
# Look for share and context
|
88
|
+
shared = decoded[:share].nil? ? :none : decoded[:share]
|
89
|
+
context = U.context_from_using_and_with(decoded, parser)
|
95
90
|
|
96
|
-
#
|
97
|
-
|
98
|
-
case context
|
99
|
-
when WLang::Parser::Context::HashScope
|
100
|
-
context = context.__branch(decoded[:with])
|
101
|
-
when Hash
|
102
|
-
context = context.merge(decoded[:with])
|
103
|
-
else
|
104
|
-
raise "Unexpected context #{context}"
|
105
|
-
end
|
106
|
-
end
|
91
|
+
# Resolve the file by delegation to the parser
|
92
|
+
file = parser.file_resolve(decoded[:uri])
|
107
93
|
|
108
|
-
|
94
|
+
# Go for it
|
109
95
|
if File.file?(file) and File.readable?(file)
|
110
|
-
|
111
|
-
|
96
|
+
parser.branch(:template => parser.file_template(file),
|
97
|
+
:offset => 0,
|
98
|
+
:shared => shared,
|
99
|
+
:scope => context) {
|
100
|
+
instantiated, forget = parser.instantiate
|
101
|
+
[instantiated, reached]
|
102
|
+
}
|
112
103
|
else
|
113
104
|
text = parser.parse(offset, "wlang/dummy")[0]
|
114
105
|
parser.error(offset, "unable to apply input-inclusion rule <<+{#{text}}, not a file or not readable (#{file})")
|
@@ -29,12 +29,11 @@ module WLang
|
|
29
29
|
|
30
30
|
# handle two different cases
|
31
31
|
if parser.has_block?(reached)
|
32
|
-
parser.
|
33
|
-
|
34
|
-
|
35
|
-
[text, reached]
|
32
|
+
parser.branch_scope(decoded[:variable] => value) {
|
33
|
+
parser.parse_block(reached)
|
34
|
+
}
|
36
35
|
else
|
37
|
-
parser.
|
36
|
+
parser.scope_define(decoded[:variable], value)
|
38
37
|
["", reached]
|
39
38
|
end
|
40
39
|
end
|
@@ -52,12 +51,11 @@ module WLang
|
|
52
51
|
|
53
52
|
# handle two different cases
|
54
53
|
if parser.has_block?(reached)
|
55
|
-
parser.
|
56
|
-
|
57
|
-
|
58
|
-
[text, reached]
|
54
|
+
parser.branch_scope(decoded[:variable] => value) {
|
55
|
+
parser.parse_block(reached)
|
56
|
+
}
|
59
57
|
else
|
60
|
-
parser.
|
58
|
+
parser.scope_define(decoded[:variable], value)
|
61
59
|
["", reached]
|
62
60
|
end
|
63
61
|
end
|
@@ -75,12 +73,11 @@ module WLang
|
|
75
73
|
|
76
74
|
# handle two different cases
|
77
75
|
if parser.has_block?(reached)
|
78
|
-
parser.
|
79
|
-
|
80
|
-
|
81
|
-
[text, reached]
|
76
|
+
parser.branch_scope(decoded[:variable] => value) {
|
77
|
+
parser.parse_block(reached)
|
78
|
+
}
|
82
79
|
else
|
83
|
-
parser.
|
80
|
+
parser.scope_define(decoded[:variable], value)
|
84
81
|
["", reached]
|
85
82
|
end
|
86
83
|
end
|
@@ -99,12 +96,11 @@ module WLang
|
|
99
96
|
|
100
97
|
# handle two different cases
|
101
98
|
if parser.has_block?(reached)
|
102
|
-
parser.
|
103
|
-
|
104
|
-
|
105
|
-
[text, reached]
|
99
|
+
parser.branch_scope(decoded[:variable] => value) {
|
100
|
+
parser.parse_block(reached)
|
101
|
+
}
|
106
102
|
else
|
107
|
-
parser.
|
103
|
+
parser.scope_define(decoded[:variable], value)
|
108
104
|
["", reached]
|
109
105
|
end
|
110
106
|
end
|
@@ -101,10 +101,10 @@ module WLang
|
|
101
101
|
end
|
102
102
|
|
103
103
|
# install arguments and parse block2, positioned at block3
|
104
|
-
parser.
|
105
|
-
|
106
|
-
|
107
|
-
|
104
|
+
parser.branch_scope(merge_each_args(names, args)) {
|
105
|
+
parsed, block3 = parser.parse_block(block2)
|
106
|
+
parser.append_buffer(buffer, parsed, true)
|
107
|
+
}
|
108
108
|
first = false
|
109
109
|
end
|
110
110
|
|
@@ -35,6 +35,9 @@ module WLang
|
|
35
35
|
# USING string for as expression
|
36
36
|
USING = VAR + '(' + '\s*,\s*' + VAR + ')*'
|
37
37
|
|
38
|
+
# SHARE string for expression
|
39
|
+
SHARE = '(all|root|none)'
|
40
|
+
|
38
41
|
# Basic blocks for building expressions
|
39
42
|
BASIC_BLOCKS = {
|
40
43
|
:dialect => {:str => DIALECT, :groups => 0, :decoder => nil},
|
@@ -46,7 +49,8 @@ module WLang
|
|
46
49
|
:uri => {:str => URI, :groups => 0, :decoder => nil},
|
47
50
|
:with => {:str => WITH, :groups => 6, :decoder => :decode_with},
|
48
51
|
:multi_as => {:str => MULTI_AS, :groups => 1, :decoder => :decode_multi_as},
|
49
|
-
:using => {:str => USING, :groups => 1, :decoder => :decode_using}
|
52
|
+
:using => {:str => USING, :groups => 1, :decoder => :decode_using},
|
53
|
+
:share => {:str => SHARE, :groups => 1, :decoder => :decode_share}
|
50
54
|
}
|
51
55
|
|
52
56
|
# Regular expressions of built expressions
|
@@ -134,11 +138,28 @@ module WLang
|
|
134
138
|
expr.split(/\s*,\s*/).collect{|s| decode_expr(s, parser)}
|
135
139
|
end
|
136
140
|
|
141
|
+
# Decodes a multi as expression
|
142
|
+
def self.decode_share(expr, parser)
|
143
|
+
expr.to_sym
|
144
|
+
end
|
145
|
+
|
146
|
+
# Converts something to a usable hash
|
147
|
+
def self.to_hash(hash)
|
148
|
+
case hash
|
149
|
+
when Hash
|
150
|
+
hash.dup
|
151
|
+
when NilClass
|
152
|
+
{}
|
153
|
+
when Array
|
154
|
+
hash.inject({}){|memo, n| memo.merge!(to_hash(n))}
|
155
|
+
else
|
156
|
+
raise ArgumentError, "Unable to convert #{hash} to an hash"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
137
160
|
# 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?
|
161
|
+
def self.context_from_using_and_with(decoded, parser = nil)
|
162
|
+
context = to_hash(decoded[:using])
|
142
163
|
context.merge!(decoded[:with]) unless decoded[:with].nil?
|
143
164
|
context
|
144
165
|
end
|
data/lib/wlang/template.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
module WLang
|
2
|
-
|
3
2
|
#
|
4
|
-
# Template in a given wlang dialect
|
3
|
+
# Template in a given wlang dialect and expecting :braces, :brackets or
|
4
|
+
# :parentheses as block delimiters. A template is an abstraction over a
|
5
|
+
# wlang source text. It also provides utilities to create friendly location
|
6
|
+
# messages for offsets in the source text.
|
5
7
|
#
|
6
8
|
class Template
|
7
9
|
|
@@ -13,28 +15,19 @@ module WLang
|
|
13
15
|
# Template wlang dialect (wlang/...)
|
14
16
|
attr_reader :dialect
|
15
17
|
|
16
|
-
# Instantiation context
|
17
|
-
attr_reader :context
|
18
|
-
|
19
18
|
# Block symbols
|
20
19
|
attr_reader :block_symbols
|
21
20
|
|
22
21
|
# Attached file source
|
23
22
|
attr_accessor :source_file
|
24
23
|
|
25
|
-
#
|
26
24
|
# Creates a template instance.
|
27
|
-
|
28
|
-
|
25
|
+
def initialize(source, dialect, block_symbols = :braces)
|
26
|
+
dialect = WLang::dialect(dialect)
|
29
27
|
raise(ArgumentError, "Source is mandatory") if source.nil?
|
30
|
-
|
31
|
-
dname, dialect = dialect, WLang::dialect(dialect)
|
32
|
-
raise(ArgumentError, "Unknown dialect #{dname}") if dialect.nil?
|
33
|
-
end
|
34
|
-
raise(ArgumentError, "Dialect is mandatory") unless WLang::Dialect===dialect
|
28
|
+
raise(ArgumentError, "Dialect instance expected for dialect, #{dialect} received") unless Dialect===dialect
|
35
29
|
@source = source
|
36
30
|
@dialect = dialect
|
37
|
-
@context = WLang::Parser::Context.new(context)
|
38
31
|
@block_symbols = block_symbols
|
39
32
|
end
|
40
33
|
|
@@ -59,40 +52,29 @@ module WLang
|
|
59
52
|
end
|
60
53
|
end
|
61
54
|
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
unless context.nil?
|
67
|
-
@context.push(context)
|
68
|
-
end
|
69
|
-
parser = WLang::Parser.instantiator(self, buffer)
|
70
|
-
instantiated = parser.instantiate
|
71
|
-
unless context.nil?
|
72
|
-
@context.pop
|
73
|
-
end
|
74
|
-
instantiated[0]
|
55
|
+
# Instantiates the template, with optinal context and hosted language.
|
56
|
+
def instantiate(context = {}, hosted = ::WLang::HostedLanguage.new)
|
57
|
+
p = ::WLang::Parser.new(hosted, self, context)
|
58
|
+
p.instantiate[0]
|
75
59
|
end
|
76
60
|
|
77
61
|
# Returns a friendly position of an offset in the source text
|
78
62
|
def where(offset)
|
79
63
|
src = source_text
|
80
|
-
|
64
|
+
source_file = self.source_file ? File.expand_path(self.source_file) : "no source file"
|
65
|
+
"#{source_file || 'inline template'}:#{src.__wlang_line_of(offset)}:#{src.__wlang_column_of(offset)-1}"
|
81
66
|
end
|
82
67
|
|
83
68
|
# Raises a WLang::Error for the given offset
|
84
69
|
def error(offset, msg = "")
|
85
|
-
|
86
|
-
line, column = src.__wlang_line_of(offset), src.__wlang_column_of(offset)-1
|
87
|
-
raise WLang::Error, "#{@source_file}:#{line}:#{column} #{msg}"
|
70
|
+
raise WLang::Error, "#{where(offset)} #{msg}"
|
88
71
|
end
|
89
72
|
|
90
73
|
# Raises a friendly ParseError, with positions and so on
|
91
74
|
def parse_error(offset, msg = "")
|
92
75
|
src = source_text
|
93
|
-
|
94
|
-
ex =
|
95
|
-
ex.line, ex.column = line, column
|
76
|
+
ex = ParseError.new("#{where(offset)} #{msg}")
|
77
|
+
ex.line, ex.column = src.__wlang_line_of(offset), src.__wlang_column_of(offset)-1
|
96
78
|
raise ex
|
97
79
|
end
|
98
80
|
|