mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-13 13:30:40 +00:00
Feature: Web REPL using Emscripten (#138)
Currently doesn't include the new page into navigation since we aren't building the .js files anywhere.
This commit is contained in:
parent
d6b3346f58
commit
aec8fbfd0f
6 changed files with 133 additions and 10 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@
|
|||
^default.prof*
|
||||
^fuzz-*
|
||||
^luau$
|
||||
/.vs
|
||||
|
|
44
CLI/Repl.cpp
44
CLI/Repl.cpp
|
@ -198,6 +198,11 @@ static std::string runCode(lua_State* L, const std::string& source)
|
|||
error += "\nstack backtrace:\n";
|
||||
error += lua_debugtrace(T);
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// nicer formatting for errors in web repl
|
||||
error = "Error:" + error;
|
||||
#endif
|
||||
|
||||
fprintf(stdout, "%s", error.c_str());
|
||||
}
|
||||
|
||||
|
@ -205,6 +210,44 @@ static std::string runCode(lua_State* L, const std::string& source)
|
|||
return std::string();
|
||||
}
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
extern "C"
|
||||
{
|
||||
const char* executeScript(const char* source)
|
||||
{
|
||||
// static string for caching result (prevents dangling ptr on function exit)
|
||||
static std::string result;
|
||||
|
||||
// setup flags
|
||||
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
|
||||
if (strncmp(flag->name, "Luau", 4) == 0)
|
||||
flag->value = true;
|
||||
|
||||
// create new state
|
||||
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
|
||||
lua_State* L = globalState.get();
|
||||
|
||||
// setup state
|
||||
setupState(L);
|
||||
|
||||
// sandbox thread
|
||||
luaL_sandboxthread(L);
|
||||
|
||||
// run code + collect error
|
||||
std::string error = runCode(L, source);
|
||||
result = error;
|
||||
|
||||
if (error.length())
|
||||
{
|
||||
return result.c_str();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Excluded from emscripten compilation to avoid -Wunused-function errors.
|
||||
#ifndef __EMSCRIPTEN__
|
||||
static void completeIndexer(lua_State* L, const char* editBuffer, size_t start, std::vector<std::string>& completions)
|
||||
{
|
||||
std::string_view lookup = editBuffer + start;
|
||||
|
@ -547,3 +590,4 @@ int main(int argc, char** argv)
|
|||
return failed;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -17,17 +17,26 @@ add_library(Luau.VM STATIC)
|
|||
|
||||
if(LUAU_BUILD_CLI)
|
||||
add_executable(Luau.Repl.CLI)
|
||||
add_executable(Luau.Analyze.CLI)
|
||||
if(NOT EMSCRIPTEN)
|
||||
add_executable(Luau.Analyze.CLI)
|
||||
else()
|
||||
# add -fexceptions for emscripten to allow exceptions to be caught in C++
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
|
||||
endif()
|
||||
|
||||
# This also adds target `name` on Linux/macOS and `name.exe` on Windows
|
||||
set_target_properties(Luau.Repl.CLI PROPERTIES OUTPUT_NAME luau)
|
||||
set_target_properties(Luau.Analyze.CLI PROPERTIES OUTPUT_NAME luau-analyze)
|
||||
|
||||
if(NOT EMSCRIPTEN)
|
||||
set_target_properties(Luau.Analyze.CLI PROPERTIES OUTPUT_NAME luau-analyze)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LUAU_BUILD_TESTS)
|
||||
if(LUAU_BUILD_TESTS AND NOT EMSCRIPTEN)
|
||||
add_executable(Luau.UnitTest)
|
||||
add_executable(Luau.Conformance)
|
||||
endif()
|
||||
|
||||
include(Sources.cmake)
|
||||
|
||||
target_compile_features(Luau.Ast PUBLIC cxx_std_17)
|
||||
|
@ -53,10 +62,6 @@ if(MSVC)
|
|||
else()
|
||||
list(APPEND LUAU_OPTIONS -Wall) # All warnings
|
||||
list(APPEND LUAU_OPTIONS -Werror) # Warnings are errors
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
list(APPEND LUAU_OPTIONS -Wno-unused) # GCC considers variables declared/checked in if() as unused
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_compile_options(Luau.Ast PRIVATE ${LUAU_OPTIONS})
|
||||
|
@ -65,7 +70,10 @@ target_compile_options(Luau.VM PRIVATE ${LUAU_OPTIONS})
|
|||
|
||||
if(LUAU_BUILD_CLI)
|
||||
target_compile_options(Luau.Repl.CLI PRIVATE ${LUAU_OPTIONS})
|
||||
target_compile_options(Luau.Analyze.CLI PRIVATE ${LUAU_OPTIONS})
|
||||
|
||||
if(NOT EMSCRIPTEN)
|
||||
target_compile_options(Luau.Analyze.CLI PRIVATE ${LUAU_OPTIONS})
|
||||
endif()
|
||||
|
||||
target_include_directories(Luau.Repl.CLI PRIVATE extern)
|
||||
target_link_libraries(Luau.Repl.CLI PRIVATE Luau.Compiler Luau.VM)
|
||||
|
@ -74,10 +82,20 @@ if(LUAU_BUILD_CLI)
|
|||
target_link_libraries(Luau.Repl.CLI PRIVATE pthread)
|
||||
endif()
|
||||
|
||||
target_link_libraries(Luau.Analyze.CLI PRIVATE Luau.Analysis)
|
||||
if(NOT EMSCRIPTEN)
|
||||
target_link_libraries(Luau.Analyze.CLI PRIVATE Luau.Analysis)
|
||||
endif()
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
# declare exported functions to emscripten
|
||||
target_link_options(Luau.Repl.CLI PRIVATE -sEXPORTED_FUNCTIONS=['_executeScript'] -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap'] -fexceptions)
|
||||
|
||||
# custom output directory for wasm + js file
|
||||
set_target_properties(Luau.Repl.CLI PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/docs/assets/luau)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LUAU_BUILD_TESTS)
|
||||
if(LUAU_BUILD_TESTS AND NOT EMSCRIPTEN)
|
||||
target_compile_options(Luau.UnitTest PRIVATE ${LUAU_OPTIONS})
|
||||
target_include_directories(Luau.UnitTest PRIVATE extern)
|
||||
target_link_libraries(Luau.UnitTest PRIVATE Luau.Analysis Luau.Compiler)
|
||||
|
|
|
@ -27,3 +27,7 @@ pages:
|
|||
url: /profile
|
||||
- title: Library
|
||||
url: /library
|
||||
|
||||
# Remove demo pages until solution is found
|
||||
# - title: Demo
|
||||
# url: /demo
|
||||
|
|
50
docs/_includes/repl.html
Normal file
50
docs/_includes/repl.html
Normal file
|
@ -0,0 +1,50 @@
|
|||
<form>
|
||||
<div>
|
||||
<label>Script:</label>
|
||||
<br>
|
||||
<textarea rows="10" cols="70" id="script">print("Hello World!")</textarea>
|
||||
<br><br>
|
||||
<button onclick="clearInput(); return false;">
|
||||
Clear Input
|
||||
</button>
|
||||
<button onclick="executeScript(); return false;">
|
||||
Run
|
||||
</button>
|
||||
</div>
|
||||
<br><br>
|
||||
<div>
|
||||
<label>Output:</label>
|
||||
<br>
|
||||
<textarea readonly rows="10" cols="70" id="output"></textarea>
|
||||
<br><br>
|
||||
<button onclick="clearOutput(); return false;">
|
||||
Clear Output
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function output(text) {
|
||||
document.getElementById("output").value += "[" + new Date().toLocaleTimeString() + "] " + text.replace('stdin:', '') + "\n";
|
||||
}
|
||||
|
||||
var Module = {
|
||||
'print': function (msg) { output(msg) }
|
||||
};
|
||||
|
||||
function clearInput() {
|
||||
document.getElementById("script").value = "";
|
||||
}
|
||||
|
||||
function clearOutput() {
|
||||
document.getElementById("output").value = "";
|
||||
}
|
||||
|
||||
function executeScript() {
|
||||
var err = Module.ccall('executeScript', 'string', ['string'], [document.getElementById("script").value]);
|
||||
if (err) {
|
||||
output('Error:' + err.replace('stdin:', ''));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script async src="assets/luau/luau.js"></script>
|
6
docs/_pages/demo.md
Normal file
6
docs/_pages/demo.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
permalink: /demo
|
||||
title: Demo
|
||||
---
|
||||
|
||||
{% include repl.html %}
|
Loading…
Reference in a new issue