lexr 0.2.3 → 0.3.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.
Files changed (3) hide show
  1. data/README.md +18 -8
  2. data/lib/lexr.rb +149 -144
  3. metadata +38 -52
data/README.md CHANGED
@@ -13,16 +13,26 @@ Install with
13
13
 
14
14
  ExpressionLexer = Lexr.that {
15
15
  ignores /\s+/ => :whitespace
16
- matches /[-+]?[0-9]*\.?[0-9]+/ => :number, :convert_with => lambda { |v| Float(v) }
17
- matches "+" => :addition
18
- matches "-" => :subtraction
19
- matches "*" => :multiplication
20
- matches "/" => :division
16
+
17
+ legal_place_for_binary_operator = lambda { |prev| [:addition,
18
+ :subtraction,
19
+ :multiplication,
20
+ :division,
21
+ :left_parenthesis,
22
+ :start].include? prev.type }
23
+
24
+ matches "+" => :addition, :unless => legal_place_for_binary_operator
25
+ matches "-" => :subtraction, :unless => legal_place_for_binary_operator
26
+ matches "*" => :multiplication, :unless => legal_place_for_binary_operator
27
+ matches "/" => :division, :unless => legal_place_for_binary_operator
28
+
21
29
  matches "(" => :left_parenthesis
22
30
  matches ")" => :right_parenthesis
31
+
32
+ matches /[-+]?[0-9]*\.?[0-9]+/ => :number, :convert_with => lambda { |v| Float(v) }
23
33
  }
24
34
 
25
- lexer = ExpressionLexer.new("1 * 12.5 / (55 + 2 - 56)")
35
+ lexer = ExpressionLexer.new("-1 * 12.5 / (55 + 2 - -56)")
26
36
 
27
37
  until lexer.end?
28
38
  puts lexer.next
@@ -30,7 +40,7 @@ Install with
30
40
 
31
41
  results in an output of
32
42
 
33
- number(1.0)
43
+ number(-1.0)
34
44
  multiplication(*)
35
45
  number(12.5)
36
46
  division(/)
@@ -39,7 +49,7 @@ results in an output of
39
49
  addition(+)
40
50
  number(2.0)
41
51
  subtraction(-)
42
- number(56.0)
52
+ number(-56.0)
43
53
  right_parenthesis())
44
54
  end()
45
55
 
data/lib/lexr.rb CHANGED
@@ -1,150 +1,155 @@
1
1
  class Lexr
2
- def self.that(&block)
3
- dsl = Lexr::Dsl.new
4
- block.arity == 1 ? block[dsl] : dsl.instance_eval(&block)
5
- dsl
6
- end
7
-
8
- def initialize(text, rules)
9
- @text, @rules = text, rules
10
- @current = nil
11
- @position = 0
12
- end
2
+ def self.that(&block)
3
+ dsl = Lexr::Dsl.new
4
+ block.arity == 1 ? block[dsl] : dsl.instance_eval(&block)
5
+ dsl
6
+ end
7
+
8
+ def initialize(text, rules)
9
+ @text, @rules = text, rules
10
+ @current = Lexr::Token.start
11
+ @position = 0
12
+ end
13
13
 
