wlang 0.8.4
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/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
|