Skip to content

Commit 0918203

Browse files
committed
fix(ast): nested unary operators always need parens
1 parent 541b579 commit 0918203

File tree

1 file changed

+17
-1
lines changed

1 file changed

+17
-1
lines changed

src/ast.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,14 +265,30 @@ function precLevel(nodeType: RegExpAST['type']) {
265265
checkedAllCases(nodeType)
266266
}
267267

268+
/**
269+
* AST nodes where no parenthesis have to be added when the parent has the same type.
270+
* E.g. in /a|(b|c)/ we can leave out the parenthesis /a|b|c/ but in /(a+)+/ the parenthesis
271+
* are needed, otherwise the expression is invalid.
272+
*/
273+
const needsNoParensOnSamePrecLevel = new Set([
274+
'concat',
275+
'positive-lookahead',
276+
'negative-lookahead',
277+
'start-marker',
278+
'end-marker',
279+
'union',
280+
])
281+
268282
/**
269283
* Surrounds expression with parenthesis if necessary. For example, in `/(a)+|b/` the parenthesis
270284
* around `a` are not necessary because `+` has higher precedence than `|`. On the other hand,
271285
* in `/(a|b)+/` the parenthesis around `a|b` are necessary. Otherwise the expression has different
272286
* semantics.
273287
*/
274288
function maybeWithParens(ast: RegExpAST, parent: RegExpAST, options: RenderOptions): string {
275-
if (ast.type === parent.type || precLevel(ast.type) > precLevel(parent.type))
289+
if (precLevel(ast.type) > precLevel(parent.type))
290+
return toString(ast, options)
291+
else if (ast.type === parent.type && needsNoParensOnSamePrecLevel.has(ast.type))
276292
return toString(ast, options)
277293
else if (options.useNonCapturingGroups)
278294
return '(?:' + toString(ast, options) + ')'

0 commit comments

Comments
 (0)