summaryrefslogtreecommitdiff
path: root/lib/coderay/scanners/lua3.rb
blob: d2d428044f6737f574228d6032599bb3eddc0595 (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
# encoding: utf-8
# Pseudocode: states optionally define groups, comments removed, counter definition?

module CodeRay
module Scanners

  # Scanner for the Lua[http://lua.org] programming lanuage.
  #
  # The language’s complete syntax is defined in
  # {the Lua manual}[http://www.lua.org/manual/5.2/manual.html],
  # which is what this scanner tries to conform to.
  class Lua3 < RuleBasedScannerX
    
    register_for :lua3
    file_extension 'lua'
    title 'Lua'
    
    # Keywords used in Lua.
    KEYWORDS = %w[and break do else elseif end
      for function goto if in
      local not or repeat return
      then until while
    ]
    
    # Constants set by the Lua core.
    PREDEFINED_CONSTANTS = %w[false true nil]
    
    # The expressions contained in this array are parts of Lua’s `basic'
    # library. Although it’s not entirely necessary to load that library,
    # it is highly recommended and one would have to provide own implementations
    # of some of these expressions if one does not do so. They however aren’t
    # keywords, neither are they constants, but nearly predefined, so they
    # get tagged as `predefined' rather than anything else.
    #
    # This list excludes values of form `_UPPERCASE' because the Lua manual
    # requires such identifiers to be reserved by Lua anyway and they are
    # highlighted directly accordingly, without the need for specific
    # identifiers to be listed here.
    PREDEFINED_EXPRESSIONS = %w[
      assert collectgarbage dofile error getmetatable
      ipairs load loadfile next pairs pcall print
      rawequal rawget rawlen rawset select setmetatable
      tonumber tostring type xpcall
    ]
    
    # Automatic token kind selection for normal words.
    IDENT_KIND = CodeRay::WordList.new(:ident).
      add(KEYWORDS, :keyword).
      add(PREDEFINED_CONSTANTS, :predefined_constant).
      add(PREDEFINED_EXPRESSIONS, :predefined)
    
    protected
    
    # Scanner initialization.
    def setup
      super
      @brace_depth = 0
      @num_equals = nil
    end
    
    counter :brace_depth
    
    state :initial, :map => :map do
      on %r/\-\-\[\=*\[/, push(:long_comment), :delimiter, set(:num_equals, -> (match) { match.count('=') })
      on %r/--.*$/, :comment
      on %r/\[=*\[/, push(:long_string), :delimiter, set(:num_equals, -> (match) { match.count('=') })
      on %r/::\s*[a-zA-Z_][a-zA-Z0-9_]+\s*::/, :label
      on %r/_[A-Z]+/, :predefined
      on check_if(:brace_depth, :>, 0), %r/([a-zA-Z_][a-zA-Z0-9_]*) (\s+)?(=)/x, groups(:key, :space, :operator)
      on %r/[a-zA-Z_][a-zA-Z0-9_]*/, kind { |match| IDENT_KIND[match] }, push_state { |match, kind|
        if kind == :keyword && match == 'function'
          :function_expected
        elsif kind == :keyword && match == 'goto'
          :goto_label_expected
        elsif kind == :keyword && match == 'local'
          :local_var_expected
        end
      }
      
      on %r/\{/, push(:map), kind { |brace_depth| brace_depth > 0 ? :inline_delimiter : :delimiter }, increment(:brace_depth)
      on check_if(:brace_depth, :==, 1), %r/\}/, :delimiter, pop, decrement(:brace_depth)
      on check_if(:brace_depth, :==, 0), %r/\}/, :error
      on %r/\}/, :inline_delimiter, pop, decrement(:brace_depth)
      
      on %r/"/, push(:double_quoted_string), :delimiter
      on %r/'/, push(:single_quoted_string), :delimiter
          
      on %r/-? (?:0x\h* \. \h+ (?:p[+\-]?\d+)? | \d*\.\d+ (?:e[+\-]?\d+)?)/ix, :float
          
      on %r/-? (?:0x\h+ (?:p[+\-]?\d+)? | \d+ (?:e[+\-]?\d+)?)/ix, :integer
      on %r/[\+\-\*\/%^\#=~<>\(\)\[\]:;,] | \.(?!\d)/x, :operator
      on %r/\s+/, :space
    end
    
    state :function_expected do
      on %r/\(.*?\)/m, :operator, pop
      on %r/[a-zA-Z_] (?:[a-zA-Z0-9_\.] (?!\.\d))* [\.\:]/x, :ident
      on %r/[a-zA-Z_][a-zA-Z0-9_]*/, :function, pop
      on %r/\s+/, :space
    end
    
    state :goto_label_expected do
      on %r/[a-zA-Z_][a-zA-Z0-9_]*/, :label, pop
      on %r/\s+/, :space
    end
    
    state :local_var_expected do
      on %r/function/, :keyword, pop, push(:function_expected)
      on %r/[a-zA-Z_][a-zA-Z0-9_]*/, :local_variable
      on %r/,/, :operator
      on %r/\=/, :operator, pop
      on %r/\n/, :space, pop
      on %r/\s+/, :space
    end
    
    state :long_comment => :comment do
      on pattern { |num_equals| %r/(.*?)(\]={#{num_equals}}\])/m }, groups(:content, :delimiter), pop(:comment)
      on %r/.*/m, :error, pop(:comment)
    end
    
    state :long_string => :string do
      on pattern { |num_equals| %r/(.*?)(\]={#{num_equals}}\])/m }, groups(:content, :delimiter), pop(:string)
      on %r/.*/m, :error, pop(:string)
    end
    
    state :single_quoted_string => :string do
      on %r/[^\\'\n]+/, :content
      on %r/\\(?:["'abfnrtv\\]|z\s*|x\h\h|\d{1,3}|\n)/m, :char
      on %r/'/, :delimiter, pop(:string)
      on %r/\n/, :error, pop(:string)
    end
    
    state :double_quoted_string => :string do
      on %r/[^\\"\n]+/, :content
      on %r/\\(?:["'abfnrtv\\]|z\s*|x\h\h|\d{1,3}|\n)/m, :char
      on %r/"/, :delimiter, pop(:string)
      on %r/\n/, :error, pop(:string)
    end
  end
  
end
end