Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
DsvLiteral.cs
Go to the documentation of this file.
1 #region License
2 /* **********************************************************************************
3  * Copyright (c) Roman Ivantsov
4  * This source code is subject to terms and conditions of the MIT License
5  * for Irony. A copy of the license can be found in the License.txt file
6  * at the root of this distribution.
7  * By using this source code in any fashion, you are agreeing to be bound by the terms of the
8  * MIT License.
9  * You must not remove this notice from this software.
10  * **********************************************************************************/
11 #endregion
12 
13 using System;
14 using System.Collections.Generic;
15 using System.Linq;
16 using System.Text;
17 
18 namespace Irony.Parsing {
19 
20  //A terminal for DSV-formatted files (Delimiter-Separated Values), a generalization of CSV (comma-separated values) format.
21  // See http://en.wikipedia.org/wiki/Delimiter-separated_values
22  // For CSV format, there's a recommendation RFC4180 (http://tools.ietf.org/html/rfc4180)
23  // It might seem that this terminal is not that useful and it is easy enough to create a custom CSV reader for a particular data format
24  // format. However, if you consider all escaping and double-quote enclosing rules, then a custom reader solution would not seem so trivial.
25  // So DsvLiteral can simplify this task.
26  public class DsvLiteral : DataLiteralBase {
27  public string Terminator = ",";
28  public bool ConsumeTerminator = true; //if true, the source pointer moves after the separator
29  private char[] _terminators;
30 
31  //For last value on the line specify terminator = null; the DsvLiteral will then look for NewLine as terminator
32  public DsvLiteral(string name, TypeCode dataType, string terminator) : this(name, dataType) {
33  Terminator = terminator;
34  }
35  public DsvLiteral(string name, TypeCode dataType) : base(name, dataType) { }
36 
37  public override void Init(GrammarData grammarData) {
38  base.Init(grammarData);
39  if (Terminator == null)
40  _terminators = new char[] { '\n', '\r'};
41  else
42  _terminators = new char[] { Terminator[0]};
43  }
44 
45  protected override string ReadBody(ParsingContext context, ISourceStream source) {
46  string body;
47  if (source.PreviewChar == '"')
48  body = ReadQuotedBody(context, source);
49  else
50  body = ReadNotQuotedBody(context, source);
51  if (ConsumeTerminator && Terminator != null)
52  MoveSourcePositionAfterTerminator(source);
53  return body;
54  }
55 
56  private string ReadQuotedBody(ParsingContext context, ISourceStream source) {
57  const char dQuoute = '"';
58  StringBuilder sb = null;
59  var from = source.Location.Position + 1; //skip initial double quote
60  while(true) {
61  var until = source.Text.IndexOf(dQuoute, from);
62  if (until < 0)
63  throw new Exception(Resources.ErrDsvNoClosingQuote); // "Could not find a closing quote for quoted value."
64  source.PreviewPosition = until; //now points at double-quote
65  var piece = source.Text.Substring(from, until - from);
66  source.PreviewPosition++; //move after double quote
67  if (source.PreviewChar != dQuoute && sb == null)
68  return piece; //quick path - if sb (string builder) was not created yet, we are looking at the very first segment;
69  // and if we found a standalone dquote, then we are done - the "piece" is the result.
70  if (sb == null)
71  sb = new StringBuilder(100);
72  sb.Append(piece);
73  if (source.PreviewChar != dQuoute)
74  return sb.ToString();
75  //we have doubled double-quote; add a single double-quoute char to the result and move over both symbols
76  sb.Append(dQuoute);
77  from = source.PreviewPosition + 1;
78  }
79  }
80 
81  private string ReadNotQuotedBody(ParsingContext context, ISourceStream source) {
82  var startPos = source.Location.Position;
83  var sepPos = source.Text.IndexOfAny(_terminators, startPos);
84  if (sepPos < 0)
85  sepPos = source.Text.Length;
86  source.PreviewPosition = sepPos;
87  var valueText = source.Text.Substring(startPos, sepPos - startPos);
88  return valueText;
89  }
90 
91  private void MoveSourcePositionAfterTerminator(ISourceStream source) {
92  while(!source.EOF()) {
93  while(source.PreviewChar != Terminator[0])
94  source.PreviewPosition++;
95  if(source.MatchSymbol(Terminator, !Grammar.CaseSensitive)) {
96  source.PreviewPosition += Terminator.Length;
97  return;
98  }//if
99  }//while
100  }//method
101 
102  }//class
103 
104 
105 }//namespace
override string ReadBody(ParsingContext context, ISourceStream source)
Definition: DsvLiteral.cs:45
char PreviewChar
Gets a char at preview position
A strongly-typed resource class, for looking up localized strings, etc.
DsvLiteral(string name, TypeCode dataType, string terminator)
Definition: DsvLiteral.cs:32
override void Init(GrammarData grammarData)
Definition: DsvLiteral.cs:37
Interface for Terminals to access the source stream and produce tokens.
static string ErrDsvNoClosingQuote
Looks up a localized string similar to Could not find a closing quote for quoted value..
DsvLiteral(string name, TypeCode dataType)
Definition: DsvLiteral.cs:35