public List<TokenMatch> Parse(string grammar) { // $month:@starttoken.value $day:@number $dayend:( st | nd | rd | th )? ( ','? $year:@number )?" _pos = 0; _grammar = grammar; _errors = new List<string>(); END = _grammar.Length; _matches = new List<TokenMatch>(); while (_pos < END) { var c = _grammar[_pos]; // Case 1: Begin a token match. if (c == '$' || c == '\'') { var match = ReadToken(); AddMatch(match); } // Case 2: Move past white space. else if (c == '\t' || c == ' ') { _pos++; } // Case 3: Last token was optional else if (c == '?') { _pos++; var last = this.Last(); if(_groupCount > 0 && last.IsGroup) { var group = ((TokenGroup) last); last = group.Matches[group.Matches.Count - 1]; } last.IsRequired = false; } // Case 4: "(" starts a grouping of token matches else if( c == '(') { _pos++; this.BeginGroup(); } else if (c == ')') { _pos++; CloseGroup(); } // Case 5: word else if (char.IsLetter(c)) { var word = ReadWord(); var match = new TokenMatch(); match.Text = word; match.IsRequired = true; AddMatch(match); } else _pos++; } return _matches; }
private void AddMatch(TokenMatch match) { var last = Last(); if(_groupCount > 0 && last != null && last.IsGroup) { ((TokenGroup) last).Matches.Add(match); } else { _matches.Add(match); } }
/// <summary> /// Reads a token value : usually after ":" and includes type e.g. @number @bool /// </summary> /// <param name="match"></param> private void ReadTokenValue(TokenMatch match) { var c = _grammar[_pos]; if (c == '@') { _pos++; var tokenType = ReadWord(); match.TokenType = "@" + tokenType; if (_pos < END) { // Check for ".value" c = _grammar[_pos]; if (c == '.') { _pos++; var val = ReadWord(); if (val == "value") { match.TokenPropEnabled = true; match.TokenPropValue = val; } } } } else if (c == '(') { _pos++; var words = ReadList(); match.Values = words; } else if (c == ':') { _pos++; match.Text = ":"; } }
/// <summary> /// Reads a token e.g. usually begins with "$" /// </summary> /// <returns></returns> private TokenMatch ReadToken() { var start = _pos; var match = new TokenMatch(); match.IsRequired = true; while (_pos < END) { var c = _grammar[_pos]; // Case 1: Begin a token match. if (c == '$') { _pos++; var word = ReadWord(); match.Name = word; // Expect ":" followed by tokenvalue this.Expect(':'); this.ReadTokenValue(match); break; } // Case 2: Move past white space. else if (c == '\t' || c == ' ') { _pos++; } else if ( c == '\'') { _pos++; match.Text = ReadString(); break; } else break; } return match; }
public void AddMatch(TokenMatch m) { this.Matches.Add(m); }
private void DoParse() { while (_pos < END) { var c = _grammar[_pos]; // Case 1: Begin a token match. if (c == '$' || c == '\'') { var match = ReadToken(); AddMatch(match); } // Case 2: '@' specific type of word/identifier else if (c == '@') { _pos++; var tokenType = ReadWord(); var match = new TokenMatch(); match.TokenType = "@" + tokenType; match.IsRequired = true; AddMatch(match); } // Case 3: Move past white space. else if (c == '\t' || c == ' ') { _pos++; } // Case 4: Last token was optional else if (c == '?') { _pos++; var last = Last(); if (_groupCount > 0 && last.IsGroup) { var group = ((TokenGroup)last); last = group.Matches[group.Matches.Count - 1]; } last.IsRequired = false; } // Case 5: "(" starts a grouping of token matches else if (c == '(') { _pos++; BeginGroup(); } else if (c == ')') { _pos++; CloseGroup(); } // Case 6: word else if (char.IsLetter(c)) { var word = ReadWord(); var match = new TokenMatch(); match.Text = word; match.IsRequired = true; AddMatch(match); } // Case 7: | else if (c == '|') { _orCount++; _operatorStack.Add("|"); _pos++; } // Case 8: escape \ else if (c == '\\') { _pos++; var escapedChar = _grammar[_pos]; var match = new TokenMatch(); match.Text = escapedChar.ToString(); match.IsRequired = true; AddMatch(match); // Move past escaped char _pos++; } // Case 9: #reference else if (c == '#') { var match = ReadToken(); match.Ref = match.Name; match.Name = null; AddMatch(match); } else { _pos++; } } }