Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature request] Implementing '...' for Variable Declaration within Scope using Anonymous Functions #144

Open
Propagram opened this issue Jul 31, 2023 · 5 comments

Comments

@Propagram
Copy link

Propagram commented Jul 31, 2023

Hi,
In the Lua language, the ellipsis ... can only be used in the last function argument.

local function a(...)
    print(...)
end

It is not possible to declare it in the scope (e.g., as local ... = 1, 2).

local ok, ... = true, true -- syntax error! ('<name>' expected near '...')

My suggestion is to implement the use of ... to declare it in the scope, using anonymous functions for that purpose. During parsing, if there is ... in the variable declaration, Yuescript can wrap that scope inside a function and call it with the values. This way, ... will be defined within the same scope.

list = {1, 2, 3, 4, 5}
fn = (ok) ->
  ok, table.unpack list
ok, ... = fn true
print ok, ...
ok, ...

compiles to:

local list = {
  1, 
  2,
  3,
  4,
  5
}
local fn
fn = function(ok)
  return ok, table.unpack(list)
end
return (function(ok, ...)
    print(ok, ...) -- "ok" and "..." is available here without errors!
    return ok, ...
end)(fn(true))
Notes

In my scripts, I use varargs (...) returns frequently. Usually, they contain 'nil' (holes), so I cannot obtain the correct length if I wrap them in tables '{...}'. It is necessary to return the length and then use 'table.unpack'

local function fn_many_args()
    return 10, nil, 20, nil, 30
end

local args = {fn_many_args()}
print(#args) --> 1

local _ = (function(...)
    print(select("#", ...)) --> 5
    print(...) --> 10, nil, 20, nil, 30
end)(fn_many_args())


-- I can pass the function to select:
print(select("#", fn_many_args())) --> 5
-- But, How can I get the items? I will need call the function twice!
print(select(1, fn_many_args())) --> 10, nil, 20, nil, 30
-- First to get length, and second to catch the items

Regards

pigpigyyy added a commit that referenced this issue Aug 7, 2023
@pigpigyyy
Copy link
Owner

Tried to add this feature, if you use vararg mutiple times in a same scope. You will get you're code block actually split in multiple anonymous functions.

list = {1, 2, 3, 4, 5}
fn = (ok) ->
  ok, table.unpack list
ok, ... = fn true
print ok, ...

fn_many_args = ->
  10, nil, 20, nil, 30

... = fn_many_args!
print select "#", ...
print ...

now compiles to:

local list = {
  1,
  2,
  3,
  4,
  5
}
local fn
fn = function(ok)
  return ok, table.unpack(list)
end
return (function(_arg_0, ...)
  local ok = _arg_0
  print(ok, ...)
  local fn_many_args
  fn_many_args = function()
    return 10, nil, 20, nil, 30
  end
  return (function(...)
    print(select("#", ...))
    return print(...)
  end)(fn_many_args())
end)(fn(true))

@Propagram
Copy link
Author

Thank you! Congratulations on the effort put into this project.

I found some inline expressions that don't compile or report errors:

... = 1, 2 if a

compiles to

return (function(...) end)(1, 2) -- 1

In this case, the inline expression should be placed within the anonymous function, or alternatively, report an error in inline expressions with varargs.

@pigpigyyy
Copy link
Owner

Fixed the behavior of varargs with inline expression to be the same as assignment statement with 33260af.

a = 1 if true
... = 1, 2 if a
print ...

compiles to:

local a
if true then
  a = 1
end
return (function(...)
  return print(...)
end)((function()
  if a then
    return 1, 2
  end
end)())

@Propagram
Copy link
Author

Propagram commented Aug 14, 2023

After running some tests, I realized that it's better if the compiler indicates an error when using all line decorators in varargs assignments.

It will print 2 when if true (ok), but it will print nil when if false (expected output was 1).

a = 1
a, ... = 2, ... if true
print a --> 2 (ok)
a = 1
a, ... = 2, ... if false
print a --> nil (1?)

I believe that if a line decorator is used in a vararg assignment, an error should be displayed because vararg assignment modifies the scope, and an explicit scoped if assignment makes more sense.

@pigpigyyy
Copy link
Owner

pigpigyyy commented Aug 17, 2023

I agree with you for raising errors when using vararg assignment with line decorator.
There is no way to tell the difference between:

a = 1
cond = false
value = 2
a, ... = value, ... if cond
print a --> expecting 1
a = 1
cond = true
value = nil
a, ... = value, ... if cond
print a --> expecting nil

So just raising error should be the best idea.
Got this fixed until commit b0c4613.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants