package parser

import (
	"fmt"
	"os"
	"strconv"

	"gitlab.com/olanguage/olang/ast"
	"gitlab.com/olanguage/olang/color"
	"gitlab.com/olanguage/olang/lexer"
	"gitlab.com/olanguage/olang/token"
)

const (
	_ int = iota
	LOWEST
	EQUALS           // ==
	LESSGREATER      // > or <
	LESSEQUALGREATER // >= or <=
	SUM              // +
	PRODUCT          // *
	PREFIX           // -X or !X
	CALL             // myFunction(X)
	INDEX            // array[index]
)

var scopeList = make([]ast.Expression, 0)

var precedences = map[token.TokenType]int{
	token.EQ:       EQUALS,
	token.NOT_EQ:   EQUALS,
	token.LT:       LESSGREATER,
	token.GT:       LESSGREATER,
	token.LT_EQ:    LESSEQUALGREATER,
	token.GT_EQ:    LESSEQUALGREATER,
	token.PLUS:     SUM,
	token.ADD_PLUS: SUM,
	token.MINUS:    SUM,
	token.SLASH:    PRODUCT,
	token.ASTERISK: PRODUCT,
	token.LPAREN:   CALL,
	token.LBRAKET:  INDEX,
}

type (
	prefixParseFn func() ast.Expression
	infixParseFn  func(ast.Expression) ast.Expression
)

type Parser struct {
	l           *lexer.Lexer
	errors      []string
	errorTokens []interface{}

	curToken  token.Token
	peekToken token.Token

	prefixParseFns map[token.TokenType]prefixParseFn
	infixParseFns  map[token.TokenType]infixParseFn
}

func New(l *lexer.Lexer) *Parser {
	p := &Parser{
		l:      l,
		errors: []string{},
	}

	// Read two tokens, so curToken and peekToken are both set
	p.nextToken()
	p.nextToken()

	p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
	p.registerPrefix(token.IDENT, p.parseIdentifier)
	p.registerPrefix(token.INT, p.parseIntegerLiteral)
	p.registerPrefix(token.FLOAT, p.parseFloatLiteral)
	p.registerPrefix(token.BANG, p.parsePrefixExpression)
	p.registerPrefix(token.MINUS, p.parsePrefixExpression)
	p.registerPrefix(token.TRUE, p.parseBoolean)
	p.registerPrefix(token.FALSE, p.parseBoolean)
	p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
	p.registerPrefix(token.IF, p.parseIfExpression)
	p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral)
	p.registerPrefix(token.LITERAL, p.parseCustomLiteral)
	p.registerPrefix(token.STRING, p.parseStringLiteral)
	p.registerPrefix(token.PRINT, p.parseIdentifier)
	p.registerPrefix(token.LBRAKET, p.parseArrayLiteral)
	p.registerPrefix(token.LBRACE, p.parseHashLiteral)
	p.registerPrefix(token.COMMENT, p.parseCommentLiteral)

	p.registerPrefix(token.ROUTE, p.parseRouteLiteral)

	p.registerPrefix(token.LOAD, p.parseLoadLiteral)
	p.registerPrefix(token.LOOP, p.parseLoopExpression)
	p.registerPrefix(token.SCOPE, p.parseScopeLiteral)
	p.registerPrefix(token.SCOPE_IDENTIFY, p.parseScopeExpression)
	p.registerPrefix(token.PROC, p.parseProcessExpression)
	p.registerPrefix(token.SOCK, p.parseSocketExpression)
	p.registerPrefix(token.ENV, p.parseEnvExpression)
	p.registerPrefix(token.FOR, p.parseForExpression)
	p.registerPrefix(token.BEGIN, p.parseBeginLiteral)
	p.registerPrefix(token.EXCEPT, p.parseExceptExpression)
	p.registerPrefix(token.RECOVER, p.parseRecoverExpression)
	p.registerPrefix(token.FINAL, p.parseFinalExpression)
	p.registerPrefix(token.SET, p.parseScopeChangeExpression)
	p.registerPrefix(token.SIGNAL, p.parseSignalExpression)
	p.registerPrefix(token.EVAL, p.parseEvalLiteral)
	p.registerPrefix(token.WHILE, p.parseWhileExpression)

	p.registerPrefix(token.ASYNC, p.parseAsyncLiteral)
	p.registerPrefix(token.WAIT, p.parseWaitExpression)

	p.infixParseFns = make(map[token.TokenType]infixParseFn)
	p.registerInfix(token.PLUS, p.parseInfixExpression)
	p.registerInfix(token.MINUS, p.parseInfixExpression)
	p.registerInfix(token.SLASH, p.parseInfixExpression)
	p.registerInfix(token.ASTERISK, p.parseInfixExpression)
	p.registerInfix(token.EQ, p.parseInfixExpression)
	p.registerInfix(token.NOT_EQ, p.parseInfixExpression)
	p.registerInfix(token.LT, p.parseInfixExpression)
	p.registerInfix(token.GT, p.parseInfixExpression)
	p.registerInfix(token.LT_EQ, p.parseInfixExpression)
	p.registerInfix(token.GT_EQ, p.parseInfixExpression)
	p.registerInfix(token.LPAREN, p.parseCallExpression)
	p.registerInfix(token.LBRAKET, p.parseIndexExpression)

	return p
}

