local n=require(script.Parent.Functions)local W="\037\100\043\037\046\063\037\100\042"local Y="\091\037\097\095\093\091\037\119\095\093\042"local v="\091\037\043\037-\037\042\047\037\037\094\093"local g="\037\040"local N="\037\041"local m="\044"local function j(n,W,Y)local v=string.match(n,"\094"..W)if v~=nil then return Y(v),string.sub(n,#v+1)end end local function d(n)return j(n,W,function(n)return{kind="\078\117\109\098\101\114";value=tonumber(n)}end)end local function o(W)return j(W,Y,function(W)local Y=n[W]~=nil and"\070\117\110\099\116\105\111\110"or"\086\097\114\105\097\098\108\101"return{kind=Y;identifier=W}end)end local function c(n)return j(n,v,function(n)return{kind="\079\112\101\114\097\116\111\114",operator=n}end)end local function Z(n)return j(n,g,function()return{kind="\079\112\101\110 \080\097\114\101\110\116\104\101\115\105\115"}end)end local function y(n)return j(n,N,function()return{kind="\067\108\111\115\101 \080\097\114\101\110\116\104\101\115\105\115"}end)end local function T(n)return j(n,m,function()return{kind="\065\114\103\117\109\101\110\116 \083\101\112\097\114\097\116\111\114"}end)end local function k(n)return string.gsub(n,"\037\115\043","")end local function t(n)return function(W)for n,Y in ipairs(n)do local v,g=Y(W)if v~=nil then return v,g end end return nil,W end end local a=t({d;o,c;Z,y,T})local function q(n)local W,Y=a(k(n))local v={}while W~=nil do table.insert(v,W)W,Y=a(Y)end if#Y>0 then error(string.format("\085\110\101\120\112\101\099\116\101\100 \115\101\113\117\101\110\099\101\058 \037\115",Y))end return v end local E={}E.__index=E function E.new(n)return setmetatable({tokens=q(n),index=1},E)end function E.peek(n)return n.tokens[n.index]end function E.next(n)local W=n.tokens[n.index]n.index+=1 return W end function E.expect(W,n)local Y=W:next()assert(Y~=nil and Y.kind==n,string.format("\069\120\112\101\099\116\101\100 \037\115",n))end return E
local Functions = require(script.Parent.Functions)

local NUMBER_PATTERN = "%d+%.?%d*"
local IDENTIFIER_PATTERN = "[%a_][%w_]*"
local OPERATOR_PATTERN = "[%+%-%*/%%^]"
local OPEN_PARENTHESIS_PATTERN = "%("
local CLOSE_PARENTHESIS_PATTERN = "%)"
local ARGUMENT_SEPARATOR_PATTERN = ","

local function match(input, pattern, tokenizer)
	local matched = string.match(input, "^" .. pattern)

	if matched ~= nil then
		return tokenizer(matched), string.sub(input, #matched + 1)
	end
end

local function matchNumber(input)
	return match(input, NUMBER_PATTERN, function(matched)
		return { kind = "Number", value = tonumber(matched) }
	end)
end

local function matchIdentifier(input)
	return match(input, IDENTIFIER_PATTERN, function(matched)
		local kind = Functions[matched] ~= nil and "Function" or "Variable"

		return { kind = kind, identifier = matched }
	end)
end

local function matchOperator(input)
	return match(input, OPERATOR_PATTERN, function(matched)
		return { kind = "Operator", operator = matched }
	end)
end

local function matchOpenParenthesis(input)
	return match(input, OPEN_PARENTHESIS_PATTERN, function()
		return { kind = "Open Parenthesis" }
	end)
end

local function matchCloseParenthesis(input)
	return match(input, CLOSE_PARENTHESIS_PATTERN, function()
		return { kind = "Close Parenthesis" }
	end)
end

local function matchArgumentSeparator(input)
	return match(input, ARGUMENT_SEPARATOR_PATTERN, function()
		return { kind = "Argument Separator" }
	end)
end

local function removeWhitespace(input)
	return string.gsub(input, "%s+", "")
end

local function matchOrElse(potentialMatches)
	return function(input)
		for _, potentialMatch in ipairs(potentialMatches) do
			local token, rest = potentialMatch(input)

			if token ~= nil then
				return token, rest
			end
		end

		return nil, input
	end
end

local findNextToken = matchOrElse({
	matchNumber,
	matchIdentifier,
	matchOperator,
	matchOpenParenthesis,
	matchCloseParenthesis,
	matchArgumentSeparator,
})

local function tokenize(input)
	local nextToken, rest = findNextToken(removeWhitespace(input))
	local tokens = {}

	while nextToken ~= nil do
		table.insert(tokens, nextToken)
		nextToken, rest = findNextToken(rest)
	end

	if #rest > 0 then
		error(string.format("Unexpected sequence: %s", rest))
	end

	return tokens
end

local Lexer = {}
Lexer.__index = Lexer

function Lexer.new(input)
	return setmetatable({
		tokens = tokenize(input),
		index = 1,
	}, Lexer)
end

function Lexer:peek()
	return self.tokens[self.index]
end

function Lexer:next()
	local token = self.tokens[self.index]

	self.index += 1

	return token
end

function Lexer:expect(kind)
	local token = self:next()

	assert(token ~= nil and token.kind == kind, string.format("Expected %s", kind))
end

return Lexer
