lbp_completion.cpp
Go to the documentation of this file.
1/*
2********************************************************************
3********************************************************************
4********************************************************************
5
6 Lua completer, derived from Lua advanced readline
7 support patch, with original copyright al follows:
8
9 Copyright (C) 2004-2006 Mike Pall.
10 Same license as Lua. See lua.h.
11
12 The below version is adapted to go with SWIG generated
13 __index functions (as opposed to the perhaps more common
14 __index tables). The interface was changed to a more
15 C++ like style, to make a better match with applications
16 like DESTool. The current implementation is preliminary
17 and requires some clean-up.
18
19 This file is included by lbp_function.cpp and the functions
20 are called by the LuaState methods for completion.
21
22 Thomas Moor, 2011.
23
24********************************************************************
25********************************************************************
26********************************************************************
27*/
28
29
30// Lua keywords
31static const char *const faudes_rl_keywords[] = {
32 "and", "break", "do", "else", "elseif", "end", "false",
33 "for", "function", "if", "in", "local", "nil", "not", "or",
34 "repeat", "return", "then", "true", "until", "while", NULL
35};
36
37// Test identifier
38static bool faudes_rl_valididentifier(const std::string& str) {
39 std::string::const_iterator sit=str.begin();
40 if(sit==str.end()) return false;
41 if(!(isalpha(*sit) || *sit=='_')) return false;
42 for(++sit; sit!=str.end(); ++sit)
43 if(!(isalnum(*sit) || *sit=='_')) return true;
44 return true;
45}
46
47// Append compound prefix + string + suffix to list and maintain common prefix.
48static void faudes_rl_dmadd(std::list< std::string > & mlist, const std::string& prefix, const std::string& str, const std::string& suffix) {
49 // iterate suffxes
50 if(suffix.size()>1) {
51 for(unsigned int i=0;i<suffix.size();i++)
52 faudes_rl_dmadd(mlist,prefix,str,std::string(1,suffix.at(i)));
53 return;
54 }
55 // build compound
56 std::string comp;
57 comp=prefix + str + suffix;
58 // initialize: matching prefix equals first entry
59 if(mlist.size()==0) {
60 mlist.push_back(comp);
61 return;
62 }
63 // initialize: matching prefix equals first entry
64 if(mlist.size()==1) {
65 mlist.push_back(*mlist.begin());
66 mlist.push_back(comp);
67 }
68 // figure maximal matching prefix
69 std::string& match=*mlist.begin();
70 std::string::iterator mit, cit;
71 mit=match.begin();
72 cit=comp.begin();
73 while(mit!=match.end() && cit!=comp.end()) {
74 if(*mit!=*cit) break;
75 mit++; cit++;
76 }
77 // adjust matching prefix in list
78 if(mit!=match.end())
79 match.erase(mit,match.end());
80 // append to list
81 mlist.push_back(comp);
82}
83
84// Get __index field of metatable of object on top of stack
85// -- return 1 for ok and leave __index field on stack
86// -- return 0 for error, nothing on stack
87static int faudes_rl_getmetaindex(lua_State *L) {
88 // object has no metatable, error
89 if(!lua_getmetatable(L, -1))
90 { lua_pop(L, 1); return 0; } // stack:
91 lua_replace(L, -2); // stack: metatable
92 // get the __ index field
93 lua_pushstring(L, "__index");
94 lua_rawget(L, -2); // stack: metatable,__index
95 lua_replace(L, -2); // stack: __index
96 // invald result, error
97 if(lua_isnil(L,-1) || lua_rawequal(L, -1, -2))
98 { lua_pop(L, 1); return 0; } // stack:
99 return 1; // stack: __index
100}
101
102// Get .fn fields of metatable of object on top of stack.
103// -- the SWIG __index function retrieves methods from the .fn attribute
104// -- allways puts a table on the stack, empty on error
105static void faudes_rl_getmetafn(lua_State *L) {
106 // object has no metatable: return empty table
107 if(!lua_getmetatable(L, -1))
108 { lua_pop(L, 1); lua_newtable(L); return; }
109 lua_replace(L, -2); // stack: metatable
110 // get .fn
111 lua_pushstring(L, ".fn");
112 lua_rawget(L, -2); // stack: metatable,.fn
113 lua_replace(L, -2); // stack: .fn
114 if(!lua_istable(L, -1))
115 { lua_pop(L, 1); lua_newtable(L); return; }
116}
117
118// Get .get field of metatable of object on top of stack.
119// -- the SWIG __index function retrieves member access from the .get attribute
120// -- allways puts a table on the stack, empty on error
121static void faudes_rl_getmetaget(lua_State *L) {
122 // object has no metatable: just pop the object
123 if(!lua_getmetatable(L, -1))
124 { lua_pop(L, 1); lua_newtable(L); return; }
125 lua_replace(L, -2); // stack: metatable
126 // get .get
127 lua_pushstring(L, ".get");
128 lua_rawget(L, -2); // stack: metatable,.get
129 lua_replace(L, -2); // stack: .get
130 if(!lua_istable(L, -1))
131 { lua_pop(L, 1); lua_newtable(L); return; }
132}
133
134// Get __index/.fn/.get field of metatable of object on top of stack.
135// -- allways puts a table on stack, empty of error
136// -- returns 1 to indicate ussage of SWIG .get table
137static int faudes_rl_getmeta(lua_State *L, bool has_colon) {
138 // try __index first
139 lua_pushvalue(L,-1); // stack: obj,obj
141 // is it a table // stack: obj,_index
142 if(lua_istable(L,-1)) {
143 // is it non-empty? // stack: obj,_index
144 lua_pushnil(L);
145 if(lua_next(L, -2)) {
146 lua_pop(L, 2); // stack: obj,_index
147 lua_replace(L, -2); // stack: _index
148 return 0;
149 }
150 }
151 lua_pop(L,1); // stack: obj1
152 } // stack: obj
153 // colon indicates method, so we use .fn
154 if(has_colon) {
155 faudes_rl_getmetafn(L); // stack: .fn
156 return 0;
157 }
158 // absence of colon indicates member, so we use .get
159 faudes_rl_getmetaget(L); // stack: .get
160 return 1;
161
162}
163
164
165// Get field from object on top of stack (without calling metamethods)
166static int faudes_rl_getfield(lua_State *L, const char *s, size_t n) {
167 int i = 20; // max loop count to limit infinite metatable loops. */
168 do {
169 if(lua_istable(L, -1)) { // if obj is a table, try to get the requested field
170 lua_pushlstring(L, s, n);
171 lua_rawget(L, -2); // stack: obj,field
172 if(!lua_isnil(L, -1)) // ok, got the field
173 {lua_replace(L, -2); return 1;}
174 lua_pop(L, 1); // stack: obj
175 }
176 if(!faudes_rl_getmetaindex(L)) break; // stack: _index
177 } while (--i > 0);
178 lua_pop(L, 1);
179 return 0;
180} /* 1: obj -- val, 0: obj -- */
181
182
183// actual completer
184static std::list< std::string > faudes_rl_complete(lua_State *L, const std::string& word) {
185
186 // C++ style interface
187 std::list< std::string > mlist;
188 const char* text= word.c_str();
189 int start=0;
190 int end=word.size();
191
192 // other locals
193 const char *s;
194 size_t i, n, dot, loop;
195 int colon;
196 int savetop;
197 bool used_swig_get=false;
198
199 // bail out on text that cannot complete to an identifier
200 if (!(text[0] == '\0' || isalpha(text[0]) || text[0] == '_')) return mlist;
201
202 // record top of stack
203 savetop = lua_gettop(L);
204
205 // figure the right most complete field in "word" and
206 // -- leave the table of globals on the stack, if there is no complete field
207 lua_pushglobaltable(L);
208 for(n = (size_t)(end-start), i = dot = 0, colon=-1; i < n; i++) {
209 if(text[i] == '.' || text[i] == ':') {
210 if(!faudes_rl_getfield(L, text+dot, i-dot))
211 { lua_settop(L, savetop); return mlist; } // error
212 dot = i+1; // Points to first char after dot/colon.
213 if(text[i] == ':') colon=dot; // record whether we have seen a colon
214 }
215 }
216
217 // Append all matches against keywords if there is no dot/colon.
218 if(dot == 0)
219 for (i = 0; (s = faudes_rl_keywords[i]) != NULL; i++)
220 if(!strncmp(s, text, n)) faudes_rl_dmadd(mlist, "", std::string(s), " ");
221
222 // Append all valid matches from all tables/metatables.
223 loop = 0; // Avoid infinite metatable loops.
224 do {
225 if(lua_istable(L, -1))
226 if((loop == 0 || 1 /* !lua_rawequal(L, -1, LUA_GLOBALSINDEX) */) ) { // TODO: this used to be Lua 5.1.3 .. what todo with Lua 5.4.8?
227 for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
228 if (lua_type(L, -2) == LUA_TSTRING) {
229 s = lua_tostring(L, -2);
230 // Only match names starting with '_' if explicitly requested.
231 if (!strncmp(s, text+dot, n-dot) && faudes_rl_valididentifier(s) &&
232 (*s != '_' || text[dot] == '_')) {
233 std::string suf = " "; // Default suffix is a space.
234 switch (lua_type(L, -1)) {
235 case LUA_TTABLE: suf = ":."; break;
236 case LUA_TFUNCTION: if(!used_swig_get) suf = "("; break;
237 case LUA_TUSERDATA:
238 if (lua_getmetatable(L, -1)) { lua_pop(L, 1); suf = ":."; }
239 break;
240 }
241 faudes_rl_dmadd(mlist, std::string(text,dot), std::string(s), suf);
242 }
243 }
244 }
245 }
246 used_swig_get = faudes_rl_getmeta(L,colon>0);
247 } while (++loop < 20);
248
249 // Fix stack
250 lua_settop(L, savetop);
251
252 // done
253 return mlist;
254}
255
static int faudes_rl_getmetaindex(lua_State *L)
static std::list< std::string > faudes_rl_complete(lua_State *L, const std::string &word)
static int faudes_rl_getfield(lua_State *L, const char *s, size_t n)
static void faudes_rl_getmetafn(lua_State *L)
static int faudes_rl_getmeta(lua_State *L, bool has_colon)
static const char *const faudes_rl_keywords[]
static void faudes_rl_dmadd(std::list< std::string > &mlist, const std::string &prefix, const std::string &str, const std::string &suffix)
static bool faudes_rl_valididentifier(const std::string &str)
static void faudes_rl_getmetaget(lua_State *L)

libFAUDES 2.33k --- 2025.09.16 --- c++ api documentaion by doxygen