func (p *Parser) Errors() []string {
	return p.errors
}

func (p *Parser) LexerReturn() interface{} {
	return p.l
}

func (p *Parser) ErrorTokens() interface{} {
	return p.errorTokens
}

func (p *Parser) nextToken() {
	p.curToken = p.peekToken
	p.peekToken = p.l.NextToken()
}

func (p *Parser) prevToken(prev int) {
	p.curToken = p.peekToken
	p.peekToken = p.l.PrevToken(prev)
}

func (p *Parser) ParseProgram() *ast.Program {
	program := &ast.Program{}
	program.Statements = []ast.Statement{}

	eof := false

	for p.curToken.Type != token.EOF {
		stmt := p.parseStatement()

		if p.curToken.Type == token.COMMENT {
			eof = true
		}

		if stmt != nil {
			program.Statements = append(program.Statements, stmt)
		}
		p.nextToken()

		if eof {
			p.curToken.Literal = ""
			p.curToken.Type = token.EOF
		}

	}

	return program
}

func (p *Parser) parseEvalLiteral() ast.Expression {
	el := &ast.EvalLiteral{Token: p.curToken}

	//TODO: string, ident change.
	if !p.expectOrPeek(token.STRING, token.IDENT) {
		return nil
	}

	el.Content = p.parseExpression(LOWEST)

	return el
}

func (p *Parser) parseAsyncLiteral() ast.Expression {
	al := &ast.AsyncLiteral{Token: p.curToken}

	if !p.expectPeek(token.IDENT) {
		return nil
	}

	al.Name = p.curToken.Literal

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	al.Block = p.parseBlockStatement()

	return al
}

func (p *Parser) parseBeginLiteral() ast.Expression {
	bl := &ast.BeginLiteral{Token: p.curToken}

	if !p.expectPeek(token.IDENT) {
		return nil
	}

	bl.Name = p.curToken.Literal

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	bl.Block = p.parseBlockStatement()

	return bl
}

func (p *Parser) parseWaitExpression() ast.Expression {
	wl := &ast.WaitExpression{Token: p.curToken}

	if !p.expectPeek(token.IDENT) {
		return nil
	}

	wl.Async = p.curToken.Literal

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	wl.Block = p.parseBlockStatement()

	return wl
}

func (p *Parser) parseRouteLiteral() ast.Expression {
	rl := &ast.RouteLiteral{Token: p.curToken}

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	rl.Config = p.parseHashLiteral()

	return rl
}

