/// <summary> /// Matches a procedure definition /// </summary> private ParsedDestructor CreateParsedDestructor(Token destructorToken, ParsedScope parentScope) { /* * DESTRUCTOR [ PUBLIC ] class-name ( ) : * Destructor-body */ // info we will extract from the current statement : string name = ""; ParseFlag flags = 0; Token token; do { token = PeekAt(1); // next token if (token is TokenEos) { break; } if (token is TokenComment) { continue; } if (token is TokenWord) { switch (token.Value.ToLower()) { case "public": flags |= ParseFlag.Public; break; default: name = token.Value; break; } } } while (MoveNext()); if (name == "") { return(null); } var newDestructor = new ParsedDestructor(name, destructorToken, parentScope) { // = end position of the EOS of the statement Flags = flags, EndPosition = token.EndPosition }; AddParsedItem(newDestructor, destructorToken.OwnerNumber); return(newDestructor); }
/// <summary> /// Parses a list of tokens into a list of parsedItems /// </summary> public Parser(GapBuffer <Token> tokens, string filePathBeingParsed, ParsedScopeBlock defaultScope, bool matchKnownWords, StringBuilder debugListOut) { // process inputs _filePathBeingParsed = filePathBeingParsed; _matchKnownWords = matchKnownWords && KnownStaticItems != null; var rootToken = new TokenEos(null, 0, 0, 0, 0) { OwnerNumber = 0 }; // the first of this list represents the file currently being parsed _parsedIncludes.Add( new ParsedIncludeFile( "root", rootToken, // the preprocessed variable {0} equals to the filename... new Dictionary <string, string>(StringComparer.CurrentCultureIgnoreCase) { { "0", Path.GetFileName(FilePathBeingParsed) } }, _filePathBeingParsed, null) ); // init context _context = new ParseContext { BlockStack = new Stack <ParsedScope>(), CurrentStatement = new ParsedStatement(rootToken), CurrentStatementIsEnded = true }; // create root item var rootScope = defaultScope ?? new ParsedFile("Root", rootToken); _context.BlockStack.Push(rootScope); if (defaultScope == null) { AddParsedItem(rootScope, 0); } // Analyze _tokenList = tokens; _tokenCount = _tokenList.Count; _tokenPos = -1; ReplaceIncludeAndPreprocVariablesAhead(1); // replaces an include or a preproc var {&x} at token position 0 ReplaceIncludeAndPreprocVariablesAhead(2); // @position 1 while (MoveNext()) { try { Analyze(); } catch (Exception e) { ErrorHandler.LogError(e, "Error while parsing the following file : " + filePathBeingParsed); } } AddLineInfo(_tokenList[_tokenList.Count - 1]); // add info on last line PopOneStatementIndentBlock(0); // make sure to pop the final block // add missing values to the line dictionary // missing values will be for the lines within a multilines comment/string for which we didn't match an EOL to add line info var currentLineInfo = _lineInfo[_tokenList[_tokenList.Count - 1].Line]; for (int i = PeekAt(-1).Line - 1; i >= 0; i--) { if (!_lineInfo.ContainsKey(i)) { _lineInfo.Add(i, currentLineInfo); } else { currentLineInfo = _lineInfo[i]; } } // check for parser errors while (_context.BlockStack.Count > 1) { ParsedScope scope = _context.BlockStack.Pop(); // check that we match a RESUME for each SUSPEND if (scope is ParsedScopePreProcBlock) { _parserErrors.Add(new ParserError(ParserErrorType.MissingUibBlockEnd, PeekAt(-1), _context.BlockStack.Count, _parsedIncludes)); } // check that we match an &ENDIF for each &IF else if (scope is ParsedScopePreProcIfBlock) { _parserErrors.Add(new ParserError(ParserErrorType.MissingPreprocEndIf, PeekAt(-1), _context.BlockStack.Count, _parsedIncludes)); } // check that we match an END. for each block else { _parserErrors.Add(new ParserError(ParserErrorType.MissingBlockEnd, PeekAt(-1), _context.BlockStack.Count, _parsedIncludes)); } } // returns the concatenation of all the tokens once the parsing is done if (debugListOut != null) { foreach (var token in _tokenList) { debugListOut.Append(token.Value); } } // dispose _context.BlockStack = null; _context = null; _tokenList = null; _functionPrototype = null; _parsedIncludes = null; _knownWords = null; // if we are parsing an include file that was saved for later use, update it if (SavedTokenizerInclude.ContainsKey(filePathBeingParsed)) { SavedTokenizerInclude.Remove(filePathBeingParsed); } }
/// <summary> /// Matches a procedure definition /// </summary> private ParsedConstructor CreateParsedConstructor(Token constructorToken, ParsedScope parentScope) { /* * CONSTRUCTOR [ PRIVATE | PROTECTED | PUBLIC | STATIC ] class-name * ( [ parameter [ , parameter ] ... ] ) : * constructor-body */ // info we will extract from the current statement : string name = ""; ParseFlag flags = 0; StringBuilder parameters = new StringBuilder(); List <ParsedDefine> parametersList = null; Token token; int state = 0; do { token = PeekAt(1); // next token if (token is TokenEos) { break; } if (token is TokenComment) { continue; } switch (state) { case 0: // default state if (token is TokenWord) { switch (token.Value.ToLower()) { case "private": flags |= ParseFlag.Private; break; case "public": flags |= ParseFlag.Public; break; case "protected": flags |= ParseFlag.Protected; break; case "static": flags |= ParseFlag.Static; break; default: name = token.Value; break; } } if (token is TokenSymbol && token.Value.Equals("(")) { state = 1; } break; case 1: // read parameters, define a ParsedDefineItem for each parametersList = GetParsedParameters(constructorToken, parameters); state = 2; break; } } while (MoveNext()); if (state < 1) { return(null); } var newConstructor = new ParsedConstructor(name, constructorToken, parentScope) { // = end position of the EOS of the statement Flags = flags, EndPosition = token.EndPosition }; if (parametersList != null) { newConstructor.Parameters = new List <ParsedDefine>(); foreach (var parsedItem in parametersList) { newConstructor.Parameters.Add(parsedItem); } } AddParsedItem(newConstructor, constructorToken.OwnerNumber); return(newConstructor); }
/// <summary> /// Matches a procedure definition /// </summary> private ParsedMethod CreateParsedMethod(Token methodToken, ParsedScope parentScope) { /* * METHOD [ PRIVATE | PROTECTED | PUBLIC ] [ STATIC | ABSTRACT ] * [ OVERRIDE ] [ FINAL ] * { VOID | return-type } method-name * ( [ parameter [ , parameter ] ... ] ) : */ // info we will extract from the current statement : string name = ""; string parsedReturnType = "CLASS"; ParseFlag flags = 0; StringBuilder parameters = new StringBuilder(); List <ParsedDefine> parametersList = null; Token token; int state = 0; do { token = PeekAt(1); // next token if (token is TokenEos) { break; } if (token is TokenComment) { continue; } switch (state) { case 0: // default state if (token is TokenWord) { switch (token.Value.ToLower()) { case "private": flags |= ParseFlag.Private; break; case "public": flags |= ParseFlag.Public; break; case "protected": flags |= ParseFlag.Protected; break; case "static": flags |= ParseFlag.Static; break; case "abstract": flags |= ParseFlag.Abstract; break; case "override": flags |= ParseFlag.Override; break; case "final": flags |= ParseFlag.Final; break; case "void": case "class": case "character": case "integer": case "int64": case "decimal": case "date": case "datetime": case "datetime-tz": case "handle": case "logical": case "longchar": case "memptr": case "recid": case "rowid": case "raw": parsedReturnType = token.Value.ToUpper(); break; default: name = token.Value; break; } } if (token is TokenSymbol && token.Value.Equals("(")) { state = 1; } break; case 1: // read parameters, define a ParsedDefineItem for each parametersList = GetParsedParameters(methodToken, parameters); state = 2; break; } } while (MoveNext()); if (state < 1) { return(null); } var newMethod = new ParsedMethod(name, methodToken, parentScope, parsedReturnType) { // = end position of the EOS of the statement Flags = flags, EndPosition = token.EndPosition }; if (parametersList != null) { newMethod.Parameters = new List <ParsedDefine>(); foreach (var parsedItem in parametersList) { newMethod.Parameters.Add(parsedItem); } } AddParsedItem(newMethod, methodToken.OwnerNumber); return(newMethod); }