package ini

import (
	
	
)
State enums for the parse table
const (
stmt -> value stmt'
stmt' -> MarkComplete | op stmt
value -> number | string | boolean | quoted_string
section -> [ section'
section' -> value section_close
section_close -> ]
SkipState will skip (NL WS)+
SkipTokenState will skip any token and push the previous state onto the stack.
comment -> # comment' | ; comment' comment' -> MarkComplete | value
MarkComplete state will complete statements and move that to the completed AST list
TerminalState signifies that the tokens have been fully parsed
parseTable is a state machine to dictate the grammar above.
var parseTable = map[ASTKind]map[TokenType]int{
	ASTKindStart: map[TokenType]int{
		TokenLit:     StatementState,
		TokenSep:     OpenScopeState,
		TokenWS:      SkipTokenState,
		TokenNL:      SkipTokenState,
		TokenComment: CommentState,
		TokenNone:    TerminalState,
	},
	ASTKindCommentStatement: map[TokenType]int{
		TokenLit:     StatementState,
		TokenSep:     OpenScopeState,
		TokenWS:      SkipTokenState,
		TokenNL:      SkipTokenState,
		TokenComment: CommentState,
		TokenNone:    MarkCompleteState,
	},
	ASTKindExpr: map[TokenType]int{
		TokenOp:      StatementPrimeState,
		TokenLit:     ValueState,
		TokenSep:     OpenScopeState,
		TokenWS:      ValueState,
		TokenNL:      SkipState,
		TokenComment: CommentState,
		TokenNone:    MarkCompleteState,
	},
	ASTKindEqualExpr: map[TokenType]int{
		TokenLit: ValueState,
		TokenWS:  SkipTokenState,
		TokenNL:  SkipState,
	},
	ASTKindStatement: map[TokenType]int{
		TokenLit:     SectionState,
		TokenSep:     CloseScopeState,
		TokenWS:      SkipTokenState,
		TokenNL:      SkipTokenState,
		TokenComment: CommentState,
		TokenNone:    MarkCompleteState,
	},
	ASTKindExprStatement: map[TokenType]int{
		TokenLit:     ValueState,
		TokenSep:     OpenScopeState,
		TokenOp:      ValueState,
		TokenWS:      ValueState,
		TokenNL:      MarkCompleteState,
		TokenComment: CommentState,
		TokenNone:    TerminalState,
		TokenComma:   SkipState,
	},
	ASTKindSectionStatement: map[TokenType]int{
		TokenLit: SectionState,
		TokenOp:  SectionState,
		TokenSep: CloseScopeState,
		TokenWS:  SectionState,
		TokenNL:  SkipTokenState,
	},
	ASTKindCompletedSectionStatement: map[TokenType]int{
		TokenWS:      SkipTokenState,
		TokenNL:      SkipTokenState,
		TokenLit:     StatementState,
		TokenSep:     OpenScopeState,
		TokenComment: CommentState,
		TokenNone:    MarkCompleteState,
	},
	ASTKindSkipStatement: map[TokenType]int{
		TokenLit:     StatementState,
		TokenSep:     OpenScopeState,
		TokenWS:      SkipTokenState,
		TokenNL:      SkipTokenState,
		TokenComment: CommentState,
		TokenNone:    TerminalState,
	},
}
ParseAST will parse input from an io.Reader using an LL(1) parser.
func ( io.Reader) ([]AST, error) {
	 := iniLexer{}
	,  := .Tokenize()
	if  != nil {
		return []AST{}, 
	}

	return parse()
}
ParseASTBytes will parse input from a byte slice using an LL(1) parser.
func ( []byte) ([]AST, error) {
	 := iniLexer{}
	,  := .tokenize()
	if  != nil {
		return []AST{}, 
	}

	return parse()
}

func ( []Token) ([]AST, error) {
	 := Start
	 := newParseStack(3, len())

	.Push()
	 := newSkipper()

:
	for .Len() > 0 {
		 := .Pop()

		var  Token
this occurs when all the tokens have been processed but reduction of what's left on the stack needs to occur.
			 = emptyToken
		} else {
			 = [0]
		}

		 := parseTable[.Kind][.Type()]
being in a skip state with no tokens will break out of the parse loop since there is nothing left to process.
			if len() == 0 {
				break 
if should skip is true, we skip the tokens until should skip is set to false.
			 = SkipTokenState
		}

		switch  {
Finished parsing. Push what should be the last statement to the stack. If there is anything left on the stack, an error in parsing has occurred.
			if .Kind != ASTKindStart {
				.MarkComplete()
			}
			break 
When skipping a token, the previous state was popped off the stack. To maintain the correct state, the previous state will be pushed onto the stack.
			.Push()
		case StatementState:
			if .Kind != ASTKindStart {
				.MarkComplete()
			}
			 := newExpression()
			.Push()
		case StatementPrimeState:
			if .Type() != TokenOp {
				.MarkComplete()
				continue
			}

			if .Kind != ASTKindExpr {
				return nil, NewParseError(
					fmt.Sprintf("invalid expression: expected Expr type, but found %T type", ),
				)
			}

			 = trimSpaces()
			 := newEqualExpr(, )
			.Push()
ValueState requires the previous state to either be an equal expression or an expression statement. This grammar occurs when the RHS is a number, word, or quoted string. equal_expr -> lit op equal_expr' equal_expr' -> number | string | quoted_string quoted_string -> " quoted_string' quoted_string' -> string quoted_string_end quoted_string_end -> " otherwise expr_stmt -> equal_expr (expr_stmt')* expr_stmt' -> ws S | op S | MarkComplete S -> equal_expr' expr_stmt'
			switch .Kind {
assigning a value to some key
				.AppendChild(newExpression())
				.Push(newExprStatement())
			case ASTKindExpr:
				.Root.raw = append(.Root.raw, .Raw()...)
				.Push()
			case ASTKindExprStatement:
				 := .GetRoot()
				 := .GetChildren()
				if len() == 0 {
					return nil, NewParseError(
						fmt.Sprintf("invalid expression: AST contains no children %s", .Kind),
					)
				}

				 := [len()-1]

				if .Root.ValueType != QuotedStringType {
					.Root.ValueType = StringType
					.Root.raw = append(.Root.raw, .Raw()...)

				}

				[len()-1] = 
				.SetChildren()

				.Push()
			}
		case OpenScopeState:
			if !runeCompare(.Raw(), openBrace) {
				return nil, NewParseError("expected '['")
If OpenScopeState is not at the start, we must mark the previous ast as complete for example: if previous ast was a skip statement; we should mark it as complete before we create a new statement
			if .Kind != ASTKindStart {
				.MarkComplete()
			}

			 := newStatement()
			.Push()
		case CloseScopeState:
			if !runeCompare(.Raw(), closeBrace) {
				return nil, NewParseError("expected ']'")
			}

			 = trimSpaces()
			.Push(newCompletedSectionStatement())
		case SectionState:
			var  AST

			switch .Kind {
If there are multiple literals inside of a scope declaration, then the current token's raw value will be appended to the Name. This handles cases like [ profile default ] k will represent a SectionStatement with the children representing the label of the section
				 = newSectionStatement()
			case ASTKindSectionStatement:
				.Root.raw = append(.Root.raw, .Raw()...)
				 = 
			default:
				return nil, NewParseError(
					fmt.Sprintf("invalid statement: expected statement: %v", .Kind),
				)
			}

			.Push()
		case MarkCompleteState:
			if .Kind != ASTKindStart {
				.MarkComplete()
			}

			if .Len() == 0 {
				.Push()
			}
		case SkipState:
			.Push(newSkipStatement())
			.Skip()
		case CommentState:
			if .Kind == ASTKindStart {
				.Push()
			} else {
				.MarkComplete()
			}

			 := newCommentStatement()
			.Push()
		default:
			return nil, NewParseError(
				fmt.Sprintf("invalid state with ASTKind %v and TokenType %v",
					, .Type()))
		}

		if len() > 0 {
			 = [1:]
		}
	}
this occurs when a statement has not been completed
	if .top > 1 {
		return nil, NewParseError(fmt.Sprintf("incomplete ini expression"))
	}
returns a sublist which excludes the start symbol
	return .List(), nil
}
trimSpaces will trim spaces on the left and right hand side of the literal.
trim left hand side of spaces
	for  := 0;  < len(.Root.raw); ++ {
		if !isWhitespace(.Root.raw[]) {
			break
		}

		.Root.raw = .Root.raw[1:]
		--
	}
trim right hand side of spaces
	for  := len(.Root.raw) - 1;  >= 0; -- {
		if !isWhitespace(.Root.raw[]) {
			break
		}

		.Root.raw = .Root.raw[:len(.Root.raw)-1]
	}

	return