func (p *Parser) parseExceptExpression() ast.Expression {
	ee := &ast.ExceptionExpression{Token: p.curToken}

	if !p.expectPeek(token.IDENT) {
		return nil
	}

	ee.Begin = p.curToken.Literal

	/* object: STRING {WORKS}, BLOCK {?}, IDENT {?} */

	if !p.expectPeek(token.LPAREN) {
		return nil
	}

	p.nextToken()
	ee.Condition = p.parseExpression(LOWEST)

	if !p.expectPeek(token.RPAREN) {
		return nil
	}

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	ee.ErrorBlock = p.parseBlockStatement()

	return ee
}

func (p *Parser) parseScopeChangeExpression() ast.Expression {
	sce := &ast.ScopeChangeExpression{Token: p.curToken}

	p.nextToken()

	sce.Left = string(p.curToken.Literal)
	p.nextToken()

	sce.Right = string(p.curToken.Literal)
	p.nextToken()

	sce.Value = p.parseStatement()

	return sce
}

func (p *Parser) parseScopeLiteral() ast.Expression {

	lit := &ast.ScopeLiteral{Token: p.curToken}

	if !p.expectPeek(token.IDENT) {
		return nil
	}

	lit.Name = p.curToken.Literal

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	lit.Body = p.parseBlockStatement()

	scopeList = append(scopeList, lit)
	return lit
}

func (p *Parser) parseStatement() ast.Statement {
	switch p.curToken.Type {
	case token.LET:
		return p.parseLetStatement()
	case token.RETURN:
		return p.parseReturnStatement()
	default:
		return p.parseExpressionStatement()
	}
}

func (p *Parser) parseScopeExpression() ast.Expression {
	stmt := &ast.ScopeExpression{Token: p.curToken}

	p.prevToken(2)

	stmt.Right = string(p.curToken.Literal)
	p.nextToken()

	stmt.Left = string(p.curToken.Literal)

	if !p.peekTokenIs(token.LPAREN) {
		return stmt
	}

	p.nextToken()

	stmt.Arguments = p.parseExpressionList(token.RPAREN)

	return stmt
}

func (p *Parser) parseRecoverExpression() ast.Expression {
	re := &ast.RecoverExpression{Token: p.curToken}

	p.nextToken()

	re.Begin = p.curToken.Literal

	p.nextToken()
	re.Block = p.parseBlockStatement()

	return re
}

func (p *Parser) parseFinalExpression() ast.Expression {
	re := &ast.FinalExpression{Token: p.curToken}

	p.nextToken()

	re.Begin = p.curToken.Literal

	p.nextToken()

	re.Block = p.parseBlockStatement()

	return re
}

func (p *Parser) parseEnvExpression() ast.Expression {
	e := &ast.EnvExpression{Token: p.curToken}
	p.nextToken()
	e.Name = string(p.curToken.Literal)
	p.nextToken()
	e.Value = string(p.curToken.Literal)
	return e
}

func (p *Parser) parseSocketExpression() ast.Expression {
	se := &ast.SocketExpression{Token: p.curToken}
	p.nextToken()
	se.Definition = string(p.curToken.Literal)
	p.nextToken()
	se.ConType = string(p.curToken.Literal)
	p.nextToken()
	se.Port = string(p.curToken.Literal)
	p.nextToken()
	se.Host = string(p.curToken.Literal)
	se.Debugger = false
	if p.peekTokenIs(token.TRUE) {
		se.Debugger = true
	}
	return se
}

func (p *Parser) parseProcessExpression() ast.Expression {
	pe := &ast.ProcessExpression{Token: p.curToken}

	p.nextToken()

	pe.Definition = p.parseExpression(LOWEST)

	p.nextToken()

	pe.Process = p.parseExpression(LOWEST)

	return pe
}

