home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1988 / 06 / deparse.c next >
Text File  |  1979-12-31  |  13KB  |  357 lines

  1.   1 /************************************************************************
  2.   2     deparse.c
  3.   3     
  4.   4     Decompiles an RPN expression into the original source code (or
  5.   5     a reasonable facsimile). Based on an algorithm by P.J. Brown in
  6.   6     'More on the Re-creation of Source Code from Reverse Polish'
  7.   7     Software - Practice and Experience, vol 7 pgs 545-551
  8.   8     
  9.   9     William May
  10.  10     303 Ridgefield Circle
  11.  11     Clinton, MA 01510
  12.  12     
  13.  13  *************************************************************************/
  14.  14 
  15.  15 #include <types.h>
  16.  16 #include <ctype.h>
  17.  17 
  18.  18 #define BUFSIZE     80
  19.  19 #define NIL         (char *)0
  20.  20 #define TRUE        1
  21.  21 #define FALSE       0
  22.  22 
  23.  23 enum {
  24.  24     NONARY_OP   = 1,
  25.  25     UNARY_OP    = 2,
  26.  26     BINARY_OP   = 4
  27.  27 } lex_types;
  28.  28 
  29.  29 /*-------------------------------------------------------------------------
  30.  30     This table describes the operators that the code re-creating
  31.  31     algorithm will handle.  The identifier is the internal representation
  32.  32     of an operator, the lexical type indicates the number of arguments to
  33.  33     the operator, and the prefixes and suffix supply transformations
  34.  34     of the operators.
  35.  35   -------------------------------------------------------------------------*/
  36.  36 typedef struct decomp_row {
  37.  37     char    ident;
  38.  38     short   lex_type;
  39.  39     char    *prefix_1;
  40.  40     char    *prefix_2;
  41.  41     char    *suffix;
  42.  42 } decomp_row;
  43.  43 
  44.  44 /*--------------------------------------------------------------------------
  45.  45     An example of the source recreation table setup.
  46.  46     Note that in real life the idents need not be printable
  47.  47     However, printable characters make the program much easier to test.
  48.  48   --------------------------------------------------------------------------*/
  49.  49 static decomp_row table[] ={
  50.  50     {'!', UNARY_OP,  "-",    NIL,   NIL},
  51.  51     {'+', BINARY_OP, NIL,    "+",   NIL},
  52.  52     {'-', BINARY_OP, NIL,    "-",   NIL},
  53.  53     {'/', BINARY_OP, NIL,    "/",   NIL},
  54.  54     {'*', BINARY_OP, NIL,    "*",   NIL},
  55.  55     {'^', BINARY_OP, NIL,    "^",   NIL},
  56.  56     {'(', UNARY_OP,  "(",    NIL,   ")"},
  57.  57     {'$', UNARY_OP,  "sum(", NIL,   ")"},
  58.  58     {'=', BINARY_OP, "let ", "=",   NIL},
  59.  59     {',', BINARY_OP, NIL,    ",",   NIL}
  60.  60 };
  61.  61 
  62.  62 #define TABLENUM (sizeof(table)/sizeof(decomp_row))
  63.  63 
  64.  64 #ifdef DEBUG
  65.  65 #include <stdio.h>
  66.  66 
  67.  67 /*--------------------------------------------------------------------
  68.  68     This is a simple test stub for the deparse function. It 
  69.  69     reads a line from stdin, displays the line and the decompiled
  70.  70     result on stdout. Leave DEBUG undefined when compiling deparse as
  71.  71     a library.
  72.  72   --------------------------------------------------------------------*/
  73.  73 main(argc, argv)
  74.  74 int     argc;
  75.  75 char    *argv[];
  76.  76 {
  77.  77     char    s[BUFSIZE]; /* input string     */
  78.  78     char    t[BUFSIZE]; /* output string    */
  79.  79     int     n;
  80.  80     void    deparse();
  81.  81     
  82.  82     printf("\n\nDecompiling test expressions:\n\n");
  83.  83 
  84.  84     /* get a line. Note that buffer s still has linefeed in it */
  85.  85     while (fgets(s, BUFSIZE, stdin)) {      
  86.  86         if (n = strlen(s)) {
  87.  87             s[n-1] = '\0';
  88.  88             printf("%s --> ", s);
  89.  89             deparse(s, t);
  90.  90             printf("%s\n", t);
  91.  91         }   
  92.  92     }
  93.  93 
  94.  94     exit(0);
  95.  95 }
  96.  96 #endif
  97.  97 
  98.  98 /*------------------------------------------------------------------------
  99.  99     deparse():  the only externally visible function, carries out deparsing 
  100. 100                 an RPN expression.
  101. 101  ------------------------------------------------------------------------*/
  102. 102 
  103. 103 void deparse(instr, outstr)
  104. 104 char *instr;
  105. 105 char *outstr;
  106. 106 {
  107. 107     char    *restart;   /* restart position after a forward scan            */
  108. 108     int     level;      /* number of operands passed during a forward scan  */
  109. 109     char    *p,         /* pointer to string to emit                        */
  110. 110             *prefix_1_for_elem(),
  111. 111             *prefix_2_for_elem(),
  112. 112             *suffix(),
  113. 113             *pop();
  114. 114     void    init_stack(),
  115. 115             push();
  116. 116     
  117. 117     init_stack();
  118. 118     
  119. 119     /* make sure outstr is terminated */
  120. 120     *outstr = '\0';
  121. 121     
  122. 122     /* scan the input string */
  123. 123     while (*instr) {
  124. 124     
  125. 125         /* look for the next operand */
  126. 126         while (elem_is_operator(*instr)) {
  127. 127             if ((p = suffix(*instr)) != NIL)
  128. 128                 strcat(outstr, p);
  129. 129 
  130. 130             advance_to_next_elem(&instr);
  131. 131 
  132. 132             if (!(*instr))
  133. 133                 return;             /* no more input, so quit */
  134. 134         }
  135. 135         
  136. 136         /* found an operand, so scan forward for prefixes to this operand. */
  137. 137         level   = 0;
  138. 138         restart = instr;
  139. 139         
  140. 140         while (level >= 0) {
  141. 141             /* get the next token */
  142. 142             advance_to_next_elem(&instr);
  143. 143             
  144. 144             /* have we reached the end of the input? */
  145. 145             if (!*instr)
  146. 146                 break;
  147. 147             
  148. 148             /*
  149. 149                 is the next token an operator or operand? If an operand then
  150. 150                 update count of intervening operands (the level). If an operator
  151. 151                 figure out if it results in a prefix to our operand (based on the level)
  152. 152             */
  153. 153             if (elem_is_operand(*instr))
  154. 154                 level++;
  155. 155             else {
  156. 156                 if (elem_is_binary_op(*instr))
  157. 157                     --level;
  158. 158                 
  159. 159                 if (level == 0)
  160. 160                     if ((p = prefix_1_for_elem(*instr)) != NIL)
  161. 161                         push(p);
  162. 162                 
  163. 163                 if (level == -1)
  164. 164                     if ((p = prefix_2_for_elem(*instr)) != NIL)
  165. 165                         push(p);
  166. 166                 
  167. 167                 /* ... can be extended for more complex operators */
  168. 168             }
  169. 169         }
  170. 170         
  171. 171         /*
  172. 172             unwind results of the forward scan by popping them off
  173. 173             the stack.
  174. 174         */
  175. 175         while (p = pop())
  176. 176             strcat(outstr, p);
  177. 177         
  178. 178         /*
  179. 179             forward scan complete, reset our scan point for another round
  180. 180         */
  181. 181         instr = restart;
  182. 182         
  183. 183         /*
  184. 184             now emit the operand itself. Note that operands will often
  185. 185             need conversions and formatting in real life, i.e. integer ->
  186. 186             string or floating point -> string conversions,
  187. 187             currency/date/time formatting, etc. Here we just append the
  188. 188             raw operand to the output string.
  189. 189         */
  190. 190         strncat(outstr, instr, 1);
  191. 191         
  192. 192         advance_to_next_elem(&instr);
  193. 193     }
  194. 194 }
  195. 195 
  196. 196 /************************************************************************
  197. 197     table handling utilities
  198. 198  ************************************************************************/
  199. 199  
  200. 200 /*------------------------------------------------------------------------
  201. 201     elem_is_operator(): figure out if code is an operator
  202. 202                         if so, then return true
  203. 203  ------------------------------------------------------------------------*/
  204. 204 static elem_is_operator(code)
  205. 205 char code;
  206. 206 {
  207. 207     decomp_row  *row, *find_op();
  208. 208     
  209. 209     if (row = find_op(code))
  210. 210         return TRUE;
  211. 211     else
  212. 212         return FALSE;
  213. 213 }
  214. 214 
  215. 215 /*------------------------------------------------------------------------
  216. 216     elem_is_operand():  figure out if code is an operand
  217. 217                         if so, then return true
  218. 218     
  219. 219     In this example all operands are alphabetic or numeric characters.
  220. 220  ------------------------------------------------------------------------*/
  221. 221 static elem_is_operand(code)
  222. 222 char code;
  223. 223 {
  224. 224     if (isalnum(code))
  225. 225         return TRUE;
  226. 226     else
  227. 227         return FALSE;
  228. 228 }
  229. 229 
  230. 230 /*------------------------------------------------------------------------
  231. 231     elem_is_binary_op():    figure out if code is a binary operator
  232. 232                         if so, then return true
  233. 233  ------------------------------------------------------------------------*/
  234. 234 static elem_is_binary_op(code)
  235. 235 char code;
  236. 236 {
  237. 237     decomp_row  *r, *find_op();
  238. 238     
  239. 239     if (r = find_op(code))
  240. 240         return (r->lex_type & BINARY_OP);
  241. 241     else
  242. 242         return false;
  243. 243 }
  244. 244 
  245. 245 /*------------------------------------------------------------------------
  246. 246     prefix_1_for_elem():    get prefix 1 for the operator
  247. 247  ------------------------------------------------------------------------*/
  248. 248 static char *prefix_1_for_elem(op)
  249. 249 char op;
  250. 250 {
  251. 251     decomp_row  *r, *find_op();
  252. 252     
  253. 253     if (r = find_op(op))
  254. 254         return (r->prefix_1);
  255. 255     else
  256. 256         return NIL;
  257. 257 }
  258. 258 
  259. 259 /*------------------------------------------------------------------------
  260. 260     prefix_2_for_elem():    get prefix 2 for the operator
  261. 261  ------------------------------------------------------------------------*/
  262. 262 static char *prefix_2_for_elem(op)
  263. 263 char op;
  264. 264 {
  265. 265     decomp_row  *r, *find_op();
  266. 266     
  267. 267     if (r = find_op(op))
  268. 268         return (r->prefix_2);
  269. 269     else
  270. 270         return NIL;
  271. 271 }
  272. 272 
  273. 273 /*------------------------------------------------------------------------
  274. 274     suffix():   get the suffix of given code
  275. 275  ------------------------------------------------------------------------*/
  276. 276 static char *suffix(code)
  277. 277 char code;
  278. 278 {
  279. 279     decomp_row *row, *find_op();
  280. 280     
  281. 281     if (row = find_op(code))
  282. 282         return row->suffix;
  283. 283     else
  284. 284         return NIL;
  285. 285 }
  286. 286 
  287. 287 /*------------------------------------------------------------------------
  288. 288     find_op():  finds the operator "op" in the decompiler table
  289. 289                 if found, it returns a pointer to the row
  290. 290                 if not, it returns NIL
  291. 291  ------------------------------------------------------------------------*/
  292. 292 decomp_row *find_op(op)
  293. 293 char op;
  294. 294 {
  295. 295     int         i;
  296. 296     decomp_row  *row;
  297. 297     
  298. 298     row = table;
  299. 299     for (i = 0; i < TABLENUM; i++, row++)
  300. 300         if (op == row->ident)
  301. 301             return row;
  302. 302 
  303. 303     return NIL; /* no hit */
  304. 304 }
  305. 305 
  306. 306 /*------------------------------------------------------------------------
  307. 307     advance_to_next_elem(): advance to next element
  308. 308                             bump scan pointer as we move
  309. 309                              
  310. 310     the function assumes that all tokens are a singe byte.
  311. 311  ------------------------------------------------------------------------*/
  312. 312 static advance_to_next_elem(p)
  313. 313 char    **p;
  314. 314 {
  315. 315     (*p)++;
  316. 316 }
  317. 317 
  318. 318 /*=======================================================================
  319. 319     a simple stack implementation
  320. 320   =======================================================================*/
  321. 321 
  322. 322 /* stack size in bytes */
  323. 323 #define STACKSIZE   40
  324. 324 
  325. 325 /*** a couple of globals for the stack ***/
  326. 326 static  char    **sp, **top;
  327. 327 static  char    stack[STACKSIZE];
  328. 328 
  329. 329 /*------------------------------------------------------------------------
  330. 330     init_stack(): prepare the stack for use
  331. 331  ------------------------------------------------------------------------*/
  332. 332 static void init_stack()
  333. 333 {
  334. 334     top  = stack + STACKSIZE - 1;
  335. 335     sp   = top + 1;
  336. 336 }
  337. 337 
  338. 338 /*------------------------------------------------------------------------
  339. 339     pop(): pop a pointer sized value from the stack
  340. 340  ------------------------------------------------------------------------*/
  341. 341 static char *pop()
  342. 342 {
  343. 343     if (sp <= top)
  344. 344         return(*sp++);
  345. 345     else
  346. 346         return NIL;
  347. 347 }
  348. 348 
  349. 349 /*------------------------------------------------------------------------
  350. 350     push(): push a pointer sized value onto the stack
  351. 351  ------------------------------------------------------------------------*/
  352. 352 static void push(p)
  353. 353 char    *p;
  354. 354 {
  355. 355     *(--sp) = p;
  356. 356 }
  357. 357