Re: [Wikitech-l] Help with Scribunto/Lua modules

2013-06-06 Thread Ori Livneh
On Wed, Jun 5, 2013 at 8:22 PM, MZMcBride z...@mzmcbride.com wrote:

 The debug console seems finicky. Its line numbers never seem to make
 sense. For example, I input this into a fresh console:


The console isn't designed to handle blocks of code. You should compose
blocks of code into the module itself and use the debug console for simple
poking and prodding. You don't have to save the page: unsaved modifications
to the page are reflected to the console.

For example, go edit a module page, and paste this snippet into the code
editor:

--
local p = {}

function p.greet(name)
return Hello,  .. name
end

return p
--

Then in the debug console, type:

=p.greet(Lua)
Hello, Lua

Lua error in console input at line 6: attempt to index local 'frame' (a
 nil value).


That's because you wrote a function that accesses a frame parameter, but
you don't provide a value for it. You'll get a similar error if you omit
the name from the invocation of the 'greet' function we wrote earlier:

=p.greet()
Lua error at line 4: attempt to concatenate local 'name' (a nil value).

Note that the error cites the correct line number: it's indeed the 'return'
statement in p.greet that is causing the error. In some programming
languages, like Python, arguments without defaults are required, and it is
an error to omit them from the invocation. In Lua and some other languages
the value of any parameter that is absent from the invocation is set to
nil, so p.greet() is equivalent to p.greet(nil).

I have no idea what line 6 refers to. Earlier when I was testing it was
 saying line 14. The line numbers never seem to match up to what I think
 I'm testing.


I think your confusion resulted from trying to work with blocks of code in
the debug console.

frame can be a bit tricky to grasp and trickier still to work with, but
once you get it, it's not so bad.