func (p *Parser) parseForExpression() ast.Expression {
	forex := &ast.ForExpression{Token: p.curToken}

	if !p.expectPeek(token.LPAREN) {
		return nil
	}
	p.nextToken()

	forex.Init = p.parseExpression(LOWEST)

	if p.expectPeek(token.IN) {
		forex.LoopToken = p.curToken
		p.nextToken()
		forex.Key = p.parseExpression(LOWEST)
		p.nextToken()
		if p.curTokenIs(token.COMMA) {
			p.nextToken()
			forex.Value = p.parseExpression(LOWEST)
		}
	} else {
		forex.Condition = p.parseExpression(LOWEST)
	}

	if !p.expectPeek(token.RPAREN) {
		return nil
	}

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	forex.Consequence = p.parseBlockStatement()

	return forex
}

func (p *Parser) parseWhileExpression() ast.Expression {
	expression := &ast.WhileExpression{
		Token: p.curToken,
	}

	if !p.expectPeek(token.LPAREN) {
		return nil
	}

	p.nextToken()
	expression.Condition = p.parseExpression(LOWEST)

	if !p.expectPeek(token.RPAREN) {
		return nil
	}

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	expression.Consequence = p.parseBlockStatement()

	return expression
}

func (p *Parser) parseScopeDefStatement() ast.Statement {

	p.prevToken(3)

	if p.curToken.Type == token.LET {
		stmt := &ast.LetStatement{Token: p.curToken}
		stmt.Value = p.parseScopeExpression()
		return stmt
	} else {
		p.nextToken()
		stmt := p.parseScopeExpression()
		returnVal := &ast.ReturnStatement{Token: p.curToken, ReturnValue: stmt}
		return returnVal
	}
}

func (p *Parser) fixIdendifier() *ast.Identifier {
	ident := &ast.Identifier{
		Token: p.curToken,
		Value: p.curToken.Literal,
	}

	for {
		if p.peekTokenIs(token.IDENT) || p.peekTokenIs(token.INT) {
			p.nextToken()
			ident.Value = ident.Value + p.curToken.Literal
		} else {
			break
		}

	}
	return ident
}

func (p *Parser) parseLetStatement() *ast.LetStatement {
	stmt := &ast.LetStatement{Token: p.curToken}

	if !p.expectOrPeek(token.IDENT, token.INT) {
		return nil
	}

	stmt.Name = p.fixIdendifier()

	if !p.expectPeek(token.ASSIGN) {
		return nil
	}

	p.nextToken()

	if p.curToken.Type == token.IDENT {
		for _, s := range scopeList {
			switch d := s.(type) {
			case *ast.ScopeLiteral:
				if d.Name == p.curToken.Literal {
					p.nextToken()
					stmt.Value = p.parseScopeExpression()

					if p.peekTokenIs(token.SEMICOLON) {
						p.nextToken()
					}

					return stmt
				} else {
					stmt.Value = p.parseExpression(LOWEST)

					if p.peekTokenIs(token.SEMICOLON) {
						p.nextToken()
					}
				}
			}
		}
	} else {
		stmt.Value = p.parseExpression(LOWEST)

		if p.peekTokenIs(token.SEMICOLON) {
			p.nextToken()
		}
	}

	return stmt
}

func (p *Parser) parseSignalExpression() ast.Expression {
	se := &ast.SignalExpression{Token: p.curToken}

	p.nextToken()

	se.Name = &ast.Identifier{
		Token: p.curToken,
		Value: p.curToken.Literal,
	}

	p.nextToken()

	se.Value = string(p.curToken.Literal)

	return se
}

func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
	stmt := &ast.ReturnStatement{
		Token: p.curToken,
	}
	p.nextToken()

	stmt.ReturnValue = p.parseExpression(LOWEST)

	if p.peekTokenIs(token.SEMICOLON) {
		p.nextToken()
	}

	return stmt
}

