Skip to content

Commit a82556f

Browse files
committed
Add icons, diagnostics, completions & snippets
Add a dedicated Bison/Flex file icon theme and SVG icons, and register it in package.json to provide distinct icons for .y/.l files. Expand CHANGELOG and README with detailed diagnostics, screenshots, and a corrected test path. Enhance language server completions: %include file path suggestions, %skeleton and %define api.value.type values, and improved Flex abbreviation completions and context handling. Add many new Bison and Flex snippets (GLR skeleton, error recovery, union, numeric/identifier handlers, nested comment handler) to improve editor productivity.
1 parent e38a7f9 commit a82556f

9 files changed

Lines changed: 334 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,25 @@ All notable changes to the **Bison/Flex Language Support** extension will be doc
1313
- Start condition highlighting (`<SC_NAME>`)
1414
- Abbreviation reference highlighting (`{name}`)
1515
- **Real-time diagnostics**
16-
- Bison: undeclared tokens, orphan `%type` declarations, missing `%%`, unclosed blocks
17-
- Flex: undefined start conditions, undefined abbreviations, missing `%%`, unclosed blocks
16+
- Bison:
17+
- Missing `%%` section separator (Error)
18+
- Unknown/invalid directive — e.g. `%prout` (Error)
19+
- Token used in grammar rules but not declared with `%token` (Warning)
20+
- `%type` declared for a non-terminal that has no rule (Warning)
21+
- Rule missing `%type` declaration when `api.value.type=variant` is active (Info)
22+
- Unclosed `%{ %}` code block (Error)
23+
- Unused grammar rules — not reachable from the start symbol (Warning)
24+
- Unused tokens — declared with `%token` but never referenced in rules (Warning)
25+
- Shift/reduce conflict heuristic — same terminal appears in two or more alternatives of a rule (Warning)
26+
- Flex:
27+
- Missing `%%` section separator (Error)
28+
- Unknown/invalid directive — e.g. `%woops` (Error)
29+
- Undefined start condition used in a rule (`<SC>` not declared with `%x`/`%s`) (Error)
30+
- Undefined abbreviation referenced in a pattern (`{name}` not in definitions section) (Warning)
31+
- Start condition declared but never used in any rule (Info)
32+
- Abbreviation declared but never referenced in any pattern (Info)
33+
- Unclosed `%{ %}` code block (Error)
34+
- Inaccessible rule — catch-all pattern before a specific pattern, or duplicate pattern (Warning)
1835
- **Autocompletion**
1936
- 30+ Bison directives with documentation
2037
- 20+ Flex `%option` values
@@ -32,3 +49,6 @@ All notable changes to the **Bison/Flex Language Support** extension will be doc
3249
- 12 Flex snippets (scanner skeleton, RE-flex skeleton, comment/string handlers)
3350
- **Language configuration**
3451
- Bracket matching, auto-closing pairs, comment toggling, folding
52+
- **File icon theme** (`bison-flex-icons`)
53+
- Distinct orange "B" icon for Bison files (`.y`, `.yy`, `.ypp`, `.bison`)
54+
- Distinct blue "F" icon for Flex files (`.l`, `.ll`, `.lex`, `.flex`)

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,25 @@ Ready-to-use templates to speed up your workflow:
7575

7676
---
7777

78+
## Screenshots
79+
80+
### Syntax Highlighting
81+
![Syntax highlighting with section-aware coloring and embedded C/C++](images/syntax-highlighting.png)
82+
83+
### Hover Documentation
84+
![Inline hover docs for Bison directives and Flex options](images/hover-docs.png)
85+
86+
### Autocompletion
87+
![Context-aware completions for directives, tokens, and semantic values](images/autocompletion.png)
88+
89+
### Diagnostics
90+
![Real-time error and warning markers for undeclared tokens, missing separators, and more](images/diagnostics.png)
91+
92+
### Snippets
93+
![Code snippet expansion for grammar skeletons and rule templates](images/snippets.png)
94+
95+
---
96+
7897
## Supported File Types
7998

8099
| Language | Extensions | Aliases |
@@ -144,7 +163,7 @@ Contributions are welcome! Here's how to get started:
144163
### Running Tests
145164

146165
```bash
147-
npx ts-node --project server/tsconfig.json test-parsers.ts
166+
npx ts-node --project server/tsconfig.json tests/test-parsers.ts
148167
```
149168

150169
### Building for Production

bison-flex-icon-theme.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"iconDefinitions": {
3+
"_bison": {
4+
"iconPath": "./images/bison-icon.svg"
5+
},
6+
"_flex": {
7+
"iconPath": "./images/flex-icon.svg"
8+
}
9+
},
10+
"fileExtensions": {
11+
"y": "_bison",
12+
"yy": "_bison",
13+
"ypp": "_bison",
14+
"bison": "_bison",
15+
"l": "_flex",
16+
"ll": "_flex",
17+
"lex": "_flex",
18+
"flex": "_flex"
19+
},
20+
"fileNames": {}
21+
}

