Mercurial > ~dholland > hg > ag > index.cgi
view examples/mpp/mas.syn @ 16:f9e4689b837d
Some minor updates for 15 years later.
author | David A. Holland |
---|---|
date | Tue, 31 May 2022 01:45:26 -0400 |
parents | 13d2b8934445 |
children |
line wrap: on
line source
{ /* * AnaGram, a System for Syntax Directed Programming * C Macro preprocessor * Macro argument substitution module * * Copyright 1993-2000 Parsifal Software. All Rights Reserved. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "mpp.h" } // Configuration Section [ //far tables // uncomment for 16 bit environment ~allow macros line numbers //escape backslashes // uncomment if using MSVC++ pointer input pointer type = token * input values ~error frame error trace ~lines and columns ~backtrack ~test range default input type = token ~declare pcb parser file name = "#.cpp" sticky { space } enum { eof =0, SPACE =' ', ANDAND ='A', // "&&" ANDassign, // "&=" ARROW, // "->" CONCAT, // "##" DECR, // "--" DIVassign, // "/=" ELLIPSIS, // "..." EQ, // "==" ERassign, // "^=" GE, // ">=" ICR, // "++" LE, // "<=" LS, // "<<" LSassign, // "<<=" MODassign, // "%=" MINUSassign, // "-=" MULTassign, // "*=" NE, // "!=" ORassign, // "|=" OROR, // "||" PLUSassign, // "+=" RS, // ">>" RSassign, // ">>=" CHARACTERconstant, // character constant STRINGliteral, // character string HEXconstant =129, OCTconstant, DECconstant, FLOATconstant, // real NAME, AUTO, // "auto" BREAK, // "break" CASE, // "case" CHAR, // "char" CONSTANT, // "const" CONTINUE, // "continue" DEFAULT, // "default" DO, // "do" DOUBLE, // "double" ELSE, // "else" ENUM, // "enum" EXTERN, // "extern" FLOAT, // "float" FOR, // "for" GOTO, // "goto" IF, // "if" INT, // "int" LONG, // "long" REGISTER, // "register" RETURN, // "return" SHORT, // "short" SIGNED, // "signed" SIZEOF, // "sizeof" STATIC, // "static" STRUCT, // "struct" SWITCH, // "switch" TYPEDEF, // "typedef" UNION, // "union" UNSIGNED, // "unsigned" VOIDkey, // "void" VOLATILE, // "volatile" WHILE, // "while" UNRECOGNIZED, } ] grammar ->space, parse unit?..., eof // Accumulate optional space space -> =reset(space_stack); -> space, ' ':s ={if (args_only) space_stack << s;} // Basic parse units parse unit -> parameter expansion -> simple parse unit, space =ta << space_stack; -> concatenation, space =ta << space_stack; -> macro:t, space =ta << t << space_stack; simple parse unit -> ~eof - NAME - CONCAT - '#'- ' ':t =ta << t; -> '#', parameter name:n =ta << make_string(n.handle); -> variable:t =ta << t; -> simple macro:t =expand_macro(t,0), concat(ta); -> macro:t, space, '(', macro arg list:n, ')' =expand_macro(t,n), concat(ta); -> defined, macro name:n =ta << defined(n.handle); (token) macro name -> space, NAME:n =n; -> space, '(', space, NAME:n, space, ')' =n; (token) variable, parameter name, simple macro, macro, defined -> NAME:n =id_macro(n); parameter expansion -> parameter name:name, space =expand_arg(name.handle), ta << space_stack; // Implementation of "##" operator concatenation -> left side, space, parameter name:name =ta << args[name.handle], concatenate(); -> left side, space, right side =concatenate(); left side -> parameter name:n, space, CONCAT =ta << args[n.handle], ++ta; -> simple parse unit, space, CONCAT =++ta; -> macro:t, space, CONCAT =ta << t, ++ta; -> concatenation, space, CONCAT =++ta; right side -> ~eof - NAME - CONCAT - '#'- ' ':t =ta << t; -> '#', parameter name:n =ta << make_string(n.handle); -> not parameter:t =ta << t; (token) not parameter -> variable -> simple macro -> macro -> defined // Gather Macro Arguments (unsigned) macro arg list -> space = 0; -> space, arg elements = 1; -> macro arg list:n, ',', space, arg elements =n+1; initial arg element -> ~eof - ',' - '(' - ')' - SPACE:t =++ta << t; -> nested elements, ')':t =ta << t; arg element -> ~eof - ',' - '(' - ')':t =ta << t; -> nested elements, ')':t =concat(ta) << t; arg elements -> initial arg element -> arg elements, arg element nested elements -> '(':t =++ta << t; -> nested elements, arg element -> nested elements, ',':t =ta << t; { // Embedded C #include "array.h" // AnaGram\CLASSLIB\INCLUDE\array.h #include "stack.h" // AnaGram\CLASSLIB\INCLUDE\stack.h // Macro Definitions #define INPUT_CODE(T) (T).id #define PCB (*mas_pcb) #define SYNTAX_ERROR syntax_error(PCB.error_message); // Static variables typedef stack<unsigned> unsigned_stack; // accomodate broken compilers static unsigned_stack active_macros(200,20); static token **args; static int args_only = 0; static mas_pcb_type *mas_pcb; static int n_concats = 0; static int n_args; static unsigned *params; static token_accumulator space_stack(100); /* expand_text() is a shell procedure which calls the mas parser a number of times. It is used to expand arguments before substituting them into a macro, and to expand the body of a macro. Notice that expand_text() is recursive, since macros encountered during the an expansion process may themselves need to be expanded. expand_text() takes three explicit arguments: token *text: points to a string of tokens, terminated by an eof token. int n: specifies the number of arguments. Defaults to 0. The arguments themselves are token strings on the token accumulator stack. expand_text() makes copies of them and stores pointers to them in the args array. unsigned *p: An array of n dictionary indices which gives the names of the parameters for which the arguments are to be substituted. p defaults to NULL. global switches Two global switches affect the expansion of text: if_clause and args_only. Setting if_clause affects the treatment of the token "defined". Setting args_only causes only macro parameters to be expanded. */ void expand_text(token *text, int n, unsigned *p) { mas_pcb_type pcb; // Save old status mas_pcb_type *save_pcb = mas_pcb; int save_n_args = n_args; token **save_args = args; unsigned *save_params = params; int save_switch = args_only; // pop args from accumlator stack and expand them args_only = 0; token **new_args; int k = n; if (n) { new_args = new token*[n]; args_only = 1; while (k--) { token t; token top = *(token *) ta; while (top.id == SPACE) ta >> t; //trim space on right array<token> arg_tokens(ta, size(ta) + 1); token *tp = arg_tokens; while (tp->id == SPACE) tp++; //trim space on left --ta; mas_pcb = &pcb; pcb.pointer = tp; ++ta; mas(); new_args[k] = copy(ta); --ta; } args_only = 0; } else new_args = NULL; // Expand text args = new_args; n_args = n; params = p; pcb.pointer = text; mas_pcb = &pcb; ++ta; ++active_macros; n_concats = 0; mas(); // If any new tokens were created by concatenation, rescan while (n_concats) { array<token> expansion(ta,size(ta) + 1); --ta; pcb.pointer = expansion; ++ta; n_concats = 0; n = size(active_macros); #ifdef _MSC_VER //Cope with peculiarity of MSVC++ while (n--) macro[*((unsigned *)active_macros + n)].busy_flag = 1; #else while (n--) macro[active_macros[n]].busy_flag = 1; #endif mas(); } n = size(active_macros); #ifdef _MSC_VER //Cope with peculiarity of MSVC++ while (n--) macro[*((unsigned *)active_macros + n)].busy_flag = 0; #else while (n--) macro[active_macros[n]].busy_flag = 0; #endif --active_macros; // Discard argument strings n = n_args; while (n--) delete [] args[n]; if (n_args) delete [] args; // Restore old status args_only = save_switch; args = save_args; n_args = save_n_args; params = save_params; mas_pcb = save_pcb; } /* expand_macro() is a shell procedure which sets up a call to expand_text for a specific macro. */ void expand_macro(token t, unsigned n_args) { unsigned id = macro_id[t.handle]; token *body = macro[id].body; assert(n_args == macro[id].n_args); if (body == NULL) { while (n_args--) --ta; ++ta; return; } expand_text(body,n_args,macro[id].arg_names); } /* expand_arg() is another shell procedure for expand_text() which does a complete expansion of a single macro argument. */ static void expand_arg(unsigned n) { expand_text(args[n]); concat(ta); } /* id_macro() is very nearly the same as id_macro() in TS.SYN. The primary difference is that this one deals in tokens, the other in character strings. */ static token id_macro(token t) { unsigned n = n_args; unsigned id; while (n--) if (t.handle == params[n]) { CHANGE_REDUCTION(parameter_name); t.handle = n; return t; } if (args_only) return t; if (if_clause && t.handle == defined_value) { CHANGE_REDUCTION(defined); return t; } id = macro_id[t.handle]; if (id == 0) return t; if (macro[id].busy_flag) return t; active_macros << id; if (macro[id].parens) CHANGE_REDUCTION(macro); else CHANGE_REDUCTION(simple_macro); return t; } /* defined() is very nearly the same as defined() in TS.SYN. The primary difference is that this one deals in tokens, the other in character strings. */ static token defined(unsigned handle) { token t; t.id = DECconstant; t.handle = macro_id[handle] ? one_value : zero_value; return t; } /* concatenate() implements the splicing together of two tokens by the "##" operator in a macro definition. Because of the way the grammar has been written, spaces have already been trimmed on both sides of the ## by the parser. If there are actually two tokens to concatenate, the last token on the left is popped off, its string value is obtained from the token dictionary and pushed onto the string accumulator, ditto for the first token on the right. The string is then identified and the token is classified. If the new token is the name of a macro, a new scan will be required to expand it. */ static void concatenate(void) { array<token> right_arg(ta, size(ta) + 1); token t; token *tp = right_arg; --ta; // discard right argument from stack if (size(ta) && tp->id != END_OF_FILE) { ta >> t; // pop left token ++sa << td[t.handle] << td[tp->handle]; // left string + right string t.handle = td << sa.top(); // identify string t.id = classify_token(sa.top()); // classify token --sa; // discard string ++tp; // discard old token on right if (macro_id[t.handle]) n_concats++; // if macro, signal rescan ta << t; // output new token } ta << tp; // remainder of right side } /* make_string() implements the '#' operator in macro expansions, that is, it turns its operand into a string constant. To do this it must provide "" marks and must quote any embedded " or \ characters with the \ character. */ static token make_string(unsigned n) { token *tp; token t; tp = args[n]; ++sa << '"'; while (tp->id != END_OF_FILE) { char *p = td[tp->handle]; char c; while ((c = *p++) != 0) { if (c == '"' || c == '\\') sa << '\\'; sa << c; } tp++; } sa << '"'; t.id = STRINGliteral; t.handle = td << sa.top(); --sa; return t; } } // End of Embedded C