bade 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Bade.gemspec +16 -15
- data/lib/bade/ast/document.rb +1 -1
- data/lib/bade/ast/node/doctype_node.rb +6 -6
- data/lib/bade/ast/node/static_text_node.rb +27 -0
- data/lib/bade/ast/node_registrator.rb +3 -4
- data/lib/bade/ast/string_serializer.rb +4 -3
- data/lib/bade/generator.rb +94 -88
- data/lib/bade/parser.rb +28 -587
- data/lib/bade/parser/parser_constants.rb +18 -0
- data/lib/bade/parser/parser_lines.rb +213 -0
- data/lib/bade/parser/parser_mixin.rb +139 -0
- data/lib/bade/parser/parser_ruby_code.rb +77 -0
- data/lib/bade/parser/parser_tag.rb +116 -0
- data/lib/bade/parser/parser_text.rb +78 -0
- data/lib/bade/precompiled.rb +2 -2
- data/lib/bade/renderer.rb +17 -12
- data/lib/bade/ruby_extensions/array.rb +9 -13
- data/lib/bade/ruby_extensions/string.rb +15 -8
- data/lib/bade/runtime/block.rb +6 -10
- data/lib/bade/runtime/mixin.rb +2 -1
- data/lib/bade/runtime/render_binding.rb +5 -5
- data/lib/bade/version.rb +1 -1
- metadata +25 -4
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
module Bade
|
5
|
+
require_relative '../parser'
|
6
|
+
|
7
|
+
class Parser
|
8
|
+
module TagRegexps
|
9
|
+
BLOCK_EXPANSION = /\A:\s+/
|
10
|
+
OUTPUT_CODE = LineIndicatorRegexps::OUTPUT_BLOCK
|
11
|
+
TEXT_START = /\A /
|
12
|
+
|
13
|
+
PARAMS_ARGS_DELIMITER = /\A\s*,/
|
14
|
+
PARAMS_END = /\A\s*\)/
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param [String] tag tag name
|
18
|
+
#
|
19
|
+
def parse_tag(tag)
|
20
|
+
tag = fixed_trailing_colon(tag)
|
21
|
+
|
22
|
+
if tag.is_a?(AST::Node)
|
23
|
+
tag_node = tag
|
24
|
+
else
|
25
|
+
tag_node = append_node(:tag, add: true)
|
26
|
+
tag_node.name = tag
|
27
|
+
end
|
28
|
+
|
29
|
+
parse_tag_attributes
|
30
|
+
|
31
|
+
case @line
|
32
|
+
when TagRegexps::BLOCK_EXPANSION
|
33
|
+
# Block expansion
|
34
|
+
@line = $'
|
35
|
+
parse_line_indicators(add_newline: false)
|
36
|
+
|
37
|
+
when TagRegexps::OUTPUT_CODE
|
38
|
+
# Handle output code
|
39
|
+
parse_line_indicators(add_newline: false)
|
40
|
+
|
41
|
+
when CLASS_TAG_RE
|
42
|
+
# Class name
|
43
|
+
@line = $'
|
44
|
+
|
45
|
+
attr_node = append_node(:tag_attr)
|
46
|
+
attr_node.name = 'class'
|
47
|
+
attr_node.value = fixed_trailing_colon($1).single_quote
|
48
|
+
|
49
|
+
parse_tag tag_node
|
50
|
+
|
51
|
+
when ID_TAG_RE
|
52
|
+
# Id name
|
53
|
+
@line = $'
|
54
|
+
|
55
|
+
attr_node = append_node(:tag_attr)
|
56
|
+
attr_node.name = 'id'
|
57
|
+
attr_node.value = fixed_trailing_colon($1).single_quote
|
58
|
+
|
59
|
+
parse_tag tag_node
|
60
|
+
|
61
|
+
when TagRegexps::TEXT_START
|
62
|
+
# Text content
|
63
|
+
@line = $'
|
64
|
+
parse_text
|
65
|
+
|
66
|
+
when ''
|
67
|
+
# nothing
|
68
|
+
|
69
|
+
else
|
70
|
+
syntax_error "Unknown symbol after tag definition #{@line}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_tag_attributes
|
75
|
+
# Check to see if there is a delimiter right after the tag name
|
76
|
+
|
77
|
+
# between tag name and attribute must not be space
|
78
|
+
# and skip when is nothing other
|
79
|
+
return unless @line.start_with?('(')
|
80
|
+
|
81
|
+
# remove starting bracket
|
82
|
+
@line.remove_first!
|
83
|
+
|
84
|
+
loop do
|
85
|
+
case @line
|
86
|
+
when CODE_ATTR_RE
|
87
|
+
# Value ruby code
|
88
|
+
@line = $'
|
89
|
+
attr_node = append_node(:tag_attr)
|
90
|
+
attr_node.name = $1
|
91
|
+
attr_node.value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG)
|
92
|
+
|
93
|
+
when TagRegexps::PARAMS_ARGS_DELIMITER
|
94
|
+
# args delimiter
|
95
|
+
@line = $'
|
96
|
+
next
|
97
|
+
|
98
|
+
when TagRegexps::PARAMS_END
|
99
|
+
# Find ending delimiter
|
100
|
+
@line = $'
|
101
|
+
break
|
102
|
+
|
103
|
+
else
|
104
|
+
# Found something where an attribute should be
|
105
|
+
@line.lstrip!
|
106
|
+
syntax_error('Expected attribute') unless @line.empty?
|
107
|
+
|
108
|
+
# Attributes span multiple lines
|
109
|
+
append_node(:newline)
|
110
|
+
syntax_error('Expected closing tag attributes delimiter `)`') if @lines.empty?
|
111
|
+
next_line
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
module Bade
|
5
|
+
require_relative '../parser'
|
6
|
+
|
7
|
+
class Parser
|
8
|
+
module TextRegexps
|
9
|
+
INTERPOLATION_START = /(\\)?(&|#)\{/
|
10
|
+
INTERPOLATION_END = /\A\}/
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_text
|
14
|
+
new_index = @line.index(TextRegexps::INTERPOLATION_START)
|
15
|
+
|
16
|
+
# the interpolation sequence is not in text, mark whole text as static
|
17
|
+
if new_index.nil?
|
18
|
+
append_node(:static_text, value: @line)
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
unparsed_part = String.new
|
23
|
+
|
24
|
+
while (new_index = @line.index(TextRegexps::INTERPOLATION_START))
|
25
|
+
if $1.nil?
|
26
|
+
static_part = unparsed_part + @line.remove_first!(new_index)
|
27
|
+
append_node(:static_text, value: static_part)
|
28
|
+
|
29
|
+
@line.remove_first!(2) # #{ or &{
|
30
|
+
|
31
|
+
dynamic_part = parse_ruby_code(TextRegexps::INTERPOLATION_END)
|
32
|
+
node = append_node(:output, value: dynamic_part)
|
33
|
+
node.escaped = $2 == '&'
|
34
|
+
|
35
|
+
@line.remove_first! # ending }
|
36
|
+
|
37
|
+
unparsed_part = String.new
|
38
|
+
else
|
39
|
+
unparsed_part << @line.remove_first!(new_index)
|
40
|
+
@line.remove_first! # symbol \
|
41
|
+
unparsed_part << @line.remove_first!(2) # #{ or &{
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# add the rest of line
|
46
|
+
append_node(:static_text, value: unparsed_part + @line) unless @line.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_text_block(first_line, text_indent = nil)
|
50
|
+
if !first_line || first_line.empty?
|
51
|
+
text_indent = nil
|
52
|
+
else
|
53
|
+
@line = first_line
|
54
|
+
parse_text
|
55
|
+
end
|
56
|
+
|
57
|
+
until @lines.empty?
|
58
|
+
if @lines.first.blank?
|
59
|
+
next_line
|
60
|
+
append_node(:newline)
|
61
|
+
else
|
62
|
+
indent = get_indent(@lines.first)
|
63
|
+
break if indent <= @indents.last
|
64
|
+
|
65
|
+
next_line
|
66
|
+
|
67
|
+
@line.remove_indent!(text_indent ? text_indent : indent, @tabsize)
|
68
|
+
|
69
|
+
parse_text
|
70
|
+
|
71
|
+
# The indentation of first line of the text block
|
72
|
+
# determines the text base indentation.
|
73
|
+
text_indent ||= indent
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/bade/precompiled.rb
CHANGED
data/lib/bade/renderer.rb
CHANGED
@@ -22,14 +22,14 @@ module Bade
|
|
22
22
|
# @param [String] reference_path reference file from which is load performed
|
23
23
|
# @param [String] msg standard message
|
24
24
|
#
|
25
|
-
def initialize(loading_path, reference_path, msg=nil)
|
25
|
+
def initialize(loading_path, reference_path, msg = nil)
|
26
26
|
super(msg)
|
27
27
|
@loading_path = loading_path
|
28
28
|
@reference_path = reference_path
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
TEMPLATE_FILE_NAME = '(__template__)'
|
32
|
+
TEMPLATE_FILE_NAME = '(__template__)'.freeze
|
33
33
|
|
34
34
|
# @return [String]
|
35
35
|
#
|
@@ -167,7 +167,8 @@ module Bade
|
|
167
167
|
# ----------------------------------------------------------------------------- #
|
168
168
|
# Render
|
169
169
|
|
170
|
-
# @param [Binding] binding custom binding for evaluating the template, but it is not recommended to use,
|
170
|
+
# @param [Binding] binding custom binding for evaluating the template, but it is not recommended to use,
|
171
|
+
# use :locals and #with_locals instead
|
171
172
|
# @param [String] new_line newline string, default is \n
|
172
173
|
# @param [String] indent indent string, default is two spaces
|
173
174
|
#
|
@@ -177,8 +178,8 @@ module Bade
|
|
177
178
|
self.lambda_binding = binding unless binding.nil? # backward compatibility
|
178
179
|
|
179
180
|
run_vars = {
|
180
|
-
|
181
|
-
|
181
|
+
Generator::NEW_LINE_NAME.to_sym => new_line,
|
182
|
+
Generator::BASE_INDENT_NAME.to_sym => indent,
|
182
183
|
}
|
183
184
|
run_vars.reject! { |_key, value| value.nil? } # remove nil values
|
184
185
|
|
@@ -219,7 +220,8 @@ module Bade
|
|
219
220
|
document
|
220
221
|
end
|
221
222
|
|
222
|
-
# Tries to find file with name, if no file could be found or there are multiple files matching the name error is
|
223
|
+
# Tries to find file with name, if no file could be found or there are multiple files matching the name error is
|
224
|
+
# raised
|
223
225
|
#
|
224
226
|
# @param [String] name name of the file that should be found
|
225
227
|
# @param [String] reference_path path to file from which is loading/finding
|
@@ -229,25 +231,28 @@ module Bade
|
|
229
231
|
def _find_file!(name, reference_path)
|
230
232
|
sub_path = File.expand_path(name, File.dirname(reference_path))
|
231
233
|
|
232
|
-
if File.
|
234
|
+
if File.exist?(sub_path)
|
233
235
|
return if sub_path.end_with?('.rb') # handled in Generator
|
234
236
|
sub_path
|
235
237
|
else
|
236
238
|
bade_path = "#{sub_path}.bade"
|
237
239
|
rb_path = "#{sub_path}.rb"
|
238
240
|
|
239
|
-
bade_exist = File.
|
240
|
-
rb_exist = File.
|
241
|
-
relative = Pathname.new(reference_path).relative_path_from(Pathname.new(File.dirname(
|
241
|
+
bade_exist = File.exist?(bade_path)
|
242
|
+
rb_exist = File.exist?(rb_path)
|
243
|
+
relative = Pathname.new(reference_path).relative_path_from(Pathname.new(File.dirname(file_path))).to_s
|
242
244
|
|
243
245
|
if bade_exist && rb_exist
|
244
|
-
|
246
|
+
message = "Found both .bade and .rb files for `#{name}` in file #{relative}, "\
|
247
|
+
'change the import path so it references uniq file.'
|
248
|
+
raise LoadError.new(name, reference_path, message)
|
245
249
|
elsif bade_exist
|
246
250
|
return bade_path
|
247
251
|
elsif rb_exist
|
248
252
|
return # handled in Generator
|
249
253
|
else
|
250
|
-
|
254
|
+
message = "Can't find file matching name `#{name}` referenced from file #{relative}"
|
255
|
+
raise LoadError.new(name, reference_path, message)
|
251
256
|
end
|
252
257
|
end
|
253
258
|
end
|
@@ -7,19 +7,17 @@ class Array
|
|
7
7
|
#
|
8
8
|
# @return [Fixnum]
|
9
9
|
#
|
10
|
-
def rindex_last_matching
|
10
|
+
def rindex_last_matching
|
11
11
|
return nil if empty?
|
12
12
|
|
13
13
|
index = nil
|
14
14
|
|
15
15
|
current_index = count - 1
|
16
16
|
reverse_each do |item|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
break
|
22
|
-
end
|
17
|
+
break unless yield item
|
18
|
+
|
19
|
+
index = current_index
|
20
|
+
current_index -= 1
|
23
21
|
end
|
24
22
|
|
25
23
|
index
|
@@ -29,15 +27,13 @@ class Array
|
|
29
27
|
#
|
30
28
|
# @return [Fixnum] count of items
|
31
29
|
#
|
32
|
-
def rcount_matching
|
30
|
+
def rcount_matching
|
33
31
|
count = 0
|
34
32
|
|
35
33
|
reverse_each do |item|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
break
|
40
|
-
end
|
34
|
+
break unless yield item
|
35
|
+
|
36
|
+
count += 1
|
41
37
|
end
|
42
38
|
|
43
39
|
count
|
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :nodoc:
|
3
4
|
class String
|
4
|
-
SPACE_CHAR = ' '
|
5
|
-
TAB_CHAR = "\t"
|
5
|
+
SPACE_CHAR = ' '.freeze
|
6
|
+
TAB_CHAR = "\t".freeze
|
6
7
|
|
7
8
|
# Creates new string surrounded by single quotes
|
8
9
|
#
|
@@ -14,7 +15,7 @@ class String
|
|
14
15
|
|
15
16
|
|
16
17
|
def blank?
|
17
|
-
strip.
|
18
|
+
strip.empty?
|
18
19
|
end
|
19
20
|
|
20
21
|
|
@@ -37,16 +38,14 @@ class String
|
|
37
38
|
|
38
39
|
def __chars_count_for_indent(indent, tabsize)
|
39
40
|
count = 0
|
40
|
-
|
41
|
+
each_char do |char|
|
41
42
|
break if indent <= 0
|
42
43
|
|
43
44
|
case char
|
44
45
|
when SPACE_CHAR
|
45
46
|
indent -= 1
|
46
47
|
when TAB_CHAR
|
47
|
-
if indent - tabsize < 0
|
48
|
-
raise StandardError, 'malformed tabs'
|
49
|
-
end
|
48
|
+
raise StandardError, 'malformed tabs' if indent - tabsize < 0
|
50
49
|
|
51
50
|
indent -= tabsize
|
52
51
|
else
|
@@ -88,7 +87,7 @@ class String
|
|
88
87
|
def get_indent(tabsize)
|
89
88
|
count = 0
|
90
89
|
|
91
|
-
|
90
|
+
each_char do |char|
|
92
91
|
if char == SPACE_CHAR
|
93
92
|
count += 1
|
94
93
|
elsif char == TAB_CHAR
|
@@ -101,4 +100,12 @@ class String
|
|
101
100
|
count
|
102
101
|
end
|
103
102
|
|
103
|
+
# source: http://apidock.com/rails/String/strip_heredoc
|
104
|
+
# @return [String]
|
105
|
+
#
|
106
|
+
def strip_heredoc
|
107
|
+
min_val = scan(/^[ \t]*(?=\S)/).min
|
108
|
+
indent = (min_val && min_val.size) || 0
|
109
|
+
gsub(/^[ \t]{#{indent}}/, '')
|
110
|
+
end
|
104
111
|
end
|
data/lib/bade/runtime/block.rb
CHANGED
@@ -57,11 +57,9 @@ module Bade
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def call!(*args)
|
60
|
-
if @block.nil?
|
61
|
-
|
62
|
-
|
63
|
-
render_binding.__buff.concat(@block.call(*args))
|
64
|
-
end
|
60
|
+
raise MissingBlockDefinitionError.new(name, :call) if @block.nil?
|
61
|
+
|
62
|
+
render_binding.__buff.concat(@block.call(*args))
|
65
63
|
end
|
66
64
|
|
67
65
|
# --- Rendering methods
|
@@ -75,11 +73,9 @@ module Bade
|
|
75
73
|
end
|
76
74
|
|
77
75
|
def render!(*args)
|
78
|
-
if @block.nil?
|
79
|
-
|
80
|
-
|
81
|
-
@block.call(*args).join
|
82
|
-
end
|
76
|
+
raise MissingBlockDefinitionError.new(name, :render) if @block.nil?
|
77
|
+
|
78
|
+
@block.call(*args).join
|
83
79
|
end
|
84
80
|
end
|
85
81
|
end
|
data/lib/bade/runtime/mixin.rb
CHANGED
@@ -9,7 +9,8 @@ module Bade
|
|
9
9
|
block.call(blocks, *args)
|
10
10
|
rescue ArgumentError => e
|
11
11
|
case e.message
|
12
|
-
when /\Awrong number of arguments \(given ([0-9]+), expected ([0-9]+)\)\Z/,
|
12
|
+
when /\Awrong number of arguments \(given ([0-9]+), expected ([0-9]+)\)\Z/,
|
13
|
+
/\Awrong number of arguments \(([0-9]+) for ([0-9]+)\)\Z/
|
13
14
|
# handle incorrect parameters count
|
14
15
|
|
15
16
|
# minus one, because first argument is always hash of blocks
|
@@ -80,17 +80,17 @@ module Bade
|
|
80
80
|
# @return [String]
|
81
81
|
#
|
82
82
|
def __html_escaped(text)
|
83
|
-
text.
|
84
|
-
.
|
85
|
-
.
|
86
|
-
.
|
83
|
+
text.gsub('&', '&')
|
84
|
+
.gsub('<', '<')
|
85
|
+
.gsub('>', '>')
|
86
|
+
.gsub('"', '"')
|
87
87
|
end
|
88
88
|
|
89
89
|
def __tag_render_attribute(name, *values)
|
90
90
|
values = values.compact
|
91
91
|
return if values.empty?
|
92
92
|
|
93
|
-
%
|
93
|
+
%( #{name}="#{values.join(' ')}")
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|