Original by Stefan Walk.
# File lib/coderay/scanners/php.rb, line 18
18: def reset_instance
19: super
20: @html_scanner.reset
21: end
# File lib/coderay/scanners/php.rb, line 227
227: def scan_tokens tokens, options
228:
229: if check(RE::PHP_START) || # starts with <?
230: (match?(/\s*<\S/) && exist?(RE::PHP_START)) || # starts with tag and contains <?
231: exist?(RE::HTML_INDICATOR) ||
232: check(/.{1,100}#{RE::PHP_START}/m) # PHP start after max 100 chars
233: # is HTML with embedded PHP, so start with HTML
234: states = [:initial]
235: else
236: # is just PHP, so start with PHP surrounded by HTML
237: states = [:initial, :php]
238: end
239:
240: label_expected = true
241: case_expected = false
242:
243: heredoc_delimiter = nil
244: delimiter = nil
245: modifier = nil
246:
247: until eos?
248:
249: match = nil
250: kind = nil
251:
252: case states.last
253:
254: when :initial # HTML
255: if scan RE::PHP_START
256: kind = :inline_delimiter
257: label_expected = true
258: states << :php
259: else
260: match = scan_until(/(?=#{RE::PHP_START})/) || scan_until(/\z/)
261: @html_scanner.tokenize match unless match.empty?
262: next
263: end
264:
265: when :php
266: if match = scan(/\s+/)
267: tokens << [match, :space]
268: next
269:
270: elsif scan(% (?m: \/\* (?: .*? \*\/ | .* ) ) | (?://|\#) .*? (?=#{RE::PHP_END}|$) !o)
271: kind = :comment
272:
273: elsif match = scan(RE::IDENTIFIER)
274: kind = Words::IDENT_KIND[match]
275: if kind == :ident && label_expected && check(/:(?!:)/)
276: kind = :label
277: label_expected = true
278: else
279: label_expected = false
280: if kind == :ident && match =~ /^[A-Z]/
281: kind = :constant
282: elsif kind == :reserved
283: case match
284: when 'class'
285: states << :class_expected
286: when 'function'
287: states << :function_expected
288: when 'case', 'default'
289: case_expected = true
290: end
291: elsif match == 'b' && check(/['"]/) # binary string literal
292: modifier = match
293: next
294: end
295: end
296:
297: elsif scan(/(?:\d+\.\d*|\d*\.\d+)(?:e[-+]?\d+)?|\d+e[-+]?\d+/)
298: label_expected = false
299: kind = :float
300:
301: elsif scan(/0x[0-9a-fA-F]+/)
302: label_expected = false
303: kind = :hex
304:
305: elsif scan(/\d+/)
306: label_expected = false
307: kind = :integer
308:
309: elsif scan(/'/)
310: tokens << [:open, :string]
311: if modifier
312: tokens << [modifier, :modifier]
313: modifier = nil
314: end
315: kind = :delimiter
316: states.push :sqstring
317:
318: elsif match = scan(/["`]/)
319: tokens << [:open, :string]
320: if modifier
321: tokens << [modifier, :modifier]
322: modifier = nil
323: end
324: delimiter = match
325: kind = :delimiter
326: states.push :dqstring
327:
328: elsif match = scan(RE::VARIABLE)
329: label_expected = false
330: kind = Words::VARIABLE_KIND[match]
331:
332: elsif scan(/\{/)
333: kind = :operator
334: label_expected = true
335: states.push :php
336:
337: elsif scan(/\}/)
338: if states.size == 1
339: kind = :error
340: else
341: states.pop
342: if states.last.is_a?(::Array)
343: delimiter = states.last[1]
344: states[1] = states.last[0]
345: tokens << [matched, :delimiter]
346: tokens << [:close, :inline]
347: next
348: else
349: kind = :operator
350: label_expected = true
351: end
352: end
353:
354: elsif scan(/@/)
355: label_expected = false
356: kind = :exception
357:
358: elsif scan RE::PHP_END
359: kind = :inline_delimiter
360: states = [:initial]
361:
362: elsif match = scan(/<<<(?:(#{RE::IDENTIFIER})|"(#{RE::IDENTIFIER})"|'(#{RE::IDENTIFIER})')/)
363: tokens << [:open, :string]
364: warn 'heredoc in heredoc?' if heredoc_delimiter
365: heredoc_delimiter = Regexp.escape(self[1] || self[2] || self[3])
366: kind = :delimiter
367: states.push self[3] ? :sqstring : :dqstring
368: heredoc_delimiter = /#{heredoc_delimiter}(?=;?$)/
369:
370: elsif match = scan(/#{RE::OPERATOR}/)
371: label_expected = match == ';'
372: if case_expected
373: label_expected = true if match == ':'
374: case_expected = false
375: end
376: kind = :operator
377:
378: else
379: getch
380: kind = :error
381:
382: end
383:
384: when :sqstring
385: if scan(heredoc_delimiter ? /[^\\\n]+/ : /[^'\\]+/)
386: kind = :content
387: elsif !heredoc_delimiter && scan(/'/)
388: tokens << [matched, :delimiter]
389: tokens << [:close, :string]
390: delimiter = nil
391: label_expected = false
392: states.pop
393: next
394: elsif heredoc_delimiter && match = scan(/\n/)
395: kind = :content
396: if scan heredoc_delimiter
397: tokens << ["\n", :content]
398: tokens << [matched, :delimiter]
399: tokens << [:close, :string]
400: heredoc_delimiter = nil
401: label_expected = false
402: states.pop
403: next
404: end
405: elsif scan(heredoc_delimiter ? /\\\\/ : /\\[\\'\n]/)
406: kind = :char
407: elsif scan(/\\./)
408: kind = :content
409: elsif scan(/\\/)
410: kind = :error
411: end
412:
413: when :dqstring
414: if scan(heredoc_delimiter ? /[^${\\\n]+/ : (delimiter == '"' ? /[^"${\\]+/ : /[^`${\\]+/))
415: kind = :content
416: elsif !heredoc_delimiter && scan(delimiter == '"' ? /"/ : /`/)
417: tokens << [matched, :delimiter]
418: tokens << [:close, :string]
419: delimiter = nil
420: label_expected = false
421: states.pop
422: next
423: elsif heredoc_delimiter && match = scan(/\n/)
424: kind = :content
425: if scan heredoc_delimiter
426: tokens << ["\n", :content]
427: tokens << [matched, :delimiter]
428: tokens << [:close, :string]
429: heredoc_delimiter = nil
430: label_expected = false
431: states.pop
432: next
433: end
434: elsif scan(/\\(?:x[0-9A-Fa-f]{1,2}|[0-7]{1,3})/)
435: kind = :char
436: elsif scan(heredoc_delimiter ? /\\[nrtvf\\$]/ : (delimiter == '"' ? /\\[nrtvf\\$"]/ : /\\[nrtvf\\$`]/))
437: kind = :char
438: elsif scan(/\\./)
439: kind = :content
440: elsif scan(/\\/)
441: kind = :error
442: elsif match = scan(/#{RE::VARIABLE}/)
443: kind = :local_variable
444: if check(/\[#{RE::IDENTIFIER}\]/)
445: tokens << [:open, :inline]
446: tokens << [match, :local_variable]
447: tokens << [scan(/\[/), :operator]
448: tokens << [scan(/#{RE::IDENTIFIER}/), :ident]
449: tokens << [scan(/\]/), :operator]
450: tokens << [:close, :inline]
451: next
452: elsif check(/\[/)
453: match << scan(/\[['"]?#{RE::IDENTIFIER}?['"]?\]?/)
454: kind = :error
455: elsif check(/->#{RE::IDENTIFIER}/)
456: tokens << [:open, :inline]
457: tokens << [match, :local_variable]
458: tokens << [scan(/->/), :operator]
459: tokens << [scan(/#{RE::IDENTIFIER}/), :ident]
460: tokens << [:close, :inline]
461: next
462: elsif check(/->/)
463: match << scan(/->/)
464: kind = :error
465: end
466: elsif match = scan(/\{/)
467: if check(/\$/)
468: kind = :delimiter
469: states[1] = [states.last, delimiter]
470: delimiter = nil
471: states.push :php
472: tokens << [:open, :inline]
473: else
474: kind = :string
475: end
476: elsif scan(/\$\{#{RE::IDENTIFIER}\}/)
477: kind = :local_variable
478: elsif scan(/\$/)
479: kind = :content
480: end
481:
482: when :class_expected
483: if scan(/\s+/)
484: kind = :space
485: elsif match = scan(/#{RE::IDENTIFIER}/)
486: kind = :class
487: states.pop
488: else
489: states.pop
490: next
491: end
492:
493: when :function_expected
494: if scan(/\s+/)
495: kind = :space
496: elsif scan(/&/)
497: kind = :operator
498: elsif match = scan(/#{RE::IDENTIFIER}/)
499: kind = :function
500: states.pop
501: else
502: states.pop
503: next
504: end
505:
506: else
507: raise_inspect 'Unknown state!', tokens, states
508: end
509:
510: match ||= matched
511: if $CODERAY_DEBUG and not kind
512: raise_inspect 'Error token %p in line %d' %
513: [[match, kind], line], tokens, states
514: end
515: raise_inspect 'Empty token', tokens, states unless match
516:
517: tokens << [match, kind]
518:
519: end
520:
521: tokens
522: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.