func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement {
	stmt := &ast.ExpressionStatement{
		Token: p.curToken,
	}
	stmt.Expression = p.parseExpression(LOWEST)

	if p.peekTokenIs(token.SEMICOLON) {
		p.nextToken()
	}

	return stmt
}

func (p *Parser) parseExpression(precedence int) ast.Expression {
	prefix := p.prefixParseFns[p.curToken.Type]
	if prefix == nil {
		p.noPrefixParseFnError(p.curToken.Type)
		return nil
	}

	leftExp := prefix()

	for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() {
		infix := p.infixParseFns[p.peekToken.Type]
		if infix == nil {
			return leftExp
		}

		p.nextToken()
		leftExp = infix(leftExp)
	}

	return leftExp
}

func (p *Parser) parseIdentifier() ast.Expression {
	return p.fixIdendifier()
}

func (p *Parser) parseIntegerLiteral() ast.Expression {
	lit := &ast.IntegerLiteral{Token: p.curToken}

	value, err := strconv.ParseInt(p.curToken.Literal, 0, 64)
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as integer", p.curToken.Literal)
		p.errors = append(p.errors, msg)
		return nil
	}

	lit.Value = value
	return lit
}

func (p *Parser) parseFloatLiteral() ast.Expression {
	lit := &ast.FloatLiteral{Token: p.curToken}

	left := "0"
	right := "0"

	if p.expectPeek(token.INT) {
		left = string(p.curToken.Literal)
	}

	if !p.expectPeek(token.DOT) {
		return nil
	}

	if p.expectPeek(token.INT) {
		right = string(p.curToken.Literal)
	}

	value, err := strconv.ParseFloat(string(left)+"."+string(right), 64)
	if err != nil {
		msg := fmt.Sprintf("could not parse %q as float", p.curToken.Literal)
		p.errors = append(p.errors, msg)
		return nil
	}

	lit.Value = value
	return lit
}

func (p *Parser) parsePrefixExpression() ast.Expression {
	expression := &ast.PrefixExpression{
		Token:    p.curToken,
		Operator: p.curToken.Literal,
	}

	p.nextToken()

	expression.Right = p.parseExpression(PREFIX)

	return expression
}

func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
	expression := &ast.InfixExpression{
		Token:    p.curToken,
		Operator: p.curToken.Literal,
		Left:     left,
	}

	precedence := p.curPrecedence()
	p.nextToken()
	expression.Right = p.parseExpression(precedence)

	return expression
}

func (p *Parser) parseBoolean() ast.Expression {
	return &ast.Boolean{
		Token: p.curToken,
		Value: p.curTokenIs(token.TRUE),
	}
}

func (p *Parser) parseGroupedExpression() ast.Expression {
	p.nextToken()

	exp := p.parseExpression(LOWEST)

	if !p.expectPeek(token.RPAREN) {
		return nil
	}

	return exp
}

func (p *Parser) parseIfExpression() ast.Expression {
	expression := &ast.IfExpression{
		Token: p.curToken,
	}

	if !p.expectPeek(token.LPAREN) {
		return nil
	}

	p.nextToken()
	expression.Condition = p.parseExpression(LOWEST)

	if !p.expectPeek(token.RPAREN) {
		return nil
	}

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	expression.Consequence = p.parseBlockStatement()

	if p.peekTokenIs(token.ELSE) {
		p.nextToken()

		if !p.expectPeek(token.LBRACE) {
			return nil
		}

		expression.Alternative = p.parseBlockStatement()
	}

	return expression
}

func (p *Parser) parseBlockStatement() *ast.BlockStatement {
	block := &ast.BlockStatement{
		Token: p.curToken,
	}
	block.Statements = []ast.Statement{}

	p.nextToken()

	for !p.curTokenIs(token.RBRACE) {
		stmt := p.parseStatement()
		if stmt != nil {
			block.Statements = append(block.Statements, stmt)
		}
		p.nextToken()
	}

	return block
}

