Modified MCAsmLexer to return error information upward
[oota-llvm.git] / tools / llvm-mc / AsmLexer.cpp
1 //===- AsmLexer.cpp - Lexer for Assembly Files ----------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This class implements the lexer for assembly files.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "AsmLexer.h"
15 #include "llvm/Support/SourceMgr.h"
16 #include "llvm/Support/MemoryBuffer.h"
17 #include "llvm/Config/config.h"  // for strtoull.
18 #include "llvm/MC/MCAsmInfo.h"
19 #include <cerrno>
20 #include <cstdio>
21 #include <cstdlib>
22 using namespace llvm;
23
24 AsmLexer::AsmLexer(SourceMgr &SM, const MCAsmInfo &_MAI) : SrcMgr(SM),
25                                                            MAI(_MAI)  {
26   CurBuffer = 0;
27   CurBuf = SrcMgr.getMemoryBuffer(CurBuffer);
28   CurPtr = CurBuf->getBufferStart();
29   TokStart = 0;
30 }
31
32 AsmLexer::~AsmLexer() {
33 }
34
35 SMLoc AsmLexer::getLoc() const {
36   return SMLoc::getFromPointer(TokStart);
37 }
38
39 void AsmLexer::PrintMessage(SMLoc Loc, const std::string &Msg, 
40                             const char *Type) const {
41   SrcMgr.PrintMessage(Loc, Msg, Type);
42 }
43
44 /// ReturnError - Set the error to the specified string at the specified
45 /// location.  This is defined to always return AsmToken::Error.
46 AsmToken AsmLexer::ReturnError(const char *Loc, const std::string &Msg) {
47   SetError(SMLoc::getFromPointer(Loc), Msg);
48   
49   return AsmToken(AsmToken::Error, StringRef(Loc, 0));
50 }
51
52 /// EnterIncludeFile - Enter the specified file.  This prints an error and
53 /// returns true on failure.
54 bool AsmLexer::EnterIncludeFile(const std::string &Filename) {
55   int NewBuf = SrcMgr.AddIncludeFile(Filename, SMLoc::getFromPointer(CurPtr));
56   if (NewBuf == -1)
57     return true;
58   
59   // Save the line number and lex buffer of the includer.
60   CurBuffer = NewBuf;
61   CurBuf = SrcMgr.getMemoryBuffer(CurBuffer);
62   CurPtr = CurBuf->getBufferStart();
63   return false;
64 }
65
66
67 int AsmLexer::getNextChar() {
68   char CurChar = *CurPtr++;
69   switch (CurChar) {
70   default:
71     return (unsigned char)CurChar;
72   case 0: {
73     // A nul character in the stream is either the end of the current buffer or
74     // a random nul in the file.  Disambiguate that here.
75     if (CurPtr-1 != CurBuf->getBufferEnd())
76       return 0;  // Just whitespace.
77     
78     // If this is the end of an included file, pop the parent file off the
79     // include stack.
80     SMLoc ParentIncludeLoc = SrcMgr.getParentIncludeLoc(CurBuffer);
81     if (ParentIncludeLoc != SMLoc()) {
82       CurBuffer = SrcMgr.FindBufferContainingLoc(ParentIncludeLoc);
83       CurBuf = SrcMgr.getMemoryBuffer(CurBuffer);
84       CurPtr = ParentIncludeLoc.getPointer();
85       
86       // Reset the token start pointer to the start of the new file.
87       TokStart = CurPtr;
88       
89       return getNextChar();
90     }
91     
92     // Otherwise, return end of file.
93     --CurPtr;  // Another call to lex will return EOF again.  
94     return EOF;
95   }
96   }
97 }
98
99 /// LexIdentifier: [a-zA-Z_.][a-zA-Z0-9_$.@]*
100 AsmToken AsmLexer::LexIdentifier() {
101   while (isalnum(*CurPtr) || *CurPtr == '_' || *CurPtr == '$' ||
102          *CurPtr == '.' || *CurPtr == '@')
103     ++CurPtr;
104   return AsmToken(AsmToken::Identifier, StringRef(TokStart, CurPtr - TokStart));
105 }
106
107 /// LexSlash: Slash: /
108 ///           C-Style Comment: /* ... */
109 AsmToken AsmLexer::LexSlash() {
110   switch (*CurPtr) {
111   case '*': break; // C style comment.
112   case '/': return ++CurPtr, LexLineComment();
113   default:  return AsmToken(AsmToken::Slash, StringRef(CurPtr, 1));
114   }
115
116   // C Style comment.
117   ++CurPtr;  // skip the star.
118   while (1) {
119     int CurChar = getNextChar();
120     switch (CurChar) {
121     case EOF:
122       return ReturnError(TokStart, "unterminated comment");
123     case '*':
124       // End of the comment?
125       if (CurPtr[0] != '/') break;
126       
127       ++CurPtr;   // End the */.
128       return LexToken();
129     }
130   }
131 }
132
133 /// LexLineComment: Comment: #[^\n]*
134 ///                        : //[^\n]*
135 AsmToken AsmLexer::LexLineComment() {
136   // FIXME: This is broken if we happen to a comment at the end of a file, which
137   // was .included, and which doesn't end with a newline.
138   int CurChar = getNextChar();
139   while (CurChar != '\n' && CurChar != '\n' && CurChar != EOF)
140     CurChar = getNextChar();
141   
142   if (CurChar == EOF)
143     return AsmToken(AsmToken::Eof, StringRef(CurPtr, 0));
144   return AsmToken(AsmToken::EndOfStatement, StringRef(CurPtr, 0));
145 }
146
147
148 /// LexDigit: First character is [0-9].
149 ///   Local Label: [0-9][:]
150 ///   Forward/Backward Label: [0-9][fb]
151 ///   Binary integer: 0b[01]+
152 ///   Octal integer: 0[0-7]+
153 ///   Hex integer: 0x[0-9a-fA-F]+
154 ///   Decimal integer: [1-9][0-9]*
155 /// TODO: FP literal.
156 AsmToken AsmLexer::LexDigit() {
157   if (*CurPtr == ':')
158     return ReturnError(TokStart, "FIXME: local label not implemented");
159   if (*CurPtr == 'f' || *CurPtr == 'b')
160     return ReturnError(TokStart, "FIXME: directional label not implemented");
161   
162   // Decimal integer: [1-9][0-9]*
163   if (CurPtr[-1] != '0') {
164     while (isdigit(*CurPtr))
165       ++CurPtr;
166     return AsmToken(AsmToken::Integer, StringRef(TokStart, CurPtr - TokStart), 
167                     strtoll(TokStart, 0, 10));
168   }
169   
170   if (*CurPtr == 'b') {
171     ++CurPtr;
172     const char *NumStart = CurPtr;
173     while (CurPtr[0] == '0' || CurPtr[0] == '1')
174       ++CurPtr;
175     
176     // Requires at least one binary digit.
177     if (CurPtr == NumStart)
178       return ReturnError(CurPtr-2, "Invalid binary number");
179     return AsmToken(AsmToken::Integer, StringRef(TokStart, CurPtr - TokStart),
180                     strtoll(NumStart, 0, 2));
181   }
182  
183   if (*CurPtr == 'x') {
184     ++CurPtr;
185     const char *NumStart = CurPtr;
186     while (isxdigit(CurPtr[0]))
187       ++CurPtr;
188     
189     // Requires at least one hex digit.
190     if (CurPtr == NumStart)
191       return ReturnError(CurPtr-2, "Invalid hexadecimal number");
192     
193     errno = 0;
194     if (errno == EINVAL)
195       return ReturnError(CurPtr-2, "Invalid hexadecimal number");
196     if (errno == ERANGE) {
197       errno = 0;
198       if (errno == EINVAL)
199         return ReturnError(CurPtr-2, "Invalid hexadecimal number");
200       if (errno == ERANGE)
201         return ReturnError(CurPtr-2, "Hexadecimal number out of range");
202     }
203     return AsmToken(AsmToken::Integer, StringRef(TokStart, CurPtr - TokStart),
204                     (int64_t) strtoull(NumStart, 0, 16));
205   }
206   
207   // Must be an octal number, it starts with 0.
208   while (*CurPtr >= '0' && *CurPtr <= '7')
209     ++CurPtr;
210   return AsmToken(AsmToken::Integer, StringRef(TokStart, CurPtr - TokStart),
211                   strtoll(TokStart, 0, 8));
212 }
213
214 /// LexQuote: String: "..."
215 AsmToken AsmLexer::LexQuote() {
216   int CurChar = getNextChar();
217   // TODO: does gas allow multiline string constants?
218   while (CurChar != '"') {
219     if (CurChar == '\\') {
220       // Allow \", etc.
221       CurChar = getNextChar();
222     }
223     
224     if (CurChar == EOF)
225       return ReturnError(TokStart, "unterminated string constant");
226
227     CurChar = getNextChar();
228   }
229   
230   return AsmToken(AsmToken::String, StringRef(TokStart, CurPtr - TokStart));
231 }
232
233 StringRef AsmLexer::LexUntilEndOfStatement() {
234   TokStart = CurPtr;
235
236   while (!isAtStartOfComment(*CurPtr) && // Start of line comment.
237           *CurPtr != ';' &&  // End of statement marker.
238          *CurPtr != '\n' &&
239          *CurPtr != '\r' &&
240          (*CurPtr != 0 || CurPtr != CurBuf->getBufferEnd())) {
241     ++CurPtr;
242   }
243   return StringRef(TokStart, CurPtr-TokStart);
244 }
245
246 bool AsmLexer::isAtStartOfComment(char Char) {
247   // FIXME: This won't work for multi-character comment indicators like "//".
248   return Char == *MAI.getCommentString();
249 }
250
251 AsmToken AsmLexer::LexToken() {
252   TokStart = CurPtr;
253   // This always consumes at least one character.
254   int CurChar = getNextChar();
255   
256   if (isAtStartOfComment(CurChar))
257     return LexLineComment();
258
259   switch (CurChar) {
260   default:
261     // Handle identifier: [a-zA-Z_.][a-zA-Z0-9_$.@]*
262     if (isalpha(CurChar) || CurChar == '_' || CurChar == '.')
263       return LexIdentifier();
264     
265     // Unknown character, emit an error.
266     return ReturnError(TokStart, "invalid character in input");
267   case EOF: return AsmToken(AsmToken::Eof, StringRef(TokStart, 0));
268   case 0:
269   case ' ':
270   case '\t':
271     // Ignore whitespace.
272     return LexToken();
273   case '\n': // FALL THROUGH.
274   case '\r': // FALL THROUGH.
275   case ';': return AsmToken(AsmToken::EndOfStatement, StringRef(TokStart, 1));
276   case ':': return AsmToken(AsmToken::Colon, StringRef(TokStart, 1));
277   case '+': return AsmToken(AsmToken::Plus, StringRef(TokStart, 1));
278   case '-': return AsmToken(AsmToken::Minus, StringRef(TokStart, 1));
279   case '~': return AsmToken(AsmToken::Tilde, StringRef(TokStart, 1));
280   case '(': return AsmToken(AsmToken::LParen, StringRef(TokStart, 1));
281   case ')': return AsmToken(AsmToken::RParen, StringRef(TokStart, 1));
282   case '[': return AsmToken(AsmToken::LBrac, StringRef(TokStart, 1));
283   case ']': return AsmToken(AsmToken::RBrac, StringRef(TokStart, 1));
284   case '{': return AsmToken(AsmToken::LCurly, StringRef(TokStart, 1));
285   case '}': return AsmToken(AsmToken::RCurly, StringRef(TokStart, 1));
286   case '*': return AsmToken(AsmToken::Star, StringRef(TokStart, 1));
287   case ',': return AsmToken(AsmToken::Comma, StringRef(TokStart, 1));
288   case '$': return AsmToken(AsmToken::Dollar, StringRef(TokStart, 1));
289   case '=': 
290     if (*CurPtr == '=')
291       return ++CurPtr, AsmToken(AsmToken::EqualEqual, StringRef(TokStart, 2));
292     return AsmToken(AsmToken::Equal, StringRef(TokStart, 1));
293   case '|': 
294     if (*CurPtr == '|')
295       return ++CurPtr, AsmToken(AsmToken::PipePipe, StringRef(TokStart, 2));
296     return AsmToken(AsmToken::Pipe, StringRef(TokStart, 1));
297   case '^': return AsmToken(AsmToken::Caret, StringRef(TokStart, 1));
298   case '&': 
299     if (*CurPtr == '&')
300       return ++CurPtr, AsmToken(AsmToken::AmpAmp, StringRef(TokStart, 2));
301     return AsmToken(AsmToken::Amp, StringRef(TokStart, 1));
302   case '!': 
303     if (*CurPtr == '=')
304       return ++CurPtr, AsmToken(AsmToken::ExclaimEqual, StringRef(TokStart, 2));
305     return AsmToken(AsmToken::Exclaim, StringRef(TokStart, 1));
306   case '%': return AsmToken(AsmToken::Percent, StringRef(TokStart, 1));
307   case '/': return LexSlash();
308   case '#': return AsmToken(AsmToken::Hash, StringRef(TokStart, 1));
309   case '"': return LexQuote();
310   case '0': case '1': case '2': case '3': case '4':
311   case '5': case '6': case '7': case '8': case '9':
312     return LexDigit();
313   case '<':
314     switch (*CurPtr) {
315     case '<': return ++CurPtr, AsmToken(AsmToken::LessLess, 
316                                         StringRef(TokStart, 2));
317     case '=': return ++CurPtr, AsmToken(AsmToken::LessEqual, 
318                                         StringRef(TokStart, 2));
319     case '>': return ++CurPtr, AsmToken(AsmToken::LessGreater, 
320                                         StringRef(TokStart, 2));
321     default: return AsmToken(AsmToken::Less, StringRef(TokStart, 1));
322     }
323   case '>':
324     switch (*CurPtr) {
325     case '>': return ++CurPtr, AsmToken(AsmToken::GreaterGreater, 
326                                         StringRef(TokStart, 2));
327     case '=': return ++CurPtr, AsmToken(AsmToken::GreaterEqual, 
328                                         StringRef(TokStart, 2));
329     default: return AsmToken(AsmToken::Greater, StringRef(TokStart, 1));
330     }
331       
332   // TODO: Quoted identifiers (objc methods etc)
333   // local labels: [0-9][:]
334   // Forward/backward labels: [0-9][fb]
335   // Integers, fp constants, character constants.
336   }
337 }