mirror of
https://github.com/elua/elua.git
synced 2025-01-08 20:56:17 +08:00
400ab5bee5
Documentation updated.
222 lines
8.6 KiB
Lua
222 lines
8.6 KiB
Lua
-- Attributes are used by various parts of the board description
|
|
-- system (for example configs and components)
|
|
|
|
module( ..., package.seeall )
|
|
local ct = require "constants"
|
|
local sf = string.format
|
|
|
|
-- Validator for a 'choice' attribute
|
|
-- Returns the value if OK, (false, errmsg) for error
|
|
-- Needs: attrvals - list of permitted values for this attribute
|
|
local function _validate_choice( adesc, aname, aval, elname, sectname )
|
|
aval = tostring( aval ):lower()
|
|
for k, v in pairs( adesc.attrvals ) do
|
|
if v == aval then return v end
|
|
end
|
|
return false, sf( "invalid value '%s' for attribute '%s' of element '%s' in section '%s'", aval, aname, elname, sectname )
|
|
end
|
|
|
|
-- Validator for a 'number' attribute
|
|
-- Returns the value if OK, (false, errmsg) for error
|
|
-- Needs: attrtype - the number type ('int' or 'float')
|
|
-- attrmin - minimum value (no minimum check will be performed if not specified)
|
|
-- attrmax - maximum value (no maximum check will be performed if not specified)
|
|
-- allow_string - allows this number to also be a string
|
|
local function _validate_number( adesc, aname, _aval, elname, sectname )
|
|
local aval = tonumber( _aval )
|
|
if not aval then
|
|
if type( _aval ) == "string" and adesc.allow_string then
|
|
return _aval
|
|
else
|
|
return false, sf( "value of attribute '%s' for element '%s' in section '%s' must be a number", aname, elname, sectname ) end
|
|
end
|
|
if adesc.attrtype == 'int' and math.floor( aval ) ~= aval then
|
|
return false, sf( "value of attribute '%s' for element '%s' in section '%s' must be an integer", aname, elname, sectname )
|
|
end
|
|
local minval = adesc.attrmin or aval
|
|
local maxval = adesc.attrmax or aval
|
|
if aval < minval then
|
|
return false, sf( "value of attribute '%s' for element '%s' in section '%s' must be larger than '%s'", aname, elname, sectname, tostring( minval ) )
|
|
end
|
|
if aval > maxval then
|
|
return false, sf( "value of attribute '%s' for element '%s' in section '%s' must be smaller than '%s'", aname, elname, sectname, tostring( maxval ) )
|
|
end
|
|
return aval
|
|
end
|
|
|
|
-- Validator for a log2 number attribute
|
|
-- Works like number, but additionaly checks that the value is a power of 2
|
|
-- Also changes the attribute value to its log2
|
|
local function _validate_log2( adesc, aname, aval, elname, sectname )
|
|
local res, err = _validate_number( adesc, aname, aval, elname, sectname )
|
|
if not res then return res, err end
|
|
if adesc.allow_string and not tonumber( res ) and type( res ) == "string" then return res end
|
|
if aval <= 0 then
|
|
return false, sf( "value of attribute '%s' for element '%s' in section '%s' must be larger than 0", aname, elname, sectname )
|
|
end
|
|
local thelog = math.log( res ) / math.log( 2 )
|
|
if thelog ~= math.floor( thelog ) then
|
|
return false, sf( "value of attribute '%s' for element '%s' in section '%s' must be a power of 2", aname, elname, sectname )
|
|
end
|
|
return thelog
|
|
end
|
|
|
|
-- Validator for a string attribute
|
|
-- Return the string if OK, (false, errmsg) for error
|
|
-- Needs: attrmaxsize - maximum size of the string
|
|
local function _validate_string( adesc, aname, aval, elname, sectname )
|
|
if type( aval ) ~= "string" then
|
|
return false, sf( "value of attribute '%s' for element '%s' in section '%s' must be a string", aname, elname, sectname )
|
|
end
|
|
maxsize = adesc.attrmaxsize or math.huge
|
|
if #aval > maxsize then
|
|
return false, sf( "value of attribute '%s' for element '%s' in section '%s' must be less than %d chars in length", aname, elname, sectname, maxsize )
|
|
end
|
|
return aval
|
|
end
|
|
|
|
-- Validator for an IP attribute
|
|
-- Return the IP as a table of numbers if OK, (false, errmsg) for error
|
|
local function _validate_ip( adesc, aname, aval, elname, sectname )
|
|
if type( aval ) == "string" then -- transform this to table
|
|
if aval:sub( -1, -1 ) ~= "." then aval = aval .. "." end -- add a final dot to make parsing easier
|
|
local t = {}
|
|
for w in aval:gmatch( "(%w+)%." ) do t[ #t + 1 ] = tonumber( w ) end
|
|
aval = t
|
|
elseif type( aval ) ~= "table" then
|
|
return false, sf( "attribute '%s' of element '%s' in section '%s' must be a string or a table", aname, elname, sectname )
|
|
end
|
|
if #aval ~= 4 then return false, sf( "invalid IP for attribute '%s' of element '%s' in section '%s'", aname, elname, sectname ) end
|
|
for i = 1, 4 do
|
|
local e = aval[ i ]
|
|
if type( e ) ~= "number" or math.floor( e ) ~= e or e < 0 or e > 255 then
|
|
return false, sf( "invalid IP for attribute '%s' of element '%s' in section '%s'", aname, elname, sectname )
|
|
end
|
|
end
|
|
return aval
|
|
end
|
|
|
|
-- Builds a validator with the given array element checker
|
|
local function build_validator( realvname )
|
|
return function( adesc, aname, aval, elname, sectname )
|
|
if not adesc.is_array then return realvname( adesc, aname, aval, elname, sectname ) end
|
|
if type( aval ) ~= "table" then
|
|
if adesc.allow_single then
|
|
aval = { aval }
|
|
else
|
|
return false, sf( "value of attribute '%s' for element '%s' in section '%s' must be an array", aname, elname, sectname )
|
|
end
|
|
end
|
|
for i = 1, #aval do
|
|
local res, err = realvname( adesc, aname, aval[ i ], elname, sectname )
|
|
if not res then
|
|
return false, sf( "error at index %d: %s", i, err )
|
|
else
|
|
aval[ i ] = res
|
|
end
|
|
end
|
|
return aval
|
|
end
|
|
end
|
|
|
|
local validate_number = build_validator( _validate_number )
|
|
local validate_choice = build_validator( _validate_choice )
|
|
local validate_log2 = build_validator( _validate_log2 )
|
|
local validate_string = build_validator( _validate_string )
|
|
local validate_ip = build_validator( _validate_ip )
|
|
|
|
-- Composite validator: run each validator in turn
|
|
local function composite_validator( adesc, aname, aval, elname, sectname )
|
|
for _, onedesc in pairs( adesc.attrs ) do
|
|
onedesc.is_array, onedesc.allow_single = adesc.is_array, onedesc.allow_single
|
|
local res, err = onedesc.validator( onedesc, aname, aval, elname, sectname )
|
|
if res then return res end
|
|
end
|
|
return false, sf( "Invalid value '%s' for attribute '%s' of element '%s' in section '%s'", tostring( aval ), aname, elname, sectname )
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- Public interface
|
|
|
|
-- Returns a new choice attribute with the given possible values
|
|
function choice_attr( macro, values, default )
|
|
return { macro = macro, default = default, validator = validate_choice, attrvals = values }
|
|
end
|
|
|
|
-- Returns a new timer attribute with the given macro and default (systmr if not specified)
|
|
function timer_attr( macro, default )
|
|
default = default or 'systmr'
|
|
local t = choice_attr( macro, utils.table_keys( ct.timer_values ), default )
|
|
t.is_timer, t.mapping = true, ct.timer_values
|
|
return t
|
|
end
|
|
|
|
-- Returns a new integer number attribute with the given limits
|
|
function int_attr( macro, minv, maxv, default )
|
|
return { macro = macro, default = default, validator = validate_number, attrtype = 'int', attrmin = minv, attrmax = maxv, allow_string = true }
|
|
end
|
|
|
|
-- Returns a new UART attribute
|
|
function uart_attr( macro, default )
|
|
local t = choice_attr( macro, utils.table_keys( ct.uart_values ), default )
|
|
t.is_uart, t.mapping = true, ct.uart_values
|
|
return t
|
|
end
|
|
|
|
-- Returns a new integer number attribute with the given limits
|
|
-- The generated value will be the integer's base 2 logarithm
|
|
function int_log2_attr( macro, minv, maxv, default )
|
|
local t = int_attr( macro, minv, maxv, default )
|
|
t.validator = validate_log2
|
|
return t
|
|
end
|
|
|
|
-- Returns a new flow control attribute
|
|
function flow_control_attr( macro, default )
|
|
default = default or 'none'
|
|
local t = choice_attr( macro, utils.table_keys( ct.uart_flow ), default )
|
|
t.mapping = ct.uart_flow
|
|
return t
|
|
end
|
|
|
|
-- Returns a new string attribute
|
|
function string_attr( macro, maxsize, default )
|
|
return { macro = macro, default = default, validator = validate_string, attrmaxsize = maxsize }
|
|
end
|
|
|
|
-- Returns a new IP attribute
|
|
function ip_attr( macro, default )
|
|
return { macro = macro, default = default, validator = validate_ip, is_ip = true }
|
|
end
|
|
|
|
-- Returns a new combined attribute
|
|
function combine_attr( macro, attrs )
|
|
return { macro = macro, validator = composite_validator, attrs = attrs }
|
|
end
|
|
|
|
-- Make the given attribute optional
|
|
function make_optional( attr )
|
|
attr.optional = true
|
|
return attr
|
|
end
|
|
|
|
-- Mark the given attribute as an array of element of the same type
|
|
function array_of( attr, allow_single )
|
|
attr.is_array = true
|
|
attr.allow_single = allow_single
|
|
return attr
|
|
end
|
|
|
|
-- Bypass the validator of a given attribute
|
|
function dont_validate( attr )
|
|
attr.validator = function( adesc, aname, aval, elname, sectname ) return aval end
|
|
return attr
|
|
end
|
|
|
|
-- Removes the string option from the given number attribute
|
|
function nostr( attr )
|
|
attr.allow_string = false
|
|
return attr
|
|
end
|
|
|