wlang 0.9.2 → 0.10.0

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.
Files changed (36) hide show
  1. data/CHANGELOG.md +96 -0
  2. data/{LICENCE.rdoc → LICENCE.md} +2 -2
  3. data/README.md +176 -0
  4. data/bin/wlang +1 -0
  5. data/doc/specification/dialects.wtpl +1 -1
  6. data/doc/specification/specification.html +1237 -1001
  7. data/doc/specification/specification.yml +12 -10
  8. data/lib/wlang.rb +2 -2
  9. data/lib/wlang/dialects/coderay_dialect.rb +1 -0
  10. data/lib/wlang/dialects/hosted_dialect.rb +50 -0
  11. data/lib/wlang/dialects/plain_text_dialect.rb +2 -2
  12. data/lib/wlang/dialects/ruby_dialect.rb +74 -5
  13. data/lib/wlang/dialects/standard_dialects.rb +41 -0
  14. data/lib/wlang/dialects/yaml_dialect.rb +30 -0
  15. data/lib/wlang/encoder.rb +0 -4
  16. data/lib/wlang/ext/string.rb +7 -2
  17. data/lib/wlang/parser.rb +1 -1
  18. data/lib/wlang/rulesets/basic_ruleset.rb +1 -1
  19. data/lib/wlang/rulesets/context_ruleset.rb +3 -3
  20. data/lib/wlang/rulesets/encoding_ruleset.rb +3 -3
  21. data/lib/wlang/rulesets/imperative_ruleset.rb +2 -2
  22. data/test/spec/test_all.rb +1 -1
  23. data/test/standard_dialects/ruby/data.rb +7 -0
  24. data/test/standard_dialects/ruby/inclusion.exp +6 -0
  25. data/test/standard_dialects/ruby/inclusion.tpl +6 -0
  26. data/test/standard_dialects/test_all.rb +29 -0
  27. data/test/standard_dialects/yaml/assumptions_test.rb +13 -0
  28. data/test/standard_dialects/yaml/data.rb +3 -0
  29. data/test/standard_dialects/yaml/inclusion_1.exp +7 -0
  30. data/test/standard_dialects/yaml/inclusion_1.tpl +2 -0
  31. data/test/standard_dialects/yaml/inclusion_2.exp +5 -0
  32. data/test/standard_dialects/yaml/inclusion_2.tpl +3 -0
  33. data/test/unit/wlang/ruby_dialect_test.rb +1 -1
  34. metadata +23 -11
  35. data/CHANGELOG.rdoc +0 -73
  36. data/README.rdoc +0 -165
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: WLang
3
- version: 0.9.1
3
+ version: 0.10.0
4
4
  sections:
5
5
  - identifier: about
6
6
  name: About
@@ -105,6 +105,8 @@ rulesets:
105
105
  - ["wlang/active-string", "Hello %{wlang/dummy}{!{name}}", "Hello !{name}"]
106
106
  - ["wlang/dummy", "Hello %{wlang/dummy}{!{name}}", "Hello %{wlang/dummy}{!{name}}"]
107
107
  - ["wlang/active-string", "Hello ^{plain-text/upcase}{${name}}", "Hello O'NEIL"]
108
+ - ["wlang/ruby", "puts +{name}", "puts \"O'Neil\""]
109
+ - ["wlang/ruby", "puts +{authors}", "puts [\"blambeau\", \"llambeau\", \"ancailliau\"]"]
108
110
  rules:
109
111
  # !{wlang/hosted}
110
112
  - name: "execution"
@@ -254,15 +256,15 @@ rulesets:
254
256
  scope is provided by the Context ruleset. All are variants of 'saving previous instantiations' in
255
257
  scope variables...
256
258
  examples:
