Skip to content

Commit 964124f

Browse files
authored
Merge pull request #920 from os-fpga/pln_added_plnShell_tcl_mode
pln: added plnShell Tcl mode
2 parents f6c15f6 + 3b0d0ef commit 964124f

File tree

9 files changed

+479
-16
lines changed

9 files changed

+479
-16
lines changed

planning/src/RS/plnShell.cpp

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
#include "RS/plnShell.h"
2+
#include "RS/rsDeal.h"
3+
#include "RS/rsEnv.h"
4+
#include "file_io/pln_Fio.h"
5+
6+
#include "globals.h"
7+
#include "read_options.h"
8+
9+
#include "RS/rsCheck.h"
10+
#include "RS/rsVPR.h"
11+
#include "RS/sta_file_writer.h"
12+
13+
#include <unistd.h>
14+
15+
#ifdef PLN_ENABLE_TCL
16+
#include <readline/history.h>
17+
#include <readline/readline.h>
18+
#endif
19+
20+
namespace pln {
21+
22+
using std::cout;
23+
using std::endl;
24+
using std::string;
25+
26+
static constexpr size_t CMD_BUF_CAP = 1048574; // ~ 1 MiB
27+
28+
bool deal_shell(const rsOpts& opts) {
29+
assert(opts.argv_);
30+
assert(opts.argv_[0]);
31+
assert(opts.argc_ > 0);
32+
33+
uint16_t tr = ltrace();
34+
const rsEnv& env = rsEnv::inst();
35+
const string& a0 = env.abs_arg0_;
36+
assert(!a0.empty());
37+
38+
bool ok = false;
39+
flush_out(true);
40+
if (tr >= 4) {
41+
lprintf(" abs_arg0: %s\n", a0.c_str());
42+
flush_out(true);
43+
}
44+
45+
#ifdef PLN_ENABLE_TCL
46+
Tcl_FindExecutable(a0.c_str());
47+
48+
Tcl_Interp* ip = Tcl_CreateInterp();
49+
assert(ip);
50+
int init_status = Tcl_Init(ip);
51+
if (init_status != TCL_OK) {
52+
flush_out(true);
53+
err_puts();
54+
lprintf2("[Error] Tcl_Init failed\n");
55+
err_puts();
56+
flush_out(true);
57+
return false;
58+
}
59+
60+
Shell sh(ip, opts);
61+
62+
ok = sh.loop();
63+
if (!ok) {
64+
lprintf2("[Error] sh.loop() failed\n");
65+
}
66+
67+
Tcl_Finalize();
68+
#endif
69+
70+
flush_out(true);
71+
return ok;
72+
}
73+
74+
#ifdef PLN_ENABLE_TCL
75+
static int help_proc(void* clientData, Tcl_Interp* ip, int argc, CStr argv[]) {
76+
lprintf("help_proc: need help\n");
77+
78+
return 0;
79+
}
80+
81+
static int reportApp_proc(void* clientData, Tcl_Interp* ip, int argc, CStr argv[]) {
82+
const rsEnv& env = rsEnv::inst();
83+
lprintf(" ==== reportApp ====\n");
84+
lprintf(" PLANNER ver. %s\n", env.shortVerCS());
85+
lprintf(" ====\n");
86+
87+
return 0;
88+
}
89+
#endif
90+
91+
Shell* Shell::instance_ = nullptr;
92+
93+
// constructor adds commands to interp_
94+
Shell::Shell(Tcl_Interp* ip, const rsOpts& opts) noexcept : opts_(opts), interp_(ip) {
95+
#ifdef PLN_ENABLE_TCL
96+
assert(interp_);
97+
assert(!instance_);
98+
instance_ = this;
99+
100+
addCmd("help", help_proc, nullptr, nullptr);
101+
addCmd("report_app", reportApp_proc, nullptr, nullptr);
102+
#endif
103+
}
104+
105+
Shell::~Shell() {
106+
#ifdef PLN_ENABLE_TCL
107+
// if (interp_)
108+
// Tcl_DeleteInterp(interp_);
109+
#endif
110+
instance_ = nullptr;
111+
}
112+
113+
int Shell::evalFile(CStr fn, string& result) {
114+
int code = 0;
115+
116+
#ifdef PLN_ENABLE_TCL
117+
assert(fn and fn[0]);
118+
assert(interp_);
119+
result.clear();
120+
if (!fn or !fn[0]) {
121+
result = "(evalFile: bad filename)";
122+
return TCL_ERROR;
123+
}
124+
125+
code = Tcl_EvalFile(interp_, fn);
126+
127+
if (code >= TCL_ERROR)
128+
result = tcStackTrace(code);
129+
else
130+
result = Tcl_GetStringResult(interp_);
131+
#endif
132+
133+
return code;
134+
}
135+
136+
int Shell::evalCmd(CStr cmd, string& result) {
137+
int code = 0;
138+
139+
#ifdef PLN_ENABLE_TCL
140+
assert(cmd and cmd[0]);
141+
assert(interp_);
142+
result.clear();
143+
144+
code = Tcl_Eval(interp_, cmd);
145+
146+
if (code >= TCL_ERROR)
147+
result = tcStackTrace(code);
148+
else
149+
result = Tcl_GetStringResult(interp_);
150+
#endif
151+
152+
return code;
153+
}
154+
155+
int Shell::evalCmd(CStr cmd) {
156+
int code = 0;
157+
#ifdef PLN_ENABLE_TCL
158+
assert(cmd and cmd[0]);
159+
assert(interp_);
160+
code = Tcl_Eval(interp_, cmd);
161+
#endif
162+
return code;
163+
}
164+
165+
void Shell::addCmd(CStr cmdName, Tcl_CmdProc proc,
166+
ClientData clientData, Tcl_CmdDeleteProc* delProc) {
167+
#ifdef PLN_ENABLE_TCL
168+
assert(cmdName and cmdName[0]);
169+
assert(interp_);
170+
Tcl_CreateCommand(interp_, cmdName, proc, clientData, delProc);
171+
#endif
172+
}
173+
174+
string Shell::tcStackTrace(int code) const noexcept {
175+
string output;
176+
177+
#ifdef PLN_ENABLE_TCL
178+
assert(interp_);
179+
Tcl_Obj* options = Tcl_GetReturnOptions(interp_, code);
180+
Tcl_Obj* key = Tcl_NewStringObj("-errorinfo", -1);
181+
Tcl_Obj* stackTrace = nullptr;
182+
183+
Tcl_IncrRefCount(key);
184+
Tcl_DictObjGet(nullptr, options, key, &stackTrace);
185+
Tcl_DecrRefCount(key);
186+
187+
output = Tcl_GetString(stackTrace);
188+
Tcl_DecrRefCount(options);
189+
#endif
190+
191+
return output;
192+
}
193+
194+
bool Shell::loop() {
195+
#ifdef PLN_ENABLE_TCL
196+
assert(interp_);
197+
198+
if (opts_.input_) {
199+
// evaluate the script passed to 'planning'
200+
string output;
201+
int code = evalFile(opts_.input_, output);
202+
if (code == TCL_OK) {
203+
lout() << output << endl;
204+
} else {
205+
flush_out(true);
206+
lprintf2("[Error] (tcl) could not evaluate command line input: %s\n",
207+
opts_.input_);
208+
flush_out(true);
209+
return false;
210+
}
211+
}
212+
213+
// start interactive UI (readline loop)
214+
Terminal t(interp_);
215+
t.rlLoop();
216+
#endif
217+
218+
return true;
219+
}
220+
221+
Shell::Terminal::Terminal(Tcl_Interp* ip) noexcept
222+
: interp_(ip), cmdObj_(nullptr),
223+
isTerm_(false), isEOF_(false),
224+
contPrompt_(false) {
225+
226+
partLine_ = (char*)::calloc(CMD_BUF_CAP + 2, 1);
227+
assert(partLine_);
228+
229+
#ifdef PLN_ENABLE_TCL
230+
isTerm_ = ::isatty(0);
231+
if (isTerm_) Tcl_SetVar(interp_, "::tcl_interactive", "1", TCL_GLOBAL_ONLY);
232+
233+
resetCmdObj();
234+
#endif
235+
}
236+
237+
Shell::Terminal::~Terminal() {
238+
p_free(partLine_);
239+
}
240+
241+
bool Shell::Terminal::evalCo(CStr lbuf) {
242+
if (!lbuf) return false;
243+
if (!lbuf[0]) return false;
244+
245+
int ecode = 0;
246+
247+
#ifdef PLN_ENABLE_TCL
248+
char cbuf[CMD_BUF_CAP + 2];
249+
cbuf[0] = 0;
250+
cbuf[1] = 0;
251+
cbuf[CMD_BUF_CAP - 1] = 0;
252+
cbuf[CMD_BUF_CAP] = 0;
253+
cbuf[CMD_BUF_CAP + 1] = 0;
254+
255+
if (partLine_[0]) {
256+
// continue incomplete line
257+
::strncpy(cbuf, partLine_, CMD_BUF_CAP);
258+
str::chomp(cbuf);
259+
::strcat(cbuf, " ");
260+
::strcat(cbuf, lbuf);
261+
partLine_[0] = 0;
262+
} else {
263+
::strcpy(cbuf, lbuf);
264+
}
265+
266+
ecode = execCmdObj(cbuf);
267+
#endif
268+
269+
return ecode == TCL_OK;
270+
}
271+
272+
int Shell::Terminal::execCmdObj(CStr cbuf) {
273+
assert(partLine_);
274+
int code = 0;
275+
276+
#ifdef PLN_ENABLE_TCL
277+
Tcl_SetStringObj(cmdObj_, cbuf, ::strlen(cbuf));
278+
279+
if (not Tcl_CommandComplete(cbuf)) {
280+
contPrompt_ = true;
281+
::strcpy(partLine_, cbuf);
282+
::strcat(partLine_, "\n");
283+
return TCL_OK;
284+
}
285+
contPrompt_ = false;
286+
287+
code = Tcl_RecordAndEvalObj(interp_, cmdObj_, TCL_EVAL_GLOBAL);
288+
resetCmdObj();
289+
290+
Tcl_Obj* objResult = Tcl_GetObjResult(interp_);
291+
Tcl_IncrRefCount(objResult);
292+
int len = 0;
293+
CStr csResult = Tcl_GetStringFromObj(objResult, &len);
294+
295+
if (code != TCL_OK) {
296+
if (csResult and len > 0) {
297+
lprintf2("[Error] (Tcl) %s\n", csResult);
298+
}
299+
}
300+
else if (isTerm_) {
301+
if (csResult and len > 0 and Tcl_GetStdChannel(TCL_STDOUT)) {
302+
lprintf("%s\n", csResult);
303+
}
304+
}
305+
Tcl_DecrRefCount(objResult);
306+
Tcl_ResetResult(interp_);
307+
#endif
308+
309+
return code;
310+
}
311+
312+
void Shell::Terminal::resetCmdObj() noexcept {
313+
#ifdef PLN_ENABLE_TCL
314+
if (cmdObj_) Tcl_DecrRefCount(cmdObj_);
315+
cmdObj_ = Tcl_NewObj();
316+
assert(cmdObj_);
317+
Tcl_IncrRefCount(cmdObj_);
318+
#endif
319+
}
320+
321+
void Shell::Terminal::rlLoop() {
322+
#ifdef PLN_ENABLE_TCL
323+
const Shell& sh = Shell::inst();
324+
325+
char lbuf[CMD_BUF_CAP + 2];
326+
lbuf[0] = 0;
327+
lbuf[1] = 0;
328+
lbuf[CMD_BUF_CAP - 1] = 0;
329+
lbuf[CMD_BUF_CAP] = 0;
330+
lbuf[CMD_BUF_CAP + 1] = 0;
331+
332+
while (!sh.inExit()) {
333+
if (not Tcl_GetStdChannel(TCL_STDIN)) break;
334+
CStr line = ::readline(contPrompt_ ? ">> " : "pln> ");
335+
if (not line) break;
336+
if (not line[0]) continue;
337+
::strncpy(lbuf, line, CMD_BUF_CAP);
338+
str::chomp(lbuf);
339+
evalCo(lbuf);
340+
}
341+
#endif
342+
}
343+
344+
}
345+

0 commit comments

Comments
 (0)