home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume44 / ibpag2 / part03 / iacc.ibp next >
Encoding:
Text File  |  1994-09-25  |  11.9 KB  |  498 lines

  1. ############################################################################
  2. #
  3. #    Name:     iacc.ibp
  4. #
  5. #    Title:     YACC-like front-end for Ibpag2 (experimental)
  6. #
  7. #    Author:     Richard L. Goerwitz
  8. #
  9. #    $Revision: 1.7 $
  10. #
  11. ############################################################################
  12. #
  13. #  Summary:
  14. #
  15. #      Iacc is a YACC-like Ibpag2 preprocessor (very experimental).
  16. #  Iacc simply reads &input (assumed to be a YACC file, but with Icon
  17. #  code in the action fields), and writes an Ibpag2 file to &output.
  18. #
  19. ############################################################################
  20. #
  21. #  Installation:
  22. #
  23. #      This file is not an Icon file, but rather an Ibpag2 file.  You
  24. #  must have Ibpag2 installed in order to run it.  To create the iacc
  25. #  executable, first create iacc.icn by typing "ibpag2 -f iacc.ibp -o
  26. #  iacc.icn," then compile iacc.icn as you would any other Icon file
  27. #  to create iacc (or on systems without direct execution, iacc.icx).
  28. #  Put more simply, iacc.ibp not only outputs Ibpag2 files, but is
  29. #  itself generated using Ibpag2 + icon{t,c}.  Note that when you com-
  30. #  pile iacc.icn under Icon version 9.0 or better you must enable
  31. #  string invocation (icont -f s).
  32. #
  33. ############################################################################
  34. #
  35. #  Implementation notes:
  36. #
  37. #      Iacc uses an YACC grammar that is actually LR(2), and not
  38. #  LR(1), as Ipbag2 would normally require in standard mode.  Iacc
  39. #  obtains the additional token lookahead via the lexical analyzer.
  40. #  The place it uses that lookahead is when it sees an identifier.  If
  41. #  the next token is a colon, then it is the LHS of a rule (C_IDENT
  42. #  below); otherwise it's an IDENT in the RHS of some rule.  Crafting
  43. #  the lexical analyzer in this fashion makes semicolons totally
  44. #  superfluous (good riddance!), but it makes it necessary for the
  45. #  lexical analyzer to suspend some dummy tokens whose only purpose is
  46. #  to make sure that it doesn't eat up C or Icon action code while
  47. #  trying to satisfy the grammar's two-token lookahead requirements
  48. #  (see how RCURL and '}' are used below in the cdef and act
  49. #  productions).
  50. #
  51. #      Iacc does its work by making six basic changes to the input
  52. #  stream: 1) puts commas between tokens and symbols in rules, 2)
  53. #  removes superfluous union and type declarations/tags, 3) inserts
  54. #  "epsilon" into the RHS of empty rules, 4) turns "$$ = x" into
  55. #  "return x", 5) rewrites rules so that all actions appear at the end
  56. #  of a production, and 6) strips all comments.
  57. #
  58. #      Although Iacc is really meant for grammars with Icon action
  59. #  code, Iacc can, in fact, accept straight YACC files, with C action
  60. #  code.  There isn't much point to using it this way, though, since
  61. #  its output is not meant to be human readable.  Rather, it is to be
  62. #  passed directly to Ibpag2 for processing.  Iacc is simply a YACCish
  63. #  front end.  Its output can be piped directly to Ibpag2 in most
  64. #  cases:  iacc < infile.iac | ibpag2 > infile.icn.
  65. #
  66. ############################################################################
  67. #
  68. #  Links: longstr, strings
  69. #  See also: ibpag2
  70. #
  71. ############################################################################
  72.  
  73. %{
  74.  
  75. link strings, longstr
  76. global newrules, lval, symbol_no
  77.  
  78. %}
  79.  
  80. # basic entities
  81. %token C_IDENT, IDENT    # identifiers and literals
  82. %token NUMBER            # [0-9]+
  83.  
  84. # reserved words:  %type -> TYPE, %left -> LEFT, etc.
  85. %token LEFT, RIGHT, NONASSOC, TOKEN, PREC, TYPE, START, UNION
  86.  
  87. # miscellaneous
  88. %token MARK   # %%
  89. %token LCURL  # %{
  90. %token RCURL  # dummy token used to start processing of C code
  91.  
  92. %start yaccf
  93.  
  94. %%
  95.  
  96. yaccf    : front, back
  97. front    : defs, MARK        { write(arg2) }
  98. back    : rules, tail        {
  99.                   every write(!\newrules)
  100.                   if write(\arg2) then
  101.                       every write(!&input)
  102.                 }
  103. tail    : epsilon        { return &null }
  104.     | MARK            { return arg1 }
  105.  
  106. defs    : epsilon
  107.     | defs, def        { write(\arg2) }
  108.     | defs, cdef        { write(\arg2) }
  109.  
  110. def    : START, IDENT        { return arg1 || " " || arg2 }
  111.     | rword, tag, nlist    {
  112.                   if arg1 == "%type"
  113.                   then return &null
  114.                   else return arg1 || " " || arg3
  115.                 }
  116. cdef    : stuff, RCURL, RCURL    { return arg1 }
  117. stuff    : UNION            { get_icon_code("%}"); return &null }
  118.     | LCURL            { return "%{ " || get_icon_code("%}") }
  119.  
  120. rword    : TOKEN    | LEFT | RIGHT | NONASSOC | TYPE
  121.  
  122. tag    : epsilon        { return &null }
  123.     | '<', IDENT, '>'    { return "<" || arg2 || ">" }
  124.  
  125. nlist    : nmno            { return arg1 }
  126.     | nlist, nmno        { return arg1 || ", " || arg2 }
  127.     | nlist, ',', nmno    { return arg1 || ", " || arg3 }
  128.  
  129. nmno    : IDENT            { return arg1 }
  130.     | IDENT, NUMBER        { return arg1 }
  131.     
  132. rules    : LHS, ':', RHS        { write(arg1, "\t: ", arg3) }
  133.     | rules, rule        { write(arg2) }
  134.  
  135. RHS    : rbody, prec        { return arg1 || " " || arg2 }
  136.  
  137. rule    : LHS, '|', RHS        { return "\t| " || arg3 }
  138.     | LHS, ':', RHS        { return arg1 || "\t: " || arg3 }
  139.  
  140. LHS    : C_IDENT        { symbol_no := 0 ; return arg1 }
  141.     | epsilon        { symbol_no := 0 }
  142.  
  143. rbody    : IDENT            { symbol_no +:= 1; return arg1 }
  144.     | act            { return "epsilon " || arg1 }
  145.     | middle, IDENT        { return arg1 || ", " || arg2 }
  146.     | middle, act        { return arg1 || " "  || arg2 }
  147.     | middle, ',', IDENT    { return arg1 || ", " || arg3 }
  148.     | epsilon        { return "epsilon" }
  149.  
  150. middle    : IDENT            { symbol_no +:= 1; return arg1 }
  151.     | act            { symbol_no +:= 1; return arg1 }
  152.     | middle, IDENT        { symbol_no +:= 1; return arg1 || ", "||arg2 }
  153.     | middle, ',', IDENT    { symbol_no +:= 1; return arg1 || ", "||arg3 }
  154.     | middle, act        {
  155.                   local i, l1, l2
  156.                   static actno
  157.                   initial { actno := 0; newrules := [] }
  158.                   actno +:= 1
  159.                   l1 := []; l2 := []
  160.                   every i := 1 to symbol_no do {
  161.                       every put(l1, ("arg"|"$") || i)
  162.                       if symbol_no-i = 0 then i := "0"
  163.                       else i := "-" || symbol_no - i
  164.                       every put(l2, ("$"|"$") || i)
  165.                   }
  166.                   put(newrules, "ACT_"|| actno ||
  167.                     "\t: epsilon "|| mapargs(arg2, l1, l2))
  168.                   symbol_no +:= 1
  169.                   return arg1 || ", " || "ACT_" || actno
  170.                 }
  171.  
  172. act    : '{', cstuff, '}', '}'    { return "{" || arg2 }
  173. cstuff    : epsilon        { return get_icon_code("}") }
  174.  
  175. prec    : epsilon        { return "" }
  176.     | PREC, IDENT        { return arg1 || arg2 }
  177.     | PREC, IDENT, act    { return arg1 || arg2 || arg3 }
  178.  
  179.  
  180. %%
  181.  
  182.  
  183. procedure iilex()
  184.  
  185.     local t
  186.     static last_token, last_lval, colon
  187.     initial colon := ord(":")
  188.  
  189.     every t := next_token() do {
  190.     iilval := last_lval
  191.     if \last_token then {
  192.         if t = colon then {
  193.         if last_token = IDENT
  194.         then suspend C_IDENT
  195.         else suspend last_token
  196.         } else
  197.         suspend last_token
  198.     }
  199.     last_token := t
  200.     last_lval := lval
  201.     }
  202.     iilval := last_lval
  203.     suspend \last_token
  204.  
  205. end
  206.  
  207.  
  208. procedure next_token()
  209.  
  210.     local reserveds, UNreserveds, c, idchars, marks
  211.  
  212.     reserveds := ["break","by","case","create","default","do",
  213.           "else","end","every","fail","global","if",
  214.           "initial","invocable","link","local","next",
  215.           "not","of","procedure","record","repeat",
  216.           "return","static","suspend","then","to","until",
  217.           "while"]
  218.  
  219.     UNreserveds := ["break_","by_","case_","create_","default_","do_",
  220.             "else_","end_","every_","fail_","global_","if_",
  221.             "initial_","invocable_","link_","local_","next_",
  222.             "not_","of_","procedure_","record_","repeat_",
  223.             "return_","static_","suspend_","then_","to_",
  224.             "until_","while_"]
  225.  
  226.     idchars := &letters ++ '._'
  227.     marks := 0
  228.  
  229.     c := reads()
  230.     repeat {
  231.     lval := &null
  232.     case c of {
  233.         "#" : { do_icon_comment(); c := reads() | break }
  234.         "<" : { suspend ord(c); c := reads() | break }
  235.         ">" : { suspend ord(c); c := reads() | break }
  236.         ":" : { suspend ord(c); c := reads() | break }
  237.         "|" : { suspend ord(c); c := reads() | break }
  238.         "," : { suspend ord(c); c := reads() | break }
  239.         "{" : { suspend ord(c | "}" | "}"); c := reads() }
  240.         "/" : {
  241.         reads() == "*" | stop("unknown YACC operator, \"/\"")
  242.         do_c_comment()
  243.         c := reads() | break
  244.         }
  245.         "'" : {
  246.         lval := "'"
  247.         while lval ||:= (c := reads()) do {
  248.             if c == "\\"
  249.             then lval ||:= reads()
  250.             else if c == "'" then {
  251.             suspend IDENT
  252.             break
  253.             }
  254.         }
  255.         c := reads() | break
  256.         }
  257.         "%" : {
  258.         lval := "%"
  259.         while any(&letters, c := reads()) do 
  260.             lval ||:= c
  261.         if *lval = 1 then {
  262.             if c == "%" then {
  263.             lval := "%%"
  264.             suspend MARK
  265.             if (marks +:= 1) > 1 then
  266.                 fail
  267.             } else {
  268.             if c == "{" then {
  269.                 lval := "%{"
  270.                 suspend LCURL | RCURL | RCURL
  271.             }
  272.             else stop("malformed %declaration")
  273.             }
  274.             c := reads() | break
  275.         } else {
  276.             case lval of {
  277.             "%prec"     : suspend PREC
  278.             "%left"     : suspend LEFT
  279.             "%token"    : suspend TOKEN
  280.             "%right"    : suspend RIGHT
  281.             "%type"     : suspend TYPE
  282.             "%start"    : suspend START
  283.             "%union"    : suspend UNION | RCURL | RCURL
  284.             "%nonassoc" : suspend NONASSOC
  285.             default    : stop("unknown % code in def section")
  286.             }
  287.         }
  288.         }
  289.         default : {
  290.         if any(&digits, c) then {
  291.             lval := c
  292.             while any(&digits, c := reads()) do
  293.             lval ||:= c
  294.             suspend NUMBER
  295.         }    
  296.         else {
  297.             if any(idchars, c) then {
  298.             lval := c
  299.             while any(&digits ++ idchars, c := reads()) do
  300.                 lval ||:= c
  301.             lval := mapargs(lval, reserveds, UNreserveds)
  302.             suspend IDENT
  303.             }
  304.             else {
  305.             # whitespace
  306.             c := reads() | break
  307.             }
  308.         }
  309.         }
  310.     }
  311.     }
  312.  
  313.  
  314. end
  315.  
  316.  
  317. procedure get_icon_code(endmark, comment)
  318.  
  319.     local yaccwords, ibpagwords, count, c, c2, s
  320.  
  321.     yaccwords := ["YYDEBUG", "YYACCEPT", "YYERROR", "yyclearin", "yyerrok"]
  322.     ibpagwords := ["IIDEBUG", "IIACCEPT", "IIERROR", "iiclearin", "iierrok"]
  323.  
  324.     s := ""
  325.     count := 1
  326.     c := reads()
  327.  
  328.     repeat {
  329.     case c of {
  330.         "\""    :  s ||:= c || do_string()
  331.         "'"     :  s ||:= c || do_charlit()
  332.         "$"     :  {
  333.         c2 := reads() | break
  334.         if c2 == "$" then {
  335.             until (c := reads()) == "="
  336.             s ||:= "return "
  337.         } else {
  338.             s ||:= c
  339.             c := c2
  340.             next
  341.         }
  342.         }
  343.         "#"     :  {
  344.         if s[-1] == "\n"
  345.         then s[-1] := ""
  346.         do_icon_comment()
  347.         }
  348.         "/" : {
  349.         c := reads() | break
  350.         if c == "*" then
  351.             do_c_comment()
  352.         else {
  353.             s ||:= c
  354.             next
  355.         }
  356.         }
  357.         "{"     :  {
  358.         s ||:= c
  359.         if endmark == "}" then
  360.             count +:= 1
  361.         }
  362.         "}"     :  {
  363.         s ||:= c
  364.         if endmark == "}" then {
  365.             count -:= 1
  366.             count = 0 & (return mapargs(s, yaccwords, ibpagwords))
  367.         }
  368.         }
  369.         "%"     :  {
  370.         s ||:= c
  371.         if endmark == "%}" then {
  372.             if (c := reads()) == "}"
  373.             then return mapargs(s || c, yaccwords, ibpagwords)
  374.             else next
  375.         }
  376.         }
  377.         default : s ||:= c
  378.     }
  379.     c := reads() | break
  380.     }
  381.  
  382.     # if there is no endmark, just go to EOF
  383.     if \endmark
  384.     then stop("input file has mis-braced { code }")
  385.     else return mapargs(s, yaccwords, ibpagwords)
  386.  
  387. end
  388.  
  389.  
  390. procedure do_string()
  391.  
  392.     local c, s
  393.  
  394.     s := ""
  395.     while c := reads() do {
  396.     case c of {
  397.         "\\"    : s ||:= c || reads()
  398.         "\""    : return s || c || reads()
  399.         default : s ||:= c
  400.     }
  401.     }
  402.  
  403.     stop("malformed string literal")
  404.  
  405. end
  406.  
  407.  
  408. procedure do_charlit()
  409.  
  410.     local c, s
  411.  
  412.     s := ""
  413.     while c := reads() do {
  414.     case c of {
  415.         "\\"    : s ||:= c || reads()
  416.         "'"     : return s || c || reads()
  417.         default : s ||:= c
  418.     }
  419.     }
  420.  
  421.     stop("malformed character literal")
  422.  
  423. end
  424.  
  425.  
  426. procedure do_c_comment()
  427.  
  428.     local c, s
  429.  
  430.     s := c := reads() |
  431.     stop("malformed C-style /* comment */")
  432.  
  433.     repeat {
  434.     if c == "*" then {
  435.         s ||:= (c := reads() | break)
  436.         if c == "/" then
  437.         return s
  438.     }
  439.     else s ||:= (c := reads() | break)
  440.     }
  441.  
  442.     return s            # EOF okay
  443.  
  444. end
  445.  
  446.  
  447. procedure do_icon_comment()
  448.  
  449.     local c, s
  450.  
  451.     s := ""
  452.     while c := reads() do {
  453.     case c of {
  454.         "\\"    : s ||:= c || (reads() | break)
  455.         "\n"    : return s
  456.         default : s ||:= c
  457.     }
  458.     }
  459.  
  460.     return s            # EOF okay
  461.  
  462. end
  463.  
  464.  
  465. procedure mapargs(s, l1, l2)
  466.  
  467.     local i, s2
  468.     static cs, tbl, last_l1, last_l2
  469.  
  470.     if /l1 | *l1 = 0 then return s
  471.  
  472.     if not (last_l1 === l1, last_l2 === l2) then {
  473.     cs := ''
  474.     every cs ++:= (!l1)[1]
  475.     tbl := table()
  476.     every i := 1 to *l1 do
  477.         insert(tbl, l1[i], (\l2)[i] | "")
  478.     }
  479.  
  480.     s2 := ""
  481.     s ? {
  482.     while s2 ||:= tab(upto(cs)) do {
  483.         (s2 <- (s2 || tbl[tab(longstr(l1))]),
  484.             not any(&letters++&digits++'_')) |
  485.             (s2 ||:= move(1))
  486.     }
  487.     s2 ||:= tab(0)
  488.     }
  489.  
  490.     return s2
  491.  
  492. end
  493.  
  494.  
  495. procedure main()
  496.     iiparse()
  497. end
  498.