wlang 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENCE.rdoc +25 -0
- data/README.rdoc +111 -0
- data/bin/wlang +24 -0
- data/doc/specification/about.rdoc +61 -0
- data/doc/specification/dialects.wtpl +0 -0
- data/doc/specification/examples.rb +3 -0
- data/doc/specification/glossary.wtpl +14 -0
- data/doc/specification/hosting.rdoc +0 -0
- data/doc/specification/overview.rdoc +116 -0
- data/doc/specification/rulesets.wtpl +87 -0
- data/doc/specification/specification.css +52 -0
- data/doc/specification/specification.html +1361 -0
- data/doc/specification/specification.js +8 -0
- data/doc/specification/specification.wtpl +41 -0
- data/doc/specification/specification.yml +430 -0
- data/doc/specification/symbols.wtpl +16 -0
- data/lib/wlang.rb +186 -0
- data/lib/wlang/basic_object.rb +19 -0
- data/lib/wlang/dialect.rb +230 -0
- data/lib/wlang/dialect_dsl.rb +136 -0
- data/lib/wlang/dialect_loader.rb +69 -0
- data/lib/wlang/dialects/coderay_dialect.rb +35 -0
- data/lib/wlang/dialects/plain_text_dialect.rb +75 -0
- data/lib/wlang/dialects/rdoc_dialect.rb +33 -0
- data/lib/wlang/dialects/ruby_dialect.rb +35 -0
- data/lib/wlang/dialects/sql_dialect.rb +38 -0
- data/lib/wlang/dialects/standard_dialects.rb +113 -0
- data/lib/wlang/dialects/xhtml_dialect.rb +40 -0
- data/lib/wlang/encoder.rb +66 -0
- data/lib/wlang/encoder_set.rb +117 -0
- data/lib/wlang/errors.rb +37 -0
- data/lib/wlang/intelligent_buffer.rb +94 -0
- data/lib/wlang/parser.rb +251 -0
- data/lib/wlang/parser_context.rb +146 -0
- data/lib/wlang/ruby_extensions.rb +21 -0
- data/lib/wlang/rule.rb +66 -0
- data/lib/wlang/rule_set.rb +93 -0
- data/lib/wlang/rulesets/basic_ruleset.rb +75 -0
- data/lib/wlang/rulesets/buffering_ruleset.rb +103 -0
- data/lib/wlang/rulesets/context_ruleset.rb +115 -0
- data/lib/wlang/rulesets/encoding_ruleset.rb +73 -0
- data/lib/wlang/rulesets/imperative_ruleset.rb +132 -0
- data/lib/wlang/rulesets/ruleset_utils.rb +296 -0
- data/lib/wlang/template.rb +79 -0
- data/lib/wlang/wlang_command.rb +54 -0
- data/lib/wlang/wlang_command_options.rb +158 -0
- data/test/sandbox.rb +1 -0
- data/test/test_all.rb +8 -0
- data/test/wlang/anagram_bugs_test.rb +111 -0
- data/test/wlang/basic_ruleset_test.rb +52 -0
- data/test/wlang/buffering_ruleset_test.rb +102 -0
- data/test/wlang/buffering_template1.wtpl +1 -0
- data/test/wlang/buffering_template2.wtpl +1 -0
- data/test/wlang/buffering_template3.wtpl +1 -0
- data/test/wlang/buffering_template4.wtpl +1 -0
- data/test/wlang/buffering_template5.wtpl +1 -0
- data/test/wlang/context_ruleset_test.rb +32 -0
- data/test/wlang/data.rb +3 -0
- data/test/wlang/encoder_set_test.rb +42 -0
- data/test/wlang/imperative_ruleset_test.rb +107 -0
- data/test/wlang/intelligent_buffer_test.rb +194 -0
- data/test/wlang/othersymbols_test.rb +16 -0
- data/test/wlang/parser_context_test.rb +29 -0
- data/test/wlang/parser_test.rb +89 -0
- data/test/wlang/plain_text_dialect_test.rb +21 -0
- data/test/wlang/ruby_dialect_test.rb +100 -0
- data/test/wlang/ruby_expected.rb +3 -0
- data/test/wlang/ruby_template.wrb +3 -0
- data/test/wlang/ruleset_utils_test.rb +245 -0
- data/test/wlang/specification_examples_test.rb +52 -0
- data/test/wlang/test_utils.rb +25 -0
- data/test/wlang/wlang_test.rb +80 -0
- metadata +136 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
module WLang
|
2
|
+
|
3
|
+
#
|
4
|
+
# Template in a given wlang dialect, providing a default context object.
|
5
|
+
#
|
6
|
+
class Template
|
7
|
+
|
8
|
+
# Recognized symbols for blocks
|
9
|
+
BLOCK_SYMBOLS = {:braces => ['{', '}'],
|
10
|
+
:brackets => ['[', ']'],
|
11
|
+
:parentheses => ['(', ')']}
|
12
|
+
|
13
|
+
# Template wlang dialect (wlang/...)
|
14
|
+
attr_reader :dialect
|
15
|
+
|
16
|
+
# Instantiation context
|
17
|
+
attr_reader :context
|
18
|
+
|
19
|
+
# Block symbols
|
20
|
+
attr_reader :block_symbols
|
21
|
+
|
22
|
+
# Attached file source
|
23
|
+
attr_accessor :source_file
|
24
|
+
|
25
|
+
#
|
26
|
+
# Creates a template instance.
|
27
|
+
#
|
28
|
+
def initialize(source, dialect, context=nil, block_symbols = :braces)
|
29
|
+
raise(ArgumentError, "Source is mandatory") if source.nil?
|
30
|
+
if String===dialect
|
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
|
35
|
+
@source = source
|
36
|
+
@dialect = dialect
|
37
|
+
@context = WLang::Parser::Context.new(context)
|
38
|
+
@block_symbols = block_symbols
|
39
|
+
end
|
40
|
+
|
41
|
+
# Resolved a relative uri
|
42
|
+
def file_resolve(uri, exists=false)
|
43
|
+
raise("Unable to resolve #{uri}, this template is not attached to a file")\
|
44
|
+
unless @source_file
|
45
|
+
target = File.join(File.dirname(@source_file), uri)
|
46
|
+
if exists and not(File.exists?(target))
|
47
|
+
raise "File '#{uri}' does not exists"
|
48
|
+
end
|
49
|
+
target
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns template's source text
|
53
|
+
def source_text
|
54
|
+
case @source
|
55
|
+
when String
|
56
|
+
@source
|
57
|
+
else
|
58
|
+
@source.to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Instantiate the template, with an additional context and an output buffer.
|
64
|
+
#
|
65
|
+
def instantiate(buffer=nil, context=nil)
|
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]
|
75
|
+
end
|
76
|
+
|
77
|
+
end # class Template
|
78
|
+
|
79
|
+
end # module WLang
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'wlang'
|
2
|
+
require 'wlang/wlang_command_options'
|
3
|
+
require 'wlang/dialects/standard_dialects'
|
4
|
+
module WLang
|
5
|
+
|
6
|
+
#
|
7
|
+
# Provides the _wlang_ commandline tool. See 'wlang --help' for documentation.
|
8
|
+
#
|
9
|
+
class WLangCommand
|
10
|
+
|
11
|
+
# Creates a commandline instance.
|
12
|
+
def initialize()
|
13
|
+
end
|
14
|
+
|
15
|
+
# Run _wlang_ commandline on specific arguments
|
16
|
+
def run(args)
|
17
|
+
# parse the options
|
18
|
+
options = Options.new.parse(args)
|
19
|
+
|
20
|
+
# get output buffer
|
21
|
+
buffer = STDOUT
|
22
|
+
if options.output_file
|
23
|
+
buffer = File.new(options.output_file, "w")
|
24
|
+
end
|
25
|
+
|
26
|
+
source = File.read(options.template_file)
|
27
|
+
dialect = options.template_dialect
|
28
|
+
braces = options.template_brace
|
29
|
+
context = options.context_object
|
30
|
+
unless options.context_name.nil?
|
31
|
+
context = {options.context_name => context}
|
32
|
+
end
|
33
|
+
template = WLang::Template.new(source, dialect, context, braces)
|
34
|
+
template.source_file = options.template_file
|
35
|
+
|
36
|
+
if options.verbosity>1
|
37
|
+
puts "Instantiating #{options.template_file}"
|
38
|
+
puts "Using dialect #{dialect} : #{dialect.class}"
|
39
|
+
puts "Block delimiters are " << Template::BLOCK_SYMBOLS[braces].inspect
|
40
|
+
puts "Context is " << context.inspect
|
41
|
+
end
|
42
|
+
|
43
|
+
buffer << template.instantiate
|
44
|
+
|
45
|
+
# Flush and close if needed
|
46
|
+
if File===buffer
|
47
|
+
buffer.flush
|
48
|
+
buffer.close
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end # class WLang
|
53
|
+
|
54
|
+
end # module WLang
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
module WLang
|
3
|
+
class WLangCommand
|
4
|
+
|
5
|
+
#
|
6
|
+
# Options of the _wlang_ commandline
|
7
|
+
#
|
8
|
+
class Options
|
9
|
+
|
10
|
+
# Source template file
|
11
|
+
attr_reader :template_file
|
12
|
+
|
13
|
+
# Template dialect
|
14
|
+
attr_reader :template_dialect
|
15
|
+
|
16
|
+
# Which brace kind used in template (:braces, :brackes, :parentheses)
|
17
|
+
attr_reader :template_brace
|
18
|
+
|
19
|
+
# Context file
|
20
|
+
attr_reader :context_file
|
21
|
+
|
22
|
+
# Context object
|
23
|
+
attr_reader :context_object
|
24
|
+
|
25
|
+
# Context kind [:yaml, :ruby, :dsl]
|
26
|
+
attr_reader :context_kind
|
27
|
+
|
28
|
+
# Name of the context
|
29
|
+
attr_reader :context_name
|
30
|
+
|
31
|
+
# Output file
|
32
|
+
attr_reader :output_file
|
33
|
+
|
34
|
+
# Verbose mode?
|
35
|
+
attr_reader :verbosity
|
36
|
+
|
37
|
+
# Initializes the options with default values
|
38
|
+
def initialize
|
39
|
+
@verbosity = 0
|
40
|
+
@template_brace = :braces
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Parses commandline options provided as an array of Strings.
|
45
|
+
#
|
46
|
+
def parse(argv)
|
47
|
+
opts = OptionParser.new do |opt|
|
48
|
+
opt.program_name = File.basename $0
|
49
|
+
opt.version = WLang::VERSION
|
50
|
+
opt.release = nil
|
51
|
+
opt.summary_indent = ' ' * 4
|
52
|
+
banner = <<-EOF
|
53
|
+
# Usage #{opt.program_name} [options] template context-file
|
54
|
+
|
55
|
+
# Template is parsed as a wlang dialect (based on its extension by default) and
|
56
|
+
# instantiated through a given context file.
|
57
|
+
EOF
|
58
|
+
opt.banner = banner.gsub(/[ \t]+# /, "")
|
59
|
+
|
60
|
+
opt.separator nil
|
61
|
+
opt.separator "Options:"
|
62
|
+
|
63
|
+
opt.on("-d", "--dialect=DIALECT",
|
64
|
+
"Interpret source template as a given wlang dialect") do |value|
|
65
|
+
@template_dialect = value
|
66
|
+
end
|
67
|
+
|
68
|
+
opt.on("-o", "--output=OUTPUT",
|
69
|
+
"Flush instantiation result in output file") do |value|
|
70
|
+
@output_file = value
|
71
|
+
end
|
72
|
+
|
73
|
+
opt.on("--brace=BRACE", ["brace", "parenthesis", "bracket"],
|
74
|
+
"Block delimiters used by the template file (braces, brackets, parentheses)") do |value|
|
75
|
+
# handle template brace
|
76
|
+
case value
|
77
|
+
when "brace", "braces", "{"
|
78
|
+
@template_brace = :braces
|
79
|
+
when "brackets", "bracket", "["
|
80
|
+
@template_brace = :brackets
|
81
|
+
when "parentheses", "parenthesis", "("
|
82
|
+
@template_brace = :parentheses
|
83
|
+
else
|
84
|
+
raise "Unknown brace kind #{brace}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
opt.on("--context-name=NAME",
|
89
|
+
"Name of the context object") do |value|
|
90
|
+
@context_name = value
|
91
|
+
end
|
92
|
+
|
93
|
+
opt.on("--context-kind=KIND", ["yaml", "ruby", "dsl"],
|
94
|
+
"Kind of context object (yaml, ruby, dsl)") do |value|
|
95
|
+
@context_kind = '.' + value
|
96
|
+
end
|
97
|
+
|
98
|
+
opt.on("--verbose", "-v",
|
99
|
+
"Display extra progress as we parse.") do |value|
|
100
|
+
@verbosity = 2
|
101
|
+
end
|
102
|
+
|
103
|
+
# No argument, shows at tail. This will print an options summary.
|
104
|
+
# Try it and see!
|
105
|
+
opt.on_tail("-h", "--help", "Show this message") do
|
106
|
+
puts opts
|
107
|
+
exit
|
108
|
+
end
|
109
|
+
|
110
|
+
# Another typical switch to print the version.
|
111
|
+
opt.on_tail("--version", "Show version") do
|
112
|
+
puts "wlang version " << WLang::VERSION << " (c) University of Louvain, Bernard & Louis Lambeau"
|
113
|
+
exit
|
114
|
+
end
|
115
|
+
|
116
|
+
opt.separator nil
|
117
|
+
end
|
118
|
+
|
119
|
+
# handle arguments
|
120
|
+
rest = opts.parse!(argv)
|
121
|
+
if rest.length<1 or rest.length>2
|
122
|
+
puts opts
|
123
|
+
exit
|
124
|
+
end
|
125
|
+
|
126
|
+
# handle template file
|
127
|
+
@template_file = File.expand_path(rest[0])
|
128
|
+
raise("File '#{@template_file}' not readable")\
|
129
|
+
unless File.file?(@template_file) and File.readable?(@template_file)
|
130
|
+
|
131
|
+
# handle context file
|
132
|
+
if rest.length==2
|
133
|
+
@context_file = rest[1]
|
134
|
+
raise("File '#{@context_file}' not readable")\
|
135
|
+
unless File.file?(@context_file) and File.readable?(@context_file)
|
136
|
+
end
|
137
|
+
|
138
|
+
# handle template dialect
|
139
|
+
unless @template_dialect
|
140
|
+
extname = File.extname(@template_file)
|
141
|
+
@template_dialect = WLang::FILE_EXTENSIONS[extname]
|
142
|
+
raise("No known dialect for file extension '#{extname}'\n"\
|
143
|
+
"Known extensions are: " << WLang::FILE_EXTENSIONS.keys.join(", "))\
|
144
|
+
if @template_dialect.nil?
|
145
|
+
end
|
146
|
+
|
147
|
+
# handle context object
|
148
|
+
if @context_file
|
149
|
+
@context_object = WLang::load_data(@context_file, @context_kind)
|
150
|
+
end
|
151
|
+
|
152
|
+
return self
|
153
|
+
end
|
154
|
+
|
155
|
+
end # class Options
|
156
|
+
|
157
|
+
end # class WLangCommand
|
158
|
+
end # module WLang
|
data/test/sandbox.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts "blambeau".to_sym
|
data/test/test_all.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'wlang'
|
3
|
+
module WLang
|
4
|
+
|
5
|
+
# Tests the IntelligentBuffer class
|
6
|
+
class AnagramBugsTest < Test::Unit::TestCase
|
7
|
+
include IntelligentBuffer::Methods
|
8
|
+
|
9
|
+
def test_on_anagram_spacing_policy
|
10
|
+
WLang::dialect("anagram") do
|
11
|
+
rule '=~' do |parser,offset|
|
12
|
+
match, reached = parser.parse(offset, "wlang/dummy")
|
13
|
+
block, reached = parser.parse_block(reached, "wlang/dummy")
|
14
|
+
|
15
|
+
# puts "Here is the block ==="
|
16
|
+
# puts "#{block.gsub(/ /, '.').gsub(/\n/, "\\n\n")}"
|
17
|
+
# puts "=== Here is the block"
|
18
|
+
|
19
|
+
[block, reached]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
template = %q{
|
23
|
+
=~{String}{
|
24
|
+
this is an anagram template
|
25
|
+
}
|
26
|
+
}.gsub(/^ {8}/, '').strip
|
27
|
+
result = template.wlang_instantiate({}, "anagram")
|
28
|
+
assert IntelligentBuffer===result
|
29
|
+
# assert_equal("this is an anagram template", result)
|
30
|
+
|
31
|
+
template = %q{
|
32
|
+
=~{String}{
|
33
|
+
module MyModule
|
34
|
+
end
|
35
|
+
}
|
36
|
+
}.gsub(/^ {8}/, '').strip
|
37
|
+
result = template.wlang_instantiate({}, "anagram")
|
38
|
+
assert IntelligentBuffer===result
|
39
|
+
assert_equal("module MyModule\nend\n", result)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_missing_end_bug
|
43
|
+
WLang::dialect("anagram") do
|
44
|
+
rules WLang::RuleSet::Basic
|
45
|
+
rules WLang::RuleSet::Imperative
|
46
|
+
rule '=~' do |parser,offset|
|
47
|
+
match, reached = parser.parse(offset, "wlang/dummy")
|
48
|
+
match = parser.evaluate(match)
|
49
|
+
block, reached = parser.parse_block(reached, "wlang/dummy")
|
50
|
+
block = block.strip_block(block)
|
51
|
+
block = block.tabto(block,0)
|
52
|
+
parser.evaluate("matching_rules") << [match, block]
|
53
|
+
|
54
|
+
# puts "Here is the block ==="
|
55
|
+
# puts "#{block.gsub(/ /, '.').gsub(/\n/, "\\n\n")}"
|
56
|
+
# puts "=== Here is the block"
|
57
|
+
|
58
|
+
["", reached]
|
59
|
+
end
|
60
|
+
rule '+~' do |parser,offset|
|
61
|
+
what, reached = parser.parse(offset, "wlang/dummy")
|
62
|
+
evaluated = parser.evaluate(what)
|
63
|
+
raise "Unexpected case, #{what} leads to nil" unless evaluated
|
64
|
+
|
65
|
+
rules = parser.evaluate("matching_rules")
|
66
|
+
found = rules.find {|r| r[0]===evaluated}
|
67
|
+
raise "Unexpected case: no match for #{what.class}" unless found
|
68
|
+
|
69
|
+
context = {"n" => evaluated, "matching_rules" => rules}
|
70
|
+
inst = found[1].wlang_instantiate(context, "anagram")
|
71
|
+
|
72
|
+
inst2 = inst.gsub(/ /, '.').gsub(/\n/, "\\n\n")
|
73
|
+
# puts "Here is the inst ==="
|
74
|
+
# puts "#{inst2}"
|
75
|
+
# puts "=== Here is the inst"
|
76
|
+
|
77
|
+
found = [inst, reached]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
template = %Q{
|
81
|
+
=~{Array}{
|
82
|
+
module MyModule
|
83
|
+
*{n as c}{
|
84
|
+
+~{c}
|
85
|
+
}
|
86
|
+
end
|
87
|
+
}
|
88
|
+
=~{Integer}{
|
89
|
+
+{n}
|
90
|
+
}
|
91
|
+
+~{n}
|
92
|
+
}.gsub(/ {8}/,'').strip
|
93
|
+
context = {'n' => [10, 20, 30], 'matching_rules' => []}
|
94
|
+
result = template.wlang_instantiate(context, "anagram")
|
95
|
+
|
96
|
+
# template = template.gsub(/ /, '.').gsub(/\n/, "\\n\n")
|
97
|
+
# puts "Here is the template ==="
|
98
|
+
# puts "#{template}"
|
99
|
+
# puts "=== Here is the template"
|
100
|
+
|
101
|
+
# result = result.gsub(/ /, '.').gsub(/\n/, "\\n\n")
|
102
|
+
# puts "Here is the result ==="
|
103
|
+
# puts "#{result}"
|
104
|
+
# puts "=== Here is the result"
|
105
|
+
result = result.strip
|
106
|
+
expected = "module MyModule\n 10\n \n 20\n \n 30\n\nend"
|
107
|
+
assert_equal expected, result
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'wlang'
|
3
|
+
require 'wlang/test_utils'
|
4
|
+
require 'wlang/rulesets/basic_ruleset'
|
5
|
+
require 'wlang/rulesets/context_ruleset'
|
6
|
+
require 'wlang/rulesets/buffering_ruleset'
|
7
|
+
|
8
|
+
# Tests the Basic ruleset
|
9
|
+
class WLang::BasicRuleSetTest < Test::Unit::TestCase
|
10
|
+
include WLang::TestUtils
|
11
|
+
|
12
|
+
# Installs a dialect on wlang
|
13
|
+
def setup
|
14
|
+
WLang::dialect "basic-test" do
|
15
|
+
rules WLang::RuleSet::Basic
|
16
|
+
rules WLang::RuleSet::Context
|
17
|
+
rules WLang::RuleSet::Buffering
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# tests !{wlang/hosted} operator
|
22
|
+
def test_execution
|
23
|
+
tests = [
|
24
|
+
["!{name}", "blambeau", {"name" => "blambeau"}],
|
25
|
+
["!{!{var}}", "blambeau", {"var" => "name", "name" => "blambeau"}],
|
26
|
+
["!{'hello'.upcase}", "HELLO", {}]
|
27
|
+
]
|
28
|
+
tests.each do |t|
|
29
|
+
template, expected, context = t
|
30
|
+
result = template.wlang(context, "basic-test")
|
31
|
+
assert_equal(expected, result)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Tests recursive_application rule
|
36
|
+
def test_recursive_application
|
37
|
+
tests = [
|
38
|
+
["#={code}{%{wlang/dummy}{+{variable}}}"\
|
39
|
+
"%!{basic-test with variable: 'hello'}{+{code}}", "hello"],
|
40
|
+
["<<={data.rb as context}"\
|
41
|
+
"#={code}{%{wlang/dummy}{+{author}}}"\
|
42
|
+
"%!{basic-test using context}{+{code}}", "blambeau"]
|
43
|
+
]
|
44
|
+
tests.each do |test|
|
45
|
+
template, expected = test
|
46
|
+
template = relative_template(template, "basic-test", __FILE__)
|
47
|
+
result = template.instantiate()
|
48
|
+
assert_equal(expected, result)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end # class WLang::BasicRuleSetTest
|