hamli 0.1.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a825c4ad1a1e140b7c8020a0137a278c2844356c0f5ab166fcca716c0f21a88
4
- data.tar.gz: e603ee2dd8d1698447bf4eb20729b0087b174f679171d2a4d94efd4f2117e580
3
+ metadata.gz: ff0371e95f3c87b4519036500b8ff6b5a7de3a91f9509f05240a9a23d53105ff
4
+ data.tar.gz: f96c2b0b84ad2d546433934ebe40e6973effe559896e0bacda88a0d769e462fc
5
5
  SHA512:
6
- metadata.gz: 759e52536c4111473a6a46d22a44a027db0e96590d2367e9bbd64395bf91cd72c5984cc4341e457f493c99c27f878d5f2142a42f4a26c77175a245c1e242b269
7
- data.tar.gz: 914d1aa219cba92b697455223ba65bd140336bb7e20fdc5378eaadf6825630d9d392f56e797b84b160a0d96ad84296042e74468b2a6c1b093c4898f05165ce7e
6
+ metadata.gz: 60c15036fe01fc5c4716cf49f8969dca86055628c42ba7c299a66de3102f08b7520d1c296f58b032840bfd925b5fcefaae068add96cc9f7d9370a68667d27c1e
7
+ data.tar.gz: a6c1a3d3b777ef1fd46f4928d2c045f57051169d84d2643050e1b917cb49cc0db0b9a20f9dee049a9fed8a1ad172ed74bae20fa4b94ab6616405d66d814970b2
data/CHANGELOG.md CHANGED
@@ -2,6 +2,48 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.5.0 - 2022-01-30
6
+
7
+ ### Added
8
+
9
+ - Support escaped output line.
10
+ - Support non-escaped output line.
11
+
12
+ ## 0.4.0 - 2022-01-26
13
+
14
+ ### Added
15
+
16
+ - Add :file option for better error report.
17
+
18
+ ### Fixed
19
+
20
+ - Fix error from parse_text_block on some case.
21
+ - Fix filter bug on :text node.
22
+ - Fix parse_interpolate_line bug.
23
+ - Fix haml comment parser bug.
24
+ - Fix pipe multi-line handling.
25
+
26
+ ## 0.3.0 - 2022-01-26
27
+
28
+ ### Added
29
+
30
+ - Support output block in tag line.
31
+ - Support HTML comment line.
32
+ - Support HTML conditional comment line.
33
+ - Support Haml comment line.
34
+ - Support output line.
35
+ - Support control line.
36
+
37
+ ## 0.2.0 - 2022-01-25
38
+
39
+ ### Added
40
+
41
+ - Add Interpolation Filter.
42
+
43
+ ### Changed
44
+
45
+ - Use :position node.
46
+
5
47
  ## 0.1.0 - 2022-01-25
6
48
 
7
49
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hamli (0.1.0)
4
+ hamli (0.5.0)
5
5
  temple
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,9 +1,12 @@
1
1
  # Hamli
2
2
 
