You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
487 lines
12 KiB
487 lines
12 KiB
/*
|
|
* FCML - Free Code Manipulation Library.
|
|
* Copyright (C) 2010-2019 Slawomir Wojtasiak
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/** @file fcml_parser.hpp
|
|
* C++ wrapper for instruction parser.
|
|
*
|
|
* @copyright Copyright (C) 2010-2015 Slawomir Wojtasiak. All rights reserved.
|
|
* This project is released under the GNU Lesser General Public License.
|
|
*/
|
|
|
|
#ifndef FCML_PARSER_HPP_
|
|
#define FCML_PARSER_HPP_
|
|
|
|
#include "fcml_common.hpp"
|
|
#include "fcml_symbols.hpp"
|
|
#include "fcml_errors.hpp"
|
|
#include "fcml_dialect.hpp"
|
|
|
|
#include "fcml_parser.h"
|
|
|
|
namespace fcml {
|
|
|
|
/**
|
|
* Something failed while parsing.
|
|
* @since 1.1.0
|
|
*/
|
|
class ParsingFailedException: public ErrorContainerAwareException {
|
|
public:
|
|
ParsingFailedException(const fcml_cstring msg,
|
|
ErrorContainer &errorContainer,
|
|
fcml_ceh_error error = FCML_CEH_GEC_NO_ERROR) :
|
|
ErrorContainerAwareException(msg, errorContainer, error) {
|
|
}
|
|
};
|
|
|
|
/** Parser configuration.
|
|
* @since 1.1.0
|
|
*/
|
|
class ParserConfig {
|
|
public:
|
|
|
|
/**
|
|
* Default constructor.
|
|
* @since 1.1.0
|
|
*/
|
|
ParserConfig() :
|
|
_throwExceptionOnError(true),
|
|
_ignoreUndefinedSymbols(false),
|
|
_disableSymbolsDeclaration(true),
|
|
_overrideLabels(false),
|
|
_allocSymbolTableIfNeeded(false),
|
|
_enableErrorMessages(true) {
|
|
}
|
|
|
|
/** @since 1.1.0 */
|
|
bool isAllocSymbolTableIfNeeded() const {
|
|
return _allocSymbolTableIfNeeded;
|
|
}
|
|
|
|
/** @since 1.1.0 */
|
|
void setAllocSymbolTableIfNeeded(bool allocSymbolTableIfNeeded) {
|
|
_allocSymbolTableIfNeeded = allocSymbolTableIfNeeded;
|
|
}
|
|
|
|
/** @since 1.1.0 */
|
|
bool isDisableSymbolsDeclaration() const {
|
|
return _disableSymbolsDeclaration;
|
|
}
|
|
|
|
/** @since 1.1.0 */
|
|
void setDisableSymbolsDeclaration(bool disableSymbolsDeclaration) {
|
|
_disableSymbolsDeclaration = disableSymbolsDeclaration;
|
|
}
|
|
|
|
/** @since 1.1.0 */
|
|
bool isEnableErrorMessages() const {
|
|
return _enableErrorMessages;
|
|
}
|
|
|
|
/** @since 1.1.0 */
|
|
void setEnableErrorMessages(bool enableErrorMessages) {
|
|
_enableErrorMessages = enableErrorMessages;
|
|
}
|
|
|
|
/** @since 1.1.0 */
|
|
bool isIgnoreUndefinedSymbols() const {
|
|
return _ignoreUndefinedSymbols;
|
|
}
|
|
|
|
/** @since 1.1.0 */
|
|
void setIgnoreUndefinedSymbols(bool ignoreUndefinedSymbols) {
|
|
_ignoreUndefinedSymbols = ignoreUndefinedSymbols;
|
|
}
|
|
|
|
/** @since 1.1.0 */
|
|
bool isOverrideLabels() const {
|
|
return _overrideLabels;
|
|
}
|
|
|
|
/** @since 1.1.0 */
|
|
void setOverrideLabels(bool overrideLabels) {
|
|
_overrideLabels = overrideLabels;
|
|
}
|
|
|
|
/**
|
|
* Gets true if exception should be thrown in case of error.
|
|
* @return True if exception should be thrown in case of error.
|
|
* @since 1.1.0
|
|
*/
|
|
bool isThrowExceptionOnError() const {
|
|
return _throwExceptionOnError;
|
|
}
|
|
|
|
/**
|
|
* Sets exception on error flag. Has to be set to true if exception
|
|
* should be thrown in case of error.
|
|
* @param throwExceptionOnError True if exception should be thrown
|
|
* in case of error.
|
|
* @since 1.1.0
|
|
*/
|
|
void setThrowExceptionOnError(bool throwExceptionOnError) {
|
|
_throwExceptionOnError = throwExceptionOnError;
|
|
}
|
|
|
|
private:
|
|
bool _throwExceptionOnError;
|
|
bool _ignoreUndefinedSymbols;
|
|
bool _disableSymbolsDeclaration;
|
|
bool _overrideLabels;
|
|
bool _allocSymbolTableIfNeeded;
|
|
bool _enableErrorMessages;
|
|
};
|
|
|
|
/** Parser context.
|
|
* @since 1.1.0
|
|
*/
|
|
class ParserContext {
|
|
public:
|
|
|
|
/**
|
|
* Creates a parser context instance for optional instruction pointer.
|
|
*
|
|
* @param ip The instruction pointer.
|
|
* @since 1.1.0
|
|
*/
|
|
ParserContext(fcml_ip ip = 0) :
|
|
_ip(ip), _symbolTable(NULL) {
|
|
}
|
|
|
|
/**
|
|
* Creates a parser context for given symbol table and optional
|
|
* instruction pointer.
|
|
*
|
|
* @param symbolTable The symbol table.
|
|
* @param ip The instruction pointer.
|
|
* @since 1.1.0
|
|
*/
|
|
ParserContext(SymbolTable *symbolTable, fcml_ip ip = 0) :
|
|
_ip(ip), _symbolTable(symbolTable) {
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Gets the parser configuration associated with the context.
|
|
*
|
|
* @return The parser configuration associated with the context.
|
|
* @since 1.1.0
|
|
*/
|
|
const ParserConfig& getConfig() const {
|
|
return _config;
|
|
}
|
|
|
|
/**
|
|
* Gets the parser configuration associated with the context.
|
|
*
|
|
* @return The parser configuration associated with the context.
|
|
* @since 1.1.0
|
|
*/
|
|
ParserConfig& getConfig() {
|
|
return _config;
|
|
}
|
|
|
|
/**
|
|
* Gets the instruction pointer.
|
|
*
|
|
* @return The instruction pointer.
|
|
* @since 1.1.0
|
|
*/
|
|
fcml_ip getIp() const {
|
|
return _ip;
|
|
}
|
|
|
|
/**
|
|
* Gets a new instruction pointer.
|
|
*
|
|
* @param ip The new instruction pointer.
|
|
* @since 1.1.0
|
|
*/
|
|
void setIp(fcml_ip ip) {
|
|
_ip = ip;
|
|
}
|
|
|
|
/**
|
|
* Increments instruction pointer by given number of bytes.
|
|
*
|
|
* @param ip The number of bytes the instruction pointer has
|
|
* to be incremented by.
|
|
* @since 1.1.0
|
|
*/
|
|
void incrementIP(fcml_ip ip) {
|
|
_ip += ip;
|
|
}
|
|
|
|
/**
|
|
* Gets the symbol table associated with the context.
|
|
*
|
|
* @return The symbol table.
|
|
* @since 1.1.0
|
|
*/
|
|
const SymbolTable* getSymbolTable() const {
|
|
return _symbolTable;
|
|
}
|
|
|
|
/**
|
|
* Gets the symbol table associated with the context.
|
|
*
|
|
* @return The symbol table.
|
|
* @since 1.1.0
|
|
*/
|
|
SymbolTable* getSymbolTable() {
|
|
return _symbolTable;
|
|
}
|
|
|
|
/**
|
|
* Sets a symbol table for the instruction.
|
|
*
|
|
* @param symbolTable The symbol table for the parser context.
|
|
* @since 1.1.0
|
|
*/
|
|
void setSymbolTable(SymbolTable *symbolTable) {
|
|
_symbolTable = symbolTable;
|
|
}
|
|
|
|
private:
|
|
|
|
/** The instruction pointer used by declared labels. */
|
|
fcml_ip _ip;
|
|
/** The parser configuration. */
|
|
ParserConfig _config;
|
|
/** The symbol table. */
|
|
SymbolTable *_symbolTable;
|
|
|
|
};
|
|
|
|
/** Parser result.
|
|
* @since 1.1.0
|
|
*/
|
|
class ParserResult {
|
|
public:
|
|
|
|
/**
|
|
* Creates an empty parser result.
|
|
* @since 1.1.0
|
|
*/
|
|
ParserResult() {
|
|
}
|
|
|
|
/**
|
|
* Gets errors container with parsing errors.
|
|
*
|
|
* @return Errors container.
|
|
* @since 1.1.0
|
|
*/
|
|
const ErrorContainer& getErrors() const {
|
|
return _errors;
|
|
}
|
|
|
|
/**
|
|
* Gets the parsed instruction.
|
|
*
|
|
* @return The parsed instruction.
|
|
* @since 1.1.0
|
|
*/
|
|
const Instruction& getInstruction() const {
|
|
return _instruction;
|
|
}
|
|
|
|
/**
|
|
* Gets declared symbol is there is any.
|
|
* @return Gets symbol if there is any.
|
|
* @since 1.1.0
|
|
*/
|
|
const Nullable<Symbol>& getSymbol() const {
|
|
return _symbol;
|
|
}
|
|
|
|
/**
|
|
* Cleans the parser result.
|
|
* @since 1.1.0
|
|
*/
|
|
void clean() {
|
|
_errors.clean();
|
|
_symbol.setNotNull(false);
|
|
_symbol.setValue(Symbol());
|
|
}
|
|
|
|
protected:
|
|
|
|
friend class Parser;
|
|
|
|
/**
|
|
* Sets error container for the context.
|
|
* @param errors A new error container.
|
|
* @since 1.1.0
|
|
*/
|
|
void setErrors(const ErrorContainer &errors) {
|
|
_errors = errors;
|
|
}
|
|
|
|
/**
|
|
* Sets an instruction for the container.
|
|
* @param instruction The instruction.
|
|
* @since 1.1.0
|
|
*/
|
|
void setInstruction(const Instruction &instruction) {
|
|
_instruction = instruction;
|
|
}
|
|
|
|
/**
|
|
* Sets symbol.
|
|
* @param symbol The symbol.
|
|
* @since 1.1.0
|
|
*/
|
|
void setSymbol(const Nullable<Symbol> &symbol) {
|
|
_symbol = symbol;
|
|
}
|
|
|
|
private:
|
|
|
|
/** Errors container. */
|
|
ErrorContainer _errors;
|
|
/** Gets declared symbol. Take into account that it can be 'empty'. */
|
|
Nullable<Symbol> _symbol;
|
|
/** The parsed instruction. */
|
|
Instruction _instruction;
|
|
|
|
};
|
|
|
|
/**
|
|
* Converts objects to their structures counterparts.
|
|
* @since 1.1.0
|
|
* @remarks Internal API, not intended to be used outside.
|
|
*/
|
|
class ParserTypeConverter {
|
|
public:
|
|
|
|
static void convert(ParserConfig &src, fcml_st_parser_config &dest) {
|
|
dest.alloc_symbol_table_if_needed = src.isAllocSymbolTableIfNeeded();
|
|
dest.disable_symbols_declaration = src.isDisableSymbolsDeclaration();
|
|
dest.enable_error_messages = src.isEnableErrorMessages();
|
|
dest.ignore_undefined_symbols = src.isIgnoreUndefinedSymbols();
|
|
dest.override_labels = src.isOverrideLabels();
|
|
}
|
|
|
|
};
|
|
|
|
/** Parser wrapper.
|
|
* @since 1.1.0
|
|
*/
|
|
class Parser: protected DialectAware, protected SymbolTableAware {
|
|
public:
|
|
|
|
/**
|
|
* Creates a parser instance for the given dialect.
|
|
*
|
|
* @param dialect The dialect instance.
|
|
* @since 1.1.0
|
|
*/
|
|
Parser(const Dialect &dialect) :
|
|
_dialect(dialect) {
|
|
}
|
|
|
|
/**
|
|
* Parses instruction given in the parameters.
|
|
*
|
|
* @param ctx Parser context.
|
|
* @param instruction Instruction mnemonic.
|
|
* @param[out] parserResult Instruction result.
|
|
* @return Error code.
|
|
* @throw ParsingFailedException Parsing failed.
|
|
* @since 1.1.0
|
|
*/
|
|
fcml_ceh_error parse(ParserContext &ctx, const fcml_cstring &instruction,
|
|
ParserResult &parserResult) {
|
|
|
|
// Prepare parser context.
|
|
fcml_st_parser_context context = { 0 };
|
|
ParserTypeConverter::convert(ctx.getConfig(), context.configuration);
|
|
context.ip = ctx.getIp();
|
|
SymbolTable *symbolTable = ctx.getSymbolTable();
|
|
context.symbol_table =
|
|
(symbolTable) ? extractSymbolTable(*symbolTable) : NULL;
|
|
context.dialect = extractDialect(_dialect);
|
|
|
|
fcml_st_parser_result parser_result;
|
|
::fcml_fn_parser_result_prepare(&parser_result);
|
|
|
|
try {
|
|
|
|
parserResult.clean();
|
|
|
|
// Prepare instruction.
|
|
fcml_ceh_error error = ::fcml_fn_parse(&context,
|
|
instruction.c_str(), &parser_result);
|
|
|
|
ErrorContainer errorContainer;
|
|
ErrorTypeConverter::convert(parser_result.errors, errorContainer);
|
|
|
|
parserResult.setErrors(errorContainer);
|
|
|
|
if (!error && !parser_result.instruction) {
|
|
// Just in case, it should never happen.
|
|
error = FCML_CEH_GEC_INTERNAL_ERROR;
|
|
}
|
|
|
|
if (error && ctx.getConfig().isThrowExceptionOnError()) {
|
|
::fcml_fn_parser_result_free(&parser_result);
|
|
throw ParsingFailedException(
|
|
errorContainer.prepareErrorMessage(
|
|
FCML_TEXT("Parsing failed")), errorContainer,
|
|
error);
|
|
}
|
|
|
|
if (!error) {
|
|
|
|
Instruction parsedInstruction;
|
|
TypeConverter::convert(*(parser_result.instruction),
|
|
parsedInstruction);
|
|
|
|
parserResult.setInstruction(parsedInstruction);
|
|
parserResult.setSymbol(
|
|
parser_result.symbol ?
|
|
Symbol(parser_result.symbol->symbol,
|
|
parser_result.symbol->value) :
|
|
Symbol());
|
|
|
|
}
|
|
|
|
::fcml_fn_parser_result_free(&parser_result);
|
|
|
|
} catch (std::exception &exc) {
|
|
// If anything failed, free assembler results.
|
|
::fcml_fn_parser_result_free(&parser_result);
|
|
throw exc;
|
|
}
|
|
|
|
return FCML_CEH_GEC_NO_ERROR;
|
|
}
|
|
|
|
private:
|
|
|
|
/** The dialect for the parser. */
|
|
const Dialect &_dialect;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#endif /* FCML_PARSER_HPP_ */
|