trenni 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|