bade 0.2.4 → 0.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38d9666b68d2c14fcebd6fe3e8f6581980d4e62fc86a62575863b9b9134c0cff
4
- data.tar.gz: e899fadf43703bbb5fc2ab6af91142cb145a149a16e3d3948b7c3be94655b822
3
+ metadata.gz: 9363fa63bbc18e9d9cb2af336438820127d766ef7e5b18306ddc37cdd608adfd
4
+ data.tar.gz: 6a6c86dea0211e852507fa26e7ec2d6d35b5b03469f5c6919455749134f102bd
5
5
  SHA512:
6
- metadata.gz: 309bcca03d9006c8fe4881f3119ff50b0544307205da57599a4892fefd7f95c9373f6e7d11d681ecb77edf90798afbcd3480855a58429e2ed476f238d2aa9493
7
- data.tar.gz: 45daec6d8d39c959a462053cbbc351e35d6a3055b63d9bc2ff64e8f70146e605f9447fabf48d024d682e66c6612d1ee40b866a8673f70dceecbb738f51e0b2ab
6
+ metadata.gz: 37b06b4ef4aa918a88e96c5bfd164ef5d41b9bc0e22949d857b3af143e6dcb462d41f8c10e880f29f4a201e0ca115309e4734e46268f0b1c6fc9f0c7c50802d2
7
+ data.tar.gz: a828c7d9c09450235b719c37381add83dcd0980d256f97b755dd6b5ceb287a1f6baca4f43a04c00523cf45a1d692aac6d7b7abd79f8b5694fe713f3686ceefa0
data/Bade.gemspec CHANGED
@@ -1,11 +1,10 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('../lib', __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  require 'bade/version'
7
7
 
8
-
9
8
  Gem::Specification.new do |spec|
10
9
  spec.name = 'bade'
11
10
  spec.version = Bade::VERSION
@@ -14,15 +13,17 @@ Gem::Specification.new do |spec|
14
13
  spec.summary = 'Minimalistic template engine for Ruby.'
15
14
  spec.homepage = 'https://github.com/epuber-io/bade'
16
15
  spec.license = 'MIT'
17
- spec.required_ruby_version = '>= 2.0'
16
+ spec.metadata = { 'rubygems_mfa_required' => 'true' }
17
+ spec.required_ruby_version = '>= 2.5'
18
18
 
19
- spec.files = Dir['bin/**/*'] + Dir['lib/**/*'] + %w(Bade.gemspec Gemfile README.md)
19
+ spec.files = Dir['bin/**/*'] + Dir['lib/**/*'] + %w[Bade.gemspec Gemfile README.md]
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
22
  spec.require_paths = ['lib']
23
23
 
24
- spec.add_development_dependency 'bundler', '~> 1'
24
+ spec.add_dependency 'psych', '>= 2.2', '< 5.0'
25
+
26
+ spec.add_development_dependency 'fakefs', '~> 1.3'
25
27
  spec.add_development_dependency 'rspec', '~> 3.2'
26
- spec.add_development_dependency 'rake', '~> 11'
27
- spec.add_development_dependency 'rubocop', '~> 0.35'
28
+ spec.add_development_dependency 'rubocop', '~> 1.14'
28
29
  end
data/Gemfile CHANGED
@@ -1,8 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
4
6
 
5
- gem 'coveralls', require: false
6
7
  gem 'benchmark-ips', require: false
7
- gem 'ruby-prof'
8
- gem 'flamegraph'
8
+
9
+ group :dev, optional: true do
10
+ gem 'debase', require: false
11
+ gem 'ruby-debug-ide', require: false
12
+ end
13
+
14
+ group :benchmarks, optional: true do
15
+ gem 'flamegraph'
16
+ gem 'ruby-prof'
17
+ end
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  # Bade
3
3
 
4
- [![Gem Version](https://badge.fury.io/rb/bade.svg)](http://badge.fury.io/rb/bade) [![Build Status](https://travis-ci.org/epuber-io/bade.svg?branch=master)](https://travis-ci.org/epuber-io/bade) [![Coverage Status](https://coveralls.io/repos/epuber-io/bade/badge.svg?branch=master&service=github)](https://coveralls.io/github/epuber-io/bade?branch=master) [![Inline docs](http://inch-ci.org/github/epuber-io/bade.svg?branch=master)](http://inch-ci.org/github/epuber-io/bade)
4
+ [![Gem Version](https://badge.fury.io/rb/bade.svg)](http://badge.fury.io/rb/bade) [![Build Status](https://github.com/epuber-io/bade/actions/workflows/tests.yml/badge.svg)](https://github.com/epuber-io/bade/actions) [![Coverage Status](https://coveralls.io/repos/epuber-io/bade/badge.svg?branch=master&service=github)](https://coveralls.io/github/epuber-io/bade?branch=master) [![Inline docs](https://inch-ci.org/github/epuber-io/bade.svg?branch=master)](https://inch-ci.org/github/epuber-io/bade)
5
5
 
6
6
 
7
7
  Minimalistic template engine written in Ruby for Ruby. Developed mainly to help with creating e-books. Highly influenced by [Jade](http://jade-lang.com) and [Slim](http://slim-lang.com).
@@ -30,7 +30,7 @@ Or install it yourself as:
30
30
 
31
31
  ## Development
32
32
 
33
- After checking out the repo, run `bundle install` to install dependencies. Then, run `rake spec` to run the tests.
33
+ After checking out the repo, run `bundle install --with dev` to install dependencies. Then, run `rake spec` to run the tests.
34
34
 
35
35
  To install this gem onto your local machine, run `bundle exec rake install`.
36
36
 
@@ -40,7 +40,7 @@ To install this gem onto your local machine, run `bundle exec rake install`.
40
40
  Bug reports and pull requests are welcome on GitHub at https://github.com/epuber-io/bade. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
41
41
 
42
42
 
43
- ## TODO
43
+ ## TODO
44
44
 
45
45
  - [ ] create documentation about syntax
46
46
  - [ ] create several examples
@@ -24,7 +24,7 @@ module Bade
24
24
 
25
25
  # @param root [Bade::Node]
26
26
  #
27
- def initialize(root: Node.new(:root), file_path: nil)
27
+ def initialize(root: Node.new(:root, nil), file_path: nil)
28
28
  @root = root
29
29
 
30
30
  @file_path = file_path.dup.freeze unless file_path.nil?
@@ -22,19 +22,19 @@ module Bade
22
22
 
23
23
  class MixinDeclarationNode < MixinCommonNode
24
24
  def allowed_parameter_types
25
- [:mixin_param, :mixin_key_param, :mixin_block_param]
25
+ %i[mixin_param mixin_key_param mixin_block_param]
26
26
  end
27
27
  end
28
28
 
29
29
  class MixinBlockNode < MixinCommonNode
30
30
  def allowed_parameter_types
31
- [:mixin_param, :mixin_key_param]
31
+ %i[mixin_param mixin_key_param]
32
32
  end
33
33
  end
34
34
 
35
35
  class MixinCallNode < MixinCommonNode
36
36
  def allowed_parameter_types
37
- [:mixin_param, :mixin_key_param]
37
+ %i[mixin_param mixin_key_param]
38
38
  end
39
39
 
40
40
  def blocks
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../ruby2_keywords'
4
+
3
5
  module Bade
4
6
  module AST
5
7
  class StaticTextNode < Node
@@ -11,8 +13,8 @@ module Bade
11
13
  #
12
14
  attr_accessor :escaped
13
15
 
14
- def initialize(*args)
15
- super
16
+ ruby2_keywords def initialize(*args)
17
+ super(*args)
16
18
 
17
19
  @escaped = false
18
20
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../ruby2_keywords'
4
+
3
5
  module Bade
4
6
  module AST
5
7
  class ValueNode < Node
@@ -15,17 +17,26 @@ module Bade
15
17
  #
16
18
  attr_accessor :conditional
17
19
 
18
- def initialize(*args)
19
- super
20
+ # @return [String, nil]
21
+ #
22
+ attr_accessor :default_value
23
+
24
+ ruby2_keywords def initialize(*args)
25
+ super(*args)
20
26
 
21
27
  @escaped = false
22
28
  @conditional = false
29
+ @default_value = nil
23
30
  end
24
31
 
25
32
  # @param [ValueNode] other
26
33
  #
27
34
  def ==(other)
28
- super && value == other.value && escaped == other.escaped && conditional == other.conditional
35
+ super &&
36
+ value == other.value &&
37
+ escaped == other.escaped &&
38
+ conditional == other.conditional &&
39
+ default_value == other.default_value
29
40
  end
30
41
  end
31
42
  end
data/lib/bade/ast/node.rb CHANGED
@@ -10,7 +10,11 @@ module Bade
10
10
  #
11
11
  attr_reader :type
12
12
 
13
- # @return [Array<Bade::Node>]
13
+ # @return [Bade::AST::Node, nil]
14
+ #
15
+ attr_accessor :parent
16
+
17
+ # @return [Array<Bade::AST::Node>]
14
18
  #
15
19
  attr_accessor :children
16
20
 
@@ -20,11 +24,16 @@ module Bade
20
24
  #
21
25
  attr_reader :lineno
22
26
 
23
- def initialize(type, lineno: nil)
27
+ # @return [String] filename
28
+ #
29
+ attr_reader :filename
30
+
31
+ def initialize(type, parent = nil, lineno: nil, filename: nil)
24
32
  @type = type
33
+ @parent = parent
25
34
  @children = []
26
-
27
35
  @lineno = lineno
36
+ @filename = filename
28
37
  end
29
38
 
30
39
  def to_s
@@ -38,12 +38,12 @@ module Bade
38
38
  #
39
39
  # @return [Bade::AST::Node]
40
40
  #
41
- def create(type, lineno)
41
+ def create(type, parent, lineno: nil, filename: nil)
42
42
  klass = registered_types[type]
43
43
 
44
44
  raise ::KeyError, "Undefined node type #{type.inspect}" if klass.nil?
45
45
 
46
- klass.new(type, lineno: lineno)
46
+ klass.new(type, parent, lineno: lineno, filename: filename)
47
47
  end
48
48
  end
49
49
 
@@ -55,6 +55,7 @@ module Bade
55
55
  register_type DoctypeNode, :doctype
56
56
 
57
57
  register_type ValueNode, :import
58
+ register_type ValueNode, :yield
58
59
 
59
60
  # --- Comments
60
61
 
@@ -37,13 +37,13 @@ module Bade
37
37
 
38
38
  children_s = ''
39
39
  if node.children.count > 0
40
- children_s = "\n" + node.children.map { |n| node_to_s(n, level + 1) }.join("\n") + "\n#{indent}"
40
+ children_s = "\n#{node.children.map { |n| node_to_s(n, level + 1) }.join("\n")}\n#{indent}"
41
41
  end
42
42
 
43
43
  other = ''
44
44
 
45
45
  case node
46
- when TagNode
46
+ when TagNode, MixinCommonNode
47
47
  other = node.name
48
48
  when KeyValueNode
49
49
  other = "#{node.name}:#{node.value}"
@@ -56,15 +56,13 @@ module Bade
56
56
  ''
57
57
  end
58
58
  other = "#{escaped_sign}#{node.value}"
59
- when MixinCommonNode
60
- other = node.name
61
59
  when Node
62
60
  # nothing
63
61
  else
64
62
  raise "Unknown node class #{node.class} of type #{node.type} for serializing"
65
63
  end
66
64
 
67
- other = ' ' + other if other && !other.empty?
65
+ other = " #{other}" if other && !other.empty?
68
66
 
69
67
  "#{indent}(#{type_s}#{other}#{children_s})"
70
68
  end
@@ -75,10 +75,9 @@ module Bade
75
75
  end
76
76
 
77
77
  def buff_code(text)
78
- @buff << ' ' * @code_indent + text
78
+ @buff << "#{' ' * @code_indent}#{text}"
79
79
  end
80
80
 
81
-
82
81
  # @param document [Bade::Document]
83
82
  #
84
83
  def visit_document(document)
@@ -87,6 +86,7 @@ module Bade
87
86
  end
88
87
 
89
88
  buff_code("# ----- start file #{document.file_path}") unless document.file_path.nil?
89
+ buff_code "__buffs_push(#{location(filename: document.file_path, lineno: 0, label: '<top>')})"
90
90
 
91
91
  new_root = if @optimize
92
92
  Optimizer.new(document.root).optimize
@@ -116,6 +116,8 @@ module Bade
116
116
  # @param current_node [Node]
117
117
  #
118
118
  def visit_node(current_node)
119
+ update_location_node(current_node)
120
+
119
121
  case current_node.type
120
122
  when :root
121
123
  visit_node_children(current_node)
@@ -135,7 +137,7 @@ module Bade
135
137
  buff_print_text ' -->'
136
138
 
137
139
  when :comment
138
- comment_text = '#' + current_node.children.map(&:value).join("\n#")
140
+ comment_text = "##{current_node.children.map(&:value).join("\n#")}"
139
141
  buff_code(comment_text)
140
142
 
141
143
  when :doctype
@@ -158,8 +160,7 @@ module Bade
158
160
  buff_print_text output_code
159
161
 
160
162
  when :newline
161
- # buff_print_value(NEW_LINE_NAME)
162
-
163
+ # no-op
163
164
  when :import
164
165
  base_path = File.expand_path(current_node.value, File.dirname(@document.file_path))
165
166
  load_path = if base_path.end_with?('.rb') && File.exist?(base_path)
@@ -168,8 +169,11 @@ module Bade
168
169
  "#{base_path}.rb"
169
170
  end
170
171
 
171
- buff_code "load('#{load_path}')" unless load_path.nil?
172
-
172
+ buff_code "__load('#{load_path}')" unless load_path.nil?
173
+ when :yield
174
+ block_name = DEFAULT_BLOCK_NAME
175
+ method = current_node.conditional ? 'call' : 'call!'
176
+ buff_code "#{block_name}.#{method}"
173
177
  else
174
178
  raise "Unknown type #{current_node.type}"
175
179
  end
@@ -269,19 +273,22 @@ module Bade
269
273
  params = mixin_node.params
270
274
  result = []
271
275
 
272
- if mixin_node.type == :mixin_call
276
+ case mixin_node.type
277
+ when :mixin_call
273
278
  blocks = mixin_node.blocks
274
279
 
275
280
  other_children = (mixin_node.children - mixin_node.blocks - mixin_node.params)
276
281
  if other_children.count { |n| n.type != :newline } > 0
277
- def_block_node = AST::NodeRegistrator.create(:mixin_block, mixin_node.lineno)
282
+ def_block_node = AST::NodeRegistrator.create(:mixin_block, mixin_node, lineno: mixin_node.lineno)
278
283
  def_block_node.name = DEFAULT_BLOCK_NAME
279
284
  def_block_node.children = other_children
280
285
 
281
286
  blocks << def_block_node
282
287
  end
283
288
 
284
- if !blocks.empty?
289
+ if blocks.empty?
290
+ result << '{}'
291
+ else
285
292
  buff_code '__blocks = {}'
286
293
 
287
294
  blocks.each do |block|
@@ -289,17 +296,18 @@ module Bade
289
296
  end
290
297
 
291
298
  result << '__blocks.dup'
292
- else
293
- result << '{}'
294
299
  end
295
- elsif mixin_node.type == :mixin_decl
300
+ when :mixin_decl
296
301
  result << '__blocks'
297
302
  end
298
303
 
304
+ # positional params
305
+ result += params.select { |n| n.type == :mixin_param }
306
+ .map { |param| param.default_value ? "#{param.value} = #{param.default_value}" : param.value }
299
307
 
300
- # normal params
301
- result += params.select { |n| n.type == :mixin_param }.map(&:value)
302
- result += params.select { |n| n.type == :mixin_key_param }.map { |param| "#{param.name}: #{param.value}" }
308
+ # key-value params
309
+ result += params.select { |n| n.type == :mixin_key_param }
310
+ .map { |param| "#{param.name}: #{param.value}" }
303
311
 
304
312
  result.join(', ')
305
313
  end
@@ -311,14 +319,10 @@ module Bade
311
319
  # @return [nil]
312
320
  #
313
321
  def block_definition(block_node)
314
- buff_code "__blocks['#{block_node.name}'] = __create_block('#{block_node.name}') do"
322
+ buff_code "__blocks['#{block_node.name}'] = __create_block('#{block_node.name}', #{location_node(block_node)}) do"
315
323
 
316
324
  code_indent do
317
- buff_code '__buffs_push()'
318
-
319
325
  visit_node_children(block_node)
320
-
321
- buff_code '__buffs_pop()'
322
326
  end
323
327
 
324
328
  buff_code 'end'
@@ -352,7 +356,7 @@ module Bade
352
356
  #
353
357
  def visit_block_decl(current_node)
354
358
  params = formatted_mixin_params(current_node)
355
- buff_code "#{MIXINS_NAME}['#{current_node.name}'] = __create_mixin('#{current_node.name}', &lambda { |#{params}|"
359
+ buff_code "#{MIXINS_NAME}['#{current_node.name}'] = __create_mixin('#{current_node.name}', #{location_node(current_node)}, &lambda { |#{params}|"
356
360
 
357
361
  code_indent do
358
362
  blocks_name_declaration(current_node)
@@ -362,8 +366,6 @@ module Bade
362
366
  buff_code '})'
363
367
  end
364
368
 
365
-
366
-
367
369
  # @param [String] str
368
370
  #
369
371
  # @return [Void]
@@ -371,6 +373,54 @@ module Bade
371
373
  def escape_double_quotes!(str)
372
374
  str.gsub!(/"/, '\"')
373
375
  end
376
+
377
+ # @param [Bade::AST::Node]
378
+ # @return [Void]
379
+ def update_location_node(node)
380
+ should_skip = case node.type
381
+ when :code
382
+ value = node.value.strip
383
+
384
+ %w[end else }].include?(value) || value.match(/^when /)
385
+ when :newline
386
+ true
387
+ else
388
+ false
389
+ end
390
+ return if should_skip
391
+ return if node.lineno.nil?
392
+
393
+ buff_code "__update_lineno(#{node.lineno})"
394
+ end
395
+
396
+ # @param [String] filename
397
+ # @param [Fixnum] lineno
398
+ # @param [String] label
399
+ # @return [String]
400
+ def location(filename:, lineno:, label:)
401
+ args = [
402
+ filename ? "path: '#{filename}'" : nil,
403
+ "lineno: #{lineno}",
404
+ "label: '#{label}'",
405
+ ].compact
406
+
407
+ "Location.new(#{args.join(',')})"
408
+ end
409
+
410
+ # @param [Node] node
411
+ # @return [String]
412
+ def location_node(node)
413
+ label = case node.type
414
+ when :mixin_decl
415
+ "+#{node.name}"
416
+ when :mixin_block
417
+ "#{node.name} in +#{node.parent.name}"
418
+ else
419
+ node.name
420
+ end
421
+
422
+ location(filename: node.filename, lineno: node.lineno, label: label)
423
+ end
374
424
  end
375
425
 
376
426
  # backward compatibility
@@ -9,10 +9,10 @@ module Bade
9
9
  NAME_RE_STRING = "(#{WORD_RE}(?:#{WORD_RE}|:|-|_)*)".freeze
10
10
 
11
11
  ATTR_NAME_RE_STRING = "\\A\\s*#{NAME_RE_STRING}".freeze
12
- CODE_ATTR_RE = /#{ATTR_NAME_RE_STRING}\s*&?:\s*/
12
+ CODE_ATTR_RE = /#{ATTR_NAME_RE_STRING}\s*&?:\s*/.freeze
13
13
 
14
- TAG_RE = /\A#{NAME_RE_STRING}/
15
- CLASS_TAG_RE = /\A\.#{NAME_RE_STRING}/
16
- ID_TAG_RE = /\A##{NAME_RE_STRING}/
14
+ TAG_RE = /\A#{NAME_RE_STRING}/.freeze
15
+ CLASS_TAG_RE = /\A\.#{NAME_RE_STRING}/.freeze
16
+ ID_TAG_RE = /\A##{NAME_RE_STRING}/.freeze
17
17
  end
18
18
  end
@@ -6,19 +6,20 @@ module Bade
6
6
 
7
7
  class Parser
8
8
  module LineIndicatorRegexps
9
- IMPORT = /\Aimport /
10
- MIXIN_DECL = /\Amixin #{NAME_RE_STRING}/
11
- MIXIN_CALL = /\A\+#{NAME_RE_STRING}/
12
- BLOCK_DECLARATION = /\Ablock #{NAME_RE_STRING}/
13
- HTML_COMMENT = %r{\A//! }
14
- NORMAL_COMMENT = %r{\A//}
15
- TEXT_BLOCK_START = /\A\|( ?)/
16
- INLINE_HTML = /\A</
17
- CODE_BLOCK = /\A-/
18
- OUTPUT_BLOCK = /\A(\??)(&?)=/
19
- DOCTYPE = /\Adoctype\s/i
20
- TAG_CLASS_START_BLOCK = /\A\./
21
- TAG_ID_START_BLOCK = /\A#/
9
+ IMPORT = /\Aimport /.freeze
10
+ YIELD = /\Ayield(!?)/.freeze
11
+ MIXIN_DECL = /\Amixin #{NAME_RE_STRING}/.freeze
12
+ MIXIN_CALL = /\A\+#{NAME_RE_STRING}/.freeze
13
+ BLOCK_DECLARATION = /\Ablock #{NAME_RE_STRING}/.freeze
14
+ HTML_COMMENT = %r{\A//! }.freeze
15
+ NORMAL_COMMENT = %r{\A//}.freeze
16
+ TEXT_BLOCK_START = /\A\|( ?)/.freeze
17
+ INLINE_HTML = /\A</.freeze
18
+ CODE_BLOCK = /\A-/.freeze
19
+ OUTPUT_BLOCK = /\A(\??)(&?)=/.freeze
20
+ DOCTYPE = /\Adoctype\s/i.freeze
21
+ TAG_CLASS_START_BLOCK = /\A\./.freeze
22
+ TAG_ID_START_BLOCK = /\A#/.freeze
22
23
  end
23
24
 
24
25
  def reset(lines = nil, stacks = nil)
@@ -135,6 +136,11 @@ module Bade
135
136
  @line = $'
136
137
  parse_import
137
138
 
139
+ when LineIndicatorRegexps::YIELD
140
+ @line = $'
141
+ node = append_node(:yield, add: true)
142
+ node.conditional = $1.nil?
143
+
138
144
  when LineIndicatorRegexps::MIXIN_DECL
139
145
  # Mixin declaration
140
146
  @line = $'
@@ -195,12 +201,8 @@ module Bade
195
201
  @line = $' if $1
196
202
  parse_tag($&)
197
203
 
198
- when LineIndicatorRegexps::TAG_CLASS_START_BLOCK
199
- # Found class name -> implicit div
200
- parse_tag 'div'
201
-
202
- when LineIndicatorRegexps::TAG_ID_START_BLOCK
203
- # Found id name -> implicit div
204
+ when LineIndicatorRegexps::TAG_CLASS_START_BLOCK, LineIndicatorRegexps::TAG_ID_START_BLOCK
205
+ # Found a class or id selector.
204
206
  parse_tag 'div'
205
207
 
206
208
  else
@@ -6,18 +6,19 @@ module Bade
6
6
 
7
7
  class Parser
8
8
  module MixinRegexps
9
- TEXT_START = /\A /
10
- BLOCK_EXPANSION = /\A:\s+/
11
- OUTPUT_CODE = /\A(&?)=/
9
+ TEXT_START = /\A /.freeze
10
+ BLOCK_EXPANSION = /\A:\s+/.freeze
11
+ OUTPUT_CODE = /\A(&?)=/.freeze
12
12
 
13
- PARAMS_END = /\A\s*\)/
13
+ PARAMS_END = /\A\s*\)/.freeze
14
14
 
15
- PARAMS_END_SPACES = /^\s*$/
16
- PARAMS_ARGS_DELIMITER = /\A\s*,/
15
+ PARAMS_END_SPACES = /^\s*$/.freeze
16
+ PARAMS_ARGS_DELIMITER = /\A\s*,/.freeze
17
17
 
18
- PARAMS_PARAM_NAME = /\A\s*#{NAME_RE_STRING}/
19
- PARAMS_BLOCK_NAME = /\A\s*&#{NAME_RE_STRING}/
18
+ PARAMS_PARAM_NAME = /\A\s*#{NAME_RE_STRING}/.freeze
19
+ PARAMS_BLOCK_NAME = /\A\s*&#{NAME_RE_STRING}/.freeze
20
20
  PARAMS_KEY_PARAM_NAME = CODE_ATTR_RE
21
+ PARAMS_PARAM_DEFAULT_START = /\A\s*=/.freeze
21
22
  end
22
23
 
23
24
  def parse_mixin_call(mixin_name)
@@ -64,7 +65,7 @@ module Bade
64
65
  @line = $'
65
66
  attr_node = append_node(:mixin_key_param)
66
67
  attr_node.name = fixed_trailing_colon($1)
67
- attr_node.value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG)
68
+ attr_node.value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG, allow_multiline: true)
68
69
 
69
70
  when MixinRegexps::PARAMS_ARGS_DELIMITER
70
71
  # args delimiter
@@ -83,7 +84,7 @@ module Bade
83
84
 
84
85
  else
85
86
  attr_node = append_node(:mixin_param)
86
- attr_node.value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG)
87
+ attr_node.value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG, allow_multiline: true)
87
88
  end
88
89
  end
89
90
  end
@@ -114,7 +115,12 @@ module Bade
114
115
 
115
116
  when MixinRegexps::PARAMS_PARAM_NAME
116
117
  @line = $'
117
- append_node(:mixin_param, value: $1)
118
+ attr_node = append_node(:mixin_param, value: $1)
119
+
120
+ if @line =~ MixinRegexps::PARAMS_PARAM_DEFAULT_START
121
+ @line = $'
122
+ attr_node.default_value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG)
123
+ end
118
124
 
119
125
  when MixinRegexps::PARAMS_BLOCK_NAME
120
126
  @line = $'
@@ -6,8 +6,8 @@ module Bade
6
6
 
7
7
  class Parser
8
8
  module ParseRubyCodeRegexps
9
- END_NEW_LINE = /\A\s*\n/
10
- END_PARAMS_ARG = /\A\s*[,)]/
9
+ END_NEW_LINE = /\A\s*\n/.freeze
10
+ END_PARAMS_ARG = /\A\s*[,)]/.freeze
11
11
  end
12
12
 
13
13
  # Parse ruby code, ended with outer delimiters
@@ -16,17 +16,27 @@ module Bade
16
16
  #
17
17
  # @return [Void] parsed ruby code
18
18
  #
19
- def parse_ruby_code(outer_delimiters)
19
+ def parse_ruby_code(outer_delimiters, allow_multiline: false)
20
20
  code = String.new
21
21
  end_re = if outer_delimiters.is_a?(Regexp)
22
22
  outer_delimiters
23
23
  else
24
24
  /\A\s*[#{Regexp.escape outer_delimiters.to_s}]/
25
25
  end
26
+
26
27
  delimiters = []
27
28
  string_start_quote_char = nil
28
29
 
29
- until @line.empty? || (delimiters.empty? && @line =~ end_re)
30
+ loop do
31
+ break if !allow_multiline && @line.empty?
32
+ break if allow_multiline && @line.empty? && (@lines && @lines.empty?)
33
+ break if delimiters.empty? && @line =~ end_re
34
+
35
+ if @line.empty? && allow_multiline && !(@lines && @lines.empty?)
36
+ next_line
37
+ code << "\n"
38
+ end
39
+
30
40
  char = @line[0]
31
41
 
32
42
  # backslash escaped delimiter
@@ -70,7 +80,7 @@ module Bade
70
80
  '{' => '}',
71
81
  }.freeze
72
82
 
73
- RUBY_QUOTES = %w(' ").freeze
83
+ RUBY_QUOTES = %w[' "].freeze
74
84
 
75
85
  RUBY_NOT_NESTABLE_DELIMITERS = RUBY_QUOTES
76
86
 
@@ -78,7 +88,7 @@ module Bade
78
88
  RUBY_END_DELIMITERS = (%w(\) ] }) + RUBY_NOT_NESTABLE_DELIMITERS).freeze
79
89
  RUBY_ALL_DELIMITERS = (RUBY_START_DELIMITERS + RUBY_END_DELIMITERS).uniq.freeze
80
90
 
81
- RUBY_START_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_START_DELIMITERS.join}]/
82
- RUBY_END_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_END_DELIMITERS.join}]/
91
+ RUBY_START_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_START_DELIMITERS.join}]/.freeze
92
+ RUBY_END_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_END_DELIMITERS.join}]/.freeze
83
93
  end
84
94
  end