Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Commit e4822b6

Browse files
committed
more tests
1 parent 95a050a commit e4822b6

File tree

2 files changed

+254
-227
lines changed

2 files changed

+254
-227
lines changed

lib/snippet-body.pegjs

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Parse issues (such as unclosed tab stops or invalid regexes) are simply treated
3535
as plain text.
3636
3737
NOTE: PEG.js is not designed for efficiency. With appropriate benchmarks, it should
38-
be a significant gain to hand write a parser.
38+
be a significant gain to hand write a parser (and remove the PEG.js dependency).
3939
4040
*/
4141

@@ -55,11 +55,11 @@ be a significant gain to hand write a parser.
5555
}
5656
}
5757

58-
// Grab anything that isn't \ or $, then try to build a special node out of it, and (at the top level) if that fails then just accept it as text
59-
topLevelContent = content:(text / escapedTopLevel / tabStop / choice / variable / any)* { return coalesce(content); }
58+
// Grab anything that isn't \ or $, then try to build a special node out of it, and (at the top level) if that fails then just accept the first character as text and continue
59+
topLevelContent = c:(text / escapedTopLevel / tabStop / choice / variable / .)* { return coalesce(c); }
6060

61-
// Placeholder content. The same as top level, except we need to fail on '}' so that it can end the tab stop (the `any` rule would eat it if we used it here)
62-
tabStopContent = content:(tabStopText / escapedTabStop / tabStop / choice / variable / notCloseBrace)* { return coalesce(content); }
61+
// Placeholder content. The same as top level, except we need to fail on '}' so that it can end the tab stop (the any matcher would eat it if we used it here)
62+
tabStopContent = c:(tabStopText / escapedTabStop / tabStop / choice / variable / [^}])* { return coalesce(c); }
6363

6464
// The forms of a tab stop. They all start with '$', so we pull that out here.
6565
tabStop = '$' t:(tabStopSimple / tabStopWithoutPlaceholder / tabStopWithPlaceholder / tabStopWithTransform) { return t; }
@@ -97,7 +97,7 @@ regexString = r:([^/\\] / '\\' c:. { return '\\' + c } )* { return r.join(""); }
9797

9898
// The form of a substitution for a transformation. It is a mix of plain text + modifiers + backreferences to the find capture groups
9999
// It cannot access tab stop values.
100-
replace = (replaceText / format / replaceModifier / escapedReplace)*
100+
replace = r:(replaceText / format / replaceModifier / escapedReplace / [^}/])* { return coalesce(r); }
101101

102102
// A reference to a capture group of the find regex of a transformation. Can conditionally
103103
// resolve based on if the match occurred, and have arbitrary modifiers applied to it.
@@ -126,28 +126,29 @@ formatWithIfElse = '{' n:integer ':?' ifContent:replace ':' elseContent:replace
126126
formatWithElse = '{' n:integer ':' '-'? elseContent:replace { return { backreference: n, elseContent }; }
127127

128128
// Used in `format`s to transform a string using a JS function
129-
modifier = '/' modifier:var { return modifier; }
129+
modifier = '/' modifier:name { return modifier; }
130130

131131
// Regex flags. Validation is performed when the regex itself is also known.
132132
flags = f:[a-z]* { return f.join(""); }
133133

134134
// A tab stop that offers a choice between several fixed values. These values are plain text only.
135135
// This feature is not implemented, but the syntax is parsed to reserve it for future use.
136136
// It will currently just default to a regular tab stop with the first value as it's placeholder.
137-
choice = '${' n:integer '|' a:choiceText b:(',' c:choiceText { return c; } )* '|}' { return { index: n, choices: [a, ...b] }; }
137+
// Empty choices are still parsed, as we may wish to assign meaning to it in future.
138+
choice = '${' n:integer '|' c:(a:choiceText b:(',' c:choiceText { return c; } )* { return [a, ...b] })? '|}' { return { index: n, choices: c || [] }; }
138139

139140
// Syntactically looks like a named tab stop. Variables are resolved in JS and may be
140141
// further processed with a transformation. Unrecognised variables are transformed into
141142
// tab stops with the variable name as a placeholder.
142143
variable = '$' v:(variableSimple / variablePlain / variableWithPlaceholder / variableWithTransform) { return v; }
143144

144-
variableSimple = v:var { return { variable: v }; }
145+
variableSimple = v:name { return { variable: v }; }
145146

146-
variablePlain = '{' v:var '}' { return { variable: v }; }
147+
variablePlain = '{' v:name '}' { return { variable: v }; }
147148

148-
variableWithPlaceholder = '{' v:var ':' content:tabStopContent '}' { return { variable: v, content }; }
149+
variableWithPlaceholder = '{' v:name ':' content:tabStopContent '}' { return { variable: v, content }; }
149150

150-
variableWithTransform = '{' v:var t:transformation '}' { return { variable: v, transformation: t }; }
151+
variableWithTransform = '{' v:name t:transformation '}' { return { variable: v, transformation: t }; }
151152

152153
// Top level text. Anything that cannot be the start of something special. False negatives are handled later by the `any` rule
153154
text = t:([^$\\}])+ { return t.join("") }
@@ -174,16 +175,10 @@ escapedChoice = '\\' c:[$\\,|] { return c; }
174175
escapedReplace = '\\' c:[$\\/] { return c; }
175176

176177
// We handle 'modifiers' separately to escapes. These indicate a change in state when building the replacement (e.g., capitalisation)
177-
replaceModifier = '\\' m:[uUlLeEnr] { return { modifier: m }; }
178+
replaceModifier = '\\' m:[ElLuUnr] { return { modifier: m }; }
178179

179180
// Match nonnegative integers like those used for tab stop ordering
180181
integer = digits:[0-9]+ { return parseInt(digits.join(""), 10); }
181182

182183
// Match variable names like TM_SELECTED_TEXT
183-
var = a:[a-zA-Z_] b:[a-zA-Z_0-9]* { return a + b.join(""); }
184-
185-
// Match any single character. Useful to resolve any parse errors where something that looked like it would be special had malformed syntax.
186-
any = a:. { return a; }
187-
188-
// Match anything that isn't a '}'. Useful for parse errors inside placeholder text, as `}` should be used to end the tab stop region
189-
notCloseBrace = a:[^}] { return a; }
184+
name = a:[a-zA-Z_] b:[a-zA-Z_0-9]* { return a + b.join(""); }

0 commit comments

Comments
 (0)