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.
- data/README.md +18 -8
- data/lib/lexr.rb +149 -144
- 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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
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
|
-
|
19
|
-
|
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
|
-
|
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
|
-
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
33
22
|
type: :development
|
34
|
-
|
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
|
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
|
-
|
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
|
-
|
62
|
-
segments:
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
segments:
|
63
54
|
- 0
|
64
|
-
|
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
|
-
|
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.
|
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
|
-
|