The way Scribunto handles {{#invoke}} is this: first, it makes a table.
This is the 'frame' table. Then it makes an 'args' table inside that table.
Then it puts the arguments into the 'args' table. Then it calls your
function, passing the frame table as the sole parameter.

When the parser invokes a Lua function from an {{#invoke}}, it *always*
passes exactly one parameter: frame. It doesn't matter what arguments (if
any) were present in {{#invoke}} or what your function thought it was
getting. It's getting a frame and it's going to like it.

As an example, here's Lua code that is (very roughly) equivalent to how
Scribunto would handle {{#invoke:Sort|asc|3|1|2}}

--
frame = {}
frame.args = { 3, 1, 2 }
sort = require('Module:Sort')
sort.asc(frame)
--

This is a bit annoying, because at first glance invoke looks like it's
just an alternate syntax for a Lua function invocation, so you expect
{{#invoke:Sort|asc|3|1|2}} to be equivalent to sort.asc(3, 1, 2), but
that's not the case.

So, how do we work with this? I can recommend two approaches. For the
purpose of demonstrating them, let's imagine we want to write a Scribunto
math module that has a function, 'sum', that takes two numbers and adds
them. One approach is work with a frame parameter from the get-go.

--
local p = {}

function p.sum(frame)
return frame.args[1] + frame.args[2]
end

return p
--

This works if we save it and then try to invoke it from somewhere:
{{#invoke:Math|sum|4|5}} should evaluate to 9. But how do we test it in the
debug console? We'll pretend we're the parser and make our own frame table.
Paste the code block above into a module, and type this into the debug
console, hitting return after each line:

frame = {}
frame.args = { 4, 5 }
=p.sum(frame)

With any luck, you'll get '9'. You can make this shorter by creating both
tables at once:

frame = { args = { 4, 5 } }
=p.sum(frame)

Or even:

=p.sum({ args = {4, 5} })

Because calling a function with a table as the parameter is very common,
Lua has a special syntax for it, which lets you omit the parentheses:

=p.sum{ args = { 4, 5 } }

All of the variants above are equivalent.

The second approach is to not deal with frames until you have to. This
workflow works especially well when you're developing on your own computer,
using the standard Lua interpreter. Just write a normal Lua function and
verify that it works.

--
local p = {}
function p.sum(a, b)
return a + b
end
return p
--

You can test that it works with =p.sum(4, 5). When you're ready to save
your module, write a helper function that takes a frame table and uses it
to call your actual function.

--
local p = {}

function p.sum_real(a, b)
return a + b
end

function p.sum(frame)
return p.sum_real(frame.args[1], frame.args[2])
end

return p
--

This lets you test your function easily from the console, using 'sum_real':

=p.sum_real(4,5)
9

But 'sum' works from wikitext as expected: {{#invoke:Math|sum|4|5}} will
evaluate to 9.

There's a note on the edit screen about using mw.log(), but with various
 inputs, I still can't seem to get anything working properly with the debug
 console.



Re: [Wikitech-l] Help with Scribunto/Lua modules

2013-06-06 Thread Brad Jorsch
On Wed, Jun 5, 2013 at 11:22 PM, MZMcBride z...@mzmcbride.com wrote:

 The debug console seems finicky. Its line numbers never seem to make
 sense.

The debug console probably could use some work. The way it works now,
it takes your past and current input, plugs all that into some
boilerplate to make a function, and then executes that function. The
line numbers reported for Lua error in console input therefore
depend on this boilerplate and on the lines you've previously entered
in the session.

It's also possible to break that boilerplate function, as you've seen below.

 For example, I input this into a fresh console:

 ---
 function p.asc(frame)
 return frame.args[1]
 end
 ---

 Then I try ...

 = print(p.asc())
 Lua error in console input at line 6: attempt to index local 'frame' (a
 nil value).

 I have no idea what line 6 refers to. Earlier when I was testing it was
 saying line 14.

It's referring to which line in that boilerplate function the return
frame.args[1] ended up at. Yes, that's not extremely helpful.

 If I paste this into the console:

 ---
[SNIP]
 return p
 ---

 I get Lua error in console input at line 18: 'end' expected (to close
 'function' at line 1) near 'return'.

This is an example of breaking the boilerplate function. In Lua, it's
an error to have unreachable code after a return statement. When you
put return p into the debug console like that, the boilerplate
function winds up looking something like this:

function ( p )
local print = mw.log
-- Previous lines go here
mw.clearLogBuffer()
return p -- This is the line you entered
return nil, mw.getLogBuffer()  -- This is boilerplate
end

Again, the console should probably be made more robust at some point
to avoid this sort of thing.

 There's a note on the edit screen about using mw.log(), but with various
 inputs, I still can't seem to get anything working properly with the debug
 console. For example:

 ---
 function p.asc(frame)
 t = {}
 t['bar'] = 'baz'
 mw.log(t)
 return frame.args[1]
 end
 ---

 This prints nothing at all when pasted into the debug console.

That's because you're defining a function but not calling it (yet).

 If I try = print(t) or print(t) I get nil.

Since 't' is a local variable inside the function, it's not defined
outside of it.

 Is there a var_dump equivalent for Lua that can be used?

Not yet.


-- 
Brad Jorsch
Software Engineer
Wikimedia Foundation

___
Wikitech-l mailing list
Wikitech-l@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/wikitech-l

Re: [Wikitech-l] Help with Scribunto/Lua modules

2013-06-06 Thread Brad Jorsch
On Thu, Jun 6, 2013 at 6:19 AM, Ori Livneh o...@wikimedia.org wrote:
 This is a bit annoying, because at first glance invoke looks like it's
 just an alternate syntax for a Lua function invocation, so you expect
 {{#invoke:Sort|asc|3|1|2}} to be equivalent to sort.asc(3, 1, 2), but
 that's not the case.

The problem there is what would {{#invoke:Module|func|foo=1|bar=2}} be
equivalent to? Maybe Module.func{ foo = 1, bar = 2}, but then your
earlier example would have to be sort.asc{ 3, 1, 2 }. And either case
your function would be getting a single table with the args as
parameters, which is only one step removed from the frame object you
actually do get.

Or, you might say, that it would somehow match up the foo and bar
with the parameter names in the function declaration. That would be
difficult to accomplish, would complicate modules that need a large
number of named parameters, and wouldn't allow for wikitext parameter
names that don't match the regex /^[a-zA-Z_][a-zA-Z0-9_]*$/.

Also, having the frame object allows for lazy parsing of the arg
values. As you probably already know, if a template argument is not
actually used inside the template, it never even gets expanded. The
same happens in Scribunto: the wikitext for an argument to #invoke is
not actually expanded until it is accessed in frame.args. We couldn't
do this if parameters to #invoke were passed directly as parameters to
the function.

 We'll pretend we're the parser and make our own frame table.
 Paste the code block above into a module, and type this into the debug
 console, hitting return after each line:

 frame = {}
 frame.args = { 4, 5 }

Better would be to use the method specifically intended for this purpose:

frame = mw.getCurrentFrame():newChild{ args = { 4, 5 } }

That creates a real frame object, so you can test functions that need
the various frame methods (e.g. frame:expandTemplate) too.

You can even test things using frame:getParent().args with this method:

parentFrame = mw.getCurrentFrame():newChild{ args = { 'parent',
'template', 'args' } }
frame = parentFrame:newChild{ args = { '#invoke', 'args' } }

Note there is a limit on the number of child frames that can be
created, to avoid encouraging people to use this relatively-expensive
method in actual modules instead of passing arguments directly to
other Lua functions. If you hit this limit, just use the debug
console's Clear button.


-- 
Brad Jorsch
Software Engineer
Wikimedia Foundation

___
Wikitech-l mailing list
Wikitech-l@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/wikitech-l

Re: [Wikitech-l] Help with Scribunto/Lua modules

2013-06-06 Thread Tyler Romeo
On Thu, Jun 6, 2013 at 10:23 AM, Brad Jorsch bjor...@wikimedia.org wrote:

 The debug console probably could use some work. The way it works now,
 it takes your past and current input, plugs all that into some
 boilerplate to make a function, and then executes that function. The
 line numbers reported for Lua error in console input therefore
 depend on this boilerplate and on the lines you've previously entered
 in the session.

 It's also possible to break that boilerplate function, as you've seen
 below.


I agree. I know it's a stretch, but the ideal situation would be to have
some sort of actual debugger. Like a full-blown console or something where
you could test code and it would tell you where it failed and why, etc. I
know some of this is supported already.

*-- *
*Tyler Romeo*
Stevens Institute of Technology, Class of 2016
Major in Computer Science
www.whizkidztech.com | tylerro...@gmail.com
___
Wikitech-l mailing list
Wikitech-l@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/wikitech-l

Re: [Wikitech-l] Help with Scribunto/Lua modules

2013-06-06 Thread Robert Rohde
On Wed, Jun 5, 2013 at 8:22 PM, MZMcBride z...@mzmcbride.com wrote:
snip
 Is there a better way to write/debug Lua modules? Any help writing these
 modules (or simply getting a working sort module on Meta-Wiki) would be
 appreciated.

I edited your code to make the Sort Module do what I think you
intended it to do.

-Robert

___
Wikitech-l mailing list
Wikitech-l@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/wikitech-l

[Wikitech-l] Help with Scribunto/Lua modules

2013-06-05 Thread MZMcBride
Hi.

I tried to write a Scribunto module in Lua today on Meta-Wiki called
Sort: https://meta.wikimedia.org/wiki/Module:Sort. I struggled a lot
to get an environment set up where I could easily test blocks of code, so
I'm looking for any help/guidance I can get.

The debug console seems finicky. Its line numbers never seem to make
sense. For example, I input this into a fresh console:

---
function p.asc(frame)
return frame.args[1]
end
---

Then I try ...

= print(p.asc())
Lua error in console input at line 6: attempt to index local 'frame' (a
nil value).

I have no idea what line 6 refers to. Earlier when I was testing it was
saying line 14. The line numbers never seem to match up to what I think
I'm testing.

If I paste this into the console:

---
local p = {}

function p.asc(frame)
return frame.args[1]
end

function p.desc(frame)
return p.args[1]
end

return p
---

I get Lua error in console input at line 18: 'end' expected (to close
'function' at line 1) near 'return'.

There's a note on the edit screen about using mw.log(), but with various
inputs, I still can't seem to get anything working properly with the debug
console. For example:

---
function p.asc(frame)
t = {}
t['bar'] = 'baz'
mw.log(t)
return frame.args[1]
end
---

This prints nothing at all when pasted into the debug console. If I try =
print(t) or print(t) I get nil.

Related to this, even when I'm able to get something to print or mw.log(),
I usually end up printing the strings table or nil, rather than the
contents of a table. Is there a var_dump equivalent for Lua that can be
used? Lua seems to be very focused on tables for storage, but dumping
what's currently in a table at particular points in the code feels
excruciating. I feel like I must be missing something. This is related to
https://bugzilla.wikimedia.org/show_bug.cgi?id=48173.

Is there a better way to write/debug Lua modules? Any help writing these
modules (or simply getting a working sort module on Meta-Wiki) would be
appreciated.

MZMcBride



___
Wikitech-l mailing list
Wikitech-l@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/wikitech-l