257
- - ["wlang/*", '={name as n}{Hello +{n}}', "Hello O'Neil"]
258
- - ["wlang/*", '={name as n}Hello +{n}', "Hello O'Neil"]
259
- - ["wlang/*", '#={name}{blambeau}{Hello +{name}} and +{name}', "Hello blambeau and O'Neil"]
260
- - ["wlang/*", '#={name}{blambeau}Hello +{name} and +{name}', "Hello blambeau and blambeau"]
261
- - ["wlang/*", '={author as name}{Hello +{name}} and +{name}', "Hello blambeau and O'Neil"]
262
- - ["wlang/*", '={author as name}Hello +{name} and +{name}', "Hello blambeau and blambeau"]
263
- - ["wlang/*", '%={wlang/dummy as hello}{Hello +{name}}{+{hello}}', "Hello +{name}"]
264
- - ["wlang/*", '^={plain-text/upcase as name}{+{author}}{Hello +{name}} and +{name}', "Hello BLAMBEAU and O'Neil"]
265
- - ["wlang/*", '^={plain-text/upcase as name}{+{author}}Hello +{name} and +{name}', "Hello BLAMBEAU and BLAMBEAU"]
259
+ - ["wlang/*", '={name as n}{Hello !{n}}', "Hello O'Neil"]
260
+ - ["wlang/*", '={name as n}Hello !{n}', "Hello O'Neil"]
261
+ - ["wlang/*", '#={name}{blambeau}{Hello !{name}} and !{name}', "Hello blambeau and O'Neil"]
262
+ - ["wlang/*", '#={name}{blambeau}Hello !{name} and !{name}', "Hello blambeau and blambeau"]
263
+ - ["wlang/*", '={author as name}{Hello !{name}} and !{name}', "Hello blambeau and O'Neil"]
264
+ - ["wlang/*", '={author as name}Hello !{name} and !{name}', "Hello blambeau and blambeau"]
265
+ - ["wlang/*", '%={wlang/dummy as hello}{Hello !{name}}{!{hello}}', "Hello !{name}"]
266
+ - ["wlang/*", '^={plain-text/upcase as name}{!{author}}{Hello !{name}} and !{name}', "Hello BLAMBEAU and O'Neil"]
267
+ - ["wlang/*", '^={plain-text/upcase as name}{!{author}}Hello !{name} and !{name}', "Hello BLAMBEAU and BLAMBEAU"]
266
268
 
267
269
  rules:
268
270
  # ={wlang/hosted as x}{...}
data/lib/wlang.rb CHANGED
@@ -20,7 +20,7 @@ require 'wlang/intelligent_buffer'
20
20
  module WLang
21
21
 
22
22
  # Current version of WLang
23
- VERSION = "0.9.2".freeze
23
+ VERSION = "0.10.0".freeze
24
24
 
25
25
  ######################################################################## About files and extensions
26
26
 
@@ -58,7 +58,7 @@ module WLang
58
58
 
59
59
  # Checks that _name_ is a valid qualified dialect name or raises an ArgumentError
60
60
  def self.check_qualified_dialect_name(name)
61
- raise ArgumentError, "Invalid dialect qualified name #{name} (/^[-a-z]+([\/][-a-z]+)*$/ expected)", caller\
61
+ raise ArgumentError, "Invalid dialect qualified name '#{name}' (/^[-a-z]+([\/][-a-z]+)*$/ expected)", caller\
62
62
  unless QUALIFIED_DIALECT_NAME_REGEXP =~ name
63
63
  end
64
64
 
@@ -1,3 +1,4 @@
1
+ require 'rubygems'
1
2
  require 'coderay'
2
3
  module WLang
3
4
  class EncoderSet
