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.
@@ -22,58 +22,73 @@
22
22
  require 'strscan'
23
23
 
24
24
  module Trenni
25
- # This scanner processes general markup into a sequence of events which are passed to a delegate.
26
- class Scanner
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
- class ScanError < StandardError
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
- @pos = scanner.pos
35
- @offset = calculate_line_number(scanner)
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
- "Scan Error: #{@message} @ [#{@offset[0]}:#{@offset[2]}]: #{@offset[4]}"
69
+ "Parse Error: #{@message} @ [#{@line[0]}:#{@line[2]}]: #{@line[4]}"
43
70
  else
44
- "Scan Error [#{@pos}]: #{@message}"
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 ScanError.new("Scanner didn't move", scanner)
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 ScanError.new("Tag cannot be closed at both ends!", scanner)
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 ScanError.new("Invalid characters in tag!", scanner)
159
+ raise ParseError.new("Invalid characters in tag!", scanner)
145
160
  end
146
161
  else
147
- raise ScanError.new("Invalid tag!", scanner)
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 ScanError.new("CDATA segment is not closed!", scanner)
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 ScanError.new("Comment is not closed!", scanner)
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 ScanError.new("Comment is not closed!", scanner)
185
+ raise ParseError.new("Comment is not closed!", scanner)
171
186
  end
172
187
  end
173
188
  end
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Trenni
22
- VERSION = "1.1.0"
22
+ VERSION = "1.2.0"
23
23
  end
@@ -25,10 +25,10 @@ require 'test/unit'
25
25
  require 'stringio'
26
26
  require 'digest/md5'
27
27
 
28
- require 'trenni/scanner'
28
+ require 'trenni/parser'
29
29
 
30
- class TestScanner < Test::Unit::TestCase
31
- class ScannerDelegate
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 = ScannerDelegate.new
45
- scanner = Trenni::Scanner.new(delegate)
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 = ScannerDelegate.new
64
- scanner = Trenni::Scanner.new(delegate)
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 = ScannerDelegate.new
81
- scanner = Trenni::Scanner.new(delegate)
84
+ delegate = ParserDelegate.new
85
+ scanner = Trenni::Parser.new(delegate)
82
86
 
83
- assert_raise Trenni::Scanner::ScanError do
87
+ assert_raise Trenni::Parser::ParseError do
84
88
  scanner.parse(%Q{<foo})
85
89
  end
86
90
 
87
- assert_raise Trenni::Scanner::ScanError do
91
+ assert_raise Trenni::Parser::ParseError do
88
92
  scanner.parse(%Q{<foo bar=>})
89
93
  end
90
94
 
91
- assert_nothing_raised Trenni::Scanner::ScanError do
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
- hash: 19
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
- description: "\tTrenni is a templating system that evaluates textual strings containing Ruby\n\
22
- \tcode. It compiles templates directly into native code which means that you\n\
23
- \tgenerally get the best possible performance.\n\n\
24
- \tIn addition, Trenni includes an SGML/XML builder to assist with the generation\n\
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/scanner.rb
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/test_scanner.rb
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
- hash: 3
66
- segments:
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ segments:
67
54
  - 0
68
- version: "0"
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
- hash: 3
75
- segments:
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ segments:
76
63
  - 0
77
- version: "0"
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/test_scanner.rb
73
+ - test/test_parser.rb
88
74
  - test/test_strings.rb
89
75
  - test/test_template.rb