func (p *Parser) parseCustomLiteral() ast.Expression {
	lit := &ast.CustomLiteral{
		Token: p.curToken,
	}

	p.nextToken()

	lit.Name = string(p.curToken.Literal)

	if !p.expectPeek(token.LPAREN) {
		return nil
	}

	lit.Parameters = p.parseFunctionParameters()

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	lit.Body = p.parseBlockStatement()

	return lit
}

func (p *Parser) parseFunctionLiteral() ast.Expression {
	lit := &ast.FunctionLiteral{
		Token: p.curToken,
	}

	if !p.expectPeek(token.LPAREN) {
		return nil
	}

	lit.Parameters = p.parseFunctionParameters()

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	lit.Body = p.parseBlockStatement()

	return lit
}

func (p *Parser) parseFunctionParameters() []*ast.Identifier {
	identifiers := []*ast.Identifier{}

	if p.peekTokenIs(token.RPAREN) {
		p.nextToken()
		return identifiers
	}

	if !p.expectOrPeek(token.IDENT, token.INT) {
		return nil
	}

	ident := p.fixIdendifier()
	identifiers = append(identifiers, ident)

	for p.peekTokenIs(token.COMMA) {
		p.nextToken()
		p.nextToken()
		ident := p.fixIdendifier()
		identifiers = append(identifiers, ident)
	}

	if !p.expectPeek(token.RPAREN) {
		return nil
	}

	return identifiers
}

func (p *Parser) parseLoopExpression() ast.Expression {
	expression := &ast.LoopExpression{
		Token: p.curToken,
	}

	if !p.expectPeek(token.LPAREN) {
		return nil
	}

	p.nextToken()
	expression.Condition = p.parseExpression(LOWEST)

	if !p.expectPeek(token.RPAREN) {
		return nil
	}

	if !p.expectPeek(token.LBRACE) {
		return nil
	}

	expression.Consequence = p.parseBlockStatement()

	return expression
}

func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression {
	exp := &ast.CallExpression{
		Token:    p.curToken,
		Function: function,
	}
	exp.Arguments = p.parseExpressionList(token.RPAREN)
	return exp
}

func (p *Parser) parseCallArguments() []ast.Expression {
	args := []ast.Expression{}

	if p.peekTokenIs(token.RPAREN) {
		p.nextToken()
		return args
	}

	p.nextToken()
	args = append(args, p.parseExpression(LOWEST))

	for p.peekTokenIs(token.COMMA) {
		p.nextToken()
		p.nextToken()
		args = append(args, p.parseExpression(LOWEST))
	}

	if !p.expectPeek(token.RPAREN) {
		return nil
	}

	return args
}

func (p *Parser) parseStringLiteral() ast.Expression {
	return &ast.StringLiteral{
		Token: p.curToken,
		Value: p.curToken.Literal,
	}
}

func (p *Parser) parseCommentLiteral() ast.Expression {
	return nil
}

func (p *Parser) parseLoadLiteral() ast.Expression {
	load := &ast.LoadStatement{Token: p.curToken}

	p.nextToken()
	load.Name = string(p.curToken.Literal)

	curDir, _ := os.Getwd()
	loadedContent, err := os.ReadFile(curDir + "/" + load.Name)

	if err == nil {
		loaded := string(loadedContent)

		if loaded != "" {
			lex := lexer.New(loaded)
			parser := New(lex)
			program := parser.ParseProgram()
			load.Statements = program.Statements
		}
	}

	return load
}

func (p *Parser) parseArrayLiteral() ast.Expression {
	array := &ast.ArrayLiteral{Token: p.curToken}

	array.Elements = p.parseExpressionList(token.RBRAKET)

	return array
}

func (p *Parser) parseExpressionList(end token.TokenType) []ast.Expression {
	list := []ast.Expression{}

	if p.peekTokenIs(end) {
		p.nextToken()
		return list
	}

	p.nextToken()
	list = append(list, p.parseExpression(LOWEST))

	for p.peekTokenIs(token.COMMA) {
		p.nextToken()
		p.nextToken()
		list = append(list, p.parseExpression(LOWEST))
	}

	if !p.expectPeek(end) {
		return nil
	}

	return list
}

