Blockly: Lua generator test failure: Loops 3

Created on 30 Jun 2017  路  13Comments  路  Source: google/blockly

Error message: [string "function unittest_report()..."]:68: '=' expected near 'continue'

generators tests help wanted bug

Most helpful comment

@rachel-fenichel I will try to have a look at the generator and rework it if possible.

All 13 comments

This is not a problem of Blockly generator. The goto statement is added in Lua 5.2.0. See here. And the REPL interpreter's Lua version is 5.1 so it cannot run the test program. I installed Lua 5.2.4 on my Mac and run the test program and it seems that it works.
```
~ tll$ lua a.lua
........

Number of tests run: 8

OK
~ tll$ lua -v
Lua 5.2.4 Copyright (C) 1994-2015 Lua.org, PUC-Rio
~ tll$ ```

Maybe we can change the only interpreter recommended to another one with version 5.2.4. Can anyone recommend me one so that I can do it?

I'm not familiar with Lua. Offhand, a few questions come up for changing the interpreter:

  • Is there a version of Lua that is more commonly used? That is, do enough people use Lua 5.2.0 that we should aim to generate code for that version?
  • When did this break?

Still investigating which Lua version is most used. The Lua website shows 5.3 for download.
It's correct that 5.2 broke this.
And alternate and up to date interpreter is: http://rextester.com/l/lua_online_compiler

Currently there are a lot of projects using Lua libraries such as NodeMCU, which uses Lua 5.1 series, and do not support these functions. I think we can re-write the generator to render it able to generate code that Lua 5.1 can use while keeping the current one and use a constant somewhere to specify which one to use.

Is it possible to just write code that works for Lua 5.1 and also works for newer versions of Lua? We much prefer keeping one version of a generator around with the minimal number of options. The more configuration flags we have the harder it gets to test.

@TonyLianLong if you understand how to rework the generator to write 5.1-compatible code, go for it and send us a pull request.

@rachel-fenichel I will try to have a look at the generator and rework it if possible.

@rachel-fenichel I found a solution online, that is to use a repeat ... until true and break in it to make the effect of jumping directly to the statement after true statement. But if we have while statement in the repeat ... until true, that is not going to work and will interrupt the while loop.
And now without an ideal solution in Lua I cannot rewrite the generator.
I have changed the test code to this and it can pass all the test in 5.1 but the is a potential problem that I mentioned above.

function unittest_report()
  -- Create test report.
  local report = {}
  local summary = {}
  local fails = 0
  for _, v in pairs(unittestResults) do
    if v["success"] then
      table.insert(summary, ".")
    else
      table.insert(summary, "F")
      fails = fails + 1
      table.insert(report, "FAIL: " .. v["title"])
      table.insert(report, v["log"])
    end
  end
  table.insert(report, 1, table.concat(summary))
  table.insert(report, "")
  table.insert(report, "Number of tests run: " .. #unittestResults)
  table.insert(report, "")
  if fails > 0 then
    table.insert(report, "FAILED (failures=" .. fails .. ")")
  else
    table.insert(report, "OK")
  end
  return table.concat(report, "\n")
end

function assertEquals(actual, expected, message)
  -- Asserts that a value equals another value.
  assert(unittestResults ~= nil, "Orphaned assert equals: " ..  message)
  if type(actual) == "table" and type(expected) == "table" then
    local lists_match = #actual == #expected
    if lists_match then
      for i, v1 in ipairs(actual) do
        local v2 = expected[i]
        if type(v1) == "number" and type(v2) == "number" then
          if math.abs(v1 - v2) > 1e-9 then
            lists_match = false
          end
        elseif v1 ~= v2 then
          lists_match = false
        end
      end
    end
    if lists_match then
      table.insert(unittestResults, {success=true, log="OK", title=message})
      return
    else
      -- produce the non-matching strings for a human-readable error
      expected = "{" .. table.concat(expected, ", ") .. "}"
      actual = "{" .. table.concat(actual, ", ") .. "}"
    end
  end
  if actual == expected or (type(actual) == "number" and type(expected) == "number" and math.abs(actual - expected) < 1e-9) then
    table.insert(unittestResults, {success=true, log="OK", title=message})
  else
    table.insert(unittestResults, {success=false, log=string.format("Expected: %s\nActual: %s", tostring(expected), tostring(actual)), title=message})
  end
end

-- Describe this function...
function test_continue()
  log = ''
  count = 0
  while count ~= 8 do
    repeat
    count = count + 1
    if count == 5 then
      break
    end
    log = log .. count
    until true
  end
  assertEquals(log, '1234678', 'while continue')
  log = ''
  count = 0
  while not (count == 8) do
    repeat
    count = count + 1
    if count == 5 then
      break
    end
    log = log .. count
    until true
  end
  assertEquals(log, '1234678', 'until continue')
  log = ''
  for x = 1, 8, 1 do
    repeat
    if x == 5 then
      break
    end
    log = log .. x
    until true
  end
  assertEquals(log, '1234678', 'count continue')
  log = ''
  for _, x in ipairs({'a', 'b', 'c', 'd'}) do
    repeat
    if x == 'c' then
      break
    end
    log = log .. x
    until true
  end
  assertEquals(log, 'abd', 'for continue')
end


-- Describe this function...
function test_break()
  count = 1
  while count ~= 10 do
    if count == 5 then
      break
    end
    count = count + 1
  end
  assertEquals(count, 5, 'while break')
  count = 1
  while not (count == 10) do
    if count == 5 then
      break
    end
    count = count + 1
  end
  assertEquals(count, 5, 'until break')
  log = ''
  for x = 1, 8, 1 do
    if x == 5 then
      break
    end
    log = log .. x
  end
  assertEquals(log, '1234', 'count break')
  log = ''
  for _, x in ipairs({'a', 'b', 'c', 'd'}) do
    if x == 'c' then
      break
    end
    log = log .. x
  end
  assertEquals(log, 'ab', 'for break')
end



unittestResults = {}
test_break()
test_continue()
report = unittest_report()
unittestResults = nil
print(report)

image
Do you have an idea in implementing continue in Lua 5.1?

@marisaleung thoughts?

As discussed offline, it looks like Blockly's Lua generator has always supported the goto statement in Lua 5.2.0. So projects that use Blockly's Lua generator have been using Lua 5.2.0. And it should be fine to just change the interpreter to a more up-to-date one.

@marisaleung So could I change the interpreter to this one and submit a pull request?

@TonyLianLong Yes that would be awesome.

Looks like you've sorted this out, but I can confirm that the generator was written for Lua >=5.2. It only adds the continue label if a continue statement is there for two reasons:

  • to keep the generated code clean
  • so that basic loops work in older versions of Lua
Was this page helpful?
0 / 5 - 0 ratings