lua-users home
lua-l archive

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


Friday, June 13, 2003, 9:39:43 PM, Eli wrote:

E> This could very easily be done as a pre-processor written in Lua...
You are right, though it has to handle string literals, comments and
may have other problems.

Here is a pre-alpha pre-processor that converts lua-scripts to
python-like syntax (just removes extra 'end' keywords for now) and
back. It is just a concept test and works only for very simple scripts
(at least it works for its own source).

-------------------------------------------------------------------
-- gets indentation level
-- does not allow mixed tabs and spaces in the same source file
local ws = nil
function get_indent(l)
   local _, len, prefix = string.find(l, "^(%s*)")
   if not ws and len > 0 then
      ws = string.sub(l, 1, 1)
   end
   assert(not ws or prefix == string.rep(ws, len))
   return len
end

local indent_tbl = {}
function get_prev_indent()
   if table.getn(indent_tbl) > 0 then
      return indent_tbl[table.getn(indent_tbl)]
   else
      return 0
   end
end

-- returns the change in indentation level
function get_indent_change(l)
   local indent = get_indent(l)
   local prev_indent = get_prev_indent(l)
   if indent > prev_indent then
      table.insert(indent_tbl, indent)
      return 1
   elseif indent == prev_indent then
      return 0;
   else
      local res = 0
      while get_prev_indent() > indent do
         res = res - 1
         table.remove(indent_tbl)
      end
      return res
   end
end

function has_end_of_block_keyword(l)
   local _, _, keyword = string.find(l, "^%s*(%w+)")
   return keyword == "end" or keyword == "else" or keyword == "elseif"
end

function has_end_keyword(l)
   local _, _, keyword = string.find(l, "^%s*(%w+)")
   return keyword == "end"
end

-- removes the first keyword in the string (returns nil if there is nothing
-- left in this string)
function remove_first_keyword(l)
   local res = string.gsub(l, "^(%s*)(%w+)", "%1")
   if string.find(res, "%S") then
      return res
   else
      return nil
   end
end

if string.find(arg[1], "%.lua$") then
-- process lua sources to produce python-like syntax
   for l in io.lines(arg[1]) do
      local indent_change = get_indent_change(l)
      if indent_change < 0 then
         assert(has_end_of_block_keyword(l))
         if has_end_keyword(l) then
            assert(indent_change == -1)
            l = remove_first_keyword(l)
         end
      end
      if l then
         print(l)
      end
   end
else
-- restore lua source from preprocessed file
   for l in io.lines(arg[1]) do
      local indent_change = get_indent_change(l)
      if has_end_of_block_keyword(l) then
         indent_change = indent_change + 1
      end
      while indent_change < 0 do
         print("end")
         indent_change = indent_change + 1
      end
      print(l)
   end
   local indent_change = get_indent_change("")
   while indent_change < 0 do
      print("end")
      indent_change = indent_change + 1
   end
end
-------------------------------------------------------------------

E> I personally like the Python syntax .. Python tends to be very legible. But I
E> really don't like the way they mix tabs and spaces. It seems to me that the
E> parser doesn't know until it's read a few lines of the file wether to use
E> spaces or tabs or both.
IMHO the source must use either tabs or spaces, the mixture of tabs
and spaces must be detected as a syntax error.

E> In Lua, it also leads to somewhat unattractive things like:

E> for i in obj:iterator():
E>         do_stuff(i)

E> Two colons on the same line? That mean completely different things? Ugh. Maybe
E> you could make "do" the beginning of a block.
This is not so easy question. Just replacing 'then' and 'do' with ':'
adds ambiguety (as you have pointed). In addition, probably this will
require some changes in the parser as we replace two different tokens
with colon.
For now, I want to try to make 'end' keyword optional and see what
happens. Maybe this idea is not good at all.