using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.ComponentModel; namespace Analyser { public class Location : Common { QSPGameCode m_gamecode; string m_name; int m_line; bool m_start; //Стартовая локация List location_codeline_text; Dictionary location_codeline_level; int currentLine; bool currentRoot; enum ControlBlockType { None, SL_If, SL_Else, SL_Act, ML_If, ML_Else, ML_Act, IfCondition, ElseIfCondition, ActCondition } struct ControlLine { public ControlBlockType controlBlock; public int line; } public Location(QSPGameCode code, string name, int line, bool start) { m_gamecode = code; m_name = name; m_line = line; m_start = start; location_codeline_text = null; location_codeline_level = null; } public string GetName() { return m_name; } public int GetLine() { return m_line; } public int GetCodeLinesCount() { return location_codeline_text.Count; } public void GetCodeLine(int lineNum, ref string text, ref int level) { //Нужно для утилиты форматирования int realNum = lineNum + 1; text = location_codeline_text[lineNum]; if (location_codeline_level.ContainsKey(realNum)) level = location_codeline_level[realNum]; else level = INVALID_INDEX; return; } new void SubmitError(string text, int line) { if (currentRoot) Common.SubmitError(text, line); else Common.SubmitError(text, currentLine); } new void SubmitWarning(string text, int line) { if (currentRoot) Common.SubmitWarning(text, line); else Common.SubmitWarning(text, currentLine); } bool qspCompileExprPushOpCode(ref int[] opStack, ref int opSp, ref int[] argStack, ref int argSp, int opCode) { if ((opSp == QSP_STACKSIZE - 1) || (argSp == QSP_STACKSIZE - 1)) { SubmitError("Quite complicated expression", currentLine); return false; } opStack[++opSp] = opCode; argStack[++argSp] = (opCode < (int)QspFunctionType.First_Function) ? qspOps[opCode].MinArgsCount : 0; return true; } bool qspAppendToCompiled(ref int itemsCount) { if (itemsCount == QSP_MAXITEMS) { SubmitError("Quite many arguments in the expression", currentLine); return false; } ++itemsCount; return true; } public bool ParseLocation(List code, bool root) { int quote = (int)QuoteType.None; if (root) { currentRoot = true; currentLine = 0; location_codeline_text = new List(code); location_codeline_level = new Dictionary(); } //Склеиваем строки List lineIndexes = new List(); List escapedLines = new List(); int linesCount = code.Count; int oldLine = 0; int curLine = 0; int curlyLevel = 0; string extraLine = ""; while (curLine < linesCount) { string line = code.ToArray()[curLine].Trim(WhiteSpace); int pos = 0; while (pos < line.Length) { char c = line[pos]; if (c == '\'') { //Апостроф if (quote == (int)QuoteType.None) { quote = (int)QuoteType.Single; } else if (quote == (int)QuoteType.Single) { if ((pos + 1 < line.Length) && (line[pos + 1] == '\'')) { //Экранированный апостроф pos++; } else { quote = (int)QuoteType.None; } } } else if (c == '"') { //Кавычка if (quote == (int)QuoteType.None) { quote = (int)QuoteType.Double; } else if (quote == (int)QuoteType.Double) { if ((pos + 1 < line.Length) && (line[pos + 1] == '"')) { //Экранированная кавычка pos++; } else { quote = (int)QuoteType.None; } } } else if (quote == (int)QuoteType.None) { if (c == '{') { curlyLevel++; } else if (c == '}') { if (curlyLevel == 0) { SubmitError("Closing curly brace without opening one", oldLine + 1); return false; } curlyLevel--; } } pos++; } if ((curlyLevel > 0) || (quote != (int)QuoteType.None) || line.EndsWith(" _")) { if (line.EndsWith(" _")) line = line.Substring(0, line.Length - 2); extraLine += line; if (curlyLevel > 0) extraLine += "\n";//Вставляем разделитель для строк, потом по нему "разобьем" строки if (curLine == linesCount - 1) { lineIndexes.Add(oldLine + 1); string escaped = extraLine + line; escapedLines.Add(escaped.Trim(WhiteSpace)); } } else { lineIndexes.Add(oldLine + 1); string escaped = extraLine + line; escapedLines.Add(escaped.Trim(WhiteSpace)); oldLine = curLine + 1; extraLine = ""; } curLine++; } //Проверка на незакрытые в конце локации многострочные строки if (quote != (int)QuoteType.None) { SubmitError("Unclosed string", oldLine + 1); return false; } //Проверка на незакрытые в конце локации фигурные скобки if (curlyLevel > 0) { SubmitError("Unclosed curly bracket", oldLine + 1); return false; } //TODO // FOR // перечисления asd|12|ddd //Разбираем код int lineNum = 0; int linePlainNum = 0; string block = ""; string quotedText = ""; int controlBlock = (int)ControlBlockType.None; Stack ControlStack = new Stack(); List blockGroup = new List(); List blockInGroupIsText = new List(); int groupInnerCounter = 0; bool blockStarted = false; bool blockCompleted = false; bool blockGroupStarted = false; bool blockGroupCompleted = false; bool quotedTextCompleted = false; string rawText = ""; string debugText = ""; foreach (string line in escapedLines) { lineNum = lineIndexes[linePlainNum]; linePlainNum++; int startIndentLevel = 0; int endIndentLevel = 0; if (currentRoot) { currentLine = lineNum; //Вычисляем уровень вложенности(для утилиты форматирования кода) int stackSize = ControlStack.Count; if (stackSize > 0) startIndentLevel = stackSize; } int pos = 0; int firstSymbolPos = INVALID_INDEX; int blockStartPos = INVALID_INDEX; int lastIfPos = INVALID_INDEX; int lastActPos = INVALID_INDEX; int bracketLevel = 0; bool comment = false; bool label = false; string labelText = ""; bool singleBlockGroup = false; //Разбор строки while (pos < line.Length) { bool appendDelimiter = false; String delimiterToAppend = ""; char c = line[pos]; if ((c.ToString().IndexOfAny(WhiteSpace) != 0) && (firstSymbolPos == INVALID_INDEX)) { firstSymbolPos = pos; } if (c == '\'') { //Апостроф if (quote == (int)QuoteType.None) { quote = (int)QuoteType.Single; quotedText = ""; if (blockStarted && !comment) blockCompleted = true; //для отладки if (rawText.Length == 0) rawText += c; else rawText += Environment.NewLine + c; } else if (quote == (int)QuoteType.Single) { if ((pos + 1 < line.Length) && (line[pos + 1] == '\'')) { //Экранированный апостроф pos++; quotedText += c; } else { quote = (int)QuoteType.None; if (!comment && !label) quotedTextCompleted = true; } //для отладки rawText += c; } else { quotedText += c; //для отладки rawText += c; } } else if (c == '"') { //Кавычка if (quote == (int)QuoteType.None) { quote = (int)QuoteType.Double; quotedText = ""; if (blockStarted && !comment) blockCompleted = true; //для отладки if (rawText.Length == 0) rawText += c; else rawText += Environment.NewLine + c; } else if (quote == (int)QuoteType.Double) { if ((pos + 1 < line.Length) && (line[pos + 1] == '"')) { //Экранированная кавычка pos++; quotedText += c; } else { quote = (int)QuoteType.None; if (!comment && !label) quotedTextCompleted = true; } //для отладки rawText += c; } else { quotedText += c; //для отладки rawText += c; } } else if (comment) { //Здесь игнорируем все до самого конца строки, но учитываем переводы строк внутри кавычек и апострофов. } else if (quote != (int)QuoteType.None) { //Строка текста внутри кавычек или апострофов quotedText += c; //для отладки rawText += c; } else if (c == '{') { if (blockStarted && !comment) blockCompleted = true; //Записываем содержимое фигурных скобок string curlyText = ""; curlyLevel = 1; pos++; int curlyQuote = (int)QuoteType.None; while (pos < line.Length) { char c2 = line[pos]; if (c2 == '\'') { //Апостроф if (curlyQuote == (int)QuoteType.None) { curlyQuote = (int)QuoteType.Single; } else if (curlyQuote == (int)QuoteType.Single) { if ((pos + 1 < line.Length) && (line[pos + 1] == '\'')) { //Экранированный апостроф curlyText += c2; pos++; c2 = line[pos]; } else { curlyQuote = (int)QuoteType.None; } } } else if (c2 == '"') { //Кавычка if (curlyQuote == (int)QuoteType.None) { curlyQuote = (int)QuoteType.Double; } else if (curlyQuote == (int)QuoteType.Double) { if ((pos + 1 < line.Length) && (line[pos + 1] == '"')) { //Экранированная кавычка curlyText += c2; pos++; c2 = line[pos]; } else { curlyQuote = (int)QuoteType.None; } } } else if (curlyQuote == (int)QuoteType.None) { if (c2 == '{') { curlyLevel++; } else if (c2 == '}') { curlyLevel--; if (curlyLevel == 0) break; } } curlyText += c2; pos++; } if (curlyLevel > 0) { SubmitError("Internal error of the analyzer - unclosed curly bracket \"{\"", currentLine); break; } if (curlyParsing) { //Парсим содержимое фигурных скобок List curlyLines = new List(curlyText.Split('\n')); currentRoot = false; ParseLocation(curlyLines, currentRoot); currentRoot = root; //Добавляем пустую "строку в кавычках", чтобы анализатор не ругался на отсутствие параметра quotedText = ""; } else { quotedText = curlyText; } quotedTextCompleted = true; } else { //Обычные символы if (label) { if (c == '&') { if (labelText.Trim(WhiteSpace).Equals(":")) { SubmitError("Empty label", lineNum); return false; } if (quote != (int)QuoteType.None) { SubmitError("Unclosed string in the label name", lineNum); return false; } label = false; } else { labelText += c; } } else if ((c == ':') && ((controlBlock == (int)ControlBlockType.IfCondition) || (controlBlock == (int)ControlBlockType.ElseIfCondition))) { //Разбор условия if (blockStarted) blockCompleted = true; blockGroupCompleted = true; //Двоеточие после условия if ((pos == line.Length - 1)) { //В конце строки - многострочный IF или ELSEIF if (firstSymbolPos == lastIfPos) { if (controlBlock == (int)ControlBlockType.IfCondition) { ControlLine cline; cline.line = lineNum; cline.controlBlock = ControlBlockType.ML_If; ControlStack.Push(cline); } else { ControlLine cline = ControlStack.Pop(); if (cline.controlBlock != ControlBlockType.ML_If) { SubmitError("ELSEIF without IF", lineNum); return false; } cline.line = lineNum; ControlStack.Push(cline); } controlBlock = (int)ControlBlockType.ML_If; } else { if (controlBlock == (int)ControlBlockType.IfCondition) SubmitError("Multiline IF should be placed at the beginning of the line", lineNum); else SubmitError("Multiline ELSEIF should be placed at the beginning of the line", lineNum); return false; } } else if (controlBlock == (int)ControlBlockType.IfCondition) { //Строка не заканчивается - однострочный IF if (controlBlock == (int)ControlBlockType.IfCondition) { ControlLine cline; cline.line = lineNum; cline.controlBlock = ControlBlockType.SL_If; ControlStack.Push(cline); controlBlock = (int)cline.controlBlock; groupInnerCounter = 0; } else { SubmitError("ELSEIF cannot be used in single line IF", lineNum); return false; } } else { SubmitError("An operator cannot be placed after colon\":\" in multiline ELSEIF", lineNum); return false; } } else if ((c == ':') && (block == "else")) { SubmitError("ELSE with :", lineNum); return false; } else if ((c == ':') && (controlBlock == (int)ControlBlockType.ActCondition)) { //Разбор аргумента if (blockStarted) blockCompleted = true; blockGroupCompleted = true; //Двоеточие после ACT if (pos == line.Length - 1) { //В конце строки - многострочный ACT if (firstSymbolPos == lastActPos) { controlBlock = (int)ControlBlockType.ML_Act; ControlLine cline; cline.line = lineNum; cline.controlBlock = ControlBlockType.ML_Act; ControlStack.Push(cline); } else { SubmitError("Multiline ACT should be placed at the beginning of the line", lineNum); return false; } } else { //Строка не заканчивается - однострочный ACT controlBlock = (int)ControlBlockType.SL_Act; groupInnerCounter = 0; } } else if (!comment && (c == '!') && !blockGroupStarted && !blockStarted) { //Начало комментария comment = true; } else if ((c == ':') && (firstSymbolPos == pos)) { //Начало метки label = true; } else if (c.ToString().IndexOfAny(Delimiters) == 0) { //Особый учет для последовательностей >=, => и т.д. bool comparer = pos + 1 < line.Length; String cmp = ""; if (comparer) { cmp = line.Substring(pos, 2); comparer = cmp.Equals("<=") || cmp.Equals(">=") || cmp.Equals("=>") || cmp.Equals("=<") || cmp.Equals("<>") || cmp.Equals("+=") || cmp.Equals("-=") || cmp.Equals("*=") || cmp.Equals("/="); } if ((c == '&') && (bracketLevel == 0)) { if (controlBlock == (int)ControlBlockType.IfCondition) { //В условии IF недопустимо использование "&" вне скобок SubmitError("Redundant \"&\": perhaps should be \"AND\"", lineNum); return false; } else if (controlBlock == (int)ControlBlockType.ActCondition) { //В названии ACT недопустимо использование "&" вне скобок SubmitError("Redundant \"&\"", lineNum); return false; } else { if (blockStarted) blockCompleted = true; if (blockGroupStarted) blockGroupCompleted = true; if (blockStarted && !blockGroupStarted) singleBlockGroup = true; } } else if (blockStarted && (c == ' ') && StartOfMultiWordOperator(block)) { //Составной оператор - DEL ACT, DEL OBJ block += c; } else if (blockStarted && (c == ' ') && (qspGetStatCode(block) == (int)QspStatementType.Close) && (line.Length > pos + 3) && line.Substring(pos + 1, 3).Equals("ALL", StringComparison.OrdinalIgnoreCase)) { //CLOSE ALL block += c; } else if (!comparer && !blockStarted && (c == '*')) { //Возможно, *PL, *P, *NL blockStarted = true; blockCompleted = false; block += c; blockStartPos = pos; } else if (!blockStarted && (c == '[')) { //Квадратная скобка должна идти вплотную к имени массива SubmitError("A square bracket has to be placed just behind the name of the array, with no spaces", lineNum); return false; } else { //Самостоятельные знаки if (c == '(') { bracketLevel++; } else if (c == ')') { if (bracketLevel == 0) { SubmitError("Redundant bracket \")\"", lineNum); return false; } bracketLevel--; } bool whiteSpace = c.ToString().IndexOfAny(WhiteSpace) == 0; if (comparer) { //<= >= => =< <> += -= if (blockStarted) { appendDelimiter = true; delimiterToAppend = cmp; } else { blockStarted = true; blockStartPos = pos; block = cmp; } blockCompleted = true; pos++; } else if (blockStarted) { blockCompleted = true; if (!whiteSpace) { appendDelimiter = true; delimiterToAppend += c; } else { //Обрабатываем END IF, END ACT int blockCode = qspGetStatCode(block); if (blockCode == (int)QspStatementType.End) { int nextPos = line.LastIndexOfAny(WhiteSpace, blockStartPos + block.Length) + 1; if (nextPos < line.Length) { int delimPos = line.IndexOfAny(Delimiters, nextPos); string nextWord = ""; if (delimPos == INVALID_INDEX) nextWord = line.Substring(nextPos); else nextWord = line.Substring(nextPos, delimPos - nextPos); int nextCode = qspGetStatCode(nextWord); if ((nextCode == (int)QspStatementType.If) || (nextCode == (int)QspStatementType.Act)) { pos = nextPos + nextWord.Length - 1; } } } } } else if (!whiteSpace) { blockStarted = true; blockCompleted = true; blockStartPos = pos; block += c; } } } else { //Просто буквы if (!blockStarted) { blockStarted = true; blockCompleted = false; blockStartPos = pos; } block += c; } } //Последний символ в строке if (blockStarted && (pos == line.Length - 1)) { blockCompleted = true; } if (blockCompleted || quotedTextCompleted) { if (!quotedTextCompleted && (block.Length == 0)) { SubmitError("Internal error of analyzer! Empty block!", lineNum); return false; } debugText += "[" + block + "] "; bool skip = false; int blockCode = qspGetStatCode(block); if (blockCode == (int)QspStatementType.If) { controlBlock = (int)ControlBlockType.IfCondition; debugText += " "; lastIfPos = blockStartPos; } if (blockCode == (int)QspStatementType.Act) { controlBlock = (int)ControlBlockType.ActCondition; debugText += " "; lastActPos = blockStartPos; } if (blockCode == (int)QspStatementType.ElseIf) { if (controlBlock == (int)ControlBlockType.ML_If) { controlBlock = (int)ControlBlockType.ElseIfCondition; debugText += " "; lastIfPos = blockStartPos; } else { SubmitError("ELSEIF без IF", lineNum); return false; } } if (blockCode == (int)QspStatementType.Else) { if (controlBlock == (int)ControlBlockType.ML_If) { if (firstSymbolPos == blockStartPos) { if ((blockStartPos + block.Length == line.Length) || (line.Substring(blockStartPos + block.Length).Trim(WhiteSpace).Equals(":"))) { controlBlock = (int)ControlBlockType.ML_Else; ControlStack.Pop(); ControlLine cline; cline.controlBlock = ControlBlockType.ML_Else; cline.line = lineNum; ControlStack.Push(cline); } else { SubmitError("An operator cannot be placed after multiline ELSE", lineNum); return false; } } else { SubmitError("Multiline ELSE should be at the beginning of the line", lineNum); return false; } } else { if (controlBlock == (int)ControlBlockType.SL_Act) { if (groupInnerCounter > 0) { if ((ControlStack.Peek().controlBlock == ControlBlockType.SL_If) && (ControlStack.Peek().line == lineNum)) { if (blockGroupStarted) { blockGroupCompleted = true; } } else { SubmitError("ELSE without IF", lineNum); return false; } } else { SubmitError("Invalid notation of single line ACT", lineNum); return false; } } else if (controlBlock == (int)ControlBlockType.SL_If) { if (blockGroupStarted) { blockGroupCompleted = true; } else if (groupInnerCounter == 0) { SubmitError("After single line IF has to be any operators before ELSE", lineNum); return false; } } else { SubmitError("ELSE without IF", lineNum); return false; } //Это правильный ELSE if (blockStartPos + block.Length < line.Length) { string restLine = line.Substring(blockStartPos + block.Length); if (restLine.Trim(WhiteSpace).StartsWith(":")) { pos += restLine.IndexOf(':') + 1; } } ControlStack.Pop(); controlBlock = (int)ControlBlockType.SL_Else; groupInnerCounter = 0; } skip = true; } if (blockCode == (int)QspStatementType.End) { if (ControlStack.Count > 0) { ControlStack.Pop(); if (ControlStack.Count > 0) { ControlLine cline = ControlStack.Peek(); controlBlock = (int)cline.controlBlock; } else { controlBlock = (int)ControlBlockType.None; } } else { SubmitError("Redundant END", lineNum); return false; } skip = true; } if (!skip) { if (!blockGroupStarted) { blockGroupStarted = true; groupInnerCounter++; if (singleBlockGroup) { singleBlockGroup = false; blockGroupCompleted = true; } } if (quotedTextCompleted) { blockGroup.Add(quotedText); blockInGroupIsText.Add(true); quotedTextCompleted = false; } else { if ((blockCode == (int)QspStatementType.Unknown) && (block.Length > 1)) { //Обрабатываем попытки "склеить" некоторые последовательности символов string first = block.Substring(0, 1); string second = block.Substring(1, 1); if (!second.Equals("=") && first.Equals("*")) { //Если была неудачная попытка прочесть *NL, *P, и т.д., "разделяем" блок на части blockGroup.Add(first); blockInGroupIsText.Add(false); block = block.Substring(1); } } blockGroup.Add(block); blockInGroupIsText.Add(false); } if (appendDelimiter) { blockGroup.Add(delimiterToAppend); blockInGroupIsText.Add(false); } if (blockGroupStarted && (pos == line.Length - 1)) { blockGroupCompleted = true; } } block = ""; blockCompleted = false; blockStarted = false; } if (blockGroupCompleted) { //Разбор аргументов if (blockGroup.Count == 0) { SubmitError("Internal error of the analyzer! Empty block!", lineNum); return false; } if (!CheckBlockGroup(blockGroup, blockInGroupIsText)) return false; blockGroup.Clear(); blockInGroupIsText.Clear(); blockGroupStarted = false; blockGroupCompleted = false; } pos++; } //Строка закончилась if (bracketLevel>0) { SubmitError("Redundant bracket \"(\"", lineNum); return false; } if (controlBlock == (int)ControlBlockType.IfCondition) { SubmitError("IF without \":\"", lineNum); return false; } if (controlBlock == (int)ControlBlockType.ActCondition) { SubmitError("ACT without \":\"", lineNum); return false; } if ((controlBlock == (int)ControlBlockType.SL_Act) || (controlBlock == (int)ControlBlockType.SL_If) || (controlBlock == (int)ControlBlockType.SL_Else)) { if (groupInnerCounter > 0) { while ((ControlStack.Count > 0) && (ControlStack.Peek().controlBlock == ControlBlockType.SL_If)) ControlStack.Pop(); if (ControlStack.Count > 0) { ControlLine cline = ControlStack.Peek(); controlBlock = (int)cline.controlBlock; } else { controlBlock = (int)ControlBlockType.None; } } else if (controlBlock == (int)ControlBlockType.SL_Act) { SubmitError("Invalid notation of single line ACT", lineNum); return false; } else if (controlBlock == (int)ControlBlockType.SL_If) { SubmitError("Invalid notation of single line IF", lineNum); return false; } else if (controlBlock == (int)ControlBlockType.SL_Else) { SubmitError("In single line IF has to be any operators after ELSE", lineNum); return false; } } //для отладки if (line.Length > 0) { debugText += Environment.NewLine; } if (currentRoot) { //Вычисляем уровень вложенности(для утилиты форматирования кода) int stackSize = ControlStack.Count; int level = 0; if (stackSize > 0) { endIndentLevel = stackSize; ControlLine cline = ControlStack.Peek(); int b = (int)cline.controlBlock; bool openingLine = cline.line == currentLine; if (openingLine && ((b == (int)ControlBlockType.ML_Act) || (b == (int)ControlBlockType.ML_If))) { level = startIndentLevel; } else if (openingLine && (b == (int)ControlBlockType.ML_Else)) { level = startIndentLevel - 1; } else { level = endIndentLevel; } } location_codeline_level.Add(currentLine, level); } } //Проверка на незакрытые в конце локации многострочные IF и ACT if (ControlStack.Count > 0) { ControlLine cline = ControlStack.Pop(); if ((cline.controlBlock == ControlBlockType.ML_If) || (cline.controlBlock == ControlBlockType.ML_Else)) { SubmitError("Multi line IF without END", cline.line); return false; } else { SubmitError("Multi line ACT without END", cline.line); return false; } } AddLocationLink(m_name, true, m_start); return true; } bool CheckBlockGroup(List blockGroup, List blockInGroupIsText) { //Разбор аргументов для операторов ACT, IF, прочих операторов и функций. string pomvarstr1; string pomvarstr2; bool iifpresent; int blockCode = qspGetStatCode(blockGroup.ToArray()[0]); if (blockInGroupIsText[0]) blockCode = (int)QspStatementType.Unknown; bool isSetOp = blockCode == (int)QspStatementType.Set; if ((blockCode == (int)QspStatementType.Unknown) || isSetOp) { //Это присваивание? int setpos = FindInTopBracketLevel(blockGroup, blockInGroupIsText, "="); if (setpos == INVALID_INDEX) setpos = FindInTopBracketLevel(blockGroup, blockInGroupIsText, "+="); if (setpos == INVALID_INDEX) setpos = FindInTopBracketLevel(blockGroup, blockInGroupIsText, "*="); if (setpos == INVALID_INDEX) setpos = FindInTopBracketLevel(blockGroup, blockInGroupIsText, "/="); if (setpos == INVALID_INDEX) setpos = FindInTopBracketLevel(blockGroup, blockInGroupIsText, "-="); if (isSetOp && (setpos == INVALID_INDEX)) { SubmitError("Not found the char \"=\"", currentLine); return false; } if (setpos != INVALID_INDEX) { //Присваивание if ((setpos == 0) || (isSetOp && (setpos == 1))) { SubmitError("The name of a variable before the character \"=\" is not specified!", currentLine); return false; } int last = blockGroup.Count - 1; if (setpos==last) { SubmitError("It should be a value after char \"=\"", currentLine); return false; } //Проверяем левую часть int start = 0; if (isSetOp) start = 1; int count = setpos - start; List args = new List(); List types = new List(); args = blockGroup.GetRange(start, count); types = blockInGroupIsText.GetRange(start, count); if (!CheckLeftAssign(args, types)) return false; string assignedVarName = args[0]; //Проверяем правую часть start = setpos + 1; count = last - start + 1; args = blockGroup.GetRange(start, count); types = blockInGroupIsText.GetRange(start, count); if (!CheckExpression(args, types)) return false; //Засчитываем присваивание некоторым переменным как обращение к локации if ((count == 1) && types[0] && ListContainsIgnoreCase(callerVariables, assignedVarName)) { AddLocationLink(args[0], false, true); } } else { //Если оператор неизвестен, пытаемся разобрать выражение if (!CheckExpression(blockGroup, blockInGroupIsText)) return false; //Разбираем код в ссылках if ((blockGroup.Count >= 1) && (blockInGroupIsText[0])) { pomvarstr1 = ""; pomvarstr2 = ""; iifpresent = false; for (int ibc = 0; ibc < blockGroup.Count; ibc++) { if (blockGroup[Math.Min(ibc + 1, blockGroup.Count-1)] != "iif") { pomvarstr1 = pomvarstr1 + blockGroup[ibc]; pomvarstr2 = pomvarstr2 + blockGroup[ibc]; } else { iifpresent = true; while (blockGroup[ibc] != ",") { ibc++; } ibc++; pomvarstr1 = pomvarstr1 + blockGroup[ibc]; ibc++; ibc++; pomvarstr2 = pomvarstr2 + blockGroup[ibc]; ibc++; ibc++; } } ParseExecInOutputText(pomvarstr1); if (iifpresent) ParseExecInOutputText(pomvarstr2); } } } else if ((blockCode == (int)QspStatementType.If) || (blockCode == (int)QspStatementType.ElseIf)) { int start = 1; int count = blockGroup.Count - 1; if (count > 0) { List args = blockGroup.GetRange(start, count); List types = blockInGroupIsText.GetRange(start, count); if (!CheckExpression(args, types)) return false; } else { if (blockCode == (int)QspStatementType.If) { SubmitError("Operator IF without condition", currentLine); } else { SubmitError("Operator ELSEIF without condition", currentLine); } return false; } } else { //Если оператор известен, проверяем его аргументы. int count = blockGroup.Count - 1; List args = new List(); List types = new List(); if (count > 0) { args = blockGroup.GetRange(1, count); types = blockInGroupIsText.GetRange(1, count); } if (!CheckStatementArgs(blockCode, args, types)) return false; } //Проверяем подвыражения вида <<$var>> в любых строках for (int i = 0; i < blockGroup.Count; i++) { if (blockInGroupIsText[i]) { if (!ParseSubExpressions(blockGroup[i])) return false; } } return true; } bool CheckStatementArgs(int code, List argsSrc, List typesSrc) { int count = argsSrc.Count; List args = new List(); List types = new List(); if (count > 0) { if ((count > 1) && argsSrc.ToArray()[0].Equals("(") && !typesSrc.ToArray()[0] && argsSrc.ToArray()[count - 1].Equals(")") && !typesSrc.ToArray()[count - 1]) { //Игнорируем одну пару скобок по краям args = argsSrc.GetRange(1, count - 2); types = typesSrc.GetRange(1, count - 2); } else { args = argsSrc.GetRange(0, count); types = typesSrc.GetRange(0, count); } } count = args.Count; if ((count >= 1) && types[0]) { //Отмечаем в списке явные ссылки на локации if ((code == (int)QspStatementType.GoSub) || (code == (int)QspStatementType.GoTo) || (code == (int)QspStatementType.XGoTo)) { AddLocationLink(args[0], false, true); } //Разбираем код в ссылках else if ((code == (int)QspStatementType.P) || (code == (int)QspStatementType.PL) || (code == (int)QspStatementType.NL) || (code == (int)QspStatementType.MP) || (code == (int)QspStatementType.MPL) || (code == (int)QspStatementType.MNL)) { ParseExecInOutputText(args[0]); } //Отмечаем в списке добавленные предметы else if (code == (int)QspStatementType.AddObj) { AddObj(args[0], true, false); } //Отмечаем в списке удаленные предметы else if (code == (int)QspStatementType.DelObj) { AddObj(args[0], false, true); } //Отмечаем в списке добавленные действия else if (code == (int)QspStatementType.Act) { AddAct(args[0], true, false); } //Отмечаем в списке удаленные действия else if (code == (int)QspStatementType.DelAct) { AddAct(args[0], false, true); } } int minArgCount = qspStats[code].MinArgsCount; int maxArgCount = qspStats[code].MaxArgsCount; int argCount = 0; int curIndex = 0; List expr = new List(); if (count > 0) { while (true) { if (argCount == maxArgCount) { SubmitError("Too many arguments for the operator", currentLine); return false; } int index = FindInTopBracketLevel(args, types, ",", curIndex); if (index != INVALID_INDEX) { if (index > curIndex) { if (!CheckExpression(args.GetRange(curIndex, index - curIndex), types.GetRange(curIndex, index - curIndex))) return false; } else { SubmitError("Argument can not be empty", currentLine); return false; } } else { if (!CheckExpression(args.GetRange(curIndex, count - curIndex), types.GetRange(curIndex, count - curIndex))) return false; } argCount++; if (index == INVALID_INDEX) break; curIndex = index + 1; if (curIndex == count) { SubmitError("Argument can not be empty", currentLine); return false; } } } if (argCount < minArgCount) { SubmitError("The operator requires more arguments", currentLine); return false; } return true; } bool CheckExpression(List args, List types) { //Проверка выражения int[] opStack = new int[QSP_STACKSIZE]; int[] argStack = new int[QSP_STACKSIZE]; bool waitForOperator = false; int opCode, itemsCount = 0, opSp = -1, argSp = -1; if (!qspCompileExprPushOpCode(ref opStack, ref opSp, ref argStack, ref argSp, (int)QspFunctionType.Start)) return false; int argIndex = 0; while (argIndex <= args.Count) { String s = ""; if (argIndex < args.Count) s = args[argIndex]; if (waitForOperator) { opCode = qspGetFunctionCode(s, false); String nextArg = ""; bool nextArgIsString = false; bool hasNextArg = argIndex + 1 < args.Count; if (hasNextArg) { nextArg = args[argIndex + 1]; nextArgIsString = types[argIndex + 1]; } if (opCode == (int)QspFunctionType.Unknown || opCode >= (int)QspFunctionType.First_Function) { SubmitError("Unknown action in the expression", currentLine); break; } if (((opCode == (int)QspFunctionType.And) || (opCode == (int)QspFunctionType.Or) || (opCode == (int)QspFunctionType.Mod)) && !(hasNextArg && (nextArgIsString || nextArg.Equals("(") || (nextArg.IndexOfAny(Delimiters) == INVALID_INDEX))) ) { SubmitError("Syntax error", currentLine); break; } bool bError = false; while (qspOps[opCode].Priority <= qspOps[opStack[opSp]].Priority && qspOps[opStack[opSp]].Priority != 127) { if (opStack[opSp] >= (int)QspFunctionType.First_Function) ++argStack[argSp]; if (!qspAppendToCompiled(ref itemsCount)) { bError = true; break; } if (--opSp < 0 || --argSp < 0) { SubmitError("Syntax error", currentLine); bError = true; break; } } if (bError) break; switch (opCode) { case (int)QspFunctionType.End: if (opSp > 0) { SubmitError("Closing bracket not found", currentLine); bError = true; break; } return itemsCount > 0; case (int)QspFunctionType.CloseBracket: if (opStack[opSp] != (int)QspFunctionType.OpenBracket) { SubmitError("Opening bracket not found", currentLine); bError = true; break; } opCode = opStack[--opSp]; if (opCode >= (int)QspFunctionType.First_Function) { if (argStack[argSp] + 1 < qspOps[opCode].MinArgsCount || argStack[argSp] + 1 > qspOps[opCode].MaxArgsCount) { SubmitError("Invalid number of arguments", currentLine); bError = true; } } else --argSp; break; case (int)QspFunctionType.Comma: if ((opSp > 0) && opStack[opSp - 1] >= (int)QspFunctionType.First_Function) { if (++argStack[argSp] > qspOps[opStack[opSp - 1]].MaxArgsCount) { SubmitError("Invalid number of arguments", currentLine); bError = true; break; } } else { if (!qspCompileExprPushOpCode(ref opStack, ref opSp, ref argStack, ref argSp, (int)QspFunctionType.Comma)) { bError = true; break; } } waitForOperator = false; break; default: if (!qspCompileExprPushOpCode(ref opStack, ref opSp, ref argStack, ref argSp, opCode)) { bError = true; break; } waitForOperator = false; break; } if (bError) break; } else { if ((argIndex < args.Count) && !types[argIndex] && IsNumber(s)) { if (opStack[opSp] == (int)QspFunctionType.Minus) { --opSp; --argSp; } if (!qspAppendToCompiled(ref itemsCount)) break; waitForOperator = true; } // I added this piece of code (next 7 lines) so the program could detect function call and the first argument -name of the function / location // I do not assure that I did it in a proper place, but it seems that it works. else if ((opStack[1] == (int)QspFunctionType.Func) && (argIndex == 2)) { AddLocationLink(s, false, true); } else if ((opStack[1] == (int)QspFunctionType.Func) && (argIndex == 3) && (s == ",")) { } else if ((argIndex < args.Count) && types[argIndex]) { if (!qspAppendToCompiled(ref itemsCount)) break; waitForOperator = true; } else if (s.Equals("+")) { } else if (s.Equals("-")) { if (!qspCompileExprPushOpCode(ref opStack, ref opSp, ref argStack, ref argSp, (int)QspFunctionType.Minus)) break; } else if (s.Equals("(")) { if (!qspCompileExprPushOpCode(ref opStack, ref opSp, ref argStack, ref argSp, (int)QspFunctionType.OpenBracket)) break; } else if (s.Equals(")")) { opCode = opStack[opSp]; if (opCode != (int)QspFunctionType.OpenBracket) { if (opCode >= (int)QspFunctionType.First_Function) SubmitError("Invalid number of arguments", currentLine); else SubmitError("Syntax error", currentLine); break; } opCode = opStack[--opSp]; if (opCode < (int)QspFunctionType.First_Function) { SubmitError("Syntax error", currentLine); break; } if (qspOps[opCode].MinArgsCount > argSp) { SubmitError("Invalid number of arguments", currentLine); break; } if (!qspAppendToCompiled(ref itemsCount)) break; --opSp; --argSp; waitForOperator = true; } else if ((argIndex < args.Count) && s.IndexOfAny(Delimiters) != 0) { if (s.IndexOfAny(Delimiters) != INVALID_INDEX) { SubmitError("Internal error of the analyzer: Invalid characters in the variable name", currentLine); break; } opCode = qspGetFunctionCode(s, true); if (opCode >= (int)QspFunctionType.First_Function) { if ((args.Count - argIndex > 1) && (args[argIndex + 1].Equals("(") && !types[argIndex + 1])) { if (!qspCompileExprPushOpCode(ref opStack, ref opSp, ref argStack, ref argSp, opCode)) break; if (!qspCompileExprPushOpCode(ref opStack, ref opSp, ref argStack, ref argSp, (int)QspFunctionType.OpenBracket)) break; argIndex++; --argSp; } else if (qspOps[opCode].MinArgsCount < 2) { if (qspOps[opCode].MinArgsCount > 0) { if (!qspCompileExprPushOpCode(ref opStack, ref opSp, ref argStack, ref argSp, opCode)) break; } else { if (!qspAppendToCompiled(ref itemsCount)) break; waitForOperator = true; } } else { SubmitError("Brackets not found", currentLine); break; } } else { int optArgsCount = 0; //Проверяем выражение(индекс массива) if ((args.Count - argIndex > 1) && (args[argIndex + 1].Equals("[") && !types[argIndex + 1])) { int optIndex = argIndex + 2; int level = 1; bool bFound = false; while (optIndex < args.Count) { if (args[optIndex].Equals("[") && !types[optIndex]) { level++; } else if (args[optIndex].Equals("]") && !types[optIndex]) { level--; if (level == 0) { bFound = true; break; } } optIndex++; } if (level > 0) { SubmitError("Unclosed parenthesis \"[\"", currentLine); break; } if (bFound) optArgsCount = optIndex - argIndex; } List subArgs = args.GetRange(argIndex, optArgsCount + 1); List subTypes = types.GetRange(argIndex, optArgsCount + 1); if (!CheckVariable(subArgs, subTypes, false)) break; argIndex += optArgsCount; if (!qspAppendToCompiled(ref itemsCount)) break; waitForOperator = true; } } else { if (opStack[opSp] >= (int)QspFunctionType.First_Function) SubmitError("Invalid number of arguments", currentLine); else SubmitError("Syntax error", currentLine); break; } } argIndex++; } return false; } bool CheckVariable(List args, List types, bool assignment) { //Проверка имени переменной if ((args.Count == 0) || (args[0].Length == 0) || types[0]) { SubmitError("Internal error of the analyzer: empty name of variable!", currentLine); return false; } string name = args[0]; int code1 = qspGetStatCode(name); int code2 = qspGetFunctionCode(name, true); if ((code1 != (int)QspStatementType.Unknown) || (code2 != (int)QspFunctionType.Unknown)) { SubmitError("QSP Keyword \"" + name + "\" cannot be used as a variable name", currentLine); return false; } //Проверяем имя переменной на валидность //В имени переменной разрешены все символы, кроме DELIMS и цифры в начале имени //Перед именем переменной может находиться префикс "$" string checkName = name; if (checkName.StartsWith("$")) checkName = checkName.Substring(1); // $ = if (checkName.Length == 0) { SubmitError("Invalid variable name \"" + name + "\"", currentLine); return false; } // 1asd = if (checkName.Substring(0, 1).IndexOfAny(Digits) != INVALID_INDEX) { SubmitError("Variable name \"" + name + "\" cannot start with a digit", currentLine); return false; } // /asd = if (checkName.IndexOfAny(Delimiters) != INVALID_INDEX) { SubmitError("Invalid symbols in the name of the variable \"" + name + "\"", currentLine); return false; } //Проверяем переменную на "смешанные раскладки" if ((checkName.IndexOfAny(LatinLetters) != INVALID_INDEX) && (checkName.IndexOfAny(RussianLetters) != INVALID_INDEX)) { SubmitWarning("English and Russian symbols are used simultaneously in the name of varible \"" + name + "\" , possibly a typo", currentLine); } //Проверяем выражение(индекс массива) if (args.Count > 1) { if (args.Count > 2 && args[1].Equals("[") && !types[1] && args[args.Count - 1].Equals("]") && !types[args.Count - 1]) { int start = 2; int count = args.Count - 3; if (count > 0) { List argsCut = args.GetRange(start, count); List typesCut = types.GetRange(start, count); if (!CheckExpression(argsCut, typesCut)) return false; } } else { SubmitError("Invalid expression", currentLine); return false; } } //Сохраняем имя переменной в глобальном списке if (!assignment) { bool usedBySystem = ListContainsIgnoreCase(systemVariables, name); AddVar(name, usedBySystem, true); } return true; } bool CheckLeftAssign(List args, List types) { //Проверка имени переменной(слева от знака "=" в операции присваивания) if ((args.Count == 0) || (args[0].Length == 0) || types[0]) { //Текстовая строка SubmitError("Name of the variable should be before char \"=\" in the assignment operation", currentLine); return false; } if (!CheckVariable(args, types, true)) return false; //Сохраняем имя переменной в глобальном списке bool usedBySystem = ListContainsIgnoreCase(systemVariables, args[0]); AddVar(args[0], true, usedBySystem); return true; } int FindInTopBracketLevel(List args, List types, string what) { return FindInTopBracketLevel(args, types, what, 0); } int FindInTopBracketLevel(List args, List types, string what, int startIndex) { //Ищем знак "=" или запятую вне скобок int bracketLevel = 0; for (int i = startIndex; i < args.Count; i++) { if (types[i]) continue; string block = args[i]; if (block.Equals("(")) { bracketLevel++; } else if (block.Equals(")")) { bracketLevel--; } else if (block.Equals(what) && (bracketLevel == 0)) { return i; } } return INVALID_INDEX; } void ParseExecInOutputText(string text) { //Разбираем код в ссылках int pos = 0; //Строим "карту" подвыражений. //Если подвыражения в строке содержат ошибки, то разбирать теги нет смысла. List subExpMap = new List(); if (!ParseSubExpressions(text, ref subExpMap)) return; int subExpSkipped = 0; while (pos < text.Length) { pos = text.IndexOf(" subExpSkipped) { int subExpStartPos = subExpMap[subExpSkipped * 2]; int subExpEndPos = subExpMap[subExpSkipped * 2 + 1]; if (subExpStartPos == pos) { //Пропускаем подвыражения вне тегов subExpSkipped++; pos = subExpEndPos + 1; continue; } } //Разбираем содержимое угловых скобок //Внутри HTML-тега кавычки экранируются не через дублирование, как в QSP, а через "\", //причем экранирование требуется только внутри окавыченной строки. List blockGroup = new List(); List blockInGroupIsText = new List(); int firstBlockPosition = INVALID_INDEX; string quotedText = ""; string block = ""; bool tagClosed = false; bool quotedTextCompleted = false; bool blockCompleted = false; pos++; int startPos = pos; if ((pos < text.Length) && (text[pos] == '/')) startPos++; int tagQuote = (int)QuoteType.None; while (!tagClosed && (pos < text.Length)) { if (subExpMap.Count / 2 > subExpSkipped) { int subExpStartPos = subExpMap[subExpSkipped * 2]; int subExpEndPos = subExpMap[subExpSkipped * 2 + 1]; if (subExpStartPos == pos) { //Пропускаем подвыражения вне тегов subExpSkipped++; string subExpText = text.Substring(pos, subExpEndPos - pos + 1); if (tagQuote == (int)QuoteType.None) block += subExpText; else quotedText += subExpText; pos = subExpEndPos + 1; continue; } } char c2 = text[pos]; if (tagQuote != (int)QuoteType.None) { //Апостроф либо кавычка if (((tagQuote == (int)QuoteType.Single) && (c2 == '\'')) || ((tagQuote == (int)QuoteType.Double) && (c2 == '"'))) { if ((pos - 1 > 0) && (text[pos - 1] == '\\')) { //Экранированный апостроф либо кавычка if (!quotedTextCompleted) quotedText = quotedText.Substring(0, quotedText.Length - 1) + c2; } else { tagQuote = (int)QuoteType.None; quotedTextCompleted = true; } } else if (!quotedTextCompleted) { quotedText += c2; } } else if (c2 == '>') { tagClosed = true; } else if ((block.Length == 0) && (c2 == '\'')) { tagQuote = (int)QuoteType.Single; if (text[pos + 1] == '\'') { pos++; } } else if ((block.Length == 0) && (c2 == '"')) { tagQuote = (int)QuoteType.Double; } else if (c2.ToString().IndexOfAny(WhiteSpace) == 0) { if (block.Length > 0) { blockCompleted = true; } } else if (c2 == '=') { if (block.Length > 0) { blockGroup.Add(block); blockInGroupIsText.Add(false); } block = "="; blockCompleted = true; } else { if (firstBlockPosition == INVALID_INDEX) { firstBlockPosition = pos - startPos; } block += c2; } if (tagClosed) { if (block.Length > 0) blockCompleted = true; } if (blockCompleted) { blockGroup.Add(block); blockInGroupIsText.Add(false); blockCompleted = false; block = ""; } if (quotedTextCompleted) { blockGroup.Add(quotedText); blockInGroupIsText.Add(true); quotedTextCompleted = false; quotedText = ""; } pos++; } if (tagQuote == (int)QuoteType.Single) { SubmitWarning("Unclosed apostrophe inside the tag", currentLine); break; } else if (tagQuote == (int)QuoteType.Double) { SubmitWarning("Unclosed quote inside the tag", currentLine); break; } if (!tagClosed) { SubmitWarning("Unclosed tag",currentLine); break; } if (firstBlockPosition > 0) { SubmitWarning("The name of the tag should follow immediately after the opening angle bracket, without spaces", currentLine); break; } //Ищем HREF в атрибутах тега A int hrefIndex = INVALID_INDEX; if ((blockGroup.Count > 3) && (blockGroup[0].Equals("A", StringComparison.OrdinalIgnoreCase)) && !blockInGroupIsText[0]) { for (int i = 1; i + 2 < blockGroup.Count; i++) { if (blockGroup[i].Equals("HREF", StringComparison.OrdinalIgnoreCase) && !blockInGroupIsText[i] && blockGroup[i + 1].Equals("=") && !blockInGroupIsText[i + 1]) { hrefIndex = i + 2; } } } if (hrefIndex != INVALID_INDEX) { string hrefText = blockGroup[hrefIndex]; int execKeywordLength = "EXEC:".Length; if (hrefText.StartsWith("EXEC:", StringComparison.OrdinalIgnoreCase) && (hrefText.Length > execKeywordLength)) { string execText = hrefText.Substring(execKeywordLength); List subExpMapExec = new List(); ParseSubExpressions(execText, ref subExpMapExec); if (subExpMapExec.Count == 0) { //В EXEC не разбираем код с подвыражениями, это невозможно без интерпретации List tagLines = new List(execText.Split('\n')); bool wasRoot = currentRoot; currentRoot = false; ParseLocation(tagLines, currentRoot); currentRoot = wasRoot; } } } } } bool ParseSubExpressions(string text) { List dummy = null; return ParseSubExpressions(text, ref dummy); } bool ParseSubExpressions(string text, ref List subExpMap) { bool quiet = subExpMap != null; //Разбираем подвыражения - <<$var>> if (quiet) subExpMap.Clear(); int pos = 0; bool valid = true; while (valid && pos < text.Length) { pos = text.IndexOf("<<", pos); if (pos == INVALID_INDEX) break; if (quiet) subExpMap.Add(pos); //Разбираем содержимое двойных угловых скобок string subText = ""; bool subClosed = false; pos += 2; int subQuote = (int)QuoteType.None; int curlyLevel = 0; while (pos < text.Length) { char c2 = text[pos]; if (subQuote == (int)QuoteType.None) { if ((c2 == '>') && (pos + 1 < text.Length) && (text[pos + 1] == '>') && (curlyLevel == 0)) { subClosed = true; break; } else if (c2 == '\'') { subQuote = (int)QuoteType.Single; } else if (c2 == '"') { subQuote = (int)QuoteType.Double; } else if (c2 == '{') { curlyLevel++; } else if (c2 == '}') { curlyLevel--; } } else if (((subQuote == (int)QuoteType.Single) && (c2 == '\'')) || ((subQuote == (int)QuoteType.Double) && (c2 == '"'))) { //Апостроф или кавычка if ((pos + 1 < text.Length) && ( ((subQuote == (int)QuoteType.Single) && (text[pos + 1] == '\'')) || ((subQuote == (int)QuoteType.Double) && (text[pos + 1] == '"')))) { //Экранированный апостроф либо кавычка } else { subQuote = (int)QuoteType.None; } } subText += c2; pos++; } if (quiet) { if (subClosed) subExpMap.Add(pos + 1); else subExpMap.Add(pos - 1); } if (subQuote == (int)QuoteType.Single) { if (!quiet) SubmitError("Unclosed apostrophe in a subexpression", currentLine); valid = false; break; } else if (subQuote == (int)QuoteType.Double) { if (!quiet) SubmitError("Unclosed quote in a subexpression", currentLine); valid = false; break; } else if (curlyLevel != 0) { if (!quiet) SubmitError("Unclosed curly bracket in a subexpression", currentLine); valid = false; break; } if (!subClosed) { if (!quiet) SubmitError("Unclosed subexpression", currentLine); valid = false; break; } if (subText.Trim(WhiteSpace).Length == 0) { if (!quiet) SubmitError("Empty subexpression", currentLine); valid = false; break; } if (!quiet) { //Парсим подвыражение как исполняемый код. //Для того, чтобы строка считалась именно выражением, добавляем ''+ в начало subText = "''+" + subText; List subLines = new List(subText.Split('\n')); bool wasRoot = currentRoot; currentRoot = false; valid = ParseLocation(subLines, currentRoot); currentRoot = wasRoot; } } return valid; } } public class QSPGameCode : Common { Hashtable m_LocationsByName; List m_LocationsByOrder; Location m_lastLocation; public QSPGameCode() { m_LocationsByName = new Hashtable(); m_LocationsByOrder = new List(); m_lastLocation = null; } public bool ParseGame(string fileName, object sender, DoWorkEventArgs e) { int totalLines = 0; StreamReader rTl = new StreamReader(fileName); while (rTl.ReadLine() != null) { totalLines++; }; m_LocationsByName.Clear(); m_LocationsByOrder.Clear(); ClearErrors(); m_lastLocation = null; currentLocation = ""; vars = new List(); locationLinks = new List(); objects = new List(); acts = new List(); StreamReader fi = null; if (!OpenStreamForReading(ref fi, fileName)) { return false; } string s; int line_counter = 0; List locationCode = new List(); bool inside = false; while ((s = fi.ReadLine()) != null) { line_counter++; string trimmed = s.Trim(WhiteSpace); if ((trimmed.Length > 0) && (trimmed[0] == '#')) { //имя локации string locName = trimmed.Substring(1).Trim(WhiteSpace); if (!AddLocation(locName, line_counter)) { fi.Close(); rTl.Close(); return false; } currentLocation = locName; inside = true; } else if ((trimmed.Length > 38) && trimmed.StartsWith("--- ") && trimmed.EndsWith(" ---------------------------------")) { inside = false; Location lastLoc = GetLastLocation(); if (lastLoc != null) { lastLoc.ParseLocation(locationCode, true); locationCode.Clear(); currentLocation = ""; } } else { if (inside && GetLastLocation() != null) locationCode.Add(s); } (sender as BackgroundWorker).ReportProgress(Convert.ToInt32(((double)line_counter/(double)totalLines) * 100)); if ((sender as BackgroundWorker).CancellationPending) { e.Cancel = true; return true; } } fi.Close(); rTl.Close(); return true; } public bool AddLocation(string name, int line) { if (name.Length <= 0) { SubmitError("Empty name of location", line); return false; } if (m_LocationsByName.ContainsKey(name)) { SubmitError("Location named " + name + " exist already", line); return false; } Location newLoc = new Location(this, name, line, m_LocationsByOrder.Count == 0); m_LocationsByName.Add(name, newLoc); m_LocationsByOrder.Add(newLoc); m_lastLocation = newLoc; return true; } public Location GetLastLocation() { return m_lastLocation; } public Hashtable GetLocationsByName() { return m_LocationsByName; } public Location GetLocation(string locationName) { Location loc = (Location)m_LocationsByName[locationName]; return loc; } public bool Beautify(string fileName) { //Причесываем код - добавляем пробелов StreamWriter fo = null; if (!OpenStreamForWriting(ref fo, fileName)) return false; for (int i = 0; i < m_LocationsByOrder.Count; i++) { Location loc = (Location)m_LocationsByOrder[i]; fo.WriteLine("# " + loc.GetName()); for (int j = 0; j < loc.GetCodeLinesCount(); j++) { string text = ""; int level = INVALID_INDEX; loc.GetCodeLine(j, ref text, ref level); if (level != INVALID_INDEX) { text = new string(' ', level * 4) + text.TrimStart(WhiteSpace); } fo.WriteLine(text); } fo.WriteLine("--- " + loc.GetName() + " ---------------------------------"); fo.WriteLine(); } fo.Close(); return true; } } }