hamli 0.1.0 → 0.5.0

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: 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