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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/COPYING.LIB +504 -0
- data/README.md +123 -0
- data/lib/haparanda/compiler.rb +29 -0
- data/lib/haparanda/content_combiner.rb +48 -0
- data/lib/haparanda/handlebars_compiler.rb +18 -0
- data/lib/haparanda/handlebars_lexer.rb +297 -0
- data/lib/haparanda/handlebars_lexer.rex +135 -0
- data/lib/haparanda/handlebars_parser.output +1691 -0
- data/lib/haparanda/handlebars_parser.rb +880 -0
- data/lib/haparanda/handlebars_parser.y +358 -0
- data/lib/haparanda/handlebars_processor.rb +347 -0
- data/lib/haparanda/template.rb +18 -0
- data/lib/haparanda/version.rb +6 -0
- data/lib/haparanda/whitespace_handler.rb +168 -0
- data/lib/haparanda.rb +15 -0
- metadata +94 -0
@@ -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: []
|