sistema_progs

Programas para customizar o meu entorno de traballo nos meus equipos persoais
Log | Files | Refs

mwg_pp.awk (47942B)


      1 #!/usr/bin/gawk -f
      2 
      3 # 20120726 行番号出力機能 (例: '#line 12 a.cpp')
      4 
      5 function awk_getfiledir(_ret) {
      6   _ret = m_lineno_cfile;
      7   sub(/[^\/\\]+$/, "", _ret);
      8   if (_ret == "/") return "/";
      9   sub(/[\/\\]$/, "", _ret);
     10   return _ret;
     11 }
     12 
     13 function print_error(title, msg) {
     14   global_errorCount++;
     15   print "\33[1;31m" title "!\33[m " msg > "/dev/stderr";
     16 }
     17 
     18 function trim(text) {
     19   #gsub(/^[ \t]+|[ \t]+$/, "", text);
     20   gsub(/^[[:space:]]+|[[:space:]]+$/, "", text);
     21   return text;
     22 }
     23 
     24 function slice(text, start, end, _l) {
     25   _l = length(text);
     26   if (start < 0) start += _l;
     27   end = end == null ? _l : end < 0 ? end + _l : end;
     28 
     29   return substr(text, start + 1, end - start);
     30 }
     31 
     32 #function head_token(text, ret, _, _i, _l) {
     33 #  _i = match(text, /[^-a-zA-Z:0-9_]/);
     34 #  _l = _i ? _i - 1 : length(text);
     35 #  ret[0] = trim(substr(text, 1, _l));
     36 #  ret[1] = trim(substr(text, _l + 1));
     37 #}
     38 
     39 #-------------------------------------------------------------------------------
     40 function unescape(text, _, _ret, _capt) {
     41   _ret = "";
     42   while (match(text, /\\(.)/, _capt) > 0) {
     43     _ret = _ret substr(text, 1, RSTART - 1) _capt[1];
     44     text = substr(text, RSTART + RLENGTH);
     45   }
     46   return _ret text;
     47 }
     48 
     49 #-------------------------------------------------------------------------------
     50 # replace
     51 function replace(text, before, after, _is_tmpl, _is_head, _captures, _rep, _ltext, _rtext) {
     52   _is_tmpl = (match(after, /\$[0-9]+/) > 0);
     53   _is_head = (substr(before, 1, 1) == "^");
     54 
     55   _ret = "";
     56   while (match(text, before, _captures) > 0) {
     57     _ltext = substr(text, 1, RSTART - 1);
     58     _rtext = substr(text, RSTART + RLENGTH);
     59 
     60     _rep = _is_tmpl ? rep_instantiate_tmpl(after, _captures) : after;
     61 
     62     _ret = _ret _ltext _rep;
     63     text = _rtext;
     64 
     65     if (_is_head) break;
     66     if (RLENGTH == 0) {
     67       _ret = _ret substr(text, 1, 1);
     68       text = substr(text, 2);
     69       if (length(text) == 0) break;
     70     }
     71   }
     72   return _ret text;
     73 }
     74 function rep_instantiate_tmpl(text, captures, _, _ret, _num, _insert) {
     75   _ret = "";
     76   while (match(text, /\\(.)|\$([0-9]+)/, _num)) {
     77     #print "dbg: $ captured: RSTART = " RSTART "; num = " _num[1] "; captures[num] = " captures[_num[1]] > "/dev/stderr"
     78     if (_num[2] != "") {
     79       _insert = captures[_num[2]];
     80     } else if (_num[1] ~ /^[$\\]$/) {
     81       _insert = _num[1];
     82     } else {
     83       _insert = _num[0];
     84     }
     85     _ret = _ret substr(text, 1, RSTART - 1) _insert;
     86     text = substr(text, RSTART + RLENGTH);
     87   }
     88   return _ret text;
     89 }
     90 
     91 #===============================================================================
     92 #  mwg_pp.eval
     93 #-------------------------------------------------------------------------------
     94 # function eval_expr(expression);
     95 
     96 # -*- mode:awk -*-
     97 
     98 function ev1scan_init_opregister(opname, optype, opprec) {
     99   ev_db_operator[opname] = optype;
    100   ev_db_operator[opname, "a"] = opprec;
    101 }
    102 
    103 function ev1scan_init() {
    104   OP_BIN = 1;
    105   OP_UNA = 2; # prefix
    106   OP_SGN = 3; # prefix or binary
    107   OP_INC = 4; # prefix or suffix
    108 
    109   ev1scan_init_opregister("." , OP_BIN, 12.0);
    110 
    111   ev1scan_init_opregister("++", OP_INC, 11.0);
    112   ev1scan_init_opregister("--", OP_INC, 11.0);
    113   ev1scan_init_opregister("!" , OP_UNA, 11.0);
    114 
    115   ev1scan_init_opregister("*" , OP_BIN, 10.0);
    116   ev1scan_init_opregister("/" , OP_BIN, 10.0);
    117   ev1scan_init_opregister("%" , OP_BIN, 10.0);
    118 
    119   ev1scan_init_opregister("+" , OP_SGN, 9.0);
    120   ev1scan_init_opregister("-" , OP_SGN, 9.0);
    121 
    122   ev1scan_init_opregister("==", OP_BIN, 8.0);
    123   ev1scan_init_opregister("!=", OP_BIN, 8.0);
    124   ev1scan_init_opregister("<" , OP_BIN, 8.0);
    125   ev1scan_init_opregister("<=", OP_BIN, 8.0);
    126   ev1scan_init_opregister(">" , OP_BIN, 8.0);
    127   ev1scan_init_opregister(">=", OP_BIN, 8.0);
    128 
    129   ev1scan_init_opregister("&" , OP_BIN, 7.4);
    130   ev1scan_init_opregister("^" , OP_BIN, 7.2);
    131   ev1scan_init_opregister("|" , OP_BIN, 7.0);
    132   ev1scan_init_opregister("&&", OP_BIN, 6.4);
    133   ev1scan_init_opregister("||", OP_BIN, 6.0);
    134 
    135   ev1scan_init_opregister("=" , OP_BIN, 2.0);
    136   ev1scan_init_opregister("+=", OP_BIN, 2.0);
    137   ev1scan_init_opregister("-=", OP_BIN, 2.0);
    138   ev1scan_init_opregister("*=", OP_BIN, 2.0);
    139   ev1scan_init_opregister("/=", OP_BIN, 2.0);
    140   ev1scan_init_opregister("%=", OP_BIN, 2.0);
    141   ev1scan_init_opregister("|=", OP_BIN, 2.0);
    142   ev1scan_init_opregister("^=", OP_BIN, 2.0);
    143   ev1scan_init_opregister("&=", OP_BIN, 2.0);
    144   ev1scan_init_opregister("," , OP_BIN, 1.0);
    145 
    146   # for ev2
    147   SE_VALU = 1;
    148   SE_PREF = 0;
    149   SE_MARK = -1;
    150 
    151   ATTR_SET = "t";
    152   ATTR_TYP = "T";
    153   ATTR_MOD = "M";
    154 
    155   MOD_NUL = 0;
    156   MOD_REF = 1;
    157   ATTR_REF = "R";
    158   MOD_ARG = 2;
    159   MOD_ARR = 4;
    160   MOD_MTH = 8;
    161   ATTR_MTH_OBJ = "Mo";
    162   ATTR_MTH_MEM = "Mf";
    163 
    164   TYPE_NUM = 0;
    165   TYPE_STR = 1;
    166 }
    167 
    168 function ev1scan_scan(expression, words, _wlen, _i, _len, _c, _t, _w) {
    169   _wlen = 0;
    170   _len = length(expression);
    171   for (_i = 0; _i < _len; _i++) {
    172     _c = substr(expression, _i + 1, 1);
    173 
    174     if (_c ~ /[.0-9]/) {
    175       _t = "n";
    176       _w = _c;
    177       while (_i + 1 < _len) {
    178         _c = substr(expression, _i + 2, 1);
    179         if (_c !~ /[.0-9]/) break;
    180         _w = _w _c;
    181         _i++;
    182       }
    183       #if (_w == ".")_w = 0;
    184       if (_w == ".") {
    185         _t = "o";
    186       }
    187     } else if (ev_db_operator[_c] != "") {
    188       _t = "o";
    189       _w = _c;
    190       while (_i + 1 < _len) {
    191         _c = substr(expression, _i + 2, 1);
    192         #print "dbg: ev_db_op[" _w _c "] = " ev_db_operator[_w _c] > "/dev/stderr"
    193         if (ev_db_operator[_w _c] != "") {
    194           _w = _w _c;
    195           _i++;
    196         } else break;
    197       }
    198     } else if (_c ~ "[[({?]") {
    199       _t = "op";
    200       _w = _c;
    201     } else if (_c ~ "[])}:]") {
    202       _t = "cl";
    203       _w = _c;
    204     } else if (_c ~ /[_a-zA-Z]/) {
    205       _t = "w";
    206       _w = _c;
    207       while (_i + 1 < _len) {
    208         _c = substr(expression, _i + 2, 1);
    209         if (_c !~ /[_a-zA-Z0-9]/) break;
    210         _w = _w _c;
    211         _i++;
    212       }
    213     } else if (_c == "\"") {
    214       # string literal
    215       _t = "S";
    216       _w = "";
    217       while (_i + 1 < _len) {
    218         _c = substr(expression, _i + 2, 1);
    219         _i++;
    220         if (_c  == "\"") {
    221           break;
    222         } else if (_c == "\\") {
    223           #print_error("dbg: (escchar = " _c " " substr(expression, _i + 2, 1) ")" );
    224           if (_i + 1 < _len) {
    225             _w = _w ev1scan_scan_escchar(substr(expression, _i + 2, 1));
    226             _i++;
    227           } else {
    228             _w = _w _c;
    229           }
    230         } else {
    231           _w = _w _c;
    232         }
    233       }
    234     } else if (_c ~ /[[:space:]]/) {
    235       continue; # ignore blank
    236     } else {
    237       print_error("mwg_pp.eval_expr", "unrecognizable character '" _c "'");
    238       continue; # ignore unknown character
    239     }
    240 
    241     words[_wlen, "t"] = _t;
    242     words[_wlen, "w"] = _w;
    243     _wlen++;
    244   }
    245 
    246   # debug
    247   #for (_i = 0; _i < _wlen; _i++) {
    248   #    print "yield " words[_i, "w"] " as " words[_i, "t"] > "/dev/stderr"
    249   #}
    250 
    251   return _wlen;
    252 }
    253 
    254 function ev1scan_scan_escchar(c) {
    255   if (c !~ /[nrtvfaeb]/) return c;
    256   if (c == "n") return "\n";
    257   if (c == "r") return "\r";
    258   if (c == "t") return "\t";
    259   if (c == "v") return "\v";
    260   if (c == "f") return "\f";
    261   if (c == "a") return "\a";
    262   if (c == "e") return "\33";
    263   if (c == "b") return "\b";
    264   return c;
    265 }
    266 
    267 function ev1scan_cast_bool(arg) {
    268   return arg != 0 && arg != "";
    269 }
    270 # -*- mode: awk -*-
    271 
    272 function eval_expr(expression) {
    273   return ev2_expr(expression);
    274 }
    275 
    276 function ev2_expr(expression, _wlen, _words, _i, _len, _t, _w, _v, _sp, _s, _sp1, _optype) {
    277   _wlen = ev1scan_scan(expression, _words);
    278 
    279   # <param name="_s">
    280   #  parsing stack
    281   #  _s[index, "t"]  : SE_PREF  SE_MARK  SE_VALU
    282   #  _s[index]       : lhs               value
    283   #  _s[index, "T"]  : dataType          dataType
    284   #  _s[index, "c"]  : b+ u!    op
    285   #  _s[index, "l"]  : assoc
    286   #
    287   #  _s[index, "M"] = MOD_ARG;
    288   #  _s[index, "A"] = length;
    289   #  _s[index, "A", i] = element;
    290   # </param>
    291 
    292   # parse
    293   _sp = -1;
    294   for (_i = 0; _i < _wlen; _i++) {
    295     # _t: token type
    296     # _w: token word
    297     # _l: token prefix level
    298     _t = _words[_i, "t"];
    299     _w = _words[_i, "w"];
    300 
    301     #-- process token --
    302     if (_t == "n") {
    303       _sp++;
    304       _s[_sp] = 0 + _w;
    305       _s[_sp, "t"] = SE_VALU;
    306       _s[_sp, "T"] = TYPE_NUM;
    307       _s[_sp, "M"] = MOD_NUL;
    308     #---------------------------------------------------------------------------
    309     } else if (_t == "o") {
    310       _optype = ev_db_operator[_w];
    311       if (_optype == OP_SGN) { # signature operator +-
    312         if (_sp >= 0 && _s[_sp, "t"] == SE_VALU) {
    313           _t = "b"; # binary operator
    314         } else {
    315           _t = "u"; # unary operator
    316         }
    317       } else if (_optype == OP_BIN) { # binary operator
    318         _t = "b";
    319       } else if (_optype == OP_UNA) { # unary prefix operator
    320         _t = "u";
    321       } else if (_optype == OP_INC) { # operator++ --
    322         if (_sp >= 0 && _s[_sp, "t"] == SE_VALU) {
    323           if (and(_s[_sp, "M"], MOD_REF)) {
    324             if (_w == "++")
    325               d_data[_s[_sp, "R"]]++;
    326             else if (_w == "--")
    327               d_data[_s[_sp, "R"]]--;
    328             else
    329               print_error("mwg_pp.eval", "unknown increment operator " _w);
    330 
    331             _s[_sp, "M"] = MOD_NUL;
    332             delete _s[_sp, "R"];
    333           }
    334 
    335           _t = "";
    336         } else {
    337           _t = "u"; # unary operator
    338         }
    339       } else {
    340         print_error("mwg_pp.eval", "unknown operator " _w);
    341       }
    342 
    343       if (_t == "b") {
    344         #-- binary operator
    345         _l = ev_db_operator[_w, "a"];
    346         #print "dbg: binary operator level = " _l > "/dev/stderr"
    347 
    348         # get lhs
    349         _sp = ev2_pop_value(_s, _sp, _l); # left assoc
    350         #_sp = ev2_pop_value(_s, _sp, _l + 0.1); # right assoc # TODO =
    351 
    352         # overwrite to lhs
    353         _s[_sp, "t"] = SE_PREF;
    354         _s[_sp, "p"] = "b";
    355         _s[_sp, "P"] = _w;
    356         _s[_sp, "l"] = _l; # assoc level
    357       } else if (_t == "u") {
    358         # unary operator
    359         _l = ev_db_operator[_w, "a"];
    360 
    361         _sp++;
    362         _s[_sp, "t"] = SE_PREF
    363         _s[_sp, "p"] = "u";
    364         _s[_sp, "P"] = _w;
    365         _s[_sp, "l"] = _l; # assoc level
    366       }
    367     #---------------------------------------------------------------------------
    368     } else if (_t == "op") {
    369       _sp++;
    370       _s[_sp, "t"] = SE_MARK;
    371       _s[_sp, "m"] = _w;
    372     } else if (_t == "cl") {
    373       if (_sp >= 0 && _s[_sp, "t"] == SE_VALU) {
    374         _sp1 = ev2_pop_value(_s, _sp, 0);
    375         _sp = _sp1-1;
    376       } else {
    377         # empty arg
    378         _sp1 = _sp+1;
    379         _s[_sp1] = "";
    380         _s[_sp1, "t"] = SE_VALU;
    381         _s[_sp1, "T"] = TYPE_STR;
    382         _s[_sp1, "M"] = MOD_ARG;
    383         _s[_sp1, "A"] = 0;
    384       }
    385       # state: [.. _sp=open _sp1]
    386 
    387       # parentheses
    388       if (!(_sp >= 0 && _s[_sp, "t"] == SE_MARK)) {
    389         print_error("mwg_pp.eval: no matching open paren to " _w " in " expression);
    390         continue;
    391       }
    392       _w = _s[_sp, "m"] _w;
    393       _sp--;
    394 
    395 
    396       # state: [_sp open _sp1]
    397       if (_sp >= 0 && _s[_sp, "t"] == SE_VALU) {
    398         if (_w == "?:") {
    399           _sp = ev2_pop_value(_s, _sp, 3.0); # assoc_value_3
    400           _v = (_s[_sp] != 0 && _s[_sp] != "") ? "T" : "F";
    401           #print_error("dbg: _s[_sp]=" _s[_sp] " _v=" _v);
    402 
    403           # last element
    404           _s[_sp] = _s[_sp1];
    405           _s[_sp, "t"] = SE_VALU;
    406           _s[_sp, "T"] = _s[_sp1, "T"];
    407           _s[_sp, "M"] = MOD_NUL; #TODO reference copy
    408 
    409           # overwrite pref
    410           _s[_sp, "t"] = SE_PREF;
    411           _s[_sp, "p"] = _w;
    412           _s[_sp, "P"] = _v;
    413           _s[_sp, "l"] = 3.0; # level
    414         } else {
    415           _sp = ev2_pop_value(_s, _sp, 12); # assoc_value_12
    416 
    417           if (_w == "[]" && and(_s[_sp, "M"], MOD_REF)) {
    418             # indexing
    419             _s[_sp] = d_data[_s[_sp, "R"], _s[_sp1]];
    420             _s[_sp, "t"] = SE_VALU;
    421             _s[_sp, "T"] = (_s[_sp] == 0 + _s[_sp]? TYPE_NUM: TYPE_STR);
    422             _s[_sp, "M"] = MOD_REF;
    423             _s[_sp, "R"] = _s[_sp, "R"] SUBSEP _s[_sp1];
    424           } else if (and(_s[_sp, "M"], MOD_REF)) {
    425             # function call
    426             ev2_funcall(_s, _sp, _s[_sp, "R"], _s, _sp1);
    427           } else if (and(_s[_sp, "M"], MOD_MTH)) {
    428             # member function call
    429             ev2_memcall(_s, _sp, _s, _sp SUBSEP ATTR_MTH_OBJ, _s[_sp, ATTR_MTH_MEM], _s, _sp1);
    430           } else {
    431             print "mwg_pp.eval: invalid function call " _s[_sp] " " _w " in " expression > "/dev/stderr"
    432           }
    433         }
    434       } else {
    435         _sp++;
    436         if (_w == "[]") {
    437           # array
    438           ev2_copy(_s, _sp, _s, _sp1);
    439           _s[_sp, "M"] = MOD_ARR;
    440         } else {
    441           # last element
    442           _s[_sp] = _s[_sp1];
    443           _s[_sp, "t"] = SE_VALU;
    444           _s[_sp, "T"] = _s[_sp1, "T"];
    445           _s[_sp, "M"] = MOD_NUL;
    446         }
    447       }
    448     #---------------------------------------------------------------------------
    449     } else if (_t == "w") {
    450       _sp++;
    451       _s[_sp] = d_data[_w];
    452       _s[_sp, "t"] = SE_VALU;
    453       _s[_sp, "T"] = (_s[_sp] == 0 + _s[_sp]? TYPE_NUM: TYPE_STR);
    454       _s[_sp, "M"] = MOD_REF;
    455       _s[_sp, "R"] = _w;
    456     } else if (_t == "S") {
    457       # string
    458       _sp++;
    459       _s[_sp] = _w;
    460       _s[_sp, "t"] = SE_VALU;
    461       _s[_sp, "T"] = TYPE_STR;
    462       _s[_sp, "M"] = MOD_NUL;
    463     } else {
    464       print_error("mwg_pp.eval:fatal", "unknown token type " _t);
    465     }
    466   }
    467 
    468   _sp = ev2_pop_value(_s, _sp, 0);
    469   return _sp >= 1? "err": _s[_sp];
    470 }
    471 
    472 function ev2_pop_value(s, sp, assoc, rDict, rName, _vp, _value) {
    473   # <param> rDict [default = s]
    474   # <param> rName [default = <final stack top>]
    475   # <returns> sp = <final stack top>
    476 
    477   # read value
    478   if (sp >= 0 && s[sp, "t"] == SE_VALU) {
    479     sp--;
    480   } else {
    481     _vp = sp + 1;
    482     s[_vp] = 0;
    483     s[_vp, "t"] = SE_VALU;
    484     s[_vp, "T"] = TYPE_NUM;
    485     s[_vp, "M"] = MOD_NUL;
    486   }
    487 
    488   # proc prefices
    489   while(sp >= 0 && s[sp, "t"] == SE_PREF && s[sp, "l"] >= assoc) {
    490     ev2_apply(s, sp, sp + 1);
    491     sp--;
    492   }
    493 
    494   if (rDict == "")
    495     sp++;
    496   else
    497     ev2_copy(rDict, rName, s, sp + 1);
    498 
    499   return sp;
    500 }
    501 
    502 function ev2_memget(dDict, dName, oDict, oName, memname) {
    503   #print_error("mwg_pp.eval", "dbg: ev2_memget(memname=" memname ")");
    504 
    505   # embedded special member
    506   if (oDict[oName, "T"] == TYPE_STR) {
    507     if (memname == "length") {
    508       dDict[dName] = length(oDict[oName]);
    509       dDict[dName, "t"] = SE_VALU;
    510       dDict[dName, "T"] = TYPE_NUM;
    511       dDict[dName, "M"] = MOD_NUL;
    512       return;
    513     } else if (memname == "replace" || memname == "Replace" || memname == "slice" || memname ~ /^to(upper|lower)$/) {
    514       ev2_copy(dDict, dName SUBSEP ATTR_MTH_OBJ, oDict, oName);
    515       dDict[dName, ATTR_MTH_MEM] = memname;
    516       dDict[dName] = "";
    517       dDict[dName, "t"] = SE_VALU;
    518       dDict[dName, "T"] = TYPE_STR;
    519       dDict[dName, "M"] = MOD_MTH;
    520       #print_error("mwg_pp.eval", "dbg: method = String#" memname);
    521       return;
    522     }
    523   } else {
    524     # members for other types (TYPE_NUM MOD_ARR etc..)
    525   }
    526 
    527   # normal data member
    528   if (and(oDict[oName, ATTR_MOD], MOD_REF)) {
    529     dDict[dName] = d_data[oDict[oName, ATTR_REF], memname];
    530     dDict[dName, "t"] = SE_VALU;
    531     dDict[dName, "T"] = (dDict[dName] == 0 + dDict[dName]? TYPE_NUM: TYPE_STR);
    532     dDict[dName, "M"] = MOD_REF;
    533     dDict[dName, ATTR_REF] = oDict[oName, ATTR_REF] SUBSEP memname;
    534   } else {
    535     print_error("mwg.eval", "invalid member name '" memname "'");
    536     dDict[dName] = "";
    537     dDict[dName, "t"] = SE_VALU;
    538     dDict[dName, "T"] = TYPE_STR;
    539     dDict[dName, "M"] = MOD_NUL;
    540   }
    541 
    542   # rep: dDict dName oDict oName
    543 }
    544 
    545 function ev2_memcall(dDict, dName, oDict, oName, memname, aDict, aName, _a, _i, _c, _result, _resultT) {
    546   #print_error("mwg_pp.eval", "dbg: ev2_memcall(memname=" memname ")");
    547 
    548   _resultT = "";
    549 
    550   # read arguments
    551   if (aDict[aName, "M"] != MOD_ARG) {
    552     _c = 1;
    553     _a[0] = aDict[aName];
    554   } else {
    555     _c = aDict[aName, "A"];
    556     for (_i = 0; _i < _c; _i++) _a[_i] = aDict[aName, "A", _i];
    557   }
    558 
    559   #-----------------
    560   # process
    561   if (oDict[oName, "T"] == TYPE_STR) {
    562     if (memname == "replace") {
    563       _result = oDict[oName];
    564       gsub(_a[0], _a[1], _result);
    565       _resultT = TYPE_STR;
    566     } else if (memname == "Replace") {
    567       _result = replace(oDict[oName], _a[0], _a[1]);
    568       _resultT = TYPE_STR;
    569     } else if (memname == "slice") {
    570       _result = slice(oDict[oName], _a[0], _a[1]);
    571       _resultT = TYPE_STR;
    572     } else if (memname == "toupper") {
    573       _result = toupper(oDict[oName]);
    574       _resultT = TYPE_STR;
    575     } else if (memname == "tolower") {
    576       _result = tolower(oDict[oName]);
    577       _resultT = TYPE_STR;
    578     }
    579   }
    580 
    581   #-----------------
    582   # write value
    583   if (_resultT == "") {
    584     print_error("mwg.eval", "invalid member function name '" memname "'");
    585     _result = "";
    586     _resultT = TYPE_STR;
    587   }
    588 
    589   dDict[dName] = _result;
    590   dDict[dName, "t"] = SE_VALU;
    591   dDict[dName, "T"] = _resultT;
    592   dDict[dName, "M"] = MOD_NUL;
    593 }
    594 
    595 function ev2_funcall(dDict, dName, funcname, aDict, aName, _a, _i, _c, _result, _resultT, _cmd, _line) {
    596   _resultT = TYPE_NUM;
    597 
    598   if (aDict[aName, "M"] != MOD_ARG) {
    599     _c = 1;
    600     _a[0] = aDict[aName];
    601   } else {
    602     _c = aDict[aName, "A"];
    603     for (_i = 0; _i < _c; _i++) _a[_i] = aDict[aName, "A", _i];
    604   }
    605 
    606   if (funcname == "int") {
    607     _result = int(_a[0]);
    608   } else if (funcname == "float") {
    609     _result = 0 + _a[0];
    610   } else if (funcname == "floor") {
    611     if (_a[0] >= 0) {
    612       _result = int(_a[0]);
    613     } else {
    614       _result = int(1 - _a[0]);
    615       _result = int(_a[0] + _result)-_result;
    616     }
    617   } else if (funcname == "ceil") {
    618     if (_a[0] <= 0) {
    619       _result = int(_a[0]);
    620     } else {
    621       _result = int(1 + _a[0]);
    622       _result = int(_a[0]-_result) + _result;
    623     }
    624   } else if (funcname == "sqrt") {
    625     _result = sqrt(_a[0]);
    626   } else if (funcname == "sin") {
    627     _result = sin(_a[0]);
    628   } else if (funcname == "cos") {
    629     _result = cos(_a[0]);
    630   } else if (funcname == "tan") {
    631     _result = sin(_a[0]) / cos(_a[0]);
    632   } else if (funcname == "atan") {
    633     _result = atan2(_a[0], 1);
    634   } else if (funcname == "atan2") {
    635     _result = atan2(_a[0], _a[1]);
    636   } else if (funcname == "exp") {
    637     _result = exp(_a[0]);
    638   } else if (funcname == "log") {
    639     _result = log(_a[0]);
    640   } else if (funcname == "sinh") {
    641     _x = exp(_a[0]);
    642     _result = 0.5*(_x-1/_x);
    643   } else if (funcname == "cosh") {
    644     _x = exp(_a[0]);
    645     _result = 0.5 * (_x + 1 / _x);
    646   } else if (funcname == "tanh") {
    647     _x = exp(2 * _a[0]);
    648     _result = (_x - 1) / (_x + 1);
    649   } else if (funcname == "rand") {
    650     _result = rand();
    651   } else if (funcname == "srand") {
    652     _result = srand(_a[0]);
    653   } else if (funcname == "trim") {
    654     _resultT = TYPE_STR;
    655     _result = _a[0];
    656     gsub(/^[[:space:]]+|[[:space:]]+$/, "", _result);
    657   } else if (funcname == "sprintf") {
    658     _resultT = TYPE_STR;
    659     _result = sprintf(_a[0], _a[1], _a[2], _a[3], _a[4], _a[5], _a[6], _a[7], _a[8], _a[9]);
    660   } else if (funcname == "slice") {
    661     _resultT = TYPE_STR;
    662     _result = slice(_a[0], _a[1], _a[2]);
    663   } else if (funcname == "length") {
    664     _result = length(_a[0]);
    665   } else if (funcname == "getenv") {
    666     _resultT = TYPE_STR;
    667     _result = ENVIRON[_a[0]];
    668   } else if (funcname == "system") {
    669     _resultT = TYPE_STR;
    670     _result = "";
    671     _cmd = _a[0];
    672     while ((_cmd | getline _line) > 0)
    673        _result = _result _line "\n";
    674     close(_cmd);
    675     sub(/\n+$/, "", _result);
    676   } else {
    677     print_error("mwg_pp.eval", "unknown function " funcname);
    678     _result = 0;
    679   }
    680 
    681   dDict[dName] = _result;
    682   dDict[dName, "t"] = SE_VALU;
    683   dDict[dName, "T"] = _resultT;
    684   dDict[dName, "M"] = MOD_NUL;
    685 }
    686 
    687 function ev2_unsigned(value) {
    688   return value >= 0 ? value : value + 0x100000000;
    689 }
    690 
    691 function ev2_apply(stk, iPre, iVal, _pT, _pW, _lhs, _rhs, _lhsT, _rhsT, _result, _i, _a, _b, _c) {
    692   # <param name="stk">stack</param>
    693   # <param name="iPre">prefix operator/resulting value</param>
    694   # <param name="iVal">input value</param>
    695 
    696   _pT = stk[iPre, "p"];
    697   _pW = stk[iPre, "P"];
    698 
    699   if (_pT == "b") {
    700     _lhs = stk[iPre];
    701     _rhs = stk[iVal];
    702     _lhsT = stk[iPre, "T"];
    703     _rhsT = stk[iVal, "T"];
    704     _resultT = TYPE_NUM;
    705 
    706     #print "binary " _lhs " " _pW " " _rhs > "/dev/stderr"
    707     if (_pW == "+") {
    708       if (_lhsT == TYPE_STR || _rhsT == TYPE_STR) {
    709         _result = _lhs _rhs;
    710         _resultT = TYPE_STR;
    711       } else
    712         _result = _lhs+_rhs;
    713     } else if (_pW == "-") _result = _lhs - _rhs;
    714     else if (_pW == "*") _result = _lhs * _rhs;
    715     else if (_pW == "/") _result = _lhs / _rhs;
    716     else if (_pW == "%") _result = _lhs % _rhs;
    717     else if (_pW == "==") _result = _lhs == _rhs;
    718     else if (_pW == "!=") _result = _lhs != _rhs;
    719     else if (_pW == ">=") _result = _lhs >= _rhs;
    720     else if (_pW == "<=") _result = _lhs <= _rhs;
    721     else if (_pW == "<") _result = _lhs < _rhs;
    722     else if (_pW == ">") _result = _lhs > _rhs;
    723     else if (_pW == "|") _result = or(ev2_unsigned(_lhs), ev2_unsigned(_rhs));
    724     else if (_pW == "^") _result = xor(ev2_unsigned(_lhs), ev2_unsigned(_rhs));
    725     else if (_pW == "&") _result = and(ev2_unsigned(_lhs), ev2_unsigned(_rhs));
    726     else if (_pW == "||") _result = ev1scan_cast_bool(_lhs) || ev1scan_cast_bool(_rhs); # not lazy evaluation
    727     else if (_pW == "&&") _result = ev1scan_cast_bool(_lhs) && ev1scan_cast_bool(_rhs); # not lazy evaluation
    728     else if (_pW ~ /[-+*/%|^&]?=/) {
    729       if (and(stk[iPre, "M"], MOD_REF)) {
    730         _resultT = TYPE_NUM;
    731         if (_pW == "=") {
    732           _result = _rhs;
    733           _resultT = _rhsT;
    734         } else if (_pW == "+=") _result = _lhs + _rhs;
    735         else if (_pW == "-=") _result = _lhs - _rhs;
    736         else if (_pW == "*=") _result = _lhs * _rhs;
    737         else if (_pW == "/=") _result = _lhs / _rhs;
    738         else if (_pW == "%=") _result = _lhs % _rhs;
    739         else if (_pW == "|=") _result = or(ev2_unsigned(_lhs), ev2_unsigned(_rhs));
    740         else if (_pW == "^=") _result = xor(ev2_unsigned(_lhs), ev2_unsigned(_rhs));
    741         else if (_pW == "&=") _result = and(ev2_unsigned(_lhs), ev2_unsigned(_rhs));
    742 
    743         stk[iPre] = _result;
    744         stk[iPre, "t"] = SE_VALU;
    745         stk[iPre, "T"] = _resultT;
    746         d_data[stk[iPre, "R"]] = _result;
    747 
    748         # TODO: array copy?
    749       } else {
    750         ev2_copy(stk, iPre, stk, iVal);
    751         # err?
    752       }
    753       return;
    754     } else if (_pW == ",") {
    755       if (stk[iPre, "M"] == MOD_ARG) {
    756         stk[iPre] = _rhs;
    757         stk[iPre, "t"] = SE_VALU;
    758         stk[iPre, "T"] = _rhsT;
    759         _i = stk[iPre, "A"]++;
    760         ev2_copy(stk, iPre SUBSEP "A" SUBSEP _i, stk, iVal);
    761       } else {
    762         stk[iPre, "t"] = SE_VALU;
    763         ev2_copy(stk, iPre SUBSEP "A" SUBSEP 0, stk, iPre);
    764         ev2_copy(stk, iPre SUBSEP "A" SUBSEP 1, stk, iVal);
    765         stk[iPre] = _rhs;
    766         stk[iPre, "T"] = _rhsT;
    767         stk[iPre, "M"] = MOD_ARG;
    768         stk[iPre, "A"] = 2;
    769       }
    770       return;
    771     } else if (_pW == ".") {
    772       _a = and(stk[iVal, "M"], MOD_REF)? stk[iVal, ATTR_REF]: _rhs;
    773       stk[iPre, "t"] = SE_VALU;
    774       ev2_memget(stk, iPre, stk, iPre, _a);
    775       return;
    776     }
    777 
    778     stk[iPre] = _result;
    779     stk[iPre, "t"] = SE_VALU;
    780     stk[iPre, "T"] = _resultT;
    781     stk[iPre, "M"] = MOD_NUL;
    782   } else if (_pT == "u") {
    783     _rhs = stk[iVal];
    784 
    785     if (_pW == "+") _result = _rhs;
    786     else if (_pW == "-") _result = -_rhs;
    787     else if (_pW == "!") _result = !ev1scan_cast_bool(_rhs);
    788     else if (_pW == "++") {
    789       _result = _rhs+1;
    790       stk[iPre] = _result;
    791       stk[iPre, "t"] = SE_VALU;
    792       stk[iPre, "T"] = TYPE_NUM;
    793       if (and(stk[iVal, "M"], MOD_REF)) {
    794         stk[iPre, "M"] = MOD_REF;
    795         stk[iPre, "R"] = stk[iVal, "R"];
    796         d_data[stk[iPre, "R"]] = _result;
    797       } else {
    798         stk[iPre, "M"] = MOD_NUL;
    799       }
    800       return;
    801     } else if (_pW == "--") {
    802       _result = _rhs-1;
    803       stk[iPre] = _result;
    804       stk[iPre, "t"] = SE_VALU;
    805       stk[iPre, "T"] = TYPE_NUM;
    806       if (and(stk[iVal, "M"], MOD_REF)) {
    807         stk[iPre, "M"] = MOD_REF;
    808         stk[iPre, "R"] = stk[iVal, "R"];
    809         d_data[stk[iPre, "R"]] = _result;
    810       } else {
    811         stk[iPre, "M"] = MOD_NUL;
    812       }
    813       return;
    814     }
    815 
    816     stk[iPre] = _result;
    817     stk[iPre, "t"] = SE_VALU;
    818     stk[iPre, "T"] = TYPE_NUM;
    819     stk[iPre, "M"] = MOD_NUL;
    820   } else if (_pT == "?:") {
    821     if (_pW == "T") {
    822       stk[iPre, "t"] = SE_VALU;
    823     } else {
    824       ev2_copy(stk, iPre, stk, iVal);
    825     }
    826   } else {
    827     ev2_copy(stk, iPre, stk, iVal);
    828   }
    829 }
    830 
    831 function ev2_copy(dDict, dName, sDict, sName, _M, _t, _i, _iN) {
    832   # assertion
    833   if (sDict[sName, "t"] != SE_VALU) {
    834     print_error("mwg_pp.eval:fatal", "copying not value element");
    835   }
    836 
    837   dDict[dName] = sDict[sName];                # value
    838   _t = dDict[dName, "t"] = sDict[sName, "t"]; # sttype
    839   _M = dDict[dName, "M"] = sDict[sName, "M"]; # mod
    840 
    841   if (_t == SE_VALU)
    842     dDict[dName, "T"] = sDict[sName, "T"];  # datatype
    843 
    844   # special data
    845   if (and(_M, MOD_REF)) {
    846     # reference
    847     dDict[dName, "R"] = sDict[sName, "R"]; # name in d_data
    848   }
    849   if (and(_M, MOD_ARG) || and(_M, MOD_ARR)) {
    850     # argument/array
    851     _iN = dDict[dName, "A"] = sDict[sName, "A"]; # array length
    852     for (_i = 0; _i < _iN; _i++)
    853       ev2_copy(dDict, dName SUBSEP "A" SUBSEP _i, sDict, sName SUBSEP "A" SUBSEP _i);
    854   }
    855   if (and(_M, MOD_MTH)) {
    856     # member function
    857     dDict[dName, ATTR_MTH_MEM] = sDict[sName, ATTR_MTH_MEM];
    858     ev2_copy(dDict, dName SUBSEP ATTR_MTH_OBJ, sDict, sName SUBSEP ATTR_MTH_OBJ);
    859   }
    860 }
    861 
    862 function ev2_delete(sDict, sName) {
    863   if (sDict[sName, "t"] != SE_VALU) {
    864     print_error("mwg_pp.eval:fatal", "deleting not value element");
    865   }
    866 
    867   delete sDict[sName];     # value
    868   delete sDict[sName, "t"]; # sttype
    869   delete sDict[sName, "T"]; # datatype
    870   _M = sDict[sName, "M"];     # mod
    871   delete sDict[sName, "M"];
    872 
    873   # special data
    874   if (_M == MOD_REF) {
    875     # reference
    876     delete sDict[sName, "R"]; # name in d_data
    877   } else if (_M == MOD_ARG || _M == MOD_ARR) {
    878     # argument/array
    879     _iN = sDict[sName, "A"];
    880     delete sDict[sName, "A"]; # array length
    881     for (_i = 0; _i < _iN; _i++)
    882       ev2_delete(sDict, sName SUBSEP "A" SUBSEP _i);
    883   }
    884 }
    885 
    886 # TODO? Dict[sp]       -> Dict[sp, "v"]
    887 # TODO? s[i, "c"]="b+" -> s[i, "k"]="b" s["o"]="+"
    888 
    889 #===============================================================================
    890 #  Parameter Expansion
    891 #-------------------------------------------------------------------------------
    892 function inline_expand(text, _, _ret, _ltext, _rtext, _mtext, _name, _r, _s, _a, _caps) {
    893   _ret = "";
    894   while (match(text, /\${([^{}]|\\.)+}|\$"([^"]|\\.)+"/) > 0) {
    895     _ltext = substr(text, 1, RSTART - 1);
    896     _mtext = substr(text, RSTART, RLENGTH);
    897     _rtext = substr(text, RSTART + RLENGTH);
    898     _name = unescape(slice(_mtext, 2, -1));
    899     if (match(_name, /^[-a-zA-Z0-9_]+$/) > 0) {                     # ${key}
    900       _r = "" d_data[_name];
    901     } else if (match(_name, /^[-a-zA-Z0-9_]+:-/) > 0) {             # ${key:-alter}
    902       _s["key"] = slice(_name, 0, RLENGTH - 2);
    903       _s["alter"] = slice(_name, RLENGTH);
    904       _r = "" d_data[_s["key"]];
    905       if (_r == "") _r = _s["alter"];
    906     } else if (match(_name, /^[-a-zA-Z0-9_]+:\+/) > 0) {            # ${key:+value}
    907       _s["key"] = slice(_name, 0, RLENGTH - 2);
    908       _s["value"] = slice(_name, RLENGTH);
    909       _r = "" d_data[_s["key"]];
    910       _r = _r == "" ? "" : _s["value"];
    911     } else if (match(_name, /^[-a-zA-Z0-9_]+:\?/) > 0) {            # ${key:?warn}
    912       _s["key"] = slice(_name, 0, RLENGTH - 2);
    913       _s["warn"] = slice(_name, RLENGTH);
    914       _r = "" d_data[_s["key"]];
    915       if (_r == "") {
    916         print "(parameter expansion:" _mtext ")! " _s["warn"] > "/dev/stderr"
    917         _ltext = _ltext _mtext;
    918         _r = "";
    919       }
    920     } else if (match(_name, /^([-a-zA-Z0-9_]+):([0-9]+):([0-9]+)$/, _caps) > 0) { # ${key:start:length}
    921       _r = substr(d_data[_caps[1]], _caps[2] + 1, _caps[3]);
    922     } else if (match(_name, /^([-a-zA-Z0-9_]+)(\/\/?)(([^/]|\\.)+)\/(.*)$/, _caps) > 0) { # ${key/before/after}
    923       _r = d_data[_caps[1]];
    924       if (_caps[3] == "/")
    925         sub(_caps[3], _caps[5], _r);
    926       else
    927         gsub(_caps[3], _caps[5], _r);
    928     } else if (match(_name, /^([-a-zA-Z0-9_]+)(##?|%%?)(.+)$/, _caps) > 0) { # ${key#head} ${key%tail}
    929       if (length(_caps[2]) == 2) {
    930         # TODO
    931         gsub(/\./, /\./, _caps[3]);
    932         gsub(/\*/, /.+/, _caps[3]);
    933         gsub(/\?/, /./, _caps[3]);
    934       }
    935       if (_caps[2] == "#" || _caps[2] == "##") {
    936         _caps[3] = "^" _caps[3];
    937       } else {
    938         _caps[3] = _caps[3] "$";
    939       }
    940 
    941       _r = d_data[_caps[1]];
    942       sub(_caps[3], "", _r);
    943     } else if (match(_name, /^#[-a-zA-Z0-9_]+$/) > 0) {             # ${#key}
    944       _r = length("" d_data[substr(_name, 2)]);
    945     } else if (match(_name, /^([-a-zA-Z0-9_]+)(\..+)$/, _caps) > 0) { # ${key.modifiers}
    946       _r = modify_text(d_data[_caps[1]], _caps[2]);
    947     } else if (match(_name, /^\.[-a-zA-Z0-9_]+./) > 0) {             # ${.function:args...}
    948       match(_name, /^\.[-a-zA-Z0-9_]+./);
    949       _s["i"] = RLENGTH;
    950       _s["func"] = substr(_name, 2, _s["i"] - 2);
    951       _s["sep"] =substr(_name, _s["i"], 1);
    952       _s["args"] = substr(_name, _s["i"] + 1);
    953       _s["argc"] = split(_s["args"], _a, _s["sep"]);
    954       if (_s["func"] == "for" && _s["argc"] == 5) {
    955         _r = inline_function_for(_a);
    956       } else if (_s["func"] == "sep_for" && _s["argc"] == 5) {
    957         _r = inline_function_sepfor(_a);
    958       } else if (_s["func"] == "for_sep" && _s["argc"] == 5) {
    959         _r = inline_function_forsep(_a);
    960       } else if (_s["func"] == "eval" && _s["argc"] == 1) {
    961         _r = inline_function_eval(_a);
    962       } else {
    963         print "(parameter function:" _s["func"] ")! unrecognized function." > "/dev/stderr";
    964         _r = _mtext;
    965       }
    966     } else {
    967       print "(parameter expansion:" _mtext ")! unrecognized expansion." > "/dev/stderr";
    968       _r = _mtext;
    969     }
    970 
    971     if (_mtext ~ /^\${/) {
    972       # enable re-expansion ${}
    973       _ret = _ret _ltext;
    974       text = _r _rtext;
    975     } else {
    976       # disable re-expansion $""
    977       _ret = _ret _ltext _r;
    978       text = _rtext;
    979     }
    980   }
    981   return _ret text;
    982 }
    983 
    984 function inline_function_forsep(args, _, _r, _sep) {
    985   _sep = args[5];
    986   _r = inline_function_for(args);
    987   return _r == "" ? "" : _r _sep;
    988 }
    989 function inline_function_sepfor(args, _, _r, _sep) {
    990   _sep = args[5];
    991   _r = inline_function_for(args);
    992   return _r == "" ? "" : _sep _r;
    993 }
    994 function inline_function_for(args, _, _rex_i, _i0, _iM, _field, _sep, _i, _r, _t) {
    995   # ${for:%i%:1:9:typename A%i%:,}
    996   _rex_i = args[1];
    997   _i0 = int(eval_expr(args[2]));
    998   _iM = int(eval_expr(args[3]));
    999   _field = args[4];
   1000   _sep = args[5];
   1001 
   1002   _r = "";
   1003   for (_i = _i0; _i < _iM; _i++) {
   1004     _t = _field;
   1005     gsub(_rex_i, _i, _t);
   1006     _r = _i == _i0?_t:_r _sep _t;
   1007   }
   1008   return _r;
   1009 }
   1010 function inline_function_eval(args) {
   1011   return eval_expr(args[1]);
   1012 }
   1013 
   1014 #===============================================================================
   1015 #   mwg.pp text modification
   1016 #-------------------------------------------------------------------------------
   1017 function modify_text__replace0(text, before, after, flags) {
   1018   if (index(flags, "R")) {
   1019     return replace(text, before, after);
   1020   } else {
   1021     gsub(before, after, text);
   1022     return text;
   1023   }
   1024 }
   1025 
   1026 function modify_text__replace(content, before, after, flags) {
   1027   if (index(flags, "m")) {
   1028     _jlen = split(content, _lines, "\n");
   1029     content = modify_text__replace0(_lines[1], before, after, flags);
   1030     #print_error("mwg_pp(modify_text)", "replace('" _lines[1] "','" before "','" after "') = '" content "'");
   1031     for (_j = 1; _j < _jlen; _j++)
   1032       content = content "\n" modify_text__replace0(_lines[_j + 1], before, after, flags);
   1033   } else {
   1034     content = modify_text__replace0(content, before, after, flags);
   1035   }
   1036   return content;
   1037 }
   1038 
   1039 function modify_text(content, args, _len, _i, _m, _s, _c, _j, _jlen, _lines) {
   1040   # _len: length of args
   1041   # _i: index in args
   1042   # _c: current character in args
   1043   # _m: current context mode in args
   1044   # _s: data store
   1045 
   1046   _m = "";
   1047   _len = length(args);
   1048   for (_i = 0; _i < _len; _i++) {
   1049     _c = substr(args, _i + 1, 1);
   1050     #-------------------------------------------------
   1051     if (_m == "c") {
   1052       if (_c == "r" || _c == "R") {
   1053         _m = "r0";
   1054         _s["flags"] = _c == "R"?_c:"";
   1055         _s["sep"] = "";
   1056         _s["rep_before"] = "";
   1057         _s["rep_after"] = "";
   1058       } else if (_c == "f") {
   1059         _m = "f0";
   1060         _s["sep"] = "";
   1061         _s["for_var"] = "";
   1062         _s["for_begin"] = "";
   1063         _s["for_end"] = "";
   1064       } else if (_c == "i") {
   1065         content = inline_expand(content);
   1066         _m = "";
   1067       } else {
   1068         print "unrecognized expand fun '" _c "'" > "/dev/stderr"
   1069       }
   1070     #-------------------------------------------------
   1071     # r, R: replace
   1072     } else if (_m == "r0") {
   1073       _s["sep"] = _c;
   1074       _m = "r1";
   1075     } else if (_m == "r1") {
   1076       if (_c == _s["sep"]) {
   1077         _m = "r2";
   1078       } else {
   1079         _s["rep_before"] = _s["rep_before"] _c;
   1080       }
   1081     } else if (_m == "r2") {
   1082       if (_c == _s["sep"]) {
   1083 
   1084         # check flag m
   1085         _c = substr(args, _i + 2, 1);
   1086         if (_c == "m") {
   1087           _s["flags"] = _s["flags"] "m";
   1088           _i++;
   1089         }
   1090 
   1091         content = modify_text__replace(content, _s["rep_before"], _s["rep_after"], _s["flags"]);
   1092         _m = "";
   1093       } else {
   1094         _s["rep_after"] = _s["rep_after"] _c;
   1095       }
   1096     #-------------------------------------------------
   1097     # f: for
   1098     } else if (_m == "f0") {
   1099       _s["sep"] = _c;
   1100       _m = "f1";
   1101     } else if (_m == "f1") {
   1102       if (_c != _s["sep"]) {
   1103         _s["for_var"] = _s["for_var"] _c;
   1104       } else {
   1105         _m = "f2";
   1106       }
   1107     } else if (_m == "f2") {
   1108       if (_c != _s["sep"]) {
   1109         _s["for_begin"] = _s["for_begin"] _c;
   1110       } else {
   1111         _s["for_begin"] = int(eval_expr(_s["for_begin"]));
   1112         _m = "f3";
   1113       }
   1114     } else if (_m == "f3") {
   1115       if (_c != _s["sep"]) {
   1116         _s["for_end"] = _s["for_end"] _c;
   1117       } else {
   1118         _s["for_end"] = int(eval_expr(_s["for_end"]));
   1119         _m = "";
   1120 
   1121         _s["content"] = content;
   1122         content = "";
   1123         for (_s["i"] = _s["for_begin"]; _s["i"] < _s["for_end"]; _s["i"]++) {
   1124           _c = _s["content"];
   1125           gsub(_s["for_var"], _s["i"], _c);
   1126           content = content _c;
   1127         }
   1128       }
   1129     #-------------------------------------------------
   1130     } else {
   1131       if (_c == ".") {
   1132         _m = "c";
   1133       } else if (_c ~ /[/#]/) {
   1134         break;
   1135       } else if (_c !~ /[ \t\r\n]/) {
   1136         print "unrecognized expand cmd '" _c  "'" > "/dev/stderr"
   1137       }
   1138     }
   1139   }
   1140 
   1141   return content;
   1142 }
   1143 #===============================================================================
   1144 #   mwg.pp commands
   1145 #-------------------------------------------------------------------------------
   1146 function range_begin(cmd, arg) {
   1147   d_level++;
   1148   d_rstack[d_level, "c"] = cmd;
   1149   d_rstack[d_level, "a"] = arg;
   1150   d_content[d_level] = "";
   1151   d_content[d_level, "L"] = "";
   1152   d_content[d_level, "F"] = "";
   1153 }
   1154 function range_end(args, _cmd, _arg, _txt, _clines, _cfiles) {
   1155   if (d_level == 0) {
   1156     print "mwg_pp.awk:#%}: no matching range beginning" > "/dev/stderr"
   1157     return;
   1158   }
   1159 
   1160   # pop data
   1161   _cmd = d_rstack[d_level, "c"];
   1162   _arg = d_rstack[d_level, "a"];
   1163   _txt = d_content[d_level];
   1164   if (m_lineno) { # 20120726
   1165     _clines = d_content[d_level, "L"];
   1166     _cfiles = d_content[d_level, "F"];
   1167   }
   1168   d_level--;
   1169 
   1170   if (args != "")
   1171     _txt = modify_text(_txt, args);
   1172 
   1173   # process
   1174   if (_cmd == "define") {
   1175     d_data[_arg] = _txt;
   1176     if (m_lineno) { # 20120726
   1177       d_data[_arg, "L"] = _clines;
   1178       d_data[_arg, "F"] = _cfiles;
   1179     }
   1180   } else if (_cmd == "expand" || _cmd == "IF1" || _cmd == "IF4") {
   1181     process_multiline(_txt, _clines, _cfiles); # 20120726
   1182   } else if (_cmd == "none" || _cmd ~ /IF[023]/) {
   1183     # do nothing
   1184   } else {
   1185     print "mwg_pp.awk:#%}: unknown range beginning '" _cmd " ( " _arg " )'" > "/dev/stderr"
   1186   }
   1187 }
   1188 
   1189 function dctv_define(args, _, _cap, _name, _name2) {
   1190   if (match(args, /^([-A-Za-z0-9_:]+)[[:space:]]*(\([[:space:]]*)?$/, _cap) > 0) {
   1191     # dctv: #%define hoge
   1192     # dctv: #%define hoge (
   1193     _name = _cap[1];
   1194     if (_name == "end")
   1195       range_end("");
   1196     else
   1197       range_begin("define", _name);
   1198   } else if (match(args, /^([-_:[:alnum:]]+)[[:space:]]+([-_:[:alnum:]]+)(.*)$/, _cap) > 0) {
   1199     # dctv: #%define a b.mods
   1200     _name = _cap[1];
   1201     _name2 = _cap[2];
   1202     _args = trim(_cap[3]);
   1203     if (_args != "")
   1204       d_data[_name] = modify_text(d_data[_name2], _args);
   1205     else
   1206       d_data[_name] = d_data[_name2];
   1207 
   1208     if (m_lineno) {
   1209       d_data[_name, "L"] = d_data[_name2, "L"];
   1210       d_data[_name, "F"] = d_data[_name2, "F"];
   1211     }
   1212   } else {
   1213     print "mwg_pp.awk:#%define: missing data name" > "/dev/stderr"
   1214     return;
   1215   }
   1216 }
   1217 
   1218 # 状態は何種類あるか?
   1219 #     END      CONDT CONDF ELSE
   1220 # IF0 出力せず IF1   IF0   IF4  (not matched)
   1221 # IF1 出力する IF2   IF2   IF3  (matched)
   1222 # IF2 出力せず IF2   IF2   IF3  (finished)
   1223 # IF3 出力せず !IF3  !IF3  !IF3 (else unmatched) 旧 "el0"
   1224 # IF4 出力する !IF3  !IF3  !IF3 (else matched)   旧 "el1"
   1225 
   1226 function dctv_if(cond, _, _cap) {
   1227   gsub(/^[ \t]+|[ \t]*(\([ \t]*)?$/, "", cond);
   1228   if (cond != "") {
   1229     #print "dbg: if( "cond " ) -> " eval_expr(cond) > "/dev/stderr"
   1230     if (cond == "end") {
   1231       range_end("");
   1232     } else if (eval_expr(cond)) {
   1233       range_begin("IF1");
   1234     } else {
   1235       range_begin("IF0");
   1236     }
   1237   } else {
   1238     print "mwg_pp.awk:#%define: missing data name" > "/dev/stderr"
   1239     return;
   1240   }
   1241 }
   1242 function dctv_elif(cond, _cmd) {
   1243   if (d_level == 0) {
   1244     print "mwg_pp.awk:#%elif: no matching if directive" > "/dev/stderr"
   1245     return;
   1246   }
   1247 
   1248   _cmd = d_rstack[d_level, "c"];
   1249   if (_cmd ~ /IF[0-4]/) {
   1250     range_end("");
   1251     if (_cmd == "IF0") {
   1252       if (eval_expr(cond)) {
   1253         range_begin("IF1");
   1254       } else {
   1255         range_begin("IF0");
   1256       }
   1257     } else if (_cmd ~ /IF[12]/) {
   1258       range_begin("IF2");
   1259     } else {
   1260       range_begin("IF3");
   1261       if (_cmd ~ /IF[34]/)
   1262         print_error("mwgpp:#%else", "if clause have already ended!");
   1263     }
   1264   } else {
   1265     print_error("mwgpp:#%else", "no matching if directive");
   1266   }
   1267 }
   1268 function dctv_else(_, _cap, _cmd) {
   1269   if (d_level == 0) {
   1270     print_error("mwgpp:#%else", "no matching if directive");
   1271     return;
   1272   }
   1273 
   1274   _cmd = d_rstack[d_level, "c"];
   1275   if (_cmd ~ /IF[0-4]/) {
   1276     range_end("");
   1277     if (_cmd == "IF0") {
   1278       range_begin("IF4");
   1279     } else {
   1280       range_begin("IF3");
   1281       if (_cmd ~ /IF[34]/)
   1282         print_error("mwgpp:#%else", "if clause have already ended!");
   1283     }
   1284   } else {
   1285     print_error("mwgpp:#%else", "no matching if directive");
   1286   }
   1287 }
   1288 
   1289 function dctv_expand(args, _, _cap, _txt, _type) {
   1290   if (match(args, /^([-a-zA-Z:0-9_]+|[\(])(.*)$/, _cap) > 0) {
   1291     if (_cap[1] == "(") {
   1292       _type = 1;
   1293     } else {
   1294       _txt = d_data[_cap[1]];
   1295       _txt = modify_text(_txt, _cap[2]);
   1296       process_multiline(_txt, d_data[_cap[1], "L"], d_data[_cap[1], "F"]);
   1297     }
   1298   } else if (match(args, /^[[:space:]]*$/) > 0) {
   1299     _type = 1;
   1300   } else {
   1301     print "mwg_pp.awk:#%expand: missing data name" > "/dev/stderr"
   1302     return;
   1303   }
   1304 
   1305   if (_type == 1) {
   1306     # begin expand
   1307     range_begin("expand", _cap[2]); # _cap[2] not used
   1308   }
   1309 }
   1310 
   1311 function dctv_modify(args, _, _i, _len, _name, _content) {
   1312   _i = match(args, /[^-a-zA-Z:0-9_]/);
   1313   _len = _i?_i - 1:length(args);
   1314   _name = substr(args, 1, _len);
   1315   args = trim(substr(args, _len + 1));
   1316 
   1317   d_data[_name] = modify_text(d_data[_name], args);
   1318 }
   1319 
   1320 function include_file(file, _line, _lines, _i, _n, _dir, _originalFile, _originalLine) {
   1321   if (file ~ /^<.+>$/) {
   1322     gsub(/^<|>$/, "", file);
   1323     file = INCLUDE_DIRECTORY "/" file;
   1324   } else {
   1325     gsub(/^"|"$/, "", file);
   1326     if (file !~ /^\//) {
   1327       _dir = awk_getfiledir();
   1328       if (_dir != "") file = _dir "/" file;
   1329     }
   1330   }
   1331 
   1332   _n = 0;
   1333   while ((_r = getline _line < file) >0)
   1334     _lines[_n++] = _line;
   1335   if (_r < 0)
   1336     print_error("could not open the include file '" file "'");
   1337   close(file);
   1338 
   1339   dependency_add(file);
   1340 
   1341   _originalFile = m_lineno_cfile;
   1342   _originalLine = m_lineno_cline;
   1343   for (_i = 0; _i < _n; _i++) {
   1344     m_lineno_cfile = file; # 20120726
   1345     m_lineno_cline = _i + 1; # 20120726
   1346     process_line(_lines[_i]);
   1347   }
   1348   m_lineno_cfile = _originalFile; # 2015-01-24
   1349   m_lineno_cline = _originalLine; # 2015-01-24
   1350 }
   1351 
   1352 function dctv_error(message, _title) {
   1353   if (m_lineno_cfile != "" || m_lineno)
   1354     _title = m_lineno_cfile ":" m_lineno_cline;
   1355   else
   1356     _title = FILENAME;
   1357 
   1358   print_error(_title, message);
   1359 }
   1360 
   1361 #===============================================================================
   1362 function data_define(pair, _sep, _i, _k, _v, _capt, _rex) {
   1363   if (pair ~ /^[^\(_a-zA-Z0-9]/) { # #%data/name/value/
   1364 
   1365     _sep = "\\" substr(pair, 1, 1);
   1366     _rex = "^" _sep "([^" _sep "]+)" _sep "([^" _sep "]+)" _sep
   1367     if (match(pair, _rex, _capt)) {
   1368       _k = _capt[1];
   1369       _v = _capt[2];
   1370       d_data[_k] = _v;
   1371     } else {
   1372       printf("(#%%data directive)! ill-formed. (pair=%s, _rex=%s)\n", pair, _rex) > "/dev/stderr"
   1373       return 0;
   1374     }
   1375   } else { # #%data name value
   1376     # #%data(=) name=value
   1377     _sep = "";
   1378     if (match(pair, /^\([^\)]+\)/) > 0) {
   1379       _sep = substr(pair, 2, RLENGTH - 2);
   1380       pair = trim(substr(pair, RLENGTH + 1));
   1381     }
   1382 
   1383     _i = _sep != ""?index(pair, _sep):match(pair, /[ \t]/);
   1384     if (_i <= 0) {
   1385       printf("(#%%data directive)! ill-formed. (pair=%s, _sep=%s)\n", pair, _sep) > "/dev/stderr"
   1386       return 0;
   1387     }
   1388 
   1389     _k = substr(pair, 1, _i - 1);
   1390     _v = trim(substr(pair, _i + length(_sep)))
   1391     d_data[_k] = _v;
   1392 
   1393     #_t[0]; head_token(pair, _t);
   1394     #d_data[_t[0]] = _t[1];
   1395   }
   1396 }
   1397 function data_print(key) {
   1398   add_line(d_data[key]);
   1399 }
   1400 function execute(command, _line, _caps, _n, _cfile) {
   1401   if (match(command, /^(>>?)[[:space:]]*([^[:space:]]*)/, _caps) > 0) {
   1402     # 出力先の変更
   1403     fflush(m_outpath);
   1404     m_outpath = _caps[2];
   1405     m_addline_cfile = "";
   1406     if (_caps[1] == ">" && m_outpath != "") {
   1407       printf("") > m_outpath
   1408     }
   1409   } else {
   1410     _n = 0;
   1411     while ((command | getline _line) > 0)
   1412       _lines[_n++] = _line;
   1413     close(command);
   1414 
   1415     _cfile = "$(" command ")";
   1416     gsub(/[\\"]/, "\\\\&", _cfile);
   1417     for (_i = 0; _i < _n; _i++) {
   1418       m_lineno_cfile = _cfile;
   1419       m_lineno_cline = _i + 1;
   1420       process_line(_lines[_i]);
   1421     }
   1422   }
   1423 }
   1424 #===============================================================================
   1425 function add_line(line) {
   1426   if (d_level == 0) {
   1427     if (m_lineno) { # 20120726
   1428       if (m_addline_cfile != m_lineno_cfile||++m_addline_cline != m_lineno_cline) {
   1429         m_addline_cline = m_lineno_cline;
   1430         m_addline_cfile = m_lineno_cfile;
   1431         if (m_addline_cline != "" && m_addline_cfile != "") {
   1432           if (m_outpath == "")
   1433             print "#line " m_addline_cline " \"" m_addline_cfile "\""
   1434           else
   1435             print "#line " m_addline_cline " \"" m_addline_cfile "\"" >> m_outpath
   1436         }
   1437       }
   1438     }
   1439 
   1440     if (m_outpath == "")
   1441       print line
   1442     else
   1443       print line >> m_outpath
   1444   } else {
   1445     d_content[d_level] = d_content[d_level] line "\n"
   1446     d_content[d_level, "L"] = d_content[d_level, "L"] m_lineno_cline "\n";
   1447     d_content[d_level, "F"] = d_content[d_level, "F"] m_lineno_cfile "\n";
   1448   }
   1449 }
   1450 
   1451 # function process_multiline2(txt, cline, cfile, _s, _l, _f, _len, _i) {
   1452 #   _len = split(txt, _s, "\n");
   1453 #   if (length(_s[_len]) == 0) _len--;
   1454 
   1455 #   split(clines, _l, "\n");
   1456 #   split(cfiles, _f, "\n");
   1457 #   for (_i = 0; _i < _len; _i++) {
   1458 #     m_lineno_cline = _l[_i + 1];
   1459 #     m_lineno_cfile = _f[_i + 1];
   1460 #     process_line(_s[_i + 1]);
   1461 #   }
   1462 # }
   1463 
   1464 function process_multiline(txt, clines, cfiles, _, _s, _l, _f, _len, _i) {
   1465   _len = split(txt, _s, "\n");
   1466   if (length(_s[_len]) == 0) _len--;
   1467 
   1468   split(clines, _l, "\n");
   1469   split(cfiles, _f, "\n");
   1470   for (_i = 0; _i < _len; _i++) {
   1471     m_lineno_cline = _l[_i + 1];
   1472     m_lineno_cfile = _f[_i + 1];
   1473     process_line(_s[_i + 1]);
   1474   }
   1475 }
   1476 
   1477 function process_line(line, _line, _text, _ind, _len, _directive, _cap) {
   1478   _line = line;
   1479 
   1480   sub(/^[ \t]+/, "", _line);
   1481   sub(/[ \t\r]+$/, "", _line);
   1482   if (m_comment_cpp)
   1483     sub(/^\/\//, "#", _line);
   1484   if (m_comment_pragma)
   1485     sub(/^[[:space:]]*#[[:space:]]*pragma/, "#", _line);
   1486   if (m_comment_c && match(_line, /^\/\*(.+)\*\/$/, _cap) > 0)
   1487     _line = "#" _cap[1];
   1488 
   1489   if (_line ~ /^#%[^%]/) {
   1490     # cut directive
   1491     if (match(_line, /^#%[ \t]*([-a-zA-Z_0-9:]+)(.*)$/, _cap) > 0) {
   1492       _directive = _cap[1];
   1493       _text = trim(_cap[2]);
   1494     } else if (match(_line, /^#%[ \t]*([^-a-zA-Z_0-9:])(.*)$/, _cap) > 0) {
   1495       _directive = _cap[1];
   1496       _text = trim(_cap[2]);
   1497     } else {
   1498       print_error("unrecognized directive line: " line);
   1499       return;
   1500     }
   1501 
   1502     # switch directive
   1503     if (_directive == "(" || _directive == "begin") {
   1504       range_begin("none", _text);
   1505     } else if (_directive == ")" || _directive == "end") {
   1506       range_end(_text);
   1507     } else if (_directive == "define" || _directive == "m") {
   1508       dctv_define(_text);
   1509     } else if (_directive == "expand" || _directive == "x") {
   1510       dctv_expand(_text);
   1511     } else if (_directive == "if") {
   1512       dctv_if(_text);
   1513     } else if (_directive == "else") {
   1514       dctv_else(_text);
   1515     } else if (_directive == "elif") {
   1516       dctv_elif(_text);
   1517     } else if (_directive == "modify") { # obsoleted. use #%define name name.mods
   1518       print_error("obsoleted directive modify");
   1519       dctv_modify(_text);
   1520     } else if (_directive == "include" || _directive == "<") {
   1521       include_file(_text);
   1522     } else if (_directive == "data") { # obs → データ設定に有意。残す?
   1523       data_define(_text);
   1524     } else if (_directive == "print") { #obs
   1525       data_print(_text);
   1526     } else if (_directive == "eval") {
   1527       eval_expr(_text);
   1528     } else if (_directive == "[" && match(_text, /^(.+)\]$/, _cap) > 0) {
   1529       eval_expr(_cap[1]);
   1530     } else if (_directive == "exec" || _directive == "$") {
   1531       execute(_text);
   1532     } else if (_directive == "#") {
   1533       # comment. just ignored.
   1534     } else if (_directive == "error") {
   1535       dctv_error(_text);
   1536     } else {
   1537       print_error("unrecognized directive " _directive);
   1538     }
   1539   } else if (_line ~ /^##+%/) {
   1540     add_line(substr(_line, 2));
   1541   } else if (_line ~ /^#%%+/) {
   1542     add_line("#" substr(_line, 3));
   1543   } else {
   1544     add_line(line);
   1545   }
   1546 }
   1547 
   1548 BEGIN{
   1549   FS = "MWG_PP" "_COMMENT";
   1550   ev1scan_init();
   1551   d_level = 0;
   1552   d_data[0] = "";
   1553 
   1554   m_outpath = "";
   1555   m_comment_c      = int(ENVIRON["PPC_C"]) != 0;
   1556   m_comment_cpp    = int(ENVIRON["PPC_CPP"]) != 0;
   1557   m_comment_pragma = int(ENVIRON["PPC_PRAGMA"]) != 0;
   1558 
   1559   m_lineno         = int(ENVIRON["PPLINENO"]) != 0;
   1560 
   1561   INCLUDE_DIRECTORY = ENVIRON["HOME"] "/.mwg/mwgpp/include"
   1562 
   1563   m_dependency_count = 0
   1564   m_dependency_guard[""] = 1;
   1565   m_dependency[0] = "";
   1566 }
   1567 
   1568 {
   1569   if (FNR == 1) {
   1570     if (ENVIRON["PPLINENO_FILE"] != "")
   1571       m_rfile = ENVIRON["PPLINENO_FILE"];
   1572     else
   1573       m_rfile = FILENAME;
   1574     dependency_add(m_rfile);
   1575   }
   1576   m_lineno_cfile = m_rfile;
   1577   m_lineno_cline = FNR;
   1578   process_line($1);
   1579 }
   1580 
   1581 function dependency_add(file) {
   1582   if (!m_dependency_guard[file]) {
   1583     m_dependency_guard[file] = 1;
   1584     m_dependency[m_dependency_count++] = file;
   1585   }
   1586 }
   1587 function dependency_generate(output, target, is_phony, _i, _iMax, _line, _file) {
   1588   if (!target) {
   1589     target = m_rfile;
   1590     sub(/\.pp$/, "", target);
   1591     target = target ".out";
   1592   }
   1593 
   1594   if (m_dependency_count == 0)
   1595     print target ":" > output;
   1596   else {
   1597     _iMax = m_dependency_count - 1;
   1598     for (_i = 0; _i < m_dependency_count; _i++) {
   1599       _file = m_dependency[_i];
   1600       gsub(/[[:space:]]/, "\\\\&", _file);
   1601       _line = _i == 0? target ": ": "  ";
   1602       _line = _line _file;
   1603       if (_i < _iMax) _line = _line " \\";
   1604       print _line > output;
   1605     }
   1606 
   1607     if (is_phony) {
   1608       for (_i = 0; _i < m_dependency_count; _i++) {
   1609         _file = m_dependency[_i];
   1610         gsub(/[[:space:]]/, "\\\\&", _file);
   1611         printf("%s:\n\n", _file) > output;
   1612       }
   1613     }
   1614   }
   1615 }
   1616 
   1617 END {
   1618   # output dependencies
   1619   DEPENDENCIES_OUTPUT = ENVIRON["DEPENDENCIES_OUTPUT"];
   1620   if (DEPENDENCIES_OUTPUT) {
   1621     is_phony = ENVIRON["DEPENDENCIES_PHONY"];
   1622     dependency_generate(DEPENDENCIES_OUTPUT, ENVIRON["DEPENDENCIES_TARGET"], is_phony);
   1623   }
   1624 
   1625   if (global_errorCount) exit(1);
   1626 }