Module:Card collection/Parser

From Yugipedia
Jump to: navigation, search
-- <pre>
--[=[Doc
@module Card collection/Parser
@description 
@author [[User:Becasita]]
@contact [[User talk:Becasita]]
]=]

local UTIL = require( 'Module:Util' )

local Reporter = require( 'Module:Reporter' )

local DefaultHandlers = require( 'Module:Card collection/DefaultHandlers' )

local mwText = mw.text
local mwTextGsplit = mwText.gsplit
local mwTextSplit = mwText.split

local function makeDependenciesPath( root, dependency )
	return table.concat( {
		'Module:Card collection',
		'modules',
		root,
		dependency,
	}, '/' )
end

local function loadParameters( root )
	local parametersPath = makeDependenciesPath( root, 'parameters' )

	local parameters = require( parametersPath ) -- TODO: should be mw.loadData, but is read only... TODO: safeRquire?

	-- Parameter 1 is mandatory and has a minimal standard definition:
	parameters[ 1 ] = parameters[ 1 ] or {}

	parameters[ 1 ].required = true

	parameters[ 1 ].allowEmpty = false

	-- Parameter `separator` has a minimal standard definition:
	parameters.separator = parameters.separator or {}

	parameters.separator.default = parameters.separator.default or ';'

	parameters.separator.allowEmpty = false

	-- TODO: split definitions to their own file and merge with specific definitions?

	return parameters
end

local function loadHandlers( root )
	local handlersPath = makeDependenciesPath( root, 'handlers' )

	local handlers = require( handlersPath ) -- or {} TODO: safeRquire?

	return setmetatable( handlers, {
		__index = DefaultHandlers,
	} )
end

local function parseEntry( handlers, rawEntry, lineno ) -- TODO: have this on utils?
	local entryPair = mwTextSplit( rawEntry, '%s*//%s*' )

	local entryValues = handlers.utils:parseValues( entryPair[ 1 ] )

	local entryOptions = handlers.utils:parseOptions(
		entryPair[ 2 ] or '',
		( 'line %d' ):format( lineno )
	)

	return {
		raw = rawEntry,
		lineno = lineno,
		values = entryValues,
		options = entryOptions,
	}
end

local Parser = {}
Parser.__index = Parser

function Parser.new( name )
	local data = {
		name = name,
		parameters = loadParameters( name ),
		handlers = loadHandlers( name ),
	}

	return setmetatable( data, Parser )
end

function Parser:parse( frame, arguments, moduleArguments )
	local reporter = Reporter( self.name )

	local handlers = self.handlers:new( self.name, reporter, frame )

	local globalData = handlers.utils:validateArguments( self.parameters, arguments, moduleArguments )

	local wrapper = mw.html.create( 'div' )
		:addClass( handlers.utils:makeCssClass() )

	if globalData.hasHaltingError then
		wrapper:node( reporter:dump() )

		return tostring( wrapper )
	end

	handlers.utils:setSeparator( globalData.separator )

	globalData = handlers:initData( globalData ) or globalData -- TODO: for internal use in modules: split here.

	local mainStructure = handlers:initStructure( globalData )
		:allDone()
		:addClass( handlers.utils:makeCssClass( 'main' ) )

	do
		local lineno = 0 -- Count of non-empty lines.

		for rawEntry in mwTextGsplit( globalData[ 1 ], '%s*\n%s*' ) do
			local rawEntry = UTIL.trim( rawEntry )

			if rawEntry then
				lineno = lineno + 1

				local entry = parseEntry( handlers, rawEntry, lineno )

				local handledEntry = handlers:handleEntry( entry, globalData )

				mainStructure:node( tostring( handledEntry ) )
			end
		end
	end

	local allStructures = { handlers:finalize( mainStructure, globalData ) }

	wrapper:node( reporter:dump() )

	for _, structure in ipairs( allStructures ) do
		wrapper:node( tostring( structure ) )
	end

	return tostring( wrapper )
end

function Parser:test( arguments, moduleArguments )
	local parseResult = self:parse(
		mw.getCurrentFrame(),
		arguments or {},
		moduleArguments or {}
	)

	return parseResult
end

return setmetatable( Parser, {
	__call = function( t, name )
		assert(
			t == Parser,
			'Cannot apply Card collection/Parser constructor except to itself'
		)

		return Parser.new( name )
	end
} )
-- </pre>