New features in 2.2.25:
- Significant indentation gets reset after each
grammarkdown
top-level element is parsed @define
directives only apply to the lines that follow, and can be reset- New
@line
directives allow you to customize whatgrammarkdown
reports for diagnostic messages
Significant Indentation
One issue with grammarkdown
previously when used with ecmarkup
(or my own pet project to write a vscode extension for ecmarkup
), is that grammarkdown
depends on whitespace significance with respect to indentation, for example:
Production :
Nonterminal1
Nonterminal2
Nonterminal3
Would be parsed as a production with two right-hand-sides (one for each line), as if they were written like this:
Production : Nonterminal1
Production : Nonterminal2 Nonterminal3
However, we failed to reset indentation at the end of parsing a top-level element like a production, so a grammar like the following would be parsed incorrectly:
Production1 :
Nonterminal1
Production2 :
Nonterminal2
Now we will correctly reset the indentation once we have fully parsed a top-level element such as a production, or after any @
directives. This can be helpful for cases like ecmarkup
which currently parses each <emu-grammar>
as an independent document. Instead, ecmarkup
could pass a single document to grammarkdown
that just concatenates each <emu-grammar>
section, as long as there is at least one blank line between each section.
@define
directives only apply to following lines
In grammarkdown
, a @define
directive allows you to control checking behavior for two compiler options: noStrictParametricProductions
and noUnusedParameters
. Prior to this release, @define
directives applied to the whole file, regardless of where they appeared. Now @define
directives apply to the content that follows it. This allows you to change and reset these options on an as-need basis:
@define noStrictParametricProductions false // use strict behavior
Production[A] :
Nonterminal1
Nonterminal2
// example often used in collapsed grammars for static semantics:
Production : Nonterminal1 // error: Production `Production` is missing required parameter `A`...
@define noStrictParametricProductions true // use sloppy behavior
Production : Nonterminal1 // ok
@define noStrictParametricProductions default // use behavior supplied by compiler options
In addition, the @define
directive now allows you to supply default
in addition to true
or false
. This resets the option to the behavior supplied by the compiler options provided to the Grammar
API object.
The @line
directive
The @line
directive is helpful for mapping a location in grammarkdown
to a different source location when reporting diagnostics, or for other language service features. The format of the @line
directive is as follows:
@line <number> ["path"]
@line default
With @line
, you change the line numbering for diagnostics to start at the provided <number>
on the following line. The optional "path"
string allows you to specify the name of the file to report in the diagnostics. The @line default
directive resets line numbering to the current line number and source file.
This can be useful in tools such as ecmarkup
, as they can generate a grammarkdown
source file with line numbers already mapped to the original source file:
spec.html
<emu-clause id="...">
<h1>Syntax</h1>
<emu-grammar>
Production :
Nonterminal1
Nonterminal2
</emu-grammar>
<emu-clause id="...">
<h1>Static Semantics</h1>
<emu-grammar>Production : Nonterminal1</emu-grammar>
</emu-clause>
</emu-clause>
spec.html.grammarkdown (generated)
@line 4 "spec.html"
Production :
Nonterminal1
Nonterminal2
@define noStrictParametricProductions true
@line 10 "spec.html"
Production : Nontermnal1
@define noStrictParametricProductions default
Now the diagnostic reported for line 7 in the generated file (due to the misspelling of Nonterminal1
) would instead be spec.html(10,31): error: GM2000: Cannot find name: 'Nontermnal1'
.
This information is exposed by the API in the following ways:
DiagnosticMessages.prototype.getDiagnosticFilename()
will return the@line
-mapped file name unless you passtrue
for new argumentraw
.DiagnosticMessages.prototype.getDiagnosticPosition()
will return the@line
-mapped position unless you passtrue
for new argumentraw
.DiagnosticMessages.prototype.getDiagnosticRange()
will return the@line
-mapped range unless you passtrue
for new argumentraw
.DiagnosticMessages.prototype.getDiagnosticInfo()
will return the@line
-mappedDiagnosticInfo
unless you pass the option{ raw: true }
.DiagnosticMessages.prototype.getDiagnosticInfosForSourceFile()
will return an array of@line
-mappedDiagnosticInfo
objects unless you pass the option{ raw: true }
.DiagnosticMessages.prototype.getDiagnosticInfos()
will return an array of@line
-mappedDiagnosticInfo
objects unless you pass the option{ raw: true }
.DiagnosticMessages.prototype.getMessage()
will return the@line
-mapped formatted diagnostic message unless you pass the option{ raw: true }
.- The
DiagnosticInfo
interface has a newfilename
property that will contain the@line
-mapped file name, if available. ThesourceFile
property will still refer to the parsedSourceFile
node. Resolver.prototype.getEffectiveFilenameAtPosition()
gets the@line
-mapped filename for the provided source position.Resolver.prototype.getEffectivePosition()
gets the@line
-mapped position for the provided source position.Resolver.prototype.getEffectiveRange()
gets the@line
-mapped range for the provided source range.