type INULL = {}
type INULL.@MetaTable = INULL
type INULL.@Name = "NULL"
type INULL.__index = INULL
type INULL.IsValid = function=(INULL)>(false)
type INULL.@Contract = INULL

§ if not io.open("examples/projects/gmod/nattlua/glua_base.nlua") then io.write("Cannot find examples/projects/gmod/nattlua/glua_base.nlua\nYou must run examples/projects/gmod/nattlua/build_glua_base.lua first\n") os.exit(1) end

import("~/nattlua/glua_base.nlua")
type NULL = INULL

analyzer function hook.Add(eventName: string, obj: any, callback: ref function=(...any)>(...any))
	local event_callback = env.typesystem.EventCallbacks:Get(eventName)
	callback:SetOutputSignature(event_callback:GetOutputSignature())
	callback:SetExplicitOutputSignature(true)
	callback:SetInputSignature(event_callback:GetInputSignature())
	callback:SetCalled(false)
end

analyzer function CompileString(code: string, name: string, should_throw: boolean | nil)
	should_throw = should_throw and should_throw:IsLiteral() and should_throw:GetData()

	if should_throw == nil then should_throw = true end

	code = code:IsLiteral() and code:GetData() or nil
	name = name and name:IsLiteral() and name:GetData() or nil

	if code then
		local func, err = nl.load(code, name)

		if func then return func end

		if should_throw then error(err) end

		return err
	end
end

analyzer function isstring(obj: any)
	local typ = analyzer:Call(env.typesystem.type, types.Tuple({obj}), analyzer.current_expression):Get(1)
	local binary_operator = require("nattlua.analyzer.operators.binary").Binary
	return binary_operator(
		analyzer,
		analyzer.current_expression,
		typ,
		types.LString("string"),
		"runtime",
		"=="
	)
end

analyzer function istable(obj: any)
	local typ = analyzer:Call(env.typesystem.type, types.Tuple({obj}), analyzer.current_expression):Get(1)
	local binary_operator = require("nattlua.analyzer.operators.binary").Binary
	return binary_operator(
		analyzer,
		analyzer.current_expression,
		typ,
		types.LString("table"),
		"runtime",
		"=="
	)
end

analyzer function isentity(obj: any)
	local Entity = env.typesystem.Entity
	local binary_operator = require("nattlua.analyzer.operators.binary").Binary
	return binary_operator(
		analyzer,
		analyzer.current_expression,
		obj:GetMetaTable() or obj,
		Entity,
		"typesystem",
		"=="
	)
end

function string.Implode(seperator: ref string, Table: ref {[1 .. inf] = string})
	return table.concat(Table, seperator)
end

function string.GetFileFromFilename(path: ref string)
	if (not path:find("\\") and not path:find("/")) then return path end

	return path:match("[\\/]([^/\\]+)$") or ""
end

function string.GetPathFromFilename(path: ref string)
	return path:match("^(.*[/\\])[^/\\]-$") or ""
end

function string.ToTable(str: ref string)
	local tbl = {}

	for i = 1, string.len(str) do
		tbl[i] = string.sub(str, i, i)
	end

	return tbl
end

function math.Clamp(n: ref number, low: ref number, high: ref number)
	return math.min(math.max(n, low), high)
end

do
	local totable = string.ToTable
	local string_sub = string.sub
	local string_find = string.find
	local string_len = string.len

	function string.Explode(separator: ref string, str: ref string, withpattern: ref boolean | nil)
		if (separator == "") then return totable(str) end

		if (withpattern == nil) then withpattern = false end

		local ret = {}
		local current_pos = 1

		for i = 1, string_len(str) do
			local start_pos, end_pos = string_find(str, separator, current_pos, not withpattern)

			if (not start_pos) then break end

			ret[i] = string_sub(str, current_pos, start_pos - 1)
			current_pos = end_pos + 1
		end

		ret[#ret + 1] = string_sub(str, current_pos)
		return ret
	end

	function string.Split(str: ref string, delimiter: ref string)
		return string.Explode(delimiter, str)
	end
end

analyzer function include(path: string)
	local full_path = analyzer:ResolvePath(path:GetData())
	local compiler = assert(require("nattlua").File(full_path))
	assert(compiler:Lex())
	assert(compiler:Parse())
	local res = analyzer:AnalyzeRootStatement(compiler.SyntaxTree)
	analyzer.loaded = analyzer.loaded or {}
	analyzer.loaded[path] = res
	return res
end