summaryrefslogtreecommitdiff
path: root/lib/coderay/scanners/delphi.rb
blob: d02c632945152d38dfe6e2e177595a248a9b56b4 (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
module CodeRay module Scanners
	
	class Delphi < Scanner

		register_for :delphi
		
		RESERVED_WORDS = [
			'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'
		]

		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'
		]

		IDENT_KIND = CaseIgnoringWordList.new(:ident).
			add(RESERVED_WORDS, :reserved).
			add(DIRECTIVES, :directive)

		def scan_tokens tokens, options

			state = :initial

			until eos?

				kind = :error
				match = nil

				if state == :initial
					
					if scan(/ \s+ /x)
						kind = :space
						
					elsif scan(%r! \{ \$ [^}]* \}? | \(\* \$ (?: .*? \*\) | .* ) !mx)
						kind = :preprocessor
						
					elsif scan(%r! // [^\n]* | \{ [^}]* \}? | \(\* (?: .*? \*\) | .* ) !mx)
						kind = :comment
						
					elsif scan(/ [-+*\/=<>:;,.@\^|\(\)\[\]]+ /x)
						kind = :operator
						
					elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
						kind = IDENT_KIND[match]
						
					elsif match = scan(/ ' ( [^\n']|'' ) (?:'|$) /x)
						tokens << [:open, :char]
						tokens << ["'", :delimiter]
						tokens << [self[1], :content]
						tokens << ["'", :delimiter]
						tokens << [:close, :char]
						next
						
					elsif match = scan(/ ' /x)
						tokens << [:open, :string]
						state = :string
						kind = :delimiter
						
					elsif scan(/ \# (?: \d+ | \$[0-9A-Fa-f]+ ) /x)
						kind = :char
						
					elsif scan(/ \$ [0-9A-Fa-f]+ /x)
						kind = :hex
						
					elsif scan(/ (?: \d+ ) (?![eE]|\.[^.]) /x)
						kind = :integer
						
					elsif scan(/ \d+ (?: \.\d+ (?: [eE][+-]? \d+ )? | [eE][+-]? \d+ ) /x)
						kind = :float

					else
						getch
					end
					
				elsif state == :string
					if scan(/[^\n']+/)
						kind = :content
					elsif scan(/''/)
						kind = :char
					elsif scan(/'/)
						tokens << ["'", :delimiter]
						tokens << [:close, :string]
						state = :initial
						next
					elsif scan(/\n/)
						state = :initial
					else
						raise "else case \' reached; %p not handled." % peek(1), tokens
					end
					
				else
					raise 'else-case reached', tokens
					
				end
				
				match ||= matched
				if $DEBUG and (not kind or kind == :error)
					raise_inspect 'Error token %p in line %d' %
						[[match, kind], line], tokens
				end
				raise_inspect 'Empty token', tokens unless match

				tokens << [match, kind]
				
			end
			
			tokens
		end

	end

end end