summaryrefslogtreecommitdiffstats
path: root/vendor/plugins/coderay-0.9.0/lib/coderay/scanners/scheme.rb
blob: c869a30c47a27b79f1bdddd62bf61f86c0f74db6 (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
module CodeRay
  module Scanners

    # Scheme scanner for CodeRay (by closure).
    # Thanks to murphy for putting CodeRay into public.
    class Scheme < Scanner
      
      register_for :scheme
      file_extension 'scm'

      CORE_FORMS = %w[
        lambda let let* letrec syntax-case define-syntax let-syntax
        letrec-syntax begin define quote if or and cond case do delay
        quasiquote set! cons force call-with-current-continuation call/cc
      ]

      IDENT_KIND = CaseIgnoringWordList.new(:ident).
        add(CORE_FORMS, :reserved)
      
      #IDENTIFIER_INITIAL = /[a-z!@\$%&\*\/\:<=>\?~_\^]/i
      #IDENTIFIER_SUBSEQUENT = /#{IDENTIFIER_INITIAL}|\d|\.|\+|-/
      #IDENTIFIER = /#{IDENTIFIER_INITIAL}#{IDENTIFIER_SUBSEQUENT}*|\+|-|\.{3}/
      IDENTIFIER = /[a-zA-Z!@$%&*\/:<=>?~_^][\w!@$%&*\/:<=>?~^.+\-]*|[+-]|\.\.\./
      DIGIT = /\d/
      DIGIT10 = DIGIT
      DIGIT16 = /[0-9a-f]/i
      DIGIT8 = /[0-7]/
      DIGIT2 = /[01]/
      RADIX16 = /\#x/i
      RADIX8 = /\#o/i
      RADIX2 = /\#b/i
      RADIX10 = /\#d/i
      EXACTNESS = /#i|#e/i
      SIGN = /[\+-]?/
      EXP_MARK = /[esfdl]/i
      EXP = /#{EXP_MARK}#{SIGN}#{DIGIT}+/
      SUFFIX = /#{EXP}?/
      PREFIX10 = /#{RADIX10}?#{EXACTNESS}?|#{EXACTNESS}?#{RADIX10}?/
      PREFIX16 = /#{RADIX16}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX16}/
      PREFIX8 = /#{RADIX8}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX8}/
      PREFIX2 = /#{RADIX2}#{EXACTNESS}?|#{EXACTNESS}?#{RADIX2}/
      UINT10 = /#{DIGIT10}+#*/
      UINT16 = /#{DIGIT16}+#*/
      UINT8 = /#{DIGIT8}+#*/
      UINT2 = /#{DIGIT2}+#*/
      DECIMAL = /#{DIGIT10}+#+\.#*#{SUFFIX}|#{DIGIT10}+\.#{DIGIT10}*#*#{SUFFIX}|\.#{DIGIT10}+#*#{SUFFIX}|#{UINT10}#{EXP}/
      UREAL10 = /#{UINT10}\/#{UINT10}|#{DECIMAL}|#{UINT10}/
      UREAL16 = /#{UINT16}\/#{UINT16}|#{UINT16}/
      UREAL8 = /#{UINT8}\/#{UINT8}|#{UINT8}/
      UREAL2 = /#{UINT2}\/#{UINT2}|#{UINT2}/
      REAL10 = /#{SIGN}#{UREAL10}/
      REAL16 = /#{SIGN}#{UREAL16}/
      REAL8 = /#{SIGN}#{UREAL8}/
      REAL2 = /#{SIGN}#{UREAL2}/
      IMAG10 = /i|#{UREAL10}i/
      IMAG16 = /i|#{UREAL16}i/
      IMAG8 = /i|#{UREAL8}i/
      IMAG2 = /i|#{UREAL2}i/
      COMPLEX10 = /#{REAL10}@#{REAL10}|#{REAL10}\+#{IMAG10}|#{REAL10}-#{IMAG10}|\+#{IMAG10}|-#{IMAG10}|#{REAL10}/
      COMPLEX16 = /#{REAL16}@#{REAL16}|#{REAL16}\+#{IMAG16}|#{REAL16}-#{IMAG16}|\+#{IMAG16}|-#{IMAG16}|#{REAL16}/
      COMPLEX8 = /#{REAL8}@#{REAL8}|#{REAL8}\+#{IMAG8}|#{REAL8}-#{IMAG8}|\+#{IMAG8}|-#{IMAG8}|#{REAL8}/
      COMPLEX2 = /#{REAL2}@#{REAL2}|#{REAL2}\+#{IMAG2}|#{REAL2}-#{IMAG2}|\+#{IMAG2}|-#{IMAG2}|#{REAL2}/
      NUM10 = /#{PREFIX10}?#{COMPLEX10}/
      NUM16 = /#{PREFIX16}#{COMPLEX16}/
      NUM8 = /#{PREFIX8}#{COMPLEX8}/
      NUM2 = /#{PREFIX2}#{COMPLEX2}/
      NUM = /#{NUM10}|#{NUM16}|#{NUM8}|#{NUM2}/
    
    private
      def scan_tokens tokens,options
        
        state = :initial
        ident_kind = IDENT_KIND
        
        until eos?
          kind = match = nil
          
          case state
          when :initial
            if scan(/ \s+ | \\\n /x)
              kind = :space
            elsif scan(/['\(\[\)\]]|#\(/)
              kind = :operator_fat
            elsif scan(/;.*/)
              kind = :comment
            elsif scan(/#\\(?:newline|space|.?)/)
              kind = :char
            elsif scan(/#[ft]/)
              kind = :pre_constant
            elsif scan(/#{IDENTIFIER}/o)
              kind = ident_kind[matched]
            elsif scan(/\./)
              kind = :operator
            elsif scan(/"/)
              tokens << [:open, :string]
              state = :string
              tokens << ['"', :delimiter]
              next
            elsif scan(/#{NUM}/o) and not matched.empty?
              kind = :integer
            elsif getch
              kind = :error
            end
            
          when :string
            if scan(/[^"\\]+/) or scan(/\\.?/)
              kind = :content
            elsif scan(/"/)
              tokens << ['"', :delimiter]
              tokens << [:close, :string]
              state = :initial
              next
            else
              raise_inspect "else case \" reached; %p not handled." % peek(1),
                tokens, state
            end
            
          else
            raise "else case reached"
          end
          
          match ||= matched
          if $DEBUG and not kind
            raise_inspect 'Error token %p in line %d' %
            [[match, kind], line], tokens
          end
          raise_inspect 'Empty token', tokens, state unless match
          
          tokens << [match, kind]
          
        end  # until eos
        
        if state == :string
          tokens << [:close, :string]
        end
        
        tokens
        
      end #scan_tokens
    end #class
  end #module scanners
end #module coderay