// File: cphinton42[P]hinton-language/src/parse.cpp
// Function: parse_decl(Parsing_Context*, Token**, Decl_Type)
Decl_AST *parse_decl(Parsing_Context *ctx, Token **current_ptr, Decl_Type decl_type)
{
    Token *current = *current_ptr;
    Token *start_section = current;
    Decl_AST *result = nullptr;
    defer {
        *current_ptr = current;
    };
    
    u32 line_number = current->line_number;
    u32 line_offset = current->line_offset;
    
    if(current->type == Token_Type::ident)
    {
        Token *ident_tok = current;
        ++current;
        Expr_AST *type = nullptr;
        
        bool not_enum = (decl_type != Decl_Type::Enum);
        // TODO: remove require_value, variables should be able to take on default values (e.g. zero initialized)
        bool require_value = (decl_type == Decl_Type::Statement);
        bool expect_expr;
        bool is_constant;
        
        if(current->type == Token_Type::colon && not_enum)
        {
            ++current;
            type = parse_expr(ctx, &current);
            if(!type)
            {
                return nullptr;
            }
            
            if(current->type == Token_Type::equal)
            {
                ++current;
                expect_expr = true;
                is_constant = false;
            }
            else if(current->type == Token_Type::colon)
            {
                ++current;
                expect_expr = true;
                is_constant = true;
            }
            else if(require_value)
            {
                report_error(ctx, start_section, current, "Expected '=' or ':'");
                return nullptr;
            }
            else
            {
                expect_expr = false;
                is_constant = false;
            }
        }
        else if(current->type == Token_Type::colon_eq && not_enum)
        {
            ++current;
            expect_expr = true;
            is_constant = false;
        }
        else if(current->type == Token_Type::double_colon)
        {
            ++current;
            expect_expr = true;
            is_constant = true;
        }
        else if(not_enum)
        {
            report_error(ctx, start_section, current, "Expected ':', ':=', or '::'");
            return nullptr;
        }
        else
        {
            expect_expr = false;
            is_constant = true;
        }
        
        Expr_AST *expr = nullptr;
        
        if(expect_expr)
        {
            if(current->type == Token_Type::semicolon)
            {
                report_error(ctx, start_section, current, "Expected expression");
                return nullptr;
            }
            expr = parse_expr(ctx, &current);
            if(!expr)
            {
                return nullptr;
            }
        }
        
        // TODO: verify that the expression is constant if is_constant?
        
        if(current->type == Token_Type::semicolon)
        {
            ++current;
        }
        else
        {
            if(decl_type == Decl_Type::Enum)
            {
                // TODO: this error is probably a bit off
                // sometimes :: was already seen ?
                report_error(ctx, start_section, current, "Expected ';' or '::'");
            }
            else
            {
                report_error(ctx, start_section, current, "Expected ';'");
            }
            return nullptr;
        }
        
        result = construct_ast(ctx->ast_pool, Decl_AST, line_number, line_offset);
        result->ident = make_ident_ast(*ident_tok);
        result->decl_type = type;
        result->expr = expr;
        
        if(is_constant)
        {
            // TODO: is this the place to do this?
            result->flags |= DECL_FLAG_CONSTANT;
            result->ident.flags |= EXPR_FLAG_CONSTANT;
            
        }
    }
    else
    {
        report_error(ctx, start_section, current, "Expected identifier to begin declaration");
    }
    
    return result;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x130, %rsp            # imm = 0x130
movq %rdi, -0x10(%rbp)
movq %rsi, -0x18(%rbp)
movl %edx, -0x1c(%rbp)
movq -0x18(%rbp), %rax
movq (%rax), %rax
movq %rax, -0x28(%rbp)
movq -0x28(%rbp), %rax
movq %rax, -0x30(%rbp)
movq $0x0, -0x38(%rbp)
leaq -0x18(%rbp), %rax
movq %rax, -0x58(%rbp)
leaq -0x28(%rbp), %rax
movq %rax, -0x50(%rbp)
leaq -0x48(%rbp), %rdi
xorl %esi, %esi
leaq -0x58(%rbp), %rdx
callq 0x12360
movq -0x28(%rbp), %rax
movl 0x8(%rax), %eax
movl %eax, -0x5c(%rbp)
movq -0x28(%rbp), %rax
movl 0xc(%rax), %eax
movl %eax, -0x60(%rbp)
movq -0x28(%rbp), %rax
cmpq $0x0, (%rax)
jne 0xcc87
movq -0x28(%rbp), %rax
movq %rax, -0x68(%rbp)
movq -0x28(%rbp), %rax
addq $0x20, %rax
movq %rax, -0x28(%rbp)
movq $0x0, -0x70(%rbp)
cmpl $0x2, -0x1c(%rbp)
setne %al
andb $0x1, %al
movb %al, -0x71(%rbp)
cmpl $0x0, -0x1c(%rbp)
sete %al
andb $0x1, %al
movb %al, -0x72(%rbp)
movq -0x28(%rbp), %rax
cmpq $0x22, (%rax)
jne 0xca23
testb $0x1, -0x71(%rbp)
je 0xca23
movq -0x28(%rbp), %rax
addq $0x20, %rax
movq %rax, -0x28(%rbp)
movq -0x10(%rbp), %rdi
leaq -0x28(%rbp), %rsi
movl $0x7, %edx
callq 0x12390
movq %rax, -0xf8(%rbp)
jmp 0xc955
movq -0xf8(%rbp), %rax
movq %rax, -0x70(%rbp)
cmpq $0x0, -0x70(%rbp)
jne 0xc99b
movq $0x0, -0x8(%rbp)
movl $0x1, -0x88(%rbp)
jmp 0xccb5
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x80(%rbp)
movl %eax, -0x84(%rbp)
leaq -0x48(%rbp), %rdi
callq 0x12d80
jmp 0xcccb
movq -0x28(%rbp), %rax
cmpq $0x3b, (%rax)
jne 0xc9bb
movq -0x28(%rbp), %rax
addq $0x20, %rax
movq %rax, -0x28(%rbp)
movb $0x1, -0x73(%rbp)
movb $0x0, -0x74(%rbp)
jmp 0xca1e
movq -0x28(%rbp), %rax
cmpq $0x22, (%rax)
jne 0xc9db
movq -0x28(%rbp), %rax
addq $0x20, %rax
movq %rax, -0x28(%rbp)
movb $0x1, -0x73(%rbp)
movb $0x1, -0x74(%rbp)
jmp 0xca1c
testb $0x1, -0x72(%rbp)
je 0xca12
movq -0x10(%rbp), %rdi
movq -0x30(%rbp), %rsi
movq -0x28(%rbp), %rdx
leaq 0xe3db(%rip), %rcx      # 0x1adcf
callq 0xc650
jmp 0xc9fb
movq $0x0, -0x8(%rbp)
movl $0x1, -0x88(%rbp)
jmp 0xccb5
movb $0x0, -0x73(%rbp)
movb $0x0, -0x74(%rbp)
jmp 0xca1c
jmp 0xca1e
jmp 0xcaae
movq -0x28(%rbp), %rax
cmpq $0x23, (%rax)
jne 0xca49
testb $0x1, -0x71(%rbp)
je 0xca49
movq -0x28(%rbp), %rax
addq $0x20, %rax
movq %rax, -0x28(%rbp)
movb $0x1, -0x73(%rbp)
movb $0x0, -0x74(%rbp)
jmp 0xcaac
movq -0x28(%rbp), %rax
cmpq $0x24, (%rax)
jne 0xca69
movq -0x28(%rbp), %rax
addq $0x20, %rax
movq %rax, -0x28(%rbp)
movb $0x1, -0x73(%rbp)
movb $0x1, -0x74(%rbp)
jmp 0xcaaa
testb $0x1, -0x71(%rbp)
je 0xcaa0
movq -0x10(%rbp), %rdi
movq -0x30(%rbp), %rsi
movq -0x28(%rbp), %rdx
leaq 0xe361(%rip), %rcx      # 0x1ade3
callq 0xc650
jmp 0xca89
movq $0x0, -0x8(%rbp)
movl $0x1, -0x88(%rbp)
jmp 0xccb5
movb $0x0, -0x73(%rbp)
movb $0x1, -0x74(%rbp)
jmp 0xcaaa
jmp 0xcaac
jmp 0xcaae
movq $0x0, -0x90(%rbp)
testb $0x1, -0x73(%rbp)
je 0xcb4a
movq -0x28(%rbp), %rax
cmpq $0x21, (%rax)
jne 0xcafe
movq -0x10(%rbp), %rdi
movq -0x30(%rbp), %rsi
movq -0x28(%rbp), %rdx
leaq 0xe31f(%rip), %rcx      # 0x1adff
callq 0xc650
jmp 0xcae7
movq $0x0, -0x8(%rbp)
movl $0x1, -0x88(%rbp)
jmp 0xccb5
movq -0x10(%rbp), %rdi
leaq -0x28(%rbp), %rsi
movl $0x7, %edx
callq 0x12390
movq %rax, -0x100(%rbp)
jmp 0xcb19
movq -0x100(%rbp), %rax
movq %rax, -0x90(%rbp)
cmpq $0x0, -0x90(%rbp)
jne 0xcb48
movq $0x0, -0x8(%rbp)
movl $0x1, -0x88(%rbp)
jmp 0xccb5
jmp 0xcb4a
movq -0x28(%rbp), %rax
cmpq $0x21, (%rax)
jne 0xcb62
movq -0x28(%rbp), %rax
addq $0x20, %rax
movq %rax, -0x28(%rbp)
jmp 0xcbb7
cmpl $0x2, -0x1c(%rbp)
jne 0xcb84
movq -0x10(%rbp), %rdi
movq -0x30(%rbp), %rsi
movq -0x28(%rbp), %rdx
leaq 0xe298(%rip), %rcx      # 0x1ae13
callq 0xc650
jmp 0xcb82
jmp 0xcba0
movq -0x10(%rbp), %rdi
movq -0x30(%rbp), %rsi
movq -0x28(%rbp), %rdx
leaq 0xe291(%rip), %rcx      # 0x1ae28
callq 0xc650
jmp 0xcb9e
jmp 0xcba0
movq $0x0, -0x8(%rbp)
movl $0x1, -0x88(%rbp)
jmp 0xccb5
movq -0x10(%rbp), %rax
movq 0x20(%rax), %rdi
movl $0x60, %esi
callq 0xcd80
movq %rax, -0x108(%rbp)
jmp 0xcbd2
movq -0x108(%rbp), %rdi
movl -0x5c(%rbp), %eax
movl %eax, %edx
movl -0x60(%rbp), %eax
movl %eax, %ecx
xorl %esi, %esi
callq 0x3c10
movq %rax, -0x38(%rbp)
movq -0x68(%rbp), %rax
movups (%rax), %xmm0
movups 0x10(%rax), %xmm1
movaps %xmm1, -0xe0(%rbp)
movaps %xmm0, -0xf0(%rbp)
movaps -0xf0(%rbp), %xmm0
movaps -0xe0(%rbp), %xmm1
movq %rsp, %rax
movups %xmm1, 0x10(%rax)
movups %xmm0, (%rax)
leaq -0xd0(%rbp), %rdi
callq 0x12d10
jmp 0xcc2d
movq -0x38(%rbp), %rdi
addq $0x10, %rdi
leaq -0xd0(%rbp), %rsi
movl $0x40, %edx
callq 0x30a0
movq -0x70(%rbp), %rcx
movq -0x38(%rbp), %rax
movq %rcx, 0x50(%rax)
movq -0x90(%rbp), %rcx
movq -0x38(%rbp), %rax
movq %rcx, 0x58(%rax)
testb $0x1, -0x74(%rbp)
je 0xcc85
movq -0x38(%rbp), %rax
movzwl 0x2(%rax), %ecx
orl $0x4, %ecx
movw %cx, 0x2(%rax)
movq -0x38(%rbp), %rax
movzwl 0x12(%rax), %ecx
orl $0x4, %ecx
movw %cx, 0x12(%rax)
jmp 0xcca3
movq -0x10(%rbp), %rdi
movq -0x30(%rbp), %rsi
movq -0x28(%rbp), %rdx
leaq 0xe19b(%rip), %rcx      # 0x1ae35
callq 0xc650
jmp 0xcca1
jmp 0xcca3
movq -0x38(%rbp), %rax
movq %rax, -0x8(%rbp)
movl $0x1, -0x88(%rbp)
leaq -0x48(%rbp), %rdi
callq 0x12d80
movq -0x8(%rbp), %rax
addq $0x130, %rsp            # imm = 0x130
popq %rbp
retq
movq -0x80(%rbp), %rdi
callq 0x3100
nopw %cs:(%rax,%rax)