Skip to content

Commit bdc9355

Browse files
nixel2007claude
andcommitted
Name every punctuation lexeme (Ampersand, Hash, Dot, …) à la bsl-parser
ANTLR's BSLLexer.g4 names every lexeme — AMPERSAND, HASH, DOT, LPAREN, RPAREN, COMMA, SEMICOLON, COLON, ASSIGN, PLUS, MINUS, MUL, QUOTIENT, MODULO, LESS, LESS_OR_EQUAL, NOT_EQUAL, GREATER, GREATER_OR_EQUAL, QUESTION, BAR, TILDA. The Lezer grammar used anonymous string literals which forced the awkward quoted-form path syntax in styleTags (Annotation/"&") — @lezer/highlight's selector mini-language only accepts identifier-shaped names in path positions. Renamed every punctuation/operator literal in both bsl.grammar and sdbl.grammar: & → Ampersand # → Hash . → Dot ~ → Tilda ? → Question ( → LParen ) → RParen [ → LBrack ] → RBrack , → Comma : → Colon ; → Semicolon + → Plus - → Minus * → Mul / → Quotient % → Modulo = → Assign <> → NotEqual < → Less <= → LessOrEqual > → Greater >= → GreaterOrEqual Every reference inside productions, every styleTags selector, and every test fixture got updated in lock-step. Tree shape now exposes named tokens (BinaryExpr(Identifier, CmpOp(Assign), Number) instead of …CmpOp, Number) — that's intentional and makes downstream tree inspection more readable. Other knock-on cleanups: - Dropped unused LBrace/RBrace defs (BSL has no { } syntax). - styleTags map for SDBL Parameter/Ampersand and BSL Annotation/Ampersand and *Preprocessor/Hash now uses bare identifier path selectors. Tests: 28/28 fixtures pass; sample-set parse rate unchanged (84% / 88%). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 065bb29 commit bdc9355

5 files changed

Lines changed: 225 additions & 185 deletions

File tree

src/bsl.grammar

Lines changed: 79 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,24 @@ ModuleAnnotation {
3232
PreprocNativeDirective
3333
}
3434

35-
PreprocUseDirective { "#" PreprocUse useTarget }
35+
PreprocUseDirective { Hash PreprocUse useTarget }
3636
useTarget { String | Identifier }
37-
PreprocNativeDirective { "#" PreprocNative }
37+
PreprocNativeDirective { Hash PreprocNative }
3838

3939
// ---- Compiler directives & annotations ----
4040

4141
Annotation {
42-
"&" AnnotationName AnnotationParams?
42+
Ampersand AnnotationName AnnotationParams?
4343
}
4444

4545
AnnotationName { Identifier }
4646

4747
AnnotationParams {
48-
"(" (AnnotationParam ("," AnnotationParam)*)? ")"
48+
LParen (AnnotationParam (Comma AnnotationParam)*)? RParen
4949
}
5050

5151
AnnotationParam {
52-
AnnotationParamName ("=" annotationValue)? |
52+
AnnotationParamName (Assign annotationValue)? |
5353
annotationValue
5454
}
5555

@@ -71,10 +71,10 @@ decoration { Annotation }
7171
// (LR(1) cannot pick between reducing here and shifting `;` as the next stmt).
7272
VarStatement {
7373
decoration*
74-
Var varDeclList ";"
74+
Var varDeclList Semicolon
7575
}
7676

77-
varDeclList { VarDeclaration ("," VarDeclaration)* }
77+
varDeclList { VarDeclaration (Comma VarDeclaration)* }
7878

7979
VarDeclaration { Identifier Export? }
8080

@@ -83,27 +83,27 @@ VarDeclaration { Identifier Export? }
8383
ProcedureDecl {
8484
decoration*
8585
Async?
86-
Procedure SubName "(" paramList? ")" Export?
86+
Procedure SubName LParen paramList? RParen Export?
8787
body
8888
EndProcedure
8989
}
9090

9191
FunctionDecl {
9292
decoration*
9393
Async?
94-
Function SubName "(" paramList? ")" Export?
94+
Function SubName LParen paramList? RParen Export?
9595
body
9696
EndFunction
9797
}
9898

9999
SubName { Identifier }
100100

101-
paramList { Param ("," Param)* }
101+
paramList { Param (Comma Param)* }
102102

