program.cpp 5.62 KB
Newer Older
Paul's avatar
Paul committed
1
#include <rtg/program.hpp>
Paul's avatar
Paul committed
2
#include <rtg/stringutils.hpp>
Paul's avatar
Paul committed
3
#include <rtg/instruction.hpp>
Paul's avatar
Paul committed
4
#include <iostream>
Paul's avatar
Paul committed
5
6
7
8
#include <algorithm>

namespace rtg {

Paul's avatar
Paul committed
9
10
11
12
13
14
struct program_impl
{
    // A list is used to keep references to an instruction stable
    std::list<instruction> instructions;
};

Paul's avatar
Paul committed
15
program::program() : impl(std::make_unique<program_impl>()) {}
Paul's avatar
Paul committed
16

Paul's avatar
Paul committed
17
program::program(program&&) noexcept = default;
Paul's avatar
Paul committed
18
19
program& program::operator=(program&&) noexcept = default;
program::~program() noexcept                    = default;
Paul's avatar
Paul committed
20

Paul's avatar
Paul committed
21
instruction_ref program::add_instruction(operation op, std::vector<instruction_ref> args)
Paul's avatar
Paul committed
22
23
24
{
    return insert_instruction(impl->instructions.end(), std::move(op), std::move(args));
}
Paul's avatar
Paul committed
25
26
instruction_ref
program::insert_instruction(instruction_ref ins, operation op, std::vector<instruction_ref> args)
Paul's avatar
Paul committed
27
{
Paul's avatar
Paul committed
28
29
30
    assert(std::all_of(
               args.begin(), args.end(), [&](instruction_ref x) { return has_instruction(x); }) &&
           "Argument is not an exisiting instruction");
Paul's avatar
Paul committed
31
32
    // TODO: Use move
    shape r     = compute_shape(op, args);
Paul's avatar
Paul committed
33
    auto result = impl->instructions.insert(ins, {op, r, args});
Paul's avatar
Paul committed
34
    backreference(result);
Paul's avatar
Paul committed
35
    assert(result->arguments == args);
Paul's avatar
Paul committed
36
    return result;
Paul's avatar
Paul committed
37
38
}

Paul's avatar
Paul committed
39
40
41
42
43
44
45
instruction_ref
program::replace_instruction(instruction_ref ins, operation op, std::vector<instruction_ref> args)
{
    assert(std::all_of(
               args.begin(), args.end(), [&](instruction_ref x) { return has_instruction(x); }) &&
           "Argument is not an exisiting instruction");

Paul's avatar
Paul committed
46
    shape r = compute_shape(op, args);
Paul's avatar
Paul committed
47
48
49
50
51
    ins->replace(op, r, args);
    backreference(ins);
    return ins;
}

Paul's avatar
Paul committed
52
instruction_ref program::add_literal(literal l)
Paul's avatar
Paul committed
53
{
Paul's avatar
Paul committed
54
55
56
57
58
59
60
61
    impl->instructions.emplace_front(std::move(l));
    return impl->instructions.begin();
}

instruction_ref program::add_outline(shape s)
{
    impl->instructions.push_front({builtin::outline{s}, s, {}});
    return impl->instructions.begin();
Paul's avatar
Paul committed
62
63
}

Paul's avatar
Paul committed
64
instruction_ref program::add_parameter(std::string name, shape s)
Paul's avatar
Paul committed
65
{
Paul's avatar
Paul committed
66
67
68
69
70
71
72
    impl->instructions.push_front({builtin::param{std::move(name)}, s, {}});
    return impl->instructions.begin();
}

shape program::get_parameter_shape(std::string name)
{
    auto ins = std::find_if(
Paul's avatar
Paul committed
73
74
75
76
77
78
79
80
81
82
83
84
85
86
        impl->instructions.begin(), impl->instructions.end(), [&](const instruction& x) {
            if(x.op.name() == "@param")
            {
                return any_cast<builtin::param>(x.op).parameter == name;
            }
            else
            {
                return false;
            }
        });
    if(ins != this->end())
        return ins->result;
    else
        return {};
Paul's avatar
Paul committed
87
88
}

Paul's avatar
Paul committed
89
bool program::has_instruction(instruction_ref ins) const
Paul's avatar
Paul committed
90
{
Paul's avatar
Paul committed
91
92
93
94
    return std::find_if(
               impl->instructions.begin(), impl->instructions.end(), [&](const instruction& x) {
                   return std::addressof(*ins) == std::addressof(x);
               }) != impl->instructions.end();
Paul's avatar
Paul committed
95
96
}

Paul's avatar
Paul committed
97
98
instruction_ref program::begin() { return impl->instructions.begin(); }
instruction_ref program::end() { return impl->instructions.end(); }
99

Paul's avatar
Paul committed
100
101
instruction_ref program::validate() const
{
Paul's avatar
Paul committed
102
103
104
    return std::find_if(impl->instructions.begin(),
                        impl->instructions.end(),
                        [](const instruction& i) { return i.valid(); });
Paul's avatar
Paul committed
105
106
}

Paul's avatar
Paul committed
107
108
109
110
111
112
113
114
void program::compile(const target& t)
{
    assert(this->validate() != impl->instructions.end());
    t.apply(*this);
    if(this->validate() == impl->instructions.end())
        RTG_THROW("Invalid program from compilation");
}

115
argument program::eval(std::unordered_map<std::string, argument> params) const
Paul's avatar
Paul committed
116
{
Paul's avatar
Paul committed
117
    assert(this->validate() != impl->instructions.end());
Paul's avatar
Paul committed
118
119
    std::unordered_map<const instruction*, argument> results;
    argument result;
Paul's avatar
Paul committed
120
    for(auto& ins : impl->instructions)
Paul's avatar
Paul committed
121
    {
Paul's avatar
Paul committed
122
        if(ins.op.name() == "@literal")
Paul's avatar
Paul committed
123
124
125
        {
            result = ins.lit.get_argument();
        }
Paul's avatar
Paul committed
126
        else if(ins.op.name() == "@param")
Paul's avatar
Paul committed
127
        {
Paul's avatar
Paul committed
128
            result = params.at(any_cast<builtin::param>(ins.op).parameter);
Paul's avatar
Paul committed
129
        }
Paul's avatar
Paul committed
130
131
132
133
        else if(ins.op.name() == "@outline")
        {
            result = argument{ins.result, nullptr};
        }
Paul's avatar
Paul committed
134
135
136
        else
        {
            std::vector<argument> values(ins.arguments.size());
Paul's avatar
Paul committed
137
138
139
            std::transform(ins.arguments.begin(),
                           ins.arguments.end(),
                           values.begin(),
Paul's avatar
Paul committed
140
                           [&](instruction_ref i) { return results.at(std::addressof(*i)); });
Paul's avatar
Paul committed
141
            result = ins.op.compute(ins.result, values);
Paul's avatar
Paul committed
142
143
144
        }
        results.emplace(std::addressof(ins), result);
    }
145
    return result;
Paul's avatar
Paul committed
146
147
}

Paul's avatar
Paul committed
148
std::ostream& operator<<(std::ostream& os, const program& p)
Paul's avatar
Paul committed
149
150
151
152
{
    std::unordered_map<const instruction*, std::string> names;
    int count = 0;

Paul's avatar
Paul committed
153
    for(auto& ins : p.impl->instructions)
Paul's avatar
Paul committed
154
155
    {
        std::string var_name = "@" + std::to_string(count);
Paul's avatar
Paul committed
156
        if(ins.op.name() == "@param")
Paul's avatar
Paul committed
157
        {
Paul's avatar
Paul committed
158
            var_name = any_cast<builtin::param>(ins.op).parameter;
Paul's avatar
Paul committed
159
160
        }

Paul's avatar
Paul committed
161
        os << var_name << " = ";
Paul's avatar
Paul committed
162

Paul's avatar
Paul committed
163
        os << ins.op;
Paul's avatar
Paul committed
164
165
166

        if(ins.op.name() == "@literal")
        {
Paul's avatar
Paul committed
167
            if(ins.lit.get_shape().elements() > 10)
Paul's avatar
Paul committed
168
                os << "{ ... }";
Paul's avatar
Paul committed
169
            else
Paul's avatar
Paul committed
170
                os << "{" << ins.lit << "}";
Paul's avatar
Paul committed
171
172
173
174
175
        }

        if(!ins.arguments.empty())
        {
            char delim = '(';
Paul's avatar
Paul committed
176
            for(auto&& arg : ins.arguments)
Paul's avatar
Paul committed
177
            {
Paul's avatar
Paul committed
178
179
                assert(p.has_instruction(arg) && "Instruction not found");
                os << delim << names.at(std::addressof(*arg));
Paul's avatar
Paul committed
180
181
                delim = ',';
            }
Paul's avatar
Paul committed
182
            os << ")";
Paul's avatar
Paul committed
183
184
        }

Paul's avatar
Paul committed
185
        os << " -> " << ins.result;
Paul's avatar
Paul committed
186

Paul's avatar
Paul committed
187
        os << std::endl;
Paul's avatar
Paul committed
188
189

        names.emplace(std::addressof(ins), var_name);
Paul's avatar
Paul committed
190
        count++;
Paul's avatar
Paul committed
191
    }
Paul's avatar
Paul committed
192
    return os;
Paul's avatar
Paul committed
193
194
}

Paul's avatar
Paul committed
195
} // namespace rtg