summaryrefslogtreecommitdiff
path: root/lib/coderay/scanners/delphi.rb
blob: b328155abf6fe28358da3f4a96d5382a756a87fc (plain)
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
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
96
97
98
99
100
101
102
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
module CodeRay
module Scanners
  
  # Scanner for the Delphi language (Object Pascal).
  # 
  # Alias: +pascal+
  class Delphi < Scanner
    
    register_for :delphi
    file_extension 'pas'
    
    KEYWORDS = [
      'and', 'array', 'as', 'at', 'asm', 'at', 'begin', 'case', 'class',
      'const', 'constructor', 'destructor', 'dispinterface', 'div', 'do',
      'downto', 'else', 'end', 'except', 'exports', 'file', 'finalization',
      'finally', 'for', 'function', 'goto', 'if', 'implementation', 'in',
      'inherited', 'initialization', 'inline', 'interface', 'is', 'label',
      'library', 'mod', 'nil', 'not', 'object', 'of', 'or', 'out', 'packed',
      'procedure', 'program', 'property', 'raise', 'record', 'repeat',
      'resourcestring', 'set', 'shl', 'shr', 'string', 'then', 'threadvar',
      'to', 'try', 'type', 'unit', 'until', 'uses', 'var', 'while', 'with',
      'xor', 'on',
    ]  # :nodoc:
    
    DIRECTIVES = [
      'absolute', 'abstract', 'assembler', 'at', 'automated', 'cdecl',
      'contains', 'deprecated', 'dispid', 'dynamic', 'export',
      'external', 'far', 'forward', 'implements', 'local', 
      'near', 'nodefault', 'on', 'overload', 'override',
      'package', 'pascal', 'platform', 'private', 'protected', 'public',
      'published', 'read', 'readonly', 'register', 'reintroduce',
      'requires', 'resident', 'safecall', 'stdcall', 'stored', 'varargs',
      'virtual', 'write', 'writeonly',
    ]  # :nodoc:
    
    IDENT_KIND = WordList::CaseIgnoring.new(:ident).
      add(KEYWORDS, :keyword).
      add(DIRECTIVES, :directive)  # :nodoc:
    
    NAME_FOLLOWS = WordList::CaseIgnoring.new(false).
      add(%w(procedure function .))  # :nodoc:
    
  protected
    
    def scan_tokens encoder, options
      
      state = :initial
      last_token = ''
      
      until eos?
        
        if state == :initial
          
          if match = scan(/ \s+ /x)
            encoder.text_token match, :space
            next
            
          elsif match = scan(%r! \{ \$ [^}]* \}? | \(\* \$ (?: .*? \*\) | .* ) !mx)
            encoder.text_token match, :preprocessor
            next
            
          elsif match = scan(%r! // [^\n]* | \{ [^}]* \}? | \(\* (?: .*? \*\) | .* ) !mx)
            encoder.text_token match, :comment
            next
            
          elsif match = scan(/ <[>=]? | >=? | :=? | [-+=*\/;,@\^|\(\)\[\]] | \.\. /x)
            encoder.text_token match, :operator
          
          elsif match = scan(/\./)
            encoder.text_token match, :operator
            next if last_token == 'end'
            
          elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
            encoder.text_token match, NAME_FOLLOWS[last_token] ? :ident : IDENT_KIND[match]
            
          elsif match = skip(/ ' ( [^\n']|'' ) (?:'|$) /x)
            encoder.begin_group :char
            encoder.text_token "'", :delimiter
            encoder.text_token self[1], :content
            encoder.text_token "'", :delimiter
            encoder.end_group :char
            next
            
          elsif match = scan(/ ' /x)
            encoder.begin_group :string
            encoder.text_token match, :delimiter
            state = :string
            
          elsif match = scan(/ \# (?: \d+ | \$[0-9A-Fa-f]+ ) /x)
            encoder.text_token match, :char
            
          elsif match = scan(/ \$ [0-9A-Fa-f]+ /x)
            encoder.text_token match, :hex
            
          elsif match = scan(/ (?: \d+ ) (?![eE]|\.[^.]) /x)
            encoder.text_token match, :integer
            
          elsif match = scan(/ \d+ (?: \.\d+ (?: [eE][+-]? \d+ )? | [eE][+-]? \d+ ) /x)
            encoder.text_token match, :float
            
          else
            encoder.text_token getch, :error
            next
            
          end
          
        elsif state == :string
          if match = scan(/[^\n']+/)
            encoder.text_token match, :content
          elsif match = scan(/''/)
            encoder.text_token match, :char
          elsif match = scan(/'/)
            encoder.text_token match, :delimiter
            encoder.end_group :string
            state = :initial
            next
          elsif match = scan(/\n/)
            encoder.end_group :string
            encoder.text_token match, :space
            state = :initial
          else
            raise "else case \' reached; %p not handled." % peek(1), encoder
          end
          
        else
          raise 'else-case reached', encoder
          
        end
        
        last_token = match
        
      end
      
      if state == :string
        encoder.end_group state
      end
      
      encoder
    end

  end

end
end