yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ellie Hermaszewskaextend fiddle to allow custom lua splices in more places (#7559)5120c1cd0

master
6.6 KiB296 linesraw
1// slang-fiddle-script.cpp
2#include "slang-fiddle-script.h"
3
4#include "compiler-core/slang-diagnostic-sink.h"
5#include "lua/lapi.h"
6#include "lua/lauxlib.h"
7#include "lua/lualib.h"
8
9#include <cstdio>
10
11namespace fiddle
12{
13DiagnosticSink* _sink = nullptr;
14StringBuilder* _builder = nullptr;
15Count _templateCounter = 0;
16
17static void _writeLuaMessage(Severity severity, String const& message)
18{
19    if (_sink)
20    {
21        _sink->diagnoseRaw(severity, message.getUnownedSlice());
22    }
23    else
24    {
25        fprintf(stderr, "%s", message.getBuffer());
26    }
27}
28
29int _trace(lua_State* L)
30{
31    int argCount = lua_gettop(L);
32
33    for (int i = 0; i < argCount; ++i)
34    {
35        lua_pushliteral(L, " ");
36        luaL_tolstring(L, i + 1, nullptr);
37    }
38    lua_concat(L, 2 * argCount);
39
40    size_t size = 0;
41    char const* buffer = lua_tolstring(L, -1, &size);
42
43    String message;
44    message.append("fiddle:");
45    message.append(UnownedStringSlice(buffer, size));
46    message.append("\n");
47
48    _writeLuaMessage(Severity::Note, message);
49    return 0;
50}
51
52int _handleLuaErrorRaised(lua_State* L)
53{
54    lua_pushliteral(L, "\n");
55    luaL_traceback(L, L, nullptr, 1);
56    lua_concat(L, 3);
57    return 1;
58}
59
60int _original(lua_State* L)
61{
62    // We ignore the text that we want to just pass
63    // through unmodified...
64    return 0;
65}
66
67int _raw(lua_State* L)
68{
69    size_t size = 0;
70    char const* buffer = lua_tolstring(L, 1, &size);
71
72    _builder->append(UnownedStringSlice(buffer, size));
73    return 0;
74}
75
76int _splice(lua_State* L)
77{
78    auto savedBuilder = _builder;
79
80    StringBuilder spliceBuilder;
81    _builder = &spliceBuilder;
82
83    lua_pushvalue(L, 1);
84    lua_call(L, 0, 1);
85
86    _builder = savedBuilder;
87
88    // The actual string value follows whatever
89    // got printed to the output (unless it is
90    // nil).
91    //
92    _builder->append(spliceBuilder.produceString());
93    if (!lua_isnil(L, -1))
94    {
95        size_t size = 0;
96        char const* buffer = luaL_tolstring(L, -1, &size);
97        _builder->append(UnownedStringSlice(buffer, size));
98    }
99    return 0;
100}
101
102int _template(lua_State* L)
103{
104    auto templateID = _templateCounter++;
105
106    _builder->append("\n#if FIDDLE_GENERATED_OUTPUT_ID == ");
107    _builder->append(templateID);
108    _builder->append("\n");
109
110    lua_pushvalue(L, 1);
111    lua_call(L, 0, 0);
112
113    _builder->append("\n#endif\n");
114
115    return 0;
116}
117
118lua_State* L = nullptr;
119
120// Add a custom searcher that handles relative paths
121// So we can do things like require("source/slang/foo.lua")
122static int path_searcher(lua_State* L)
123{
124    const char* modname = luaL_checkstring(L, 1);
125
126    if (luaL_loadfile(L, modname) == LUA_OK)
127    {
128        lua_pushstring(L, modname); // Push filename as second return
129        return 2;
130    }
131
132    // Not found
133    lua_pushfstring(L, "\n\tno file '%s'", modname);
134    return 1;
135}
136
137void install_path_searcher(lua_State* L)
138{
139    lua_getglobal(L, "package");
140    lua_getfield(L, -1, "searchers");
141
142    // Insert at position 2 (after preload)
143    lua_pushcfunction(L, path_searcher);
144    lua_rawseti(L, -2, 2);
145
146    lua_pop(L, 2);
147}
148
149void ensureLuaInitialized()
150{
151    if (L)
152        return;
153
154    L = luaL_newstate();
155    luaL_openlibs(L);
156
157    lua_pushcclosure(L, &_trace, 0);
158    lua_setglobal(L, "TRACE");
159
160    lua_pushcclosure(L, &_original, 0);
161    lua_setglobal(L, "ORIGINAL");
162
163    lua_pushcclosure(L, &_raw, 0);
164    lua_setglobal(L, "RAW");
165
166    lua_pushcclosure(L, &_splice, 0);
167    lua_setglobal(L, "SPLICE");
168
169    lua_pushcclosure(L, &_template, 0);
170    lua_setglobal(L, "TEMPLATE");
171
172    install_path_searcher(L);
173
174    // TODO: register custom stuff here...
175}
176
177lua_State* getLuaState()
178{
179    ensureLuaInitialized();
180    return L;
181}
182
183static void setupLuaEnvironment(const String& originalFileName)
184{
185    ensureLuaInitialized();
186
187    lua_pushstring(L, originalFileName.getBuffer());
188    lua_setglobal(L, "THIS_FILE");
189
190    lua_pushcfunction(L, &_handleLuaErrorRaised);
191}
192
193static void handleLuaError(
194    SourceLoc loc,
195    DiagnosticSink* sink,
196    const char* errorType,
197    DiagnosticInfo diagnosticID)
198{
199    size_t size = 0;
200    char const* buffer = lua_tolstring(L, -1, &size);
201    String message = UnownedStringSlice(buffer, size);
202    message = message + "\n";
203
204    sink->diagnose(loc, diagnosticID, message);
205
206    String abortMessage = "fiddle failed during Lua ";
207    abortMessage = abortMessage + errorType;
208    SLANG_ABORT_COMPILATION(abortMessage.getBuffer());
209}
210
211String evaluateScriptCode(
212    SourceLoc loc,
213    String originalFileName,
214    String scriptSource,
215    DiagnosticSink* sink)
216{
217    StringBuilder builder;
218    _builder = &builder;
219    _templateCounter = 0;
220
221    setupLuaEnvironment(originalFileName);
222
223    String luaChunkName = "@" + originalFileName;
224
225    if (LUA_OK != luaL_loadbuffer(
226                      L,
227                      scriptSource.getBuffer(),
228                      scriptSource.getLength(),
229                      luaChunkName.getBuffer()))
230    {
231        handleLuaError(loc, sink, "script loading", fiddle::Diagnostics::scriptLoadError);
232    }
233
234    if (LUA_OK != lua_pcall(L, 0, 0, -2))
235    {
236        handleLuaError(loc, sink, "script execution", fiddle::Diagnostics::scriptExecutionError);
237    }
238
239    _builder = nullptr;
240    return builder.produceString();
241}
242
243String evaluateLuaExpression(
244    SourceLoc loc,
245    String originalFileName,
246    String luaExpression,
247    DiagnosticSink* sink)
248{
249    setupLuaEnvironment(originalFileName);
250
251    String luaChunkName = "@" + originalFileName;
252
253    // Wrap expression in return statement to get its value
254    String wrappedExpression = "return " + luaExpression;
255
256    if (LUA_OK != luaL_loadbuffer(
257                      L,
258                      wrappedExpression.getBuffer(),
259                      wrappedExpression.getLength(),
260                      luaChunkName.getBuffer()))
261    {
262        handleLuaError(loc, sink, "expression loading", fiddle::Diagnostics::scriptLoadError);
263    }
264
265    // Execute and expect 1 return value
266    if (LUA_OK != lua_pcall(L, 0, 1, -2))
267    {
268        handleLuaError(
269            loc,
270            sink,
271            "expression evaluation",
272            fiddle::Diagnostics::scriptExecutionError);
273    }
274
275    // Convert the result to string
276    size_t resultSize = 0;
277    const char* resultBuffer = lua_tolstring(L, -1, &resultSize);
278
279    if (!resultBuffer)
280    {
281        sink->diagnose(
282            loc,
283            fiddle::Diagnostics::scriptExecutionError,
284            "Lua expression did not return a string value\n");
285        SLANG_ABORT_COMPILATION("fiddle failed: non-string expression result");
286    }
287
288    String result;
289    result.append(resultBuffer, resultSize);
290
291    // Pop the result and error handler
292    lua_pop(L, 2);
293
294    return result;
295}
296} // namespace fiddle