-
Notifications
You must be signed in to change notification settings - Fork 51
/
Val.scala
561 lines (480 loc) · 21.8 KB
/
Val.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
package sjsonnet
import java.util
import java.util.Arrays
import sjsonnet.Expr.Member.Visibility
import sjsonnet.Expr.Params
import scala.annotation.tailrec
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.reflect.ClassTag
/**
* [[Lazy]] models lazy evaluation within a Jsonnet program. Lazily
* evaluated dictionary values, array contents, or function parameters
* are all wrapped in [[Lazy]] and only truly evaluated on-demand
*/
abstract class Lazy {
protected[this] var cached: Val = null
def compute(): Val
final def force: Val = {
if(cached == null) cached = compute()
cached
}
}
/**
* [[Val]]s represented Jsonnet values that are the result of evaluating
* a Jsonnet program. The [[Val]] data structure is essentially a JSON tree,
* except evaluation of object attributes and array contents are lazy, and
* the tree can contain functions.
*/
sealed abstract class Val extends Lazy {
cached = this // avoid a megamorphic call to compute() when forcing
final def compute() = this
def pos: Position
def prettyName: String
def cast[T: ClassTag: PrettyNamed] =
if (implicitly[ClassTag[T]].runtimeClass.isInstance(this)) this.asInstanceOf[T]
else Error.fail("Expected " + implicitly[PrettyNamed[T]].s + ", found " + prettyName)
private[this] def failAs(err: String): Nothing =
Error.fail("Wrong parameter type: expected " + err + ", got " + prettyName)
def asString: String = failAs("String")
def asBoolean: Boolean = failAs("Boolean")
def asInt: Int = failAs("Int")
def asDouble: Double = failAs("Number")
def asObj: Val.Obj = failAs("Object")
def asArr: Val.Arr = failAs("Array")
def asFunc: Val.Func = failAs("Function")
}
class PrettyNamed[T](val s: String)
object PrettyNamed{
implicit def strName: PrettyNamed[Val.Str] = new PrettyNamed("string")
implicit def numName: PrettyNamed[Val.Num] = new PrettyNamed("number")
implicit def arrName: PrettyNamed[Val.Arr] = new PrettyNamed("array")
implicit def objName: PrettyNamed[Val.Obj] = new PrettyNamed("object")
implicit def funName: PrettyNamed[Val.Func] = new PrettyNamed("function")
}
object Val{
abstract class Literal extends Val with Expr
abstract class Bool extends Literal {
override def asBoolean: Boolean = this.isInstanceOf[True]
}
def bool(pos: Position, b: Boolean) = if (b) True(pos) else False(pos)
case class True(pos: Position) extends Bool {
def prettyName = "boolean"
}
case class False(pos: Position) extends Bool {
def prettyName = "boolean"
}
case class Null(pos: Position) extends Literal {
def prettyName = "null"
}
case class Str(pos: Position, value: String) extends Literal {
def prettyName = "string"
override def asString: String = value
}
case class Num(pos: Position, value: Double) extends Literal {
def prettyName = "number"
override def asInt: Int = value.toInt
override def asDouble: Double = value
}
class Arr(val pos: Position, private val value: Array[_ <: Lazy]) extends Literal {
def prettyName = "array"
override def asArr: Arr = this
def length: Int = value.length
def force(i: Int) = value(i).force
def asLazy(i: Int) = value(i)
def asLazyArray: Array[Lazy] = value.asInstanceOf[Array[Lazy]]
def asStrictArray: Array[Val] = value.map(_.force)
def concat(newPos: Position, rhs: Arr): Arr =
new Arr(newPos, value ++ rhs.value)
def iterator: Iterator[Val] = value.iterator.map(_.force)
def foreach[U](f: Val => U) = {
var i = 0
while(i < value.length) {
f(value(i).force)
i += 1
}
}
def forall(f: Val => Boolean): Boolean = {
var i = 0
while(i < value.length) {
if(!f(value(i).force)) return false
i += 1
}
true
}
}
object Obj{
abstract class Member(val add: Boolean, val visibility: Visibility, val cached: Boolean = true) {
def invoke(self: Obj, sup: Obj, fs: FileScope, ev: EvalScope): Val
}
class ConstMember(add: Boolean, visibility: Visibility, v: Val, cached: Boolean = true)
extends Member(add, visibility, cached) {
def invoke(self: Obj, sup: Obj, fs: FileScope, ev: EvalScope): Val = v
}
def mk(pos: Position, members: (String, Obj.Member)*): Obj = {
val m = new util.LinkedHashMap[String, Obj.Member]()
for((k, v) <- members) m.put(k, v)
new Obj(pos, m, false, null, null)
}
}
final class Obj(val pos: Position,
private[this] var value0: util.LinkedHashMap[String, Obj.Member],
static: Boolean,
triggerAsserts: Val.Obj => Unit,
`super`: Obj,
valueCache: mutable.HashMap[Any, Val] = mutable.HashMap.empty[Any, Val],
private[this] var allKeys: util.LinkedHashMap[String, java.lang.Boolean] = null) extends Literal with Expr.ObjBody {
var asserting: Boolean = false
def getSuper = `super`
private[this] def getValue0: util.LinkedHashMap[String, Obj.Member] = {
if(value0 == null) {
val value0 = new java.util.LinkedHashMap[String, Val.Obj.Member]
allKeys.forEach { (k, _) =>
value0.put(k, new Val.Obj.ConstMember(false, Visibility.Normal, valueCache(k)))
}
// Only assign to field after initialization is complete to allow unsynchronized multi-threaded use:
this.value0 = value0
}
value0
}
@tailrec def triggerAllAsserts(obj: Val.Obj): Unit = {
if(triggerAsserts != null) triggerAsserts(obj)
if(`super` != null) `super`.triggerAllAsserts(obj)
}
def addSuper(pos: Position, lhs: Val.Obj): Val.Obj = {
`super` match{
case null => new Val.Obj(pos, getValue0, false, triggerAsserts, lhs)
case x => new Val.Obj(pos, getValue0, false, triggerAsserts, x.addSuper(pos, lhs))
}
}
def prettyName = "object"
override def asObj: Val.Obj = this
private def gatherKeys(mapping: util.LinkedHashMap[String, java.lang.Boolean]): Unit = {
if(static) mapping.putAll(allKeys)
else {
if(`super` != null) `super`.gatherKeys(mapping)
getValue0.forEach { (k, m) =>
val vis = m.visibility
if(!mapping.containsKey(k)) mapping.put(k, vis == Visibility.Hidden)
else if(vis == Visibility.Hidden) mapping.put(k, true)
else if(vis == Visibility.Unhide) mapping.put(k, false)
}
}
}
private def getAllKeys = {
if(allKeys == null) {
allKeys = new util.LinkedHashMap[String, java.lang.Boolean]
gatherKeys(allKeys)
}
allKeys
}
@inline def hasKeys = !getAllKeys.isEmpty
@inline def containsKey(k: String): Boolean = getAllKeys.containsKey(k)
@inline def containsVisibleKey(k: String): Boolean = getAllKeys.get(k) == java.lang.Boolean.FALSE
lazy val allKeyNames: Array[String] = getAllKeys.keySet().toArray(new Array[String](getAllKeys.size()))
lazy val visibleKeyNames: Array[String] = if(static) allKeyNames else {
val buf = mutable.ArrayBuilder.make[String]
getAllKeys.forEach((k, b) => if(b == java.lang.Boolean.FALSE) buf += k)
buf.result()
}
def value(k: String,
pos: Position,
self: Obj = this)
(implicit evaluator: EvalScope): Val = {
if(static) {
valueCache.getOrElse(k, null) match {
case null => Error.fail("Field does not exist: " + k, pos)
case x => x
}
} else {
val cacheKey = if(self eq this) k else (k, self)
valueCache.getOrElse(cacheKey, {
valueRaw(k, self, pos, valueCache, cacheKey) match {
case null => Error.fail("Field does not exist: " + k, pos)
case x => x
}
})
}
}
private def renderString(v: Val)(implicit evaluator: EvalScope): String =
evaluator.materialize(v).transform(new Renderer()).toString
def mergeMember(l: Val,
r: Val,
pos: Position)
(implicit evaluator: EvalScope) = {
val lStr = l.isInstanceOf[Val.Str]
val rStr = r.isInstanceOf[Val.Str]
if(lStr || rStr) {
val ll = if(lStr) l.asInstanceOf[Val.Str].value else renderString(l)
val rr = if(rStr) r.asInstanceOf[Val.Str].value else renderString(r)
Val.Str(pos, ll ++ rr)
} else if(l.isInstanceOf[Val.Num] && r.isInstanceOf[Val.Num]) {
val ll = l.asInstanceOf[Val.Num].value
val rr = r.asInstanceOf[Val.Num].value
Val.Num(pos, ll + rr)
} else if(l.isInstanceOf[Val.Arr] && r.isInstanceOf[Val.Arr]) {
val ll = l.asInstanceOf[Val.Arr].asLazyArray
val rr = r.asInstanceOf[Val.Arr].asLazyArray
new Val.Arr(pos, ll ++ rr)
} else if(l.isInstanceOf[Val.Obj] && r.isInstanceOf[Val.Obj]) {
val ll = l.asInstanceOf[Val.Obj]
val rr = r.asInstanceOf[Val.Obj]
rr.addSuper(pos, ll)
} else throw new MatchError((l, r))
}
def valueRaw(k: String,
self: Obj,
pos: Position,
addTo: mutable.HashMap[Any, Val] = null,
addKey: Any = null)
(implicit evaluator: EvalScope): Val = {
if(static) {
val v = valueCache.getOrElse(k, null)
if(addTo != null && v != null) addTo(addKey) = v
v
} else {
val s = this.`super`
getValue0.get(k) match{
case null =>
if(s == null) null else s.valueRaw(k, self, pos, addTo, addKey)
case m =>
val vv = m.invoke(self, s, pos.fileScope, evaluator)
val v = if(s != null && m.add) {
s.valueRaw(k, self, pos, null, null) match {
case null => vv
case supValue => mergeMember(supValue, vv, pos)
}
} else vv
if(addTo != null && m.cached) addTo(addKey) = v
v
}
}
}
def foreachElement(sort: Boolean, pos: Position)(f: (String, Val) => Unit)(implicit ev: EvalScope): Unit = {
val keys = if (sort) visibleKeyNames.sorted else visibleKeyNames
for(k <- keys) {
val v = value(k, pos)
f(k, v)
}
}
}
final class StaticObjectFieldSet(protected val keys: Array[String]) {
override def hashCode(): Int = {
Arrays.hashCode(keys.asInstanceOf[Array[Object]])
}
override def equals(obj: scala.Any): Boolean = {
obj match {
case that: StaticObjectFieldSet =>
keys.sameElements(that.keys)
case _ => false
}
}
}
def staticObject(
pos: Position,
fields: Array[Expr.Member.Field],
internedKeyMaps: mutable.HashMap[StaticObjectFieldSet, java.util.LinkedHashMap[String, java.lang.Boolean]],
internedStrings: mutable.HashMap[String, String]): Obj = {
// Set the initial capacity to the number of fields divided by the default load factor + 1 -
// this ensures that we can fill up the map to the total number of fields without resizing.
// From JavaDoc - true for both Scala & Java HashMaps
val hashMapDefaultLoadFactor = 0.75f
val capacity = (fields.length / hashMapDefaultLoadFactor).toInt + 1
val cache = mutable.HashMap.empty[Any, Val]
val allKeys = new util.LinkedHashMap[String, java.lang.Boolean](capacity, hashMapDefaultLoadFactor)
val keys = new Array[String](fields.length)
var idx = 0
fields.foreach {
case Expr.Member.Field(_, Expr.FieldName.Fixed(k), _, _, _, rhs: Val.Literal) =>
val uniqueKey = internedStrings.getOrElseUpdate(k, k)
cache.put(uniqueKey, rhs)
allKeys.put(uniqueKey, false)
keys(idx) = uniqueKey
idx += 1
}
val fieldSet = new StaticObjectFieldSet(keys)
new Val.Obj(pos, null, true, null, null, cache, internedKeyMaps.getOrElseUpdate(fieldSet, allKeys))
}
abstract class Func(val pos: Position,
val defSiteValScope: ValScope,
val params: Params) extends Val with Expr {
def evalRhs(scope: ValScope, ev: EvalScope, fs: FileScope, pos: Position): Val
def evalDefault(expr: Expr, vs: ValScope, es: EvalScope): Val = null
def prettyName = "function"
override def exprErrorString: String = "Function"
override def asFunc: Func = this
def apply(argsL: Array[_ <: Lazy], namedNames: Array[String], outerPos: Position)(implicit ev: EvalScope): Val = {
val simple = namedNames == null && params.names.length == argsL.length
val funDefFileScope: FileScope = pos match { case null => outerPos.fileScope case p => p.fileScope }
//println(s"apply: argsL: ${argsL.length}, namedNames: $namedNames, paramNames: ${params.names.mkString(",")}")
if(simple) {
val newScope = defSiteValScope.extendSimple(argsL)
evalRhs(newScope, ev, funDefFileScope, outerPos)
} else {
val newScopeLen = math.max(params.names.length, argsL.length)
// Initialize positional args
val base = defSiteValScope.length
val newScope = defSiteValScope.extendBy(newScopeLen)
val argVals = newScope.bindings
val posArgs = if(namedNames == null) argsL.length else argsL.length - namedNames.length
System.arraycopy(argsL, 0, argVals, base, posArgs)
if(namedNames != null) { // Add named args
var i = 0
var j = posArgs
while(i < namedNames.length) {
val idx = params.paramMap.getOrElse(namedNames(i),
Error.fail(s"Function has no parameter ${namedNames(i)}", outerPos))
if(argVals(base+idx) != null)
Error.fail(s"binding parameter a second time: ${namedNames(i)}", outerPos)
argVals(base+idx) = argsL(j)
i += 1
j += 1
}
}
if(argsL.length > params.names.length)
Error.fail("Too many args, function has " + params.names.length + " parameter(s)", outerPos)
if(params.names.length != argsL.length) { // Args missing -> add defaults
var missing: ArrayBuffer[String] = null
var i = posArgs
var j = base + posArgs
while(j < argVals.length) {
if(argVals(j) == null) {
val default = params.defaultExprs(i)
if(default != null) {
argVals(j) = () => evalDefault(default, newScope, ev)
} else {
if(missing == null) missing = new ArrayBuffer
missing.+=(params.names(i))
}
}
i += 1
j += 1
}
if(missing != null) {
val plural = if(missing.size > 1) "s" else ""
Error.fail(s"Function parameter$plural ${missing.mkString(", ")} not bound in call", outerPos)
}
//println(argVals.mkString(s"params: ${params.names.length}, argsL: ${argsL.length}, argVals: [", ", ", "]"))
}
evalRhs(newScope, ev, funDefFileScope, outerPos)
}
}
def apply0(outerPos: Position)(implicit ev: EvalScope): Val = {
if(params.names.length != 0) apply(Evaluator.emptyLazyArray, null, outerPos)
else {
val funDefFileScope: FileScope = pos match { case null => outerPos.fileScope case p => p.fileScope }
evalRhs(defSiteValScope, ev, funDefFileScope, outerPos)
}
}
def apply1(argVal: Lazy, outerPos: Position)(implicit ev: EvalScope): Val = {
if(params.names.length != 1) apply(Array(argVal), null, outerPos)
else {
val funDefFileScope: FileScope = pos match { case null => outerPos.fileScope case p => p.fileScope }
val newScope: ValScope = defSiteValScope.extendSimple(argVal)
evalRhs(newScope, ev, funDefFileScope, outerPos)
}
}
def apply2(argVal1: Lazy, argVal2: Lazy, outerPos: Position)(implicit ev: EvalScope): Val = {
if(params.names.length != 2) apply(Array(argVal1, argVal2), null, outerPos)
else {
val funDefFileScope: FileScope = pos match { case null => outerPos.fileScope case p => p.fileScope }
val newScope: ValScope = defSiteValScope.extendSimple(argVal1, argVal2)
evalRhs(newScope, ev, funDefFileScope, outerPos)
}
}
def apply3(argVal1: Lazy, argVal2: Lazy, argVal3: Lazy, outerPos: Position)(implicit ev: EvalScope): Val = {
if(params.names.length != 3) apply(Array(argVal1, argVal2, argVal3), null, outerPos)
else {
val funDefFileScope: FileScope = pos match { case null => outerPos.fileScope case p => p.fileScope }
val newScope: ValScope = defSiteValScope.extendSimple(argVal1, argVal2, argVal3)
evalRhs(newScope, ev, funDefFileScope, outerPos)
}
}
}
/** Superclass for standard library functions */
abstract class Builtin(paramNames: Array[String], defaults: Array[Expr] = null)
extends Func(null, ValScope.empty, Params(paramNames,
if(defaults == null) new Array[Expr](paramNames.length) else defaults)) {
override final def evalDefault(expr: Expr, vs: ValScope, es: EvalScope): Val = expr.asInstanceOf[Val]
final def evalRhs(scope: ValScope, ev: EvalScope, fs: FileScope, pos: Position): Val = {
val args = new Array[Val](params.names.length)
var i = 0
var j = scope.length - args.length
while(i < args.length) {
args(i) = scope.bindings(i).force
i += 1
}
evalRhs(args, ev, pos)
}
def evalRhs(args: Array[Val], ev: EvalScope, pos: Position): Val
override def apply1(argVal: Lazy, outerPos: Position)(implicit ev: EvalScope): Val =
if(params.names.length != 1) apply(Array(argVal), null, outerPos)
else evalRhs(Array(argVal.force), ev, outerPos)
override def apply2(argVal1: Lazy, argVal2: Lazy, outerPos: Position)(implicit ev: EvalScope): Val =
if(params.names.length != 2) apply(Array(argVal1, argVal2), null, outerPos)
else evalRhs(Array(argVal1.force, argVal2.force), ev, outerPos)
/** Specialize a call to this function in the optimizer. Must return either `null` to leave the
* call-site as it is or a pair of a (possibly different) `Builtin` and the arguments to pass
* to it (usually a subset of the supplied arguments).
* @param args the positional arguments for this function call. Named arguments and defaults have
* already been resolved. */
def specialize(args: Array[Expr]): (Builtin, Array[Expr]) = null
/** Is this builtin safe to use in static evaluation */
def staticSafe: Boolean = true
}
abstract class Builtin1(pn1: String, def1: Expr = null) extends Builtin(Array(pn1), if(def1 == null) null else Array(def1)) {
final def evalRhs(args: Array[Val], ev: EvalScope, pos: Position): Val =
evalRhs(args(0), ev, pos)
def evalRhs(arg1: Val, ev: EvalScope, pos: Position): Val
override def apply(argVals: Array[_ <: Lazy], namedNames: Array[String], outerPos: Position)(implicit ev: EvalScope): Val =
if(namedNames == null && argVals.length == 1) evalRhs(argVals(0).force, ev, outerPos)
else super.apply(argVals, namedNames, outerPos)
override def apply1(argVal: Lazy, outerPos: Position)(implicit ev: EvalScope): Val =
if(params.names.length == 1) evalRhs(argVal.force, ev, outerPos)
else super.apply(Array(argVal), null, outerPos)
}
abstract class Builtin2(pn1: String, pn2: String, defs: Array[Expr] = null) extends Builtin(Array(pn1, pn2), defs) {
final def evalRhs(args: Array[Val], ev: EvalScope, pos: Position): Val =
evalRhs(args(0), args(1), ev, pos)
def evalRhs(arg1: Val, arg2: Val, ev: EvalScope, pos: Position): Val
override def apply(argVals: Array[_ <: Lazy], namedNames: Array[String], outerPos: Position)(implicit ev: EvalScope): Val =
if(namedNames == null && argVals.length == 2)
evalRhs(argVals(0).force, argVals(1).force, ev, outerPos)
else super.apply(argVals, namedNames, outerPos)
override def apply2(argVal1: Lazy, argVal2: Lazy, outerPos: Position)(implicit ev: EvalScope): Val =
if(params.names.length == 2) evalRhs(argVal1.force, argVal2.force, ev, outerPos)
else super.apply(Array(argVal1, argVal2), null, outerPos)
}
abstract class Builtin3(pn1: String, pn2: String, pn3: String, defs: Array[Expr] = null) extends Builtin(Array(pn1, pn2, pn3), defs) {
final def evalRhs(args: Array[Val], ev: EvalScope, pos: Position): Val =
evalRhs(args(0), args(1), args(2), ev, pos)
def evalRhs(arg1: Val, arg2: Val, arg3: Val, ev: EvalScope, pos: Position): Val
override def apply(argVals: Array[_ <: Lazy], namedNames: Array[String], outerPos: Position)(implicit ev: EvalScope): Val =
if(namedNames == null && argVals.length == 3)
evalRhs(argVals(0).force, argVals(1).force, argVals(2).force, ev, outerPos)
else super.apply(argVals, namedNames, outerPos)
}
abstract class Builtin4(pn1: String, pn2: String, pn3: String, pn4: String, defs: Array[Expr] = null) extends Builtin(Array(pn1, pn2, pn3, pn4), defs) {
final def evalRhs(args: Array[Val], ev: EvalScope, pos: Position): Val =
evalRhs(args(0), args(1), args(2), args(3), ev, pos)
def evalRhs(arg1: Val, arg2: Val, arg3: Val, arg4: Val, ev: EvalScope, pos: Position): Val
override def apply(argVals: Array[_ <: Lazy], namedNames: Array[String], outerPos: Position)(implicit ev: EvalScope): Val =
if(namedNames == null && argVals.length == 4)
evalRhs(argVals(0).force, argVals(1).force, argVals(2).force, argVals(3).force, ev, outerPos)
else super.apply(argVals, namedNames, outerPos)
}
}
/**
* [[EvalScope]] models the per-evaluator context that is propagated
* throughout the Jsonnet evaluation.
*/
abstract class EvalScope extends EvalErrorScope {
def visitExpr(expr: Expr)
(implicit scope: ValScope): Val
def materialize(v: Val): ujson.Value
def equal(x: Val, y: Val): Boolean
val emptyMaterializeFileScope = new FileScope(wd / "(materialize)")
val emptyMaterializeFileScopePos = new Position(emptyMaterializeFileScope, -1)
def settings: Settings
def warn(e: Error): Unit
}