aboutsummaryrefslogtreecommitdiff
path: root/lib/lyaml/implicit.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lyaml/implicit.lua')
-rw-r--r--lib/lyaml/implicit.lua283
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,
+}