1 ----------------------------------------------------------------------
2 -- Metalua: $Id: mlp_misc.lua,v 1.6 2006/11/15 09:07:50 fab13n Exp $
4 -- Summary: metalua parser, miscellaneous utility functions.
6 ----------------------------------------------------------------------
8 -- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
10 -- This software is released under the MIT Licence, see licence.txt
13 ----------------------------------------------------------------------
15 -- $Log: mlp_misc.lua,v $
16 -- Revision 1.6 2006/11/15 09:07:50 fab13n
17 -- debugged meta operators.
18 -- Added command line options handling.
20 -- Revision 1.5 2006/11/10 02:11:17 fab13n
21 -- compiler faithfulness to 5.1 improved
23 -- mlp.expr refactored
25 -- Revision 1.4 2006/11/09 09:39:57 fab13n
28 -- Revision 1.3 2006/11/07 04:38:00 fab13n
29 -- first bootstrapping version.
31 -- Revision 1.2 2006/11/05 15:08:34 fab13n
32 -- updated code generation, to be compliant with 5.1
34 ----------------------------------------------------------------------
36 --------------------------------------------------------------------------------
45 -- * [mlp.opt_string()]
46 -- * [mlp.id2string()]
48 --------------------------------------------------------------------------------
53 module ("mlp", package.seeall)
55 --------------------------------------------------------------------------------
56 -- returns a function that takes the [n]th element of a table.
57 -- if [tag] is provided, then this element is expected to be a
58 -- table, and this table receives a "tag" field whose value is
61 -- The primary purpose of this is to generate builders for
62 -- grammar generators. It has little purpose in metalua, as lambda has
63 -- a lightweight syntax.
64 --------------------------------------------------------------------------------
66 function fget (n, tag)
67 assert (type (n) == "number")
69 assert (type (tag) == "string")
71 assert (type (x[n]) == "table")
72 return {tag=tag, unpack(x[n])} end
74 return function (x) return x[n] end
79 --------------------------------------------------------------------------------
80 -- Try to read an identifier (possibly as a splice), or return [false] if no
82 --------------------------------------------------------------------------------
85 if lx:is_keyword (a, "-{") then
86 local v = gg.sequence{ "-{", splice_content, "}" } (lx) [1]
87 if v.tag ~= "Id" and v.tag ~= "Splice" then
88 gg.parse_error(lx,"Bad id splice")
91 elseif a.tag == "Id" then return lx:next()
95 --------------------------------------------------------------------------------
96 -- Mandatory reading of an id: causes an error if it can't read one.
97 --------------------------------------------------------------------------------
99 return opt_id (lx) or gg.parse_error(lx,"Identifier expected")
102 --------------------------------------------------------------------------------
103 -- Common helper function
104 --------------------------------------------------------------------------------
105 id_list = gg.list { primary = mlp.id, separators = "," }
107 --------------------------------------------------------------------------------
108 -- Symbol generator: [gensym()] returns a guaranteed-to-be-unique identifier.
109 -- The main purpose is to avoid variable capture in macros.
111 -- If a string is passed as an argument, theis string will be part of the
112 -- id name (helpful for macro debugging)
113 --------------------------------------------------------------------------------
116 function gensym (arg)
117 gensymidx = gensymidx + 1
118 return { tag="Id", _G.string.format(".%i.%s", gensymidx, arg or "")}
121 --------------------------------------------------------------------------------
122 -- Converts an identifier into a string. Hopefully one day it'll handle
123 -- splices gracefully, but that proves quite tricky.
124 --------------------------------------------------------------------------------
125 function id2string (id)
126 --print("id2string:", disp.ast(id))
127 if id.tag == "Id" then id.tag = "String"; return id
128 elseif id.tag == "Splice" then
129 assert (in_a_quote, "can't do id2string on an outermost splice")
130 error ("id2string on splice not implemented")
131 -- Evaluating id[1] will produce `Id{ xxx },
132 -- and we want it to produce `String{ xxx }
133 -- Morally, this is what I want:
134 -- return `String{ `Index{ `Splice{ id[1] }, `Number 1 } }
135 -- That is, without sugar:
136 return {tag="String", {tag="Index", {tag="Splice", id[1] },
137 {tag="Number", 1 } } }
138 else error ("Identifier expected: "..table.tostring(id)) end
141 --------------------------------------------------------------------------------
142 -- Read a string, possibly spliced, or return an error if it can't
143 --------------------------------------------------------------------------------
146 if lx:is_keyword (a, "-{") then
147 local v = gg.sequence{ "-{", splice_content, "}" } (lx) [1]
148 if v.tag ~= "" and v.tag ~= "Splice" then
149 gg.parse_error(lx,"Bad string splice")
152 elseif a.tag == "String" then return lx:next()
153 else error "String expected" end
156 --------------------------------------------------------------------------------
157 -- Try to read a string, or return false if it can't. No splice allowed.
158 --------------------------------------------------------------------------------
159 function opt_string (lx)
160 return lx:peek().tag == "String" and lx:next()
163 --------------------------------------------------------------------------------
164 -- Chunk reader: block + Eof
165 --------------------------------------------------------------------------------
166 function skip_initial_sharp_comment (lx)
167 -- Dirty hack: I'm happily fondling lexer's private parts
168 -- FIXME: redundant with lexer:newstream()
170 local i = lx.src:match ("^#.-\n()", lx.i)
171 if i then lx.i, lx.column_offset, lx.line = i, i, lx.line+1 end
174 local function _chunk (lx)
175 if lx:peek().tag == 'Eof' then return { } -- handle empty files
177 skip_initial_sharp_comment (lx)
178 local chunk = block (lx)
179 if lx:peek().tag ~= "Eof" then error "End-of-file expected" end
184 -- chunk is wrapped in a sequence so that it has a "transformer" field.
185 chunk = gg.sequence { _chunk, builder = unpack }