lexr 0.2.3 → 0.3.1

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