aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/lua/luaexpander.cpp
blob: 46731a7390b95158c40fffd19e4737da3c378f23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#include "luaengine.h"
#include "luatr.h"

#include <utils/macroexpander.h>

using namespace Utils;

namespace Lua::Internal {

void setNext(
    MacroExpander *expander,
    sol::state &lua,
    auto &table,
    const QByteArray &key,
    QList<QByteArray>::const_iterator it,
    QList<QByteArray>::const_iterator end)
{
    if (it + 1 == end) {
        if (expander->isPrefixVariable(key)) {
            table.set_function(it->toStdString(), [key, expander](const QString &s) -> QString {
                return expander->value(key + s.toUtf8());
            });
        } else {
            table.set(it->toStdString(), expander->value(key));
        }
    } else {
        auto existingTable = table.template get<sol::optional<sol::table>>(it->toStdString());
        if (existingTable) {
            setNext(expander, lua, *existingTable, key, it + 1, end);
        } else {
            sol::table newTable = lua.create_table();
            setNext(expander, lua, newTable, key, it + 1, end);
            table.set(it->toStdString(), newTable);
        }
    }
};

sol::protected_function_result run(sol::state &lua, QString statement, MacroExpander *expander)
{
    return runFunction(lua, statement, "Statement", [expander](sol::state &lua) {
        sol::global_table &t = lua.globals();

        for (QByteArray key : expander->visibleVariables()) {
            if (key != "Lua:<value>") {
                if (key.endsWith(":<value>"))
                    key = key.chopped(7);

                QList<QByteArray> parts = key.split(':');
                parts.removeIf([](const QByteArray &part) { return part.isEmpty(); });
                setNext(expander, lua, t, key, parts.cbegin(), parts.cend());
            }
        }
    });
}

Result<QString> tryRun(const QString statement, MacroExpander *expander)
{
    sol::state lua;

    auto result = run(lua, statement, expander);
    if (!result.valid()) {
        sol::error err = result;
        return make_unexpected(QString::fromStdString(err.what()));
    }

    QStringList results;

    for (int i = 1; i <= result.return_count(); i++) {
        size_t l;
        const char *s = luaL_tolstring(result.lua_state(), int(i), &l);
        results.append(QString::fromUtf8(s, int(l)));
    }
    return results.join(QLatin1Char(' '));
}

void setupLuaExpander(MacroExpander *expander)
{
    expander->registerPrefix(
        "Lua",
        Tr::tr("Evaluate simple Lua statements.<br>"
               "Literal '}' characters must be escaped as \"\\}\", "
               "'\\' characters must be escaped as \"\\\\\", "
               "'#' characters must be escaped as \"\\#\", "
               "and \"%{\" must be escaped as \"%\\{\"."),
        [expander](const QString &statement) -> QString {
            if (statement.isEmpty())
                return Tr::tr("No Lua statement to evaluate.");

            Result<QString> result = tryRun("return " + statement, expander);
            if (result)
                return *result;

            result = tryRun(statement, expander);
            if (!result)
                return result.error();

            return *result;
        });
}

} // namespace Lua::Internal