@@ -0,0 +1,50 @@
1
+ module WLang
2
+ class EncoderSet
3
+
4
+ # Encoders for ruby
5
+ module Hosted
6
+
7
+ # Default encoders
8
+ DEFAULT_ENCODERS = {"main-encoding" => :main_encoding,
9
+ "single-quoting" => :single_quoting,
10
+ "double-quoting" => :double_quoting,
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
16
+
17
+ # Single-quoting encoding
18
+ def self.single_quoting(src, options); src.gsub(/([^\\])'/,%q{\1\\\'}); end
19
+
20
+ # Double-quoting encoding
21
+ def self.double_quoting(src, options); src.gsub('"','\"'); end
22
+
23
+ # Regexp-escaping encoding
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
34
+
35
+
36
+ end # module Hosted
37
+
38
+ end
39
+ class RuleSet
40
+
41
+ # Defines rulset of the wlang/ruby dialect
42
+ module Hosted
43
+
44
+ # Default mapping between tag symbols and methods
45
+ DEFAULT_RULESET = {}
46
+
47
+ end # module Hosted
48
+
49
+ end # class RuleSet
50
+ end # module WLang
@@ -47,7 +47,7 @@ module WLang
47
47
 
48
48
  # Upcase rule as <tt>+{wlang/hosted}</tt>
49
49
  def self.upcase(parser, offset)
50
- expression, reached = parser.parse(offset, "wlang/ruby")
50
+ expression, reached = parser.parse(offset, "wlang/hosted")
51
51
  value = parser.evaluate(expression)
52
52
  value = value.nil? ? "" : value.to_s
53
53
  result = WLang::EncoderSet::PlainText.upcase(value)
@@ -56,7 +56,7 @@ module WLang
56
56
 
57
57
  # Downcase rule as <tt>-{wlang/hosted}</tt>
58
58
  def self.downcase(parser, offset)
59
- expression, reached = parser.parse(offset, "wlang/ruby")
59
+ expression, reached = parser.parse(offset, "wlang/hosted")
60
60
  value = parser.evaluate(expression)
61
61
  value = value.nil? ? "" : value.to_s
62
62
  result = EncoderSet::PlainText.downcase(value)
@@ -5,11 +5,12 @@ module WLang
5
5
  module Ruby
6
6
 
7
7
  # Default encoders
8
- DEFAULT_ENCODERS = {"main-encoding" => :main_encoding,
8
+ DEFAULT_ENCODERS = {"main-encoding" => :main_encoding,
9
9
  "single-quoting" => :single_quoting,
10
10
  "double-quoting" => :double_quoting,
11
11
  "regex-escaping" => :regex_escaping,
12
- "method-case" => :method_case}
12
+ "method-case" => :method_case,
13
+ "literal" => :to_literal}
13
14
 
14
15
  # No-op encoding here
15
16
  def self.main_encoding(src, options); src; end
@@ -31,6 +32,66 @@ module WLang
31
32
  gsub(/^([^a-z])/){ "_" + $1 }.
32
33
  gsub(/\s+/){"_"}
33
34
  end
35
+
36
+ # Ruby classes for which value.inspect will work for returning
37
+ # a valid literal
38
+ SAFE_LITERAL_CLASSES = {}
39
+ [NilClass, TrueClass, FalseClass,
40
+ Fixnum, Bignum,
41
+ Float,
42
+ String,
43
+ Symbol,
44
+ Class,
45
+ Module,
46
+ Regexp].each{|c| SAFE_LITERAL_CLASSES[c] = true}
47
+
48
+ #
49
+ # Converts _value_ to a ruby literal and returns it.
50
+ #
51
+ # Behavior of the algorithm when th value cannot be recognized depends
52
+ # on the :fallback option:
53
+ # * when set to :fail, it raises a NoSuchLiteralError
54
+ # * when set to :inspect, it returns <code>value.inspect</code>
55
+ # * when set to :marshal it uses <code>Marshal::dump(value)</code>
56
+ # * when set to :json it uses <code>JSON::generate(value)</code>
57
+ #
58
+ def self.to_literal(value, options = {:fallback => :fail})
59
+ if value.respond_to?(:to_ruby_literal)
60
+ value.to_ruby_literal
61
+ elsif value.respond_to?(:to_ruby)
62
+ value.to_ruby
63
+ elsif value == (1.0/0)
64
+ return '(1.0/0)'
65
+ elsif value == -(1.0/0)
66
+ return '(-1.0/0)'
67
+ elsif SAFE_LITERAL_CLASSES.key?(value.class)
68
+ value.inspect
69
+ elsif value.kind_of?(Array)
70
+ "[" + value.collect{|v| to_literal(v, options)}.join(', ') + "]"
71
+ elsif value.kind_of?(Hash)
72
+ "{" + value.collect{|pair| "#{to_literal(pair[0], options)} => #{to_literal(pair[1], options)}"}.join(', ') + "}"
73
+ elsif value.kind_of?(Date)
74
+ "Date::parse(#{value.to_s.inspect})"
75
+ elsif value.kind_of?(Time)
76
+ "Time::parse(#{value.inspect.inspect})"
77
+ else
78
+ case options[:fallback]
79
+ when :to_s
80
+ value.to_s
81
+ when :inspect
82
+ value.inspect
83
+ when :marshal
84
+ "Marshal::load(#{Marshal::dump(value).inspect})"
85
+ when :json
86
+ require 'json'
87
+ JSON::generate(value)
88
+ when :fail, nil
89
+ raise WLang::Error, "Unable to convert #{value.inspect} to a ruby literal"
90
+ else
91
+ raise ArgumentError, "Invalid fallback option #{options[:fallback]}"
92
+ end
93
+ end
94
+ end
34
95
 
35
96
  end # module Ruby
36
97
 
@@ -41,9 +102,17 @@ module WLang
41
102
  module Ruby
42
103
 
43
104
  # Default mapping between tag symbols and methods
44
- DEFAULT_RULESET = {}
105
+ DEFAULT_RULESET = {'+' => :inclusion}
45
106
 
107
+ # Rule implementation of <tt>+{wlang/ruby}</tt>.
108
+ def self.inclusion(parser, offset)
109
+ expression, reached = parser.parse(offset, "wlang/hosted")
110
+ value = parser.evaluate(expression)
111
+ result = WLang::EncoderSet::Ruby.to_literal(value)
112
+ [result, reached]
113
+ end
114
+
46
115
  end # module Ruby
47
116
 
48
- end
49
- end
117
+ end # class RuleSet
118
+ end # module WLang
@@ -27,6 +27,13 @@ WLang::dialect("ruby", ".rb", ".ruby") do
27
27
  end
28
28
  end
29
29
 
30
+ # yaml dialect
31
+ WLang::dialect("yaml", ".yaml", ".yml") do
32
+ ruby_require "wlang/dialects/yaml_dialect" do
33
+ encoders WLang::EncoderSet::YAML
34
+ end
35
+ end
36
+
30
37
  # sql dialect
31
38
  WLang::dialect("sql", ".sql") do
32
39
  ruby_require "wlang/dialects/sql_dialect" do
@@ -72,12 +79,32 @@ WLang::dialect("wlang") do
72
79
  dialect("dummy") do
73
80
  end
74
81
 
82
+ # wlang/ruby dialect
83
+ dialect("hosted") do
84
+ ruby_require "wlang/dialects/hosted_dialect" do
85
+ encoders WLang::EncoderSet::Hosted
86
+ rules WLang::RuleSet::Basic
87
+ rules WLang::RuleSet::Encoding
88
+ rules WLang::RuleSet::Imperative
89
+ rules WLang::RuleSet::Context
90
+ rules WLang::RuleSet::Hosted
91
+ end
92
+ end
93
+
75
94
  # wlang/active-string dialect
76
95
  dialect("active-string") do
77
96
  rules WLang::RuleSet::Basic
78
97
  rules WLang::RuleSet::Imperative
79
98
  end
80
99
 
100
+ # wlang/active-text dialect
101
+ dialect("active-text") do
102
+ rules WLang::RuleSet::Basic
103
+ rules WLang::RuleSet::Imperative
104
+ rules WLang::RuleSet::Buffering
105
+ rules WLang::RuleSet::Context
106
+ end
107
+
81
108
  # wlang/uri dialect
82
109
  dialect("uri") do
83
110
  rules WLang::RuleSet::Basic
@@ -90,11 +117,25 @@ WLang::dialect("wlang") do
90
117
  rules WLang::RuleSet::Basic
91
118
  rules WLang::RuleSet::Encoding
92
119
  rules WLang::RuleSet::Imperative
120
+ rules WLang::RuleSet::Buffering
93
121
  rules WLang::RuleSet::Context
94
122
  rules WLang::RuleSet::Ruby
95
123
  end
96
124
  end
97
125
 
126
+ # wlang/ruby dialect
127
+ dialect("yaml", ".wyaml", ".wyml") do
128
+ ruby_require "wlang/dialects/yaml_dialect" do
129
+ encoders WLang::EncoderSet::YAML
130
+ rules WLang::RuleSet::Basic
131
+ rules WLang::RuleSet::Encoding
132
+ rules WLang::RuleSet::Imperative
133
+ rules WLang::RuleSet::Buffering
134
+ rules WLang::RuleSet::Context
135
+ rules WLang::RuleSet::YAML
136
+ end
137
+ end
138
+
98
139
  # wlang/ruby dialect
99
140
  dialect("xhtml", ".wtpl", ".whtml") do
100
141
  ruby_require "cgi", "wlang/dialects/xhtml_dialect" do
@@ -0,0 +1,30 @@
1
+ require "yaml"
2
+ module WLang
3
+ class EncoderSet
4
+ module YAML
5
+
6
+ # Default encoders
7
+ DEFAULT_ENCODERS = {}
8
+
9
+ end # module YAML
10
+ end
11
+ class RuleSet
12
+ module YAML
13
+
14
+ # Default mapping between tag symbols and methods
15
+ DEFAULT_RULESET = {'+' => :inclusion}
16
+
17
+ # Rule implementation of <tt>+{wlang/ruby}</tt>.
18
+ def self.inclusion(parser, offset)
19
+ expression, reached = parser.parse(offset, "wlang/hosted")
20
+ value = parser.evaluate(expression)
21
+ col = parser.buffer.__wlang_column_of(parser.buffer.length)
22
+ result = value.to_yaml.
23
+ gsub(/^\s*---\s*|\n$/,"").
24
+ __wlang_realign(col-1, true)
25
+ [result, reached]
26
+ end
27
+
28
+ end # module YAML
29
+ end # class RuleSet
30
+ end # module WLang
data/lib/wlang/encoder.rb CHANGED
@@ -37,10 +37,6 @@ module WLang
37
37
  # same arguments.
38
38
  #
39
39
  def initialize(&block)
40
- unless block.nil?
41
- raise(ArgumentError, "Expected a rule block of arity 2")\
42
- unless block.arity==2
43
- end
44
40
  @block = block
45
41
  end
46
42
 
@@ -34,6 +34,11 @@ class String
34
34
  def __wlang_line_of(index)
35
35
  self[0...index].count("\n") + 1
36
36
  end
37
-
37
+
38
+ def __wlang_realign(offset, strip_first = false)
39
+ s = gsub(/^/, ' ' * offset)
40
+ strip_first ? s.gsub(/\A\s*/, "") : s
41
+ end
42
+
38
43
  end
39
-
44
+
data/lib/wlang/parser.rb CHANGED
@@ -315,7 +315,7 @@ module WLang
315
315
  end
316
316
 
317
317
  # Protected methods are...
318
- protected :hosted, :offset, :source_text, :buffer, :dialect
318
+ protected :hosted, :offset, :source_text, :dialect
319
319
 
320
320
  end # class Parser
321
321
  end # module WLang
@@ -19,7 +19,7 @@ module WLang
19
19
 
20
20
  # Rule implementation of <tt>!{wlang/ruby}</tt>.
21
21
  def self.execution(parser, offset)
22
- expression, reached = parser.parse(offset, "wlang/ruby")
22
+ expression, reached = parser.parse(offset, "wlang/hosted")
23
23
  value = parser.evaluate(expression)
24
24
  result = value.nil? ? "" : value.to_s
25
25
  [result, reached]
@@ -18,7 +18,7 @@ module WLang
18
18
 
19
19
  # Rule implementation of <tt>={wlang/hosted as x}{...}</tt>
20
20
  def self.assignment(parser, offset)
21
- expr, reached = parser.parse(offset, "wlang/ruby")
21
+ expr, reached = parser.parse(offset, "wlang/hosted")
22
22
 
23
23
  # decode expression
24
24
  decoded = U.decode_expr_as(expr)
@@ -62,7 +62,7 @@ module WLang
62
62
 
63
63
  # Rule implementation of <tt>%={wlang/active-string as x}{...}{...}</tt>
64
64
  def self.modulo_assignment(parser, offset)
65
- dialect_as, reached = parser.parse(offset, "wlang/ruby")
65
+ dialect_as, reached = parser.parse(offset, "wlang/hosted")
66
66
 
67
67
  # decode expression
68
68
  decoded = U.decode_qdialect_as(dialect_as)
@@ -84,7 +84,7 @@ module WLang
84
84
 
85
85
  # Rule implementation of <tt>^={wlang/active-string as x}{...}{...}</tt>
86
86
  def self.encoding_assignment(parser, offset)
87
- encoding_as, reached = parser.parse(offset, "wlang/ruby")
87
+ encoding_as, reached = parser.parse(offset, "wlang/hosted")
88
88
 
89
89
  # decode expression
90
90
  decoded = U.decode_qencoder_as(encoding_as)
@@ -45,7 +45,7 @@ module WLang
45
45
 
46
46
  # Injection as <tt>${wlang/ruby}</tt>
47
47
  def self.injection(parser, offset)
48
- expression, reached = parser.parse(offset, "wlang/ruby")
48
+ expression, reached = parser.parse(offset, "wlang/hosted")
49
49
  result = parser.evaluate(expression)
50
50
  encoded = parser.encode(result.to_s, "main-encoding")
51
51
  [encoded, reached]
@@ -53,7 +53,7 @@ module WLang
53
53
 
54
54
  # Single-quoted as <tt>'{wlang/ruby}</tt>
55
55
  def self.single_quoted(parser, offset)
56
- expression, reached = parser.parse(offset, "wlang/ruby")
56
+ expression, reached = parser.parse(offset, "wlang/hosted")
57
57
  result = parser.evaluate(expression)
58
58
  encoded = parser.encode(result.to_s, "single-quoting")
59
59
  ["'" << encoded, reached]
@@ -61,7 +61,7 @@ module WLang
61
61
 
62
62
  # Double-quoted as <tt>"{wlang/ruby}</tt>
63
63
  def self.double_quoted(parser, offset)
64
- expression, reached = parser.parse(offset, "wlang/ruby")
64
+ expression, reached = parser.parse(offset, "wlang/hosted")
65
65
  result = parser.evaluate(expression)
66
66
  encoded = parser.encode(result.to_s, "double-quoting")
67
67
  ['"' << encoded, reached]
@@ -27,7 +27,7 @@ module WLang
27
27
  # otherwise instantiates #3 if present.
28
28
  #
29
29
  def self.conditional(parser, offset)
30
- expression, reached = parser.parse(offset, "wlang/ruby")
30
+ expression, reached = parser.parse(offset, "wlang/hosted")
31
31
  value = parser.evaluate(expression)
32
32
  if value
33
33
  then_block, reached = parser.parse_block(reached)
@@ -64,7 +64,7 @@ module WLang
64
64
  # name x in the scope). If #3 is present, it is instantiated between elements.
65
65
  #
66
66
  def self.enumeration(parser, offset)
67
- expression, reached = parser.parse(offset, "wlang/ruby")
67
+ expression, reached = parser.parse(offset, "wlang/hosted")
68
68
 
69
69
  # decode 'wlang/hosted using each as x' expression
70
70
  hash = U.expr(:expr,