lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


As well an interesting extension to Lua would be to allow declaration of "parsing attributes" at the beginning of blocks (i.e. after keywords "do", "repeat", "then", "else", or the closing parenthesis ")" of a function definition, or the main block containing any list of statements): these attributes have local scope within the block. They could also be used just after the opening parenthesis "(" of a subexpression.

It would be simply take the form of a string constant (which will be parsed as if it was the content of a table constructor). That string value can only be used by the parser: when parsing it it will build a table containing normal key/values pairs. Each key can be a simple identifier or expressions between square braces. Values can be expressions. Expressions for keys or values are evaluated in the parser's current lexical scope: it could then call functions or reference variables defined in the script itself. This evaluation occurs only once, directly when parsing the script, not when running blocks (which may not run at all or run multiple times, depending on conditions determined by the script or the environment where it is used).

Note that a string constant cannot currently start any block (or any statement in the block), so
- it has to be itself between pairs of parentheses to be used as an object to call its string methods, for example: ('A'):len()
- a string constant directly at start of a statement or start of a subexpression between parentheses cannot be followed by any operator.
- that string constant for attributes can use the short form between quotes '...' or "...", or the long form between double squares, i.e. [[...]], or [=[...]=], etc.
- attributes at start of subexpressions between pairs of parentheses MUST be followed by an actual _expression_ (possibly nil)
- attributes are just mapping 

E.g.:

[[strict]] -- example of an attribute at start of the script
local x = ('strict' 1 / 3 * 3) -- strict IEEE rounding of 1/3 (rounded down) before multiplying by 3, the result is a bit lower than 1.0
function fun(a, b, c) 'strict' -- example of attribute allowing mathematical evaluation with strict IEEE rounding
   -- other statements here...
end
function fun(a, b, c) [[strict]] -- using long string syntax
  -- other statements here...
end
do 'strict'
    if true then 'strict' -- other statements here...
    elseif 1 then "strict" -- other statements here...
    elseif 2 then [==[strict]==] -- other statements here...
    elseif 3 then [[strict]]
    else [[strict]]
        repeat [[strict]]
        until true
    while true do [[strict]]
        break
    end
end

These attributes can alter the way the block (or the subexpression) is evaluated. For example they could allow changing the role of commas "," used inside the block in lists of expressions (for function calls, or for table constructors or for assignment statements). They can change rounding modes for number expressions, assign a function to call to evaluate unary or binary operators.
Basically attributes have a name and an optional value which is the name of a function accessible in the current lexical scope. E.g.

local function add8(a,b)
    return (math.floor(a)+math.floor(b))%256
end
local function strictcomma(arg1, ...)
    return arg1 -- return only the 1st argument (or nil if there's none), discard other arguments 
end
local function func(n)
    if type(n)=='number' and n<2 then return n, n+1 end -- return two numbers
    -- otherwise return an empty list; interpreted as nil in strict assignments
end
function computebytes() [[-- attributes in a "long string"
        __add = add8, -- example of remapping for the "+" binary operator
        __comma = strictcomma, -- change variable-length lists of values into a single value or nil
    ]]
    local x = 256 + 1 -- will do: local x=add8(256, 1)
    local y, z = func(1), func(2) -- ensures that y=(func(1)) i.e. y=1, and that z=(func(2)) i.e. z=nil
    t = {func(1), func(2)} -- will do t = {[1] = 1, [2] = nil}
end


Le lun. 6 sept. 2021 à 19:53, Paul K <paul@zerobrane.com> a écrit :
> The problem is that `('A':byte(2)` actually does not return `nil` in its list of upvalues, it just returns an empty list:

You can wrap it into parentheses to force `nil` if that's what's
needed in this case:

print(type( ( ('A'):byte(2) ) ))

This prints "nil" for me in all Lua versions 5.1+

Paul.

On Mon, Sep 6, 2021 at 9:19 AM Philippe Verdy <verdyp@gmail.com> wrote:
>
> The Lua builtin function "type(value)" unexpectedly signals an error (sometimes but not always) when the argument value is nil.
>
> This occurs when the value nil is the result of a function call, and not a simple variable containing nil or the constant nil. This is incoherent !
>
> That error is wrong. `type(nil)` or `type()` or `type(f())` (where `f()` returns nil or nothing), or `type(nil, 'dummy')` or `type(...)` (called from inside a function called without parameters or with a nil parameter in first position of the varaible argument list) should return 'nil' in all cases.
>
> E.g. run in a Lua console:
>
> val = ('A'):byte(1); typ = type(val); print typ ..'=' ..  tostring(val); print type(('A'):byte(1))
> --> number=65
> --> number
> val = ('A'):byte(2); typ = type(val); print typ .. '=' .. tostring(val); print type(('A'):byte(2))
> --> nil=nil
> --> Error: bad argument #1 to 'type' (value expected).
>
> The problem is that `('A':byte(2)` actually does not return `nil` in its list of upvalues, it just returns an empty list:
> * when assigning an empty list to a variable, that variables get `nil` (with the correct type).
> * when passing the empty list returned by a function to `type()`, this function gets no parameter and incorrectly signals an "error".
>
> Yes the builtin `type(value)` function "requires" at least one argument, but that argument in first position CAN be `nil`. So it may not be passed at all and the first argument should be initialized with the `nil` value, and the builtin `type(value)` function SHOULD return the string `"nil"`, without any error (which I think is a very legacy behavior of very old versions of Lua before major changes in how lists of values were passed and returned through function calls).
>
> And the builtin `string.byte(index)` function SHOULD also be fixed to return a true `nil` for a given index past the end of string, and not an empty list (or this should not make any difference for calling type()).
>
>