Show More
@@ -1071,46 +1071,85 b' class revsetalias(object):' | |||
|
1071 | 1071 | h = heads(default) |
|
1072 | 1072 | b($1) = ancestors($1) - ancestors(default) |
|
1073 | 1073 | ''' |
|
1074 | if isinstance(name, tuple): # parameter substitution | |
|
1075 | self.tree = name | |
|
1076 |
self. |
|
|
1077 | else: # alias definition | |
|
1078 | m = self.funcre.search(name) | |
|
1079 |
|
|
|
1080 | self.tree = ('func', ('symbol', m.group(1))) | |
|
1081 | self.args = [x.strip() for x in m.group(2).split(',')] | |
|
1082 | for arg in self.args: | |
|
1083 | value = value.replace(arg, repr(arg)) | |
|
1084 | else: | |
|
1085 | self.tree = ('symbol', name) | |
|
1074 | m = self.funcre.search(name) | |
|
1075 | if m: | |
|
1076 | self.name = m.group(1) | |
|
1077 | self.tree = ('func', ('symbol', m.group(1))) | |
|
1078 | self.args = [x.strip() for x in m.group(2).split(',')] | |
|
1079 | for arg in self.args: | |
|
1080 | value = value.replace(arg, repr(arg)) | |
|
1081 | else: | |
|
1082 | self.name = name | |
|
1083 | self.tree = ('symbol', name) | |
|
1084 | ||
|
1085 | self.replacement, pos = parse(value) | |
|
1086 | if pos != len(value): | |
|
1087 | raise error.ParseError(_('invalid token'), pos) | |
|
1086 | 1088 | |
|
1087 | self.replacement, pos = parse(value) | |
|
1088 | if pos != len(value): | |
|
1089 | raise error.ParseError(_('invalid token'), pos) | |
|
1089 | def _getalias(aliases, tree): | |
|
1090 | """If tree looks like an unexpanded alias, return it. Return None | |
|
1091 | otherwise. | |
|
1092 | """ | |
|
1093 | if isinstance(tree, tuple) and tree: | |
|
1094 | if tree[0] == 'symbol' and len(tree) == 2: | |
|
1095 | name = tree[1] | |
|
1096 | alias = aliases.get(name) | |
|
1097 | if alias and alias.args is None and alias.tree == tree: | |
|
1098 | return alias | |
|
1099 | if tree[0] == 'func' and len(tree) > 1: | |
|
1100 | if tree[1][0] == 'symbol' and len(tree[1]) == 2: | |
|
1101 | name = tree[1][1] | |
|
1102 | alias = aliases.get(name) | |
|
1103 | if alias and alias.args is not None and alias.tree == tree[:2]: | |
|
1104 | return alias | |
|
1105 | return None | |
|
1090 | 1106 | |
|
1091 | def process(self, tree): | |
|
1092 | if isinstance(tree, tuple): | |
|
1093 | if self.args is None: | |
|
1094 | if tree == self.tree: | |
|
1095 | return self.replacement | |
|
1096 | elif tree[:2] == self.tree: | |
|
1097 | l = getlist(tree[2]) | |
|
1098 | if len(l) != len(self.args): | |
|
1099 | raise error.ParseError( | |
|
1100 | _('invalid number of arguments: %s') % len(l)) | |
|
1101 | result = self.replacement | |
|
1102 | for a, v in zip(self.args, l): | |
|
1103 | valalias = revsetalias(('string', a), v) | |
|
1104 | result = valalias.process(result) | |
|
1105 | return result | |
|
1106 | return tuple(map(self.process, tree)) | |
|
1107 | def _expandargs(tree, args): | |
|
1108 | """Replace all occurences of ('string', name) with the | |
|
1109 | substitution value of the same name in args, recursively. | |
|
1110 | """ | |
|
1111 | if not isinstance(tree, tuple): | |
|
1112 | return tree | |
|
1113 | if len(tree) == 2 and tree[0] == 'string': | |
|
1114 | return args.get(tree[1], tree) | |
|
1115 | return tuple(_expandargs(t, args) for t in tree) | |
|
1116 | ||
|
1117 | def _expandaliases(aliases, tree, expanding): | |
|
1118 | """Expand aliases in tree, recursively. | |
|
1119 | ||
|
1120 | 'aliases' is a dictionary mapping user defined aliases to | |
|
1121 | revsetalias objects. | |
|
1122 | """ | |
|
1123 | if not isinstance(tree, tuple): | |
|
1124 | # Do not expand raw strings | |
|
1107 | 1125 | return tree |
|
1126 | alias = _getalias(aliases, tree) | |
|
1127 | if alias is not None: | |
|
1128 | if alias in expanding: | |
|
1129 | raise error.ParseError(_('infinite expansion of revset alias "%s" ' | |
|
1130 | 'detected') % alias.name) | |
|
1131 | expanding.append(alias) | |
|
1132 | result = alias.replacement | |
|
1133 | if alias.args is not None: | |
|
1134 | l = getlist(tree[2]) | |
|
1135 | if len(l) != len(alias.args): | |
|
1136 | raise error.ParseError( | |
|
1137 | _('invalid number of arguments: %s') % len(l)) | |
|
1138 | result = _expandargs(result, dict(zip(alias.args, l))) | |
|
1139 | # Recurse in place, the base expression may have been rewritten | |
|
1140 | result = _expandaliases(aliases, result, expanding) | |
|
1141 | expanding.pop() | |
|
1142 | else: | |
|
1143 | result = tuple(_expandaliases(aliases, t, expanding) | |
|
1144 | for t in tree) | |
|
1145 | return result | |
|
1108 | 1146 | |
|
1109 | 1147 | def findaliases(ui, tree): |
|
1148 | aliases = {} | |
|
1110 | 1149 | for k, v in ui.configitems('revsetalias'): |
|
1111 | 1150 | alias = revsetalias(k, v) |
|
1112 | tree = alias.process(tree) | |
|
1113 | return tree | |
|
1151 | aliases[alias.name] = alias | |
|
1152 | return _expandaliases(aliases, tree, []) | |
|
1114 | 1153 | |
|
1115 | 1154 | parse = parser.parser(tokenize, elements).parse |
|
1116 | 1155 |
@@ -430,6 +430,7 b' aliases:' | |||
|
430 | 430 | |
|
431 | 431 | $ echo '[revsetalias]' >> .hg/hgrc |
|
432 | 432 | $ echo 'm = merge()' >> .hg/hgrc |
|
433 | $ echo 'sincem = descendants(m)' >> .hg/hgrc | |
|
433 | 434 | $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc |
|
434 | 435 | $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc |
|
435 | 436 | $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc |
@@ -438,6 +439,46 b' aliases:' | |||
|
438 | 439 | ('symbol', 'm') |
|
439 | 440 | ('func', ('symbol', 'merge'), None) |
|
440 | 441 | 6 |
|
442 | ||
|
443 | test alias recursion | |
|
444 | ||
|
445 | $ try sincem | |
|
446 | ('symbol', 'sincem') | |
|
447 | ('func', ('symbol', 'descendants'), ('func', ('symbol', 'merge'), None)) | |
|
448 | 6 | |
|
449 | 7 | |
|
450 | ||
|
451 | test infinite recursion | |
|
452 | ||
|
453 | $ echo 'recurse1 = recurse2' >> .hg/hgrc | |
|
454 | $ echo 'recurse2 = recurse1' >> .hg/hgrc | |
|
455 | $ try recurse1 | |
|
456 | ('symbol', 'recurse1') | |
|
457 | hg: parse error: infinite expansion of revset alias "recurse1" detected | |
|
458 | [255] | |
|
459 | ||
|
460 | test nesting and variable passing | |
|
461 | ||
|
462 | $ echo 'nested($1) = nested2($1)' >> .hg/hgrc | |
|
463 | $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc | |
|
464 | $ echo 'nested3($1) = max($1)' >> .hg/hgrc | |
|
465 | $ try 'nested(2:5)' | |
|
466 | ('func', ('symbol', 'nested'), ('range', ('symbol', '2'), ('symbol', '5'))) | |
|
467 | ('func', ('symbol', 'max'), ('range', ('symbol', '2'), ('symbol', '5'))) | |
|
468 | 5 | |
|
469 | ||
|
470 | test variable isolation, variable placeholders are rewritten as string | |
|
471 | then parsed and matched again as string. Check they do not leak too | |
|
472 | far away. | |
|
473 | ||
|
474 | $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc | |
|
475 | $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc | |
|
476 | $ try 'callinjection(2:5)' | |
|
477 | ('func', ('symbol', 'callinjection'), ('range', ('symbol', '2'), ('symbol', '5'))) | |
|
478 | ('func', ('symbol', 'descendants'), ('func', ('symbol', 'max'), ('string', '$1'))) | |
|
479 | abort: unknown revision '$1'! | |
|
480 | [255] | |
|
481 | ||
|
441 | 482 | $ try 'd(2:5)' |
|
442 | 483 | ('func', ('symbol', 'd'), ('range', ('symbol', '2'), ('symbol', '5'))) |
|
443 | 484 | ('func', ('symbol', 'reverse'), ('func', ('symbol', 'sort'), ('list', ('range', ('symbol', '2'), ('symbol', '5')), ('symbol', 'date')))) |
General Comments 0
You need to be logged in to leave comments.
Login now