14
- def next
15
- return @current = Lexr::Token.end if @position >= @text.length
16
- @rules.each do |rule|
17
- next unless result = rule.match(unprocessed_text)
18
- @position += result.characters_matched
19
- return self.next if rule.ignore?
20
- return @current = result.token
21
- end
22
- raise Lexr::UnmatchableTextError.new(unprocessed_text[0..0], @position)
23
- end
24
-
25
- def peek
26
- pos = @position
27
- cur = @current
28
- result = self.send :next
29
- @position = pos
30
- @current = cur
31
- result
32
- end
33
-
34
- def current
35
- @current
36
- end
37
-
38
- def end?
39
- @current == Lexr::Token.end
40
- end
41
-
42
- private
43
-
44
- def unprocessed_text
45
- @text[@position..-1]
46
- end
47
-
48
- class Token
49
- attr_reader :value, :type
50
-
51
- def initialize(value, type = nil)
52
- @value, @type = value, type
53
- end
54
-
55
- def self.method_missing(sym, *args)
56
- self.new(args.first, sym)
57
- end
58
-
59
- def to_s
60
- "#{type}(#{value})"
61
- end
62
-
63
- def ==(other)
64
- @type == other.type && @value == other.value
65
- end
66
- end
67
-
68
- class Rule
69
- attr_reader :pattern, :symbol
70
-
71
- def converter ; @opts[:convert_with] ; end
72
- def ignore? ; @opts[:ignore] ; end
73
-
74
- def initialize(pattern, symbol, opts = {})
75
- @pattern, @symbol, @opts = pattern, symbol, opts
76
- end
77
-
78
- def match(text)
79
- text_matched = self.send :"#{pattern.class.name.downcase}_matcher", text
80
- return nil unless text_matched
81
- value = converter ? converter[text_matched] : text_matched
82
- Lexr::MatchData.new(text_matched.length, Lexr::Token.new(value, symbol))
83
- end
84
-
85
- def ==(other)
86
- @pattern == other.pattern &&
87
- @symbol == other.symbol &&
88
- @opts[:convert_with] == other.converter &&
89
- @opts[:ignore] == other.ignore?
90
- end
91
-
92
- private
93
-
94
- def string_matcher(text)
95
- return nil unless text[0..pattern.length-1] == pattern
14
+ def next
15
+ return @current = Lexr::Token.end if @position >= @text.length
16
+ @rules.each do |rule|
17
+ next unless result = rule.match(unprocessed_text, @current)
18
+ @position += result.characters_matched
19
+ return self.next if rule.ignore?
20
+ return @current = result.token
21
+ end
22
+ raise Lexr::UnmatchableTextError.new(unprocessed_text[0..0], @position)
23
+ end
24
+
25
+ def peek
26
+ pos = @position
27
+ cur = @current
28
+ result = self.send :next
29
+ @position = pos
30
+ @current = cur
31
+ result
32
+ end
33
+
34
+ def current
35
+ @current
36
+ end
37
+
38
+ def end?
39
+ @current == Lexr::Token.end
40
+ end
41
+
42
+ private
43
+
44
+ def unprocessed_text
45
+ @text[@position..-1]
46
+ end
47
+
48
+ class Token
49
+ attr_reader :value, :type
50
+
51
+ def initialize(value, type = nil)
52
+ @value, @type = value, type
53
+ end
54
+
55
+ def self.method_missing(sym, *args)
56
+ self.new(args.first, sym)
57
+ end
58
+
59
+ def to_s
60
+ "#{type}(#{value})"
61
+ end
62
+
63
+ def ==(other)
64
+ @type == other.type && @value == other.value
65
+ end
66
+ end
67
+
68
+ class Rule
69
+ attr_reader :pattern, :symbol
70
+
71
+ def converter ; @opts[:convert_with] ; end
72
+ def ignore? ; @opts[:ignore] ; end
73
+
74
+ def initialize(pattern, symbol, opts = {})
75
+ @pattern, @symbol, @opts = pattern, symbol, opts
76
+ end
77
+
78
+ def match(text, previous_token = nil)
79
+ text_matched = self.send :"#{pattern.class.name.downcase}_matcher", text
80
+ return nil unless text_matched
81
+ return nil if predecate && !predecate[previous_token]
82
+ value = converter ? converter[text_matched] : text_matched
83
+ Lexr::MatchData.new(text_matched.length, Lexr::Token.new(value, symbol))
84
+ end
85
+
86
+ def ==(other)
87
+ @pattern == other.pattern &&
88
+ @symbol == other.symbol &&
89
+ @opts[:convert_with] == other.converter &&
90
+ @opts[:ignore] == other.ignore?
91
+ end
92
+
93
+ private
94
+
95
+ def predecate
96
+ @opts[:if] || (@opts[:unless] ? lambda { |prev| ! @opts[:unless][prev] } : nil)
97
+ end
98
+
99
+ def string_matcher(text)
100
+ return nil unless text[0..pattern.length-1] == pattern
96
101
  pattern
