Completely refactor code generation

This commit is contained in:
Matthias Schiffer 2015-04-16 22:07:29 +02:00
parent bb018683c9
commit 6c8cd11dad
18 changed files with 1057 additions and 707 deletions

View file

@ -1,11 +1,14 @@
add_executable(solar add_executable(solar
codegen.cpp
generator.cpp generator.cpp
generator_lr0.cpp generator_lr0.cpp
generator_slr.cpp generator_slr.cpp
lex.cpp lex.cpp
output.cpp output_common.cpp
output_lr0.cpp output_header.cpp
output_slr.cpp output_slr.cpp
output_source.cpp
output_source_slr.cpp
parse.cpp parse.cpp
solar.cpp solar.cpp
) )

46
src/codegen.cpp Normal file
View file

@ -0,0 +1,46 @@
/*
Copyright (c) 2013-2015, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "codegen.hpp"
#include <cerrno>
#include <system_error>
namespace solar {
codegen_t::codegen_t(const char *filename) : level(0) {
file = std::fopen(filename, "w");
if (!file)
throw std::system_error(errno, std::generic_category(), "unable to open output file `" + std::string(filename) + "' for writing");
}
codegen_t::~codegen_t() {
std::fclose(file);
}
}

219
src/codegen.hpp Normal file
View file

@ -0,0 +1,219 @@
/*
Copyright (c) 2013-2015, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <cstdio>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
namespace solar {
class nocopy_t {
private:
nocopy_t(const nocopy_t &o) = delete;
nocopy_t(nocopy_t &&o) = delete;
nocopy_t & operator=(const nocopy_t &o) = delete;
nocopy_t & operator=(nocopy_t &&o) = delete;
protected:
nocopy_t() {}
};
class codegen_t : private nocopy_t {
public:
struct variable_t {
std::string str;
std::string name;
variable_t(const std::pair<std::string, std::string> &value, bool func = false) : str(value.first), name(value.second) {
if (!func) {
char last = str[str.length()-1];
if (last != '*' && last != '&')
str += ' ';
str += name;
}
}
};
struct function_t {
variable_t type_name;
std::vector<variable_t> args;
void add_arg(const std::pair<std::string, std::string> &value, bool func = false) {
args.emplace_back(value, func);
}
void add_arg(const std::string &str, const std::string &name, bool func = false) {
add_arg(std::make_pair(str, name), func);
}
function_t(const std::string &type, const std::string &name) : type_name({type + ' ' + name, name}, true) {}
};
private:
FILE *file;
unsigned level;
template<typename... Args> void open_block(Args ...args) {
write_line(args..., " {");
level++;
}
template<typename... Args> void close_block(Args ...args) {
level--;
write_line("}", args...);
}
static void handle_arg(std::ostringstream *s, bool is_call, const variable_t &var) {
if (is_call)
*s << var.name;
else
*s << var.str;
}
static void handle_arg(std::ostringstream *s, bool is_call, const std::vector<variable_t> &vars) {
bool first = true;
for (const variable_t &var : vars) {
if (!first)
*s << ", ";
handle_arg(s, is_call, var);
first = false;
}
}
static void handle_arg(std::ostringstream *s, bool is_call, const function_t &f) {
handle_arg(s, is_call, f.type_name);
*s << "(";
if (f.args.empty() && !is_call)
*s << "void";
else
handle_arg(s, is_call, f.args);
*s << ")";
}
template<typename T> static void handle_arg(std::ostringstream *s, __attribute__((unused)) bool is_call, T arg) {
*s << arg;
}
static void handle_args(__attribute__((unused)) std::ostringstream *s, __attribute__((unused)) bool is_call) {
}
template<typename T, typename... Args> static void handle_args(std::ostringstream *s, bool is_call, T arg, Args ...args) {
handle_arg(s, is_call, arg);
handle_args(s, is_call, args...);
}
template<typename... Args> void write_offset(int offset, Args ...args) {
for (int i = 0; i < int(level) + offset; i++)
std::fputc('\t', file);
std::fputs(combine(args...).c_str(), file);
}
void write_line_offset(__attribute__((unused)) int offset) {
std::fputs("\n", file);
}
template<typename... Args> void write_line_offset(int offset, Args ...args) {
write_offset(offset, args..., "\n");
}
public:
class block_t : private nocopy_t {
private:
codegen_t *codegen;
std::string close_string;
public:
template<typename... Args> block_t(codegen_t *codegen0, Args ...args) : codegen(codegen0) {
codegen->open_block(args...);
}
template<typename... Args> void close(Args ...args) {
close_string = combine(args...);
}
template<typename... Args> void close_(Args ...args) {
close(args..., ";");
}
~block_t() {
codegen->close_block(close_string);
}
};
codegen_t(const char *filename);
~codegen_t();
template<typename... Args> static std::string combine(Args ...args) {
std::ostringstream s;
handle_args(&s, false, args...);
return s.str();
}
template<typename... Args> static std::string call(Args ...args) {
std::ostringstream s;
handle_args(&s, true, args...);
return s.str();
}
template<typename... Args> void write_line(Args ...args) {
write_line_offset(0, args...);
}
template<typename... Args> void write_line_(Args ...args) {
write_line(args..., ";");
}
template<typename... Args> void _write_line(Args ...args) {
write_line_offset(1, args...);
}
template<typename... Args> void _write_line_(Args ...args) {
write_line_offset(1, args..., ";");
}
template<typename... Args> void write_case(Args ...args) {
write_line_offset(-1, "case ", args..., ":");
}
template<typename... Args> void write_default() {
write_line_offset(-1, "default:");
}
};
}

View file

@ -1,344 +0,0 @@
/*
Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "output.hpp"
#include <cerrno>
#include <cstring>
#include <system_error>
namespace solar {
output_t::output_t(const char *header, const char *source)
: prefix_str("parse_"),
token_prefix_str("TOK_"),
stack_size(100),
header_filename(header) {
header_file = std::fopen(header, "w");
if (!header_file)
throw std::system_error(errno, std::generic_category(), "unable to open header output file for writing");
source_file = std::fopen(source, "w");
if (!source_file)
throw std::system_error(errno, std::generic_category(), "unable to open source output file for writing");
}
output_t::~output_t() {
std::fclose(header_file);
std::fclose(source_file);
}
void output_t::initialize() {
for (const std::string &nonterm : get_generator()->get_nonterminals())
symbol_values.insert(std::make_pair(symbol_t::make_nonterm(nonterm.c_str()), "symbol_" + nonterm));
for (const symbol_t &term : get_generator()->get_terminals()) {
if (term.get_type() == SYMBOL_TYPE_TERM)
tokens.insert(std::make_pair(term.get_value(), tokens.size()));
symbol_values.insert(std::make_pair(term, "token." + get_generator()->get_grammar().get_term_type(term).second));
}
}
std::string output_t::symbol_case(const symbol_t &sym) {
if (sym.get_type() == SYMBOL_TYPE_CHAR) {
switch (sym.get_value()[0]) {
case '\a':
return "'\\a'";
case '\b':
return "'\\b'";
case '\f':
return "'\\f'";
case '\n':
return "'\\n'";
case '\r':
return "'\\r'";
case '\t':
return "'\\t'";
case '\v':
return "'\\v'";
case '\\':
return "'\\\\'";
case '\'':
return "'\\''";
default:
return "'" + sym.get_value() + "'";
}
}
else {
if (sym.get_value().empty())
return "0";
else
return token_prefix_str + sym.get_value();
}
}
void output_t::emit_tokens() {
if (tokens.empty())
return;
std::fprintf(header_file, "enum %stoken_t {\n", prefix());
for (const auto &token : tokens)
std::fprintf(header_file, "\t%s%s = %u,\n", token_prefix(), token.first.c_str(), token.second + 256);
std::fprintf(header_file, "};\n\n");
}
void output_t::emit_token_value() {
std::fprintf(header_file, "typedef struct %stoken_value {\n", prefix());
std::map<std::string, std::string> token_values;
for (const symbol_t &term : get_generator()->get_terminals()) {
const auto &type = get_generator()->get_grammar().get_term_type(term);
if (!type.first.empty())
token_values.insert(std::make_pair(type.second, type.first));
}
for (const auto &value : token_values)
std::fprintf(header_file, "\t%s %s;\n", value.second.c_str(), value.first.c_str());
std::fprintf(header_file, "} %stoken_value_t;\n\n", prefix());
}
void output_t::emit_header() {
std::fprintf(header_file, "#pragma once\n\n");
if (!get_generator()->get_grammar().header_block.empty())
std::fprintf(header_file, "%s\n", get_generator()->get_grammar().header_block.c_str());
emit_tokens();
emit_token_value();
std::fprintf(header_file, "typedef struct %scontext %scontext_t;\n\n", prefix(), prefix());
std::fprintf(header_file, "%scontext_t * %salloc(void *(*alloc_func)(size_t));\n", prefix(), prefix());
std::fprintf(header_file, "void %sfree(%scontext_t *parser, void (*free_func)(void *));\n\n", prefix(), prefix());
std::fprintf(header_file, "int %spush(%scontext_t *parser, int token, const %stoken_value_t *value", prefix(), prefix(), prefix());
for (const auto &arg : get_generator()->get_grammar().extra_args)
std::fprintf(header_file, ", %s %s", arg.first.c_str(), arg.second.c_str());
std::fprintf(header_file, ");\n");
}
void output_t::emit_reduction(unsigned rule_id) {
const rule_t &rule = get_generator()->get_grammar().rules[rule_id];
std::fprintf(source_file, "static inline ");
const item_t &item = rule.item;
const std::string &type = get_generator()->get_grammar().get_nonterm_type(item.get_lhs());
if (type.empty())
std::fprintf(source_file, "void");
else
std::fprintf(source_file, "%s", type.c_str());
std::fprintf(source_file, " %sreduce_%u(", prefix(), rule_id);
bool empty = true;
for (unsigned i = 0; i < rule.variables.size(); i++) {
const auto &var = rule.variables[i];
if (var.first.empty())
continue;
if (!empty)
std::fprintf(source_file, ", ");
std::fprintf(source_file, "%s %s", get_generator()->get_grammar().get_type(item.get_rhs()[i]).c_str(), var.first.c_str());
empty = false;
}
for (const auto &arg : get_generator()->get_grammar().extra_args) {
if (!empty)
std::fprintf(source_file, ", ");
std::fprintf(source_file, "%s %s", arg.first.c_str(), arg.second.c_str());
empty = false;
}
if (empty)
std::fprintf(source_file, "void");
std::fprintf(source_file, ") {");
std::fprintf(source_file, "%s", rule.action.c_str());
std::fprintf(source_file, "}\n\n");
}
void output_t::emit_reductions() {
const auto &rules = get_generator()->get_grammar().rules;
for (size_t i = 0; i < rules.size(); i++) {
if (!rules[i].action.empty())
emit_reduction(i);
}
}
void output_t::emit_gotos(const std::string &lhs) {
std::map<unsigned, std::set<unsigned>> gotos;
for (size_t state = 0; state < get_generator()->get_state_count(); state++) {
auto it = get_generator()->get_gotos().find(std::make_pair(state, lhs));
if (it == get_generator()->get_gotos().end())
continue;
std::set<unsigned> &states = gotos.insert(std::make_pair(it->second, std::set<unsigned>())).first->second;
states.insert(state);
}
if (gotos.size() == 1) {
auto it = gotos.begin();
std::fprintf(source_file, "\t\t\t\tparser->stack[++parser->top].state = %u;\n", it->first);
}
else {
std::fprintf(source_file, "\t\t\t\tswitch (parser->stack[parser->top].state) {\n");
for (const auto &entry : gotos) {
for (unsigned state : entry.second)
std::fprintf(source_file, "\t\t\t\tcase %u:\n", state);
std::fprintf(source_file, "\t\t\t\t\tparser->stack[++parser->top].state = %u;\n", entry.first);
std::fprintf(source_file, "\t\t\t\t\tbreak;\n\n");
}
std::fprintf(source_file, "\t\t\t\t}\n");
}
}
void output_t::emit_states() {
for (size_t state = 0; state < get_generator()->get_state_count(); state++)
emit_state(state);
}
void output_t::emit_header_include() {
#ifdef _WIN32
const char sep = '\\';
#else
const char sep = '/';
#endif
const char *slash = std::strrchr(header_filename.c_str(), sep);
const char *basename = slash ? slash+1 : header_filename.c_str();
std::fprintf(source_file, "#include \"%s\"\n\n", basename);
}
void output_t::emit_source() {
emit_header_include();
if (!get_generator()->get_grammar().source_block.empty())
std::fprintf(source_file, "%s\n\n", get_generator()->get_grammar().source_block.c_str());
std::fprintf(source_file, "typedef union %ssymbol_value {\n", prefix());
std::fprintf(source_file, "\t%stoken_value_t token;\n", prefix());
for (const auto &nonterm : get_generator()->get_nonterminals()) {
const std::string &type = get_generator()->get_grammar().get_nonterm_type(nonterm);
if (!type.empty())
std::fprintf(source_file, "\t%s symbol_%s;\n", type.c_str(), nonterm.c_str());
}
std::fprintf(source_file, "} %ssymbol_value_t;\n\n", prefix());
std::fprintf(source_file, "typedef struct %scontext_state {\n", prefix());
std::fprintf(source_file, "\tunsigned state;\n");
std::fprintf(source_file, "\t%ssymbol_value_t value;\n", prefix());
std::fprintf(source_file, "} %scontext_state_t;\n\n", prefix());
std::fprintf(source_file, "struct %scontext {\n", prefix());
std::fprintf(source_file, "\tunsigned top;\n");
std::fprintf(source_file, "\t%scontext_state_t stack[%u];\n", prefix(), stack_size);
std::fprintf(source_file, "};\n\n\n");
std::fprintf(source_file, "%scontext_t * %salloc(void *(*alloc_func)(size_t)) {\n", prefix(), prefix());
std::fprintf(source_file, "\t%scontext_t *parser = (%scontext_t *)alloc_func(sizeof(%scontext_t));\n", prefix(), prefix(), prefix());
std::fprintf(source_file, "\tparser->top = 0;\n");
std::fprintf(source_file, "\tparser->stack[0].state = 0;\n");
std::fprintf(source_file, "\treturn parser;\n");
std::fprintf(source_file, "}\n\n");
std::fprintf(source_file, "void %sfree(%scontext_t *parser, void (*free_func)(void *)) {\n", prefix(), prefix());
std::fprintf(source_file, "\tfree_func(parser);\n");
std::fprintf(source_file, "}\n\n");
emit_reductions();
std::fprintf(source_file, "static int %sdo_push(%scontext_t *parser, int token", prefix(), prefix());
for (const auto &arg : get_generator()->get_grammar().extra_args)
std::fprintf(source_file, ", %s %s", arg.first.c_str(), arg.second.c_str());
std::fprintf(source_file, ") {\n");
std::fprintf(source_file, "\t%ssymbol_value_t result;\n\n", prefix());
std::fprintf(source_file, "\twhile (1) {\n");
std::fprintf(source_file, "\t\tswitch (parser->stack[parser->top].state) {\n");
emit_states();
std::fprintf(source_file, "\t\t}\n");
std::fprintf(source_file, "\t}\n");
std::fprintf(source_file, "}\n\n");
std::fprintf(source_file, "int %spush(%scontext_t *parser, int token, const %stoken_value_t *value", prefix(), prefix(), prefix());
for (const auto &arg : get_generator()->get_grammar().extra_args)
std::fprintf(source_file, ", %s %s", arg.first.c_str(), arg.second.c_str());
std::fprintf(source_file, ") {\n");
std::fprintf(source_file, "\tint ret = %sdo_push(parser, token", prefix());
for (const auto &arg : get_generator()->get_grammar().extra_args)
std::fprintf(source_file, ", %s", arg.second.c_str());
std::fprintf(source_file, ");\n\n");
std::fprintf(source_file, "\tif (ret > 0)\n");
std::fprintf(source_file, "\t\tparser->stack[parser->top-1].value.token = *value;\n\n");
std::fprintf(source_file, "\treturn ret;\n");
std::fprintf(source_file, "}\n");
}
void output_t::write() {
emit_header();
emit_source();
}
}

88
src/output_common.cpp Normal file
View file

@ -0,0 +1,88 @@
/*
Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "output_common.hpp"
namespace solar {
output_common_t::output_common_t(const generator_t *generator0, const std::string &prefix0, const std::string &token_prefix0) :
generator(generator0), prefix(prefix0), token_prefix(token_prefix0) {
for (const std::string &nonterm : generator->get_nonterminals())
symbol_values.insert(std::make_pair(symbol_t::make_nonterm(nonterm.c_str()), "symbol_" + nonterm));
for (const symbol_t &term : generator->get_terminals()) {
if (term.get_type() == SYMBOL_TYPE_TERM)
tokens.insert(std::make_pair(token_prefix + term.get_value(), tokens.size()));
symbol_values.insert(std::make_pair(term, "token." + generator->get_grammar().get_term_type(term).second));
}
}
std::string output_common_t::symbol_case(const symbol_t &sym) const {
if (sym.get_type() == SYMBOL_TYPE_CHAR) {
switch (sym.get_value()[0]) {
case '\a':
return "'\\a'";
case '\b':
return "'\\b'";
case '\f':
return "'\\f'";
case '\n':
return "'\\n'";
case '\r':
return "'\\r'";
case '\t':
return "'\\t'";
case '\v':
return "'\\v'";
case '\\':
return "'\\\\'";
case '\'':
return "'\\''";
default:
return "'" + sym.get_value() + "'";
}
}
else {
if (sym.get_value().empty())
return "0";
else
return token_prefix + sym.get_value();
}
}
}

95
src/output_common.hpp Normal file
View file

@ -0,0 +1,95 @@
/*
Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "generator.hpp"
#include "codegen.hpp"
namespace solar {
class output_common_t {
private:
const generator_t *generator;
std::string prefix;
std::string token_prefix;
std::map<std::string, unsigned> tokens;
std::map<symbol_t, std::string> symbol_values;
protected:
std::string prefixed(const std::string &str) const {
return (prefix + str);
}
std::string symbol_case(const symbol_t &sym) const;
std::string symbol_value(const symbol_t &sym) const {
return symbol_values.at(sym);
}
const generator_t * get_generator() const {
return generator;
}
const std::map<std::string, unsigned> & get_tokens() const {
return tokens;
}
codegen_t::function_t sig_alloc() const {
codegen_t::function_t sig(prefixed("context_t *"), prefixed("alloc"));
sig.add_arg("void *(*alloc_func)(size_t)", "alloc_func", true);
return sig;
}
codegen_t::function_t sig_free() const {
codegen_t::function_t sig("void", prefixed("free"));
sig.add_arg(prefixed("context_t *"), "parser");
sig.add_arg("void (*free_func)(void *)", "free_func", true);
return sig;
}
codegen_t::function_t sig_push() const {
codegen_t::function_t sig("int", prefixed("push"));
sig.add_arg(prefixed("context_t *"), "parser");
sig.add_arg("int", "token");
sig.add_arg("const " + prefixed("token_value_t *"), "value");
for (const auto &arg : generator->get_grammar().extra_args)
sig.add_arg(arg);
return sig;
}
public:
output_common_t(const generator_t *generator0, const std::string &prefix0, const std::string &token_prefix0);
};
}

85
src/output_header.cpp Normal file
View file

@ -0,0 +1,85 @@
/*
Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "output_header.hpp"
namespace solar {
void output_header_t::emit_tokens() {
if (get_tokens().empty())
return;
block_t token(this, "typedef enum ", prefixed("token"));
for (const auto &token : get_tokens())
write_line(token.first.c_str(), " = ", token.second + 256, ",");
token.close_(" ", prefixed("token_t"));
}
void output_header_t::emit_token_value() {
block_t token_value(this, "typedef struct ", prefixed("token_value"));
std::map<std::string, std::string> token_values;
for (const symbol_t &term : get_generator()->get_terminals()) {
const auto &type = get_generator()->get_grammar().get_term_type(term);
if (!type.first.empty())
token_values.insert(std::make_pair(type.second, type.first));
}
for (const auto &value : token_values)
write_line_(variable_t({value.second.c_str(), value.first.c_str()}));
token_value.close_(" ", prefixed("token_value_t"));
}
void output_header_t::write() {
write_line("#pragma once");
write_line();
if (!get_generator()->get_grammar().header_block.empty()) {
write_line(get_generator()->get_grammar().header_block);
write_line();
}
emit_tokens();
write_line();
emit_token_value();
write_line();
write_line_("typedef struct ", prefixed("context"), " ", prefixed("context_t"));
write_line();
write_line_(sig_alloc());
write_line_(sig_free());
write_line();
write_line_(sig_push());
}
};

48
src/output_header.hpp Normal file
View file

@ -0,0 +1,48 @@
/*
Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "codegen.hpp"
#include "output_common.hpp"
namespace solar {
class output_header_t : protected codegen_t, protected output_common_t {
private:
void emit_tokens();
void emit_token_value();
public:
output_header_t(const output_common_t &common, const char *header) :
codegen_t(header),
output_common_t(common) {}
void write();
};
};

View file

@ -1,126 +0,0 @@
/*
Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "output_lr0.hpp"
namespace solar {
void output_lr0_t::emit_state_shift(unsigned state) {
std::fprintf(source_file, "\t\t\tswitch (token) {\n");
if (generator->get_shifts().find(std::make_pair(state, symbol_t::make_nonterm(""))) != generator->get_shifts().end()) {
std::fprintf(source_file, "\t\t\tcase 0:\n");
std::fprintf(source_file, "\t\t\t\treturn 0;\n\n");
}
for (const auto &token : generator->get_terminals()) {
auto it = generator->get_shifts().find(std::make_pair(state, token));
if (it == generator->get_shifts().end())
continue;
std::fprintf(source_file, "\t\t\tcase %s:\n", symbol_case(token).c_str());
std::fprintf(source_file, "\t\t\t\tparser->stack[++parser->top].state = %u;\n", unsigned(it->second));
std::fprintf(source_file, "\t\t\t\treturn 1;\n\n");
}
std::fprintf(source_file, "\t\t\tdefault:\n");
std::fprintf(source_file, "\t\t\t\treturn -1;\n");
std::fprintf(source_file, "\t\t\t}\n");
}
void output_lr0_t::emit_state_reduce(const item_t &item, int rule_id) {
const auto &rhs = item.get_rhs();
if (rhs.size())
std::fprintf(source_file, "\t\t\tparser->top -= %u;\n", unsigned(rhs.size()));
if (rule_id >= 0) {
const std::string &type = generator->get_grammar().get_nonterm_type(item.get_lhs());
std::fprintf(source_file, "\t\t\t");
if (!type.empty())
std::fprintf(source_file, "result.symbol_%s = ", item.get_lhs().c_str());
std::fprintf(source_file, "%sreduce_%i(", prefix(), rule_id);
bool empty = true;
const auto &vars = generator->get_grammar().rules[rule_id].variables;
for (unsigned i = 0; i < vars.size(); i++) {
if (vars[i].first.empty())
continue;
if (!empty)
std::fprintf(source_file, ", ");
std::fprintf(source_file, "parser->stack[parser->top + %u].value.%s", i, symbol_values[rhs[i]].c_str());
empty = false;
}
for (const auto &arg : generator->get_grammar().extra_args) {
if (!empty)
std::fprintf(source_file, ", ");
std::fprintf(source_file, "%s", arg.second.c_str());
empty = false;
}
std::fprintf(source_file, ");\n");
for (unsigned i = 0; i < vars.size(); i++) {
if (!vars[i].second)
continue;
auto it = generator->get_grammar().destructors.find(rhs[i]);
if (it == generator->get_grammar().destructors.end())
continue;
std::fprintf(source_file, "\t\t\t%s(parser->stack[parser->top + %u].value.%s);\n", it->second.c_str(), i, symbol_values[rhs[i]].c_str());
}
if (!type.empty())
std::fprintf(source_file, "\t\t\tparser->stack[parser->top].value.symbol_%s = result.symbol_%s;\n", item.get_lhs().c_str(), item.get_lhs().c_str());
}
emit_gotos(item.get_lhs());
}
void output_lr0_t::emit_state(unsigned state) {
std::fprintf(source_file, "\t\tcase %u:\n", state);
auto it = generator->get_reductions().find(state);
if (it == generator->get_reductions().end()) {
emit_state_shift(state);
}
else {
const rule_t &rule = generator->get_grammar().rules[it->second];
emit_state_reduce(rule.item, rule.action.empty() ? -1 : it->second);
}
std::fprintf(source_file, "\t\t\tbreak;\n\n");
}
}

View file

@ -24,139 +24,21 @@
*/ */
#include "output_header.hpp"
#include "output_slr.hpp" #include "output_slr.hpp"
#include "output_source_slr.hpp"
namespace solar { namespace solar {
void output_slr_t::emit_state_shift(unsigned state) { void output_slr(const generator_slr_t *generator, const char *source, const char *header) {
std::map<unsigned, std::set<symbol_t>> shifts; output_common_t common(generator, "parse_", "TOK_");
for (const symbol_t &token : generator->get_terminals()) { output_source_slr_t output_source(common, source, header);
auto it = generator->get_shifts().find(std::make_pair(state, token)); output_source.write();
if (it == generator->get_shifts().end())
continue;
std::set<symbol_t> &symbols = shifts.insert(std::make_pair(it->second, std::set<symbol_t>())).first->second; output_header_t output_header(common, header);
symbols.insert(token); output_header.write();
}
for (const auto &entry : shifts) {
for (const symbol_t &sym : entry.second)
std::fprintf(source_file, "\t\t\tcase %s:\n", symbol_case(sym).c_str());
std::fprintf(source_file, "\t\t\t\tparser->stack[++parser->top].state = %u;\n", entry.first);
std::fprintf(source_file, "\t\t\t\treturn 1;\n\n");
}
}
void output_slr_t::emit_state_reduce_code(const item_t &item, int rule_id) {
const auto &rhs = item.get_rhs();
if (rhs.size())
std::fprintf(source_file, "\t\t\t\tparser->top -= %u;\n", unsigned(rhs.size()));
if (rule_id >= 0) {
const std::string &type = generator->get_grammar().get_nonterm_type(item.get_lhs());
std::fprintf(source_file, "\t\t\t\t");
if (!type.empty())
std::fprintf(source_file, "result.symbol_%s = ", item.get_lhs().c_str());
std::fprintf(source_file, "%sreduce_%i(", prefix(), rule_id);
bool empty = true;
const auto &vars = generator->get_grammar().rules[rule_id].variables;
for (unsigned i = 0; i < vars.size(); i++) {
if (vars[i].first.empty())
continue;
if (!empty)
std::fprintf(source_file, ", ");
std::fprintf(source_file, "parser->stack[parser->top + %u].value.%s", i, symbol_values[rhs[i]].c_str());
empty = false;
}
for (const auto &arg : generator->get_grammar().extra_args) {
if (!empty)
std::fprintf(source_file, ", ");
std::fprintf(source_file, "%s", arg.second.c_str());
empty = false;
}
std::fprintf(source_file, ");\n");
for (unsigned i = 0; i < vars.size(); i++) {
if (!vars[i].second)
continue;
auto it = generator->get_grammar().destructors.find(rhs[i]);
if (it == generator->get_grammar().destructors.end())
continue;
std::fprintf(source_file, "\t\t\t\t%s(parser->stack[parser->top + %u].value.%s);\n", it->second.c_str(), i, symbol_values[rhs[i]].c_str());
}
if (!type.empty())
std::fprintf(source_file, "\t\t\t\tparser->stack[parser->top].value.symbol_%s = result.symbol_%s;\n", item.get_lhs().c_str(), item.get_lhs().c_str());
}
emit_gotos(item.get_lhs());
}
bool output_slr_t::emit_state_reduce(unsigned state) {
std::map<unsigned, std::set<symbol_t>> reductions;
for (const symbol_t &token : generator->get_terminals()) {
auto it = generator->get_reductions().find(std::make_pair(state, token));
if (it == generator->get_reductions().end())
continue;
std::set<symbol_t> &symbols = reductions.insert(std::make_pair(it->second, std::set<symbol_t>())).first->second;
symbols.insert(token);
}
for (const auto &entry : reductions) {
if (reductions.size() == 1) {
std::fprintf(source_file, "\t\t\tdefault:\n");
}
else {
for (const symbol_t &sym : entry.second)
std::fprintf(source_file, "\t\t\tcase %s:\n", symbol_case(sym).c_str());
}
const rule_t &rule = generator->get_grammar().rules[entry.first];
emit_state_reduce_code(rule.item, rule.action.empty() ? -1 : entry.first);
if (reductions.size() != 1)
std::fprintf(source_file, "\t\t\t\tbreak;\n\n");
}
return (reductions.size() == 1);
}
void output_slr_t::emit_state(unsigned state) {
std::fprintf(source_file, "\t\tcase %u:\n", state);
std::fprintf(source_file, "\t\t\tswitch (token) {\n");
if (generator->get_shifts().find(std::make_pair(state, symbol_t::make_nonterm(""))) != generator->get_shifts().end()) {
std::fprintf(source_file, "\t\t\tcase 0:\n");
std::fprintf(source_file, "\t\t\t\treturn 0;\n\n");
}
emit_state_shift(state);
bool def = emit_state_reduce(state);
if (!def) {
std::fprintf(source_file, "\t\t\tdefault:\n");
std::fprintf(source_file, "\t\t\t\treturn -1;\n");
}
std::fprintf(source_file, "\t\t\t}\n");
std::fprintf(source_file, "\t\t\tbreak;\n\n");
} }
} }

