private static bool TryParseSecondaryAddress(ref ParserState state, out SecondaryAddressSyntaxNode result)
        {
            result = default;

            var snapshot = state;

            state.ConsumeWhiteSpace();

            foreach (var(expanded, abbreviated, requiresRange) in PostalDesignators.SecondaryAddressUnitDesignators)
            {
                if (!requiresRange && state.TryConsume(t => t.Matches(expanded) || t.Matches(abbreviated)))
                {
                    result = new SecondaryAddressSyntaxNode(abbreviated, state.TakeBuffer());

                    return(true);
                }
            }

            if (!TryParseStreetSuffix(ref state, out _) && TryParseRange(ref state, out var rangeUnitIdentifier))
            {
                state.ConsumeWhiteSpace();

                string designator = null;

                if (state.TryConsume(t => t.IsOctothorpe))
                {
                    designator = "#";
                    state.ConsumeWhiteSpace();
                }

                if (state.TryConsume(t => t.IsCommaOrPeriod))
                {
                    state.ConsumeWhiteSpace();
                }

                foreach (var(expanded, abbreviated, _) in PostalDesignators.SecondaryAddressUnitDesignators)
                {
                    if (state.TryConsume(t => t.Matches(expanded) || t.Matches(abbreviated)))
                    {
                        designator = abbreviated;
                        break;
                    }
                }

                if (designator != null)
                {
                    result = new SecondaryAddressSyntaxNode(designator, state.TakeBuffer(), rangeUnitIdentifier);

                    return(true);
                }
            }

            state = snapshot;

            return(false);
        }
        private static bool TryParsePostOfficeBoxKeywords(ref ParserState state, out ImmutableArray <Token> result)
        {
            result = default;

            var snapshot = state;

            state.ConsumeWhiteSpace();

            if (state.TryConsume("office"))
            {
                state.ConsumeWhiteSpace();

                if (state.TryConsume("post"))
                {
                    result = state.TakeBuffer();
                }

                return(true);
            }
            else
            {
                state.TryConsume(t => t.IsCommaOrPeriod);

                state.ConsumeWhiteSpace();

                if (state.TryConsume("po"))
                {
                    result = state.TakeBuffer();

                    return(true);
                }
                else if (state.TryConsume("o"))
                {
                    state.ConsumeWhiteSpace();

                    state.TryConsume(t => t.IsCommaOrPeriod);

                    state.ConsumeWhiteSpace();

                    if (state.TryConsume("p"))
                    {
                        result = state.TakeBuffer();

                        return(true);
                    }
                }
            }

            state = snapshot;

            return(false);
        }
        private static bool TryParseStreetSuffix(ref ParserState state, out StreetSuffixSyntaxNode result)
        {
            result = default;

            var snapshot = state;

            state.ConsumeWhiteSpace();

            state.TryConsume(t => t.IsCommaOrPeriod);

            foreach (var(common, expanded, abbreviated) in PostalDesignators.StreetSuffixes)
            {
                // The check against a current numeric token prevents us from errantly picking
                // up things like the "RD" in "3RD".
                if (state.TryConsume(t => t.Matches(common)) && !state.Current.IsNumeric)
                {
                    result = new StreetSuffixSyntaxNode(abbreviated, state.TakeBuffer());

                    return(true);
                }
            }

            state = snapshot;

            return(false);
        }
        private static bool TryParsePostOfficeBox(ref ParserState state, out PostOfficeBoxSyntaxNode result)
        {
            result = null;

            var snapshot = state;

            if (TryParseRange(ref state, out var boxNumber))
            {
                state.ConsumeWhiteSpace();

                if (state.TryConsume(t => t.IsOctothorpe))
                {
                    state.ConsumeWhiteSpace();
                }

                if (state.TryConsume("caller"))
                {
                    state.ConsumeWhiteSpace();

                    state.TryConsume("firm");
                }
                else if (state.TryConsume(t => t.Matches("box") || t.Matches("drawer") || t.Matches("bin") || t.Matches("drawer") || t.Matches("lockbox")))
                {
                    // no-op
                }
                else
                {
                    goto Failure;
                }

                var boxKeywordTokens = state.TakeBuffer();

                if (TryParsePostOfficeBoxKeywords(ref state, out var additionalTokens))
                {
                    boxKeywordTokens = additionalTokens.AddRange(boxKeywordTokens);
                }

                result = new PostOfficeBoxSyntaxNode(boxKeywordTokens, boxNumber);

                return(true);
            }

Failure:
            state = snapshot;
            return(false);
        }
        private static bool TryParseRange(ref ParserState state, out RangeSyntaxNode result)
        {
            result = null;

            var snapshot = state;

            state.ConsumeWhiteSpace();

            var fraction = Token.None;

            if (state.TryConsume(t => t.Kind == TokenKind.Fraction))
            {
                fraction = state.Buffer.Last();

                state.ConsumeWhiteSpace();
            }

            if (state.TryConsume(t => t.IsRangeComponent))
            {
                var tokens = state.TakeBuffer();

                result = new RangeSyntaxNode(
                    tokens.Take(1).ToImmutableArray(),
                    fraction,
                    tokens);

                TryParseRangeAppend(ref state, ref result);

                if (result != null)
                {
                    state.TakeBuffer();
                    return(true);
                }
            }

            state = snapshot;

            return(false);
        }
        private static bool TryParseHighwayContractRouteBox(ref ParserState state, out HighwayContractRouteBoxSyntaxNode result)
        {
            result = null;

            var snapshot = state;

            RangeSyntaxNode        routeNumber;
            ImmutableArray <Token> boxKeywordTokens;

            if (TryParseRange(ref state, out var boxNumber))
            {
                state.ConsumeWhiteSpace();

                if (state.TryConsume(t => t.IsOctothorpe))
                {
                    state.ConsumeWhiteSpace();
                }

                if (state.TryConsume("box"))
                {
                    state.ConsumeWhiteSpace();

                    if (state.TryConsume(t => t.IsCommaOrPeriod))
                    {
                        state.ConsumeWhiteSpace();
                    }

                    boxKeywordTokens = state.TakeBuffer();

                    if (TryParseRange(ref state, out routeNumber))
                    {
                        state.ConsumeWhiteSpace();

                        if (state.TryConsume(t => t.IsOctothorpe))
                        {
                            state.ConsumeWhiteSpace();
                        }

                        if (state.TryConsume(t => t.IsCommaOrPeriod))
                        {
                            state.ConsumeWhiteSpace();
                        }

                        if (state.TryConsume("hc"))
                        {
                            goto Parsed;
                        }

                        if (state.TryConsume("c"))
                        {
                            state.ConsumeWhiteSpace();

                            if (state.TryConsume(t => t.IsCommaOrPeriod))
                            {
                                state.ConsumeWhiteSpace();
                            }

                            if (state.TryConsume("h"))
                            {
                                goto Parsed;
                            }
                        }
                        else if (state.TryConsume(t => t.Matches("route") || t.Matches("rt")))
                        {
                            state.ConsumeWhiteSpace();

                            if (state.TryConsume("contract"))
                            {
                                state.ConsumeWhiteSpace();

                                if (state.TryConsume("highway"))
                                {
                                    goto Parsed;
                                }
                            }
                            else if (state.TryConsume("star"))
                            {
                                goto Parsed;
                            }
                        }
                        else if (state.TryConsume("contract"))
                        {
                            state.ConsumeWhiteSpace();

                            if (state.TryConsume("highway"))
                            {
                                goto Parsed;
                            }
                        }
                    }
                }
            }

            state = snapshot;

            return(false);

Parsed:

            result = new HighwayContractRouteBoxSyntaxNode(
                state.TakeBuffer(),
                routeNumber,
                boxKeywordTokens,
                boxNumber);

            return(true);
        }
        private static bool TryParseDirectional(ref ParserState state, out DirectionalSyntaxNode result)
        {
            result = default;

            var snapshot = state;

            state.ConsumeWhiteSpace();

            if (state.TryConsume("northeast"))
            {
                result = new DirectionalSyntaxNode("NE", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("northwest"))
            {
                result = new DirectionalSyntaxNode("NW", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("southeast"))
            {
                result = new DirectionalSyntaxNode("SE", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("southwest"))
            {
                result = new DirectionalSyntaxNode("SW", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("north"))
            {
                result = new DirectionalSyntaxNode("N", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("south"))
            {
                result = new DirectionalSyntaxNode("S", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("east"))
            {
                result = new DirectionalSyntaxNode("E", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("west"))
            {
                result = new DirectionalSyntaxNode("W", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("ne"))
            {
                result = new DirectionalSyntaxNode("NE", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("nw"))
            {
                result = new DirectionalSyntaxNode("NW", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("se"))
            {
                result = new DirectionalSyntaxNode("SE", state.TakeBuffer());
                return(true);
            }
            else if (state.TryConsume("sw"))
            {
                result = new DirectionalSyntaxNode("SW", state.TakeBuffer());
                return(true);
            }
            else
            {
                state.TryConsume(t => t.IsCommaOrPeriod);

                if (state.TryConsume("e"))
                {
                    //parser.ConsumeWhiteSpace();

                    if (state.TryConsume(t => t.IsCommaOrPeriod))
                    {
                        //parser.ConsumeWhiteSpace();
                    }

                    if (state.TryConsume("n"))
                    {
                        result = new DirectionalSyntaxNode("NE", state.TakeBuffer());
                    }
                    else if (state.TryConsume("s"))
                    {
                        result = new DirectionalSyntaxNode("SE", state.TakeBuffer());
                    }
                    else
                    {
                        result = new DirectionalSyntaxNode("E", state.TakeBuffer());
                    }

                    return(true);
                }
                else if (state.TryConsume("w"))
                {
                    //parser.ConsumeWhiteSpace();

                    if (state.TryConsume(t => t.IsCommaOrPeriod))
                    {
                        //parser.ConsumeWhiteSpace();
                    }

                    if (state.TryConsume("n"))
                    {
                        result = new DirectionalSyntaxNode("NW", state.TakeBuffer());
                    }
                    else if (state.TryConsume("s"))
                    {
                        result = new DirectionalSyntaxNode("SW", state.TakeBuffer());
                    }
                    else
                    {
                        result = new DirectionalSyntaxNode("W", state.TakeBuffer());
                    }

                    return(true);
                }
                else if (state.TryConsume("s"))
                {
                    result = new DirectionalSyntaxNode("S", state.TakeBuffer());

                    return(true);
                }
                else if (state.TryConsume("n"))
                {
                    result = new DirectionalSyntaxNode("N", state.TakeBuffer());

                    return(true);
                }
            }

            state = snapshot;

            return(false);
        }