------------------------------------------------------------------------------- -- Copyright (c) 2006-2013 Fabien Fleutot and others. -- -- All rights reserved. -- -- This program and the accompanying materials are made available -- under the terms of the Eclipse Public License v1.0 which -- accompanies this distribution, and is available at -- http://www.eclipse.org/legal/epl-v10.html -- -- This program and the accompanying materials are also made available -- under the terms of the MIT public license which accompanies this -- distribution, and is available at http://www.lua.org/license.html -- -- Contributors: -- Fabien Fleutot - API and implementation -- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- -- Summary: metalua parser, miscellaneous utility functions. -- ------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- -- Exported API: -- * [mlp.fget()] -- * [mlp.id()] -- * [mlp.opt_id()] -- * [mlp.id_list()] -- * [mlp.string()] -- * [mlp.opt_string()] -- * [mlp.id2string()] -- -------------------------------------------------------------------------------- local pp = require 'metalua.pprint' local gg = require 'metalua.grammar.generator' -- TODO: replace splice-aware versions with naive ones, move etensions in ./meta return function(M) local _M = gg.future(M) -------------------------------------------------------------------------------- -- Try to read an identifier (possibly as a splice), or return [false] if no -- id is found. -------------------------------------------------------------------------------- function M.opt_id (lx) local a = lx:peek(); if lx:is_keyword (a, "-{") then local v = M.meta.splice(lx) if v.tag ~= "Id" and v.tag ~= "Splice" then gg.parse_error(lx, "Bad id splice") end return v elseif a.tag == "Id" then return lx:next() else return false end end -------------------------------------------------------------------------------- -- Mandatory reading of an id: causes an error if it can't read one. -------------------------------------------------------------------------------- function M.id (lx) return M.opt_id (lx) or gg.parse_error(lx,"Identifier expected") end -------------------------------------------------------------------------------- -- Common helper function -------------------------------------------------------------------------------- M.id_list = gg.list { primary = _M.id, separators = "," } -------------------------------------------------------------------------------- -- Converts an identifier into a string. Hopefully one day it'll handle -- splices gracefully, but that proves quite tricky. -------------------------------------------------------------------------------- function M.id2string (id) --print("id2string:", disp.ast(id)) if id.tag == "Id" then id.tag = "String"; return id elseif id.tag == "Splice" then error ("id2string on splice not implemented") -- Evaluating id[1] will produce `Id{ xxx }, -- and we want it to produce `String{ xxx }. -- The following is the plain notation of: -- +{ `String{ `Index{ `Splice{ -{id[1]} }, `Number 1 } } } return { tag="String", { tag="Index", { tag="Splice", id[1] }, { tag="Number", 1 } } } else error ("Identifier expected: "..pp.tostring(id, 'nohash')) end end -------------------------------------------------------------------------------- -- Read a string, possibly spliced, or return an error if it can't -------------------------------------------------------------------------------- function M.string (lx) local a = lx:peek() if lx:is_keyword (a, "-{") then local v = M.meta.splice(lx) if v.tag ~= "String" and v.tag ~= "Splice" then gg.parse_error(lx,"Bad string splice") end return v elseif a.tag == "String" then return lx:next() else error "String expected" end end -------------------------------------------------------------------------------- -- Try to read a string, or return false if it can't. No splice allowed. -------------------------------------------------------------------------------- function M.opt_string (lx) return lx:peek().tag == "String" and lx:next() end -------------------------------------------------------------------------------- -- Chunk reader: block + Eof -------------------------------------------------------------------------------- function M.skip_initial_sharp_comment (lx) -- Dirty hack: I'm happily fondling lexer's private parts -- FIXME: redundant with lexer:newstream() lx :sync() local i = lx.src:match ("^#.-\n()", lx.i) if i then lx.i = i lx.column_offset = i lx.line = lx.line and lx.line + 1 or 1 end end local function chunk (lx) if lx:peek().tag == 'Eof' then return { } -- handle empty files else M.skip_initial_sharp_comment (lx) local chunk = M.block (lx) if lx:peek().tag ~= "Eof" then gg.parse_error(lx, "End-of-file expected") end return chunk end end -- chunk is wrapped in a sequence so that it has a "transformer" field. M.chunk = gg.sequence { chunk, builder = unpack } return M end