Lexical and Parser user-defined context data
For advanced use cases, it is sometimes helpful to be able to have
additional data available in either the parsing context, such as
sdt actions, or the lexical context, on the lexer or the tokens
it produces.
Distinction between Lexical and Parser Context
Both parser
and token
define Context
as interface{}
.
Gocc does not care if the parser and lexical contexts are the same, or
whether you use one and not the other.
Lexical Context
When the lexer.Lexer object is configured with a non-nil Context value,
the value will be assigned to the Pos.Context property of every token
the lexer generates.
If you are parsing multiple files, this could allow you to inform the
user that tokenA is redeclared in file2.txt after being previously
declared in file1.txt, rather than simply "I've seen tokenA before".
Assigning:
// assigning
l := lexer.NewLexer()
l.Context = &MyContext{ /* populate fields */ }
// or just
l.Context = filename
Use (assumes MyContext has method 'Source()' that returns filename)
func duplicate(newToken, oldToken *token.Token) error {
newPos, oldPos := newToken.Pos, oldToken.Pos
return fmt.Errorf("%s:%d:%d: error: '%s' redefined.\n%s:%d:%d: previous definition",
newPos.Context.(*MyContext).Source(), newPos.Line, newPos.Column,
string(newToken.Lit),
oldPos.Context.(*MyContext).Source(), oldPos.Line, oldPos.Column)
Parser Context
This allows you to surface contextual information when invoking SDT actions. It
corresponds to the Context
property of the parser.Parser
class, and can be
accessed with the SDT token $Context
.
Example:
bnf
Identifier: identifier << ast.NewIdentifier($0, $Context);
ast/main.go
func NewIdentifier(nameAttr Attrib, context interface{}) (string, error) {
name := string(nameAttr.(*token.Token).Lit)
if previous, exists := context.(*ParseContext).Identifiers[name]; exists {
...