Press n or j to go to the next uncovered block, b, p or k for the previous block.
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 | 1x 1x 16x 17x 6x 10x 16x 16x 33x 16x 1349x 2698x 1349x 1349x 1749x 2698x 1749x 1749x 1749x 3498x 791x 791x 396x 395x 395x 766x 1349x 1349x 1349x 2698x 2698x 2166x 532x 266x 525x 266x 17x 17x 266x 6x 6x 266x 266x 266x 266x 525x 266x 10x 10x | import { ASTNode } from './ast'; import { hashObject } from './utils'; // A NodeSpec declares the types of the fields of an ASTNode. // It is used to compute hashes, to validate nodes (to prevent the construction // of an ill-formed AST), and to deal with some edits. // Its constructor expects a list of: // // - required: an ASTNode. // - optional: either an ASTNode, or `null`. // - list: an array of ASTNodes. // - value: an ordinary value that does not contain an ASTNode. // nodeSpec :: Array<ChildSpec> -> NodeSpec export function nodeSpec(childSpecs) { return new NodeSpec(childSpecs); } // required :: String -> ChildSpec export function required(fieldName) { return new Required(fieldName); } // optional :: String -> ChildSpec export function optional(fieldName) { return new Optional(fieldName); } // list :: String -> ChildSpec export function list(fieldName) { return new List(fieldName); } // value :: any -> ChildSpec export function value(fieldName) { return new Value(fieldName); } class NodeSpec { constructor(childSpecs) { Iif (!(childSpecs instanceof Array)) { throw new Error("NodeSpec: expected to receive an array of required/optional/list specs."); } for (const childSpec of childSpecs) { Iif (!(childSpec instanceof ChildSpec)) { throw new Error("NodeSpec: all child specs must be created by one of the functions: required/optional/list."); } } this.childSpecs = childSpecs; } validate(node) { for (const childSpec of this.childSpecs) { childSpec.validate(node); } } hash(node) { let hashes = new HashIterator(node, this); return hashObject([node.type, [...hashes]]); } children(node) { return new ChildrenIterator(node, this); } fieldNames() { return this.childSpecs.map((spec) => spec.fieldName); } } class ChildrenIterator { constructor(parent, nodeSpec) { this.parent = parent; this.nodeSpec = nodeSpec; } *[Symbol.iterator]() { for (let spec of this.nodeSpec.childSpecs) { if (spec instanceof Value) continue; let field = this.parent[spec.fieldName]; if (field instanceof ASTNode) { yield field; } else Eif (field instanceof Array) { for (let elem of field) { yield elem; } } } } } class HashIterator { constructor(parent, nodeSpec) { this.parent = parent; this.nodeSpec = nodeSpec; } *[Symbol.iterator]() { for (let spec of this.nodeSpec.childSpecs) { let field = this.parent[spec.fieldName]; if (spec instanceof Value) { yield hashObject(field); } else if (spec instanceof List) { for (let elem of field) { yield elem.hash; } } else { yield (field == null)? hashObject(null) : field.hash; } } } } class ChildSpec {} export class Required extends ChildSpec { constructor(fieldName) { super(); this.fieldName = fieldName; } validate(parent) { Iif (!(parent[this.fieldName] instanceof ASTNode)) { throw new Error(`Expected the required field '${this.fieldName}' of '${parent.type}' to contain an ASTNode.`); } } } export class Optional extends ChildSpec { constructor(fieldName) { super(); this.fieldName = fieldName; } validate(parent) { let child = parent[this.fieldName]; if (child !== null && !(child instanceof ASTNode)) { throw new Error(`Expected the optional field '${this.fieldName}' of '${parent.type}' to contain an ASTNode or null.`); } } } export class List extends ChildSpec { constructor(fieldName) { super(); this.fieldName = fieldName; } validate(parent) { let array = parent[this.fieldName]; let valid = true; Eif (array instanceof Array) { for (const elem of array) { Iif (!(elem instanceof ASTNode)) valid = false; } } else { valid = false; } Iif (!valid) { throw new Error(`Expected the listy field '${this.fieldName}' of '${parent.type}' to contain an array of ASTNodes.`); } } } export class Value extends ChildSpec { constructor(fieldName) { super(); this.fieldName = fieldName; } validate(_parent) { // Any value is valid, even `undefined`, so there's nothing to check. } } |