using System.Collections.Generic;
using System.Globalization;
using System.Diagnostics;
+using System.Collections;
namespace Mono.CSharp
{
}
//
- // This class has to be used in the parser only, it reuses token
- // details after each parse
+ // This class has to be used by parser only, it reuses token
+ // details after each file parse completion
//
public class LocatedToken
{
- int row, column;
- string value;
+ public int row, column;
+ public string value;
+ public SourceFile file;
- static LocatedToken[] buffer = new LocatedToken[0];
- static int pos;
-
- private LocatedToken ()
+ public LocatedToken ()
{
}
- public static LocatedToken Create (int row, int column)
+ public LocatedToken (string value, Location loc)
{
- return Create (null, row, column);
+ this.value = value;
+ file = loc.SourceFile;
+ row = loc.Row;
+ column = loc.Column;
}
- public static LocatedToken Create (string value, Location loc)
+ public override string ToString ()
{
- return Create (value, loc.Row, loc.Column);
+ return string.Format ("Token '{0}' at {1},{2}", Value, row, column);
}
- public static LocatedToken Create (string value, int row, int column)
+ public Location Location {
+ get { return new Location (file, row, column); }
+ }
+
+ public string Value {
+ get { return value; }
+ }
+ }
+
+ public class LocatedTokenBuffer
+ {
+ readonly LocatedToken[] buffer;
+ public int pos;
+
+ public LocatedTokenBuffer ()
+ {
+ buffer = new LocatedToken[0];
+ }
+
+ public LocatedTokenBuffer (LocatedToken[] buffer)
+ {
+ this.buffer = buffer ?? new LocatedToken[0];
+ }
+
+ public LocatedToken Create (SourceFile file, int row, int column)
+ {
+ return Create (null, file, row, column);
+ }
+
+ public LocatedToken Create (string value, SourceFile file, int row, int column)
{
//
// TODO: I am not very happy about the logic but it's the best
if (pos >= buffer.Length) {
entry = new LocatedToken ();
} else {
- entry = buffer [pos];
+ entry = buffer[pos];
if (entry == null) {
entry = new LocatedToken ();
- buffer [pos] = entry;
+ buffer[pos] = entry;
}
++pos;
}
entry.value = value;
+ entry.file = file;
entry.row = row;
entry.column = column;
return entry;
// Used for token not required by expression evaluator
//
[Conditional ("FULL_AST")]
- public static void CreateOptional (int row, int col, ref object token)
+ public void CreateOptional (SourceFile file, int row, int col, ref object token)
{
- token = Create (row, col);
- }
-
- public static void Initialize ()
- {
-#if !FULL_AST
- if (buffer.Length == 0)
- buffer = new LocatedToken [15000];
-#endif
- pos = 0;
- }
-
- public Location Location {
- get { return new Location (row, column); }
- }
-
- public string Value {
- get { return value; }
+ token = Create (file, row, col);
}
}
List<Location> escaped_identifiers;
int parsing_generic_less_than;
readonly bool doc_processing;
+ readonly LocatedTokenBuffer ltb;
//
// Used mainly for parser optimizations. Some expressions for instance
//
Stack<int> ifstack;
- const int max_id_size = 512;
- const int max_number_size = 512;
-
-#if FULL_AST
- readonly char [] id_builder = new char [max_id_size];
+ public const int MaxIdentifierLength = 512;
+ public const int MaxNumberLength = 512;
- Dictionary<char[], string>[] identifiers = new Dictionary<char[], string>[max_id_size + 1];
-
- char [] number_builder = new char [max_number_size];
+ readonly char[] id_builder;
+ readonly Dictionary<char[], string>[] identifiers;
+ readonly char[] number_builder;
int number_pos;
- char[] value_builder = new char[256];
-#else
- static readonly char [] id_builder = new char [max_id_size];
-
- static Dictionary<char[], string>[] identifiers = new Dictionary<char[], string>[max_id_size + 1];
-
- static char [] number_builder = new char [max_number_size];
- static int number_pos;
-
- static char[] value_builder = new char[256];
-#endif
+ char[] value_builder = new char[64];
public int Line {
get {
}
}
- public Tokenizer (SeekableStreamReader input, CompilationSourceFile file)
+ public Tokenizer (SeekableStreamReader input, CompilationSourceFile file, ParserSession session)
{
this.source_file = file;
this.context = file.Compiler;
this.current_source = file.SourceFile;
+ this.identifiers = session.Identifiers;
+ this.id_builder = session.IDBuilder;
+ this.number_builder = session.NumberBuilder;
+ this.ltb = new LocatedTokenBuffer (session.LocatedTokens);
reader = input;
doc_processing = context.Settings.DocumentationFile != null;
tab_size = context.Settings.TabSize;
-
- Mono.CSharp.Location.Push (current_source);
}
public void PushPosition ()
PushPosition ();
xtoken ();
if (xtoken () != Token.ARROW)
- res = -1;
+ goto default;
PopPosition ();
break;
default:
+ // peek_token could overwrite id_buffer
+ id_builder [0] = 'a'; id_builder [1] = 's'; id_builder [2] = 'y'; id_builder [3] = 'n'; id_builder [4] = 'c';
res = -1;
break;
}
public Location Location {
get {
- return new Location (ref_line, col);
+ return new Location (current_source, ref_line, col);
}
}
start:
int the_token = token ();
if (the_token == Token.OPEN_BRACKET) {
- do {
+ while (true) {
the_token = token ();
- } while (the_token != Token.CLOSE_BRACKET);
+ if (the_token == Token.EOF)
+ return true;
+
+ if (the_token == Token.CLOSE_BRACKET)
+ break;
+ }
the_token = token ();
} else if (the_token == Token.IN || the_token == Token.OUT) {
the_token = token ();
case Token.OPEN_BRACKET:
case Token.OP_GENERICS_GT:
case Token.INTERR:
+ case Token.OP_COALESCING:
next_token = Token.INTERR_NULLABLE;
break;
int ntoken;
int interrs = 1;
int colons = 0;
+ int braces = 0;
//
// All shorcuts failed, do it hard way
//
while ((ntoken = xtoken ()) != Token.EOF) {
+ if (ntoken == Token.OPEN_BRACE) {
+ ++braces;
+ continue;
+ }
+
+ if (ntoken == Token.CLOSE_BRACE) {
+ --braces;
+ continue;
+ }
+
+ if (braces != 0)
+ continue;
+
if (ntoken == Token.SEMICOLON)
break;
}
}
- next_token = colons != interrs ? Token.INTERR_NULLABLE : Token.INTERR;
+ next_token = colons != interrs && braces == 0 ? Token.INTERR_NULLABLE : Token.INTERR;
break;
}
}
bool seen_digits = false;
if (c != -1){
- if (number_pos == max_number_size)
+ if (number_pos == MaxNumberLength)
Error_NumericConstantTooLong ();
number_builder [number_pos++] = (char) c;
}
//
while ((d = peek_char2 ()) != -1){
if (d >= '0' && d <= '9'){
- if (number_pos == max_number_size)
+ if (number_pos == MaxNumberLength)
Error_NumericConstantTooLong ();
number_builder [number_pos++] = (char) d;
get_char ();
if (c == 'e' || c == 'E'){
is_real = true;
- if (number_pos == max_number_size)
+ if (number_pos == MaxNumberLength)
Error_NumericConstantTooLong ();
number_builder [number_pos++] = (char) c;
c = get_char ();
if (c == '+'){
- if (number_pos == max_number_size)
+ if (number_pos == MaxNumberLength)
Error_NumericConstantTooLong ();
number_builder [number_pos++] = '+';
c = -1;
} else if (c == '-') {
- if (number_pos == max_number_size)
+ if (number_pos == MaxNumberLength)
Error_NumericConstantTooLong ();
number_builder [number_pos++] = '-';
c = -1;
} else {
- if (number_pos == max_number_size)
+ if (number_pos == MaxNumberLength)
Error_NumericConstantTooLong ();
number_builder [number_pos++] = '+';
}
public void putback (int c)
{
- if (putback_char != -1){
- Console.WriteLine ("Col: " + col);
- Console.WriteLine ("Row: " + line);
- Console.WriteLine ("Name: " + current_source.Name);
- Console.WriteLine ("Current [{0}] putting back [{1}] ", putback_char, c);
- throw new Exception ("This should not happen putback on putback");
+ if (putback_char != -1) {
+ throw new InternalErrorException (string.Format ("Secondary putback [{0}] putting back [{1}] is not allowed", (char)putback_char, (char) c), Location);
}
+
if (c == '\n' || col == 0) {
// It won't happen though.
line--;
}
if (pos != 0) {
- if (pos > max_id_size)
+ if (pos > MaxIdentifierLength)
arg = new string (value_builder, 0, pos);
else
arg = InternIdentifier (value_builder, pos);
}
ref_line = line;
- Location.Push (current_source);
return true;
}
if (new_file_name != null) {
current_source = context.LookupFile (source_file, new_file_name);
source_file.AddIncludeFile (current_source);
- Location.Push (current_source);
}
if (!hidden_block_start.IsNull) {
//
// The syntax is ` "foo.txt" "{guid}" "hash"'
//
+ // guid is predefined hash algorithm guid {406ea660-64cf-4c82-b6f0-42d48172a799} for md5
+ //
int c = get_char ();
if (c != '"')
// Any length of checksum
List<byte> checksum_bytes = new List<byte> (16);
+ var checksum_location = Location;
c = peek_char ();
while (c != '"' && c != -1) {
checksum_bytes.Add (read_hex (out error));
return false;
}
- file.SetChecksum (guid_bytes, checksum_bytes.ToArray ());
- current_source.AutoGenerated = true;
+ if (context.Settings.GenerateDebugInfo) {
+ var chsum = checksum_bytes.ToArray ();
+
+ if (file.HasChecksum) {
+ if (!ArrayComparer.IsEqual (file.Checksum, chsum)) {
+ // TODO: Report.SymbolRelatedToPreviousError
+ Report.Warning (1697, 1, checksum_location, "Different checksum values specified for file `{0}'", file.Name);
+ }
+ }
+
+ file.SetChecksum (guid_bytes, chsum);
+ current_source.AutoGenerated = true;
+ }
+
return true;
}
-#if !FULL_AST
- static
-#endif
bool IsTokenIdentifierEqual (char[] identifier)
{
for (int i = 0; i < identifier.Length; ++i) {
}
Report.Warning (1634, 1, Location, "Expected disable or restore");
+
+ // Eat any remaining characters on the line
+ while (c != '\n' && c != -1)
+ c = get_char ();
+
return;
}
#endif
while (true){
- c = get_char ();
+ // Cannot use get_char because of \r in quoted strings
+ if (putback_char != -1) {
+ c = putback_char;
+ putback_char = -1;
+ } else {
+ c = reader.Read ();
+ }
+
if (c == '"') {
+ ++col;
+
if (quoted && peek_char () == '"') {
if (pos == value_builder.Length)
Array.Resize (ref value_builder, pos * 2);
if (c == '\n') {
if (!quoted) {
Report.Error (1010, Location, "Newline in constant");
+
+ advance_line ();
+
+ // Don't add \r to string literal
+ if (pos > 1 && value_builder [pos - 1] == '\r')
+ --pos;
+
val = new StringLiteral (context.BuiltinTypes, new string (value_builder, 0, pos), start_location);
return Token.LITERAL;
}
+
+ advance_line ();
} else if (c == '\\' && !quoted) {
int surrogate;
c = escape (c, out surrogate);
} else if (c == -1) {
Report.Error (1039, Location, "Unterminated string literal");
return Token.EOF;
+ } else {
+ ++col;
}
if (pos == value_builder.Length)
if (id_builder [0] >= '_' && !quoted) {
int keyword = GetKeyword (id_builder, pos);
if (keyword != -1) {
- val = LocatedToken.Create (keyword == Token.AWAIT ? "await" : null, ref_line, column);
+ val = ltb.Create (keyword == Token.AWAIT ? "await" : null, current_source, ref_line, column);
return keyword;
}
}
string s = InternIdentifier (id_builder, pos);
- val = LocatedToken.Create (s, ref_line, column);
+ val = ltb.Create (s, current_source, ref_line, column);
if (quoted && parsing_attribute_section)
AddEscapedIdentifier (((LocatedToken) val).Location);
return Token.IDENTIFIER;
}
-#if !FULL_AST
- static
-#endif
string InternIdentifier (char[] charBuffer, int length)
{
//
return consume_identifier (c);
case '{':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
return Token.OPEN_BRACE;
case '}':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
return Token.CLOSE_BRACE;
case '[':
// To block doccomment inside attribute declaration.
if (doc_state == XmlCommentState.Allowed)
doc_state = XmlCommentState.NotAllowed;
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
if (parsing_block == 0 || lambda_arguments_parsing)
return Token.OPEN_BRACKET;
return Token.OPEN_BRACKET_EXPR;
}
case ']':
- LocatedToken.CreateOptional (ref_line, col, ref val);
+ ltb.CreateOptional (current_source, ref_line, col, ref val);
return Token.CLOSE_BRACKET;
case '(':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
//
// An expression versions of parens can appear in block context only
//
return Token.OPEN_PARENS;
case ')':
- LocatedToken.CreateOptional (ref_line, col, ref val);
+ ltb.CreateOptional (current_source, ref_line, col, ref val);
return Token.CLOSE_PARENS;
case ',':
- LocatedToken.CreateOptional (ref_line, col, ref val);
+ ltb.CreateOptional (current_source, ref_line, col, ref val);
return Token.COMMA;
case ';':
- LocatedToken.CreateOptional (ref_line, col, ref val);
+ ltb.CreateOptional (current_source, ref_line, col, ref val);
return Token.SEMICOLON;
case '~':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
return Token.TILDE;
case '?':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
return TokenizePossibleNullableType ();
case '<':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
if (parsing_generic_less_than++ > 0)
return Token.OP_GENERICS_LT;
return TokenizeLessThan ();
case '>':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
d = peek_char ();
if (d == '='){
return Token.OP_GT;
case '+':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
d = peek_char ();
if (d == '+') {
d = Token.OP_INC;
return d;
case '-':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
d = peek_char ();
if (d == '-') {
d = Token.OP_DEC;
return d;
case '!':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
if (peek_char () == '='){
get_char ();
return Token.OP_NE;
return Token.BANG;
case '=':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
d = peek_char ();
if (d == '='){
get_char ();
return Token.ASSIGN;
case '&':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
d = peek_char ();
if (d == '&'){
get_char ();
return Token.BITWISE_AND;
case '|':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
d = peek_char ();
if (d == '|'){
get_char ();
return Token.BITWISE_OR;
case '*':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
if (peek_char () == '='){
get_char ();
return Token.OP_MULT_ASSIGN;
case '/':
d = peek_char ();
if (d == '='){
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
get_char ();
return Token.OP_DIV_ASSIGN;
}
update_formatted_doc_comment (current_comment_start);
continue;
}
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
return Token.DIV;
case '%':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
if (peek_char () == '='){
get_char ();
return Token.OP_MOD_ASSIGN;
return Token.PERCENT;
case '^':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
if (peek_char () == '='){
get_char ();
return Token.OP_XOR_ASSIGN;
return Token.CARRET;
case ':':
- val = LocatedToken.Create (ref_line, col);
+ val = ltb.Create (current_source, ref_line, col);
if (peek_char () == ':') {
get_char ();
return Token.DOUBLE_COLON;
if (d >= '0' && d <= '9')
return is_number (c);
- LocatedToken.CreateOptional (ref_line, col, ref val);
+ ltb.CreateOptional (current_source, ref_line, col, ref val);
return Token.DOT;
case '#':