trenni 1.1.0 → 1.2.0

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