private static void SkipWhitespaceAndComments(CodeReader cr) { bool isStillWhiteSpace = true; while (isStillWhiteSpace) { while (!cr.EOF && char.IsWhiteSpace(cr.PeekChar())) { cr.AdvancePosition(); } if (!cr.EOF && cr.PeekChar() == '/') { char ahead = cr.PeekCharAhead(); if (ahead != '\0') { switch (ahead) { case '/': cr.AdvancePosition(2); while (!cr.EOF && !cr.PeekChar().In('\n', '\r')) { cr.AdvancePosition(); } break; case '*': cr.AdvancePosition(2); while (!cr.EOF && cr.PeekChar() != '*' && cr.PeekCharAhead() != '/') { cr.AdvancePosition(); } if (!cr.EOF) { cr.AdvancePosition(2); } break; default: // Ran into a non-comment; whitespace must be over isStillWhiteSpace = false; break; } } else { // EOF ahead isStillWhiteSpace = false; } } else { // Ran into a non-comment; whitespace must be over isStillWhiteSpace = false; } } }
private static Token ReadToken(CodeReader cr, bool gms2) { SkipWhitespaceAndComments(cr); if (cr.EOF) { return(new Token(Token.TokenKind.EOF)); } if (cr.PeekChar() == '#') { // Skip preprocessor directive/macro/etc. Maybe support could be added later... but not yet. while (!cr.EOF) { if (cr.PeekChar() == '\n') { break; } cr.AdvancePosition(); } } char c = cr.PeekChar(); if (!char.IsLetter(c) && c != '_') { // Numbers/hex if (c == '$' || (c == '0' && cr.PeekCharAhead() == 'x')) { return(ReadHexLiteral(cr)); } if (char.IsDigit(c) || (c == '.' && char.IsDigit(cr.PeekCharAhead()))) { return(ReadNumberLiteral(cr)); } // Strings if (gms2) { if (c == '@') { char c2 = cr.PeekCharAhead(); if (c2 == '"' || c2 == '\'') { cr.AdvancePosition(); return(ReadStringLiteralNoEscape(cr)); } } else if (c == '"') { return(ReadStringLiteral(cr)); } } else { if (c == '"' || c == '\'') { return(ReadStringLiteral(cr)); } } // Operators/everything else switch (c) { case '{': cr.AdvancePosition(); return(new Token(Token.TokenKind.OpenBlock, cr.GetPositionInfo(cr.Position - 1))); case '}': cr.AdvancePosition(); return(new Token(Token.TokenKind.CloseBlock, cr.GetPositionInfo(cr.Position - 1))); case '(': cr.AdvancePosition(); return(new Token(Token.TokenKind.OpenParen, cr.GetPositionInfo(cr.Position - 1))); case ')': cr.AdvancePosition(); return(new Token(Token.TokenKind.CloseParen, cr.GetPositionInfo(cr.Position - 1))); case '|': { char ahead = cr.PeekCharAhead(); if (ahead != '\0') { switch (ahead) { case '|': cr.AdvancePosition(2); return(new Token(Token.TokenKind.LogicalOr, cr.GetPositionInfo(cr.Position - 2))); case '=': cr.AdvancePosition(2); return(new Token(Token.TokenKind.AssignOr, cr.GetPositionInfo(cr.Position - 2))); } } } cr.AdvancePosition(); return(new Token(Token.TokenKind.BitwiseOr, cr.GetPositionInfo(cr.Position - 1))); case '^': { char ahead = cr.PeekCharAhead(); if (ahead != '\0') { switch (ahead) { case '^': cr.AdvancePosition(2); return(new Token(Token.TokenKind.LogicalXor, cr.GetPositionInfo(cr.Position - 2))); case '=': cr.AdvancePosition(2); return(new Token(Token.TokenKind.AssignXor, cr.GetPositionInfo(cr.Position - 2))); } } } cr.AdvancePosition(); return(new Token(Token.TokenKind.BitwiseXor, cr.GetPositionInfo(cr.Position - 1))); case '&': { char ahead = cr.PeekCharAhead(); if (ahead != '\0') { switch (ahead) { case '&': cr.AdvancePosition(2); return(new Token(Token.TokenKind.LogicalAnd, cr.GetPositionInfo(cr.Position - 2))); case '=': cr.AdvancePosition(2); return(new Token(Token.TokenKind.AssignAnd, cr.GetPositionInfo(cr.Position - 2))); } } } cr.AdvancePosition(); return(new Token(Token.TokenKind.BitwiseAnd, cr.GetPositionInfo(cr.Position - 1))); case '%': { if (cr.PeekCharAhead() == '=') { cr.AdvancePosition(2); return(new Token(Token.TokenKind.AssignMod, cr.GetPositionInfo(cr.Position - 2))); } } cr.AdvancePosition(); return(new Token(Token.TokenKind.Mod, cr.GetPositionInfo(cr.Position - 1))); case '~': cr.AdvancePosition(); return(new Token(Token.TokenKind.BitwiseNegate, cr.GetPositionInfo(cr.Position - 1))); case '!': cr.AdvancePosition(); if (cr.PeekChar() == '=') { cr.AdvancePosition(); return(new Token(Token.TokenKind.CompareNotEqual, cr.GetPositionInfo(cr.Position - 2))); } return(new Token(Token.TokenKind.Not, cr.GetPositionInfo(cr.Position - 1))); case '[': { char ahead = cr.PeekCharAhead(); if (ahead != '\0') { switch (ahead) { case '?': cr.AdvancePosition(2); return(new Token(Token.TokenKind.OpenArrayMap, cr.GetPositionInfo(cr.Position - 2))); case '@': cr.AdvancePosition(2); return(new Token(Token.TokenKind.OpenArrayBaseArray, cr.GetPositionInfo(cr.Position - 2))); case '#': cr.AdvancePosition(2); return(new Token(Token.TokenKind.OpenArrayGrid, cr.GetPositionInfo(cr.Position - 2))); case '|': cr.AdvancePosition(2); return(new Token(Token.TokenKind.OpenArrayList, cr.GetPositionInfo(cr.Position - 2))); } } } cr.AdvancePosition(); return(new Token(Token.TokenKind.OpenArray, cr.GetPositionInfo(cr.Position - 1))); case ']': cr.AdvancePosition(); return(new Token(Token.TokenKind.CloseArray, cr.GetPositionInfo(cr.Position - 1))); case '*': cr.AdvancePosition(); if (cr.PeekChar() == '=') { cr.AdvancePosition(); return(new Token(Token.TokenKind.AssignTimes, cr.GetPositionInfo(cr.Position - 2))); } return(new Token(Token.TokenKind.Times, cr.GetPositionInfo(cr.Position - 1))); case '/': cr.AdvancePosition(); if (cr.PeekChar() == '=') { cr.AdvancePosition(); return(new Token(Token.TokenKind.AssignDivide, cr.GetPositionInfo(cr.Position - 2))); } return(new Token(Token.TokenKind.Divide, cr.GetPositionInfo(cr.Position - 1))); case '+': cr.AdvancePosition(); { char next = cr.PeekChar(); if (next == '=') { cr.AdvancePosition(); return(new Token(Token.TokenKind.AssignPlus, cr.GetPositionInfo(cr.Position - 2))); } if (next == '+') { cr.AdvancePosition(); return(new Token(Token.TokenKind.Increment, cr.GetPositionInfo(cr.Position - 2))); } } return(new Token(Token.TokenKind.Plus, cr.GetPositionInfo(cr.Position - 1))); case '-': cr.AdvancePosition(); { char next = cr.PeekChar(); if (next == '=') { cr.AdvancePosition(); return(new Token(Token.TokenKind.AssignMinus, cr.GetPositionInfo(cr.Position - 2))); } if (next == '-') { cr.AdvancePosition(); return(new Token(Token.TokenKind.Decrement, cr.GetPositionInfo(cr.Position - 2))); } } return(new Token(Token.TokenKind.Minus, cr.GetPositionInfo(cr.Position - 1))); // converted to negate later if necessary case ',': cr.AdvancePosition(); return(new Token(Token.TokenKind.Comma, cr.GetPositionInfo(cr.Position - 1))); case '.': cr.AdvancePosition(); return(new Token(Token.TokenKind.Dot, cr.GetPositionInfo(cr.Position - 1))); case ':': cr.AdvancePosition(); if (cr.PeekChar() == '=') { cr.AdvancePosition(); return(new Token(Token.TokenKind.Assign, ":=", cr.GetPositionInfo(cr.Position - 2))); // Apparently this exists } return(new Token(Token.TokenKind.Colon, cr.GetPositionInfo(cr.Position - 1))); case ';': cr.AdvancePosition(); return(new Token(Token.TokenKind.EndStatement, cr.GetPositionInfo(cr.Position - 1))); case '=': cr.AdvancePosition(); if (cr.PeekChar() == '=') { cr.AdvancePosition(); return(new Token(Token.TokenKind.CompareEqual, cr.GetPositionInfo(cr.Position - 2))); } return(new Token(Token.TokenKind.Assign, "=", cr.GetPositionInfo(cr.Position - 1))); case '<': { char ahead = cr.PeekCharAhead(); if (ahead != '\0') { switch (ahead) { case '<': cr.AdvancePosition(2); return(new Token(Token.TokenKind.BitwiseShiftLeft, cr.GetPositionInfo(cr.Position - 2))); case '=': cr.AdvancePosition(2); return(new Token(Token.TokenKind.CompareLessEqual, cr.GetPositionInfo(cr.Position - 2))); case '>': cr.AdvancePosition(2); return(new Token(Token.TokenKind.CompareNotEqual, cr.GetPositionInfo(cr.Position - 2))); // <> exists, it's just legacy } } } cr.AdvancePosition(); return(new Token(Token.TokenKind.CompareLess, cr.GetPositionInfo(cr.Position - 1))); case '>': { char ahead = cr.PeekCharAhead(); if (ahead != '\0') { switch (ahead) { case '>': cr.AdvancePosition(2); return(new Token(Token.TokenKind.BitwiseShiftRight, cr.GetPositionInfo(cr.Position - 2))); case '=': cr.AdvancePosition(2); return(new Token(Token.TokenKind.CompareGreaterEqual, cr.GetPositionInfo(cr.Position - 2))); } } } cr.AdvancePosition(); return(new Token(Token.TokenKind.CompareGreater, cr.GetPositionInfo(cr.Position - 1))); case '?': cr.AdvancePosition(); return(new Token(Token.TokenKind.Conditional, cr.GetPositionInfo(cr.Position - 1))); default: cr.AdvancePosition(); return(new Token(Token.TokenKind.Error, c.ToString(), cr.GetPositionInfo(cr.Position - 1))); } } else { // Identifier return(ReadIdentifier(cr)); } }
private static Token ReadStringLiteral(CodeReader cr) { StringBuilder sb = new StringBuilder(); int index = cr.Position; char type = cr.PeekChar(); cr.AdvancePosition(); char c = cr.PeekChar(); char c2 = cr.PeekCharAhead(); while (!cr.EOF) { switch (c) { // Escape character case '\\': if (cr.compileContext.Data?.IsGameMaker2() == false) { sb.Append(c); cr.AdvancePosition(); c = cr.PeekChar(); c2 = cr.PeekCharAhead(); continue; } cr.AdvancePosition(); c = cr.PeekChar(); c2 = cr.PeekCharAhead(); switch (c) { case 'a': sb.Append('\a'); break; case 'n': sb.Append('\n'); break; case 'r': sb.Append('\r'); break; case 't': sb.Append('\t'); break; case 'v': sb.Append('\v'); break; case 'f': sb.Append('\f'); break; case 'b': sb.Append('\b'); break; case '\n': break; case 'u': { int calc = 0; for (int i = 0; i < 4 && (IsHexCharacter(c2)); i++) { calc *= 16; calc = (!char.IsDigit(c2)) ? (calc + char.ToLower(c2) - 87) : (calc + c2 - 48); cr.AdvancePosition(); c = cr.PeekChar(); c2 = cr.PeekCharAhead(); } sb.Append((char)(ushort)calc); } break; case 'x': cr.AdvancePosition(); c = cr.PeekChar(); c2 = cr.PeekCharAhead(); if (IsHexCharacter(c)) { char[] arr = new char[2] { c, ' ' }; for (int i = 1; i < 2; i++) { if (!IsHexCharacter(c2)) { break; } c = cr.PeekChar(); c2 = cr.PeekCharAhead(); arr[i] = c; } char value = (char)Convert.ToInt32(new string(arr), 16); sb.Append(value); } break; default: if (c >= '0' && c <= '7') { // Octal StringBuilder sb2 = new StringBuilder(); sb2.Append(c); for (int i = 1; i < 3; i++) { if (c2 < '0' || c > '7') { break; } cr.AdvancePosition(); c = cr.PeekChar(); if (c == '\0') { c = ' '; } c2 = cr.PeekCharAhead(); if (c2 == '\0') { c2 = ' '; } sb2.Append(c); } sb.Append(Convert.ToInt32(sb2.ToString(), 8)); } else { sb.Append(c); } break; } cr.AdvancePosition(); c = cr.PeekChar(); c2 = cr.PeekCharAhead(); continue; default: if (c == type) { break; } sb.Append(c); cr.AdvancePosition(); c = cr.PeekChar(); c2 = cr.PeekCharAhead(); continue; } break; } cr.AdvancePosition(); return(new Token(Token.TokenKind.String, sb.ToString(), cr.GetPositionInfo(index))); }