Example #1
0
        ParserOutput ParseOneNonAdecl(CharBuffer chars, [NotNull] out ISourceLine sourceLine)
        {
            try
            {
                // handle whitespace
                if (!SkipWhitespace(chars))
                {
                    sourceLine = new FileSourceLine(site.CurrentFilePath, line);
                    return(ParserOutput.EndOfInput);
                }

                sourceLine = new FileSourceLine(site.CurrentFilePath, line);
                var c = chars.Current;

                // '!' adds 128 to the next character (assuming it's below 128)
                if (c == '!')
                {
                    if (!chars.MoveNext())
                    {
                        throw new ExpectedButFound("character after '!'", "<EOF>");
                    }

                    c = chars.Current;

                    // two bangs in a row? preposterous.
                    if (c == '!')
                    {
                        throw new ExpectedButFound("character after '!'", "another '!'");
                    }

                    if (c < 128)
                    {
                        c += (char)128;
                    }
                }

                switch (c)
                {
                case '(':
                    return(ParserOutput.FromObject(
                               ParseCurrentStructure(
                                   chars,
                                   ')', Bang.RightParen,
                                   zos => new ZilList(zos))));

                case '<':
                    return(ParserOutput.FromObject(
                               ParseCurrentStructure(
                                   chars,
                                   '>', Bang.RightAngle,
                                   zos => new ZilForm(zos))));

                case '[':
                    return(ParserOutput.FromObject(
                               ParseCurrentStructure(
                                   chars,
                                   ']', Bang.RightBracket,
                                   zos => new ZilVector(zos.ToArray()))));

                case Bang.LeftParen:
                    // !(foo!) is identical to (foo)
                    return(ParserOutput.FromObject(
                               ParseCurrentStructure(
                                   chars,
                                   ')', Bang.RightParen,
                                   zos => new ZilList(zos))));

                case Bang.LeftAngle:
                    // !<foo!> is a segment
                    return(ParserOutput.FromObject(
                               ParseCurrentStructure(
                                   chars,
                                   '>', Bang.RightAngle,
                                   zos => new ZilSegment(new ZilForm(zos)))));

                case Bang.LeftBracket:
                    // ![foo!] is a uvector, but we alias it to vector
                    return(ParserOutput.FromObject(
                               ParseCurrentStructure(
                                   chars,
                                   ']', Bang.RightBracket,
                                   zos => new ZilVector(zos.ToArray()))));

                case Bang.Dot:
                case Bang.Comma:
                case Bang.SingleQuote:
                    // !.X is equivalent to !<LVAL X>, and so on
                    chars.PushBack((char)(c - 128));
                    return(ParsePrefixed(chars, c,
                                         zo => ParserOutput.FromObject(new ZilSegment(zo))));

                case '{':
                case Bang.LeftCurly:
                    return(ParseCurrentStructure(
                               chars,
                               '}', Bang.RightCurly,
                               zos =>
                    {
                        var zarr = zos.ToArray();
                        if (zarr.Length != 1)
                        {
                            throw new ExpectedButFound("1 object inside '{}'", zarr.Length.ToString());
                        }
                        foreach (var zo in ZilObject.ExpandTemplateToken(zarr[0], templateParams))
                        {
                            heldObjects.Enqueue(zo);
                        }
                        return heldObjects.Count == 0
                                    ? ParserOutput.EmptySplice
                                    : ParserOutput.FromObject(heldObjects.Dequeue());
                    }));

                case '.':
                    return(ParsePrefixed(chars, c,
                                         zo => ParserOutput.FromObject(
                                             new ZilForm(new[] { site.ParseAtom("LVAL"), zo }))));

                case ',':
                    return(ParsePrefixed(chars, c,
                                         zo => ParserOutput.FromObject(
                                             new ZilForm(new[] { site.ParseAtom("GVAL"), zo }))));

                case '\'':
                    return(ParsePrefixed(chars, c,
                                         zo => ParserOutput.FromObject(
                                             new ZilForm(new[] { site.ParseAtom("QUOTE"), zo }))));

                case '%':
                case Bang.Percent:
                    bool drop = false;
                    if (chars.MoveNext())
                    {
                        c = chars.Current;
                        if (c == '%' || c == Bang.Percent)
                        {
                            drop = true;
                        }
                        else
                        {
                            chars.PushBack(c);
                        }
                    }

                    var po = ParsePrefixed(chars, c,
                                           zo => ParserOutput.FromObject(site.Evaluate(zo)));

                    if (po.Type != ParserOutputType.Object)
                    {
                        return(po);
                    }

                    if (drop)
                    {
                        return(ParserOutput.EmptySplice);
                    }

                    if (po.Object is ZilSplice splice)
                    {
                        foreach (var zo in splice)
                        {
                            heldObjects.Enqueue(zo);
                        }

                        return(heldObjects.Count == 0
                                ? ParserOutput.EmptySplice
                                : ParserOutput.FromObject(heldObjects.Dequeue()));
                    }

                    return(po);

                case '#':
                case Bang.Hash:
                    return(ParsePrefixed(
                               chars,
                               c,
                               zo =>
                    {
                        switch (zo)
                        {
                        case ZilFix fix when fix.Value == 2:
                            if (!SkipWhitespace(chars))
                            {
                                throw new ExpectedButFound("binary number after '#2'", "<EOF>");
                            }

                            var sb = new StringBuilder();

                            bool run = true;
                            do
                            {
                                var c2 = chars.Current;
                                switch (c2)
                                {
                                case '0':
                                case '1':
                                    sb.Append(c2);
                                    break;

                                case var _ when c2.IsTerminator():
                                    chars.PushBack(c2);
                                    run = false;
                                    break;

                                default:
                                    throw new ExpectedButFound("binary number after '#2'", $"{sb}{c2}");
                                }
                            } while (run && chars.MoveNext());

                            try
                            {
                                return ParserOutput.FromObject(new ZilFix(Convert.ToInt32(sb.ToString(), 2)));
                            }
                            catch (OverflowException ex)
                            {
                                throw new ParsedNumberOverflowed(sb.ToString(), "binary", ex);
                            }

                        case ZilAtom atom:
                            return ParsePrefixed(
                                chars,
                                atom.Text,
                                zo2 => ParserOutput.FromObject(site.ChangeType(zo2, atom)));
                        }

                        throw new ExpectedButFound($"atom or '2' after '{c.Rebang()}'", site.GetTypeAtom(zo).ToString());
                    }));

                case ';':
                case Bang.Semicolon:
                    return(ParsePrefixed(chars, c, ParserOutput.FromComment));

                case '"':
                    return(ParserOutput.FromObject(ParseCurrentString(chars)));

                case var _ when c.IsTerminator():
                    chars.PushBack(c);

                    return(ParserOutput.Terminator);

                case Bang.Backslash:
                case Bang.DoubleQuote:
                    if (chars.MoveNext())
                    {
                        return(ParserOutput.FromObject(new ZilChar(chars.Current)));
                    }
                    throw new ExpectedButFound("character after '!\\'", "<EOF>");

                case var _ when c.IsNonAtomChar():
                    throw new ExpectedButFound("atom", $"'{c.Rebang()}'");

                default:
                    return(ParserOutput.FromObject(ParseCurrentAtomOrNumber(chars)));
                }
            }
            catch (ParserException ex)
            {
                sourceLine = new FileSourceLine(site.CurrentFilePath, line);
                return(ParserOutput.FromException(ex));
            }
        }