3
3
  [![test](https://github.com/r7kamura/hamli/actions/workflows/test.yml/badge.svg)](https://github.com/r7kamura/hamli/actions/workflows/test.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/hamli.svg)](https://rubygems.org/gems/hamli)
4
5
 
5
6
  Yet another implementation for [Haml](https://github.com/haml/haml) template language.
6
7
 
8
+ Hamli is used by [Hamlcop](https://github.com/r7kamura/hamlcop).
9
+
7
10
  ## Installation
8
11
 
9
12
  Add this line to your application's Gemfile:
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temple'
4
+
5
+ module Hamli
6
+ module Filters
7
+ # Pass-through some expressions which are unknown for Temple.
8
+ class Base < ::Temple::HTML::Filter
9
+ # @param [Integer] begin_
10
+ # @param [Integer] end_
11
+ # @param [Array] expression
12
+ # @return [Array]
13
+ def on_hamli_position(begin_, end_, expression)
14
+ [:hamli, :position, begin_, end_, compile(expression)]
15
+ end
16
+
17
+ # @param [Array] expression
18
+ # @return [Array]
19
+ def on_hamli_text(expression)
20
+ [:hamli, :text, compile(expression)]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strscan'
4
+
5
+ module Hamli
6
+ module Filters
7
+ class Interpolation < Base
8
+ # @param [Integer] begin_
9
+ # @param [Integer] end_
10
+ # @return [Array] S-expression.
11
+ def on_hamli_interpolate(begin_, _end_, string)
12
+ block = [:multi]
13
+ scanner = ::StringScanner.new(string)
14
+ until scanner.eos?
15
+ charpos = scanner.charpos
16
+ if (value = scanner.scan(/\\#\{/))
17
+ block << [:static, value]
18
+ elsif scanner.scan(/#\{((?>[^{}]|(\{(?>[^{}]|\g<1>)*\}))*)\}/)
19
+ code = scanner[1]
20
+ begin2 = begin_ + charpos + 2
21
+ if code.start_with?('{') && code.end_with?('}')
22
+ escape = true
23
+ code = code[1..-2]
24
+ begin2 -= 1
25
+ else
26
+ escape = false
27
+ end
28
+ block << [:hamli, :position, begin2, begin2 + code.length, [:hamli, :output, escape, code, [:multi]]]
29
+ elsif (value = scanner.scan(/([#\\]?[^#\\]*([#\\][^\\\#{][^#\\]*)*)/)) # rubocop:disable Lint/DuplicateBranch
30
+ block << [:static, value]
31
+ end
32
+ end
33
+ block
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hamli
4
+ module Filters
5
+ autoload :Base, 'hamli/filters/base'
6
+ autoload :Interpolation, 'hamli/filters/interpolation'
7
+ end
8
+ end
data/lib/hamli/parser.rb CHANGED
@@ -4,6 +4,15 @@ require 'temple'
4
4
 
5
5
  module Hamli
6
6
  class Parser < ::Temple::Parser
7
+ define_options(
8
+ :file
9
+ )
10
+
11
+ def initialize(_options = {})
12
+ super
13
+ @file_path = options[:file] || '(__TEMPLATE__)'
14
+ end
15
+
7
16
  # @param [String] source Haml template source.
8
17
  # @return [Array]
9
18
  def call(source)
@@ -25,6 +34,20 @@ module Hamli
25
34
  parse_tag_line
26
35
  elsif @scanner.match?(/[#.]/)
27
36
  parse_div_line
37
+ elsif @scanner.match?(%r{/\[})
38
+ parse_html_conditional_comment_line
39
+ elsif @scanner.match?(%r{/})
40
+ parse_html_comment_line
41
+ elsif @scanner.match?(/-#/)
42
+ parse_haml_comment_line
43
+ elsif @scanner.match?(/-/)
44
+ parse_control_line
45
+ elsif @scanner.match?(/=/)
46
+ parse_output_line
47
+ elsif @scanner.match?(/&=/)
48
+ parse_escaped_output_line
49
+ elsif @scanner.match?(/!=/)
50
+ parse_non_escaped_output_line
28
51
  else
29
52
  parse_text_line
30
53
  end
@@ -110,10 +133,19 @@ module Hamli
110
133
  content = [:multi]
111
134
  tag << content
112
135
  @stacks << content
113
- elsif @scanner.scan(/[ \t]*=([<>])*/)
114
- # TODO
136
+ elsif @scanner.scan(/[ \t]*([<>]*)=/)
137
+ # white_space_marker = @scanner[1]
138
+ # without_outer_white_space = white_space_marker.include?('>') # TODO
139
+ # without_inner_white_space = white_space_marker.include?('<') # TODO
140
+ @scanner.skip(/[ \t]+/)
141
+ begin_ = @scanner.charpos
142
+ content = parse_broken_lines
143
+ end_ = @scanner.charpos
144
+ block = [:multi]
145
+ tag << [:hamli, :position, begin_, end_, [:hamli, :output, false, content, block]]
146
+ @stacks << block
115
147
  elsif @scanner.scan(%r{[ \t]*/[ \t]*})
116
- # TODO
148
+ # Does nothing.
117
149
  else
118
150
  @scanner.scan(/[ \t]+/)
119
151
  tag << [:hamli, :text, parse_text_block]
@@ -274,7 +306,7 @@ module Hamli
274
306
  )
275
307
  /x
276
308
  )
277
- [:hamli, :object_reference, begin_, @scanner.charpos, value]
309
+ [:hamli, :object_reference, [:hamli, :position, begin_, @scanner.charpos, value]]
278
310
  end
279
311
 
280
312
  # Parse ruby attributes part.
@@ -292,7 +324,51 @@ module Hamli
292
324
  )
293
325
  /x
294
326
  )
295
- [:hamli, :ruby_attributes, begin_, @scanner.charpos, value]
327
+ [:hamli, :ruby_attributes, [:hamli, :position, begin_, @scanner.charpos, value]]
328
+ end
329
+
330
+ # Parse HTML comment part.
331
+ # e.g. /[if IE]
332
+ # ^^^^^^^^
333
+ def parse_html_conditional_comment_line
334
+ @scanner.pos += 1
335
+ condition = @scanner.scan(
336
+ /
337
+ (?<brackets>
338
+ \[
339
+ (?:[^\[\]] | \g<brackets>)*
340
+ \]
341
+ )
342
+ /x
343
+ )
344
+ block = [:multi]
345
+ @stacks.last << [:html, :condcomment, condition, block]
346
+ @stacks << block
347
+ parse_line_ending
348
+ end
349
+
350
+ # Parse HTML comment part.
351
+ # e.g. / abc
352
+ # ^^^^^
353
+ def parse_html_comment_line
354
+ @scanner.pos += 1
355
+ block = [:multi]
356
+ block << [:static, @scanner.scan(/[^\r\n]*/)]
357
+ @stacks.last << [:html, :comment, block]
358
+ @stacks << block
359
+ parse_line_ending
360
+ end
361
+
362
+ # Parse Haml comment part.
363
+ # e.g. -# abc
364
+ # ^^^^^^
365
+ def parse_haml_comment_line
366
+ @scanner.pos += 2
367
+ @scanner.scan(/[^\r\n]*/)
368
+ while !@scanner.eos? && (@scanner.match?(/[ \t]*(?=\r|$)/) || peek_indent > @indents.last)
369
+ @scanner.scan(/[^\r\n]*/)
370
+ parse_line_ending
371
+ end
296
372
  end
297
373
 
298
374
  # Parse text block part.
@@ -325,7 +401,7 @@ module Hamli
325
401
 
326
402
  # @return [Array, nil]
327
403
  def parse_interpolate_line
328
- return unless @scanner.match?(/[^\r\n]/)
404
+ return unless @scanner.match?(/[^\r\n]+/)
329
405
 
330
406
  begin_ = @scanner.charpos
331
407
  value = @scanner.matched
@@ -334,6 +410,70 @@ module Hamli
334
410
  [:hamli, :interpolate, begin_, end_, value]
335
411
  end
336
412
 
413
+ # Parse escaped output line part.
414
+ # e.g. != abc
415
+ # ^^^^^^
416
+ # @todo Support :escape_html option on this parser, then rethink about escaping.
417
+ def parse_non_escaped_output_line
418
+ @scanner.pos += 1
419
+ parse_output_line
420
+ end
421
+
422
+ # Parse escaped output line part.
423
+ # e.g. &= abc
424
+ # ^^^^^^
425
+ def parse_escaped_output_line
426
+ @scanner.pos += 2
427
+ @scanner.scan(/[ \t]*/)
428
+ parse_ruby_line(escaped: true, name: :output)
429
+ end
430
+
431
+ # Parse output line part.
432
+ # e.g. = abc
433
+ # ^^^^^
434
+ def parse_output_line
435
+ @scanner.pos += 1
436
+ @scanner.scan(/[ \t]*/)
437
+ parse_ruby_line(escaped: false, name: :output)
438
+ end
439
+
440
+ # Parse control line part.
441
+ # e.g. - abc
442
+ # ^^^^^
443
+ def parse_control_line
444
+ @scanner.pos += 1
445
+ @scanner.scan(/[ \t]*/)
446
+ parse_ruby_line(escaped: false, name: :control)
447
+ end
448
+
449
+ # @param [Boolean] escaped
450
+ # @param [Symbol] name
451
+ def parse_ruby_line(escaped:, name:)
452
+ @scanner.scan(/[ \t]*/)
453
+ block = [:multi]
454
+ begin_ = @scanner.charpos
455
+ content = parse_broken_lines
456
+ end_ = @scanner.charpos
457
+ @stacks.last << [:hamli, :position, begin_, end_, [:hamli, name, escaped, content, block]]
458
+ @stacks << block
459
+ end
460
+
461
+ # @note Broken line means line-breaked lines, separated by trailing "," or "|".
462
+ # @return [String]
463
+ def parse_broken_lines
464
+ result = +''
465
+ result << @scanner.scan(/[^\r\n]*/)
466
+ while result.end_with?(',') || result.end_with?('|')
467
+ syntax_error!(Errors::UnexpectedEosError) unless @scanner.scan(/\r?\n/)
468
+
469
+ result << "\n"
470
+ result << @scanner.scan(/[^\r\n]*/)
471
+ end
472
+ lines = result.lines
473
+ result.gsub!(/\|$/, '') if lines.length >= 2 && lines.all? { |line| line.end_with?("|\n") }
474
+ result.delete_suffix("\n")
475
+ end
476
+
337
477
  # @param [Class] syntax_error_class A child class of Hamli::Errors::HamlSyntaxError.
338
478
  # @raise [Hamli::Errors::HamlSyntaxError]
339
479
  def syntax_error!(syntax_error_class)
@@ -348,7 +488,7 @@ module Hamli
348
488
 
349
489
  # @return [Integer]
350
490
  def indent_from_last_match
351
- @scanner.matched.chars.map do |char|
491
+ (@scanner.matched || '').chars.map do |char|
352
492
  case char
353
493
  when "\t"
354
494
  4
@@ -364,5 +504,11 @@ module Hamli
364
504
  def expecting_indentation?
365
505
  @stacks.length > @indents.length
366
506
  end
507
+
508
+ # @return [Integer] Indent level.
509
+ def peek_indent
510
+ @scanner.match?(/[ \t]*/)
511
+ indent_from_last_match
512
+ end
367
513
  end
368
514
  end
data/lib/hamli/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hamli
4
- VERSION = '0.1.0'
4
+ VERSION = '0.5.0'
5
5
  end
data/lib/hamli.rb CHANGED
@@ -4,6 +4,7 @@ require_relative 'hamli/version'
4
4
 
5
5
  module Hamli
6
6
  autoload :Errors, 'hamli/errors'
7
+ autoload :Filters, 'hamli/filters'
7
8
  autoload :Parser, 'hamli/parser'
8
9
  autoload :Range, 'hamli/range'
9
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hamli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-25 00:00:00.000000000 Z
11
+ date: 2022-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: temple
@@ -43,6 +43,9 @@ files:
43
43
  - hamli.gemspec
44
44
  - lib/hamli.rb
45
45
  - lib/hamli/errors.rb
46
+ - lib/hamli/filters.rb
47
+ - lib/hamli/filters/base.rb
48
+ - lib/hamli/filters/interpolation.rb
46
49
  - lib/hamli/parser.rb
47
50
  - lib/hamli/range.rb
48
51
  - lib/hamli/version.rb