97
- end
98
-
99
- def regexp_matcher(text)
100
- return nil unless m = text.match(/\A#{pattern}/)
101
- m[0]
102
- end
103
- end
104
-
105
- class Dsl
106
- def initialize
107
- @rules = []
108
- end
109
-
110
- def matches(rule_hash)
111
- pattern = rule_hash.keys.reject { |k| k.class == Symbol }.first
112
- symbol = rule_hash[pattern]
113
- opts = rule_hash.delete_if { |k, v| k.class != Symbol }
114
- @rules << Rule.new(pattern, symbol, opts)
115
- end
116
-
117
- def ignores(rule_hash)
118
- matches rule_hash.merge(:ignore => true)
119
- end
120
-
121
- def new(str)
122
- Lexr.new(str, @rules)
123
- end
124
- end
125
-
126
- class UnmatchableTextError < StandardError
127
- attr_reader :character, :position
128
-
129
- def initialize(character, position)
130
- @character, @position = character, position
131
- end
132
-
133
- def message
134
- "Unexpected character '#{character}' at position #{position + 1}"
135
- end
136
-
137
- def inspect
138
- message
139
- end
140
- end
141
-
142
- class MatchData
143
- attr_reader :characters_matched, :token
144
-
145
- def initialize(characters_matched, token)
146
- @characters_matched = characters_matched
147
- @token = token
102
+ end
103
+
104
+ def regexp_matcher(text)
105
+ return nil unless m = text.match(/\A#{pattern}/)
106
+ m[0]
107
+ end
108
+ end
109
+
110
+ class Dsl
111
+ def initialize
112
+ @rules = []
113
+ end
114
+
115
+ def matches(rule_hash)
116
+ pattern = rule_hash.keys.reject { |k| k.class == Symbol }.first
117
+ symbol = rule_hash[pattern]
118
+ opts = rule_hash.delete_if { |k, v| k.class != Symbol }
119
+ @rules << Rule.new(pattern, symbol, opts)
120
+ end
121
+
122
+ def ignores(rule_hash)
123
+ matches rule_hash.merge(:ignore => true)
124
+ end
125
+
126
+ def new(str)
127
+ Lexr.new(str, @rules)
128
+ end
129
+ end
130
+
131
+ class UnmatchableTextError < StandardError
132
+ attr_reader :character, :position
133
+
134
+ def initialize(character, position)
135
+ @character, @position = character, position
136
+ end
137
+
138
+ def message
139
+ "Unexpected character '#{character}' at position #{position + 1}"
140
+ end
141
+
142
+ def inspect
143
+ message
144
+ end
145
+ end
146
+
147
+ class MatchData
148
+ attr_reader :characters_matched, :token
149
+
150
+ def initialize(characters_matched, token)
151
+ @characters_matched = characters_matched
152
+ @token = token
148
153
  end
149
154
  end
150
155
  end
metadata CHANGED
@@ -1,82 +1,68 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: lexr
3
- version: !ruby/object:Gem::Version
4
- hash: 17
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 3
10
- version: 0.2.3
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Michael Baldry
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-05-17 00:00:00 +01:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2012-10-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: rspec
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 0
32
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
33
22
  type: :development
34
- version_requirements: *id001
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
35
30
  description:
36
- email: michael.baldry@uswitch.com
31
+ email: michael@brightbits.co.uk
37
32
  executables: []
38
-
39
33
  extensions: []
40
-
41
- extra_rdoc_files:
34
+ extra_rdoc_files:
42
35
  - README.md
43
- files:
36
+ files:
44
37
  - README.md
45
38
  - lib/lexr.rb
46
- has_rdoc: true
47
- homepage: http://www.forwardtechnology.co.uk
39
+ homepage: http://www.brightbits.co.uk
48
40
  licenses: []
49
-
50
41
  post_install_message:
51
- rdoc_options:
42
+ rdoc_options:
52
43
  - --main
53
44
  - README.md
54
- require_paths:
45
+ require_paths:
55
46
  - lib
56
- required_ruby_version: !ruby/object:Gem::Requirement
47
+ required_ruby_version: !ruby/object:Gem::Requirement
57
48
  none: false
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- hash: 3
62
- segments:
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ segments:
63
54
  - 0
64
- version: "0"
65
- required_rubygems_version: !ruby/object:Gem::Requirement
55
+ hash: -344489456942955657
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
57
  none: false
67
- requirements:
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- hash: 3
71
- segments:
72
- - 0
73
- version: "0"
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
74
62
  requirements: []
75
-
76
63
  rubyforge_project:
77
- rubygems_version: 1.6.2
64
+ rubygems_version: 1.8.24
78
65
  signing_key:
79
66
  specification_version: 3
80
67
  summary: A lightweight and pretty lexical analyser
81
68
  test_files: []
82
-