103103
Param {
104104
Annotation*
105105
Val?
106-
Identifier ("=" DefaultValue)?
106+
Identifier (Assign DefaultValue)?
107107
}
108108

109109
DefaultValue { unaryConstValue }
@@ -133,15 +133,15 @@ body { (BodyStatement | Preprocessor | VarStatement)* }
133133
// that lets Label sit on its own *or* prefix the next statement.
134134
BodyStatement {
135135
Label ~labelOpt flatStatement |
136-
Label ~labelOpt flatStatement !stmtSemi ";" |
136+
Label ~labelOpt flatStatement !stmtSemi Semicolon |
137137
Label ~labelOpt blockStatement ~blockSemi |
138-
Label ~labelOpt blockStatement ~blockSemi !stmtSemi ";" |
138+
Label ~labelOpt blockStatement ~blockSemi !stmtSemi Semicolon |
139139
Label ~labelOpt |
140140
flatStatement |
141-
flatStatement !stmtSemi ";" |
141+
flatStatement !stmtSemi Semicolon |
142142
blockStatement ~blockSemi |
143-
blockStatement ~blockSemi !stmtSemi ";" |
144-
";"
143+
blockStatement ~blockSemi !stmtSemi Semicolon |
144+
Semicolon
145145
}
146146

147147
flatStatement {
@@ -166,10 +166,10 @@ blockStatement {
166166
TryStatement
167167
}
168168

169-
Label { "~" LabelName ":" }
169+
Label { Tilda LabelName Colon }
170170
LabelName { Identifier }
171171

172-
Assignment { lvalue "=" expression }
172+
Assignment { lvalue Assign expression }
173173

174174
CallStatement { reference }
175175

@@ -185,7 +185,7 @@ ElseBranch { Else body }
185185
WhileStatement { While expression Do body EndDo }
186186

187187
ForStatement {
188-
For Identifier "=" expression To expression Do body EndDo
188+
For Identifier Assign expression To expression Do body EndDo
189189
}
190190

191191
ForEachStatement {
@@ -211,9 +211,9 @@ RaiseStatement[@dynamicPrecedence=1] {
211211
ContinueStatement { Continue }
212212
BreakStatement { Break }
213213
ExecuteStatement { Execute expression }
214-
GotoStatement { Goto "~" Identifier }
215-
AddHandlerStatement { AddHandler expression "," expression }
216-
RemoveHandlerStatement { RemoveHandler expression "," expression }
214+
GotoStatement { Goto Tilda Identifier }
215+
AddHandlerStatement { AddHandler expression Comma expression }
216+
RemoveHandlerStatement { RemoveHandler expression Comma expression }
217217
AwaitStatement { Await expression }
218218

219219
// ---- Expressions ----
@@ -239,10 +239,10 @@ UnaryExpr { !unary UnaryOp expression }
239239

240240
OrOp { Or }
241241
AndOp { And }
242-
CmpOp { "=" | "<>" | "<" | "<=" | ">" | ">=" }
243-
AddOp { "+" | "-" }
244-
MulOp { "*" | "/" | "%" }
245-
UnaryOp { "+" | "-" | Not }
242+
CmpOp { Assign | NotEqual | Less | LessOrEqual | Greater | GreaterOrEqual }
243+
AddOp { Plus | Minus }
244+
MulOp { Mul | Quotient | Modulo }
245+
UnaryOp { Plus | Minus | Not }
246246

247247
primary {
248248
constValue |
@@ -253,7 +253,7 @@ primary {
253253
reference
254254
}
255255

256-
ParenExpr { "(" expression ")" accessChain }
256+
ParenExpr { LParen expression RParen accessChain }
257257

258258
reference { refHead accessChain }
259259

@@ -274,8 +274,8 @@ access {
274274
// as identifiers. We can't do mode-shifting in Lezer's specializer, so the
275275
// parser rules below explicitly accept every BSL keyword term *or* a plain
276276
// Identifier in the dot position.
277-
PropertyAccess { "." dotIdentifier }
278-
CallAccess { "." dotIdentifier callArgs }
277+
PropertyAccess { Dot dotIdentifier }
278+
CallAccess { Dot dotIdentifier callArgs }
279279

280280
dotIdentifier { Identifier | dotKeyword }
281281

@@ -292,17 +292,17 @@ dotKeyword {
292292
And | Or | Not |
293293
True | False | Undefined | Null
294294
}
295-
IndexAccess { "[" expression "]" }
295+
IndexAccess { LBrack expression RBrack }
296296

297297
// Argument list — comma-skipping (`Метод(a,,b)` / `Метод(,a)`) is not
298298
// supported. Removed the failed attempt to model `,` as a stand-alone item;
299299
// it fundamentally collides with `f(g(...))` inner-call parsing in LR(1).
300300
// See README → Limitations for context.
301-
callArgs { "(" callArgsList? ")" }
302-
callArgsList { callArg ("," callArg)* }
301+
callArgs { LParen callArgsList? RParen }
302+
callArgsList { callArg (Comma callArg)* }
303303
callArg { expression }
304304

305-
TernaryOp { "?" "(" expression "," expression "," expression ")" }
305+
TernaryOp { Question LParen expression Comma expression Comma expression RParen }
306306
NewExpr { New (Identifier callArgs? | callArgs) }
307307
AwaitExpr { Await expression }
308308

@@ -334,8 +334,8 @@ Preprocessor {
334334
PreprocessorEndInsert
335335
}
336336

337-
Region { "#" PreprocRegion RegionName }
338-
EndRegion { "#" PreprocEndRegion }
337+
Region { Hash PreprocRegion RegionName }
338+
EndRegion { Hash PreprocEndRegion }
339339
RegionName { Identifier }
340340

341341
// Preprocessor `#Если/#ИначеЕсли/#Иначе/#КонецЕсли/#Тогда` and their logical
@@ -344,24 +344,24 @@ RegionName { Identifier }
344344
// cannot do natively against the existing identifier specialize hook), we
345345
// reuse the BSL terms here and let styleTags discriminate via parent
346346
// selectors like `"PreprocessorIf/If": t.macroName`.
347-
PreprocessorIf { "#" If preprocExpr Then }
348-
PreprocessorElsif { "#" Elsif preprocExpr Then }
349-
PreprocessorElse { "#" Else }
350-
PreprocessorEndIf { "#" EndIf }
347+
PreprocessorIf { Hash If preprocExpr Then }
348+
PreprocessorElsif { Hash Elsif preprocExpr Then }
349+
PreprocessorElse { Hash Else }
350+
PreprocessorEndIf { Hash EndIf }
351351

352352
// Configuration-extension preprocessor markers. `#Удаление`/`#КонецУдаления`
353353
// brackets code that the *base* configuration has but that the extension
354354
// removes; `#Вставка`/`#КонецВставки` brackets code the extension *adds*.
355355
// For highlighting purposes both are simple bracket-style directive pairs;
356356
// the BSL between them stays in the regular token stream.
357-
PreprocessorDelete { "#" PreprocDelete }
358-
PreprocessorEndDelete { "#" PreprocEndDelete }
359-
PreprocessorInsert { "#" PreprocInsert }
360-
PreprocessorEndInsert { "#" PreprocEndInsert }
357+
PreprocessorDelete { Hash PreprocDelete }
358+
PreprocessorEndDelete { Hash PreprocEndDelete }
359+
PreprocessorInsert { Hash PreprocInsert }
360+
PreprocessorEndInsert { Hash PreprocEndInsert }
361361

362362
preprocExpr {
363363
Not preprocExpr |
364-
"(" preprocExpr ")" |
364+
LParen preprocExpr RParen |
365365
preprocExpr !preprocBool preprocBoolOp preprocExpr |
366366
PreprocSymbol
367367
}
@@ -431,18 +431,41 @@ preprocBoolOp { Or | And }
431431
// Shebang line — matched as a single token so its content is not parsed.
432432
ShebangLine { "#!" ![\n]* }
433433

434-
// Punctuation
435-
"(" ")" "[" "]" "{" "}"
436-
"," ":" ";" "."
437-
"+" "-" "*" "/" "%"
438-
"=" "<>" "<=" ">=" "<" ">"
439-
"?" "&" "~" "#"
440-
441-
@precedence { "<=", "<>", "<" }
442-
@precedence { ">=", ">" }
443-
@precedence { DocComment, LineComment, "/" }
444-
@precedence { Number, "." }
445-
@precedence { ShebangLine, "#" }
434+
// Named punctuation and operator tokens. Naming each lexeme mirrors the
435+
// upstream ANTLR convention from `1c-syntax/bsl-parser` (AMPERSAND, HASH,
436+
// DOT, …) and — more pragmatically — sidesteps @lezer/highlight's
437+
// selector mini-language, which only accepts identifier-shaped names in
438+
// path positions. With named tokens the styleTags selectors can read
439+
// `Annotation/Ampersand` instead of the awkward quoted `Annotation/"&"`.
440+
LParen { "(" }
441+
RParen { ")" }
442+
LBrack { "[" }
443+
RBrack { "]" }
444+
Comma { "," }
445+
Colon { ":" }
446+
Semicolon { ";" }
447+
Dot { "." }
448+
Plus { "+" }
449+
Minus { "-" }
450+
Mul { "*" }
451+
Quotient { "/" }
452+
Modulo { "%" }
453+
Assign { "=" }
454+
NotEqual { "<>" }
455+
LessOrEqual { "<=" }
456+
GreaterOrEqual { ">=" }
457+
Less { "<" }
458+
Greater { ">" }
459+
Question { "?" }
460+
Ampersand { "&" }
461+
Tilda { "~" }
462+
Hash { "#" }
463+
464+
@precedence { LessOrEqual, NotEqual, Less }
465+
@precedence { GreaterOrEqual, Greater }
466+
@precedence { DocComment, LineComment, Quotient }
467+
@precedence { Number, Dot }
468+
@precedence { ShebangLine, Hash }
446469
}
447470

448471
// ---- External specialization: case-insensitive keyword matching ----

src/index.ts

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,12 @@ export const sdblLanguage = sdblParser.configure({
119119
Number: t.number,
120120
StringLit: t.string,
121121
// Parameter references (&ИмяПараметра) — tagged as a typed local so
122-
// they stand out from regular identifiers.
122+
// they stand out from regular identifiers; the `&` punctuation gets
123+
// the modifier tag so it renders together with the parameter name.
123124
"Parameter/Identifier": t.local(t.variableName),
124-
"Parameter/&": t.modifier,
125+
"Parameter/Ampersand": t.modifier,
125126
// Punctuation
126-
"( )": t.paren,
127+
"LParen RParen": t.paren,
127128
"AddOp MulOp": t.arithmeticOperator,
128129
CmpOp: t.compareOperator,
129130
Punct: t.punctuation,
@@ -251,13 +252,11 @@ export const bslLanguage = LRLanguage.define({
251252

252253
// ---- Annotations (compiler directives like &НаКлиенте) ----
253254
// Every leaf token of an annotation gets `t.annotation` so the whole
254-
// `&Имя` reads as one visual unit. The `AnnotationName/Identifier`
255-
// selector is needed because @lezer/highlight resolves tags at the
256-
// leaf level — a parent-only `AnnotationName: t.annotation` would
257-
// lose against the global `Identifier: t.variableName`. The quoted
258-
// `"&"` form is the only way to reference a non-identifier-shaped
259-
// literal inside a path selector.
260-
'Annotation/AnnotationName AnnotationName/Identifier Annotation/"&"': t.annotation,
255+
// `&Имя` reads as one visual unit. AnnotationName/Identifier is
256+
// needed because @lezer/highlight resolves at the leaf level —
257+
// `AnnotationName: t.annotation` alone would lose against the global
258+
// `Identifier: t.variableName`.
259+
"Annotation/AnnotationName AnnotationName/Identifier Annotation/Ampersand": t.annotation,
261260

262261
// ---- Preprocessor ----
263262
// Per bsl-language-server PreprocessorSemanticTokensSupplier:
@@ -274,37 +273,38 @@ export const bslLanguage = LRLanguage.define({
274273
// at the leaf — parent-only tags lose against any leaf rule.
275274

276275
// Namespace bucket — regions and use directive.
277-
'Region EndRegion PreprocUseDirective Region/"#" EndRegion/"#" PreprocUseDirective/"#" PreprocRegion PreprocEndRegion PreprocUse': t.namespace,
276+
"Region EndRegion PreprocUseDirective Region/Hash EndRegion/Hash PreprocUseDirective/Hash PreprocRegion PreprocEndRegion PreprocUse": t.namespace,
278277

279278
// Macro bucket — conditional directives, native, configuration-
280279
// extension markers. The `Preprocessor*/If` etc. selectors retag
281280
// BSL control-flow terms that share their surface form with
282281
// preprocessor keywords.
283-
'PreprocessorIf PreprocessorElsif PreprocessorElse PreprocessorEndIf PreprocessorDelete PreprocessorEndDelete PreprocessorInsert PreprocessorEndInsert PreprocNativeDirective PreprocessorIf/"#" PreprocessorElsif/"#" PreprocessorElse/"#" PreprocessorEndIf/"#" PreprocessorDelete/"#" PreprocessorEndDelete/"#" PreprocessorInsert/"#" PreprocessorEndInsert/"#" PreprocNativeDirective/"#" PreprocessorIf/If PreprocessorIf/Then PreprocessorElsif/Elsif PreprocessorElsif/Then PreprocessorElse/Else PreprocessorEndIf/EndIf PreprocessorIf/Not PreprocessorElsif/Not PreprocessorIf/And PreprocessorElsif/And PreprocessorIf/Or PreprocessorElsif/Or PreprocDelete PreprocEndDelete PreprocInsert PreprocEndInsert PreprocNative': t.macroName,
282+
"PreprocessorIf PreprocessorElsif PreprocessorElse PreprocessorEndIf PreprocessorDelete PreprocessorEndDelete PreprocessorInsert PreprocessorEndInsert PreprocNativeDirective PreprocessorIf/Hash PreprocessorElsif/Hash PreprocessorElse/Hash PreprocessorEndIf/Hash PreprocessorDelete/Hash PreprocessorEndDelete/Hash PreprocessorInsert/Hash PreprocessorEndInsert/Hash PreprocNativeDirective/Hash PreprocessorIf/If PreprocessorIf/Then PreprocessorElsif/Elsif PreprocessorElsif/Then PreprocessorElse/Else PreprocessorEndIf/EndIf PreprocessorIf/Not PreprocessorElsif/Not PreprocessorIf/And PreprocessorElsif/And PreprocessorIf/Or PreprocessorElsif/Or PreprocDelete PreprocEndDelete PreprocInsert PreprocEndInsert PreprocNative": t.macroName,
284283

285284
ShebangLine: t.processingInstruction,
286285
"Region/RegionName": t.variableName,
287286

288287
// ---- Punctuation and operators ----
289-
// Punctuation literals are tagged via their wrapping named nodes
290-
// (MulOp, AddOp, etc.) — the styleTags selector mini-language treats
291-
// a bare `*` as a wildcard, so we cannot list multiplication directly.
288+
// Every lexeme is named (Ampersand, Hash, Dot, …) so styleTags can
289+
// reference them by identifier — no quoted-literal syntax needed.
290+
// Leaf tokens get their tag directly; the *Op wrappers also get a
291+
// tag for downstream consumers walking the tree, but the leaves win
292+
// for highlighting.
292293
"OrOp AndOp": t.logicOperator,
293-
CmpOp: t.compareOperator,
294-
AddOp: t.arithmeticOperator,
295-
MulOp: t.arithmeticOperator,
294+
"CmpOp Assign NotEqual Less LessOrEqual Greater GreaterOrEqual": t.compareOperator,
295+
"AddOp Plus Minus": t.arithmeticOperator,
296+
"MulOp Mul Quotient Modulo": t.arithmeticOperator,
296297
UnaryOp: t.operator,
297-
"( )": t.paren,
298-
"[ ]": t.squareBracket,
299-
"{ }": t.brace,
300-
", ;": t.separator,
301-
":": t.punctuation,
302-
".": t.derefOperator,
303-
"?": t.controlOperator,
298+
"LParen RParen": t.paren,
299+
"LBrack RBrack": t.squareBracket,
300+
"Comma Semicolon": t.separator,
301+
Colon: t.punctuation,
302+
Dot: t.derefOperator,
303+
Question: t.controlOperator,
304304
// Default colour for `&`/`~`/`#` outside their usual contexts —
305-
// contextual selectors above (Annotation/&, Preprocessor/#, etc.)
306-
// take precedence when the token sits inside the matching parent.
307-
"& ~ #": t.punctuation,
305+
// contextual selectors above (Annotation/Ampersand, *Preprocessor/Hash,
306+
// etc.) take precedence when the token sits inside the matching parent.
307+
"Ampersand Tilda Hash": t.punctuation,
308308

309309
// ---- Comments ----
310310
LineComment: t.lineComment,

0 commit comments

Comments
 (0)