Ast.SetNode ParseSet(SeekableStringReader sr) { // set = '{' expr_list trailing_comma '}' . // trailing_comma = '' | ',' . sr.Read(); // { sr.SkipWhitespace(); Ast.SetNode setnode = new Ast.SetNode(); List <Ast.INode> elts = ParseExprList(sr); // handle trailing comma if present sr.SkipWhitespace(); if (!sr.HasMore()) { throw new ParseException("missing '}'"); } if (sr.Peek() == ',') { sr.Read(); } if (!sr.HasMore()) { throw new ParseException("missing '}'"); } char closechar = sr.Read(); if (closechar != '}') { throw new ParseException("expected '}'"); } // make sure it has set semantics (remove duplicate elements) HashSet <Ast.INode> h = new HashSet <Ast.INode>(elts); setnode.Elements = new List <Ast.INode>(h); return(setnode); }
Ast.INode ParseCompound(SeekableStringReader sr) { // compound = tuple | dict | list | set . sr.SkipWhitespace(); switch (sr.Peek()) { case '[': return(ParseList(sr)); case '{': { int bm = sr.Bookmark(); try { return(ParseSet(sr)); } catch (ParseException) { sr.FlipBack(bm); return(ParseDict(sr)); } } case '(': // tricky case here, it can be a tuple but also a complex number. // try complex number first { int bm = sr.Bookmark(); try { return(ParseComplex(sr)); } catch (ParseException) { sr.FlipBack(bm); return(ParseTuple(sr)); } } default: throw new ParseException("invalid sequencetype char"); } }
private Ast.PrimitiveNode <string> ParseString(SeekableStringReader sr) { char quotechar = sr.Read(); // ' or " StringBuilder sb = new StringBuilder(10); while (sr.HasMore()) { char c = sr.Read(); if (c == '\\') { // backslash unescape c = sr.Read(); switch (c) { case '\\': sb.Append('\\'); break; case '\'': sb.Append('\''); break; case '"': sb.Append('"'); break; case 'a': sb.Append('\a'); break; case 'b': sb.Append('\b'); break; case 'f': sb.Append('\f'); break; case 'n': sb.Append('\n'); break; case 'r': sb.Append('\r'); break; case 't': sb.Append('\t'); break; case 'v': sb.Append('\v'); break; case 'x': // "\x00" sb.Append((char)int.Parse(sr.Read(2), NumberStyles.HexNumber)); break; case 'u': // "\u0000" sb.Append((char)int.Parse(sr.Read(4), NumberStyles.HexNumber)); break; default: sb.Append(c); break; } } else if (c == quotechar) { // end of string return(new Ast.StringNode(sb.ToString())); } else { sb.Append(c); } } throw new ParseException("unclosed string"); }
private double ParseImaginaryPart(SeekableStringReader sr) { //imaginary = ['+' | '-' ] ( float | int ) 'j' . // string numberstr = sr.ReadUntil('j'); // try { // return this.ParseDouble(numberstr); // } catch(FormatException x) { // throw new ParseException("invalid float format", x); // } if (!sr.HasMore()) { throw new ParseException("unexpected end of input string"); } char signOrDigit = sr.Peek(); if (signOrDigit == '+') { sr.Read(); // skip the '+' } // now an int or float follows. double doubleValue; int bookmark = sr.Bookmark(); try { doubleValue = ParseFloat(sr).Value; } catch (ParseException) { sr.FlipBack(bookmark); var integerPart = ParseInt(sr); var integerNode = integerPart as Ast.IntegerNode; if (integerNode != null) { doubleValue = integerNode.Value; } else { var longNode = integerPart as Ast.LongNode; if (longNode != null) { doubleValue = longNode.Value; } else { var decimalNode = integerPart as Ast.DecimalNode; if (decimalNode != null) { doubleValue = Convert.ToDouble(decimalNode.Value); } else { throw new ParseException("not an integer for the imaginary part"); } } } } // now a 'j' must follow! sr.SkipWhitespace(); try { if (sr.Read() != 'j') { throw new ParseException("not an imaginary part"); } } catch (IndexOutOfRangeException) { throw new ParseException("not an imaginary part"); } return(doubleValue); }
private Ast.ComplexNumberNode ParseComplex(SeekableStringReader sr) { //complex = complextuple | imaginary . //imaginary = ['+' | '-' ] ( float | int ) 'j' . //complextuple = '(' ( float | int ) imaginary ')' . if (sr.Peek() == '(') { // complextuple sr.Read(); // ( string numberstr; if (sr.Peek() == '-' || sr.Peek() == '+') { // starts with a sign, read that first otherwise the readuntil will return immediately numberstr = sr.Read(1) + sr.ReadUntil('+', '-'); } else { numberstr = sr.ReadUntil('+', '-'); } sr.Rewind(1); // rewind the +/- // because we're a bit more cautious here with reading chars than in the float parser, // it can be that the parser now stopped directly after the 'e' in a number like "3.14e+20". // ("3.14e20" is fine) So, check if the last char is 'e' and if so, continue reading 0..9. if (numberstr.EndsWith("e", StringComparison.InvariantCultureIgnoreCase)) { // if the next symbol is + or -, accept it, then read the exponent integer if (sr.Peek() == '-' || sr.Peek() == '+') { numberstr += sr.Read(1); } numberstr += sr.ReadWhile("0123456789"); } sr.SkipWhitespace(); double realpart; try { realpart = ParseDouble(numberstr); } catch (FormatException x) { throw new ParseException("invalid float format", x); } double imaginarypart = ParseImaginaryPart(sr); if (sr.Read() != ')') { throw new ParseException("expected ) to end a complex number"); } return(new Ast.ComplexNumberNode { Real = realpart, Imaginary = imaginarypart }); } // imaginary double imag = ParseImaginaryPart(sr); return(new Ast.ComplexNumberNode { Real = 0, Imaginary = imag }); }
private Ast.INode ParseDict(SeekableStringReader sr) { //dict = '{' keyvalue_list trailing_comma '}' . //keyvalue_list = keyvalue { ',' keyvalue } . //keyvalue = expr ':' expr . // trailing_comma = '' | ',' . sr.Read(); // { sr.SkipWhitespace(); Ast.DictNode dict = new Ast.DictNode(); if (sr.Peek() == '}') { sr.Read(); return(dict); // empty dict } var elts = ParseKeyValueList(sr); // handle trailing comma if present sr.SkipWhitespace(); if (!sr.HasMore()) { throw new ParseException("missing '}'"); } if (sr.Peek() == ',') { sr.Read(); } if (!sr.HasMore()) { throw new ParseException("missing '}'"); } char closechar = sr.Read(); if (closechar != '}') { throw new ParseException("expected '}'"); } // make sure it has dict semantics (remove duplicate keys) var fixedDict = new Dictionary <Ast.INode, Ast.INode>(elts.Count); foreach (var node in elts) { var kv = (Ast.KeyValueNode)node; fixedDict[kv.Key] = kv.Value; } foreach (var kv in fixedDict) { dict.Elements.Add(new Ast.KeyValueNode { Key = kv.Key, Value = kv.Value }); } // SPECIAL CASE: {'__class__':'float','value':'nan'} ---> Double.NaN if (dict.Elements.Count != 2) { return(dict); } if (!dict.Elements.Contains(new Ast.KeyValueNode(new Ast.StringNode("__class__"), new Ast.StringNode("float")))) { return(dict); } if (dict.Elements.Contains(new Ast.KeyValueNode(new Ast.StringNode("value"), new Ast.StringNode("nan")))) { return(new Ast.DoubleNode(double.NaN)); } return(dict); }
private Ast.TupleNode ParseTuple(SeekableStringReader sr) { //tuple = tuple_empty | tuple_one | tuple_more //tuple_empty = '()' . //tuple_one = '(' expr ',' <whitespace> ')' . //tuple_more = '(' expr_list trailing_comma ')' . // trailing_comma = '' | ',' . sr.Read(); // ( sr.SkipWhitespace(); Ast.TupleNode tuple = new Ast.TupleNode(); if (sr.Peek() == ')') { sr.Read(); return(tuple); // empty tuple } Ast.INode firstelement = ParseExpr(sr); if (sr.Peek() == ',') { sr.Read(); sr.SkipWhitespace(); if (sr.Read() == ')') { // tuple with just a single element tuple.Elements.Add(firstelement); return(tuple); } sr.Rewind(1); // undo the thing that wasn't a ) } tuple.Elements = ParseExprList(sr); tuple.Elements.Insert(0, firstelement); // handle trailing comma if present sr.SkipWhitespace(); if (!sr.HasMore()) { throw new ParseException("missing ')'"); } if (sr.Peek() == ',') { sr.Read(); } if (!sr.HasMore()) { throw new ParseException("missing ')'"); } char closechar = sr.Read(); if (closechar == ',') { closechar = sr.Read(); } if (closechar != ')') { throw new ParseException("expected ')'"); } return(tuple); }
/// <summary> /// Make a nested reader with its own cursor and bookmark. /// The cursor starts at the same position as the parent. /// </summary> /// <param name="parent"></param> public SeekableStringReader(SeekableStringReader parent) { str = parent.str; cursor = parent.cursor; }
/// <summary> /// Sync the position and bookmark with the current position in another reader. /// </summary> public void Sync(SeekableStringReader inner) { bookmark = inner.bookmark; cursor = inner.cursor; }
/// <summary> /// Make a nested reader with its own cursor and bookmark. /// The cursor starts at the same position as the parent. /// </summary> /// <param name="parent"></param> public SeekableStringReader(SeekableStringReader parent) { _str = parent._str; _cursor = parent._cursor; }
/// <summary> /// Sync the position and bookmark with the current position in another reader. /// </summary> public void Sync(SeekableStringReader inner) { _bookmark = inner._bookmark; _cursor = inner._cursor; }