// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the GNU LGPLv3.0 license
// a copy of which can be found in the LICENSE file.

#include "compiler_internal.h"
#include "parser_internal.h"


// --- Parser base methods

/**
 * Advance to the next non-comment token.
 *
 * @param c the current context.
 */
inline void advance(ParseContext *c)
{
	if (tok_is(c, TOKEN_EOF))
	{
		return;
	}
	c->tok = c->lexer.token_type;
	c->data = c->lexer.data;
	c->prev_span = c->span;
	c->span = c->lexer.tok_span;
	if (!lexer_next_token(&c->lexer))
	{
		exit_compiler(1);
	}
}

bool try_consume(ParseContext *c, TokenType type)
{
	if (tok_is(c, type))
	{
		advance(c);
		return true;
	}
	return false;
}

bool consume(ParseContext *c, TokenType type, const char *message, ...)
{
	if (try_consume(c, type))
	{
		return true;
	}

	va_list args;
	va_start(args, message);
	sema_verror_range(c->span, message, args);
	va_end(args);
	return false;
}


// --- Extern functions


/**
 * module? imports top_level_statement*
 * @param c
 */
static inline void parse_translation_unit(ParseContext *c)
{
	// Prime everything
	advance(c);
	advance(c);
	while (!tok_is(c, TOKEN_EOF))
	{
		Decl *decl = parse_top_level_statement(c, &c);
		if (!decl) continue;
		if (decl_ok(decl))
		{
			add_decl_to_list(&c->unit->global_decls, decl);
		}
		else
		{
			recover_top_level(c);
		}
	}
}


/**
 * Parse a file, generating a default context for it.
 * More contexts may be created on demand during parsing.
 *
 * @param file the file to parse.
 * @return true if parsing succeeds.
 */
bool parse_file(File *file)
{
	CompilationUnit *unit = unit_create(file);
	ParseContext parse_context = { .unit = unit };
	parse_context.lexer = (Lexer) { .file = file, .context =  &parse_context };
	lexer_init(&parse_context.lexer);
	if (global_context.errors_found) return false;
	parse_translation_unit(&parse_context);
	return !global_context.errors_found;
}

File stdin_file;

/**
 * Parse stdin
 *
 * @return true if parsing succeeds.
 */
bool parse_stdin(void)
{
	stdin_file = (File){
			.name = "stdin",
			.file_id = stdin_file_id,
			.full_path = "<stdin>",
	};
#define BUF_SIZE 65536
	char buffer[BUF_SIZE];
	size_t capacity = BUF_SIZE;
	size_t len = 0;
	char *data = buffer;
	while (true)
	{
		int c = getchar();
		if (c == -1) break;
		if (len >= capacity - 1)
		{
			capacity *= 2;
			if (buffer == data)
			{
				data = malloc(capacity);
				memcpy(data, buffer, len);
			}
			else
			{
				data = realloc(data, capacity);
			}
		}
		data[len++] = c;
	}
	buffer[len] = 0;
	char *stdin_data = MALLOC(len + 1);
	memcpy(stdin_data, data, len + 1);
	if (data != buffer) free(data);
	stdin_file.contents = stdin_data;
	CompilationUnit *unit = unit_create(&stdin_file);
	ParseContext parse_context = { .unit = unit };
	parse_context.lexer = (Lexer) { .file = &stdin_file, .context =  &parse_context };
	lexer_init(&parse_context.lexer);
	if (global_context.errors_found) return false;
	parse_translation_unit(&parse_context);
	return !global_context.errors_found;
}