View file

@ -27,32 +27,10 @@
#pragma once #pragma once
#include "generator_slr.hpp" #include "generator_slr.hpp"
#include "output.hpp"
namespace solar { namespace solar {
class output_slr_t : public output_t { void output_slr(const generator_slr_t *generator, const char *source, const char *header);
private:
const generator_slr_t *generator;
void emit_state_shift(unsigned state);
void emit_state_reduce_code(const item_t &item, int rule_id);
bool emit_state_reduce(unsigned state);
protected:
virtual const generator_t * get_generator() {
return generator;
}
virtual void emit_state(unsigned state);
public:
output_slr_t(const generator_slr_t *generator0, const char *header, const char *source) : output_t(header, source), generator(generator0) {
initialize();
}
};
} }

234
src/output_source.cpp Normal file
View file

@ -0,0 +1,234 @@
/*
Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "output_source.hpp"
#include <cstring>
namespace solar {
void output_source_t::emit_header_include() {
#ifdef _WIN32
const char sep = '\\';
#else
const char sep = '/';
#endif
const char *slash = std::strrchr(header_filename, sep);
const char *basename = slash ? slash+1 : header_filename;
write_line("#include \"", basename, "\"");
}
void output_source_t::emit_type_symbol_value() {
block_t symbol_value(this, "typedef union ", prefixed("symbol_value"));
write_line_(variable_t({prefixed("token_value_t"), "token"}));
for (const auto &nonterm : get_generator()->get_nonterminals()) {
const std::string &type = get_generator()->get_grammar().get_nonterm_type(nonterm);
if (!type.empty())
write_line_(variable_t({type, "symbol_" + nonterm}));
}
symbol_value.close_(" ", prefixed("symbol_value_t"));
}
void output_source_t::emit_type_context_state() {
block_t context_state(this, "typedef struct ", prefixed("context_state"));
write_line_(variable_t({"unsigned", "state"}));
write_line_(variable_t({prefixed("symbol_value_t"), "value"}));
context_state.close_(" ", prefixed("context_state_t"));
}
void output_source_t::emit_type_context() {
block_t context(this, "struct ", prefixed("context"));
write_line_(variable_t({"unsigned", "top"}));
write_line_(variable_t({prefixed("context_state_t"), combine("stack[", stack_size, "]")}));
context.close_();
}
void output_source_t::emit_types() {
emit_type_symbol_value();
write_line();
emit_type_context_state();
write_line();
emit_type_context();
write_line();
}
void output_source_t::emit_alloc() {
block_t alloc(this, sig_alloc());
write_line_(prefixed("context_t *"), "parser = (", prefixed("context_t *"), ")alloc_func(sizeof(", prefixed("context_t"), "))");
write_line_("parser->top = 0");
write_line_("parser->stack[0].state = 0");
write_line_("return parser");
}
void output_source_t::emit_free() {
block_t free_(this, sig_free());
write_line_("free_func(parser)");
}
codegen_t::function_t output_source_t::sig_reduction(unsigned rule_id) {
const rule_t &rule = get_generator()->get_grammar().rules[rule_id];
const item_t &item = rule.item;
const std::string &type = get_generator()->get_grammar().get_nonterm_type(item.get_lhs());
function_t sig("static inline " + (type.empty() ? "void" : type), prefixed(combine("reduce_", rule_id)));
for (unsigned i = 0; i < rule.variables.size(); i++) {
const auto &var = rule.variables[i];
if (var.first.empty())
continue;
sig.add_arg(get_generator()->get_grammar().get_type(item.get_rhs()[i]), var.first);
}
for (const auto &arg : get_generator()->get_grammar().extra_args)
sig.add_arg(arg);
return sig;
}
void output_source_t::emit_reduction(unsigned rule_id) {
function_t sig = sig_reduction(rule_id);
write_line(sig, " {", get_generator()->get_grammar().rules[rule_id].action, "}");
write_line();
}
void output_source_t::emit_reductions() {
const auto &rules = get_generator()->get_grammar().rules;
for (size_t i = 0; i < rules.size(); i++) {
if (!rules[i].action.empty())
emit_reduction(i);
}
}
void output_source_t::emit_gotos(const std::string &lhs) {
std::map<unsigned, std::set<unsigned>> gotos;
for (size_t state = 0; state < get_generator()->get_state_count(); state++) {
auto it = get_generator()->get_gotos().find(std::make_pair(state, lhs));
if (it == get_generator()->get_gotos().end())
continue;
std::set<unsigned> &states = gotos.insert(std::make_pair(it->second, std::set<unsigned>())).first->second;
states.insert(state);
}
if (gotos.size() == 1) {
write_line_("parser->stack[++parser->top].state = ", gotos.begin()->first);
}
else {
block_t switch_state(this, "switch (parser->stack[parser->top].state)");
for (const auto &entry : gotos) {
for (unsigned state : entry.second)
write_case(state);
write_line_("parser->stack[++parser->top].state = ", entry.first);
write_line_("break");
write_line();
}
}
}
void output_source_t::emit_states() {
for (size_t state = 0; state < get_generator()->get_state_count(); state++) {
write_case(state);
emit_state(state);
write_line_("break");
write_line();
}
}
void output_source_t::emit_do_push() {
function_t do_push_sig("static int", prefixed("do_push"));
do_push_sig.add_arg(prefixed("context_t *"), "parser");
do_push_sig.add_arg("int", "token");
for (const auto &arg : get_generator()->get_grammar().extra_args)
do_push_sig.add_arg(arg);
block_t do_push(this, do_push_sig);
write_line_(prefixed("symbol_value_t"), " result");
write_line();
block_t while_1(this, "while (1)");
block_t switch_state(this, "switch (parser->stack[parser->top].state)");
emit_states();
}
void output_source_t::emit_push() {
block_t push(this, sig_push());
write_line_("int ret = parse_do_push(parser, token, grammar)");
write_line();
write_line("if (ret > 0)");
_write_line_("parser->stack[parser->top-1].value.token = *value");
write_line();
write_line_("return ret");
}
void output_source_t::write() {
emit_header_include();
write_line();
if (!get_generator()->get_grammar().source_block.empty()) {
write_line(get_generator()->get_grammar().source_block);
write_line();
}
emit_types();
write_line();
emit_alloc();
write_line();
emit_free();
write_line();
write_line();
emit_reductions();
write_line();
emit_do_push();
write_line();
emit_push();
}
}

View file

@ -26,61 +26,52 @@
#pragma once #pragma once
#include "generator.hpp" #include "codegen.hpp"
#include "output_common.hpp"
#include <cstdio>
namespace solar { namespace solar {
class output_t { class output_source_t : protected codegen_t, protected output_common_t {
protected: private:
std::string prefix_str; const char *header_filename;
std::string token_prefix_str;
unsigned stack_size; unsigned stack_size;
std::string header_filename;
std::FILE *header_file;
std::FILE *source_file;
std::map<std::string, unsigned> tokens; std::map<std::string, unsigned> tokens;
std::map<std::string, unsigned> nonterms;
std::map<symbol_t, std::string> symbol_values; std::map<symbol_t, std::string> symbol_values;
const char * prefix() const {
return prefix_str.c_str();
}
const char * token_prefix() const { void emit_header_include();
return token_prefix_str.c_str();
}
std::string symbol_case(const symbol_t &sym); void emit_type_symbol_value();
void emit_type_context_state();
void emit_type_context();
void emit_types();
void emit_tokens(); void emit_alloc();
void emit_token_value(); void emit_free();
void emit_header();
function_t sig_reduction(unsigned rule_id);
void emit_reduction(unsigned rule_id); void emit_reduction(unsigned rule_id);
void emit_reductions(); void emit_reductions();
void emit_gotos(const std::string &lhs);
void emit_states(); void emit_states();
void emit_header_include(); void emit_do_push();
void emit_source(); void emit_push();
void initialize();
virtual const generator_t * get_generator() = 0;
virtual void emit_state(unsigned state) = 0; virtual void emit_state(unsigned state) = 0;
output_t(const char *header, const char *source); protected:
void emit_gotos(const std::string &lhs);
output_source_t(const output_common_t &common, const char *source, const char *header) :
codegen_t(source),
output_common_t(common),
header_filename(header),
stack_size(1024) {}
public: public:
virtual ~output_t();
void write(); void write();
}; };
} };

151
src/output_source_slr.cpp Normal file
View file

@ -0,0 +1,151 @@
/*
Copyright (c) 2015, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "output_source_slr.hpp"
namespace solar {
void output_source_slr_t::emit_state_shift(unsigned state) {
std::map<unsigned, std::set<symbol_t>> shifts;
for (const symbol_t &token : get_generator()->get_terminals()) {
auto it = get_generator()->get_shifts().find(std::make_pair(state, token));
if (it == get_generator()->get_shifts().end())
continue;
std::set<symbol_t> &symbols = shifts.insert(std::make_pair(it->second, std::set<symbol_t>())).first->second;
symbols.insert(token);
}
for (const auto &entry : shifts) {
for (const symbol_t &sym : entry.second)
write_case(symbol_case(sym));
write_line_("parser->stack[++parser->top].state = ", entry.first);
write_line_("return 1");
write_line();
}
}
void output_source_slr_t::emit_state_reduce_code(const item_t &item, unsigned rule_id) {
const auto &rhs = item.get_rhs();
const std::string &type = get_generator()->get_grammar().get_nonterm_type(item.get_lhs());
const auto &vars = get_generator()->get_grammar().rules[rule_id].variables;
function_t reduce_func("", prefixed(combine("reduce_", rule_id)));
for (unsigned i = 0; i < vars.size(); i++) {
if (vars[i].first.empty())
continue;
reduce_func.add_arg("", combine("parser->stack[parser->top + ", i, "].value.", symbol_value(rhs[i])));
}
for (const auto &arg : get_generator()->get_grammar().extra_args)
reduce_func.add_arg(arg);
if (type.empty())
write_line_(call(reduce_func));
else
write_line_("result.symbol_", item.get_lhs(), " = ", call(reduce_func));
for (unsigned i = 0; i < vars.size(); i++) {
if (!vars[i].second)
continue;
auto it = get_generator()->get_grammar().destructors.find(rhs[i]);
if (it == get_generator()->get_grammar().destructors.end())
continue;
write_line_(it->second, "(parser->stack[parser->top + ", i, "].value.", symbol_value(rhs[i]), ")");
}
if (!type.empty())
write_line_("parser->stack[parser->top].value.symbol_", item.get_lhs(), " = result.symbol_", item.get_lhs());
}
bool output_source_slr_t::emit_state_reduce(unsigned state) {
std::map<unsigned, std::set<symbol_t>> reductions;
for (const symbol_t &token : get_generator()->get_terminals()) {
auto it = get_generator()->get_reductions().find(std::make_pair(state, token));
if (it == get_generator()->get_reductions().end())
continue;
std::set<symbol_t> &symbols = reductions.insert(std::make_pair(it->second, std::set<symbol_t>())).first->second;
symbols.insert(token);
}
for (const auto &entry : reductions) {
if (reductions.size() == 1) {
write_default();
}
else {
for (const symbol_t &sym : entry.second)
write_case(symbol_case(sym));
}
const rule_t &rule = get_generator()->get_grammar().rules[entry.first];
if (rule.item.get_rhs().size())
write_line_("parser->top -= ", rule.item.get_rhs().size());
if (!rule.action.empty())
emit_state_reduce_code(rule.item, entry.first);
emit_gotos(rule.item.get_lhs());
if (reductions.size() != 1) {
write_line_("break");
write_line();
}
}
return (reductions.size() == 1);
}
void output_source_slr_t::emit_state(unsigned state) {
block_t switch_token(this, "switch (token)");
if (get_generator()->get_shifts().find(std::make_pair(state, symbol_t::make_nonterm(""))) != get_generator()->get_shifts().end()) {
write_case(0);
write_line_("return 0");
write_line();
}
emit_state_shift(state);
bool def = emit_state_reduce(state);
if (!def) {
write_default();
write_line_("return -1");
}
}
}

View file

@ -26,29 +26,28 @@
#pragma once #pragma once
#include "generator_lr0.hpp" #include "generator_slr.hpp"
#include "output.hpp" #include "output_source.hpp"
namespace solar { namespace solar {
class output_lr0_t : public output_t { class output_source_slr_t : public output_source_t {
private: private:
const generator_lr0_t *generator; const generator_slr_t * get_generator() {
return dynamic_cast<const generator_slr_t *>(output_common_t::get_generator());
void emit_state_shift(unsigned state);
void emit_state_reduce(const item_t &item, int rule_id);
protected:
virtual const generator_t * get_generator() {
return generator;
} }
void emit_state_shift(unsigned state);
void emit_state_reduce_code(const item_t &item, unsigned rule_id);
bool emit_state_reduce(unsigned state);
protected:
virtual void emit_state(unsigned state); virtual void emit_state(unsigned state);
public: public:
output_lr0_t(const generator_lr0_t *generator0, const char *header, const char *source) : output_t(header, source), generator(generator0) { output_source_slr_t(const output_common_t &common, const char *source, const char *header) : output_source_t(common, source, header) {
initialize();
} }
}; };

View file

@ -39,7 +39,7 @@ typedef struct parse_context_state {
struct parse_context { struct parse_context {
unsigned top; unsigned top;
parse_context_state_t stack[100]; parse_context_state_t stack[1024];
}; };
@ -54,6 +54,7 @@ void parse_free(parse_context_t *parser, void (*free_func)(void *)) {
free_func(parser); free_func(parser);
} }
static inline void parse_reduce_3(std::string *nonterm, std::string *type, __attribute__((unused)) solar::grammar_t *grammar) { static inline void parse_reduce_3(std::string *nonterm, std::string *type, __attribute__((unused)) solar::grammar_t *grammar) {
grammar->nonterm_types.insert(std::make_pair(*nonterm, *type)); grammar->nonterm_types.insert(std::make_pair(*nonterm, *type));
} }
@ -142,6 +143,7 @@ static inline std::string * parse_reduce_24(std::string * v, __attribute__((unus
static inline std::string * parse_reduce_25(std::string *v, __attribute__((unused)) solar::grammar_t *grammar) {return v;} static inline std::string * parse_reduce_25(std::string *v, __attribute__((unused)) solar::grammar_t *grammar) {return v;}
static int parse_do_push(parse_context_t *parser, int token, __attribute__((unused)) solar::grammar_t *grammar) { static int parse_do_push(parse_context_t *parser, int token, __attribute__((unused)) solar::grammar_t *grammar) {
parse_symbol_value_t result; parse_symbol_value_t result;

View file

@ -3,7 +3,8 @@
#include "grammar.hpp" #include "grammar.hpp"
enum parse_token_t {
typedef enum parse_token {
TOK_BLOCK = 256, TOK_BLOCK = 256,
TOK_CHAR = 257, TOK_CHAR = 257,
TOK_SQBLOCK = 258, TOK_SQBLOCK = 258,
@ -11,7 +12,7 @@ enum parse_token_t {
TOK_SYMBOL = 260, TOK_SYMBOL = 260,
TOK_SYMBOL_LC = 261, TOK_SYMBOL_LC = 261,
TOK_SYMBOL_UC = 262, TOK_SYMBOL_UC = 262,
}; } parse_token_t;
typedef struct parse_token_value { typedef struct parse_token_value {
char c; char c;

View file

@ -89,9 +89,7 @@ int main(int argc, char *argv[]) {
return 1; return 1;
generator_slr_t generator(grammar); generator_slr_t generator(grammar);
output_slr(&generator, argv[2], argv[3]);
output_slr_t output(&generator, argv[3], argv[2]);
output.write();
return 0; return 0;
} }