1 $ // This file is part of NIT ( http://www.nitlanguage.org ).
3 $ // Copyright 2008 Jean Privat <jean@pryen.org>
4 $ // Based on algorithms developped for ( http://www.sablecc.org/ ).
6 $ // Licensed under the Apache License, Version 2.0 (the "License");
7 $ // you may not use this file except in compliance with the License.
8 $ // You may obtain a copy of the License at
10 $ // http://www.apache.org/licenses/LICENSE-2.0
12 $ // Unless required by applicable law or agreed to in writing, software
13 $ // distributed under the License is distributed on an "AS IS" BASIS,
14 $ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 $ // See the License for the specific language governing permissions and
16 $ // limitations under the License.
18 $ template make_lexer()
20 # The lexer extract NIT tokens from an input stream.
21 # It is better user with the Parser
24 var _token: nullable Token
29 # Name of the stream (as given to tokens)
30 readable var _filename: String
32 # Input stream where character are read
35 # Pushback buffer to store unread character
36 var _stream_buf: Buffer
38 # Number of character stored in the pushback buffer
41 # Current line number in the input stream
44 # Current column in the input stream
47 # Was the last character a cariage-return?
50 # If the end of stream?
51 var _eof: Bool = false
53 # Current working text read from the input stream
56 $ foreach {lexer_data/state}
57 # Constante state values
58 private fun state_${translate(@name,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}: Int do return @id end
61 # Create a new lexer for a stream (and a name)
62 init(stream: IStream, fname: String)
68 _stream_buf = new Buffer
73 # Give the next token (but do not consume it)
76 while _token == null do
79 return _token.as(not null)
82 # Give and consume the next token
86 while result == null do
90 return result.as(not null)
93 # Get a token, or null if it is discarded
94 private fun get_token: nullable Token
99 var start_line = _line
101 var accept_state = -1
102 var accept_token = -1
103 var accept_length = -1
107 var goto_table = _goto_table[_state]
108 var accept = _accept_table[_state]
137 var first_loop = true # aka until
138 while dfa_state < -1 or first_loop do
139 var old_state = dfa_state
140 if dfa_state < -1 then
141 old_state = -2 - dfa_state
146 var tmp0 = goto_table[old_state]
148 var high = tmp0.length - 1
151 var tmp1 = tmp0.intern_items
153 var middle = (low + high) / 2
154 var tmp2 = tmp1[middle].intern_items
158 else if c > tmp2[1] then
162 low = high + 1 # aka break
166 first_loop = false # aka until
176 if dfa_state >= 0 then
177 if accept[dfa_state] != -1 then
178 accept_state = dfa_state
179 accept_token = accept[dfa_state]
180 accept_length = text.length
185 if accept_state != -1 then
187 if accept_token == ${position()-1} then
188 var location = new Location(_filename, start_line + 1, accept_line + 1, start_pos + 1, accept_pos)
191 var token_text = text.substring(0, accept_length)
192 var token = new @ename.init_tk(token_text, location)
195 var token = new @ename.init_tk(location)
197 push_back(accept_length)
200 $ if {count(transition[@from!=@to])!=0}
201 var state_id = _state
202 $ foreach transition in {transition[@from!=@to]}
203 if state_id == ${/parser/lexer_data/state[@name=$transition/@from]/@id} then
204 _state = state_${translate(@to,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")}
216 var location = new Location(_filename, start_line + 1, start_line + 1, start_pos + 1, start_pos + 1)
217 if text.length > 0 then
218 var token = new PError.init_error("Syntax error: unknown token {text}.", location)
221 var token = new EOF(location)
230 # Read the next character.
231 # The character is read from the stream of from the pushback buffer.
232 private fun get_char: Int
242 var res = _stream_buf[_stream_pos]
246 result = _stream.read_char
256 # Unread some characters.
257 # Unread characters are stored in the pushback buffer.
258 private fun push_back(accept_length: Int)
260 var length = _text.length
262 while i >= accept_length do
264 _stream_pos = _stream_pos + 1
265 _stream_buf[_stream_pos] = _text[i]
270 var _goto_table: Array[Array[Array[Array[Int]]]]
271 private fun build_goto_table
274 $ foreach {lexer_data/goto_table/state}
277 $ if {count(goto)!=0}
280 [@low, @high, @state][-sep ','-]
292 private fun nil_array: Array[Array[Int]]
294 return once new Array[Array[Int]]
297 var _accept_table: Array[Array[Int]]
298 private fun build_accept_table do
299 _accept_table = once [
300 $ foreach {lexer_data/accept_table/state}
302 [-foreach {i}-]${.}[-sep ','-][-end foreach-]