* String interpolation. Expressions like
"--with-freetype2-library=" + freetype + "/lib"
  can now be written as
    "--with-freetype2-library=${freetype}/lib"
  An arbitrary expression can be enclosed within ${...}, not just
  identifiers.
* Escaping in string literals: \n, \r, \t interpreted as in C, any
  other character following \ is interpreted as-is.
  
* Newlines are now allowed in string literals.
			
			
This commit is contained in:
		
							parent
							
								
									6cecad2be0
								
							
						
					
					
						commit
						0064599a27
					
				
					 7 changed files with 88 additions and 15 deletions
				
			
		|  | @ -231,7 +231,7 @@ static ATerm concatStrings(EvalState & state, const ATermVector & args) | ||||||
| { | { | ||||||
|     ATermList context = ATempty; |     ATermList context = ATempty; | ||||||
|     ostringstream s; |     ostringstream s; | ||||||
|     bool isPath; |     bool isPath = false; | ||||||
| 
 | 
 | ||||||
|     for (ATermVector::const_iterator i = args.begin(); i != args.end(); ++i) { |     for (ATermVector::const_iterator i = args.begin(); i != args.end(); ++i) { | ||||||
|         bool isPath2; |         bool isPath2; | ||||||
|  | @ -449,6 +449,14 @@ Expr evalExpr2(EvalState & state, Expr e) | ||||||
|         return makeList(ATconcat(l1, l2)); |         return makeList(ATconcat(l1, l2)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /* String concatenation. */ | ||||||
|  |     ATermList es; | ||||||
|  |     if (matchConcatStrings(e, es)) { | ||||||
|  |         ATermVector args; | ||||||
|  |         for (ATermIterator i(es); i; ++i) args.push_back(*i); | ||||||
|  |         return concatStrings(state, args); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /* Barf. */ |     /* Barf. */ | ||||||
|     throw badTerm("invalid expression", e); |     throw badTerm("invalid expression", e); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,6 +3,9 @@ | ||||||
| %option never-interactive | %option never-interactive | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | %x STRING | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| %{ | %{ | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <aterm2.h> | #include <aterm2.h> | ||||||
|  | @ -28,6 +31,9 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | ATerm toATerm(const char * s); | ||||||
|  | ATerm unescapeStr(const char * s); | ||||||
|  | 
 | ||||||
| #define YY_USER_INIT initLoc(yylloc) | #define YY_USER_INIT initLoc(yylloc) | ||||||
| #define YY_USER_ACTION adjustLoc(yylloc, yytext, yyleng); | #define YY_USER_ACTION adjustLoc(yylloc, yytext, yyleng); | ||||||
| 
 | 
 | ||||||
|  | @ -36,7 +42,6 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) | ||||||
| 
 | 
 | ||||||
| ID          [a-zA-Z\_][a-zA-Z0-9\_\']* | ID          [a-zA-Z\_][a-zA-Z0-9\_\']* | ||||||
| INT         [0-9]+ | INT         [0-9]+ | ||||||
| STR         \"[^\n\"]*\" |  | ||||||
| PATH        [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+ | PATH        [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+ | ||||||
| URI         [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+ | URI         [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+ | ||||||
| 
 | 
 | ||||||
|  | @ -61,19 +66,27 @@ inherit     { return INHERIT; } | ||||||
| \/\/        { return UPDATE; } | \/\/        { return UPDATE; } | ||||||
| \+\+        { return CONCAT; } | \+\+        { return CONCAT; } | ||||||
| 
 | 
 | ||||||
| {ID}        { yylval->t = ATmake("<str>", yytext); return ID; /* !!! alloc */ } | {ID}        { yylval->t = toATerm(yytext); return ID; /* !!! alloc */ } | ||||||
| {INT}       { int n = atoi(yytext); /* !!! overflow */ | {INT}       { int n = atoi(yytext); /* !!! overflow */ | ||||||
|               yylval->t = ATmake("<int>", n); |               yylval->t = ATmake("<int>", n); | ||||||
|               return INT; |               return INT; | ||||||
|             } |             } | ||||||
| {STR}       { int len = strlen(yytext); | 
 | ||||||
|               yytext[len - 1] = 0; | \"          { BEGIN(STRING); return '"'; } | ||||||
|               yylval->t = ATmake("<str>", yytext + 1); | <STRING>([^\$\"\\]|\\.|\$[^\{\$])+ { | ||||||
|               yytext[len - 1] = '\"'; | /* Note: a dollar *is* allowed as-is in a string, as long as it's | ||||||
|               return STR; /* !!! alloc */ |    not followed by a open brace.  This should probably be disallowed | ||||||
|  |    eventually. */ | ||||||
|  |               yylval->t = unescapeStr(yytext); /* !!! alloc */  | ||||||
|  |               return STR; | ||||||
|             } |             } | ||||||
| {PATH}      { yylval->t = ATmake("<str>", yytext); return PATH; /* !!! alloc */ } | <STRING>\$\{  { BEGIN(INITIAL); return DOLLAR_CURLY; } | ||||||
| {URI}       { yylval->t = ATmake("<str>", yytext); return URI; /* !!! alloc */ } | <STRING>\"  { BEGIN(INITIAL); return '"'; } | ||||||
|  | <STRING>.   return yytext[0]; /* just in case: shouldn't be reached */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | {PATH}      { yylval->t = toATerm(yytext); return PATH; /* !!! alloc */ } | ||||||
|  | {URI}       { yylval->t = toATerm(yytext); return URI; /* !!! alloc */ } | ||||||
| 
 | 
 | ||||||
| [ \t\n]+    /* eat up whitespace */ | [ \t\n]+    /* eat up whitespace */ | ||||||
| \#[^\n]*    /* single-line comments */ | \#[^\n]*    /* single-line comments */ | ||||||
|  | @ -83,3 +96,13 @@ inherit     { return INHERIT; } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| %% | %% | ||||||
|  | 
 | ||||||
|  | /* Horrible, disgusting hack: allow the parser to set the scanner | ||||||
|  |    start condition back to STRING.  Necessary in interpolations like | ||||||
|  |    "foo${expr}bar"; after the close brace we have to go back to the | ||||||
|  |    STRING state. */ | ||||||
|  | void backToString(yyscan_t scanner) | ||||||
|  | { | ||||||
|  |     struct yyguts_t * yyg = (struct yyguts_t*) scanner; | ||||||
|  |     BEGIN(STRING); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ SubPath | Expr Expr | Expr | | ||||||
| OpHasAttr | Expr string | Expr | | OpHasAttr | Expr string | Expr | | ||||||
| OpPlus | Expr Expr | Expr | | OpPlus | Expr Expr | Expr | | ||||||
| OpConcat | Expr Expr | Expr | | OpConcat | Expr Expr | Expr | | ||||||
|  | ConcatStrings | ATermList | Expr | | ||||||
| Call | Expr Expr | Expr | | Call | Expr Expr | Expr | | ||||||
| Select | Expr string | Expr | | Select | Expr string | Expr | | ||||||
| Var | string | Expr | | Var | string | Expr | | ||||||
|  |  | ||||||
|  | @ -71,9 +71,29 @@ const char * getPath(ParseData * data) | ||||||
|     return data->path.c_str(); |     return data->path.c_str(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int yyparse(yyscan_t scanner, ParseData * data); | Expr unescapeStr(const char * s) | ||||||
|  | { | ||||||
|  |     string t; | ||||||
|  |     char c; | ||||||
|  |     while (c = *s++) { | ||||||
|  |         if (c == '\\') { | ||||||
|  |             assert(*s); | ||||||
|  |             c = *s++; | ||||||
|  |             if (c == 'n') t += "\n"; | ||||||
|  |             else if (c == 'r') t += "\r"; | ||||||
|  |             else if (c == 't') t += "\t"; | ||||||
|  |             else t += c; | ||||||
|  |         } | ||||||
|  |         else t += c; | ||||||
|  |     } | ||||||
|  |     return makeStr(toATerm(t)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int yyparse(yyscan_t scanner, ParseData * data); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } /* end of C functions */ | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| static void checkAttrs(ATermMap & names, ATermList bnds) | static void checkAttrs(ATermMap & names, ATermList bnds) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ void parseError(void * data, char * error, int line, int column); | ||||||
| ATerm absParsedPath(void * data, ATerm t); | ATerm absParsedPath(void * data, ATerm t); | ||||||
| ATerm fixAttrs(int recursive, ATermList as); | ATerm fixAttrs(int recursive, ATermList as); | ||||||
| const char * getPath(void * data); | const char * getPath(void * data); | ||||||
|  | void backToString(yyscan_t scanner); | ||||||
| 
 | 
 | ||||||
| void yyerror(YYLTYPE * loc, yyscan_t scanner, void * data, char * s) | void yyerror(YYLTYPE * loc, yyscan_t scanner, void * data, char * s) | ||||||
| { | { | ||||||
|  | @ -73,9 +74,10 @@ static void freeAndUnprotect(void * p) | ||||||
| 
 | 
 | ||||||
| %type <t> start expr expr_function expr_if expr_op | %type <t> start expr expr_function expr_if expr_op | ||||||
| %type <t> expr_app expr_select expr_simple bind inheritsrc formal | %type <t> expr_app expr_select expr_simple bind inheritsrc formal | ||||||
| %type <ts> binds ids expr_list formals | %type <ts> binds ids expr_list formals string_parts | ||||||
| %token <t> ID INT STR PATH URI | %token <t> ID INT STR PATH URI | ||||||
| %token IF THEN ELSE ASSERT WITH LET REC INHERIT EQ NEQ AND OR IMPL | %token IF THEN ELSE ASSERT WITH LET REC INHERIT EQ NEQ AND OR IMPL | ||||||
|  | %token DOLLAR_CURLY /* == ${ */ | ||||||
| 
 | 
 | ||||||
| %nonassoc IMPL | %nonassoc IMPL | ||||||
| %left OR | %left OR | ||||||
|  | @ -142,7 +144,12 @@ expr_select | ||||||
| expr_simple | expr_simple | ||||||
|   : ID { $$ = makeVar($1); } |   : ID { $$ = makeVar($1); } | ||||||
|   | INT { $$ = makeInt(ATgetInt((ATermInt) $1)); } |   | INT { $$ = makeInt(ATgetInt((ATermInt) $1)); } | ||||||
|   | STR { $$ = makeStr($1); } |   | '"' string_parts '"' { | ||||||
|  |       /* For efficiency, and to simplify parse trees a bit. */ | ||||||
|  |       if ($2 == ATempty) $$ = makeStr(toATerm("")); | ||||||
|  |       else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2); | ||||||
|  |       else $$ = makeConcatStrings(ATreverse($2)); | ||||||
|  |   } | ||||||
|   | PATH { $$ = makePath(absParsedPath(data, $1)); } |   | PATH { $$ = makePath(absParsedPath(data, $1)); } | ||||||
|   | URI { $$ = makeUri($1); } |   | URI { $$ = makeUri($1); } | ||||||
|   | '(' expr ')' { $$ = $2; } |   | '(' expr ')' { $$ = $2; } | ||||||
|  | @ -157,6 +164,12 @@ expr_simple | ||||||
|   | '[' expr_list ']' { $$ = makeList($2); } |   | '[' expr_list ']' { $$ = makeList($2); } | ||||||
|   ; |   ; | ||||||
| 
 | 
 | ||||||
|  | string_parts | ||||||
|  |   : string_parts STR { $$ = ATinsert($1, $2); } | ||||||
|  |   | string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = ATinsert($1, $3); } | ||||||
|  |   | { $$ = ATempty; } | ||||||
|  |   ; | ||||||
|  | 
 | ||||||
| binds | binds | ||||||
|   : binds bind { $$ = ATinsert($1, $2); } |   : binds bind { $$ = ATinsert($1, $2); } | ||||||
|   | { $$ = ATempty; } |   | { $$ = ATempty; } | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| Str("foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/y") | Str("foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/yescape: \"quote\" \n \\end\nof\nlinefoobarblaat") | ||||||
|  |  | ||||||
|  | @ -1 +1,9 @@ | ||||||
| "foo" + "bar" + toString (/a/b + /c/d) + (/foo/bar + "/../xyzzy/." + "/foo.txt") + ("/../foo" + /x/y) | "foo" + "bar" | ||||||
|  |   + toString (/a/b + /c/d) | ||||||
|  |   + (/foo/bar + "/../xyzzy/." + "/foo.txt") | ||||||
|  |   + ("/../foo" + /x/y) | ||||||
|  |   + "escape: \"quote\" \n \\" | ||||||
|  |   + "end | ||||||
|  | of | ||||||
|  | line" | ||||||
|  |   + "foo${if true then "b${"a" + "r"}" else "xyzzy"}blaat" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue