Traqula, through its parser and generator allows you to parse, manipulate and generate queries. This manipulation can be done in a round-tripping fashion, empowering powerful tools useful for structured languages (e.g. linters).
This documentation page goes into detail on the AST structure used by Traqula and how to both create and modify ASTs.
The AST structure is declared using TypeScript types as a tree of @traqula/core Nodes. The AST nodes contain the information to identify the type and subType of the AST-node, and also contain information to round-trip the node through the SourceLocation information.
The source location (loc) optionally relates an AST-node to the string character range the node represents.
This information is then used by the Traqula core generator to enable round-tripping.
There are six kinds of source location information:
In order to support round tripping, the AST tree structure has the following restrictions regarding the ranges:
Furthermore, parsers may choose to emit only SourceLocationNodeAutoGenerate since collecting the source information puts additional strain on the lexer. This is what happens for Traqula's SPARQL parsers, and it's why you need to enable collecting the info in the context.
The SPARQL AST is declared as union type Sparql11Nodes
within @traqula/rules-sparql-1-1.
Additionally,
the package contains a factory simplifying the creation of AST nodes.
In the example bellow we parse a SPARQL query and append the prefix ex: to the list of prefixes:
import { Parser } from '@traqula/parser-sparql-1-1'
import { Generator } from '@traqula/generator-sparql-1-1'
import { AstFactory, type QuerySelect } from '@traqula/rules-sparql-1-1'
const parser = new Parser();
const generator = new Generator();
const F = new AstFactory();
const query = `
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
SELECT * { ?s ?p ?o }
`
const ast = <QuerySelect> parser.parse(query);
ast.context.append(F.contextDefinitionPrefix(
F.gen(),
'ex',
F.termNamed(F.gen(), 'https://example.com/')
));
const truety = generator.generate(ast) === `
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX ex: <https://example.com/>
SELECT * { ?s ?p ?o }
`;
In order to facilitate the manipulation of multiple nodes, possibly deep within the AST,
Traqula provides a generic transformer that uses the type and subType of the AST nodes.
The example below shows how the transformer can capitalize all string literals in a SPARQL query:
import { AstTransformer, AstFactory } from "@traqula/rules-sparql-1-1";
import { Parser } from '@traqula/parser-sparql-1-1'
import { Generator } from '@traqula/generator-sparql-1-1'
const parser = new Parser();
const transformer = new AstTransformer();
const F = new AstFactory();
const generator = new Generator();
const ast = parser.parse(`
SELECT * {
?s ?p 'bang', 'help',
'monster' .
}
`)
const transformed = transformer.transformNodeSpecific(ast, {}, {
// Target Nodes of type 'term' and subType 'literal', providing the function to transform them.
'term': { 'literal': { transform: (literal) => {
if (typeof literal.langOrIri === 'object' &&
literal.langOrIri.value === 'http://www.w3.org/2001/XMLSchema#string') {
return F.literal(F.sourceLocationNodeReplaceUnsafe(literal), literal.value.toUpperCase());
}
return literal;
}}}
});
const truety = generator.generate(transformed) === `
SELECT * {
?s ?p 'BANG', 'HELP',
'MONSTER' .
}
`