MixFix.cxx

Go to the documentation of this file.
00001 /**************************************************************************
00002  *
00003  * Copyright (C) 2010, Jonathan S. Shapiro
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or
00007  * without modification, are permitted provided that the following
00008  * conditions are met:
00009  *
00010  *   - Redistributions of source code must contain the above 
00011  *     copyright notice, this list of conditions, and the following
00012  *     disclaimer. 
00013  *
00014  *   - Redistributions in binary form must reproduce the above
00015  *     copyright notice, this list of conditions, and the following
00016  *     disclaimer in the documentation and/or other materials 
00017  *     provided with the distribution.
00018  *
00019  *   - Neither the names of the copyright holders nor the names of any
00020  *     of any contributors may be used to endorse or promote products
00021  *     derived from this software without specific prior written
00022  *     permission. 
00023  *
00024  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00025  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00026  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00027  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00028  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00029  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00030  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00031  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00032  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00033  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00034  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00035  *
00036  **************************************************************************/
00037 
00067 #include <limits.h>
00068 #include <iostream>
00069 #include "shared_ptr.hxx"
00070 
00071 #include "Options.hxx"
00072 #include "AST.hxx"
00073 #include "MixFix.hxx"
00074 #include "UocInfo.hxx"
00075 #include "libsherpa/INOstream.hxx"
00076 
00077 #include "BUILD/TransitionParser.hxx"
00078 
00079 // Debug levels:
00080 // 5 = show non-candidate rules
00081 // 4 = rule matching
00082 // 3 = shifting
00083 // 2 = reduce actions
00084 // 1 = Summary input and result
00085 #define MIXDEBUG(n) if (Options::mixfixDebug >= (n))
00086 
00087 using namespace std;
00088 using namespace boost;
00089 using namespace sherpa;
00090 
00091 typedef shared_ptr<AST> ASTPtr;
00092 typedef std::vector<ASTPtr> ASTVec;
00093 typedef std::vector<std::string> StringVec;
00094 
00095 // We are going to use a vector as a stack here, because the
00096 // std::stack implementation does not provide indexed access, and we
00097 // are going to need that.
00098 
00099 template<class T>
00100 static inline
00101 const T& top(const std::vector<T>& vec)
00102 {
00103   assert(vec.size() >= 1);
00104   return vec[vec.size()-1];
00105 }
00106 
00107 template<class T>
00108 static inline
00109 T& top(std::vector<T>& vec)
00110 {
00111   assert(vec.size() >= 1);
00112   return vec[vec.size()-1];
00113 }
00114 
00115 template<class T>
00116 static inline
00117 const T& topNext(const std::vector<T>& vec)
00118 {
00119   assert(vec.size() >= 2);
00120   return vec[vec.size()-2];
00121 }
00122 
00123 template<class T>
00124 static inline
00125 void push(std::vector<T>& vec, const T& elem)
00126 {
00127   vec.push_back(elem);
00128 }
00129 
00130 template<class T>
00131 static inline
00132 T pop(std::vector<T>& vec)
00133 {
00134   T elem = top(vec);
00135   vec.pop_back();
00136   return elem;
00137 }
00138 
00139 
00140 struct QuasiKeyword {
00144   int nOccur;
00145 
00148   int nFirst;
00149 
00152   int nPre;
00153 
00156   int nPost;
00157 
00158   QuasiKeyword()
00159   {
00160     nOccur = nPre = nPost = nFirst = 0;
00161   }
00162 };
00163 
00164 struct QuasiKeywordMap {
00165   std::map<std::string, QuasiKeyword> theMap;
00166 
00167   QuasiKeywordMap()
00168   {
00169   }
00170 
00171   void add(std::string qkwd);
00172   void remove(std::string qkwd);
00173 
00174   QuasiKeyword& operator[](std::string theKey)
00175   {
00176     return theMap[theKey];
00177   }
00178 
00179   bool contains(std::string theKey)
00180   {
00181     return (theMap.find(theKey) != theMap.end());
00182   }
00183 
00184   size_t erase(std::string theKey)
00185   {
00186     return theMap.erase(theKey);
00187   }
00188 };
00189 
00190 enum MixElemModifierValues {
00191   MEMV_NONE,
00192 
00193   MEMV_THUNK                    // Hole expression should be thunked.
00194 };
00195 typedef sherpa::EnumSet<MixElemModifierValues> MixElemModifiers;
00196 
00197 struct MixRuleElement {
00198   SyntacticCategory sc;
00199   MixElemModifiers mods;
00200   std::string name;
00201 
00202   MixRuleElement(SyntacticCategory _sc)
00203   {
00204     sc = _sc;
00205     mods = MEMV_NONE;
00206   }
00207   MixRuleElement(std::string _name)
00208   {
00209     sc = msc_keyword;
00210     name = _name;
00211   }
00212 
00213   bool isHole() { return sc != msc_keyword; };
00214 };
00215 typedef std::vector<MixRuleElement> MixElemVec;
00216 
00217 struct MixRule : public boost::enable_shared_from_this<MixRule> {
00219   std::string name;
00220 
00222   MixElemVec rhs;
00223 
00225   int fixity;
00226 
00228   SyntacticCategory sc;
00229 
00231   int prec;
00232 
00236   Associativity assoc;
00237 
00239   bool isInfix()   const { return fixity == infix; }
00240   bool isPrefix()  const { return fixity == prefix; }
00241   bool isClosed()  const { return fixity == closed; }
00242   bool isPostfix() const { return fixity == postfix; }
00243 
00244   bool isHole(size_t pos) {
00245     return (rhs[pos].sc != msc_keyword);
00246   }
00247 
00248   bool isKwd(size_t pos) {
00249     return (rhs[pos].sc == msc_keyword);
00250   }
00251 
00252   bool isInitialHole(size_t pos) {
00253     return (pos == 0) && isHole(pos);
00254   }
00255 
00256   bool isFinalHole(size_t pos) {
00257     return ((pos == rhs.size() - 1) && isHole(pos));
00258   }
00259 
00260   bool hasLeadingHole() 
00261   {
00262     return (fixity == infix) || (fixity == postfix);
00263   }
00264   bool hasTrailingHole() 
00265   {
00266     return (fixity == infix) || (fixity == prefix);
00267   }
00268 
00269 private:
00271   void extract();
00272 
00273 public:
00274   MixRule(SyntacticCategory _sc, const std::string& _nm, int _prec, 
00275           Associativity _assoc = assoc_none)
00276   {
00277     sc = _sc;
00278     name = _nm;
00279     prec = _prec;
00280     assoc = _assoc;
00281     extract();
00282   }
00283 
00284   static shared_ptr<MixRule> 
00285   make(SyntacticCategory _sc, const std::string& _nm, int _prec, 
00286        Associativity _assoc = assoc_none)
00287   {
00288     MixRule *tmp = new MixRule(_sc, _nm, _prec, _assoc);
00289     return boost::shared_ptr<MixRule>(tmp);
00290   }
00291 
00292   void PrettyPrint(INOstream& out) const;
00293 };
00294 typedef shared_ptr<MixRule> MixRulePtr;
00295 
00296 INOstream& operator<<(INOstream& out, MixRule& rule)
00297 {
00298   rule.PrettyPrint(out);
00299   return out;
00300 }
00301 
00302 
00303 struct MixFixNode {
00305   ASTPtr ast;
00306 
00308   MixRulePtr rule;
00309 
00310   MixFixNode(ASTPtr _ast, MixRulePtr _rule)
00311   {
00312     ast = _ast;
00313     rule = _rule;
00314   }
00315 
00316   bool matchesKwd(const MixRuleElement& elem) const {
00317     assert(ast);
00318     return (elem.sc == msc_keyword && ast->s == elem.name);
00319   }
00320 
00321   void PrettyPrint(sherpa::INOstream& out, PrettyPrintFlags flags)
00322   {
00323     if (ast->astType != at_ident)
00324       out << '{';
00325 
00326     ast->PrettyPrint(out, flags & ~pp_FinalNewline);
00327 
00328     if (ast->astType != at_ident)
00329       out << '}';
00330 
00331     if (flags & pp_FinalNewline)
00332       out << endl;
00333   }
00334 
00335   bool isKwd() const
00336   { return (!rule); }
00337 
00338   bool isExpr() const 
00339   { return rule && rule->sc == msc_expr; }
00340 
00341   bool isType() const 
00342   { return rule && rule->sc == msc_type; }
00343 };
00344 
00345 typedef std::vector<MixFixNode> MixInput;
00346 
00347 struct MixContext {
00348   typedef std::set<MixRulePtr> RuleSet;
00349   RuleSet rules;
00350   QuasiKeywordMap kwMap;
00351 
00352   void add(MixRulePtr rule);
00353   void remove(MixRulePtr rule);
00354 
00355   MixRulePtr
00356   ParseOneMixFix(sherpa::INOstream& errStream, MixInput& origInput, 
00357                  MixRulePtr parentRule);
00358                   
00359   MixRulePtr
00360   ParseMixFix(sherpa::INOstream& errStream, MixInput& origInput, 
00361               MixRulePtr parentRule);
00362                   
00363   bool isKwd(const MixFixNode& mixNode);
00364 
00371   bool cannotPossiblyMatch(sherpa::INOstream& errStream,
00372                            MixRulePtr rule,
00373                            const MixInput& mixExpr);
00374 
00375 } TheMixContext;
00376 
00377 void
00378 MixRule::PrettyPrint(INOstream& out) const
00379 {
00380   switch(fixity) {
00381   case infix:
00382     out << "infix";
00383 
00384     switch(assoc) {
00385     case assoc_left:   out << 'l'; break;
00386     case assoc_right:  out << 'r'; break;
00387     case assoc_none:   out << 'n'; break;
00388     }
00389 
00390     break;
00391   case prefix:
00392     out << "prefix";
00393     break;
00394   case postfix:
00395     out << "postfix";
00396     break;
00397   case closed:
00398     out << "closed";
00399     break;
00400   }
00401 
00402   out << " "
00403       << name
00404       << "("
00405       << prec
00406       << ")";
00407 }
00408 
00410 void
00411 MixRule::extract()
00412 {
00413   std::string rule = name;
00414   
00415   while(rule.size()) {
00416     if (rule[0] == '_') {
00417       rhs.push_back(MixRuleElement(sc));
00418       rule = rule.substr(1);
00419     }
00420     else if (rule[0] == '#') {  // need to parse it
00421       SyntacticCategory sc;
00422 
00423       switch (rule[1]) {
00424       case 'e':
00425         sc = msc_expr;
00426         break;
00427       case 't':
00428         sc = msc_type;
00429         break;
00430       case 'k':
00431         sc = msc_kind;
00432         break;
00433       default:
00434         assert(false);
00435       }
00436 
00437       MixRuleElement elem(sc);
00438 
00439       rule = rule.substr(2);
00440       while (rule[0] != '_') {
00441         switch(rule[0]) {
00442         case 'T':
00443           elem.mods |= MEMV_THUNK;
00444           break;
00445         default:
00446           assert(false);
00447         }
00448 
00449         rule = rule.substr(1);
00450       }
00451       
00452       rhs.push_back(elem);
00453       rule = rule.substr(1);
00454     }
00455     else {
00456       if (rule[0] == '@')       // keyword separator
00457         rule = rule.substr(1);
00458 
00459       // Find the end of this quasi-keyword, or end-of-rule:
00460       size_t nxt = rule.find_first_of("#_@");
00461 
00462       if (nxt == string::npos) {
00463         rhs.push_back(MixRuleElement(rule));
00464         rule.clear();
00465       }
00466       else {
00467         rhs.push_back(MixRuleElement(rule.substr(0, nxt)));
00468         rule = rule.substr(nxt);
00469       }
00470     }
00471   }
00472 
00473   if (isHole(0) && isHole(rhs.size()-1))
00474     fixity = infix;
00475   else if (isHole(0))
00476     fixity = postfix;
00477   else if (isHole(rhs.size()-1))
00478     fixity = prefix;
00479   else
00480     fixity = closed;
00481 }
00482 
00483 bool
00484 MixContext::isKwd(const MixFixNode& node)
00485 {
00486   if (node.ast->astType != at_ident)
00487     return false;
00488 
00489   return kwMap.contains(node.ast->s);
00490 }
00491 
00492 bool 
00493 MixContext::cannotPossiblyMatch(sherpa::INOstream& errStream,
00494                                 MixRulePtr rule, 
00495                                 const MixInput& input)
00496 {
00497   MixElemVec& rhs = rule->rhs;
00498 
00499   // Are enough components left?
00500   if (input.size() < rhs.size())
00501     return true;
00502 
00503   // If this rule has a leading hole, the first thing must be a hole
00504   // and the next thing must match:
00505   if (rule->hasLeadingHole()) {
00506     bool might = (top(input).isExpr()
00507                   && ((rule->rhs.size() == 1)
00508                       || topNext(input).matchesKwd(rule->rhs[1])));
00509 
00510     MIXDEBUG(might ? 4 : 5)
00511       errStream << (*rule)
00512                 << (might ? " might" : " cannot") << " match."
00513                 << endl;
00514     
00515     if (might)
00516       return false;
00517 
00518     return true;
00519   }
00520   
00521   // For closed and prefix expressions, the initial keyword must
00522   // match, but if the next position is a keyword it may ultimately
00523   // reduce to an expression, so we can't conclude anything from that.
00524   bool might = top(input).matchesKwd(rule->rhs[0]);
00525   if (might)
00526     return false;
00527 
00528   MIXDEBUG(might ? 4 : 5)
00529     errStream << (*rule)
00530               << (might ? " might" : " cannot") << " match."
00531               << endl;
00532 
00533   return true;
00534 }
00535 
00537 void
00538 QuasiKeywordMap::add(std::string qkwd)
00539 {
00540   if (!contains(qkwd)) {
00541     QuasiKeyword qks;
00542     theMap.insert(std::pair<std::string, QuasiKeyword>(qkwd,qks));
00543   }
00544 
00545   theMap[qkwd].nOccur++;
00546 }
00547 
00550 void
00551 QuasiKeywordMap::remove(std::string qkwd)
00552 {
00553   if (!contains(qkwd))
00554     return;
00555 
00556   theMap[qkwd].nOccur--;
00557 
00558   if (theMap[qkwd].nOccur == 0)
00559     theMap.erase(qkwd);
00560 }
00561 
00563 void 
00564 MixContext::add(MixRulePtr rule)
00565 {
00566   MixElemVec& rhs = rule->rhs;
00567   
00568   for (size_t i = 0; i < rhs.size(); i++)
00569     if (rule->isKwd(i))
00570       kwMap.add(rhs[i].name);
00571 
00572   // Closed and prefix rules start with a token:
00573   if (!rule->hasLeadingHole())
00574     kwMap[rhs[0].name].nFirst++;
00575 
00576   // Prefix and infix rules end with a hole:
00577   if (rule->hasTrailingHole())
00578     kwMap[rhs[rhs.size()-2].name].nPre++;
00579 
00580   // Postfix and infix rules start with a hole:
00581   if (rule->hasLeadingHole())
00582     kwMap[rhs[1].name].nPost++;
00583 
00584   rules.insert(rule);
00585 }
00586 
00588 void
00589 MixContext::remove(MixRulePtr rule)
00590 {
00591   MixElemVec& rhs = rule->rhs;
00592 
00593   // Closed and prefix rules start with a token:
00594   if (!rule->hasLeadingHole())
00595     kwMap[rhs[0].name].nFirst--;
00596 
00597   // Prefix and infix rules end with a hole:
00598   if (rule->hasTrailingHole())
00599     kwMap[rhs[rhs.size()-2].name].nPre--;
00600 
00601   // Postfix and infix rules start with a hole:
00602   if (rule->hasLeadingHole())
00603     kwMap[rhs[1].name].nPost--;
00604 
00605   for (size_t i = 0; i < rhs.size(); i++)
00606     if (rule->isKwd(i))
00607       kwMap.remove(rhs[i].name);
00608 
00609   rules.erase(rule);
00610 }
00611 
00614 static ASTPtr
00615 CleanMixFix(INOstream& errStream, ASTPtr ast)
00616 {
00617   // This used to be an else-less IF designed to allow fall-through
00618   // intentionally. That led to a number of bugs, because the value of
00619   // 'fn' could become stale. I've therefore switched to a loop
00620   // structure.
00621 
00622   if (ast->astType == at_apply) {
00623     std::string fn = ast->child(0)->s;
00624 
00625     // This is the start rule - take all of these out.
00626     if (fn == "_") {
00627       return CleanMixFix(errStream, ast->child(1));
00628     }
00629     else if (fn == "_._") {
00630       ast = AST::make(at_select, ast->loc, ast->child(1), ast->child(2));
00631     }
00632     else if (fn == "_,_") {
00633       LToken pair(tk_BlkIdent, ast->child(0)->loc, ast->child(0)->endLoc(), "pair");
00634       ast->children[0] = AST::make(at_ident, pair);
00635     }
00636     else if (fn == "(_)") {
00637       return CleanMixFix(errStream, ast->child(1));
00638     }
00639     else if (fn == "_(_)") {
00640       shared_ptr<AST> argAst = ast->child(2);
00641 
00642       // Rotate the applied function into the proper position
00643       ast->children[0] = ast->children[1];
00644       ast->children.erase(ast->children.begin()+1, ast->children.end());
00645 
00646       while((argAst->astType == at_apply) && (argAst->child(0)->s == "_,_")) {
00647         ast->addChild(argAst->child(1));
00648         argAst = argAst->child(2);
00649       }
00650       ast->addChild(argAst);
00651     }
00652     else if (fn == "_(@)") {
00653       // Rotate the applied function into the proper position
00654       ast->children[0] = ast->children[1];
00655       ast->children.erase(ast->children.begin()+1, ast->children.end());
00656     }
00657     else if (fn == "(@)") {
00658       ast->astType = at_unit;
00659       ast->children.clear();
00660       return ast;
00661     }
00662     else if (fn == "_[_]") {    // Array indexing:
00663       ast = AST::make(at_nth, ast->loc, ast->child(1), ast->child(2));
00664     }
00665 
00666 #if 0
00667     // Check for ([a, b, c]) convenience syntax. Otherwise eliminate
00668     // "(_)" nodes.
00669     else if (fn == "(_)") {
00670       if ((ast->child(1)->astType == at_apply) &&
00671           (ast->child(1)->child(0)->s == "[_]")) {
00672         
00673         ast = argListToConsList(ast->child(1)->child(1));
00674 
00675         // Re-write the argument chain into a chain of CONS applications:
00676         shared_ptr<AST> argAst = inAST->child(1)->child(1);
00677         while((argAst->astType == at_apply) && (argAst->child(0)->s == "_,_")) {
00678           argAst->children[0] = 
00679             argAst = argAst->child(2);
00680         }
00681         ast->addChild(argAst);
00682       }
00683       else {
00684         return CleanMixFix(ast->child(1));
00685       }
00686     }
00687 #endif
00688     else if (fn == "[_]") {
00689       ast = AST::make(at_nth, ast->loc, ast->child(1), ast->child(2));
00690     }
00691     if (fn == "_and_" || fn == "_&&_") {
00692       ast->astType = at_and;
00693       ast->children.erase(ast->children.begin());
00694     }
00695     if (fn == "_or_" || fn == "_||_") {
00696       ast->astType = at_or;
00697       ast->children.erase(ast->children.begin());
00698     }
00699   }
00700 
00701   for (size_t c = 0; c < ast->children.size(); c++)
00702     ast->children[c] = CleanMixFix(errStream, ast->children[c]);
00703 
00704   return ast;
00705 }
00706 
00707 static ASTPtr
00708 CheckMixFix(INOstream& errStream, ASTPtr ast)
00709 {
00710   return ast;
00711 }
00712 
00713 static MixRulePtr MixNoRuleFound = MixRule::make(msc_expr, "_", INT_MIN, assoc_none);
00714 static MixRulePtr MixStartRule = MixRule::make(msc_expr, "_", INT_MIN, assoc_none);
00715 static MixRulePtr MixInputRule = MixRule::make(msc_expr, "(_)", INT_MIN, assoc_none);
00716 
00722 shared_ptr<AST>
00723 ProcessMixFix(std::ostream& err, shared_ptr<AST> mixAST)
00724 {
00725   INOstream errStream(err);
00726 
00727   if (TheMixContext.rules.empty())
00728     errStream << "Error! No Rules!" << endl;
00729 
00730   ASTVec& children = mixAST->children;
00731 
00732   MixInput input;
00733   for(ASTVec::reverse_iterator itr = children.rbegin();
00734       itr != children.rend();
00735       itr++) {
00736     push(input, MixFixNode(*itr, MixStartRule));
00737 
00738     // Input quasi-keywords should not be marked by a rule.
00739     if (TheMixContext.isKwd(top(input)))
00740       top(input).rule = GC_NULL;
00741   }
00742 
00743   MIXDEBUG(1) {
00744     errStream << "Processing mixfix expression at "
00745               << mixAST->loc << endl;
00746     errStream.more();
00747     for(MixInput::reverse_iterator itr = input.rbegin();
00748         itr != input.rend();
00749         itr++) {
00750       if (itr != input.rbegin()) errStream << " ";
00751       (*itr).PrettyPrint(errStream, pp_NONE);
00752     }
00753     errStream << endl;
00754     errStream.less();
00755   }
00756 
00757 
00758   while( input.size() > 1 
00759          && TheMixContext.ParseMixFix(errStream, input, MixStartRule))
00760     ;
00761 
00762   if (input.size() != 1) {
00763     MIXDEBUG(1)
00764       errStream << "Yields Incomplete Parse" << endl;
00765     return GC_NULL;
00766   }
00767 
00768   MIXDEBUG(2) {
00769     errStream << "Produced: ";
00770     input[0].PrettyPrint(errStream, pp_FinalNewline);
00771   }
00772 
00773   shared_ptr<AST> result = CleanMixFix(errStream, input[0].ast);
00774   result = CheckMixFix(errStream, result);
00775 
00776   MIXDEBUG(1) {
00777     errStream << "Giving: ";
00778     result->PrettyPrint(errStream, pp_FinalNewline);
00779   }
00780 
00781   return result;
00782 }
00783 
00784 static MixFixNode
00785 reduce(INOstream& errStream, MixInput& shunt, MixRulePtr rule)
00786 {
00787   // Children under shunt are now what we want. Build an apply node
00788   // for them, and push them back onto the front of the input vector.
00789   // Don't build unnecessary apply nodes for the catch-all expression
00790   // rule.
00791 
00792   shared_ptr<AST> result = shunt[0].ast; // until proven otherwise
00793 
00794   shared_ptr<AST> fnName = 
00795     AST::make(at_ident, LToken(tk_BlkIdent, rule->name));
00796 
00797   if (rule->sc == msc_expr) {
00798     result = AST::make(at_apply, shunt[0].ast->loc, fnName);
00799   }
00800   else if (rule->sc == msc_type) {
00801     result = AST::make(at_typeapp, shunt[0].ast->loc, fnName);
00802   }
00803 
00804   assert(rule->rhs.size() == shunt.size());
00805 
00806   for(size_t i = 0; i < shunt.size(); i++) {
00807     if (rule->rhs[i].isHole()) {
00808       assert(!shunt[i].isKwd());
00809       
00810       shared_ptr<AST> tree = shunt[i].ast;
00811 
00812       if (rule->rhs[i].mods & MEMV_THUNK) {
00818         shared_ptr<AST> iRetBody = 
00819           AST::make(at_labeledBlock, tree->loc,
00820                     AST::make(at_ident, LToken(tk_BlkIdent, "__return")),
00821                     tree);
00822 
00823         tree = 
00824           AST::make(at_lambda, tree->loc,
00825                     AST::make(at_argVec, tree->loc), // empty
00826                     iRetBody);
00827       }
00828 
00829       result->addChild(tree);
00830     }
00831   }
00832 
00833   MIXDEBUG(2) {
00834     errStream << "Reduced " <<  rule->name << " giving ";
00835     result->PrettyPrint(errStream, pp_FinalNewline);
00836   }
00837 
00838   return MixFixNode(result, rule);
00839 }
00840 
00871 MixRulePtr
00872 MixContext::ParseOneMixFix(INOstream& errStream, MixInput& origInput, 
00873                            MixRulePtr parentRule)
00874 {
00875   errStream.more();
00876 
00877   MixInput shunt;
00878 
00879   struct BestRule {
00880     MixRulePtr rule;
00881     MixInput result;
00882     bool tie;
00883   } best = { GC_NULL, MixInput(), false };
00884 
00885   MIXDEBUG(4) {
00886     errStream << "Parsing:";
00887 
00888     for(MixInput::reverse_iterator itr = origInput.rbegin();
00889         itr != origInput.rend();
00890         itr++) {
00891       errStream << " ";
00892       (*itr).PrettyPrint(errStream, pp_NONE);
00893     }
00894     errStream << " in context of "<< (*parentRule);
00895     errStream << endl;
00896   }
00897 
00898   // We are proceding through all of the parse rules, trying to find
00899   // one that reduces the input without regard to operator
00900   // precedence. We'll fix up operator precedence later using a tree
00901   // re-writing.
00902 
00903   for(RuleSet::iterator itr = rules.begin();
00904       itr != rules.end();
00905       ++itr) {
00906     MixRulePtr rule = *itr;
00907 
00908     // ParentRule is actually telling us the hole precedence:
00909     if (rule->prec < parentRule->prec) {
00910       MIXDEBUG(5)
00911         errStream << (*rule)
00912                   << "rejected (below required precedence)"
00913                   << endl;
00914       continue;
00915     }
00916     
00917     if (rule->prec == parentRule->prec) {
00918       if ((rule->assoc != assoc_right) ||
00919           (parentRule->assoc != assoc_right)) {
00920         MIXDEBUG(5)
00921           errStream << (*rule)
00922                     << "rejected (prec OK, not right-assoc)"
00923                     << endl;
00924         continue;
00925       }
00926     }
00927     
00928     // If the candidate rule has lower precedence than the one we actually
00929     // found, the current best rule would be preferred.
00930     if (best.rule && rule->prec < best.rule->prec)
00931       continue;
00932     
00933     if (cannotPossiblyMatch(errStream, rule, origInput))
00934       continue;
00935 
00936     // The parse processing is destructive, so we need to operate on a
00937     // copy:
00938     MixInput input = origInput;
00939     shunt = MixInput();
00940 
00941     MIXDEBUG(4) {
00942       errStream << "Attempting rule " << rule->name
00943                 << " on";
00944 
00945         for(MixInput::reverse_iterator itr = input.rbegin();
00946             itr != input.rend();
00947             itr++) {
00948           errStream << " ";
00949           (*itr).PrettyPrint(errStream, pp_NONE);
00950         }
00951       errStream << endl;
00952     }
00953 
00954     errStream.more();
00955 
00956     // If we survived cannotPossiblyMatch(), then the first element
00957     // matches, so shift that:
00958     MIXDEBUG(3) {
00959       errStream << "Shifting first element: ";
00960       top(input).PrettyPrint(errStream, pp_FinalNewline);
00961     }
00962     push(shunt, pop(input));
00963 
00964     for (size_t pos = 1; pos < rule->rhs.size(); pos++) {
00965       // Parsing of expressions can cause the remaining input to
00966       // become too small for this rule to succeed. Need to check here
00967       // to avoid a range overrun on the input:
00968       if (input.size() < (rule->rhs.size() - pos))
00969         break;
00970 
00971       // Note that if matchesKwd() passes then we know the node
00972       // actually *was* a keyword by virtue of the fact that we are
00973       // matching a keyword position in the rule. There is no need
00974       // to check that redundantly.
00975       //
00976       // We handle right-most holes specially because they have to
00977       // have higher precedence than their parent rule, and when we
00978       // recurse we are only looking for potential child nodes.
00979 
00980       if (rule->isKwd(pos) && top(input).matchesKwd(rule->rhs[pos])) {
00981         MIXDEBUG(3) {
00982           errStream << "Shifting: ";
00983           top(input).PrettyPrint(errStream, pp_FinalNewline);
00984         }
00985 
00986         push(shunt, pop(input));
00987       }
00988       // Note: Want to do this whether the next thing in the input is
00989       // an expr or a kwd! Consider _-_ matching 1 - (2) 
00990       else if (rule->isHole(pos) &&
00991                ParseMixFix(errStream, input, 
00992                            rule->isFinalHole(pos) ? rule : MixInputRule)) {
00993         MIXDEBUG(3) {
00994           errStream << "Shifting: ";
00995           top(input).PrettyPrint(errStream, pp_FinalNewline);
00996         }
00997 
00998         push(shunt, pop(input));
00999       }
01000       else if(rule->isHole(pos) && top(input).isExpr()) {
01001         // No way to reduce. Shift this element because it is an
01002         // pre-existing expression constructed by the initial parser.
01003         MIXDEBUG(3) {
01004           errStream << "Shifting: ";
01005           top(input).PrettyPrint(errStream, pp_FinalNewline);
01006         }
01007 
01008         push(shunt, pop(input));
01009       }
01010       else {
01011         // Rule did not match.
01012         break;
01013       }
01014     }
01015 
01016     // Did we get a complete match for this rule? If so, compute the
01017     // reduction:
01018     MIXDEBUG(4) {
01019       errStream << "Rule " << rule->name
01020                 << " (size " << rule->rhs.size()
01021                 << ")";
01022       if (rule->rhs.size() == shunt.size()) {
01023         errStream << " matched stack " << shunt.size();
01024 
01025         if (best.rule) {
01026           if (rule->prec > best.rule->prec)
01027             errStream << " and is preferred";
01028           else
01029             errStream << " but existing rule is better";
01030         }
01031       }
01032       else
01033         errStream << " did not match stack " << shunt.size();
01034 
01035       errStream << endl;
01036     }
01037 
01038     errStream.less();
01039 
01040     if (rule->rhs.size() == shunt.size()) {
01041       MixFixNode nd = reduce(errStream, shunt, rule);
01042       
01043       assert(nd.ast);
01044 
01045       if (best.rule && rule->prec == best.rule->prec) {
01046         // FIX: Somehow need to track the alternatives here for
01047         // reporting purposes.
01048         best.tie = true;
01049         continue;
01050       }
01051 
01052       push(input, nd);
01053       best.rule = rule;
01054       best.result = input;
01055       best.tie = false;
01056     }
01057   }
01058 
01059   errStream.less();
01060 
01063   if (best.tie)
01064     return GC_NULL;
01065 
01066   if (!best.rule)               // no rule fonud
01067     return GC_NULL;
01068 
01069   origInput = best.result;
01070   return best.rule;
01071 }
01072 
01079 MixRulePtr
01080 MixContext::ParseMixFix(INOstream& errStream, MixInput& origInput,
01081                         MixRulePtr parentRule)
01082 {
01083   MixRulePtr best = GC_NULL;
01084   MixRulePtr p = GC_NULL;
01085 
01086   while (p = ParseOneMixFix(errStream, origInput, parentRule)) {
01087     best = p;
01088   }
01089 
01090   return best;
01091 }
01092 
01103 MixRulePtr MixRules[] =  {
01105   // Beginning of user-defined mixfix range
01107 
01108   MixRule::make(msc_expr, "_or_",  0, assoc_left), // lazy OR (syntax)
01109   MixRule::make(msc_expr, "_||_",  0, assoc_left), // lazy OR (syntax)
01110   MixRule::make(msc_expr, "_and_", 1, assoc_left), // lazy AND (syntax)
01111   MixRule::make(msc_expr, "_&&_",  1, assoc_left), // lazy AND (syntax)
01112   MixRule::make(msc_expr, "_!=_",  2, assoc_left),
01113   MixRule::make(msc_expr, "_==_",  2, assoc_left),
01114   MixRule::make(msc_expr, "_<_",   3, assoc_left),
01115   MixRule::make(msc_expr, "_<=_",  3, assoc_left),
01116   MixRule::make(msc_expr, "_>_",   3, assoc_left),
01117   MixRule::make(msc_expr, "_>=_",  3, assoc_left),
01118   MixRule::make(msc_expr, "_::_",  4, assoc_left), // infix cons
01119   MixRule::make(msc_expr, "_|_",   5, assoc_left),
01120   MixRule::make(msc_expr, "_^_",   6, assoc_left),
01121   MixRule::make(msc_expr, "_&_",   7, assoc_left),
01122   MixRule::make(msc_expr, "_<<_",  8, assoc_left),
01123   MixRule::make(msc_expr, "_>>_",  8, assoc_left),
01124   MixRule::make(msc_expr, "_+_",   9, assoc_left),
01125   MixRule::make(msc_expr, "_-_",   9, assoc_left),
01126   MixRule::make(msc_expr, "_%_",  10, assoc_left),
01127   MixRule::make(msc_expr, "_*_",  10, assoc_left),
01128   MixRule::make(msc_expr, "_/_",  10, assoc_left),
01129 
01130   MixRule::make(msc_expr, "_**_",    11, assoc_left), // exponentiation
01131   MixRule::make(msc_expr, "_**_+_",  11, assoc_left),  // hypothetical mul-add for testing
01132 
01133   MixRule::make(msc_expr, "-_",   12, assoc_right), // Unary negation
01134   MixRule::make(msc_expr, "!_",   13, assoc_right), // Boolean inverse
01135   MixRule::make(msc_expr, "not_", 13, assoc_right), // Boolean inverse
01136   MixRule::make(msc_expr, "~_",   14, assoc_right), // Bitwise inverse
01137 
01139   // End of user-defined mixfix range
01141 
01143   // Rules associated with built-in structure start here.
01144   //
01145   // These productions yield (after massage) syntactic forms rather
01146   // than applications.
01148 
01149   // The precedence of these next two doesn't really matter, because
01150   // (a) they are closed, and (b) the related forms below don't have
01151   // the same first token.
01152 
01153   // cpair convenience and precedence override:
01154   MixRule::make(msc_expr, "(_)",  128, assoc_none),
01155 
01156   // list convenience syntax:
01157   MixRule::make(msc_expr, "[_]",  128, assoc_none),
01158 
01159   // Kind matching is not yet implemented:
01160   // MixRule::make(msc_type, "_:#k_", 129, assoc_none),
01161 
01162   // The design note called for:
01163   //   MixRule::make(msc_expr, "__",  0, assoc_left),
01164   // but I'm giving these a try instead:
01165 
01166   MixRule::make(msc_expr, "_,_",  -1, assoc_right), // arg assembly, cpair assembly
01167 
01168   MixRule::make(msc_expr, "(@)",   129, assoc_left), // unit constructor
01169 
01170   MixRule::make(msc_expr, "_(@)",  130, assoc_left), // nullary application
01171   MixRule::make(msc_expr, "_(_)",  130, assoc_left), // application
01172   MixRule::make(msc_expr, "_[_]",  130, assoc_left), // array/vector subscript
01173   MixRule::make(msc_expr, "_._",   130, assoc_left), // dot notation (select or usesel)
01174 
01175   // Which might quite possibly be a better approach
01176 };
01177 static size_t nMixRules = sizeof(MixRules) / sizeof(MixRules[0]);
01178 
01179 void
01180 mixfix_init()
01181 {
01182   for (size_t i = 0; i < nMixRules; i++) {
01183     TheMixContext.add(MixRules[i]);
01184   }
01185 }
01186 
01187 static bool 
01188 HandleMixFix(std::ostream& errStream, shared_ptr<AST> ast)
01189 {
01190   bool errFree = true;
01191 
01192   // In this pass, we proceed bottom-up
01193   for (size_t c = 0; c < ast->children.size(); c++)
01194     errFree = errFree && HandleMixFix(errStream, ast->child(c));
01195 
01196   if (ast->astType == at_mixfix) {
01197     shared_ptr<AST> newAst = ProcessMixFix(errStream, ast);
01198 
01199     if (!newAst)
01200       errFree = false;
01201 
01202     if (newAst && newAst != ast) {
01203       // Clobber the old node in-place:
01204       ast->s = newAst->s;
01205       ast->astType = newAst->astType;
01206       ast->identType = newAst->identType;
01207       ast->litValue = newAst->litValue;
01208       ast->litBase = newAst->litBase;
01209       ast->flags = newAst->flags;
01210       ast->children = newAst->children;
01211       ast->fqn = newAst->fqn;
01212     }
01213   }
01214 
01215   return errFree;
01216 }
01217 
01218 bool
01219 UocInfo::fe_mixfix(std::ostream& errStream,
01220                    bool init, unsigned long flags)
01221 {
01222   return HandleMixFix(errStream, uocAst);
01223 }

Generated on Thu May 17 23:59:16 2012 for BitC Compiler by  doxygen 1.4.7