trenni 1.1.0 → 1.2.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.
- data/lib/trenni/{scanner.rb → parser.rb} +51 -36
- data/lib/trenni/version.rb +1 -1
- data/test/{test_scanner.rb → test_parser.rb} +27 -12
- metadata +30 -44
@@ -22,58 +22,73 @@
|
|
22
22
|
require 'strscan'
|
23
23
|
|
24
24
|
module Trenni
|
25
|
-
# This
|
26
|
-
class
|
25
|
+
# This parser processes general markup into a sequence of events which are passed to a delegate.
|
26
|
+
class Parser
|
27
27
|
OPENED_TAG = :opened
|
28
28
|
CLOSED_TAG = :closed
|
29
29
|
|
30
|
-
|
30
|
+
def self.line_at_offset(input, input_offset)
|
31
|
+
line_number = 1
|
32
|
+
line_offset = offset = 0
|
33
|
+
|
34
|
+
input.each_line do |line|
|
35
|
+
line_offset = offset
|
36
|
+
offset += line.size
|
37
|
+
|
38
|
+
if offset >= input_offset
|
39
|
+
return {
|
40
|
+
# The line that contains the input_offset:
|
41
|
+
:line_number => line_number,
|
42
|
+
# The offset to the start of that line:
|
43
|
+
:line_offset => line_offset,
|
44
|
+
# The number of characters from the start of the line to the input_offset:
|
45
|
+
:character_offset => input_offset - line_offset,
|
46
|
+
# The line of text itself:
|
47
|
+
:text => line.chomp
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
line_number += 1
|
52
|
+
end
|
53
|
+
|
54
|
+
return nil
|
55
|
+
end
|
56
|
+
|
57
|
+
class ParseError < StandardError
|
31
58
|
def initialize(message, scanner)
|
32
59
|
@message = message
|
33
60
|
|
34
|
-
@
|
35
|
-
@
|
61
|
+
@position = scanner.pos
|
62
|
+
@line = Parser.line_at_offset(scanner.string, @position)
|
36
63
|
end
|
37
64
|
|
38
65
|
attr :offset
|
39
66
|
|
40
67
|
def to_s
|
41
68
|
if @offset
|
42
|
-
"
|
69
|
+
"Parse Error: #{@message} @ [#{@line[0]}:#{@line[2]}]: #{@line[4]}"
|
43
70
|
else
|
44
|
-
"
|
71
|
+
"Parse Error [#{@position}]: #{@message}"
|
45
72
|
end
|
46
73
|
end
|
47
|
-
|
48
|
-
protected
|
49
|
-
|
50
|
-
def calculate_line_number(scanner)
|
51
|
-
at = scanner.pos
|
52
|
-
|
53
|
-
line_no = 1
|
54
|
-
line_offset = offset = 0
|
55
|
-
|
56
|
-
scanner.string.lines.each do |line|
|
57
|
-
line_offset = offset
|
58
|
-
offset += line.size
|
59
|
-
|
60
|
-
if offset >= at
|
61
|
-
return [line_no, line_offset, at - line_offset, offset, line]
|
62
|
-
end
|
63
|
-
|
64
|
-
line_no += 1
|
65
|
-
end
|
66
|
-
|
67
|
-
return nil
|
68
|
-
end
|
69
74
|
end
|
70
75
|
|
71
76
|
def initialize(delegate)
|
72
77
|
@delegate = delegate
|
78
|
+
# The delegate must respond to:
|
79
|
+
# .begin_parse(scanner)
|
80
|
+
# .text(escaped-data)
|
81
|
+
# .cdata(unescaped-data)
|
82
|
+
# .attribute(name, value-or-true)
|
83
|
+
# .begin_tag(name, :opened or :closed)
|
84
|
+
# .end_tag(begin_tag_type, :opened or :closed)
|
85
|
+
# .comment(comment-text)
|
86
|
+
# .instruction(instruction-text)
|
73
87
|
end
|
74
88
|
|
75
89
|
def parse(string)
|
76
90
|
scanner = StringScanner.new(string)
|
91
|
+
@delegate.begin_parse(scanner)
|
77
92
|
|
78
93
|
until scanner.eos?
|
79
94
|
start_pos = scanner.pos
|
@@ -82,7 +97,7 @@ module Trenni
|
|
82
97
|
scan_tag(scanner)
|
83
98
|
|
84
99
|
if start_pos == scanner.pos
|
85
|
-
raise
|
100
|
+
raise ParseError.new("Scanner didn't move", scanner)
|
86
101
|
end
|
87
102
|
end
|
88
103
|
end
|
@@ -134,17 +149,17 @@ module Trenni
|
|
134
149
|
|
135
150
|
if scanner.scan(/\/>/)
|
136
151
|
if begin_tag_type == CLOSED_TAG
|
137
|
-
raise
|
152
|
+
raise ParseError.new("Tag cannot be closed at both ends!", scanner)
|
138
153
|
else
|
139
154
|
@delegate.finish_tag(begin_tag_type, CLOSED_TAG)
|
140
155
|
end
|
141
156
|
elsif scanner.scan(/>/)
|
142
157
|
@delegate.finish_tag(begin_tag_type, OPENED_TAG)
|
143
158
|
else
|
144
|
-
raise
|
159
|
+
raise ParseError.new("Invalid characters in tag!", scanner)
|
145
160
|
end
|
146
161
|
else
|
147
|
-
raise
|
162
|
+
raise ParseError.new("Invalid tag!", scanner)
|
148
163
|
end
|
149
164
|
end
|
150
165
|
|
@@ -152,7 +167,7 @@ module Trenni
|
|
152
167
|
if scanner.scan_until(/(.*?)\]\]>/m)
|
153
168
|
@delegate.cdata(scanner[1])
|
154
169
|
else
|
155
|
-
raise
|
170
|
+
raise ParseError.new("CDATA segment is not closed!", scanner)
|
156
171
|
end
|
157
172
|
end
|
158
173
|
|
@@ -161,13 +176,13 @@ module Trenni
|
|
161
176
|
if scanner.scan_until(/(.*?)-->/m)
|
162
177
|
@delegate.comment("--" + scanner[1] + "--")
|
163
178
|
else
|
164
|
-
raise
|
179
|
+
raise ParseError.new("Comment is not closed!", scanner)
|
165
180
|
end
|
166
181
|
else
|
167
182
|
if scanner.scan_until(/(.*?)>/)
|
168
183
|
@delegate.comment(scanner[1])
|
169
184
|
else
|
170
|
-
raise
|
185
|
+
raise ParseError.new("Comment is not closed!", scanner)
|
171
186
|
end
|
172
187
|
end
|
173
188
|
end
|
data/lib/trenni/version.rb
CHANGED
@@ -25,10 +25,10 @@ require 'test/unit'
|
|
25
25
|
require 'stringio'
|
26
26
|
require 'digest/md5'
|
27
27
|
|
28
|
-
require 'trenni/
|
28
|
+
require 'trenni/parser'
|
29
29
|
|
30
|
-
class
|
31
|
-
class
|
30
|
+
class TestParser < Test::Unit::TestCase
|
31
|
+
class ParserDelegate
|
32
32
|
def initialize
|
33
33
|
@events = []
|
34
34
|
end
|
@@ -38,11 +38,15 @@ class TestScanner < Test::Unit::TestCase
|
|
38
38
|
def method_missing(*args)
|
39
39
|
@events << args
|
40
40
|
end
|
41
|
+
|
42
|
+
def begin_parse(scanner)
|
43
|
+
# ignore this event
|
44
|
+
end
|
41
45
|
end
|
42
46
|
|
43
47
|
def test_markup
|
44
|
-
delegate =
|
45
|
-
scanner = Trenni::
|
48
|
+
delegate = ParserDelegate.new
|
49
|
+
scanner = Trenni::Parser.new(delegate)
|
46
50
|
|
47
51
|
scanner.parse(%Q{<foo bar="20" baz>Hello World</foo>})
|
48
52
|
|
@@ -60,8 +64,8 @@ class TestScanner < Test::Unit::TestCase
|
|
60
64
|
end
|
61
65
|
|
62
66
|
def test_cdata
|
63
|
-
delegate =
|
64
|
-
scanner = Trenni::
|
67
|
+
delegate = ParserDelegate.new
|
68
|
+
scanner = Trenni::Parser.new(delegate)
|
65
69
|
|
66
70
|
scanner.parse(%Q{<test><![CDATA[Hello World]]></test>})
|
67
71
|
|
@@ -77,19 +81,30 @@ class TestScanner < Test::Unit::TestCase
|
|
77
81
|
end
|
78
82
|
|
79
83
|
def test_errors
|
80
|
-
delegate =
|
81
|
-
scanner = Trenni::
|
84
|
+
delegate = ParserDelegate.new
|
85
|
+
scanner = Trenni::Parser.new(delegate)
|
82
86
|
|
83
|
-
assert_raise Trenni::
|
87
|
+
assert_raise Trenni::Parser::ParseError do
|
84
88
|
scanner.parse(%Q{<foo})
|
85
89
|
end
|
86
90
|
|
87
|
-
assert_raise Trenni::
|
91
|
+
assert_raise Trenni::Parser::ParseError do
|
88
92
|
scanner.parse(%Q{<foo bar=>})
|
89
93
|
end
|
90
94
|
|
91
|
-
assert_nothing_raised Trenni::
|
95
|
+
assert_nothing_raised Trenni::Parser::ParseError do
|
92
96
|
scanner.parse(%Q{<foo bar="" baz>})
|
93
97
|
end
|
94
98
|
end
|
99
|
+
|
100
|
+
def test_line_at_offset
|
101
|
+
data = %Q{Hello\nWorld\nFoo\nBar!}
|
102
|
+
|
103
|
+
line = Trenni::Parser.line_at_offset(data, 7)
|
104
|
+
|
105
|
+
assert_equal "World", line[:text]
|
106
|
+
assert_equal 2, line[:line_number]
|
107
|
+
assert_equal 6, line[:line_offset]
|
108
|
+
assert_equal 1, line[:character_offset]
|
109
|
+
end
|
95
110
|
end
|
metadata
CHANGED
@@ -1,37 +1,26 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: trenni
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.2.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 1
|
9
|
-
- 0
|
10
|
-
version: 1.1.0
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Samuel Williams
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2012-10-26 00:00:00 Z
|
12
|
+
date: 2012-10-26 00:00:00.000000000 Z
|
19
13
|
dependencies: []
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
\tof pleasantly formatted markup.\n"
|
26
|
-
email:
|
14
|
+
description: ! "\tTrenni is a templating system that evaluates textual strings containing
|
15
|
+
Ruby\n\tcode. It compiles templates directly into native code which means that you\n\tgenerally
|
16
|
+
get the best possible performance.\n\n\tIn addition, Trenni includes an SGML/XML
|
17
|
+
builder to assist with the generation\n\tof pleasantly formatted markup.\n"
|
18
|
+
email:
|
27
19
|
- samuel.williams@oriontransfer.co.nz
|
28
20
|
executables: []
|
29
|
-
|
30
21
|
extensions: []
|
31
|
-
|
32
22
|
extra_rdoc_files: []
|
33
|
-
|
34
|
-
files:
|
23
|
+
files:
|
35
24
|
- .gitignore
|
36
25
|
- .travis.yml
|
37
26
|
- Gemfile
|
@@ -40,50 +29,47 @@ files:
|
|
40
29
|
- lib/trenni.rb
|
41
30
|
- lib/trenni/builder.rb
|
42
31
|
- lib/trenni/extensions/symbol-1.8.7.rb
|
43
|
-
- lib/trenni/
|
32
|
+
- lib/trenni/parser.rb
|
44
33
|
- lib/trenni/strings.rb
|
45
34
|
- lib/trenni/template.rb
|
46
35
|
- lib/trenni/version.rb
|
47
36
|
- test/test_builder.rb
|
48
|
-
- test/
|
37
|
+
- test/test_parser.rb
|
49
38
|
- test/test_strings.rb
|
50
39
|
- test/test_template.rb
|
51
40
|
- trenni.gemspec
|
52
41
|
homepage: https://github.com/ioquatix/trenni
|
53
42
|
licenses: []
|
54
|
-
|
55
43
|
post_install_message:
|
56
44
|
rdoc_options: []
|
57
|
-
|
58
|
-
require_paths:
|
45
|
+
require_paths:
|
59
46
|
- lib
|
60
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
48
|
none: false
|
62
|
-
requirements:
|
63
|
-
- -
|
64
|
-
- !ruby/object:Gem::Version
|
65
|
-
|
66
|
-
segments:
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
segments:
|
67
54
|
- 0
|
68
|
-
|
69
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
hash: -514891289960522672
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
57
|
none: false
|
71
|
-
requirements:
|
72
|
-
- -
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
|
75
|
-
segments:
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
segments:
|
76
63
|
- 0
|
77
|
-
|
64
|
+
hash: -514891289960522672
|
78
65
|
requirements: []
|
79
|
-
|
80
66
|
rubyforge_project:
|
81
67
|
rubygems_version: 1.8.24
|
82
68
|
signing_key:
|
83
69
|
specification_version: 3
|
84
70
|
summary: A fast native templating system that compiles directly to Ruby code.
|
85
|
-
test_files:
|
71
|
+
test_files:
|
86
72
|
- test/test_builder.rb
|
87
|
-
- test/
|
73
|
+
- test/test_parser.rb
|
88
74
|
- test/test_strings.rb
|
89
75
|
- test/test_template.rb
|