@malloydata/malloy 0.0.396 → 0.0.398
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/annotation.d.ts +85 -1
- package/dist/annotation.js +133 -47
- package/dist/api/core.js +2 -7
- package/dist/api/foundation/core.d.ts +10 -0
- package/dist/api/foundation/core.js +32 -9
- package/dist/api/foundation/runtime.js +1 -1
- package/dist/lang/composite-source-utils.js +1 -1
- package/dist/lang/malloy-to-ast.d.ts +7 -0
- package/dist/lang/malloy-to-ast.js +26 -10
- package/dist/lang/malloy-to-stable-query.js +3 -3
- package/dist/lang/parse-log.d.ts +6 -1
- package/dist/lang/parse-log.js +8 -0
- package/dist/lang/parse-malloy.js +1 -1
- package/dist/lang/parse-tree-walkers/model-annotation-walker.js +3 -11
- package/dist/lang/parse-utils.d.ts +10 -2
- package/dist/lang/parse-utils.js +89 -29
- package/dist/model/malloy_types.d.ts +7 -0
- package/dist/model/persist_utils.js +1 -1
- package/dist/model/query_node.js +1 -1
- package/dist/prefix.d.ts +51 -0
- package/dist/prefix.js +99 -0
- package/dist/taggable.d.ts +17 -1
- package/dist/test/resultMatchers.js +2 -1
- package/dist/to_stable.d.ts +7 -1
- package/dist/to_stable.js +13 -16
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
package/dist/annotation.d.ts
CHANGED
|
@@ -1,12 +1,96 @@
|
|
|
1
1
|
import type { Tag } from '@malloydata/malloy-tag';
|
|
2
|
-
import type { Annotation } from './model';
|
|
2
|
+
import type { Annotation, DocumentLocation } from './model';
|
|
3
3
|
import type { LogMessage } from './lang';
|
|
4
|
+
/**
|
|
5
|
+
* @deprecated Argument shape for the deprecated RegExp form of
|
|
6
|
+
* {@link annotationToTag}. The RegExp form cannot see block annotations
|
|
7
|
+
* (`#|`…`|#`). Pass a route string to `annotationToTag` instead, or use the
|
|
8
|
+
* {@link Annotations} view on a tagged entity.
|
|
9
|
+
*/
|
|
4
10
|
export interface TagParseSpec {
|
|
5
11
|
prefix?: RegExp;
|
|
6
12
|
}
|
|
13
|
+
/** One annotation, unparsed — its raw text and where its content begins. */
|
|
14
|
+
export interface AnnotationText {
|
|
15
|
+
/** The annotation exactly as written — prefix + content. */
|
|
16
|
+
rawText: string;
|
|
17
|
+
/** Offset where the content begins; `rawText.slice(contentIndex)` is the content. */
|
|
18
|
+
contentIndex: number;
|
|
19
|
+
/** Where `rawText` begins in the source document. */
|
|
20
|
+
at: DocumentLocation;
|
|
21
|
+
/**
|
|
22
|
+
* For block annotations: characters of leading whitespace removed from
|
|
23
|
+
* each body line by the translator's dedent pass. A BYO parser that wants
|
|
24
|
+
* source-mapped error columns adds this to the parser's reported column for
|
|
25
|
+
* body lines (`source_col = indentStripped + parser_col`).
|
|
26
|
+
*/
|
|
27
|
+
indentStripped?: number;
|
|
28
|
+
}
|
|
29
|
+
/** An {@link AnnotationText} that also carries its route (`''` is MOTLY). */
|
|
30
|
+
export interface RoutedAnnotation extends AnnotationText {
|
|
31
|
+
route: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Collect annotations, using the shared prefix parser.
|
|
35
|
+
* - no `route`: every annotation, each carrying its own `route` (the only way
|
|
36
|
+
* to reach one whose prefix is malformed).
|
|
37
|
+
* - a `route`: only annotations on that route, `route` omitted from each result
|
|
38
|
+
* (you passed it); malformed prefixes excluded.
|
|
39
|
+
*/
|
|
40
|
+
export declare function collectAnnotations(annote: Annotation | undefined): RoutedAnnotation[];
|
|
41
|
+
export declare function collectAnnotations(annote: Annotation | undefined, route: string): AnnotationText[];
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated The RegExp form cannot see block annotations (`#|`…`|#`). Use
|
|
44
|
+
* `new Annotations(annote).texts(route)` instead, or the {@link Annotations}
|
|
45
|
+
* view on a tagged entity (`entity.annotations.texts(route)`).
|
|
46
|
+
*/
|
|
7
47
|
export declare function annotationToTaglines(annote: Annotation | undefined, prefix?: RegExp): string[];
|
|
8
48
|
export interface MalloyTagParse {
|
|
9
49
|
tag: Tag;
|
|
10
50
|
log: LogMessage[];
|
|
11
51
|
}
|
|
52
|
+
/** Parse the annotations on `route` (default `''`, the MOTLY tag route) as MOTLY. */
|
|
53
|
+
export declare function annotationToTag(annote: Annotation | undefined, route?: string): MalloyTagParse;
|
|
54
|
+
/**
|
|
55
|
+
* @deprecated The RegExp `prefix` form cannot see block annotations
|
|
56
|
+
* (`#|`…`|#`) and cannot report content offsets for error mapping. Pass a route
|
|
57
|
+
* string (the other overload), or use {@link Annotations.parseAsTag} on a
|
|
58
|
+
* tagged entity.
|
|
59
|
+
*/
|
|
12
60
|
export declare function annotationToTag(annote: Annotation | undefined, spec?: TagParseSpec): MalloyTagParse;
|
|
61
|
+
/**
|
|
62
|
+
* The route-aware annotation API for a tagged entity.
|
|
63
|
+
*
|
|
64
|
+
* An annotation has a *prefix* (everything from `#`/`##` up to the first
|
|
65
|
+
* whitespace) that resolves to a *route* — a namespace key. Built-in routes:
|
|
66
|
+
* `''` (MOTLY tags, the human default), `!` (compiler flags), `@` (persistence
|
|
67
|
+
* directives), `"` (doc-string markdown). Apps stake their own routes with
|
|
68
|
+
* brackets: `#(myApp) ...` is route `myApp`. The grammar (forms, bracket
|
|
69
|
+
* pairs, malformation warnings) lives in `./prefix.ts`.
|
|
70
|
+
*
|
|
71
|
+
* All annotation reading lives here, written once; each tagged class only has
|
|
72
|
+
* to say *where* its annotation is (by handing it to the constructor). Unlike
|
|
73
|
+
* the deprecated RegExp readers (`tagParse`/`getTaglines`), this sees block
|
|
74
|
+
* annotations.
|
|
75
|
+
*/
|
|
76
|
+
export declare class Annotations {
|
|
77
|
+
private readonly annote;
|
|
78
|
+
constructor(annote: Annotation | undefined);
|
|
79
|
+
/**
|
|
80
|
+
* Raw annotation text strings (prefix + content) — all routes if `route` is
|
|
81
|
+
* omitted, just that route's otherwise. The route-based successor to the
|
|
82
|
+
* deprecated `getTaglines`. For source-mapped offsets (bring-your-own
|
|
83
|
+
* parsers), see {@link forRoute}.
|
|
84
|
+
*/
|
|
85
|
+
texts(route?: string): string[];
|
|
86
|
+
/**
|
|
87
|
+
* Your route's annotations as objects (`rawText` + `contentIndex` + `at`) —
|
|
88
|
+
* the bring-your-own-parser door. A non-MOTLY app (e.g. JSON on its own
|
|
89
|
+
* route) reads these to slice the content (`rawText.slice(contentIndex)`)
|
|
90
|
+
* itself and map its parser's errors back to source via `at`. Malformed-prefix
|
|
91
|
+
* annotations are excluded.
|
|
92
|
+
*/
|
|
93
|
+
forRoute(route: string): AnnotationText[];
|
|
94
|
+
/** Parse a route's annotations as a MOTLY tag. Default `''` is the tag route. */
|
|
95
|
+
parseAsTag(route?: string): MalloyTagParse;
|
|
96
|
+
}
|
package/dist/annotation.js
CHANGED
|
@@ -1,50 +1,80 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Annotations = void 0;
|
|
4
|
+
exports.collectAnnotations = collectAnnotations;
|
|
3
5
|
exports.annotationToTaglines = annotationToTaglines;
|
|
4
6
|
exports.annotationToTag = annotationToTag;
|
|
5
7
|
const malloy_tag_1 = require("@malloydata/malloy-tag");
|
|
8
|
+
const prefix_1 = require("./prefix");
|
|
9
|
+
/** Every Note of an annotation, inherited first, in document order. */
|
|
10
|
+
function* notesInOrder(annote) {
|
|
11
|
+
if (annote.inherits)
|
|
12
|
+
yield* notesInOrder(annote.inherits);
|
|
13
|
+
if (annote.blockNotes)
|
|
14
|
+
yield* annote.blockNotes;
|
|
15
|
+
if (annote.notes)
|
|
16
|
+
yield* annote.notes;
|
|
17
|
+
}
|
|
18
|
+
function collectAnnotations(annote, route) {
|
|
19
|
+
if (route === undefined) {
|
|
20
|
+
return Array.from(notesInOrder(annote !== null && annote !== void 0 ? annote : {}), note => {
|
|
21
|
+
const { route: noteRoute, contentIndex } = (0, prefix_1.parsePrefix)(note.text);
|
|
22
|
+
return {
|
|
23
|
+
rawText: note.text,
|
|
24
|
+
contentIndex,
|
|
25
|
+
at: note.at,
|
|
26
|
+
route: noteRoute,
|
|
27
|
+
indentStripped: note.indentStripped,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const matching = [];
|
|
32
|
+
for (const note of notesInOrder(annote !== null && annote !== void 0 ? annote : {})) {
|
|
33
|
+
const parsed = (0, prefix_1.parsePrefix)(note.text);
|
|
34
|
+
if (parsed.route === route && parsed.malformation !== 'malformed-route') {
|
|
35
|
+
matching.push({
|
|
36
|
+
rawText: note.text,
|
|
37
|
+
contentIndex: parsed.contentIndex,
|
|
38
|
+
at: note.at,
|
|
39
|
+
indentStripped: note.indentStripped,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return matching;
|
|
44
|
+
}
|
|
6
45
|
/**
|
|
7
46
|
* Collect all matching Notes from an Annotation, walking the inherits
|
|
8
47
|
* chain. Returns notes in inheritance order (inherited first).
|
|
48
|
+
*
|
|
49
|
+
* @deprecated RegExp prefix matching; use {@link collectAnnotations} with a route.
|
|
9
50
|
*/
|
|
10
51
|
function collectNotes(annote, prefix) {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
: [];
|
|
14
|
-
const allNotes = [];
|
|
15
|
-
if (annote.blockNotes) {
|
|
16
|
-
allNotes.push(...annote.blockNotes);
|
|
17
|
-
}
|
|
18
|
-
if (annote.notes) {
|
|
19
|
-
allNotes.push(...annote.notes);
|
|
20
|
-
}
|
|
21
|
-
if (prefix) {
|
|
22
|
-
const matching = allNotes.filter(note => note.text.match(prefix));
|
|
23
|
-
return inherited.concat(matching);
|
|
24
|
-
}
|
|
25
|
-
return inherited.concat(allNotes);
|
|
52
|
+
const notes = [...notesInOrder(annote)];
|
|
53
|
+
return prefix ? notes.filter(note => note.text.match(prefix)) : notes;
|
|
26
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* @deprecated The RegExp form cannot see block annotations (`#|`…`|#`). Use
|
|
57
|
+
* `new Annotations(annote).texts(route)` instead, or the {@link Annotations}
|
|
58
|
+
* view on a tagged entity (`entity.annotations.texts(route)`).
|
|
59
|
+
*/
|
|
27
60
|
function annotationToTaglines(annote, prefix) {
|
|
28
61
|
return collectNotes(annote || {}, prefix).map(n => n.text);
|
|
29
62
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
annote || (annote = {});
|
|
33
|
-
const notes = collectNotes(annote, prefix);
|
|
63
|
+
/** Parse a run of Notes as MOTLY into one Tag, collecting errors. */
|
|
64
|
+
function parseTaglines(lines) {
|
|
34
65
|
const allErrs = [];
|
|
35
66
|
const session = new malloy_tag_1.TagParser();
|
|
36
|
-
for (const
|
|
67
|
+
for (const line of lines) {
|
|
37
68
|
const origin = {
|
|
38
|
-
url:
|
|
39
|
-
startLine:
|
|
40
|
-
startColumn:
|
|
69
|
+
url: line.at.url,
|
|
70
|
+
startLine: line.at.range.start.line,
|
|
71
|
+
startColumn: line.at.range.start.character,
|
|
41
72
|
};
|
|
42
|
-
const noteParse = session.parseAnnotation(
|
|
43
|
-
allErrs.push(...noteParse.log.map((e) => mapMalloyError(e,
|
|
73
|
+
const noteParse = session.parseAnnotation(line.text, origin);
|
|
74
|
+
allErrs.push(...noteParse.log.map((e) => mapMalloyError(e, line)));
|
|
44
75
|
}
|
|
45
76
|
const tag = session.finish();
|
|
46
|
-
const
|
|
47
|
-
for (const refError of refErrors) {
|
|
77
|
+
for (const refError of tag.validateReferences()) {
|
|
48
78
|
allErrs.push({
|
|
49
79
|
code: 'tag-reference-error',
|
|
50
80
|
severity: 'warn',
|
|
@@ -53,25 +83,78 @@ function annotationToTag(annote, spec = {}) {
|
|
|
53
83
|
}
|
|
54
84
|
return { tag, log: allErrs };
|
|
55
85
|
}
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
86
|
+
function annotationToTag(annote, arg) {
|
|
87
|
+
if (typeof arg === 'object') {
|
|
88
|
+
const prefix = arg.prefix || /^##? /;
|
|
89
|
+
return parseTaglines(collectNotes(annote !== null && annote !== void 0 ? annote : {}, prefix));
|
|
90
|
+
}
|
|
91
|
+
const matched = collectAnnotations(annote, arg !== null && arg !== void 0 ? arg : '');
|
|
92
|
+
return parseTaglines(matched.map(a => ({
|
|
93
|
+
text: a.rawText,
|
|
94
|
+
at: a.at,
|
|
95
|
+
indentStripped: a.indentStripped,
|
|
96
|
+
})));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The route-aware annotation API for a tagged entity.
|
|
100
|
+
*
|
|
101
|
+
* An annotation has a *prefix* (everything from `#`/`##` up to the first
|
|
102
|
+
* whitespace) that resolves to a *route* — a namespace key. Built-in routes:
|
|
103
|
+
* `''` (MOTLY tags, the human default), `!` (compiler flags), `@` (persistence
|
|
104
|
+
* directives), `"` (doc-string markdown). Apps stake their own routes with
|
|
105
|
+
* brackets: `#(myApp) ...` is route `myApp`. The grammar (forms, bracket
|
|
106
|
+
* pairs, malformation warnings) lives in `./prefix.ts`.
|
|
107
|
+
*
|
|
108
|
+
* All annotation reading lives here, written once; each tagged class only has
|
|
109
|
+
* to say *where* its annotation is (by handing it to the constructor). Unlike
|
|
110
|
+
* the deprecated RegExp readers (`tagParse`/`getTaglines`), this sees block
|
|
111
|
+
* annotations.
|
|
112
|
+
*/
|
|
113
|
+
class Annotations {
|
|
114
|
+
constructor(annote) {
|
|
115
|
+
this.annote = annote;
|
|
64
116
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Raw annotation text strings (prefix + content) — all routes if `route` is
|
|
119
|
+
* omitted, just that route's otherwise. The route-based successor to the
|
|
120
|
+
* deprecated `getTaglines`. For source-mapped offsets (bring-your-own
|
|
121
|
+
* parsers), see {@link forRoute}.
|
|
122
|
+
*/
|
|
123
|
+
texts(route) {
|
|
124
|
+
const items = route === undefined
|
|
125
|
+
? collectAnnotations(this.annote)
|
|
126
|
+
: collectAnnotations(this.annote, route);
|
|
127
|
+
return items.map(a => a.rawText);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Your route's annotations as objects (`rawText` + `contentIndex` + `at`) —
|
|
131
|
+
* the bring-your-own-parser door. A non-MOTLY app (e.g. JSON on its own
|
|
132
|
+
* route) reads these to slice the content (`rawText.slice(contentIndex)`)
|
|
133
|
+
* itself and map its parser's errors back to source via `at`. Malformed-prefix
|
|
134
|
+
* annotations are excluded.
|
|
135
|
+
*/
|
|
136
|
+
forRoute(route) {
|
|
137
|
+
return collectAnnotations(this.annote, route);
|
|
138
|
+
}
|
|
139
|
+
/** Parse a route's annotations as a MOTLY tag. Default `''` is the tag route. */
|
|
140
|
+
parseAsTag(route = '') {
|
|
141
|
+
return annotationToTag(this.annote, route);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.Annotations = Annotations;
|
|
145
|
+
function mapMalloyError(e, note) {
|
|
146
|
+
var _a;
|
|
147
|
+
// MOTLY reports `e.line` / `e.offset` into the *stripped* note text it
|
|
148
|
+
// parsed. To map back to source:
|
|
149
|
+
// line 0 (opener line): col = opener_col + prefix_len + e.offset
|
|
150
|
+
// line N>0 (body lines): col = indentStripped + e.offset
|
|
151
|
+
// `indentStripped` is the per-line dedent recorded on the Note by the
|
|
152
|
+
// translator (uniform per block, so the same formula serves every body
|
|
153
|
+
// line). Prefix length is everything before the separator, via parsePrefix.
|
|
71
154
|
const line = note.at.range.start.line + e.line;
|
|
72
155
|
const character = e.line === 0
|
|
73
|
-
? note.at.range.start.character +
|
|
74
|
-
: e.offset;
|
|
156
|
+
? note.at.range.start.character + prefixLength(note.text) + e.offset
|
|
157
|
+
: ((_a = note.indentStripped) !== null && _a !== void 0 ? _a : 0) + e.offset;
|
|
75
158
|
const loc = { line, character };
|
|
76
159
|
return {
|
|
77
160
|
code: 'tag-parse-error',
|
|
@@ -79,11 +162,14 @@ function mapMalloyError(e, note) {
|
|
|
79
162
|
message: e.message,
|
|
80
163
|
at: {
|
|
81
164
|
url: note.at.url,
|
|
82
|
-
range: {
|
|
83
|
-
start: loc,
|
|
84
|
-
end: loc,
|
|
85
|
-
},
|
|
165
|
+
range: { start: loc, end: loc },
|
|
86
166
|
},
|
|
87
167
|
};
|
|
88
168
|
}
|
|
169
|
+
/** Length of the annotation prefix per malloy-tag's `stripPrefix`: index of
|
|
170
|
+
* the first whitespace, or 0 if none. */
|
|
171
|
+
function prefixLength(text) {
|
|
172
|
+
const { contentIndex } = (0, prefix_1.parsePrefix)(text);
|
|
173
|
+
return contentIndex === text.length ? 0 : contentIndex - 1;
|
|
174
|
+
}
|
|
89
175
|
//# sourceMappingURL=annotation.js.map
|
package/dist/api/core.js
CHANGED
|
@@ -56,7 +56,6 @@ const lang_1 = require("../lang");
|
|
|
56
56
|
const model_1 = require("../model");
|
|
57
57
|
const to_stable_1 = require("../to_stable");
|
|
58
58
|
const sql_block_1 = require("../model/sql_block");
|
|
59
|
-
const annotation_1 = require("../annotation");
|
|
60
59
|
const malloy_tag_1 = require("@malloydata/malloy-tag");
|
|
61
60
|
const util_1 = require("./util");
|
|
62
61
|
const timing_1 = require("../timing");
|
|
@@ -506,9 +505,7 @@ function statedCompileQuery(state) {
|
|
|
506
505
|
defaultRowLimit: state.defaultRowLimit,
|
|
507
506
|
});
|
|
508
507
|
timer.contribute([sqlTimer.stop()]);
|
|
509
|
-
const modelAnnotations = (0,
|
|
510
|
-
value: l,
|
|
511
|
-
}));
|
|
508
|
+
const modelAnnotations = (0, to_stable_1.toStableAnnotations)(result.modelDef.annotation);
|
|
512
509
|
let source;
|
|
513
510
|
if (query.compositeResolvedSourceDef) {
|
|
514
511
|
source = query.compositeResolvedSourceDef;
|
|
@@ -522,9 +519,7 @@ function statedCompileQuery(state) {
|
|
|
522
519
|
source = (0, model_1.safeRecordGet)(result.modelDef.contents, query.structRef);
|
|
523
520
|
}
|
|
524
521
|
}
|
|
525
|
-
const sourceAnnotations = (0,
|
|
526
|
-
value: l,
|
|
527
|
-
}));
|
|
522
|
+
const sourceAnnotations = (0, to_stable_1.toStableAnnotations)(source.annotation);
|
|
528
523
|
const sourceMetadataTag = malloy_tag_1.Tag.withPrefix('#(malloy) ');
|
|
529
524
|
sourceMetadataTag.set(['source', 'name'], translatedQuery.sourceExplore);
|
|
530
525
|
const sourceArguments = (_b = translatedQuery.sourceArguments) !== null && _b !== void 0 ? _b : ((0, model_1.isSourceDef)(source) ? source.arguments : undefined);
|
|
@@ -5,6 +5,7 @@ import type { Dialect } from '../../dialect';
|
|
|
5
5
|
import type { BuildGraph, CompileQueryOptions } from './types';
|
|
6
6
|
import { Tag } from '@malloydata/malloy-tag';
|
|
7
7
|
import type { MalloyTagParse, TagParseSpec } from '../../annotation';
|
|
8
|
+
import { Annotations } from '../../annotation';
|
|
8
9
|
import type * as Malloy from '@malloydata/malloy-interfaces';
|
|
9
10
|
import type { Taggable } from '../../taggable';
|
|
10
11
|
declare abstract class Entity {
|
|
@@ -101,6 +102,7 @@ export declare class Explore extends Entity implements Taggable {
|
|
|
101
102
|
isExploreField(): this is ExploreField;
|
|
102
103
|
tagParse(spec?: TagParseSpec): MalloyTagParse;
|
|
103
104
|
getTaglines(prefix?: RegExp): string[];
|
|
105
|
+
get annotations(): Annotations;
|
|
104
106
|
private parsedModelTag?;
|
|
105
107
|
get modelTag(): Tag;
|
|
106
108
|
/**
|
|
@@ -141,6 +143,7 @@ export declare class AtomicField extends Entity implements Taggable {
|
|
|
141
143
|
get type(): AtomicFieldType;
|
|
142
144
|
tagParse(spec?: TagParseSpec): MalloyTagParse;
|
|
143
145
|
getTaglines(prefix?: RegExp): string[];
|
|
146
|
+
get annotations(): Annotations;
|
|
144
147
|
isIntrinsic(): boolean;
|
|
145
148
|
isQueryField(): this is QueryField;
|
|
146
149
|
isExploreField(): this is ExploreField;
|
|
@@ -208,6 +211,7 @@ export declare class QueryField extends Query implements Taggable {
|
|
|
208
211
|
constructor(turtleDef: TurtleDef, parent: Explore, source?: Query);
|
|
209
212
|
tagParse(spec?: TagParseSpec): MalloyTagParse;
|
|
210
213
|
getTaglines(prefix?: RegExp): string[];
|
|
214
|
+
get annotations(): Annotations;
|
|
211
215
|
isQueryField(): this is QueryField;
|
|
212
216
|
isExploreField(): this is ExploreField;
|
|
213
217
|
isAtomicField(): this is AtomicField;
|
|
@@ -223,6 +227,7 @@ export declare class ExploreField extends Explore {
|
|
|
223
227
|
get isRecord(): boolean;
|
|
224
228
|
get isArray(): boolean;
|
|
225
229
|
tagParse(spec?: TagParseSpec): MalloyTagParse;
|
|
230
|
+
get annotations(): Annotations;
|
|
226
231
|
isQueryField(): this is QueryField;
|
|
227
232
|
isExploreField(): this is ExploreField;
|
|
228
233
|
isAtomicField(): this is AtomicField;
|
|
@@ -282,6 +287,7 @@ export declare class Model implements Taggable {
|
|
|
282
287
|
get givens(): ReadonlyMap<string, Given>;
|
|
283
288
|
tagParse(spec?: TagParseSpec): MalloyTagParse;
|
|
284
289
|
getTaglines(prefix?: RegExp): string[];
|
|
290
|
+
get annotations(): Annotations;
|
|
285
291
|
/**
|
|
286
292
|
* Retrieve a document reference for the token at the given position within
|
|
287
293
|
* the document that produced this model.
|
|
@@ -419,6 +425,7 @@ export declare class PersistSource implements Taggable {
|
|
|
419
425
|
* Get annotation taglines matching an optional prefix.
|
|
420
426
|
*/
|
|
421
427
|
getTaglines(prefix?: RegExp): string[];
|
|
428
|
+
get annotations(): Annotations;
|
|
422
429
|
/**
|
|
423
430
|
* The connection name for this source.
|
|
424
431
|
*/
|
|
@@ -484,6 +491,7 @@ export declare class Given implements Taggable {
|
|
|
484
491
|
get location(): DocumentLocation | undefined;
|
|
485
492
|
tagParse(spec?: TagParseSpec): MalloyTagParse;
|
|
486
493
|
getTaglines(prefix?: RegExp): string[];
|
|
494
|
+
get annotations(): Annotations;
|
|
487
495
|
}
|
|
488
496
|
export declare class PreparedQuery implements Taggable {
|
|
489
497
|
private _model;
|
|
@@ -494,6 +502,7 @@ export declare class PreparedQuery implements Taggable {
|
|
|
494
502
|
get _modelDef(): ModelDef;
|
|
495
503
|
tagParse(spec?: TagParseSpec): MalloyTagParse;
|
|
496
504
|
getTaglines(prefix?: RegExp): string[];
|
|
505
|
+
get annotations(): Annotations;
|
|
497
506
|
/**
|
|
498
507
|
* Generate the SQL for this query.
|
|
499
508
|
*
|
|
@@ -537,6 +546,7 @@ export declare class PreparedResult implements Taggable {
|
|
|
537
546
|
static fromJson({ query, modelDef, }: PreparedResultJSON): PreparedResult;
|
|
538
547
|
tagParse(spec?: TagParseSpec): MalloyTagParse;
|
|
539
548
|
getTaglines(prefix?: RegExp): string[];
|
|
549
|
+
get annotations(): Annotations;
|
|
540
550
|
get annotation(): Annotation | undefined;
|
|
541
551
|
get modelAnnotation(): Annotation | undefined;
|
|
542
552
|
get modelTag(): Tag;
|
|
@@ -154,8 +154,11 @@ class Explore extends Entity {
|
|
|
154
154
|
getTaglines(prefix) {
|
|
155
155
|
return (0, annotation_1.annotationToTaglines)(this._structDef.annotation, prefix);
|
|
156
156
|
}
|
|
157
|
+
get annotations() {
|
|
158
|
+
return new annotation_1.Annotations(this._structDef.annotation);
|
|
159
|
+
}
|
|
157
160
|
get modelTag() {
|
|
158
|
-
this.parsedModelTag || (this.parsedModelTag =
|
|
161
|
+
this.parsedModelTag || (this.parsedModelTag = new annotation_1.Annotations(this._structDef.modelAnnotation).parseAsTag().tag);
|
|
159
162
|
return this.parsedModelTag;
|
|
160
163
|
}
|
|
161
164
|
/**
|
|
@@ -465,6 +468,9 @@ class AtomicField extends Entity {
|
|
|
465
468
|
getTaglines(prefix) {
|
|
466
469
|
return (0, annotation_1.annotationToTaglines)(this.fieldTypeDef.annotation, prefix);
|
|
467
470
|
}
|
|
471
|
+
get annotations() {
|
|
472
|
+
return new annotation_1.Annotations(this.fieldTypeDef.annotation);
|
|
473
|
+
}
|
|
468
474
|
isIntrinsic() {
|
|
469
475
|
return (0, model_1.fieldIsIntrinsic)(this.fieldTypeDef);
|
|
470
476
|
}
|
|
@@ -666,6 +672,9 @@ class QueryField extends Query {
|
|
|
666
672
|
getTaglines(prefix) {
|
|
667
673
|
return (0, annotation_1.annotationToTaglines)(this.turtleDef.annotation, prefix);
|
|
668
674
|
}
|
|
675
|
+
get annotations() {
|
|
676
|
+
return new annotation_1.Annotations(this.turtleDef.annotation);
|
|
677
|
+
}
|
|
669
678
|
isQueryField() {
|
|
670
679
|
return true;
|
|
671
680
|
}
|
|
@@ -719,6 +728,9 @@ class ExploreField extends Explore {
|
|
|
719
728
|
tagParse(spec) {
|
|
720
729
|
return (0, annotation_1.annotationToTag)(this._structDef.annotation, spec);
|
|
721
730
|
}
|
|
731
|
+
get annotations() {
|
|
732
|
+
return new annotation_1.Annotations(this._structDef.annotation);
|
|
733
|
+
}
|
|
722
734
|
isQueryField() {
|
|
723
735
|
return false;
|
|
724
736
|
}
|
|
@@ -807,6 +819,9 @@ class Model {
|
|
|
807
819
|
getTaglines(prefix) {
|
|
808
820
|
return (0, annotation_1.annotationToTaglines)(this.modelDef.annotation, prefix);
|
|
809
821
|
}
|
|
822
|
+
get annotations() {
|
|
823
|
+
return new annotation_1.Annotations(this.modelDef.annotation);
|
|
824
|
+
}
|
|
810
825
|
/**
|
|
811
826
|
* Retrieve a document reference for the token at the given position within
|
|
812
827
|
* the document that produced this model.
|
|
@@ -932,7 +947,7 @@ class Model {
|
|
|
932
947
|
*/
|
|
933
948
|
getBuildPlan() {
|
|
934
949
|
// Require experimental.persistence compiler flag
|
|
935
|
-
const modelTag = this.
|
|
950
|
+
const modelTag = this.annotations.parseAsTag('!').tag;
|
|
936
951
|
if (!modelTag.has('experimental', 'persistence')) {
|
|
937
952
|
throw new Error('Model must have ##! experimental.persistence to use getBuildPlan()');
|
|
938
953
|
}
|
|
@@ -1057,6 +1072,9 @@ class PersistSource {
|
|
|
1057
1072
|
getTaglines(prefix) {
|
|
1058
1073
|
return this.explore.getTaglines(prefix);
|
|
1059
1074
|
}
|
|
1075
|
+
get annotations() {
|
|
1076
|
+
return this.explore.annotations;
|
|
1077
|
+
}
|
|
1060
1078
|
/**
|
|
1061
1079
|
* The connection name for this source.
|
|
1062
1080
|
*/
|
|
@@ -1159,6 +1177,9 @@ class Given {
|
|
|
1159
1177
|
getTaglines(prefix) {
|
|
1160
1178
|
return (0, annotation_1.annotationToTaglines)(this._internal.annotation, prefix);
|
|
1161
1179
|
}
|
|
1180
|
+
get annotations() {
|
|
1181
|
+
return new annotation_1.Annotations(this._internal.annotation);
|
|
1182
|
+
}
|
|
1162
1183
|
}
|
|
1163
1184
|
exports.Given = Given;
|
|
1164
1185
|
class PreparedQuery {
|
|
@@ -1177,6 +1198,9 @@ class PreparedQuery {
|
|
|
1177
1198
|
getTaglines(prefix) {
|
|
1178
1199
|
return (0, annotation_1.annotationToTaglines)(this._query.annotation, prefix);
|
|
1179
1200
|
}
|
|
1201
|
+
get annotations() {
|
|
1202
|
+
return new annotation_1.Annotations(this._query.annotation);
|
|
1203
|
+
}
|
|
1180
1204
|
/**
|
|
1181
1205
|
* Generate the SQL for this query.
|
|
1182
1206
|
*
|
|
@@ -1281,6 +1305,9 @@ class PreparedResult {
|
|
|
1281
1305
|
getTaglines(prefix) {
|
|
1282
1306
|
return (0, annotation_1.annotationToTaglines)(this.inner.annotation, prefix);
|
|
1283
1307
|
}
|
|
1308
|
+
get annotations() {
|
|
1309
|
+
return new annotation_1.Annotations(this.inner.annotation);
|
|
1310
|
+
}
|
|
1284
1311
|
get annotation() {
|
|
1285
1312
|
return this.inner.annotation;
|
|
1286
1313
|
}
|
|
@@ -1288,7 +1315,7 @@ class PreparedResult {
|
|
|
1288
1315
|
return this.modelDef.annotation;
|
|
1289
1316
|
}
|
|
1290
1317
|
get modelTag() {
|
|
1291
|
-
return
|
|
1318
|
+
return new annotation_1.Annotations(this.modelDef.annotation).parseAsTag().tag;
|
|
1292
1319
|
}
|
|
1293
1320
|
/**
|
|
1294
1321
|
* @return The name of the connection this query should be run against.
|
|
@@ -1361,9 +1388,7 @@ class PreparedResult {
|
|
|
1361
1388
|
const structs = this.inner.structs;
|
|
1362
1389
|
const struct = structs[structs.length - 1];
|
|
1363
1390
|
const schema = { fields: (0, to_stable_1.convertFieldInfos)(struct, struct.fields) };
|
|
1364
|
-
const annotations = (0,
|
|
1365
|
-
value: l,
|
|
1366
|
-
}));
|
|
1391
|
+
const annotations = (0, to_stable_1.toStableAnnotations)(this.inner.annotation);
|
|
1367
1392
|
const metadataAnnot = struct.resultMetadata
|
|
1368
1393
|
? (0, to_stable_1.getResultStructMetadataAnnotation)(struct, struct.resultMetadata)
|
|
1369
1394
|
: undefined;
|
|
@@ -1392,9 +1417,7 @@ class PreparedResult {
|
|
|
1392
1417
|
.set(['query_name'], this.inner.queryName || struct.name)
|
|
1393
1418
|
.toString(),
|
|
1394
1419
|
});
|
|
1395
|
-
const modelAnnotations = (0,
|
|
1396
|
-
value: l,
|
|
1397
|
-
}));
|
|
1420
|
+
const modelAnnotations = (0, to_stable_1.toStableAnnotations)(this.modelDef.annotation);
|
|
1398
1421
|
return {
|
|
1399
1422
|
schema,
|
|
1400
1423
|
data,
|
|
@@ -1010,7 +1010,7 @@ class QueryMaterializer extends FluentState {
|
|
|
1010
1010
|
buildManifest = undefined;
|
|
1011
1011
|
}
|
|
1012
1012
|
if (buildManifest) {
|
|
1013
|
-
const modelTag = preparedQuery.model.
|
|
1013
|
+
const modelTag = preparedQuery.model.annotations.parseAsTag('!').tag;
|
|
1014
1014
|
if (!modelTag.has('experimental', 'persistence')) {
|
|
1015
1015
|
if (explicitManifest) {
|
|
1016
1016
|
// Explicitly passed non-empty manifest requires persistence support
|
|
@@ -547,7 +547,7 @@ function getPartitionCompositeFilter(partitionComposite, fieldUsage) {
|
|
|
547
547
|
function getPartitionCompositeDesc(annotation, structDef, logTo) {
|
|
548
548
|
if (annotation === undefined)
|
|
549
549
|
return undefined;
|
|
550
|
-
const compilerFlags =
|
|
550
|
+
const compilerFlags = new annotation_1.Annotations(annotation).parseAsTag('!').tag;
|
|
551
551
|
const partitionCompositeTag = compilerFlags.tag('experimental', 'partition_composite');
|
|
552
552
|
if (partitionCompositeTag === undefined)
|
|
553
553
|
return undefined;
|
|
@@ -92,6 +92,13 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
|
|
|
92
92
|
*/
|
|
93
93
|
protected parseTime(pcx: ParserRuleContext, parse: (s: string) => ast.ExpressionDef | undefined): ast.ExpressionDef;
|
|
94
94
|
protected getAnnotation(cx: parse.AnnotationContext): Note;
|
|
95
|
+
/**
|
|
96
|
+
* Warn if the annotation prefix is not a well-formed route. The note is still
|
|
97
|
+
* stored either way — the malformation only drives the diagnostic, never the
|
|
98
|
+
* IR. Warnings fire at note construction; inherited annotations carry no
|
|
99
|
+
* malformation marker through the IR and are not re-warned by importers.
|
|
100
|
+
*/
|
|
101
|
+
private warnIfMalformedPrefix;
|
|
95
102
|
protected getNotes(cx: HasAnnotations): Note[];
|
|
96
103
|
protected getIsNotes(cx: parse.IsDefineContext): Note[];
|
|
97
104
|
visitMalloyDocument(pcx: parse.MalloyDocumentContext): ast.Document;
|
|
@@ -62,6 +62,7 @@ const AbstractParseTreeVisitor_1 = require("antlr4ts/tree/AbstractParseTreeVisit
|
|
|
62
62
|
const ast = __importStar(require("./ast"));
|
|
63
63
|
const parse_log_1 = require("./parse-log");
|
|
64
64
|
const Interval_1 = require("antlr4ts/misc/Interval");
|
|
65
|
+
const prefix_1 = require("../prefix");
|
|
65
66
|
const ast_1 = require("./ast");
|
|
66
67
|
const parse_utils_1 = require("./parse-utils");
|
|
67
68
|
const malloy_types_1 = require("../model/malloy_types");
|
|
@@ -271,12 +272,26 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
271
272
|
return this.astAt(def, pcx);
|
|
272
273
|
}
|
|
273
274
|
getAnnotation(cx) {
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
275
|
+
const note = (0, parse_utils_1.noteFromAnnotation)(cx, this.parseInfo);
|
|
276
|
+
this.warnIfMalformedPrefix(note.text, cx);
|
|
277
|
+
return note;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Warn if the annotation prefix is not a well-formed route. The note is still
|
|
281
|
+
* stored either way — the malformation only drives the diagnostic, never the
|
|
282
|
+
* IR. Warnings fire at note construction; inherited annotations carry no
|
|
283
|
+
* malformation marker through the IR and are not re-warned by importers.
|
|
284
|
+
*/
|
|
285
|
+
warnIfMalformedPrefix(text, cx) {
|
|
286
|
+
const parsed = (0, prefix_1.parsePrefix)(text);
|
|
287
|
+
if (parsed.malformation === undefined)
|
|
288
|
+
return;
|
|
289
|
+
// The slice up to contentIndex is "prefix + separator"; trim trailing
|
|
290
|
+
// whitespace to land on the prefix the user wrote. (A no-content single-
|
|
291
|
+
// line note like `#malformed\n` exposes this: contentIndex === text.length
|
|
292
|
+
// but the slice still ends at the `\n`.)
|
|
293
|
+
const prefix = text.slice(0, parsed.contentIndex).replace(/\s+$/, '');
|
|
294
|
+
this.contextError(cx, parsed.malformation, { prefix });
|
|
280
295
|
}
|
|
281
296
|
getNotes(cx) {
|
|
282
297
|
return cx.annotation().map(a => this.getAnnotation(a));
|
|
@@ -1533,10 +1548,11 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
1533
1548
|
this.contextError(pcx, 'unclosed-block-annotation', 'Block annotation is not closed, add correctly indented "|##"');
|
|
1534
1549
|
}
|
|
1535
1550
|
}
|
|
1536
|
-
const allNotes = pcx.docAnnotation().map(a =>
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1551
|
+
const allNotes = pcx.docAnnotation().map(a => {
|
|
1552
|
+
const note = (0, parse_utils_1.noteFromAnnotation)(a, this.parseInfo);
|
|
1553
|
+
this.warnIfMalformedPrefix(note.text, a);
|
|
1554
|
+
return note;
|
|
1555
|
+
});
|
|
1540
1556
|
const tags = new ast.ModelAnnotation(allNotes);
|
|
1541
1557
|
this.updateCompilerFlags(tags);
|
|
1542
1558
|
return tags;
|
|
@@ -101,9 +101,9 @@ class MalloyToQuery extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor
|
|
|
101
101
|
* @returns Array of texts for the annotations
|
|
102
102
|
*/
|
|
103
103
|
getAnnotations(cx) {
|
|
104
|
-
const annotations = cx.annotation().map(a => {
|
|
105
|
-
|
|
106
|
-
});
|
|
104
|
+
const annotations = cx.annotation().map(a => ({
|
|
105
|
+
value: (0, parse_utils_1.getAnnotationText)(a),
|
|
106
|
+
}));
|
|
107
107
|
return annotations.length > 0 ? annotations : undefined;
|
|
108
108
|
}
|
|
109
109
|
getIsAnnotations(cx) {
|
package/dist/lang/parse-log.d.ts
CHANGED
|
@@ -358,7 +358,12 @@ type MessageParameterTypes = {
|
|
|
358
358
|
'failed-to-parse-function-name': string;
|
|
359
359
|
'orphaned-object-annotation': string;
|
|
360
360
|
'unclosed-block-annotation': string;
|
|
361
|
-
'
|
|
361
|
+
'malformed-route': {
|
|
362
|
+
prefix: string;
|
|
363
|
+
};
|
|
364
|
+
'reserved-route': {
|
|
365
|
+
prefix: string;
|
|
366
|
+
};
|
|
362
367
|
'misplaced-model-annotation': string;
|
|
363
368
|
'unexpected-non-source-query-expression-node': string;
|
|
364
369
|
'sql-not-like': string;
|
package/dist/lang/parse-log.js
CHANGED
|
@@ -105,6 +105,14 @@ exports.MESSAGE_FORMATTERS = {
|
|
|
105
105
|
message: e,
|
|
106
106
|
tag: 'restricted-mode',
|
|
107
107
|
}),
|
|
108
|
+
'malformed-route': e => ({
|
|
109
|
+
message: `Annotation prefix \`${e.prefix}\` is not a well-formed route — write \`# ...\` for a tag (note the space) or \`#(name)\` for an app route`,
|
|
110
|
+
severity: 'warn',
|
|
111
|
+
}),
|
|
112
|
+
'reserved-route': e => ({
|
|
113
|
+
message: `Annotation prefix \`${e.prefix}\` uses an unclaimed sigil; punct-only prefixes are reserved for Malloy's own use`,
|
|
114
|
+
severity: 'warn',
|
|
115
|
+
}),
|
|
108
116
|
};
|
|
109
117
|
function makeLogMessage(code, parameters, options) {
|
|
110
118
|
var _a, _b, _c, _d, _e;
|
|
@@ -478,7 +478,7 @@ class TranslateStep {
|
|
|
478
478
|
// seeding (e.g. TestTranslator's compilerFlags option) survive.
|
|
479
479
|
if (extendingModel && !this.importedAnnotations) {
|
|
480
480
|
const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
|
|
481
|
-
that.compilerFlagSrc.push(...
|
|
481
|
+
that.compilerFlagSrc.push(...new annotation_1.Annotations(extendingModel.annotation).texts('!'));
|
|
482
482
|
stepTimer.contribute([parseCompilerFlagsTimer.stop()]);
|
|
483
483
|
this.importedAnnotations = true;
|
|
484
484
|
}
|
|
@@ -32,18 +32,10 @@ class ModelAnnotationWalker {
|
|
|
32
32
|
this.parseInfo = parseInfo;
|
|
33
33
|
this.notes = [];
|
|
34
34
|
}
|
|
35
|
-
getLocation(cx) {
|
|
36
|
-
return {
|
|
37
|
-
url: this.parseInfo.sourceURL,
|
|
38
|
-
range: this.translator.rangeFromContext(cx),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
35
|
enterDocAnnotations(pcx) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}));
|
|
46
|
-
this.notes.push(...allNotes);
|
|
36
|
+
for (const a of pcx.docAnnotation()) {
|
|
37
|
+
this.notes.push((0, parse_utils_1.noteFromAnnotation)(a, this.parseInfo));
|
|
38
|
+
}
|
|
47
39
|
}
|
|
48
40
|
get annotation() {
|
|
49
41
|
return { notes: this.notes };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ParserRuleContext } from 'antlr4ts';
|
|
2
2
|
import type { DocAnnotationContext } from './lib/Malloy/MalloyParser';
|
|
3
3
|
import { type StringContext, type ShortStringContext, type SqlStringContext, type IdContext, AnnotationContext } from './lib/Malloy/MalloyParser';
|
|
4
|
+
import type { Note } from '../model/malloy_types';
|
|
5
|
+
import type { MalloyParseInfo } from './malloy-parse-info';
|
|
4
6
|
/**
|
|
5
7
|
* Take the text of a matched string, including the matching quote
|
|
6
8
|
* characters, and return the actual contents of the string after
|
|
@@ -35,6 +37,12 @@ export declare function unIndent(parts: (string | unknown)[]): void;
|
|
|
35
37
|
* @returns string part and an error list.
|
|
36
38
|
*/
|
|
37
39
|
export declare function getPlainString(cx: HasString, strictCheck?: boolean): [string | undefined, ParserRuleContext[]];
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Build the IR `Note` for an annotation: reads the text, dedents the body if
|
|
42
|
+
* it's a block, and computes the source `at` from the parse context. The
|
|
43
|
+
* single entry point for going from a parse-tree annotation to an IR note.
|
|
44
|
+
*/
|
|
45
|
+
export declare function noteFromAnnotation(cx: AnnotationContext | DocAnnotationContext, parseInfo: MalloyParseInfo): Note;
|
|
46
|
+
/** Text-only reader, for callers that don't need an IR `Note`. */
|
|
47
|
+
export declare function getAnnotationText(cx: AnnotationContext | DocAnnotationContext): string;
|
|
40
48
|
export {};
|
package/dist/lang/parse-utils.js
CHANGED
|
@@ -30,9 +30,11 @@ exports.idToStr = idToStr;
|
|
|
30
30
|
exports.getOptionalId = getOptionalId;
|
|
31
31
|
exports.unIndent = unIndent;
|
|
32
32
|
exports.getPlainString = getPlainString;
|
|
33
|
+
exports.noteFromAnnotation = noteFromAnnotation;
|
|
33
34
|
exports.getAnnotationText = getAnnotationText;
|
|
34
35
|
const MalloyParser_1 = require("./lib/Malloy/MalloyParser");
|
|
35
36
|
const malloy_tag_1 = require("@malloydata/malloy-tag");
|
|
37
|
+
const utils_1 = require("./utils");
|
|
36
38
|
/**
|
|
37
39
|
* Take the text of a matched string, including the matching quote
|
|
38
40
|
* characters, and return the actual contents of the string after
|
|
@@ -182,51 +184,109 @@ function getPlainString(cx, strictCheck = false) {
|
|
|
182
184
|
// string: shortString | sqlString; So this will never happen
|
|
183
185
|
return ['', errorList];
|
|
184
186
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Python `textwrap.dedent`-style: find the longest leading-whitespace prefix
|
|
189
|
+
* common to every non-blank body line and strip it from each line that starts
|
|
190
|
+
* with it. Blank (whitespace-only) lines don't constrain the prefix. Returns
|
|
191
|
+
* the stripped text and the number of characters removed per line — the
|
|
192
|
+
* latter is stored on the `Note` so payload-parser error columns can be
|
|
193
|
+
* mapped back to source (`source_col = indentStripped + parser_col`).
|
|
194
|
+
*
|
|
195
|
+
* Replaces an older "strip exactly opener_column spaces" rule that fired
|
|
196
|
+
* warnings for less-indented lines and had no clean column mapping when
|
|
197
|
+
* stripping was inconsistent. Common prefix is uniform per block, so column
|
|
198
|
+
* mapping is one number per block.
|
|
199
|
+
*/
|
|
200
|
+
function dedentBlockLines(lines) {
|
|
201
|
+
let common;
|
|
202
|
+
for (const line of lines) {
|
|
203
|
+
const content = line.replace(/\r?\n$/, '');
|
|
204
|
+
if (!/\S/.test(content))
|
|
205
|
+
continue;
|
|
206
|
+
const indent = content.match(/^[ \t]*/)[0];
|
|
207
|
+
if (common === undefined) {
|
|
208
|
+
common = indent;
|
|
209
|
+
continue;
|
|
200
210
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
211
|
+
let n = 0;
|
|
212
|
+
while (n < common.length && n < indent.length && common[n] === indent[n]) {
|
|
213
|
+
n++;
|
|
204
214
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
215
|
+
common = common.slice(0, n);
|
|
216
|
+
if (common === '')
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
const prefix = common !== null && common !== void 0 ? common : '';
|
|
220
|
+
if (prefix === '')
|
|
221
|
+
return { text: lines.join(''), indentStripped: 0 };
|
|
222
|
+
return {
|
|
223
|
+
text: lines
|
|
224
|
+
.map(line => (line.startsWith(prefix) ? line.slice(prefix.length) : line))
|
|
225
|
+
.join(''),
|
|
226
|
+
indentStripped: prefix.length,
|
|
227
|
+
};
|
|
208
228
|
}
|
|
209
229
|
function stripTrailingNewline(s) {
|
|
210
|
-
|
|
230
|
+
// A trailing line ending may be CRLF or LF — strip either.
|
|
231
|
+
return s.replace(/\r?\n$/, '');
|
|
211
232
|
}
|
|
212
|
-
|
|
233
|
+
/**
|
|
234
|
+
* Annotation note text is normalized to LF line endings, so a block's stored
|
|
235
|
+
* text and content are identical regardless of the source's CRLF/LF style.
|
|
236
|
+
* The lexer keeps the source `\r` in token text (it sits at line ends, after a
|
|
237
|
+
* line's content); this is where it is dropped.
|
|
238
|
+
*/
|
|
239
|
+
function normalizeEol(s) {
|
|
240
|
+
return s.replace(/\r\n/g, '\n');
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Read the text and dedent amount of an annotation from its parse tree.
|
|
244
|
+
* Internal — public callers want `noteFromAnnotation` or `getAnnotationText`.
|
|
245
|
+
*/
|
|
246
|
+
function readAnnotation(cx) {
|
|
213
247
|
if (cx instanceof MalloyParser_1.AnnotationContext) {
|
|
214
248
|
const annot = cx.ANNOTATION();
|
|
215
249
|
if (annot)
|
|
216
|
-
return annot.text;
|
|
250
|
+
return { text: normalizeEol(annot.text), indentStripped: 0 };
|
|
217
251
|
const block = cx.blockAnnotation();
|
|
218
252
|
const beginToken = block.BLOCK_ANNOTATION_BEGIN();
|
|
219
253
|
const textLines = block.BLOCK_ANNOTATION_TEXT().map(t => t.text);
|
|
220
|
-
|
|
221
|
-
|
|
254
|
+
const dedented = dedentBlockLines(textLines);
|
|
255
|
+
return {
|
|
256
|
+
text: normalizeEol(stripTrailingNewline(beginToken.text + dedented.text)),
|
|
257
|
+
indentStripped: dedented.indentStripped,
|
|
258
|
+
};
|
|
222
259
|
}
|
|
223
260
|
const doc = cx.DOC_ANNOTATION();
|
|
224
261
|
if (doc)
|
|
225
|
-
return doc.text;
|
|
262
|
+
return { text: normalizeEol(doc.text), indentStripped: 0 };
|
|
226
263
|
const block = cx.docBlockAnnotation();
|
|
227
264
|
const beginToken = block.DOC_BLOCK_ANNOTATION_BEGIN();
|
|
228
265
|
const textLines = block.BLOCK_ANNOTATION_TEXT().map(t => t.text);
|
|
229
|
-
|
|
230
|
-
|
|
266
|
+
const dedented = dedentBlockLines(textLines);
|
|
267
|
+
return {
|
|
268
|
+
text: normalizeEol(stripTrailingNewline(beginToken.text + dedented.text)),
|
|
269
|
+
indentStripped: dedented.indentStripped,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Build the IR `Note` for an annotation: reads the text, dedents the body if
|
|
274
|
+
* it's a block, and computes the source `at` from the parse context. The
|
|
275
|
+
* single entry point for going from a parse-tree annotation to an IR note.
|
|
276
|
+
*/
|
|
277
|
+
function noteFromAnnotation(cx, parseInfo) {
|
|
278
|
+
const { text, indentStripped } = readAnnotation(cx);
|
|
279
|
+
const at = {
|
|
280
|
+
url: parseInfo.sourceURL,
|
|
281
|
+
range: (0, utils_1.rangeFromContext)(parseInfo.sourceInfo, cx),
|
|
282
|
+
};
|
|
283
|
+
const note = { text, at };
|
|
284
|
+
if (indentStripped > 0)
|
|
285
|
+
note.indentStripped = indentStripped;
|
|
286
|
+
return note;
|
|
287
|
+
}
|
|
288
|
+
/** Text-only reader, for callers that don't need an IR `Note`. */
|
|
289
|
+
function getAnnotationText(cx) {
|
|
290
|
+
return readAnnotation(cx).text;
|
|
231
291
|
}
|
|
232
292
|
//# sourceMappingURL=parse-utils.js.map
|
|
@@ -1138,6 +1138,13 @@ export interface Annotation {
|
|
|
1138
1138
|
export interface Note {
|
|
1139
1139
|
text: string;
|
|
1140
1140
|
at: DocumentLocation;
|
|
1141
|
+
/**
|
|
1142
|
+
* For block annotations: characters of leading whitespace removed from each
|
|
1143
|
+
* body line by the dedent pass. Used to map payload-parser error columns
|
|
1144
|
+
* back to source (`source_col = indentStripped + parser_col` for body lines).
|
|
1145
|
+
* Omitted for single-line notes and blocks with no common indent.
|
|
1146
|
+
*/
|
|
1147
|
+
indentStripped?: number;
|
|
1141
1148
|
}
|
|
1142
1149
|
/** Annotations with a uuid to make it easier to stream */
|
|
1143
1150
|
export interface ModelAnnotation extends Annotation {
|
|
@@ -24,7 +24,7 @@ function resolveSource(modelDef, name) {
|
|
|
24
24
|
function checkPersistAnnotation(source) {
|
|
25
25
|
if (!source.annotation)
|
|
26
26
|
return { persist: false, log: [] };
|
|
27
|
-
const { tag, log } =
|
|
27
|
+
const { tag, log } = new annotation_1.Annotations(source.annotation).parseAsTag('@');
|
|
28
28
|
return { persist: tag.has('persist'), log };
|
|
29
29
|
}
|
|
30
30
|
/**
|
package/dist/model/query_node.js
CHANGED
|
@@ -206,7 +206,7 @@ class QueryStruct {
|
|
|
206
206
|
modelCompilerFlags() {
|
|
207
207
|
if (this._modelTag === undefined) {
|
|
208
208
|
const annotation = this.structDef.modelAnnotation;
|
|
209
|
-
const { tag } =
|
|
209
|
+
const { tag } = new annotation_1.Annotations(annotation).parseAsTag('!');
|
|
210
210
|
this._modelTag = tag;
|
|
211
211
|
}
|
|
212
212
|
return this._modelTag;
|
package/dist/prefix.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The result of parsing an annotation's leading prefix.
|
|
3
|
+
*
|
|
4
|
+
* An annotation is `prefix sep content`. `parsePrefix` splits the captured
|
|
5
|
+
* annotation text at the first whitespace, strips the sigil (`#`/`##` plus an
|
|
6
|
+
* optional block `|`), and classifies the routing into one of three forms.
|
|
7
|
+
* `route` is one field; `contentIndex` and `malformation` are equally why this
|
|
8
|
+
* routine exists.
|
|
9
|
+
*/
|
|
10
|
+
export interface ParsedPrefix {
|
|
11
|
+
/** The route the prefix resolves to. `''` is the MOTLY (empty) route. */
|
|
12
|
+
route: string;
|
|
13
|
+
/**
|
|
14
|
+
* Offset into the annotation text where the content begins;
|
|
15
|
+
* `text.slice(contentIndex)` is the content, with the single separator
|
|
16
|
+
* character excluded.
|
|
17
|
+
*/
|
|
18
|
+
contentIndex: number;
|
|
19
|
+
/**
|
|
20
|
+
* Set iff the prefix is not a well-formed route. The annotation is still
|
|
21
|
+
* stored and reachable via the all-routes API; this only drives a warning.
|
|
22
|
+
* - `malformed-route`: not one of the three forms — a bare word, an unclosed
|
|
23
|
+
* or mismatched bracket, an empty bracketed name, or trailing junk.
|
|
24
|
+
* - `reserved-route`: a punct-only (sigil) routing that Malloy has not
|
|
25
|
+
* claimed; the punct-only namespace is reserved for Malloy's own use.
|
|
26
|
+
*/
|
|
27
|
+
malformation?: 'malformed-route' | 'reserved-route';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse the leading prefix of a captured annotation.
|
|
31
|
+
*
|
|
32
|
+
* `text` is the entire annotation as the lexer captured it, marker included: a
|
|
33
|
+
* single line `#... content`, or a block `#|...\n<body>` whose body the lexer
|
|
34
|
+
* has already de-indented and whose closer it has removed. This routine never
|
|
35
|
+
* parses the content — it returns where the content begins.
|
|
36
|
+
*
|
|
37
|
+
* The prefix runs from the marker to the **first whitespace**; that boundary
|
|
38
|
+
* never moves (no bracket or quote changes it, so a route can never contain
|
|
39
|
+
* whitespace). After stripping the sigil (`^##?\|?`), the routing matches one
|
|
40
|
+
* of three forms:
|
|
41
|
+
*
|
|
42
|
+
* 1. empty -> route `''` (MOTLY, the human default)
|
|
43
|
+
* 2. PUNCT+ (no bracket) -> sigil route (reserved; unclaimed warns)
|
|
44
|
+
* 3. OPEN ... CLOSE -> route = bracketed text (opaque app route)
|
|
45
|
+
*
|
|
46
|
+
* Anything else is `malformed-route`. Bracket pairs are `()`, `<>`, `[]`, `{}`;
|
|
47
|
+
* the route is everything up to the matching close (first close wins, no
|
|
48
|
+
* nesting), taken literally — no character classification, so `#(bar-chart)`,
|
|
49
|
+
* `#(https://x/y)`, and `#(v1.2.3)` are all just their bracketed text.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parsePrefix(text: string): ParsedPrefix;
|
package/dist/prefix.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.parsePrefix = parsePrefix;
|
|
8
|
+
/**
|
|
9
|
+
* Punct-only (sigil) routes are reserved for Malloy's own use. This is the
|
|
10
|
+
* closed, enumerated set the compiler claims; any other punct-only routing
|
|
11
|
+
* warns `reserved-route`. The set being closed is what makes the warning
|
|
12
|
+
* possible — the compiler knows its own complete sigil vocabulary.
|
|
13
|
+
*/
|
|
14
|
+
const CLAIMED_SIGILS = new Set(['!', '@', '"', ':']);
|
|
15
|
+
/** Bracket pairs an app route may use, as [open, close] — the single source. */
|
|
16
|
+
const BRACKET_PAIRS = [
|
|
17
|
+
['(', ')'],
|
|
18
|
+
['<', '>'],
|
|
19
|
+
['[', ']'],
|
|
20
|
+
['{', '}'],
|
|
21
|
+
];
|
|
22
|
+
/** Two views of BRACKET_PAIRS: open->close for lookup, all halves for membership. */
|
|
23
|
+
const OPEN_TO_CLOSE = new Map(BRACKET_PAIRS);
|
|
24
|
+
const BRACKETS = new Set(BRACKET_PAIRS.flat());
|
|
25
|
+
/** Letter, number, or underscore — the "word" characters. */
|
|
26
|
+
const WORDISH = /[\p{L}\p{N}_]/u;
|
|
27
|
+
/**
|
|
28
|
+
* Parse the leading prefix of a captured annotation.
|
|
29
|
+
*
|
|
30
|
+
* `text` is the entire annotation as the lexer captured it, marker included: a
|
|
31
|
+
* single line `#... content`, or a block `#|...\n<body>` whose body the lexer
|
|
32
|
+
* has already de-indented and whose closer it has removed. This routine never
|
|
33
|
+
* parses the content — it returns where the content begins.
|
|
34
|
+
*
|
|
35
|
+
* The prefix runs from the marker to the **first whitespace**; that boundary
|
|
36
|
+
* never moves (no bracket or quote changes it, so a route can never contain
|
|
37
|
+
* whitespace). After stripping the sigil (`^##?\|?`), the routing matches one
|
|
38
|
+
* of three forms:
|
|
39
|
+
*
|
|
40
|
+
* 1. empty -> route `''` (MOTLY, the human default)
|
|
41
|
+
* 2. PUNCT+ (no bracket) -> sigil route (reserved; unclaimed warns)
|
|
42
|
+
* 3. OPEN ... CLOSE -> route = bracketed text (opaque app route)
|
|
43
|
+
*
|
|
44
|
+
* Anything else is `malformed-route`. Bracket pairs are `()`, `<>`, `[]`, `{}`;
|
|
45
|
+
* the route is everything up to the matching close (first close wins, no
|
|
46
|
+
* nesting), taken literally — no character classification, so `#(bar-chart)`,
|
|
47
|
+
* `#(https://x/y)`, and `#(v1.2.3)` are all just their bracketed text.
|
|
48
|
+
*/
|
|
49
|
+
function parsePrefix(text) {
|
|
50
|
+
// 1. Split prefix from content at the first whitespace. The single separator
|
|
51
|
+
// character is excluded from the content.
|
|
52
|
+
// `\r` is in the class because Windows line endings put a `\r` right before
|
|
53
|
+
// the `\n` at every line end (the lexer keeps `\r` out of line *content*),
|
|
54
|
+
// so on a content-less prefix like `#(docs)\r\n` the `\r` must not be pulled
|
|
55
|
+
// into the routing. We split on it rather than mutating the text, so source
|
|
56
|
+
// offsets stay intact for error mapping; any line-ending bytes remain in the
|
|
57
|
+
// content, which is faithful to source and harmless to payload parsers.
|
|
58
|
+
const boundary = text.search(/[ \t\r\n]/);
|
|
59
|
+
const prefix = boundary === -1 ? text : text.slice(0, boundary);
|
|
60
|
+
const contentIndex = boundary === -1 ? text.length : boundary + 1;
|
|
61
|
+
// 2. Strip the sigil: one or two '#', then an optional block '|'.
|
|
62
|
+
const sigil = /^(#{1,2})(\|?)/.exec(prefix);
|
|
63
|
+
if (!sigil) {
|
|
64
|
+
// A captured annotation always starts with '#'; if it somehow does not,
|
|
65
|
+
// there is no route to speak of — likely a compiler bug upstream.
|
|
66
|
+
return { route: prefix, contentIndex, malformation: 'malformed-route' };
|
|
67
|
+
}
|
|
68
|
+
const routing = prefix.slice(sigil[0].length);
|
|
69
|
+
// Form 1: empty routing -> the MOTLY namespace.
|
|
70
|
+
if (routing === '') {
|
|
71
|
+
return { route: '', contentIndex };
|
|
72
|
+
}
|
|
73
|
+
// Form 3: opens with a bracket -> route is the text up to the matching close.
|
|
74
|
+
// A non-undefined close both proves routing[0] is an open bracket and gives
|
|
75
|
+
// us its partner in one lookup.
|
|
76
|
+
const close = OPEN_TO_CLOSE.get(routing[0]);
|
|
77
|
+
if (close !== undefined) {
|
|
78
|
+
const closeIdx = routing.indexOf(close, 1);
|
|
79
|
+
const malformed = closeIdx === -1 || // unclosed
|
|
80
|
+
closeIdx !== routing.length - 1 || // trailing junk after the close
|
|
81
|
+
closeIdx === 1; // empty bracketed text: `#()`
|
|
82
|
+
if (malformed) {
|
|
83
|
+
return { route: routing, contentIndex, malformation: 'malformed-route' };
|
|
84
|
+
}
|
|
85
|
+
return { route: routing.slice(1, closeIdx), contentIndex };
|
|
86
|
+
}
|
|
87
|
+
// Form 2: pure punctuation (no word chars, no brackets) -> sigil route.
|
|
88
|
+
const isPurePunct = [...routing].every(c => !WORDISH.test(c) && !BRACKETS.has(c));
|
|
89
|
+
if (isPurePunct) {
|
|
90
|
+
return {
|
|
91
|
+
route: routing,
|
|
92
|
+
contentIndex,
|
|
93
|
+
malformation: CLAIMED_SIGILS.has(routing) ? undefined : 'reserved-route',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Otherwise: a bare word, or mixed text with no brackets.
|
|
97
|
+
return { route: routing, contentIndex, malformation: 'malformed-route' };
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=prefix.js.map
|
package/dist/taggable.d.ts
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
|
-
import type { TagParseSpec, MalloyTagParse } from './annotation';
|
|
1
|
+
import type { TagParseSpec, MalloyTagParse, Annotations } from './annotation';
|
|
2
2
|
/**
|
|
3
3
|
* Interface for objects that have Malloy tag annotations.
|
|
4
4
|
* This is part of the runtime API - objects returned from the Malloy
|
|
5
5
|
* runtime implement this interface to expose their tag metadata.
|
|
6
6
|
*/
|
|
7
7
|
export interface Taggable {
|
|
8
|
+
/**
|
|
9
|
+
* Route-aware annotation access — read annotations by *route*. See
|
|
10
|
+
* `Annotations` for what a route is and which ones are claimed. Unlike
|
|
11
|
+
* `tagParse`/`getTaglines`, this sees block annotations (`#|`…`|#`).
|
|
12
|
+
*/
|
|
13
|
+
readonly annotations: Annotations;
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated The RegExp form cannot see block annotations (`#|`…`|#`) and
|
|
16
|
+
* cannot report content offsets for error mapping. Use
|
|
17
|
+
* `annotations.parseAsTag(route)` instead.
|
|
18
|
+
*/
|
|
8
19
|
tagParse: (spec?: TagParseSpec) => MalloyTagParse;
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated The RegExp form cannot see block annotations. Use
|
|
22
|
+
* `annotations.texts(route)` (raw strings) or `annotations.forRoute(route)`
|
|
23
|
+
* (objects with offsets) instead.
|
|
24
|
+
*/
|
|
9
25
|
getTaglines: (prefix?: RegExp) => string[];
|
|
10
26
|
}
|
|
@@ -70,7 +70,8 @@ async function runQueryInternal(tm, src) {
|
|
|
70
70
|
let queryTestTag = undefined;
|
|
71
71
|
try {
|
|
72
72
|
query = tm.model.loadQuery(src);
|
|
73
|
-
const queryTags = (await query.getPreparedQuery()).
|
|
73
|
+
const queryTags = (await query.getPreparedQuery()).annotations.parseAsTag()
|
|
74
|
+
.tag;
|
|
74
75
|
queryTestTag = queryTags.tag('test');
|
|
75
76
|
}
|
|
76
77
|
catch (e) {
|
package/dist/to_stable.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import * as Malloy from '@malloydata/malloy-interfaces';
|
|
2
|
-
import type { FieldDef, ModelDef, ResultStructMetadataDef, SourceDef } from './model';
|
|
2
|
+
import type { Annotation, FieldDef, ModelDef, ResultStructMetadataDef, SourceDef } from './model';
|
|
3
3
|
import { Tag } from '@malloydata/malloy-tag';
|
|
4
4
|
export declare function sourceDefToSourceInfo(sourceDef: SourceDef): Malloy.SourceInfo;
|
|
5
5
|
export declare function modelDefToModelInfo(modelDef: ModelDef): Malloy.ModelInfo;
|
|
6
|
+
/**
|
|
7
|
+
* IR annotation → stable `Malloy.Annotation[]` shape used across `to_stable`
|
|
8
|
+
* and the api surfaces. The stable shape carries the raw annotation strings;
|
|
9
|
+
* routes are derivable at the consumer via `parsePrefix` (`./prefix.ts`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function toStableAnnotations(annot: Annotation | undefined): Malloy.Annotation[];
|
|
6
12
|
export declare function convertFieldInfos(source: SourceDef, fields: FieldDef[]): Malloy.FieldInfo[];
|
|
7
13
|
export declare function writeLiteralToTag(tag: Tag, path: (string | number)[], literal: Malloy.LiteralValue): void;
|
|
8
14
|
export declare function getResultStructMetadataAnnotation(field: SourceDef, resultMetadata: ResultStructMetadataDef): Malloy.Annotation | undefined;
|
package/dist/to_stable.js
CHANGED
|
@@ -41,6 +41,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
42
|
exports.sourceDefToSourceInfo = sourceDefToSourceInfo;
|
|
43
43
|
exports.modelDefToModelInfo = modelDefToModelInfo;
|
|
44
|
+
exports.toStableAnnotations = toStableAnnotations;
|
|
44
45
|
exports.convertFieldInfos = convertFieldInfos;
|
|
45
46
|
exports.writeLiteralToTag = writeLiteralToTag;
|
|
46
47
|
exports.getResultStructMetadataAnnotation = getResultStructMetadataAnnotation;
|
|
@@ -77,7 +78,7 @@ function sourceDefToSourceInfo(sourceDef) {
|
|
|
77
78
|
fields: convertFieldInfos(sourceDef, sourceDef.fields),
|
|
78
79
|
},
|
|
79
80
|
parameters,
|
|
80
|
-
annotations:
|
|
81
|
+
annotations: toStableAnnotations(sourceDef.annotation),
|
|
81
82
|
};
|
|
82
83
|
return sourceInfo;
|
|
83
84
|
}
|
|
@@ -98,7 +99,7 @@ function modelDefToModelInfo(modelDef) {
|
|
|
98
99
|
}
|
|
99
100
|
else if (entry.type === 'query') {
|
|
100
101
|
const outputStruct = (0, model_1.getResultStructDefForQuery)(modelDef, entry);
|
|
101
|
-
const annotations =
|
|
102
|
+
const annotations = toStableAnnotations(entry.annotation);
|
|
102
103
|
const resultMetadataAnnotation = outputStruct.resultMetadata
|
|
103
104
|
? getResultStructMetadataAnnotation(outputStruct, outputStruct.resultMetadata)
|
|
104
105
|
: undefined;
|
|
@@ -119,7 +120,7 @@ function modelDefToModelInfo(modelDef) {
|
|
|
119
120
|
}
|
|
120
121
|
for (const query of modelDef.queryList) {
|
|
121
122
|
const outputStruct = (0, model_1.getResultStructDefForQuery)(modelDef, query);
|
|
122
|
-
const annotations =
|
|
123
|
+
const annotations = toStableAnnotations(query.annotation);
|
|
123
124
|
const resultMetadataAnnotation = outputStruct.resultMetadata
|
|
124
125
|
? getResultStructMetadataAnnotation(outputStruct, outputStruct.resultMetadata)
|
|
125
126
|
: undefined;
|
|
@@ -173,11 +174,13 @@ function convertParameterDefaultValue(value) {
|
|
|
173
174
|
throw new Error('Invalid parameter default value');
|
|
174
175
|
}
|
|
175
176
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
/**
|
|
178
|
+
* IR annotation → stable `Malloy.Annotation[]` shape used across `to_stable`
|
|
179
|
+
* and the api surfaces. The stable shape carries the raw annotation strings;
|
|
180
|
+
* routes are derivable at the consumer via `parsePrefix` (`./prefix.ts`).
|
|
181
|
+
*/
|
|
182
|
+
function toStableAnnotations(annot) {
|
|
183
|
+
return new annotation_1.Annotations(annot).texts().map(value => ({ value }));
|
|
181
184
|
}
|
|
182
185
|
function convertFieldInfos(source, fields) {
|
|
183
186
|
var _a, _b, _c;
|
|
@@ -186,10 +189,7 @@ function convertFieldInfos(source, fields) {
|
|
|
186
189
|
const isPublic = field.accessModifier === undefined;
|
|
187
190
|
if (!isPublic)
|
|
188
191
|
continue;
|
|
189
|
-
const
|
|
190
|
-
const rawAnnotations = taglines.map(tagline => ({
|
|
191
|
-
value: tagline,
|
|
192
|
-
}));
|
|
192
|
+
const rawAnnotations = toStableAnnotations(field.annotation);
|
|
193
193
|
const annotations = rawAnnotations.length > 0 ? rawAnnotations : undefined;
|
|
194
194
|
if ((0, model_1.isTurtle)(field)) {
|
|
195
195
|
const outputStruct = (0, model_1.getResultStructDefForView)(source, field);
|
|
@@ -452,10 +452,7 @@ function convertRecordType(field) {
|
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
if (f.annotation) {
|
|
455
|
-
|
|
456
|
-
annotations.push(...taglines.map(tagline => ({
|
|
457
|
-
value: tagline,
|
|
458
|
-
})));
|
|
455
|
+
annotations.push(...toStableAnnotations(f.annotation));
|
|
459
456
|
}
|
|
460
457
|
if ((0, model_1.isAtomic)(f)) {
|
|
461
458
|
return {
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MALLOY_VERSION = "0.0.
|
|
1
|
+
export declare const MALLOY_VERSION = "0.0.398";
|
package/dist/version.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MALLOY_VERSION = void 0;
|
|
4
4
|
// generated with 'generate-version-file' script; do not edit manually
|
|
5
|
-
exports.MALLOY_VERSION = '0.0.
|
|
5
|
+
exports.MALLOY_VERSION = '0.0.398';
|
|
6
6
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/malloy",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.398",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@malloydata/malloy-filter": "0.0.
|
|
55
|
-
"@malloydata/malloy-interfaces": "0.0.
|
|
56
|
-
"@malloydata/malloy-tag": "0.0.
|
|
54
|
+
"@malloydata/malloy-filter": "0.0.398",
|
|
55
|
+
"@malloydata/malloy-interfaces": "0.0.398",
|
|
56
|
+
"@malloydata/malloy-tag": "0.0.398",
|
|
57
57
|
"@noble/hashes": "^1.8.0",
|
|
58
58
|
"antlr4ts": "^0.5.0-alpha.4",
|
|
59
59
|
"assert": "^2.0.0",
|