LTP GCOV extension - code coverage report
Current view: directory - src/libexpr - parser.y
Test: app.info
Date: 2008-11-20 Instrumented lines: 240
Code covered: 97.1 % Executed lines: 233

       1                 : %glr-parser
       2                 : %pure-parser
       3                 : %locations
       4                 : %error-verbose
       5                 : %defines
       6                 : /* %no-lines */
       7                 : %parse-param { yyscan_t scanner }
       8                 : %parse-param { ParseData * data }
       9                 : %lex-param { yyscan_t scanner }
      10                 : 
      11                 : 
      12                 : %{
      13                 : /* Newer versions of Bison copy the declarations below to
      14                 :    parser-tab.hh, which sucks bigtime since lexer.l doesn't want that
      15                 :    stuff.  So allow it to be excluded. */
      16                 : #ifndef BISON_HEADER_HACK
      17                 : #define BISON_HEADER_HACK
      18                 :     
      19                 : #include <stdio.h>
      20                 : #include <stdlib.h>
      21                 : #include <string.h>
      22                 : 
      23                 : #include "aterm.hh"
      24                 : #include "util.hh"
      25                 :     
      26                 : #include "parser-tab.hh"
      27                 : #include "lexer-tab.hh"
      28                 : 
      29                 : #include "nixexpr.hh"
      30                 : #include "nixexpr-ast.hh"
      31                 : 
      32                 : 
      33                 : using namespace nix;
      34                 : 
      35                 : 
      36                 : namespace nix {
      37                 : 
      38                 :     
      39                 : struct ParseData 
      40             370 : {
      41                 :     Expr result;
      42                 :     Path basePath;
      43                 :     Path path;
      44                 :     string error;
      45                 : };
      46                 : 
      47                 : 
      48             507 : static Expr fixAttrs(int recursive, ATermList as)
      49                 : {
      50             507 :     ATermList bs = ATempty, cs = ATempty;
      51             507 :     ATermList * is = recursive ? &cs : &bs;
      52            2433 :     for (ATermIterator i(as); i; ++i) {
      53                 :         ATermList names;
      54                 :         Expr src;
      55                 :         ATerm pos;
      56            1926 :         if (matchInherit(*i, src, names, pos)) {
      57             135 :             bool fromScope = matchScope(src);
      58             337 :             for (ATermIterator j(names); j; ++j) {
      59             202 :                 Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j);
      60             202 :                 *is = ATinsert(*is, makeBind(*j, rhs, pos));
      61                 :             }
      62            1791 :         } else bs = ATinsert(bs, *i);
      63                 :     }
      64             507 :     if (recursive)
      65             140 :         return makeRec(bs, cs);
      66                 :     else
      67             367 :         return makeAttrs(bs);
      68                 : }
      69                 : 
      70                 : 
      71              15 : static Expr stripIndentation(ATermList es)
      72                 : {
      73              15 :     if (es == ATempty) return makeStr("");
      74                 :     
      75                 :     /* Figure out the minimum indentation.  Note that by design
      76                 :        whitespace-only final lines are not taken into account.  (So
      77                 :        the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */
      78              14 :     bool atStartOfLine = true; /* = seen only whitespace in the current line */
      79              14 :     unsigned int minIndent = 1000000;
      80              14 :     unsigned int curIndent = 0;
      81                 :     ATerm e;
      82              50 :     for (ATermIterator i(es); i; ++i) {
      83              53 :         if (!matchIndStr(*i, e)) {
      84                 :             /* Anti-quotations end the current start-of-line whitespace. */
      85              17 :             if (atStartOfLine) {
      86               4 :                 atStartOfLine = false;
      87               4 :                 if (curIndent < minIndent) minIndent = curIndent;
      88                 :             }
      89              17 :             continue;
      90                 :         }
      91              36 :         string s = aterm2String(e);
      92            2456 :         for (unsigned int j = 0; j < s.size(); ++j) {
      93            2420 :             if (atStartOfLine) {
      94             345 :                 if (s[j] == ' ')
      95             283 :                     curIndent++;
      96              62 :                 else if (s[j] == '\n') {
      97                 :                     /* Empty line, doesn't influence minimum
      98                 :                        indentation. */
      99               8 :                     curIndent = 0;
     100                 :                 } else {
     101              54 :                     atStartOfLine = false;
     102              54 :                     if (curIndent < minIndent) minIndent = curIndent;
     103                 :                 }
     104            2075 :             } else if (s[j] == '\n') {
     105              57 :                 atStartOfLine = true;
     106              57 :                 curIndent = 0;
     107                 :             }
     108                 :         }
     109                 :     }
     110                 : 
     111                 :     /* Strip spaces from each line. */
     112              14 :     ATermList es2 = ATempty;
     113              14 :     atStartOfLine = true;
     114              14 :     unsigned int curDropped = 0;
     115              14 :     unsigned int n = ATgetLength(es);
     116              50 :     for (ATermIterator i(es); i; ++i, --n) {
     117              53 :         if (!matchIndStr(*i, e)) {
     118              17 :             atStartOfLine = false;
     119              17 :             curDropped = 0;
     120              17 :             es2 = ATinsert(es2, *i);
     121              17 :             continue;
     122                 :         }
     123                 :         
     124              36 :         string s = aterm2String(e);
     125              36 :         string s2;
     126            2456 :         for (unsigned int j = 0; j < s.size(); ++j) {
     127            2420 :             if (atStartOfLine) {
     128             345 :                 if (s[j] == ' ') {
     129             283 :                     if (curDropped++ >= minIndent)
     130              35 :                         s2 += s[j];
     131                 :                 }
     132              62 :                 else if (s[j] == '\n') {
     133               8 :                     curDropped = 0;
     134               8 :                     s2 += s[j];
     135                 :                 } else {
     136              54 :                     atStartOfLine = false;
     137              54 :                     curDropped = 0;
     138              54 :                     s2 += s[j];
     139                 :                 }
     140                 :             } else {
     141            2075 :                 s2 += s[j];
     142            2075 :                 if (s[j] == '\n') atStartOfLine = true;
     143                 :             }
     144                 :         }
     145                 : 
     146                 :         /* Remove the last line if it is empty and consists only of
     147                 :            spaces. */
     148              36 :         if (n == 1) {
     149              14 :             unsigned int p = s2.find_last_of('\n');
     150              14 :             if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
     151              11 :                 s2 = string(s2, 0, p + 1);
     152                 :         }
     153                 :             
     154              36 :         es2 = ATinsert(es2, makeStr(s2));
     155                 :     }
     156                 : 
     157              14 :     return makeConcatStrings(ATreverse(es2));
     158                 : }
     159                 : 
     160                 : 
     161                 : void backToString(yyscan_t scanner);
     162                 : void backToIndString(yyscan_t scanner);
     163                 : 
     164                 : 
     165            2488 : static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
     166                 : {
     167                 :     return makePos(toATerm(data->path),
     168            2488 :         loc->first_line, loc->first_column);
     169                 : }
     170                 : 
     171                 : #define CUR_POS makeCurPos(yylocp, data)
     172                 : 
     173                 : 
     174                 : }
     175                 : 
     176                 : 
     177               1 : void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
     178                 : {
     179                 :     data->error = (format("%1%, at `%2%':%3%:%4%")
     180               1 :         % error % data->path % loc->first_line % loc->first_column).str();
     181               1 : }
     182                 : 
     183                 : 
     184                 : /* Make sure that the parse stack is scanned by the ATerm garbage
     185                 :    collector. */
     186             556 : static void * mallocAndProtect(size_t size)
     187                 : {
     188             556 :     void * p = malloc(size);
     189             556 :     if (p) ATprotectMemory(p, size);
     190             556 :     return p;
     191                 : }
     192                 : 
     193             556 : static void freeAndUnprotect(void * p)
     194                 : {
     195             556 :     ATunprotectMemory(p);
     196             556 :     free(p);
     197             556 : }
     198                 : 
     199                 : #define YYMALLOC mallocAndProtect
     200                 : #define YYFREE freeAndUnprotect
     201                 : 
     202                 : 
     203                 : #endif
     204                 : 
     205                 : 
     206                 : %}
     207                 : 
     208                 : %union {
     209                 :   ATerm t;
     210                 :   ATermList ts;
     211                 :   struct {
     212                 :     ATermList formals;
     213                 :     bool ellipsis;
     214                 :   } formals;
     215                 : }
     216                 : 
     217                 : %type <t> start expr expr_function expr_if expr_op
     218                 : %type <t> expr_app expr_select expr_simple bind inheritsrc formal
     219                 : %type <t> pattern pattern2
     220                 : %type <ts> binds ids expr_list string_parts ind_string_parts
     221                 : %type <formals> formals
     222                 : %token <t> ID INT STR IND_STR PATH URI
     223                 : %token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
     224                 : %token DOLLAR_CURLY /* == ${ */
     225                 : %token IND_STRING_OPEN IND_STRING_CLOSE
     226                 : %token ELLIPSIS
     227                 : 
     228                 : %nonassoc IMPL
     229                 : %left OR
     230                 : %left AND
     231                 : %nonassoc EQ NEQ
     232                 : %right UPDATE
     233                 : %left NEG
     234                 : %left '+'
     235                 : %right CONCAT
     236                 : %nonassoc '?'
     237                 : %nonassoc '~'
     238                 : 
     239                 : %%
     240                 : 
     241             184 : start: expr { data->result = $1; };
     242             184 : 
     243                 : expr: expr_function;
     244                 : 
     245                 : expr_function
     246                 :   : pattern ':' expr_function
     247             506 :     { $$ = makeFunction($1, $3, CUR_POS); }
     248             506 :   | ASSERT expr ';' expr_function
     249              40 :     { $$ = makeAssert($2, $4, CUR_POS); }
     250              40 :   | WITH expr ';' expr_function
     251              16 :     { $$ = makeWith($2, $4, CUR_POS); }
     252              16 :   | LET binds IN expr_function
     253              40 :     { $$ = makeSelect(fixAttrs(1, ATinsert($2, makeBind(toATerm("<let-body>"), $4, CUR_POS))), toATerm("<let-body>")); }
     254              40 :   | expr_if
     255                 :   ;
     256                 : 
     257                 : expr_if
     258                 :   : IF expr THEN expr ELSE expr
     259             190 :     { $$ = makeIf($2, $4, $6); }
     260             190 :   | expr_op
     261                 :   ;
     262                 : 
     263                 : expr_op
     264              14 :   : '!' expr_op %prec NEG { $$ = makeOpNot($2); }
     265              14 :   | expr_op EQ expr_op { $$ = makeOpEq($1, $3); }
     266              96 :   | expr_op NEQ expr_op { $$ = makeOpNEq($1, $3); }
     267              10 :   | expr_op AND expr_op { $$ = makeOpAnd($1, $3); }
     268             122 :   | expr_op OR expr_op { $$ = makeOpOr($1, $3); }
     269               2 :   | expr_op IMPL expr_op { $$ = makeOpImpl($1, $3); }
     270               8 :   | expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); }
     271              41 :   | expr_op '~' expr_op { $$ = makeSubPath($1, $3); }
     272              17 :   | expr_op '?' ID { $$ = makeOpHasAttr($1, $3); }
     273             190 :   | expr_op '+' expr_op { $$ = makeOpPlus($1, $3); }
     274             123 :   | expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); }
     275              39 :   | expr_app
     276                 :   ;
     277                 : 
     278                 : expr_app
     279                 :   : expr_app expr_select
     280            2019 :     { $$ = makeCall($1, $2); }
     281            2019 :   | expr_select { $$ = $1; }
     282            3667 :   ;
     283                 : 
     284                 : expr_select
     285                 :   : expr_select '.' ID
     286             485 :     { $$ = makeSelect($1, $3); }
     287             485 :   | expr_simple { $$ = $1; }
     288            6561 :   ;
     289                 : 
     290                 : expr_simple
     291            2533 :   : ID { $$ = makeVar($1); }
     292            2533 :   | INT { $$ = makeInt(ATgetInt((ATermInt) $1)); }
     293             195 :   | '"' string_parts '"' {
     294                 :       /* For efficiency, and to simplify parse trees a bit. */
     295            1955 :       if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty);
     296            1932 :       else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2);
     297              24 :       else $$ = makeConcatStrings(ATreverse($2));
     298                 :   }
     299            1955 :   | IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
     300              15 :       $$ = stripIndentation(ATreverse($2));
     301                 :   }
     302              15 :   | PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); }
     303             253 :   | URI { $$ = makeStr($1, ATempty); }
     304               8 :   | '(' expr ')' { $$ = $2; }
     305             718 :   /* Let expressions `let {..., body = ...}' are just desugared
     306                 :      into `(rec {..., body = ...}).body'. */
     307                 :   | LET '{' binds '}'
     308              69 :     { $$ = makeSelect(fixAttrs(1, $3), toATerm("body")); }
     309              69 :   | REC '{' binds '}'
     310              31 :     { $$ = fixAttrs(1, $3); }
     311              31 :   | '{' binds '}'
     312             367 :     { $$ = fixAttrs(0, $2); }
     313             367 :   | '[' expr_list ']' { $$ = makeList($2); }
     314             425 :   ;
     315                 : 
     316                 : string_parts
     317            1953 :   : string_parts STR { $$ = ATinsert($1, $2); }
     318            1953 :   | string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = ATinsert($1, $3); }
     319              25 :   | { $$ = ATempty; }
     320            1955 :   ;
     321                 : 
     322                 : ind_string_parts
     323              39 :   : ind_string_parts IND_STR { $$ = ATinsert($1, $2); }
     324              39 :   | ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = ATinsert($1, $3); }
     325              14 :   | { $$ = ATempty; }
     326              15 :   ;
     327                 : 
     328                 : pattern
     329               6 :   : pattern2 '@' pattern { $$ = makeAtPat($1, $3); }
     330               6 :   | pattern2
     331                 :   ;
     332                 : 
     333                 : pattern2
     334             413 :   : ID { $$ = makeVarPat($1); }
     335             413 :   | '{' formals '}' { $$ = makeAttrsPat($2.formals, $2.ellipsis ? eTrue : eFalse); }
     336              99 :   ;
     337                 : 
     338                 : binds
     339            1886 :   : binds bind { $$ = ATinsert($1, $2); }
     340            1886 :   | { $$ = ATempty; }
     341             508 :   ;
     342                 : 
     343                 : bind
     344                 :   : ID '=' expr ';'
     345            1751 :     { $$ = makeBind($1, $3, CUR_POS); }
     346            1751 :   | INHERIT inheritsrc ids ';'
     347             135 :     { $$ = makeInherit($2, $3, CUR_POS); }
     348             135 :   ;
     349                 : 
     350                 : inheritsrc
     351               3 :   : '(' expr ')' { $$ = $2; }
     352               3 :   | { $$ = makeScope(); }
     353             132 :   ;
     354                 : 
     355             337 : ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; };
     356             337 : 
     357                 : expr_list
     358             875 :   : expr_select expr_list { $$ = ATinsert($2, $1); }
     359             875 :     /* yes, this is right-recursive, but it doesn't matter since
     360                 :        otherwise we would need ATreverse which requires unbounded
     361                 :        stack space */
     362             425 :   | { $$ = ATempty; }
     363             425 :   ;
     364                 : 
     365                 : formals
     366                 :   : formal ',' formals /* idem - right recursive */
     367             171 :     { $$.formals = ATinsert($3.formals, $1); $$.ellipsis = $3.ellipsis; }
     368             171 :   | formal
     369              96 :     { $$.formals = ATinsert(ATempty, $1); $$.ellipsis = false; }
     370              96 :   |
     371               2 :     { $$.formals = ATempty; $$.ellipsis = false; }
     372               2 :   | ELLIPSIS
     373               2 :     { $$.formals = ATempty; $$.ellipsis = true; }
     374               2 :   ;
     375                 : 
     376                 : formal
     377             189 :   : ID { $$ = makeFormal($1, makeNoDefaultValue()); }
     378             189 :   | ID '?' expr { $$ = makeFormal($1, makeDefaultValue($3)); }
     379                 :   ;
     380                 :   
     381                 : %%
     382                 : 
     383                 : 
     384                 : #include "eval.hh"  
     385                 : 
     386                 : #include <sys/types.h>
     387                 : #include <sys/stat.h>
     388                 : #include <fcntl.h>
     389                 : #include <unistd.h>
     390                 : 
     391                 : 
     392                 : namespace nix {
     393                 :       
     394                 : 
     395             643 : static void checkAttrs(ATermMap & names, ATermList bnds)
     396                 : {
     397            2629 :     for (ATermIterator i(bnds); i; ++i) {
     398                 :         ATerm name;
     399                 :         Expr e;
     400                 :         ATerm pos;
     401            1989 :         if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
     402            1989 :         if (names.get(name))
     403                 :             throw EvalError(format("duplicate attribute `%1%' at %2%")
     404               3 :                 % aterm2String(name) % showPos(pos));
     405            1986 :         names.set(name, name);
     406                 :     }
     407             640 : }
     408                 : 
     409                 : 
     410             516 : static void checkPatternVars(ATerm pos, ATermMap & map, Pattern pat)
     411                 : {
     412                 :     ATerm name;
     413                 :     ATermList formals;
     414                 :     Pattern pat1, pat2;
     415                 :     ATermBool ellipsis;
     416             516 :     if (matchVarPat(pat, name)) {
     417             412 :         if (map.get(name))
     418                 :             throw EvalError(format("duplicate formal function argument `%1%' at %2%")
     419               0 :                 % aterm2String(name) % showPos(pos));
     420             412 :         map.set(name, name);
     421                 :     }
     422             104 :     else if (matchAttrsPat(pat, formals, ellipsis)) { 
     423             357 :         for (ATermIterator i(formals); i; ++i) {
     424                 :             ATerm d1;
     425             261 :             if (!matchFormal(*i, name, d1)) abort();
     426             261 :             if (map.get(name))
     427                 :                 throw EvalError(format("duplicate formal function argument `%1%' at %2%")
     428               2 :                     % aterm2String(name) % showPos(pos));
     429             259 :             map.set(name, name);
     430                 :         }
     431                 :     }
     432               6 :     else if (matchAtPat(pat, pat1, pat2)) {
     433               6 :         checkPatternVars(pos, map, pat1);
     434               6 :         checkPatternVars(pos, map, pat2);
     435                 :     }
     436               0 :     else abort();
     437             513 : }
     438                 : 
     439                 : 
     440           35362 : static void checkAttrSets(ATerm e)
     441                 : {
     442                 :     ATerm pat, body, pos;
     443           35362 :     if (matchFunction(e, pat, body, pos)) {
     444             504 :         ATermMap map(16);
     445             504 :         checkPatternVars(pos, map, pat);
     446                 :     }
     447                 : 
     448                 :     ATermList bnds;
     449           35360 :     if (matchAttrs(e, bnds)) {
     450             365 :         ATermMap names(ATgetLength(bnds));
     451             365 :         checkAttrs(names, bnds);
     452                 :     }
     453                 :     
     454                 :     ATermList rbnds, nrbnds;
     455           35358 :     if (matchRec(e, rbnds, nrbnds)) {
     456             139 :         ATermMap names(ATgetLength(rbnds) + ATgetLength(nrbnds));
     457             139 :         checkAttrs(names, rbnds);
     458             139 :         checkAttrs(names, nrbnds);
     459                 :     }
     460                 :     
     461           35357 :     if (ATgetType(e) == AT_APPL) {
     462           26871 :         int arity = ATgetArity(ATgetAFun(e));
     463           58808 :         for (int i = 0; i < arity; ++i)
     464           31943 :             checkAttrSets(ATgetArgument(e, i));
     465                 :     }
     466                 : 
     467            8486 :     else if (ATgetType(e) == AT_LIST)
     468            6455 :         for (ATermIterator i((ATermList) e); i; ++i)
     469            3237 :             checkAttrSets(*i);
     470           35349 : }
     471                 : 
     472                 : 
     473                 : static Expr parse(EvalState & state,
     474                 :     const char * text, const Path & path,
     475             185 :     const Path & basePath)
     476                 : {
     477                 :     yyscan_t scanner;
     478             185 :     ParseData data;
     479             185 :     data.basePath = basePath;
     480             185 :     data.path = path;
     481                 : 
     482             185 :     yylex_init(&scanner);
     483             185 :     yy_scan_string(text, scanner);
     484             185 :     int res = yyparse(scanner, &data);
     485             185 :     yylex_destroy(scanner);
     486                 :     
     487             185 :     if (res) throw EvalError(data.error);
     488                 : 
     489                 :     try {
     490             184 :         checkVarDefs(state.primOps, data.result);
     491               4 :     } catch (Error & e) {
     492               2 :         throw EvalError(format("%1%, in `%2%'") % e.msg() % path);
     493                 :     }
     494                 :     
     495             182 :     checkAttrSets(data.result);
     496                 : 
     497             177 :     return data.result;
     498                 : }
     499                 : 
     500                 : 
     501             171 : Expr parseExprFromFile(EvalState & state, Path path)
     502                 : {
     503             171 :     assert(path[0] == '/');
     504                 : 
     505                 : #if 0
     506                 :     /* Perhaps this is already an imploded parse tree? */
     507                 :     Expr e = ATreadFromNamedFile(path.c_str());
     508                 :     if (e) return e;
     509                 : #endif
     510                 : 
     511                 :     /* If `path' is a symlink, follow it.  This is so that relative
     512                 :        path references work. */
     513                 :     struct stat st;
     514               0 :     while (true) {
     515             171 :         if (lstat(path.c_str(), &st))
     516               0 :             throw SysError(format("getting status of `%1%'") % path);
     517             171 :         if (!S_ISLNK(st.st_mode)) break;
     518               0 :         path = absPath(readLink(path), dirOf(path));
     519                 :     }
     520                 : 
     521                 :     /* If `path' refers to a directory, append `/default.nix'. */
     522             171 :     if (stat(path.c_str(), &st))
     523               0 :         throw SysError(format("getting status of `%1%'") % path);
     524             171 :     if (S_ISDIR(st.st_mode))
     525              46 :         path = canonPath(path + "/default.nix");
     526                 : 
     527                 :     /* Read and parse the input file. */
     528             171 :     return parse(state, readFile(path).c_str(), path, dirOf(path));
     529                 : }
     530                 : 
     531                 : 
     532                 : Expr parseExprFromString(EvalState & state,
     533              14 :     const string & s, const Path & basePath)
     534                 : {
     535              14 :     return parse(state, s.c_str(), "(string)", basePath);
     536                 : }
     537                 : 
     538               0 :  
     539                 : }

Generated by: LTP GCOV extension version 1.6