haparanda 0.0.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.
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sexp_processor"
4
+
5
+ module Haparanda
6
+ # Process the handlebars AST just to do the whitespace stripping.
7
+ class WhitespaceHandler < SexpProcessor # rubocop:disable Metrics/ClassLength
8
+ def initialize(ignore_standalone: false)
9
+ super()
10
+
11
+ @ignore_standalone = ignore_standalone
12
+
13
+ self.require_empty = false
14
+ end
15
+
16
+ def process_root(expr)
17
+ _, statements = expr
18
+
19
+ statements = process(statements)
20
+ item = statements.sexp_body[0]
21
+ if item.sexp_type == :block
22
+ content = item.dig(4, 2, 1)
23
+ clear_following_whitespace(content) if following_whitespace?(content)
24
+ end
25
+ s(:root, statements)
26
+ end
27
+
28
+ def process_block(expr)
29
+ _, name, params, hash, program, inverse_chain, open_strip, close_strip = expr
30
+
31
+ program = process(program)
32
+ if inverse_chain && inverse_chain.last.nil?
33
+ body = inverse_chain.sexp_body
34
+ body[-1] = close_strip
35
+ inverse_chain.sexp_body = body
36
+ end
37
+ inverse_chain = process(inverse_chain)
38
+
39
+ statements = program&.at(2)&.sexp_body
40
+ if statements
41
+ strip_initial_whitespace(statements.first, open_strip)
42
+ strip_final_whitespace(statements.last, close_strip)
43
+ end
44
+
45
+ if statements && inverse_chain
46
+ strip_standalone_whitespace(statements.last, first_item(inverse_chain))
47
+ end
48
+
49
+ s(:block, name, params, hash, program, inverse_chain, open_strip, close_strip)
50
+ end
51
+
52
+ def process_inverse(expr)
53
+ _, block_params, statements, open_strip, close_strip = expr
54
+
55
+ block_params = process(block_params)
56
+ statements = process(statements)
57
+
58
+ case statements.sexp_type
59
+ when :statements
60
+ if (items = statements&.sexp_body)
61
+ strip_initial_whitespace(items.first, open_strip)
62
+ strip_final_whitespace(items.last, close_strip)
63
+ end
64
+ end
65
+ # TODO: Handle :block sexp_type
66
+
67
+ s(:inverse, block_params, statements, open_strip, close_strip)
68
+ end
69
+
70
+ def process_statements(expr)
71
+ statements = expr.sexp_body
72
+
73
+ statements.each_cons(2) do |prev, item|
74
+ strip_final_whitespace(prev, open_strip_for(item)) if item.sexp_type != :content
75
+ strip_initial_whitespace(item, close_strip_for(prev)) if prev.sexp_type != :content
76
+
77
+ strip_standalone_whitespace(prev, item.dig(4, 2, 1)) if item.sexp_type == :block
78
+ strip_standalone_whitespace(last_item(prev), item) if prev.sexp_type == :block
79
+ end
80
+ statements = statements.map { process(_1) }
81
+
82
+ s(:statements, *statements)
83
+ end
84
+
85
+ private
86
+
87
+ def first_item(container)
88
+ case container.sexp_type
89
+ when :statements
90
+ container.sexp_body.first
91
+ when :block
92
+ container.dig(4, 2, 1)
93
+ when :inverse
94
+ first_item container[2]
95
+ else
96
+ raise NotImplementedError
97
+ end
98
+ end
99
+
100
+ def last_item(container)
101
+ return if container.nil?
102
+
103
+ case container.sexp_type
104
+ when :block
105
+ last_item(container[5] || container[4])
106
+ when :statements
107
+ container.sexp_body.last
108
+ when :inverse, :program
109
+ last_item container[2]
110
+ when :content
111
+ container
112
+ else
113
+ raise NotImplementedError
114
+ end
115
+ end
116
+
117
+ def strip_initial_whitespace(item, strip)
118
+ item[1] = item[1].sub(/^\s*/, "") if item.sexp_type == :content && strip[2]
119
+ end
120
+
121
+ def strip_final_whitespace(item, strip)
122
+ item[1] = item[1].sub(/\s*$/, "") if item.sexp_type == :content && strip&.at(1)
123
+ end
124
+
125
+ def strip_standalone_whitespace(before, after)
126
+ return unless preceding_whitespace? before
127
+ return unless following_whitespace? after
128
+
129
+ clear_preceding_whitespace(before)
130
+ clear_following_whitespace(after)
131
+ end
132
+
133
+ def preceding_whitespace?(before)
134
+ before&.sexp_type == :content && before[1] =~ /\n\s*$/
135
+ end
136
+
137
+ def following_whitespace?(after)
138
+ after&.sexp_type == :content && after[1] =~ /^\s*\n/
139
+ end
140
+
141
+ # Strip trailing whitespace before but leave the \n
142
+ def clear_preceding_whitespace(before)
143
+ return if @ignore_standalone
144
+
145
+ before[1] = before[1].sub(/\n[ \t]+$/, "\n")
146
+ end
147
+
148
+ # Strip leading whitespace after including the \n
149
+ def clear_following_whitespace(after)
150
+ return if @ignore_standalone
151
+
152
+ after[1] = after[1].sub(/^[ \t]*\n/, "")
153
+ end
154
+
155
+ def open_strip_for(item)
156
+ case item.sexp_type
157
+ when :block
158
+ item.at(-2)
159
+ else
160
+ item.last
161
+ end
162
+ end
163
+
164
+ def close_strip_for(item)
165
+ item.last
166
+ end
167
+ end
168
+ end
data/lib/haparanda.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # All code in this gem lives in the Haparanda module
4
+ module Haparanda
5
+ # Empty for now
6
+ end
7
+
8
+ require_relative "haparanda/version"
9
+
10
+ require_relative "haparanda/handlebars_lexer"
11
+ require_relative "haparanda/handlebars_parser"
12
+ require_relative "haparanda/handlebars_compiler"
13
+ require_relative "haparanda/handlebars_processor"
14
+
15
+ require_relative "haparanda/compiler"
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: haparanda
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matijs van Zuijlen
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: racc
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.8'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.8'
26
+ - !ruby/object:Gem::Dependency
27
+ name: sexp_processor
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '4.17'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '4.17'
40
+ description: |
41
+ Haparanda aims to be a fast implementation of all of Handlebars written in
42
+ Ruby. The lexer and parser track the upstream .l and .y files.
43
+ email:
44
+ - matijs@matijs.net
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files:
48
+ - CHANGELOG.md
49
+ - README.md
50
+ files:
51
+ - CHANGELOG.md
52
+ - COPYING.LIB
53
+ - README.md
54
+ - lib/haparanda.rb
55
+ - lib/haparanda/compiler.rb
56
+ - lib/haparanda/content_combiner.rb
57
+ - lib/haparanda/handlebars_compiler.rb
58
+ - lib/haparanda/handlebars_lexer.rb
59
+ - lib/haparanda/handlebars_lexer.rex
60
+ - lib/haparanda/handlebars_parser.output
61
+ - lib/haparanda/handlebars_parser.rb
62
+ - lib/haparanda/handlebars_parser.y
63
+ - lib/haparanda/handlebars_processor.rb
64
+ - lib/haparanda/template.rb
65
+ - lib/haparanda/version.rb
66
+ - lib/haparanda/whitespace_handler.rb
67
+ homepage: https://github.com/mvz/haparanda
68
+ licenses:
69
+ - LGPL-2.1-or-later
70
+ metadata:
71
+ homepage_uri: https://github.com/mvz/haparanda
72
+ source_code_uri: https://github.com/mvz/haparanda
73
+ changelog_uri: https://github.com/mvz/haparanda/blob/master/CHANGELOG.md
74
+ rubygems_mfa_required: 'true'
75
+ rdoc_options:
76
+ - "--main"
77
+ - README.md
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 3.2.0
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.6.9
92
+ specification_version: 4
93
+ summary: Pure Ruby Handlebars Parser
94
+ test_files: []