Extending Identifier Characters

lua-users home
wiki

After some discussion of how nice it might be if '?' and '!' were legitimate characters in Lua identifiers, I realized that it's possible to do simply by changing the locale, since Lua is locale-aware. It's only necessary to create a locale whose LC_CTYPE identifies '?' and '!' as alpha.

In general, there is a problem with distributing Lua programs which use a locale other than the C locale, because it makes the files locale-dependent. However, with a bit of hackery, it's possible to replace the various Lua functions which read files with functions which are locale-aware.

The following example only causes require to be locale aware, but that's the most difficult case; replace loadfile is quite a bit easier. It assumes that files start with a standard Unix "shebang" line which looks something like:


#!/bin/env LC_CTYPE=pt_BR lua

The loader defined below checks for anything that looks like a LC_CTYPE definition in the shebang line, and sets the locale accordingly while parsing the file. This has not been tested very much, but it seemed useful enough to record.

local pkg = require"package"

local function findfile(pname, pathtype)

  local path = pkg[pathtype]

  local fname = pname:gsub("%.", "/")

  local errs = {} 

  for seg in path:gmatch"[^;]+" do

    local fname = seg:gsub("%?", fname)

    local file, err = io.open(fname)

    if file then

      return file, fname

    else

      errs[#errs+1] = ("\n\tno file '%s'"):format(fname)

    end

  end

  return nil, table.concat(errs)

end

  

local yield = coroutine.yield

local function Reader(file)

  return coroutine.wrap(function()

    local data = file:read(1)

    if data ~= '#' then

      yield(data)

      yield(file:read"*a")

    else

      file:seek("set")

      local lines = file:lines() 

      local shebang = lines()

      local ctype = shebang:match"LC_CTYPE=(%S+)"

      if ctype then os.setlocale(ctype, "ctype") end

      yield"\n"

      for l in lines do yield(l.."\n") end

    end 

  end)

end

  

local function myloader(pname)

  local file, fname = findfile(pname, "path")

  if file then

    local locale = os.setlocale(nil, "ctype")

    local chunk, err = load(Reader(file), "@"..fname)

    os.setlocale(locale, "ctype")

    file:close()

    if chunk then

      return chunk

    else

      error(("error loading '%s' from '%s':\n\t%s"):format(pname, fname, err), 0)

    end

  else return fname end

end

    

pkg.loaders[2] = myloader


RecentChanges · preferences
edit · history
Last edited July 13, 2007 5:10 am GMT (diff)