func (p *Parser) parseIndexExpression(left ast.Expression) ast.Expression {
	exp := &ast.IndexExpression{
		Token: p.curToken,
		Left:  left,
	}

	p.nextToken()
	exp.Index = p.parseExpression(LOWEST)

	if !p.expectPeek(token.RBRAKET) {
		return nil
	}

	return exp
}

func (p *Parser) parseHashLiteral() ast.Expression {
	hash := &ast.HashLiteral{Token: p.curToken}
	hash.Pairs = make(map[ast.Expression]ast.Expression)

	for !p.peekTokenIs(token.RBRACE) {
		p.nextToken()
		key := p.parseExpression(LOWEST)

		if !p.expectPeek(token.COLON) {
			return nil
		}

		p.nextToken()
		value := p.parseExpression(LOWEST)

		hash.Pairs[key] = value

		if !p.peekTokenIs(token.RBRACE) && !p.expectPeek(token.COMMA) {
			return nil
		}
	}

	if !p.expectPeek(token.RBRACE) {
		return nil
	}

	return hash
}

func (p *Parser) curTokenIs(t token.TokenType) bool {
	return p.curToken.Type == t
}

func (p *Parser) curTokenIsNot(t token.TokenType) bool {
	return p.curToken.Type != t
}

func (p *Parser) peekTokenIs(t token.TokenType) bool {
	return p.peekToken.Type == t
}

func (p *Parser) peekTokenIsNot(t token.TokenType) bool {
	return p.peekToken.Type != t
}

func (p *Parser) peekPrecedence() int {
	if p, ok := precedences[p.peekToken.Type]; ok {
		return p
	}

	return LOWEST
}

func (p *Parser) curPrecedence() int {
	if p, ok := precedences[p.curToken.Type]; ok {
		return p
	}

	return LOWEST
}

func (p *Parser) expectPeek(t token.TokenType) bool {
	if p.peekTokenIs(t) {
		p.nextToken()
		return true
	}

	p.peekError(t)
	return false
}

func (p *Parser) currentTokensIsOr(tf token.TokenType, tn token.TokenType) bool {
	if p.curTokenIs(tf) || p.curTokenIs(tn) {
		return true
	}

	return false
}

func (p *Parser) expectOrPeek(tf token.TokenType, tn token.TokenType) bool {
	if p.peekTokenIs(tf) || p.peekTokenIs(tn) {
		p.nextToken()
		return true
	}

	p.peekOrError(tf, tn)
	return false
}

func (p *Parser) peekError(t token.TokenType) {
	msg := fmt.Sprintf("expected next token to be %s, got %s instead", color.Bold(color.Green(t)), color.Bold(color.Red(p.peekToken.Type)))
	p.errors = append(p.errors, msg)
	p.errorTokens = append(p.errorTokens, p.peekToken)
}

func (p *Parser) peekOrError(tf token.TokenType, tn token.TokenType) {
	msg := fmt.Sprintf("expected next token to be %s or %s, got %s instead", color.Bold(color.Green(tf)), color.Bold(color.Green(tn)), color.Bold(color.Red(p.peekToken.Type)))
	p.errors = append(p.errors, msg)
	p.errorTokens = append(p.errorTokens, p.peekToken)
}

func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) {
	p.prefixParseFns[tokenType] = fn
}

func (p *Parser) registerInfix(tokenType token.TokenType, fn infixParseFn) {
	p.infixParseFns[tokenType] = fn
}

func (p *Parser) noPrefixParseFnError(t token.TokenType) {
	msg := fmt.Sprintf("no prefix parse function for %s found", color.Bold(color.Red(t)))
	p.errors = append(p.errors, msg)
}
