/// <summary> /// Matches a new definition /// </summary> private void CreateParsedDefine(Token functionToken, bool isDynamic) { // info we will extract from the current statement : string name = ""; ParsedAsLike asLike = ParsedAsLike.None; ParseDefineType type = ParseDefineType.None; string tempPrimitiveType = ""; string viewAs = ""; string bufferFor = ""; bool isExtended = false; _lastTokenWasSpace = true; StringBuilder left = new StringBuilder(); StringBuilder strFlags = new StringBuilder(); // for temp tables: string likeTable = ""; bool isTempTable = false; var fields = new List<ParsedField>(); ParsedField currentField = new ParsedField("", "", "", 0, ParsedFieldFlag.None, "", "", ParsedAsLike.None); StringBuilder useIndex = new StringBuilder(); bool isPrimary = false; Token token; int state = 0; do { token = PeekAt(1); // next token if (token is TokenEos) break; if (token is TokenComment) continue; string lowerToken; bool matchedLikeTable = false; switch (state) { case 0: // matching until type of define is found if (!(token is TokenWord)) break; lowerToken = token.Value.ToLower(); switch (lowerToken) { case "buffer": case "browse": case "stream": case "button": case "dataset": case "frame": case "query": case "event": case "image": case "menu": case "rectangle": case "property": case "sub-menu": case "parameter": var token1 = lowerToken; foreach (var typ in Enum.GetNames(typeof (ParseDefineType)).Where(typ => token1.Equals(typ.ToLower()))) { type = (ParseDefineType) Enum.Parse(typeof (ParseDefineType), typ, true); break; } state++; break; case "data-source": type = ParseDefineType.DataSource; state++; break; case "var": case "variable": type = ParseDefineType.Variable; state++; break; case "temp-table": case "work-table": case "workfile": isTempTable = true; state++; break; case "new": case "global": case "shared": case "private": case "protected": case "public": case "static": case "abstract": case "override": // flags found before the type if (strFlags.Length > 0) strFlags.Append(" "); strFlags.Append(lowerToken); break; case "input": case "output": case "input-output": case "return": // flags found before the type in case of a define parameter strFlags.Append(lowerToken); break; } break; case 1: // matching the name if (!(token is TokenWord)) break; name = token.Value; if (type == ParseDefineType.Variable) state = 10; if (type == ParseDefineType.Buffer) { tempPrimitiveType = "buffer"; state = 31; } if (type == ParseDefineType.Parameter) { lowerToken = token.Value.ToLower(); switch (lowerToken) { case "buffer": case "table": case "table-handle": case "dataset": case "dataset-handle": tempPrimitiveType = lowerToken; state = 30; break; default: state = 10; break; } } if (isTempTable) state = 20; if (state != 1) break; state = 99; break; case 10: // define variable : match as or like if (!(token is TokenWord)) break; lowerToken = token.Value.ToLower(); if (lowerToken.Equals("as")) asLike = ParsedAsLike.As; else if (lowerToken.Equals("like")) asLike = ParsedAsLike.Like; if (asLike != ParsedAsLike.None) state = 11; break; case 11: // define variable : match a primitive type or a field in db if (!(token is TokenWord)) break; tempPrimitiveType = token.Value; state = 12; break; case 12: // define variable : match a view-as (or extent) AddTokenToStringBuilder(left, token); if (!(token is TokenWord)) break; lowerToken = token.Value.ToLower(); if (lowerToken.Equals("view-as")) state = 13; if (lowerToken.Equals("extent")) isExtended = true; break; case 13: // define variable : match a view-as AddTokenToStringBuilder(left, token); if (!(token is TokenWord)) break; viewAs = token.Value; state = 99; break; case 20: // define temp-table if (!(token is TokenWord)) break; lowerToken = token.Value.ToLower(); switch (lowerToken) { case "field": // matches FIELD state = 22; break; case "index": // matches INDEX state = 25; break; case "use-index": // matches USE-INDEX (after a like/like-sequential, we can have this keyword) state = 26; break; case "help": // a field has a help text: state = 27; break; case "extent": // a field is extent: currentField.Flag = currentField.Flag | ParsedFieldFlag.Extent; break; default: // matches a LIKE table // ReSharper disable once ConditionIsAlwaysTrueOrFalse, resharper doesn't get this one if ((lowerToken.Equals("like") || lowerToken.Equals("like-sequential")) && !matchedLikeTable) state = 21; // After a USE-UNDEX and the index name, we can match a AS PRIMARY for the previously defined index if (lowerToken.Equals("primary") && useIndex.Length > 0) useIndex.Append("!"); break; } break; case 21: // define temp-table : match a LIKE table, get the table name in asLike // ReSharper disable once RedundantAssignment matchedLikeTable = true; if (!(token is TokenWord)) break; likeTable = token.Value.ToLower(); state = 20; break; case 22: // define temp-table : matches a FIELD name if (!(token is TokenWord)) break; currentField = new ParsedField(token.Value, "", "", 0, ParsedFieldFlag.None, "", "", ParsedAsLike.None); state = 23; break; case 23: // define temp-table : matches a FIELD AS or LIKE if (!(token is TokenWord)) break; currentField.AsLike = token.Value.EqualsCi("like") ? ParsedAsLike.Like : ParsedAsLike.As; state = 24; break; case 24: // define temp-table : match a primitive type or a field in db if (!(token is TokenWord)) break; currentField.TempType = token.Value; // push the field to the fields list fields.Add(currentField); state = 20; break; case 25: // define temp-table : match an index definition if (!(token is TokenWord)) break; lowerToken = token.Value.ToLower(); if (lowerToken.Equals("primary")) { // ReSharper disable once RedundantAssignment isPrimary = true; break; } var found = fields.Find(field => field.Name.EqualsCi(lowerToken)); if (found != null) found.Flag = isPrimary ? ParsedFieldFlag.Primary : ParsedFieldFlag.None; if (lowerToken.Equals("index")) // ReSharper disable once RedundantAssignment isPrimary = false; break; case 26: // define temp-table : match a USE-INDEX name if (!(token is TokenWord)) break; useIndex.Append(","); useIndex.Append(token.Value); state = 20; break; case 27: // define temp-table : match HELP for a field if (!(token is TokenString)) break; currentField.Description = GetTokenStrippedValue(token); state = 20; break; case 30: // define parameter : match a temptable, table, dataset or buffer name if (!(token is TokenWord)) break; if (token.Value.ToLower().Equals("for")) break; name = token.Value; state++; break; case 31: // match the table/dataset name that the buffer or handle is FOR if (!(token is TokenWord)) break; lowerToken = token.Value.ToLower(); if (lowerToken.Equals("for") || lowerToken.Equals("temp-table")) break; bufferFor = lowerToken; state = 99; break; case 99: // matching the rest of the define AddTokenToStringBuilder(left, token); break; } } while (MoveNext()); if (state <= 1) return; if (isTempTable) AddParsedItem(new ParsedTable(name, functionToken, "", "", name, "", likeTable, true, fields, new List<ParsedIndex>(), new List<ParsedTrigger>(), strFlags.ToString(), useIndex.ToString()) { // = end position of the EOS of the statement EndPosition = token.EndPosition }); else AddParsedItem(new ParsedDefine(name, functionToken, strFlags.ToString(), asLike, left.ToString(), type, tempPrimitiveType, viewAs, bufferFor, isExtended, isDynamic) { // = end position of the EOS of the statement EndPosition = token.EndPosition }); }
/// <summary> /// This method parses the output of the .p procedure that exports the database info /// and fills _dataBases /// It then updates the parser with the new info /// </summary> private static void Read(string filePath) { if (!File.Exists(filePath)) return; _dataBases.Clear(); _sequences.Clear(); var defaultToken = new TokenEos(null, 0, 0, 0, 0); ParsedDataBase currentDb = null; ParsedTable currentTable = null; Utils.ForEachLine(filePath, null, (i, items) => { var splitted = items.Split('\t'); switch (items[0]) { case 'H': // base //#H|<Dump date ISO 8601>|<Dump time>|<Logical DB name>|<Physical DB name>|<Progress version> if (splitted.Count() != 6) return; currentDb = new ParsedDataBase( splitted[3], splitted[4], splitted[5], new List<ParsedTable>()); _dataBases.Add(currentDb); break; case 'S': if (splitted.Count() != 3 || currentDb == null) return; _sequences.Add(new CompletionItem { DisplayText = splitted[1], Type = CompletionType.Sequence, SubString = currentDb.LogicalName }); break; case 'T': // table //#T|<Table name>|<Table ID>|<Table CRC>|<Dump name>|<Description> if (splitted.Count() != 6 || currentDb == null) return; currentTable = new ParsedTable( splitted[1], defaultToken, splitted[2], splitted[3], splitted[4], splitted[5], "", false, new List<ParsedField>(), new List<ParsedIndex>(), new List<ParsedTrigger>() , "", ""); currentDb.Tables.Add(currentTable); break; case 'X': // trigger //#X|<Parent table>|<Event>|<Proc name>|<Trigger CRC> if (splitted.Count() != 5 || currentTable == null) return; currentTable.Triggers.Add(new ParsedTrigger( splitted[2], splitted[3])); break; case 'I': // index //#I|<Parent table>|<Index name>|<Primary? 0/1>|<Unique? 0/1>|<Index CRC>|<Fileds separated with %> if (splitted.Count() != 7 || currentTable == null) return; var flag = splitted[3].Equals("1") ? ParsedIndexFlag.Primary : ParsedIndexFlag.None; if (splitted[4].Equals("1")) flag = flag | ParsedIndexFlag.Unique; currentTable.Indexes.Add(new ParsedIndex( splitted[2], flag, splitted[6].Split('%').ToList())); break; case 'F': // field //#F|<Parent table>|<Field name>|<Type>|<Format>|<Order #>|<Mandatory? 0/1>|<Extent? 0/1>|<Part of index? 0/1>|<Part of PK? 0/1>|<Initial value>|<Desription> if (splitted.Count() != 12 || currentTable == null) return; var flag2 = splitted[6].Equals("1") ? ParsedFieldFlag.Mandatory : ParsedFieldFlag.None; if (splitted[7].Equals("1")) flag2 = flag2 | ParsedFieldFlag.Extent; if (splitted[8].Equals("1")) flag2 = flag2 | ParsedFieldFlag.Index; if (splitted[9].Equals("1")) flag2 = flag2 | ParsedFieldFlag.Primary; var curField = new ParsedField( splitted[2], splitted[3], splitted[4], int.Parse(splitted[5]), flag2, splitted[10], splitted[11], ParsedAsLike.None); curField.Type = ParserHandler.ConvertStringToParsedPrimitiveType(curField.TempType, false); currentTable.Fields.Add(curField); break; } }); }