Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,23 @@ jobs:

steps:
- uses: actions/checkout@v4

- name: Set reusable strings
# Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
id: strings
shell: bash
run: |
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"

- name: Setup and Build Lute
uses: ./.github/actions/setup-and-build
with:
config: debug
options: ${{ matrix.options }}

- name: Run Analysis
run: ${{ steps.strings.outputs.build-output-dir }}/lute --check std batteries examples

check-format:
runs-on: ubuntu-latest

Expand Down
1 change: 1 addition & 0 deletions .luaurc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"aliases": {
"batteries": "batteries",
"std": "std",
"lute": "definitions"
}
}
23 changes: 14 additions & 9 deletions batteries/json.luau
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ local function writeSpaces(state: SerializerState)
state.cursor += 4
end
else
buffer.writeu8(state.buf, state.cursor, string.byte(" "))
-- FIXME: cast required by Luau bug (https://github.com/luau-lang/luau/issues/1703)
buffer.writeu8(state.buf, state.cursor, string.byte(" ") :: number)

state.cursor += 1
end
Expand Down Expand Up @@ -185,7 +186,7 @@ local function serializeTable(state: SerializerState, object: Object)
writeByte(state, string.byte("}"))
end

serializeAny = function(state: SerializerState, value: Value)
function serializeAny(state: SerializerState, value: Value)
local valueType = type(value)

if value == json.NULL then
Expand All @@ -197,7 +198,7 @@ serializeAny = function(state: SerializerState, value: Value)
elseif valueType == "string" then
serializeString(state, value :: string)
elseif valueType == "table" then
if #(value :: {}) == 0 and next(value :: {}) ~= nil then
if #(value :: {}) == 0 and next(value :: { [any]: any }) ~= nil then
serializeTable(state, value :: Object)
else
serializeArray(state, value :: Array)
Expand Down Expand Up @@ -430,7 +431,7 @@ local function deserializeObject(state: DeserializerState): Object
return current
end

deserialize = function(state: DeserializerState): Value
function deserialize(state: DeserializerState): Value
skipWhitespace(state)

local fourChars = string.sub(state.src, state.cursor, state.cursor + 3)
Expand All @@ -446,16 +447,20 @@ deserialize = function(state: DeserializerState): Value
return false
elseif string.match(state.src, "^[%d%.]", state.cursor) then
-- number
return deserializeNumber(state)
return deserializeNumber(state :: DeserializerState) -- FIXME: cast required by Luau bug (https://github.com/luau-lang/luau/issues/1702)
elseif string.byte(state.src, state.cursor) == string.byte('"') then
return deserializeString(state)
return deserializeString(state :: DeserializerState) -- FIXME: cast required by Luau bug (https://github.com/luau-lang/luau/issues/1702)
elseif string.byte(state.src, state.cursor) == string.byte("[") then
return deserializeArray(state)
return deserializeArray(state :: DeserializerState) -- FIXME: cast required by Luau bug (https://github.com/luau-lang/luau/issues/1702)
elseif string.byte(state.src, state.cursor) == string.byte("{") then
return deserializeObject(state)
return deserializeObject(state :: DeserializerState) -- FIXME: cast required by Luau bug (https://github.com/luau-lang/luau/issues/1702)
end

return deserializerError(state, `Unexpected token '{string.sub(state.src, state.cursor, state.cursor)}'`)
-- FIXME: cast required by Luau bug (https://github.com/luau-lang/luau/issues/1702)
return deserializerError(
state :: DeserializerState,
`Unexpected token '{string.sub(state.src, state.cursor, state.cursor)}'`
)
end

-- user-facing
Expand Down
89 changes: 60 additions & 29 deletions cli/tc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,9 @@
#include "Luau/Error.h"
#include "Luau/Transpiler.h"
#include "Luau/TypeAttach.h"
#include "Luau/Require.h"

static const std::string kLuteDefinitions = R"LUTE_TYPES(
-- Net api
declare net: {
get: (string) -> string,
getAsync: (string) -> string,
}
-- fs api
declare class file end
declare fs: {
-- probably not the correct sig
open: (string, "r" | "w" | "a" | "r+" | "w+") -> file,
close: (file) -> (),
read: (file) -> string,
write: (file, string) -> (),
readfiletostring : (string) -> string,
writestringtofile : (string, string) -> (),
-- is this right? I feel like we want a promise type here
readasync : (string) -> string,
}

-- globals
declare function spawn(path: string): any

)LUTE_TYPES";
LUAU_FASTFLAG(LuauSolverV2)

struct LuteFileResolver : Luau::FileResolver
{
Expand Down Expand Up @@ -57,7 +35,21 @@ struct LuteFileResolver : Luau::FileResolver

std::optional<Luau::ModuleInfo> resolveModule(const Luau::ModuleInfo* context, Luau::AstExpr* node) override
{
// TODO: Need to handle requires
if (Luau::AstExprConstantString* expr = node->as<Luau::AstExprConstantString>())
{
std::string path{expr->value.data, expr->value.size};

AnalysisRequireContext requireContext{context->name};
AnalysisCacheManager cacheManager;
AnalysisErrorHandler errorHandler;

RequireResolver resolver(path, requireContext, cacheManager, errorHandler);
RequireResolver::ResolvedRequire resolvedRequire = resolver.resolveRequire();

if (resolvedRequire.status == RequireResolver::ModuleStatus::FileRead)
return {{resolvedRequire.identifier}};
}

return std::nullopt;
}

Expand All @@ -69,7 +61,46 @@ struct LuteFileResolver : Luau::FileResolver
}

private:
// TODO: add require resolver;
struct AnalysisRequireContext : RequireResolver::RequireContext
{
explicit AnalysisRequireContext(std::string path)
: path(std::move(path))
{
}

std::string getPath() override
{
return path;
}

bool isRequireAllowed() override
{
return true;
}

bool isStdin() override
{
return path == "-";
}

std::string createNewIdentifer(const std::string& path) override
{
return path;
}

private:
std::string path;
};

struct AnalysisCacheManager : public RequireResolver::CacheManager
{
AnalysisCacheManager() = default;
};

struct AnalysisErrorHandler : RequireResolver::ErrorHandler
{
AnalysisErrorHandler() = default;
};
};

struct LuteConfigResolver : Luau::ConfigResolver
Expand Down Expand Up @@ -222,6 +253,9 @@ std::vector<std::string> processSourceFiles(const std::vector<std::string>& sour

int typecheck(const std::vector<std::string>& sourceFilesInput)
{
// Lute only supports the new type solver
FFlag::LuauSolverV2.value = true;

std::vector<std::string> sourceFiles = processSourceFiles(sourceFilesInput);

if (sourceFiles.empty())
Expand All @@ -243,9 +277,6 @@ int typecheck(const std::vector<std::string>& sourceFilesInput)
Luau::Frontend frontend(&fileResolver, &configResolver, frontendOptions);

Luau::registerBuiltinGlobals(frontend, frontend.globals);
Luau::LoadDefinitionFileResult loadResult =
frontend.loadDefinitionFile(frontend.globals, frontend.globals.globalScope, kLuteDefinitions, "@luau", false, false);
LUAU_ASSERT(loadResult.success);
Luau::freeze(frontend.globals.globalTypes);

for (const std::string& path : sourceFiles)
Expand Down
63 changes: 63 additions & 0 deletions definitions/fs.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
local fs = {}

export type FileHandle = {
fd: number,
err: number,
}

export type FileType = "file" | "dir" | "link" | "fifo" | "socket" | "char" | "block" | "unknown"

export type DirectoryEntry = {
name: string,
type: FileType,
}

function fs.open(path: string): FileHandle
error("not implemented")
end

function fs.read(handle: FileHandle): string
error("not implemented")
end

function fs.write(handle: FileHandle, contents: string): ()
error("not implemented")
end

function fs.close(handle: FileHandle): ()
error("not implemented")
end

function fs.remove(path: string): ()
error("not implemented")
end

function fs.type(path: string): FileType
error("not implemented")
end

function fs.mkdir(path: string): ()
error("not implemented")
end

function fs.listdir(path: string): { DirectoryEntry }
error("not implemented")
end

function fs.rmdir(path: string): ()
error("not implemented")
end

function fs.readfiletostring(filepath: string): string
error("not implemented")
end

function fs.writestringtofile(filepath: string, contents: string): ()
error("not implemented")
end

function fs.readasync(filepath: string): string
error("not implemented")
end

return fs
16 changes: 16 additions & 0 deletions definitions/luau.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
local luau = {}

type ParseResult = {
root: any,
lines: number,
}

function luau.parse(source: string): ParseResult
error("not implemented")
end

function luau.parseexpr(source: string): ParseResult
error("not implemented")
end

return luau
11 changes: 11 additions & 0 deletions definitions/net.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
local net = {}

function net.get(url: string): string
error("not implemented")
end

function net.getAsync(url: string): string
error("not implemented")
end

return net
5 changes: 5 additions & 0 deletions definitions/task.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
local task = {}

function task.defer() end

return task
7 changes: 7 additions & 0 deletions definitions/vm.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
local vm = {}

function vm.create(file: string): any
error("not implemented")
end

return vm
2 changes: 1 addition & 1 deletion examples/json.luau
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ print(serialize)

local deserialized = json.deserialize([[{
"hello": "world"
}]])
}]]) :: { hello: string }

print(deserialized.hello)
Loading