]> git.lizzy.rs Git - metalua.git/blob - src/compiler/mlp_misc.lua
mlp.chunk.transformers had disappeared because mlp.chunk wasn't a regular parser...
[metalua.git] / src / compiler / mlp_misc.lua
1 ----------------------------------------------------------------------
2 -- Metalua:  $Id: mlp_misc.lua,v 1.6 2006/11/15 09:07:50 fab13n Exp $
3 --
4 -- Summary: metalua parser, miscellaneous utility functions.
5 --
6 ----------------------------------------------------------------------
7 --
8 -- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
9 --
10 -- This software is released under the MIT Licence, see licence.txt
11 -- for details.
12 --
13 ----------------------------------------------------------------------
14 -- History:
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.
19 --
20 -- Revision 1.5  2006/11/10 02:11:17  fab13n
21 -- compiler faithfulness to 5.1 improved
22 -- gg.expr extended
23 -- mlp.expr refactored
24 --
25 -- Revision 1.4  2006/11/09 09:39:57  fab13n
26 -- some cleanup
27 --
28 -- Revision 1.3  2006/11/07 04:38:00  fab13n
29 -- first bootstrapping version.
30 --
31 -- Revision 1.2  2006/11/05 15:08:34  fab13n
32 -- updated code generation, to be compliant with 5.1
33 --
34 ----------------------------------------------------------------------
35
36 --------------------------------------------------------------------------------
37 --
38 -- Exported API:
39 -- * [mlp.fget()]
40 -- * [mlp.id()]
41 -- * [mlp.opt_id()]
42 -- * [mlp.id_list()]
43 -- * [mlp.gensym()]
44 -- * [mlp.string()]
45 -- * [mlp.opt_string()]
46 -- * [mlp.id2string()]
47 --
48 --------------------------------------------------------------------------------
49
50 --require "gg"
51 --require "mll"
52
53 module ("mlp", package.seeall)
54
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
59 -- set to [tag].
60 --
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 --------------------------------------------------------------------------------
65
66 function fget (n, tag) 
67    assert (type (n) == "number")
68    if tag then
69       assert (type (tag) == "string")
70       return function (x) 
71          assert (type (x[n]) == "table")       
72          return {tag=tag, unpack(x[n])} end 
73    else
74       return function (x) return x[n] end 
75    end
76 end
77
78
79 --------------------------------------------------------------------------------
80 -- Try to read an identifier (possibly as a splice), or return [false] if no
81 -- id is found.
82 --------------------------------------------------------------------------------
83 function opt_id (lx)
84    local a = lx:peek();
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")
89       end
90       return v
91    elseif a.tag == "Id" then return lx:next()
92    else return false end
93 end
94
95 --------------------------------------------------------------------------------
96 -- Mandatory reading of an id: causes an error if it can't read one.
97 --------------------------------------------------------------------------------
98 function id (lx)
99    return opt_id (lx) or gg.parse_error(lx,"Identifier expected")
100 end
101
102 --------------------------------------------------------------------------------
103 -- Common helper function
104 --------------------------------------------------------------------------------
105 id_list = gg.list { primary = mlp.id, separators = "," }
106
107 --------------------------------------------------------------------------------
108 -- Symbol generator: [gensym()] returns a guaranteed-to-be-unique identifier.
109 -- The main purpose is to avoid variable capture in macros.
110 --
111 -- If a string is passed as an argument, theis string will be part of the
112 -- id name (helpful for macro debugging)
113 --------------------------------------------------------------------------------
114 local gensymidx = 0
115
116 function gensym (arg)
117    gensymidx = gensymidx + 1
118    return { tag="Id", _G.string.format(".%i.%s", gensymidx, arg or "")}
119 end
120
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
139 end
140
141 --------------------------------------------------------------------------------
142 -- Read a string, possibly spliced, or return an error if it can't
143 --------------------------------------------------------------------------------
144 function string (lx)
145    local a = lx:peek()
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")
150       end
151       return v
152    elseif a.tag == "String" then return lx:next()
153    else error "String expected" end
154 end
155
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()
161 end
162    
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()
169    lx :sync()
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
172 end
173
174 local function _chunk (lx)
175    if lx:peek().tag == 'Eof' then return { } -- handle empty files
176    else 
177       skip_initial_sharp_comment (lx)
178       local chunk = block (lx)
179       if lx:peek().tag ~= "Eof" then error "End-of-file expected" end
180       return chunk
181    end
182 end
183
184 -- chunk is wrapped in a sequence so that it has a "transformer" field.
185 chunk = gg.sequence { _chunk, builder = unpack }