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
data/lib/wlang/dialect.rb
CHANGED
@@ -3,8 +3,8 @@ require 'wlang/rule_set'
|
|
3
3
|
module WLang
|
4
4
|
|
5
5
|
#
|
6
|
-
# Implements the _dialect_ abstraction (see {README}[link://files/README.
|
7
|
-
# A dialect instance is
|
6
|
+
# Implements the _dialect_ abstraction (see {README}[link://files/README.rdoc]).
|
7
|
+
# A dialect instance is an aggregation of encoders and ruleset (through EncoderSet
|
8
8
|
# and RuleSet classes). A dialect is also a node in the dialect tree and has a
|
9
9
|
# qualified name through this tree. For example <tt>wlang/xhtml</tt> is the
|
10
10
|
# qualified name of a <tt>xhtml</tt> dialect which is a child dialect of
|
@@ -14,6 +14,7 @@ module WLang
|
|
14
14
|
# Language instead (see WLang::Dialect::DSL).
|
15
15
|
#
|
16
16
|
# === For developers only
|
17
|
+
#
|
17
18
|
# In order to avoid having users to install all required gems of all dialects
|
18
19
|
# wlang implements a lazy load design pattern on the dialect tree, through the
|
19
20
|
# WLang::Dialect::DSL and WLang::Dialect::Loader classes. The former only creates
|
@@ -26,7 +27,7 @@ module WLang
|
|
26
27
|
# Standard dialect obtention methods (WLang#dialect as well as WLang::Dialect#dialect)
|
27
28
|
# ensure that returned dialects are built. If you obtain dialects another way,
|
28
29
|
# be sure that they are built before using them (is_built? and build! are your
|
29
|
-
# friends to
|
30
|
+
# friends to achieve that goal).
|
30
31
|
#
|
31
32
|
# Moreover, child dialects may require tools of their ancestors. The following
|
32
33
|
# invariant should always be respected: if a dialect is built, all its ancestors
|
@@ -47,6 +48,9 @@ module WLang
|
|
47
48
|
# Parent dialect
|
48
49
|
attr_reader :parent
|
49
50
|
|
51
|
+
# Sub dialects by name
|
52
|
+
attr_reader :dialects
|
53
|
+
|
50
54
|
#
|
51
55
|
# Creates a dialect instance. _builder_ block is a chunk of code of the DSL
|
52
56
|
# that will be executed twice: once at construction time to create sub dialects
|
@@ -13,7 +13,9 @@ module WLang
|
|
13
13
|
|
14
14
|
# Default encoders
|
15
15
|
DEFAULT_ENCODERS = {"java" => :coderay, "ruby" => :coderay, "html" => :coderay,
|
16
|
-
"yaml" => :coderay
|
16
|
+
"yaml" => :coderay, "sql" => :coderay, "css" => :coderay,
|
17
|
+
"javascript" => :coderay, "json" => :coderay, "php" => :coderay,
|
18
|
+
"xml" => :coderay}
|
17
19
|
|
18
20
|
# Upcase encoding
|
19
21
|
def self.coderay(src, options);
|
@@ -21,10 +23,8 @@ module WLang
|
|
21
23
|
encoder = $1.to_sym
|
22
24
|
tokens = CodeRay.scan src, encoder
|
23
25
|
highlighted = tokens.html({
|
24
|
-
:line_numbers => :inline,
|
25
26
|
:wrap => :div,
|
26
|
-
:css => :
|
27
|
-
:style => :cygnus}
|
27
|
+
:css => :class}
|
28
28
|
)
|
29
29
|
return highlighted
|
30
30
|
end
|
@@ -5,20 +5,14 @@ module WLang
|
|
5
5
|
module PlainText
|
6
6
|
|
7
7
|
# Default encoders
|
8
|
-
DEFAULT_ENCODERS = {"upcase"
|
9
|
-
"downcase"
|
10
|
-
"capitalize"
|
11
|
-
"camel
|
8
|
+
DEFAULT_ENCODERS = {"upcase" => :upcase,
|
9
|
+
"downcase" => :downcase,
|
10
|
+
"capitalize" => :capitalize,
|
11
|
+
"camel" => :camel_case,
|
12
|
+
"camel-case" => :camel_case,
|
13
|
+
"upper-camel" => :camel_case,
|
14
|
+
"lower-camel" => :lower_camel_case}
|
12
15
|
|
13
|
-
# Accents to replace when camel-casing
|
14
|
-
# ACCENTS = { ['á','à','â','ä','ã','Ã','Ä','Â','À'] => 'a',
|
15
|
-
# ['é','è','ê','ë','Ë','É','È','Ê'] => 'e',
|
16
|
-
# ['í','ì','î','ï','I','Î','Ì'] => 'i',
|
17
|
-
# ['ó','ò','ô','ö','õ','Õ','Ö','Ô','Ò'] => 'o',
|
18
|
-
# ['œ'] => 'oe',
|
19
|
-
# ['ß'] => 'ss',
|
20
|
-
# ['ú','ù','û','ü','U','Û','Ù'] => 'u'}
|
21
|
-
|
22
16
|
# Upcase encoding
|
23
17
|
def self.upcase(src, options); src.upcase; end
|
24
18
|
|
@@ -30,15 +24,15 @@ module WLang
|
|
30
24
|
|
31
25
|
# Converts a string as CamelCase
|
32
26
|
def self.camel_case(src, options)
|
33
|
-
|
34
|
-
# ac.each do |s|
|
35
|
-
# src.gsub!(s, rep)
|
36
|
-
# end
|
37
|
-
# end
|
38
|
-
src.gsub!(/[^a-zA-Z ]/," ")
|
27
|
+
src.gsub!(/[^a-zA-Z\s]/," ")
|
39
28
|
src = " " + src.split.join(" ")
|
40
29
|
src.gsub!(/ (.)/) { $1.upcase }
|
41
30
|
end
|
31
|
+
|
32
|
+
# Converts a string to lower camelCase
|
33
|
+
def self.lower_camel_case(src, options)
|
34
|
+
camel_case(src, options).gsub(/^([A-Z])/){ $1.downcase }
|
35
|
+
end
|
42
36
|
|
43
37
|
end # module PlainText
|
44
38
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
module WLang
|
3
|
+
class EncoderSet
|
4
|
+
module RedClothEncoders
|
5
|
+
|
6
|
+
# Default encoders
|
7
|
+
DEFAULT_ENCODERS = {"xhtml" => :xhtml_encoding}
|
8
|
+
|
9
|
+
# RDoc encoding
|
10
|
+
def self.xhtml_encoding(src, options)
|
11
|
+
RedCloth.new(src).to_html
|
12
|
+
end
|
13
|
+
|
14
|
+
end # RedClothEncoders
|
15
|
+
end # module EncoderSet
|
16
|
+
end # module WLang
|
@@ -5,9 +5,14 @@ module WLang
|
|
5
5
|
module Ruby
|
6
6
|
|
7
7
|
# Default encoders
|
8
|
-
DEFAULT_ENCODERS = {"
|
8
|
+
DEFAULT_ENCODERS = {"main-encoding" => :main_encoding,
|
9
|
+
"single-quoting" => :single_quoting,
|
9
10
|
"double-quoting" => :double_quoting,
|
10
|
-
"regex-escaping" => :regex_escaping
|
11
|
+
"regex-escaping" => :regex_escaping,
|
12
|
+
"method-case" => :method_case}
|
13
|
+
|
14
|
+
# No-op encoding here
|
15
|
+
def self.main_encoding(src, options); src; end
|
11
16
|
|
12
17
|
# Single-quoting encoding
|
13
18
|
def self.single_quoting(src, options); src.gsub(/([^\\])'/,%q{\1\\\'}); end
|
@@ -17,6 +22,15 @@ module WLang
|
|
17
22
|
|
18
23
|
# Regexp-escaping encoding
|
19
24
|
def self.regex_escaping(src, options); Regexp.escape(src); end
|
25
|
+
|
26
|
+
# Converts any source to a typical ruby method name
|
27
|
+
def self.method_case(src, options)
|
28
|
+
src.strip.gsub(/[^a-zA-Z0-9\s]/," ").
|
29
|
+
gsub(/([A-Z])/){ " " + $1.downcase}.
|
30
|
+
strip.
|
31
|
+
gsub(/^([^a-z])/){ "_" + $1 }.
|
32
|
+
gsub(/\s+/){"_"}
|
33
|
+
end
|
20
34
|
|
21
35
|
end # module Ruby
|
22
36
|
|
@@ -27,6 +27,18 @@ WLang::dialect("ruby", ".rb", ".ruby") do
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
# sql dialect
|
31
|
+
WLang::dialect("sql", ".sql") do
|
32
|
+
ruby_require "wlang/dialects/sql_dialect" do
|
33
|
+
encoders WLang::EncoderSet::SQL
|
34
|
+
end
|
35
|
+
dialect("sybase", '.sybsql') do
|
36
|
+
ruby_require "wlang/dialects/sql_dialect" do
|
37
|
+
encoders WLang::EncoderSet::SQL::Sybase
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
30
42
|
# ruby dialect
|
31
43
|
WLang::dialect("xhtml", ".html", ".xhtml", ".htm") do
|
32
44
|
ruby_require "cgi", "wlang/dialects/xhtml_dialect" do
|
@@ -46,6 +58,13 @@ WLang::dialect("rdoc") do
|
|
46
58
|
end
|
47
59
|
end
|
48
60
|
|
61
|
+
# rdoc dialect
|
62
|
+
WLang::dialect("redcloth") do
|
63
|
+
ruby_require "RedCloth", "wlang/dialects/redcloth_dialect" do
|
64
|
+
encoders WLang::EncoderSet::RedClothEncoders
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
49
68
|
# wlang dialects
|
50
69
|
WLang::dialect("wlang") do
|
51
70
|
|
@@ -56,6 +75,7 @@ WLang::dialect("wlang") do
|
|
56
75
|
# wlang/active-string dialect
|
57
76
|
dialect("active-string") do
|
58
77
|
rules WLang::RuleSet::Basic
|
78
|
+
rules WLang::RuleSet::Imperative
|
59
79
|
end
|
60
80
|
|
61
81
|
# wlang/uri dialect
|
@@ -31,7 +31,30 @@ module WLang
|
|
31
31
|
module XHtml
|
32
32
|
|
33
33
|
# Default mapping between tag symbols and methods
|
34
|
-
DEFAULT_RULESET = {}
|
34
|
+
DEFAULT_RULESET = {'@' => :at}
|
35
|
+
|
36
|
+
def self.at(parser, offset)
|
37
|
+
# parse the url
|
38
|
+
url, reached = parser.parse(offset, 'wlang/active-string')
|
39
|
+
url = WLang::encode(url, 'wlang/xhtml/double-quoting')
|
40
|
+
|
41
|
+
# parse the label if there is one
|
42
|
+
label = nil
|
43
|
+
if parser.has_block?(reached)
|
44
|
+
label, reached = parser.parse_block(reached)
|
45
|
+
label = WLang::encode(label, 'wlang/xhtml/entities-encoding')
|
46
|
+
end
|
47
|
+
|
48
|
+
if label and url.respond_to?(:to_xhtml_link)
|
49
|
+
[url.to_xhtml_link(url, label), reached]
|
50
|
+
elsif url.respond_to?(:to_xhtml_href)
|
51
|
+
[url.to_xhtml_href(url), reached]
|
52
|
+
elsif label
|
53
|
+
["<a href=\"#{url}\">#{label}</a>", reached]
|
54
|
+
else
|
55
|
+
[url, reached]
|
56
|
+
end
|
57
|
+
end
|
35
58
|
|
36
59
|
end # module XHtml
|
37
60
|
|
data/lib/wlang/encoder.rb
CHANGED
@@ -56,7 +56,7 @@ module WLang
|
|
56
56
|
# - options: encoding options through a Hash. Available options are documented
|
57
57
|
# by encoders themselve.
|
58
58
|
#
|
59
|
-
def encode(src, options)
|
59
|
+
def encode(src, options = {})
|
60
60
|
raise(NotImplementedError) unless @block
|
61
61
|
@block.call(src, options)
|
62
62
|
end
|
data/lib/wlang/encoder_set.rb
CHANGED
@@ -25,6 +25,11 @@ module WLang
|
|
25
25
|
@encoders = {}
|
26
26
|
end
|
27
27
|
|
28
|
+
# Yields the block with name, encoder pairs
|
29
|
+
def each
|
30
|
+
@encoders.each_pair{|name,encoder| yield(name, encoder)}
|
31
|
+
end
|
32
|
+
|
28
33
|
#
|
29
34
|
# Adds an encoder under a specific name. Supported signatures are as follows:
|
30
35
|
# - _name_ is a symbol: _encoder_ and _block_ are ignored and encoder is
|
data/lib/wlang/errors.rb
CHANGED
@@ -1,16 +1,80 @@
|
|
1
1
|
module WLang
|
2
2
|
|
3
3
|
# Main error of all WLang errors.
|
4
|
-
class Error < StandardError
|
4
|
+
class Error < StandardError
|
5
|
+
|
6
|
+
# The parser state whe this error has been raised
|
7
|
+
attr_accessor :parser_state
|
8
|
+
|
9
|
+
# Optional cause (other lower level exception)
|
10
|
+
attr_accessor :cause
|
11
|
+
|
12
|
+
# Creates an error instance with a given parser state
|
13
|
+
def initialize(msg = nil, parser_state = nil, cause = nil)
|
14
|
+
raise ArgumentError, "msg expected to be nil or a String" unless (msg.nil? or String===msg)
|
15
|
+
raise ArgumentError, "parser_state expected to be nil or a State"\
|
16
|
+
unless (parser_state.nil? or ::WLang::Parser::State===parser_state)
|
17
|
+
super(msg)
|
18
|
+
@parser_state = parser_state
|
19
|
+
@cause = cause
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns a friendly wlang backtrace
|
23
|
+
def wlang_backtrace
|
24
|
+
parser_state ? parser_state.backtrace : ["no backtrace information, sorry"]
|
25
|
+
end
|
26
|
+
|
27
|
+
end # class Error
|
28
|
+
|
29
|
+
#
|
30
|
+
# Raised by hosted languages when something fails during
|
31
|
+
# evaluation.
|
32
|
+
#
|
33
|
+
class EvalError < ::WLang::Error
|
34
|
+
|
35
|
+
# The expression whose evaluation failed
|
36
|
+
attr_accessor :expression
|
37
|
+
|
38
|
+
# Creates an error instance with an optional expression that
|
39
|
+
# failed
|
40
|
+
def initialize(msg = nil, parser_state = nil, expression = nil, cause = nil)
|
41
|
+
super(msg, parser_state, cause)
|
42
|
+
@expression = expression
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"Evaluation of #{@expression} failed, #{@cause ? @cause.message : ''}"
|
47
|
+
end
|
48
|
+
|
49
|
+
end # class EvalError
|
5
50
|
|
6
|
-
#
|
7
|
-
|
51
|
+
#
|
52
|
+
# Raised when a variable may not be found in the current
|
53
|
+
# parser scope
|
54
|
+
#
|
55
|
+
class UndefinedVariableError < ::WLang::EvalError
|
56
|
+
|
57
|
+
# Name of the variable that could not be found
|
58
|
+
attr_accessor :variable
|
59
|
+
|
60
|
+
# Creates an error instance with an optional variable name
|
61
|
+
def initialize(msg = nil, parser_state = nil, expression = nil, variable = nil)
|
62
|
+
super(msg, parser_state, expression)
|
63
|
+
@variable = variable
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
"Unable to find variable #{@variable}"
|
68
|
+
end
|
69
|
+
|
70
|
+
end # class UndefinedVariableError
|
8
71
|
|
9
72
|
# Error raised by a WLang parser instanciation when an error occurs.
|
10
|
-
class ParseError <
|
73
|
+
class ParseError < ::WLang::Error
|
11
74
|
|
75
|
+
# Where did the parsing failed
|
12
76
|
attr_accessor :line, :column
|
13
77
|
|
14
|
-
end
|
78
|
+
end # class ParseError
|
15
79
|
|
16
|
-
end # module WLang
|
80
|
+
end # module WLang
|
@@ -4,19 +4,21 @@
|
|
4
4
|
class String
|
5
5
|
|
6
6
|
# Converts the string to a wlang template
|
7
|
-
def wlang_template(dialect="wlang/active-string",
|
8
|
-
WLang::
|
7
|
+
def wlang_template(dialect = "wlang/active-string", block_symbols = :braces)
|
8
|
+
WLang::template(self, dialect, block_symbols)
|
9
9
|
end
|
10
10
|
|
11
11
|
#
|
12
12
|
# Instantiates the string as a wlang template using
|
13
13
|
# a context object and a dialect.
|
14
14
|
#
|
15
|
-
def wlang_instantiate(context=nil, dialect="wlang/active-string", block_symbols
|
16
|
-
|
15
|
+
def wlang_instantiate(context = nil, dialect = "wlang/active-string", block_symbols = :braces)
|
16
|
+
WLang::instantiate(self, context, dialect, block_symbols)
|
17
17
|
end
|
18
18
|
alias :wlang :wlang_instantiate
|
19
|
-
|
19
|
+
|
20
|
+
# Computes the column number for a given offset in
|
21
|
+
# this string
|
20
22
|
def __wlang_column_of(index)
|
21
23
|
return 1 if index == 0
|
22
24
|
newline_index = rindex("\n", index - 1)
|
@@ -27,6 +29,8 @@ class String
|
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
32
|
+
# Computes the line number for a given offset in
|
33
|
+
# this string
|
30
34
|
def __wlang_line_of(index)
|
31
35
|
self[0...index].count("\n") + 1
|
32
36
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
module WLang
|
3
|
+
#
|
4
|
+
# Implements a scoping mechanism on top of a ruby hash (accessible through pairing).
|
5
|
+
# Such a scope mimics hashses for has_key?, [] and []= methods. A scope has an
|
6
|
+
# accessible parent. Scopes form a tree, the root being accessible using its
|
7
|
+
# natural accessor. Scope lookup (has_key? and []) uses the hierarchy to find
|
8
|
+
# accessible variables.
|
9
|
+
#
|
10
|
+
# Branching a scope allows installing new variables that hide variables with the
|
11
|
+
# same name in the parent scope. Branching is made easy through the branch methods
|
12
|
+
# that accepts a block, passing the child as first argument:
|
13
|
+
#
|
14
|
+
# scope = HashScope.new(:name => 'wlang')
|
15
|
+
# puts scope[:name] # prints 'wlang'
|
16
|
+
# scope.branch(:name => 'other') do |child|
|
17
|
+
# puts child[:name] # prints 'other'
|
18
|
+
# end
|
19
|
+
# puts scope[:name] # prints 'wlang'
|
20
|
+
#
|
21
|
+
# This branching mechanism is intended to be used to keep a current scope as instance
|
22
|
+
# variable of a using class:
|
23
|
+
#
|
24
|
+
# # We create an initial scope at construction
|
25
|
+
# def initialize
|
26
|
+
# @scope = HashScope.new
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # Appends the current scope with new key/value pairs. Yields the block
|
30
|
+
# # with the new scope and restore the original one after that.
|
31
|
+
# def do_something_with_a_new_scope(hash = {})
|
32
|
+
# @scope = @scope.branch(:name => 'other')
|
33
|
+
# yield if block_given?
|
34
|
+
# @scope = @scope.parent
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
class HashScope
|
38
|
+
|
39
|
+
# The parent scope, or nil if no such parent
|
40
|
+
attr_reader :parent
|
41
|
+
|
42
|
+
# The key/value pairing inside this scope
|
43
|
+
attr_reader :pairing
|
44
|
+
|
45
|
+
# Creates a scope instance with a parent and initial
|
46
|
+
# pairing through a Hash
|
47
|
+
def initialize(pairing = nil, parent = nil)
|
48
|
+
raise ArgumentError, "Hash expected for pairing #{pairing.class} received" unless (pairing.nil? or Hash===pairing)
|
49
|
+
@pairing = pairing || {}
|
50
|
+
@parent = parent
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the root scope
|
54
|
+
def root
|
55
|
+
@root ||= (parent ? parent.root : self)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Checks if a key exists in this scope, delegating to
|
59
|
+
# parent if not found and allowed
|
60
|
+
def has_key?(key, delegate = true)
|
61
|
+
pairing.has_key?(key) || (!delegate.nil? && !parent.nil? && parent.has_key?(key))
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the value associated to a key, delegating to parent
|
65
|
+
# if not found and allowed
|
66
|
+
def [](key, delegate = true)
|
67
|
+
pairing.has_key?(key) ? pairing[key] : (delegate && parent && parent[key])
|
68
|
+
end
|
69
|
+
|
70
|
+
# Associates a key to a value inside this scope
|
71
|
+
def []=(key, value)
|
72
|
+
pairing[key] = value
|
73
|
+
end
|
74
|
+
|
75
|
+
# Creates a new child scope and returns it. If a block is given,
|
76
|
+
# yields the block with the child scope
|
77
|
+
def branch(pairing = nil)
|
78
|
+
child = HashScope.new(pairing, self)
|
79
|
+
yield child if block_given?
|
80
|
+
child
|
81
|
+
end
|
82
|
+
|
83
|
+
# Converts this scope to a full hash with all variables
|
84
|
+
def to_h
|
85
|
+
parent ? parent.to_h.merge(pairing) : pairing.dup
|
86
|
+
end
|
87
|
+
|
88
|
+
end # class HashScope
|
89
|
+
end # module WLang
|