diff options
Diffstat (limited to 'lib/lyaml/implicit.lua')
-rw-r--r-- | lib/lyaml/implicit.lua | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/lib/lyaml/implicit.lua b/lib/lyaml/implicit.lua new file mode 100644 index 000000000000..fe58025b560d --- /dev/null +++ b/lib/lyaml/implicit.lua @@ -0,0 +1,283 @@ +-- LYAML parse implicit type tokens. +-- Written by Gary V. Vaughan, 2015 +-- +-- Copyright(C) 2015-2022 Gary V. Vaughan +-- +-- Permission is hereby granted, free of charge, to any person obtaining +-- a copy of this software and associated documentation files(the +-- "Software"), to deal in the Software without restriction, including +-- without limitation the rights to use, copy, modify, merge, publish, +-- distribute, sublicense, and/or sell copies of the Software, and to +-- permit persons to whom the Software is furnished to do so, subject to +-- the following conditions: +-- +-- The above copyright notice and this permission notice shall be +-- included in all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--- @module lyaml.implicit + + +local NULL = require 'lyaml.functional'.NULL +local find = string.find +local floor = math.floor +local gsub = string.gsub +local sub = string.sub + +local tointeger = (function(f) + if not tointeger then + -- No host tointeger implementation, use our own. + return function(x) + if type(x) == 'number' and x - floor(x) == 0.0 then + return x + end + end + + elseif f '1' ~= nil then + -- Don't perform implicit string-to-number conversion! + return function(x) + if type(x) == 'number' then + return tointeger(x) + end + end + end + + -- Host tointeger is good! + return f +end)(math.tointeger) + + +local function int(x) + local r = tonumber(x) + if r ~= nil then + return tointeger(r) + end +end + + +local is_null = {['']=true, ['~']=true, null=true, Null=true, NULL=true} + + +--- Parse a null token to a null value. +-- @param value token +-- @return[1] lyaml.null, for an empty string or literal ~ +-- @return[2] nil otherwise, nil +-- @usage maybe_null = implicit.null(token) +local function null(value) + if is_null[value] then + return NULL + end +end + + +local to_bool = { + ['true'] = true, True = true, TRUE = true, + ['false'] = false, False = false, FALSE = false, + yes = true, Yes = true, YES = true, + no = false, No = false, NO = false, + on = true, On = true, ON = true, + off = false, Off = false, OFF = false, +} + + +--- Parse a boolean token to the equivalent value. +-- Treats capilalized, lower and upper-cased variants of true/false, +-- yes/no or on/off tokens as boolean `true` and `false` values. +-- @param value token +-- @treturn[1] bool if a valid boolean token was recognized +-- @treturn[2] nil otherwise, nil +-- @usage maybe_bool = implicit.bool(token) +local function bool(value) + return to_bool[value] +end + + +--- Parse a binary token, such as '0b1010\_0111\_0100\_1010\_1110'. +-- @tparam string value token +-- @treturn[1] int integer equivalent, if a valid token was recognized +-- @treturn[2] nil otherwise, nil +-- @usage maybe_int = implicit.binary(value) +local function binary(value) + local r + gsub(value, '^([+-]?)0b_*([01][01_]+)$', function(sign, rest) + r = 0 + gsub(rest, '_*(.)', function(digit) + r = r * 2 + int(digit) + end) + if sign == '-' then + r = r * -1 + end + end) + return r +end + + +--- Parse an octal token, such as '012345'. +-- @tparam string value token +-- @treturn[1] int integer equivalent, if a valid token was recognized +-- @treturn[2] nil otherwise, nil +-- @usage maybe_int = implicit.octal(value) +local function octal(value) + local r + gsub(value, '^([+-]?)0_*([0-7][0-7_]*)$', function(sign, rest) + r = 0 + gsub(rest, '_*(.)', function(digit) + r = r * 8 + int(digit) + end) + if sign == '-' then + r = r * -1 + end + end) + return r +end + + +--- Parse a decimal token, such as '0' or '12345'. +-- @tparam string value token +-- @treturn[1] int integer equivalent, if a valid token was recognized +-- @treturn[2] nil otherwise, nil +-- @usage maybe_int = implicit.decimal(value) +local function decimal(value) + local r + gsub(value, '^([+-]?)_*([0-9][0-9_]*)$', function(sign, rest) + rest = gsub(rest, '_', '') + if rest == '0' or #rest > 1 or sub(rest, 1, 1) ~= '0' then + r = int(rest) + if sign == '-' then + r = r * -1 + end + end + end) + return r +end + + +--- Parse a hexadecimal token, such as '0xdeadbeef'. +-- @tparam string value token +-- @treturn[1] int integer equivalent, if a valid token was recognized +-- @treturn[2] nil otherwise, nil +-- @usage maybe_int = implicit.hexadecimal(value) +local function hexadecimal(value) + local r + gsub(value, '^([+-]?)(0x_*[0-9a-fA-F][0-9a-fA-F_]*)$', function(sign, rest) + rest = gsub(rest, '_', '') + r = int(rest) + if sign == '-' then + r = r * -1 + end + end) + return r +end + + +--- Parse a sexagesimal token, such as '190:20:30'. +-- Useful for times and angles. +-- @tparam string value token +-- @treturn[1] int integer equivalent, if a valid token was recognized +-- @treturn[2] nil otherwise, nil +-- @usage maybe_int = implicit.sexagesimal(value) +local function sexagesimal(value) + local r + gsub(value, '^([+-]?)([0-9]+:[0-5]?[0-9][:0-9]*)$', function(sign, rest) + r = 0 + gsub(rest, '([0-9]+):?', function(digit) + r = r * 60 + int(digit) + end) + if sign == '-' then + r = r * -1 + end + end) + return r +end + + +local isnan = {['.nan']=true, ['.NaN']=true, ['.NAN']=true} + + +--- Parse a `nan` token. +-- @tparam string value token +-- @treturn[1] nan not-a-number, if a valid token was recognized +-- @treturn[2] nil otherwise, nil +-- @usage maybe_nan = implicit.nan(value) +local function nan(value) + if isnan[value] then + return 0/0 + end +end + + +local isinf = { + ['.inf'] = math.huge, ['.Inf'] = math.huge, ['.INF'] = math.huge, + ['+.inf'] = math.huge, ['+.Inf'] = math.huge, ['+.INF'] = math.huge, + ['-.inf'] = -math.huge, ['-.Inf'] = -math.huge, ['-.INF'] = -math.huge, +} + + +--- Parse a signed `inf` token. +-- @tparam string value token +-- @treturn[1] number plus/minus-infinity, if a valid token was recognized +-- @treturn[2] nil otherwise, nil +-- @usage maybe_inf = implicit.inf(value) +local function inf(value) + return isinf[value] +end + + +--- Parse a floating point number token, such as '1e-3' or '-0.12'. +-- @tparam string value token +-- @treturn[1] number float equivalent, if a valid token was recognized +-- @treturn[2] nil otherwise, nil +-- @usage maybe_float = implicit.float(value) +local function float(value) + local r = tonumber((gsub(value, '_', ''))) + if r and find(value, '[%.eE]') then + return r + end +end + + +--- Parse a sexagesimal float, such as '190:20:30.15'. +-- Useful for times and angles. +-- @tparam string value token +-- @treturn[1] number float equivalent, if a valid token was recognized +-- @treturn[2] nil otherwise, nil +-- @usage maybe_float = implicit.sexfloat(value) +local function sexfloat(value) + local r + gsub(value, '^([+-]?)([0-9]+:[0-5]?[0-9][:0-9]*)(%.[0-9]+)$', + function(sign, rest, float) + r = 0 + gsub(rest, '([0-9]+):?', function(digit) + r = r * 60 + int(digit) + end) + r = r + tonumber(float) + if sign == '-' then + r = r * -1 + end + end + ) + return r +end + + +--- @export +return { + binary = binary, + decimal = decimal, + float = float, + hexadecimal = hexadecimal, + inf = inf, + nan = nan, + null = null, + octal = octal, + sexagesimal = sexagesimal, + sexfloat = sexfloat, + bool = bool, +} |