public string Markup(string code, out int outdentThis, out int indentNext) { var sb = new StringBuilder(); var lexer = new Lexer(code); int parenDepth = 0, squareDepth = 0; outdentThis = indentNext = 0; bool statementStart = true; bool ifStatement = false; bool justSawThen = false; while (!lexer.AtEnd) { int start = lexer.position; // grab whitespace (normally skipped by the lexer) if (Lexer.IsWhitespace(code[lexer.position])) { while (!lexer.AtEnd && Lexer.IsWhitespace(code[lexer.position])) { lexer.position++; } sb.Append(code.Substring(start, lexer.position - start)); if (lexer.AtEnd) { break; } start = lexer.position; } // also check for a comment (which the lexer would also skip over) if (lexer.position < code.Length - 2 && code[lexer.position] == '/' && code[lexer.position + 1] == '/') { while (!lexer.AtEnd && code[lexer.position] != '\n') { lexer.position++; } sb.Append(comment.startTags); sb.Append(code.Substring(start, lexer.position - start)); sb.Append(comment.endTags); if (lexer.AtEnd) { break; } start = lexer.position; } // now, grab and process the next token (being sure to catch and handle lexer exceptions) Token tok = null; try { tok = lexer.Dequeue(); } catch (LexerException exc) { tok = new Token(); lexer.position = code.Length; } if (tok.text == "self") { tok.type = Token.Type.Keyword; // (special case) } if (justSawThen && tok.type != Token.Type.Comment && tok.type != Token.Type.EOL) { // If anything (other than a comment) comes after "then", then this // is a single-line if, and should not indent the next line. indentNext = 0; } justSawThen = false; switch (tok.type) { case Token.Type.Keyword: sb.Append(keyword.startTags); sb.Append(tok.text); sb.Append(keyword.endTags); // Styling's done, but also figure out how this keyword changes indentation. if (statementStart) { if (tok.text == "if") { indentNext++; ifStatement = true; } else { ifStatement = false; } if (tok.text == "while" || tok.text == "for" || tok.text == "else" || tok.text == "else if") { indentNext++; } if (tok.text.StartsWith("end ") || tok.text == "else" || tok.text == "else if") { outdentThis++; } } else { if (tok.text == "function") { indentNext++; } if (tok.text == "then") { justSawThen = true; } } break; case Token.Type.Colon: sb.Append(colon.startTags); sb.Append(":"); sb.Append(colon.endTags); statementStart = true; break; case Token.Type.Identifier: sb.Append(identifier.startTags); sb.Append(tok.text); sb.Append(identifier.endTags); break; case Token.Type.String: sb.Append(stringLiteral.startTags); sb.Append("\""); // (note that lexer strips the surrounding quotes) sb.Append(tok.text.Replace("\"", "\"\"")); // and un-doubles internal quotes sb.Append("\""); sb.Append(stringLiteral.endTags); break; case Token.Type.Number: sb.Append(numericLiteral.startTags); sb.Append(tok.text); sb.Append(numericLiteral.endTags); break; case Token.Type.LParen: case Token.Type.RParen: if (tok.type == Token.Type.LParen) { parenDepth++; } if (rotatingParenColors) { float h, s, v; Color.RGBToHSV(baseParenColor, out h, out s, out v); h = Mathf.Repeat(h + 0.22f * (parenDepth - 1), 1); Color color = Color.HSVToRGB(h, s, v); if (parenDepth < 1) { color = Color.red; } sb.Append("<color=#"); sb.Append(ColorUtility.ToHtmlStringRGB(color)); sb.Append(tok.type == Token.Type.LParen ? ">(</color>" : ">)</color>"); } else { sb.Append(tok.type == Token.Type.LParen ? ">(</color>" : ">)</color>"); } if (tok.type == Token.Type.RParen) { parenDepth--; } break; case Token.Type.LSquare: case Token.Type.RSquare: if (tok.type == Token.Type.LSquare) { squareDepth++; } if (rotatingSquareColors) { float h, s, v; Color.RGBToHSV(baseSquareColor, out h, out s, out v); h = Mathf.Repeat(h + 0.22f * (squareDepth - 1), 1); Color color = Color.HSVToRGB(h, s, v); if (squareDepth < 1) { color = Color.red; } sb.Append("<color=#"); sb.Append(ColorUtility.ToHtmlStringRGB(color)); sb.Append(tok.type == Token.Type.LSquare ? ">[</color>" : ">]</color>"); } else { sb.Append(tok.type == Token.Type.LSquare ? ">[</color>" : ">]</color>"); } if (tok.type == Token.Type.RSquare) { squareDepth--; } break; case Token.Type.Unknown: if (code[start] == '"') { sb.Append(openString.startTags); sb.Append(code.Substring(start, lexer.position - start)); sb.Append(openString.endTags); } else { sb.Append(code.Substring(start, lexer.position - start)); } break; default: sb.Append(operators.startTags); sb.Append(code.Substring(start, lexer.position - start)); sb.Append(operators.endTags); break; } statementStart = false; } return(sb.ToString()); }