wlang 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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,