images/bison-icon.svg

Lines changed: 15 additions & 0 deletions
Loading

images/flex-icon.svg

Lines changed: 15 additions & 0 deletions
Loading

package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@
9797
"path": "./snippets/flex.json"
9898
}
9999
],
100+
"iconThemes": [
101+
{
102+
"id": "bison-flex-icons",
103+
"label": "Bison/Flex File Icons",
104+
"path": "./bison-flex-icon-theme.json"
105+
}
106+
],
100107
"configuration": {
101108
"title": "Bison & Flex",
102109
"properties": {

server/src/providers/completion.ts

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import {
66
TextDocument,
77
} from 'vscode-languageserver';
88
import { BisonDocument, FlexDocument, DocumentModel, isBisonDocument } from '../parser/types';
9+
import { fileURLToPath } from 'url';
10+
import * as fs from 'fs';
11+
import * as path from 'path';
912
import {
1013
bisonDirectiveDocs,
1114
bisonDefineDocs,
@@ -25,7 +28,7 @@ export function getCompletions(
2528
const linePrefix = lineText.substring(0, position.character);
2629

2730
if (isBisonDocument(doc)) {
28-
return getBisonCompletions(doc, linePrefix, position, text);
31+
return getBisonCompletions(doc, linePrefix, position, text, textDoc);
2932
} else {
3033
return getFlexCompletions(doc, linePrefix, position, text);
3134
}
@@ -35,10 +38,78 @@ function getBisonCompletions(
3538
doc: BisonDocument,
3639
linePrefix: string,
3740
position: Position,
38-
_text: string
41+
_text: string,
42+
textDoc: TextDocument
3943
): CompletionItem[] {
4044
const items: CompletionItem[] = [];
4145

46+
// T1. %include file completions
47+
if (linePrefix.match(/%include\s+["'<][^"'<>]*$/)) {
48+
const match = linePrefix.match(/%include\s+["'<](.*)$/);
49+
const partial = match ? match[1] : '';
50+
try {
51+
const fileDir = path.dirname(fileURLToPath(textDoc.uri));
52+
const targetDir = partial.includes('/') || partial.includes(path.sep)
53+
? path.join(fileDir, path.dirname(partial))
54+
: fileDir;
55+
const entries = fs.readdirSync(targetDir, { withFileTypes: true });
56+
return entries.map(entry => ({
57+
label: entry.name,
58+
kind: entry.isDirectory() ? CompletionItemKind.Folder : CompletionItemKind.File,
59+
detail: entry.isDirectory() ? 'Directory' : 'File',
60+
sortText: (entry.isDirectory() ? '0' : '1') + entry.name,
61+
}));
62+
} catch {
63+
return [];
64+
}
65+
}
66+
67+
// T3. %skeleton value completions
68+
if (linePrefix.match(/%skeleton\s+\S*$/)) {
69+
return [
70+
{ label: '"lalr1.cc"', kind: CompletionItemKind.Value, detail: 'C++ LALR(1) parser (default for C++)', sortText: '0' },
71+
{ label: '"glr.cc"', kind: CompletionItemKind.Value, detail: 'C++ GLR parser', sortText: '1' },
72+
{ label: '"lalr1.c"', kind: CompletionItemKind.Value, detail: 'C LALR(1) parser', sortText: '2' },
73+
{ label: '"glr.c"', kind: CompletionItemKind.Value, detail: 'C GLR parser', sortText: '3' },
74+
{ label: '"location.cc"', kind: CompletionItemKind.Value, detail: 'Location tracking only', sortText: '4' },
75+
];
76+
}
77+
78+
// T2. %define api.value.type value completions
79+
if (linePrefix.match(/%define\s+api\.value\.type\s+\S*$/)) {
80+
return [
81+
{
82+
label: 'variant',
83+
kind: CompletionItemKind.Value,
84+
detail: 'C++ std::variant (Bison ≥ 3.2, requires C++17)',
85+
documentation: { kind: 'markdown', value: 'Each semantic value is stored as `std::variant`. Requires `%language "c++"` and a C++17 compiler.' },
86+
sortText: '0',
87+
},
88+
{
89+
label: 'union',
90+
kind: CompletionItemKind.Value,
91+
detail: 'C union (classic default)',
92+
documentation: { kind: 'markdown', value: 'Uses the `YYSTYPE` union. Compatible with C and older C++ Bison grammars.' },
93+
sortText: '1',
94+
},
95+
{
96+
label: 'union-directive',
97+
kind: CompletionItemKind.Value,
98+
detail: 'Union defined by %union directive',
99+
documentation: { kind: 'markdown', value: 'Use a union body declared with `%union { ... }`.' },
100+
sortText: '2',
101+
},
102+
{
103+
label: 'std::variant<int, std::string>',
104+
kind: CompletionItemKind.Value,
105+
detail: 'Explicit std::variant type list',
106+
insertText: 'std::variant<${1:int, std::string}>',
107+
insertTextFormat: InsertTextFormat.Snippet,
108+
sortText: '3',
109+
},
110+
];
111+
}
112+
42113
// 1. Directive completions (after %)
43114
if (linePrefix.match(/%\w*$/) && !linePrefix.match(/%%.*/)) {
44115
for (const [name, entry] of bisonDirectiveDocs) {
@@ -232,8 +303,9 @@ function getFlexCompletions(
232303
return items;
233304
}
234305

235-
// 4. Abbreviation completions (after {)
236-
if (linePrefix.match(/\{[a-zA-Z_]*$/)) {
306+
// T4. Abbreviation completions in rule patterns (after {, in rules section only)
307+
const isInFlexRules = doc.separators.length > 0 && position.line > doc.separators[0];
308+
if (isInFlexRules && linePrefix.match(/\{[a-zA-Z_]*$/)) {
237309
for (const [name, abbr] of doc.abbreviations) {
238310
items.push({
239311
label: name,
@@ -243,7 +315,21 @@ function getFlexCompletions(
243315
sortText: '0' + name,
244316
});
245317
}
246-
return items;
318+
if (items.length > 0) return items;
319+
}
320+
321+
// Abbreviation completions in definitions section (nested abbreviations)
322+
if (!isInFlexRules && linePrefix.match(/\{[a-zA-Z_]*$/)) {
323+
for (const [name, abbr] of doc.abbreviations) {
324+
items.push({
325+
label: name,
326+
kind: CompletionItemKind.Variable,
327+
detail: `Abbreviation: ${abbr.pattern}`,
328+
insertText: name + '}',
329+
sortText: '0' + name,
330+
});
331+
}
332+
if (items.length > 0) return items;
247333
}
248334

249335
// 5. Snippets

snippets/bison.json

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,86 @@
157157
"body": [
158158
"%prec ${1:TOKEN}"
159159
]
160+
},
161+
162+
"GLR Grammar Skeleton": {
163+
"prefix": "glr-skeleton",
164+
"description": "Complete Bison GLR grammar with %glr-parser, %define parse.lac, and ambiguous rule example",
165+
"body": [
166+
"// -*- C++ -*-",
167+
"%require \"3.8\"",
168+
"%glr-parser",
169+
"%language \"c++\"",
170+
"",
171+
"%define api.namespace {${1:parse}}",
172+
"%define api.parser.class {${2:parser}}",
173+
"%define api.value.type variant",
174+
"%define api.token.constructor",
175+
"%define parse.error verbose",
176+
"%define parse.lac full",
177+
"",
178+
"%defines",
179+
"%locations",
180+
"",
181+
"%code requires",
182+
"{",
183+
" #include <string>",
184+
" $3",
185+
"}",
186+
"",
187+
"%token <int> INT \"integer\"",
188+
"%token <std::string> ID \"identifier\"",
189+
"%token PLUS \"+\"",
190+
"",
191+
"%type <${4:int}> ${5:expr}",
192+
"",
193+
"%start ${6:program}",
194+
"",
195+
"%%",
196+
"",
197+
"$6:",
198+
" $5 { $$ = $1; }",
199+
";",
200+
"",
201+
"$5:",
202+
" INT { $$ = $1; }",
203+
"| $5 \"+\" $5 { $$ = $1 + $3; }",
204+
";",
205+
"",
206+
"%%",
207+
"",
208+
"void",
209+
"${1}::${2}::error(const location_type& l, const std::string& m)",
210+
"{",
211+
" std::cerr << l << \": \" << m << std::endl;",
212+
"}"
213+
]
214+
},
215+
216+
"Error Recovery Rule": {
217+
"prefix": "error-recovery",
218+
"description": "Grammar rule with error token for panic-mode error recovery",
219+
"body": [
220+
"${1:statement}:",
221+
" ${2:expr} '${3:;}' { $$ = $1; }",
222+
"| error '${3}' { yyerrok; $$ = ${4:0}; }",
223+
";"
224+
]
225+
},
226+
227+
"Union Declaration": {
228+
"prefix": "union-decl",
229+
"description": "%union with common C types for YYSTYPE",
230+
"body": [
231+
"%union",
232+
"{",
233+
" int ival; /* integer value */",
234+
" double dval; /* float value */",
235+
" char* sval; /* string value */",
236+
" char cval; /* char value */",
237+
" unsigned int uval; /* unsigned value */",
238+
" ${1:/* custom field */}",
239+
"}"
240+
]
160241
}
161242
}

0 commit comments

Comments
 (0)