wlang 0.8.5 → 0.9.1
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/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
|
|