@malloydata/malloy 0.0.395 → 0.0.397
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/compile.d.ts +7 -6
- package/dist/api/foundation/compile.js +22 -6
- package/dist/api/foundation/core.d.ts +10 -0
- package/dist/api/foundation/core.js +32 -9
- package/dist/api/foundation/runtime.d.ts +85 -5
- package/dist/api/foundation/runtime.js +184 -14
- package/dist/api/foundation/types.d.ts +2 -0
- package/dist/lang/ast/expressions/expr-func.js +30 -11
- package/dist/lang/ast/expressions/expr-given.js +1 -0
- package/dist/lang/ast/field-space/reference-field.js +1 -1
- package/dist/lang/ast/source-elements/sql-source.js +4 -0
- package/dist/lang/ast/source-elements/table-source.js +4 -0
- package/dist/lang/ast/statements/define-given.d.ts +1 -0
- package/dist/lang/ast/statements/define-given.js +7 -0
- package/dist/lang/ast/statements/import-statement.js +4 -0
- package/dist/lang/ast/types/annotation-elements.d.ts +1 -0
- package/dist/lang/ast/types/annotation-elements.js +10 -3
- package/dist/lang/ast/types/malloy-element.d.ts +1 -0
- package/dist/lang/ast/types/malloy-element.js +4 -0
- package/dist/lang/composite-source-utils.js +1 -1
- package/dist/lang/malloy-to-ast.d.ts +9 -1
- package/dist/lang/malloy-to-ast.js +37 -11
- package/dist/lang/malloy-to-stable-query.js +3 -3
- package/dist/lang/parse-log.d.ts +7 -1
- package/dist/lang/parse-log.js +12 -0
- package/dist/lang/parse-malloy.d.ts +4 -1
- package/dist/lang/parse-malloy.js +26 -4
- 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/lang/test/test-translator.d.ts +19 -5
- package/dist/lang/test/test-translator.js +15 -12
- package/dist/lang/zone.d.ts +2 -0
- package/dist/lang/zone.js +10 -0
- package/dist/model/constant_expression_compiler.js +14 -5
- package/dist/model/expression_compiler.js +19 -17
- package/dist/model/field_instance.js +7 -3
- package/dist/model/given_binding.js +26 -21
- package/dist/model/index.d.ts +1 -0
- package/dist/model/index.js +3 -1
- package/dist/model/malloy_compile_error.d.ts +13 -0
- package/dist/model/malloy_compile_error.js +23 -0
- package/dist/model/malloy_types.d.ts +9 -0
- package/dist/model/persist_utils.js +1 -1
- package/dist/model/query_model_impl.js +2 -1
- package/dist/model/query_node.d.ts +5 -5
- package/dist/model/query_node.js +22 -17
- package/dist/model/query_query.js +23 -11
- package/dist/model/sql_compiled.js +6 -3
- 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
|
@@ -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");
|
|
@@ -92,10 +93,11 @@ const LEGAL_FILTER_TYPES = 'string, number, boolean, date, timestamp, timestampt
|
|
|
92
93
|
* AST from an ANTLR parse tree.
|
|
93
94
|
*/
|
|
94
95
|
class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
95
|
-
constructor(parseInfo, msgLog, compilerFlagSrc) {
|
|
96
|
+
constructor(parseInfo, msgLog, compilerFlagSrc, restrictedMode = false) {
|
|
96
97
|
super();
|
|
97
98
|
this.parseInfo = parseInfo;
|
|
98
99
|
this.msgLog = msgLog;
|
|
100
|
+
this.restrictedMode = restrictedMode;
|
|
99
101
|
this.timer = new timing_1.Timer('generate_ast');
|
|
100
102
|
const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
|
|
101
103
|
this.compilerFlagSrc = [...DEFAULT_COMPILER_FLAGS, ...compilerFlagSrc];
|
|
@@ -270,12 +272,26 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
270
272
|
return this.astAt(def, pcx);
|
|
271
273
|
}
|
|
272
274
|
getAnnotation(cx) {
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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 });
|
|
279
295
|
}
|
|
280
296
|
getNotes(cx) {
|
|
281
297
|
return cx.annotation().map(a => this.getAnnotation(a));
|
|
@@ -1498,6 +1514,15 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
1498
1514
|
}
|
|
1499
1515
|
updateCompilerFlags(tags) {
|
|
1500
1516
|
const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
|
|
1517
|
+
if (this.restrictedMode) {
|
|
1518
|
+
// `##!` lines in restricted text never become active flags. The
|
|
1519
|
+
// user-facing rejection is logged at execute time (see
|
|
1520
|
+
// ModelAnnotation.execute) so it doesn't short-circuit ASTStep,
|
|
1521
|
+
// which lets every other restricted-mode violation in the same
|
|
1522
|
+
// compile log its own diagnostic.
|
|
1523
|
+
this.timer.contribute([parseCompilerFlagsTimer.stop()]);
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1501
1526
|
const newLines = tags.getCompilerFlagLines();
|
|
1502
1527
|
if (newLines.length > 0) {
|
|
1503
1528
|
const oldLength = this.compilerFlagSrc.length;
|
|
@@ -1523,10 +1548,11 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
1523
1548
|
this.contextError(pcx, 'unclosed-block-annotation', 'Block annotation is not closed, add correctly indented "|##"');
|
|
1524
1549
|
}
|
|
1525
1550
|
}
|
|
1526
|
-
const allNotes = pcx.docAnnotation().map(a =>
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
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
|
+
});
|
|
1530
1556
|
const tags = new ast.ModelAnnotation(allNotes);
|
|
1531
1557
|
this.updateCompilerFlags(tags);
|
|
1532
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;
|
|
@@ -430,6 +435,7 @@ type MessageParameterTypes = {
|
|
|
430
435
|
'missing-required-group-by': string;
|
|
431
436
|
'invalid-partition-composite': string;
|
|
432
437
|
'integer-literal-out-of-range': string;
|
|
438
|
+
'restricted-construct-forbidden': string;
|
|
433
439
|
};
|
|
434
440
|
export declare const MESSAGE_FORMATTERS: PartialErrorCodeMessageMap;
|
|
435
441
|
export type MessageCode = keyof MessageParameterTypes;
|
package/dist/lang/parse-log.js
CHANGED
|
@@ -101,6 +101,18 @@ exports.MESSAGE_FORMATTERS = {
|
|
|
101
101
|
'inline-no-default': e => `inline given \`${e.name}\` must have a value — there is nothing to inline without one`,
|
|
102
102
|
'inline-bad-operator': e => `inline given \`${e.name}\` uses operator(s) not allowed in inline expressions: ${e.operators}`,
|
|
103
103
|
'invalid-given-modifier': e => `Unknown modifier \`${e.modifier}\` on \`given:\` declaration; the only modifier allowed here is \`inline\``,
|
|
104
|
+
'restricted-construct-forbidden': e => ({
|
|
105
|
+
message: e,
|
|
106
|
+
tag: 'restricted-mode',
|
|
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
|
+
}),
|
|
104
116
|
};
|
|
105
117
|
function makeLogMessage(code, parameters, options) {
|
|
106
118
|
var _a, _b, _c, _d, _e;
|
|
@@ -193,6 +193,7 @@ export declare class MalloyChildTranslator extends MalloyTranslation {
|
|
|
193
193
|
*/
|
|
194
194
|
export declare class MalloyTranslator extends MalloyTranslation {
|
|
195
195
|
private readonly eventStream;
|
|
196
|
+
readonly restrictedMode: boolean;
|
|
196
197
|
schemaZone: Zone<SourceDef>;
|
|
197
198
|
importZone: Zone<string>;
|
|
198
199
|
pretranslatedModels: Map<string, ModelDef>;
|
|
@@ -200,8 +201,10 @@ export declare class MalloyTranslator extends MalloyTranslation {
|
|
|
200
201
|
connectionDialectZone: Zone<string>;
|
|
201
202
|
logger: BaseMessageLogger;
|
|
202
203
|
readonly root: MalloyTranslator;
|
|
203
|
-
constructor(rootURL: string, importURL?: string | null, preload?: ParseUpdate | null, eventStream?: EventStream | null);
|
|
204
|
+
constructor(rootURL: string, importURL?: string | null, preload?: ParseUpdate | null, eventStream?: EventStream | null, restrictedMode?: boolean);
|
|
204
205
|
update(dd: ParseUpdate): void;
|
|
206
|
+
private lockZonesIfRestricted;
|
|
207
|
+
translate(extendingModel?: ModelDef): TranslateResponse;
|
|
205
208
|
logError<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: Omit<LogMessageOptions, 'severity'>): T;
|
|
206
209
|
}
|
|
207
210
|
interface ErrorData {
|
|
@@ -139,6 +139,12 @@ class ImportsAndTablesStep {
|
|
|
139
139
|
if (parseReq.parse === undefined) {
|
|
140
140
|
return parseReq;
|
|
141
141
|
}
|
|
142
|
+
// Every reference this step would register — connection dialects,
|
|
143
|
+
// imports, table schemas — is for a construct that's forbidden in
|
|
144
|
+
// restricted code.
|
|
145
|
+
if (that.root.restrictedMode) {
|
|
146
|
+
return { timingInfo: parseReq.timingInfo };
|
|
147
|
+
}
|
|
142
148
|
if (!this.parseReferences) {
|
|
143
149
|
this.parseReferences = (0, find_external_references_1.findReferences)(that, parseReq.parse.tokenStream, parseReq.parse.root);
|
|
144
150
|
// Register connection dialects and imports immediately. Table
|
|
@@ -278,7 +284,7 @@ class ASTStep {
|
|
|
278
284
|
throw new Error('TRANSLATOR INTERNAL ERROR: Translator parse response had no errors, but also no parser');
|
|
279
285
|
}
|
|
280
286
|
stepTimer.incorporate(parseResponse.timingInfo);
|
|
281
|
-
const secondPass = new malloy_to_ast_1.MalloyToAST(parse, that.root.logger, that.compilerFlagSrc);
|
|
287
|
+
const secondPass = new malloy_to_ast_1.MalloyToAST(parse, that.root.logger, that.compilerFlagSrc, that.root.restrictedMode);
|
|
282
288
|
const { ast: newAST, compilerFlagSrc, timingInfo } = secondPass.run();
|
|
283
289
|
stepTimer.contribute([timingInfo]);
|
|
284
290
|
that.compilerFlagSrc = compilerFlagSrc;
|
|
@@ -466,10 +472,13 @@ class TranslateStep {
|
|
|
466
472
|
modelWasModified: false,
|
|
467
473
|
};
|
|
468
474
|
}
|
|
469
|
-
//
|
|
475
|
+
// Layer the extending model's compiler flags on top of whatever was
|
|
476
|
+
// there already. In production the array starts empty so push vs.
|
|
477
|
+
// overwrite produce the same result; the push lets constructor-time
|
|
478
|
+
// seeding (e.g. TestTranslator's compilerFlags option) survive.
|
|
470
479
|
if (extendingModel && !this.importedAnnotations) {
|
|
471
480
|
const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
|
|
472
|
-
that.compilerFlagSrc
|
|
481
|
+
that.compilerFlagSrc.push(...new annotation_1.Annotations(extendingModel.annotation).texts('!'));
|
|
473
482
|
stepTimer.contribute([parseCompilerFlagsTimer.stop()]);
|
|
474
483
|
this.importedAnnotations = true;
|
|
475
484
|
}
|
|
@@ -821,9 +830,10 @@ exports.MalloyChildTranslator = MalloyChildTranslator;
|
|
|
821
830
|
* no need to call again, the translation is finished or error'd.
|
|
822
831
|
*/
|
|
823
832
|
class MalloyTranslator extends MalloyTranslation {
|
|
824
|
-
constructor(rootURL, importURL = null, preload = null, eventStream = null) {
|
|
833
|
+
constructor(rootURL, importURL = null, preload = null, eventStream = null, restrictedMode = false) {
|
|
825
834
|
super(rootURL, importURL);
|
|
826
835
|
this.eventStream = eventStream;
|
|
836
|
+
this.restrictedMode = restrictedMode;
|
|
827
837
|
this.schemaZone = new zone_1.Zone();
|
|
828
838
|
this.importZone = new zone_1.Zone();
|
|
829
839
|
this.pretranslatedModels = new Map();
|
|
@@ -845,6 +855,18 @@ class MalloyTranslator extends MalloyTranslation {
|
|
|
845
855
|
this.pretranslatedModels.set(url, dd.translations[url]);
|
|
846
856
|
}
|
|
847
857
|
}
|
|
858
|
+
lockZonesIfRestricted() {
|
|
859
|
+
if (!this.restrictedMode)
|
|
860
|
+
return;
|
|
861
|
+
this.schemaZone.lock();
|
|
862
|
+
this.importZone.lock();
|
|
863
|
+
this.sqlQueryZone.lock();
|
|
864
|
+
this.connectionDialectZone.lock();
|
|
865
|
+
}
|
|
866
|
+
translate(extendingModel) {
|
|
867
|
+
this.lockZonesIfRestricted();
|
|
868
|
+
return super.translate(extendingModel);
|
|
869
|
+
}
|
|
848
870
|
logError(code, parameters, options) {
|
|
849
871
|
this.logger.log((0, parse_log_1.makeLogMessage)(code, parameters, { severity: 'error', ...options }));
|
|
850
872
|
return code;
|
|
@@ -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
|
|
@@ -42,12 +42,27 @@ export declare class TestChildTranslator extends MalloyChildTranslator {
|
|
|
42
42
|
translate(): TranslateResponse;
|
|
43
43
|
addChild(url: string): void;
|
|
44
44
|
}
|
|
45
|
+
export interface TestTranslatorOptions {
|
|
46
|
+
rootRule?: string;
|
|
47
|
+
importBaseURL?: string | null;
|
|
48
|
+
eventStream?: EventStream | null;
|
|
49
|
+
internalModel?: ModelDef;
|
|
50
|
+
restrictedMode?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Each entry is a compiler-flag tag fragment — the content that
|
|
53
|
+
* would follow `##! ` in a Malloy source annotation. Rendered to
|
|
54
|
+
* `##! <flag>\n` and pushed onto compilerFlagSrc before any
|
|
55
|
+
* TranslateStep seeding, so the flags are active during AST-build
|
|
56
|
+
* inExperiment() checks.
|
|
57
|
+
*/
|
|
58
|
+
compilerFlags?: string[];
|
|
59
|
+
}
|
|
45
60
|
export declare class TestTranslator extends MalloyTranslator {
|
|
46
61
|
readonly testSrc: string;
|
|
47
62
|
allDialectsEnabled: boolean;
|
|
48
63
|
testRoot?: TestRoot;
|
|
49
64
|
internalModel: ModelDef;
|
|
50
|
-
constructor(testSrc: string,
|
|
65
|
+
constructor(testSrc: string, options?: TestTranslatorOptions);
|
|
51
66
|
translate(): TranslateResponse;
|
|
52
67
|
addChild(url: string): void;
|
|
53
68
|
ast(): MalloyElement | undefined;
|
|
@@ -64,7 +79,7 @@ export declare class TestTranslator extends MalloyTranslator {
|
|
|
64
79
|
export declare class BetaExpression extends TestTranslator {
|
|
65
80
|
readonly sourceName: string;
|
|
66
81
|
private compiled?;
|
|
67
|
-
constructor(src: string, model?: ModelDef, sourceName?: string);
|
|
82
|
+
constructor(src: string, model?: ModelDef, sourceName?: string, options?: Omit<TestTranslatorOptions, 'rootRule' | 'internalModel'>);
|
|
68
83
|
private testFS;
|
|
69
84
|
compile(): void;
|
|
70
85
|
generated(): ExprValue;
|
|
@@ -85,12 +100,11 @@ interface HasTranslator<TT extends TestTranslator> extends MarkedSource {
|
|
|
85
100
|
}
|
|
86
101
|
export declare function expr(unmarked: TemplateStringsArray, ...marked: string[]): HasTranslator<BetaExpression>;
|
|
87
102
|
export declare function model(unmarked: TemplateStringsArray, ...marked: string[]): HasTranslator<TestTranslator>;
|
|
88
|
-
export declare function makeModelFunc(options: {
|
|
89
|
-
model?: ModelDef;
|
|
103
|
+
export declare function makeModelFunc(options: TestTranslatorOptions & {
|
|
90
104
|
prefix?: string;
|
|
91
105
|
wrap?: (code: string) => string;
|
|
92
106
|
}): (unmarked: TemplateStringsArray, ...marked: string[]) => HasTranslator<TestTranslator>;
|
|
93
|
-
export declare function makeExprFunc(model
|
|
107
|
+
export declare function makeExprFunc(model?: ModelDef, sourceName?: string, options?: Omit<TestTranslatorOptions, 'rootRule' | 'internalModel'>): (unmarked: TemplateStringsArray, ...marked: string[]) => HasTranslator<TestTranslator>;
|
|
94
108
|
export declare function markSource(unmarked: TemplateStringsArray, ...marked: string[]): MarkedSource;
|
|
95
109
|
export declare function getSelectOneStruct(sqlBlock: SQLSourceRequest): {
|
|
96
110
|
[key: string]: SQLSourceDef;
|
|
@@ -330,8 +330,9 @@ class TestChildTranslator extends parse_malloy_1.MalloyChildTranslator {
|
|
|
330
330
|
exports.TestChildTranslator = TestChildTranslator;
|
|
331
331
|
const testURI = 'internal://test/langtests/root.malloy';
|
|
332
332
|
class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
333
|
-
constructor(testSrc,
|
|
334
|
-
|
|
333
|
+
constructor(testSrc, options = {}) {
|
|
334
|
+
var _a, _b, _c, _d, _e;
|
|
335
|
+
super(testURI, (_a = options.importBaseURL) !== null && _a !== void 0 ? _a : null, null, (_b = options.eventStream) !== null && _b !== void 0 ? _b : null, (_c = options.restrictedMode) !== null && _c !== void 0 ? _c : false);
|
|
335
336
|
this.testSrc = testSrc;
|
|
336
337
|
this.allDialectsEnabled = true;
|
|
337
338
|
/*
|
|
@@ -415,16 +416,19 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
|
415
416
|
},
|
|
416
417
|
},
|
|
417
418
|
};
|
|
418
|
-
this.grammarRule = rootRule;
|
|
419
|
+
this.grammarRule = (_d = options.rootRule) !== null && _d !== void 0 ? _d : 'malloyDocument';
|
|
419
420
|
this.importZone.define(testURI, testSrc);
|
|
420
|
-
if (internalModel !== undefined) {
|
|
421
|
-
this.internalModel = internalModel;
|
|
421
|
+
if (options.internalModel !== undefined) {
|
|
422
|
+
this.internalModel = options.internalModel;
|
|
422
423
|
}
|
|
423
424
|
for (const actualSchema of exports.mockSchema) {
|
|
424
425
|
this.schemaZone.define(`${actualSchema.connection}:${actualSchema.tablePath}`, actualSchema);
|
|
425
426
|
}
|
|
426
427
|
this.connectionDialectZone.define('_db_', exports.TEST_DIALECT);
|
|
427
428
|
this.connectionDialectZone.define('_bq_', 'standardsql');
|
|
429
|
+
for (const flag of (_e = options.compilerFlags) !== null && _e !== void 0 ? _e : []) {
|
|
430
|
+
this.compilerFlagSrc.push(`##! ${flag}\n`);
|
|
431
|
+
}
|
|
428
432
|
}
|
|
429
433
|
translate() {
|
|
430
434
|
return super.translate(this.internalModel);
|
|
@@ -532,8 +536,8 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
|
532
536
|
exports.TestTranslator = TestTranslator;
|
|
533
537
|
TestTranslator.inspectCompile = false;
|
|
534
538
|
class BetaExpression extends TestTranslator {
|
|
535
|
-
constructor(src, model, sourceName = 'ab') {
|
|
536
|
-
super(src,
|
|
539
|
+
constructor(src, model, sourceName = 'ab', options = {}) {
|
|
540
|
+
super(src, { ...options, rootRule: 'debugExpr', internalModel: model });
|
|
537
541
|
this.sourceName = sourceName;
|
|
538
542
|
}
|
|
539
543
|
testFS() {
|
|
@@ -627,21 +631,20 @@ function model(unmarked, ...marked) {
|
|
|
627
631
|
}
|
|
628
632
|
function makeModelFunc(options) {
|
|
629
633
|
return function model(unmarked, ...marked) {
|
|
630
|
-
var _a;
|
|
631
634
|
const ms = markSource(unmarked, ...marked);
|
|
635
|
+
const { prefix, wrap, ...ttOptions } = options;
|
|
632
636
|
return {
|
|
633
637
|
...ms,
|
|
634
|
-
translator: new TestTranslator((
|
|
635
|
-
(options.wrap ? options.wrap(ms.code) : ms.code), null, null, undefined, options === null || options === void 0 ? void 0 : options.model),
|
|
638
|
+
translator: new TestTranslator((prefix !== null && prefix !== void 0 ? prefix : '') + (wrap ? wrap(ms.code) : ms.code), ttOptions),
|
|
636
639
|
};
|
|
637
640
|
};
|
|
638
641
|
}
|
|
639
|
-
function makeExprFunc(model, sourceName) {
|
|
642
|
+
function makeExprFunc(model, sourceName = 'ab', options = {}) {
|
|
640
643
|
return function expr(unmarked, ...marked) {
|
|
641
644
|
const ms = markSource(unmarked, ...marked);
|
|
642
645
|
return {
|
|
643
646
|
...ms,
|
|
644
|
-
translator: new BetaExpression(ms.code, model, sourceName),
|
|
647
|
+
translator: new BetaExpression(ms.code, model, sourceName, options),
|
|
645
648
|
};
|
|
646
649
|
};
|
|
647
650
|
}
|
package/dist/lang/zone.d.ts
CHANGED
|
@@ -28,7 +28,9 @@ export type ZoneEntry<T> = EntryPresent<T> | ReferenceEntry | EntryErrored;
|
|
|
28
28
|
export declare class Zone<TValue> {
|
|
29
29
|
zone: Map<string, ZoneEntry<TValue>>;
|
|
30
30
|
location: Record<string, DocumentLocation>;
|
|
31
|
+
private locked;
|
|
31
32
|
constructor();
|
|
33
|
+
lock(): void;
|
|
32
34
|
get(str: string): TValue | undefined;
|
|
33
35
|
getEntry(str: string): ZoneEntry<TValue>;
|
|
34
36
|
/**
|
package/dist/lang/zone.js
CHANGED
|
@@ -33,8 +33,12 @@ exports.Zone = void 0;
|
|
|
33
33
|
class Zone {
|
|
34
34
|
constructor() {
|
|
35
35
|
this.location = {};
|
|
36
|
+
this.locked = false;
|
|
36
37
|
this.zone = new Map();
|
|
37
38
|
}
|
|
39
|
+
lock() {
|
|
40
|
+
this.locked = true;
|
|
41
|
+
}
|
|
38
42
|
get(str) {
|
|
39
43
|
const zst = this.zone.get(str);
|
|
40
44
|
if ((zst === null || zst === void 0 ? void 0 : zst.status) === 'present') {
|
|
@@ -57,6 +61,8 @@ class Zone {
|
|
|
57
61
|
* @param val
|
|
58
62
|
*/
|
|
59
63
|
define(str, val) {
|
|
64
|
+
if (this.locked)
|
|
65
|
+
return;
|
|
60
66
|
this.zone.set(str, { status: 'present', value: val });
|
|
61
67
|
}
|
|
62
68
|
/**
|
|
@@ -65,6 +71,8 @@ class Zone {
|
|
|
65
71
|
* @param loc The location of the reference
|
|
66
72
|
*/
|
|
67
73
|
reference(str, loc) {
|
|
74
|
+
if (this.locked)
|
|
75
|
+
return;
|
|
68
76
|
const zst = this.zone.get(str);
|
|
69
77
|
if ((zst === null || zst === void 0 ? void 0 : zst.status) === undefined) {
|
|
70
78
|
this.zone.set(str, { status: 'reference', firstReference: loc });
|
|
@@ -89,6 +97,8 @@ class Zone {
|
|
|
89
97
|
* @param errorData Pass on errors encountered during fetch
|
|
90
98
|
*/
|
|
91
99
|
updateFrom(updateData, errorData) {
|
|
100
|
+
if (this.locked)
|
|
101
|
+
return;
|
|
92
102
|
if (updateData) {
|
|
93
103
|
for (const [updateKey, updateVal] of Object.entries(updateData)) {
|
|
94
104
|
if (updateVal !== undefined) {
|
|
@@ -14,11 +14,20 @@ const field_instance_1 = require("./field_instance");
|
|
|
14
14
|
* Used to distinguish expected errors from unexpected ones.
|
|
15
15
|
*/
|
|
16
16
|
class ConstantExpressionError extends Error {
|
|
17
|
-
constructor(message) {
|
|
17
|
+
constructor(message, at) {
|
|
18
18
|
super(message);
|
|
19
|
+
this.at = at;
|
|
19
20
|
this.name = 'ConstantExpressionError';
|
|
20
21
|
}
|
|
21
22
|
}
|
|
23
|
+
function locSuffix(at) {
|
|
24
|
+
if (!at)
|
|
25
|
+
return '';
|
|
26
|
+
// Display 1-based line/column; range positions are 0-based.
|
|
27
|
+
const line = at.range.start.line + 1;
|
|
28
|
+
const col = at.range.start.character + 1;
|
|
29
|
+
return ` at ${at.url}:${line}:${col}`;
|
|
30
|
+
}
|
|
22
31
|
/**
|
|
23
32
|
* Minimal FieldInstanceResultRoot for constant expressions.
|
|
24
33
|
* This serves as both the result set and its own root, providing
|
|
@@ -106,11 +115,11 @@ class ConstantQueryStruct extends query_node_1.QueryStruct {
|
|
|
106
115
|
/**
|
|
107
116
|
* These methods should not be called for constant expressions
|
|
108
117
|
*/
|
|
109
|
-
getFieldByName(path) {
|
|
110
|
-
throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions
|
|
118
|
+
getFieldByName(path, at) {
|
|
119
|
+
throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions${locSuffix(at)}`, at);
|
|
111
120
|
}
|
|
112
|
-
getStructByName(path) {
|
|
113
|
-
throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions
|
|
121
|
+
getStructByName(path, at) {
|
|
122
|
+
throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions${locSuffix(at)}`, at);
|
|
114
123
|
}
|
|
115
124
|
getSQLIdentifier() {
|
|
116
125
|
throw new ConstantExpressionError('Constant expressions do not need SQL identifiers');
|