You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
14 KiB
356 lines
14 KiB
/*
|
|
* [The "BSD licence"]
|
|
* Copyright (c) 2005-2008 Terence Parr
|
|
* All rights reserved.
|
|
*
|
|
* Conversion to C#:
|
|
* Copyright (c) 2008-2009 Sam Harwell, Pixel Mine, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
namespace Antlr.Runtime {
|
|
using ConditionalAttribute = System.Diagnostics.ConditionalAttribute;
|
|
|
|
/** <summary>
|
|
* A lexer is recognizer that draws input symbols from a character stream.
|
|
* lexer grammars result in a subclass of this object. A Lexer object
|
|
* uses simplified match() and error recovery mechanisms in the interest
|
|
* of speed.
|
|
* </summary>
|
|
*/
|
|
public abstract class Lexer : BaseRecognizer, ITokenSource {
|
|
/** <summary>Where is the lexer drawing characters from?</summary> */
|
|
protected ICharStream input;
|
|
|
|
public Lexer() {
|
|
}
|
|
|
|
public Lexer(ICharStream input) {
|
|
this.input = input;
|
|
}
|
|
|
|
public Lexer(ICharStream input, RecognizerSharedState state)
|
|
: base(state) {
|
|
this.input = input;
|
|
}
|
|
|
|
#region Properties
|
|
public string Text {
|
|
/** <summary>Return the text matched so far for the current token or any text override.</summary> */
|
|
get {
|
|
if (state.text != null) {
|
|
return state.text;
|
|
}
|
|
return input.Substring(state.tokenStartCharIndex, CharIndex - state.tokenStartCharIndex);
|
|
}
|
|
/** <summary>Set the complete text of this token; it wipes any previous changes to the text.</summary> */
|
|
set {
|
|
state.text = value;
|
|
}
|
|
}
|
|
public int Line {
|
|
get {
|
|
return input.Line;
|
|
}
|
|
set {
|
|
input.Line = value;
|
|
}
|
|
}
|
|
public int CharPositionInLine {
|
|
get {
|
|
return input.CharPositionInLine;
|
|
}
|
|
set {
|
|
input.CharPositionInLine = value;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public override void Reset() {
|
|
base.Reset(); // reset all recognizer state variables
|
|
// wack Lexer state variables
|
|
if (input != null) {
|
|
input.Seek(0); // rewind the input
|
|
}
|
|
if (state == null) {
|
|
return; // no shared state work to do
|
|
}
|
|
state.token = null;
|
|
state.type = TokenTypes.Invalid;
|
|
state.channel = TokenChannels.Default;
|
|
state.tokenStartCharIndex = -1;
|
|
state.tokenStartCharPositionInLine = -1;
|
|
state.tokenStartLine = -1;
|
|
state.text = null;
|
|
}
|
|
|
|
/** <summary>Return a token from this source; i.e., match a token on the char stream.</summary> */
|
|
public virtual IToken NextToken() {
|
|
for (; ; ) {
|
|
state.token = null;
|
|
state.channel = TokenChannels.Default;
|
|
state.tokenStartCharIndex = input.Index;
|
|
state.tokenStartCharPositionInLine = input.CharPositionInLine;
|
|
state.tokenStartLine = input.Line;
|
|
state.text = null;
|
|
if (input.LA(1) == CharStreamConstants.EndOfFile) {
|
|
IToken eof = new CommonToken((ICharStream)input, CharStreamConstants.EndOfFile, TokenChannels.Default, input.Index, input.Index);
|
|
eof.Line = Line;
|
|
eof.CharPositionInLine = CharPositionInLine;
|
|
return eof;
|
|
}
|
|
try {
|
|
mTokens();
|
|
if (state.token == null) {
|
|
Emit();
|
|
} else if (state.token == Tokens.Skip) {
|
|
continue;
|
|
}
|
|
return state.token;
|
|
} catch (NoViableAltException nva) {
|
|
ReportError(nva);
|
|
Recover(nva); // throw out current char and try again
|
|
} catch (RecognitionException re) {
|
|
ReportError(re);
|
|
// match() routine has already called recover()
|
|
}
|
|
}
|
|
}
|
|
|
|
/** <summary>
|
|
* Instruct the lexer to skip creating a token for current lexer rule
|
|
* and look for another token. nextToken() knows to keep looking when
|
|
* a lexer rule finishes with token set to SKIP_TOKEN. Recall that
|
|
* if token==null at end of any token rule, it creates one for you
|
|
* and emits it.
|
|
* </summary>
|
|
*/
|
|
public virtual void Skip() {
|
|
state.token = Tokens.Skip;
|
|
}
|
|
|
|
/** <summary>This is the lexer entry point that sets instance var 'token'</summary> */
|
|
public abstract void mTokens();
|
|
|
|
public virtual ICharStream CharStream {
|
|
get {
|
|
return input;
|
|
}
|
|
/** <summary>Set the char stream and reset the lexer</summary> */
|
|
set {
|
|
input = null;
|
|
Reset();
|
|
input = value;
|
|
}
|
|
}
|
|
|
|
public override string SourceName {
|
|
get {
|
|
return input.SourceName;
|
|
}
|
|
}
|
|
|
|
/** <summary>
|
|
* Currently does not support multiple emits per nextToken invocation
|
|
* for efficiency reasons. Subclass and override this method and
|
|
* nextToken (to push tokens into a list and pull from that list rather
|
|
* than a single variable as this implementation does).
|
|
* </summary>
|
|
*/
|
|
public virtual void Emit(IToken token) {
|
|
state.token = token;
|
|
}
|
|
|
|
/** <summary>
|
|
* The standard method called to automatically emit a token at the
|
|
* outermost lexical rule. The token object should point into the
|
|
* char buffer start..stop. If there is a text override in 'text',
|
|
* use that to set the token's text. Override this method to emit
|
|
* custom Token objects.
|
|
* </summary>
|
|
*
|
|
* <remarks>
|
|
* If you are building trees, then you should also override
|
|
* Parser or TreeParser.getMissingSymbol().
|
|
* </remarks>
|
|
*/
|
|
public virtual IToken Emit() {
|
|
IToken t = new CommonToken(input, state.type, state.channel, state.tokenStartCharIndex, CharIndex - 1);
|
|
t.Line = state.tokenStartLine;
|
|
t.Text = state.text;
|
|
t.CharPositionInLine = state.tokenStartCharPositionInLine;
|
|
Emit(t);
|
|
return t;
|
|
}
|
|
|
|
public virtual void Match(string s) {
|
|
int i = 0;
|
|
while (i < s.Length) {
|
|
if (input.LA(1) != s[i]) {
|
|
if (state.backtracking > 0) {
|
|
state.failed = true;
|
|
return;
|
|
}
|
|
MismatchedTokenException mte = new MismatchedTokenException(s[i], input, TokenNames);
|
|
Recover(mte);
|
|
throw mte;
|
|
}
|
|
i++;
|
|
input.Consume();
|
|
state.failed = false;
|
|
}
|
|
}
|
|
|
|
public virtual void MatchAny() {
|
|
input.Consume();
|
|
}
|
|
|
|
public virtual void Match(int c) {
|
|
if (input.LA(1) != c) {
|
|
if (state.backtracking > 0) {
|
|
state.failed = true;
|
|
return;
|
|
}
|
|
MismatchedTokenException mte = new MismatchedTokenException(c, input, TokenNames);
|
|
Recover(mte); // don't really recover; just consume in lexer
|
|
throw mte;
|
|
}
|
|
input.Consume();
|
|
state.failed = false;
|
|
}
|
|
|
|
public virtual void MatchRange(int a, int b) {
|
|
if (input.LA(1) < a || input.LA(1) > b) {
|
|
if (state.backtracking > 0) {
|
|
state.failed = true;
|
|
return;
|
|
}
|
|
MismatchedRangeException mre = new MismatchedRangeException(a, b, input);
|
|
Recover(mre);
|
|
throw mre;
|
|
}
|
|
input.Consume();
|
|
state.failed = false;
|
|
}
|
|
|
|
/** <summary>What is the index of the current character of lookahead?</summary> */
|
|
public virtual int CharIndex {
|
|
get {
|
|
return input.Index;
|
|
}
|
|
}
|
|
|
|
public override void ReportError(RecognitionException e) {
|
|
/** TODO: not thought about recovery in lexer yet.
|
|
*
|
|
// if we've already reported an error and have not matched a token
|
|
// yet successfully, don't report any errors.
|
|
if ( errorRecovery ) {
|
|
//System.err.print("[SPURIOUS] ");
|
|
return;
|
|
}
|
|
errorRecovery = true;
|
|
*/
|
|
|
|
DisplayRecognitionError(this.TokenNames, e);
|
|
}
|
|
|
|
public override string GetErrorMessage(RecognitionException e, string[] tokenNames) {
|
|
string msg = null;
|
|
if (e is MismatchedTokenException) {
|
|
MismatchedTokenException mte = (MismatchedTokenException)e;
|
|
msg = "mismatched character " + GetCharErrorDisplay(e.Character) + " expecting " + GetCharErrorDisplay(mte.Expecting);
|
|
} else if (e is NoViableAltException) {
|
|
NoViableAltException nvae = (NoViableAltException)e;
|
|
// for development, can add "decision=<<"+nvae.grammarDecisionDescription+">>"
|
|
// and "(decision="+nvae.decisionNumber+") and
|
|
// "state "+nvae.stateNumber
|
|
msg = "no viable alternative at character " + GetCharErrorDisplay(e.Character);
|
|
} else if (e is EarlyExitException) {
|
|
EarlyExitException eee = (EarlyExitException)e;
|
|
// for development, can add "(decision="+eee.decisionNumber+")"
|
|
msg = "required (...)+ loop did not match anything at character " + GetCharErrorDisplay(e.Character);
|
|
} else if (e is MismatchedNotSetException) {
|
|
MismatchedNotSetException mse = (MismatchedNotSetException)e;
|
|
msg = "mismatched character " + GetCharErrorDisplay(e.Character) + " expecting set " + mse.Expecting;
|
|
} else if (e is MismatchedSetException) {
|
|
MismatchedSetException mse = (MismatchedSetException)e;
|
|
msg = "mismatched character " + GetCharErrorDisplay(e.Character) + " expecting set " + mse.Expecting;
|
|
} else if (e is MismatchedRangeException) {
|
|
MismatchedRangeException mre = (MismatchedRangeException)e;
|
|
msg = "mismatched character " + GetCharErrorDisplay(e.Character) + " expecting set " +
|
|
GetCharErrorDisplay(mre.A) + ".." + GetCharErrorDisplay(mre.B);
|
|
} else {
|
|
msg = base.GetErrorMessage(e, tokenNames);
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
public virtual string GetCharErrorDisplay(int c) {
|
|
string s = ((char)c).ToString();
|
|
switch (c) {
|
|
case TokenTypes.EndOfFile:
|
|
s = "<EOF>";
|
|
break;
|
|
case '\n':
|
|
s = "\\n";
|
|
break;
|
|
case '\t':
|
|
s = "\\t";
|
|
break;
|
|
case '\r':
|
|
s = "\\r";
|
|
break;
|
|
}
|
|
return "'" + s + "'";
|
|
}
|
|
|
|
/** <summary>
|
|
* Lexers can normally match any char in it's vocabulary after matching
|
|
* a token, so do the easy thing and just kill a character and hope
|
|
* it all works out. You can instead use the rule invocation stack
|
|
* to do sophisticated error recovery if you are in a fragment rule.
|
|
* </summary>
|
|
*/
|
|
public virtual void Recover(RecognitionException re) {
|
|
//System.out.println("consuming char "+(char)input.LA(1)+" during recovery");
|
|
//re.printStackTrace();
|
|
input.Consume();
|
|
}
|
|
|
|
[Conditional("ANTLR_TRACE")]
|
|
public virtual void TraceIn(string ruleName, int ruleIndex) {
|
|
string inputSymbol = ((char)input.LT(1)) + " line=" + Line + ":" + CharPositionInLine;
|
|
base.TraceIn(ruleName, ruleIndex, inputSymbol);
|
|
}
|
|
|
|
[Conditional("ANTLR_TRACE")]
|
|
public virtual void TraceOut(string ruleName, int ruleIndex) {
|
|
string inputSymbol = ((char)input.LT(1)) + " line=" + Line + ":" + CharPositionInLine;
|
|
base.TraceOut(ruleName, ruleIndex, inputSymbol);
|
|
}
|
|
}
|
|
}
|