-
Notifications
You must be signed in to change notification settings - Fork 99
/
Copy pathlexical_analyzer.rb
56 lines (49 loc) · 1.77 KB
/
lexical_analyzer.rb
1
2
3
4
5
6
7
8
9
10
11
12
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
class LexicalAnalyzer < Struct.new(:string)
GRAMMAR = [
{ token: 'i', pattern: /if/ }, # if keyword
{ token: 'e', pattern: /else/ }, # else keyword
{ token: 'w', pattern: /while/ }, # while keyword
{ token: 'd', pattern: /do-nothing/ }, # do-nothing keyword
{ token: '(', pattern: /\(/ }, # opening bracket
{ token: ')', pattern: /\)/ }, # closing bracket
{ token: '{', pattern: /\{/ }, # opening curly bracket
{ token: '}', pattern: /\}/ }, # closing curly bracket
{ token: ';', pattern: /;/ }, # semicolon
{ token: '=', pattern: /=/ }, # equals sign
{ token: '+', pattern: /\+/ }, # addition sign
{ token: '*', pattern: /\*/ }, # multiplication sign
{ token: '<', pattern: /</ }, # less-than sign
{ token: 'n', pattern: /[0-9]+/ }, # number
{ token: 'b', pattern: /true|false/ }, # boolean
{ token: 'v', pattern: /[a-z]+/ } # variable name
]
def analyze
[].tap do |tokens|
while more_tokens?
tokens.push(next_token)
end
end
end
def more_tokens?
!string.empty?
end
def next_token
rule, match = rule_matching(string)
self.string = string_after(match)
rule[:token]
end
def rule_matching(string)
matches = GRAMMAR.map { |rule| match_at_beginning(rule[:pattern], string) }
rules_with_matches = GRAMMAR.zip(matches).reject { |rule, match| match.nil? }
rule_with_longest_match(rules_with_matches)
end
def match_at_beginning(pattern, string)
/\A#{pattern}/.match(string)
end
def rule_with_longest_match(rules_with_matches)
rules_with_matches.max_by { |rule, match| match.to_s.length }
end
def string_after(match)
match.post_match.lstrip
end
end