| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import { ListAstNode } from './ast.js';
- /**
- * Concatenates a list of (2,3) AstNode's into a single (2,3) AstNode.
- * This mutates the items of the input array!
- * If all items have the same height, this method has runtime O(items.length).
- * Otherwise, it has runtime O(items.length * max(log(items.length), items.max(i => i.height))).
- */
- export function concat23Trees(items) {
- if (items.length === 0) {
- return null;
- }
- if (items.length === 1) {
- return items[0];
- }
- let i = 0;
- /**
- * Reads nodes of same height and concatenates them to a single node.
- */
- function readNode() {
- if (i >= items.length) {
- return null;
- }
- const start = i;
- const height = items[start].listHeight;
- i++;
- while (i < items.length && items[i].listHeight === height) {
- i++;
- }
- if (i - start >= 2) {
- return concat23TreesOfSameHeight(start === 0 && i === items.length ? items : items.slice(start, i), false);
- }
- else {
- return items[start];
- }
- }
- // The items might not have the same height.
- // We merge all items by using a binary concat operator.
- let first = readNode(); // There must be a first item
- let second = readNode();
- if (!second) {
- return first;
- }
- for (let item = readNode(); item; item = readNode()) {
- // Prefer concatenating smaller trees, as the runtime of concat depends on the tree height.
- if (heightDiff(first, second) <= heightDiff(second, item)) {
- first = concat(first, second);
- second = item;
- }
- else {
- second = concat(second, item);
- }
- }
- const result = concat(first, second);
- return result;
- }
- export function concat23TreesOfSameHeight(items, createImmutableLists = false) {
- if (items.length === 0) {
- return null;
- }
- if (items.length === 1) {
- return items[0];
- }
- let length = items.length;
- // All trees have same height, just create parent nodes.
- while (length > 3) {
- const newLength = length >> 1;
- for (let i = 0; i < newLength; i++) {
- const j = i << 1;
- items[i] = ListAstNode.create23(items[j], items[j + 1], j + 3 === length ? items[j + 2] : null, createImmutableLists);
- }
- length = newLength;
- }
- return ListAstNode.create23(items[0], items[1], length >= 3 ? items[2] : null, createImmutableLists);
- }
- function heightDiff(node1, node2) {
- return Math.abs(node1.listHeight - node2.listHeight);
- }
- function concat(node1, node2) {
- if (node1.listHeight === node2.listHeight) {
- return ListAstNode.create23(node1, node2, null, false);
- }
- else if (node1.listHeight > node2.listHeight) {
- // node1 is the tree we want to insert into
- return append(node1, node2);
- }
- else {
- return prepend(node2, node1);
- }
- }
- /**
- * Appends the given node to the end of this (2,3) tree.
- * Returns the new root.
- */
- function append(list, nodeToAppend) {
- list = list.toMutable();
- let curNode = list;
- const parents = new Array();
- let nodeToAppendOfCorrectHeight;
- while (true) {
- // assert nodeToInsert.listHeight <= curNode.listHeight
- if (nodeToAppend.listHeight === curNode.listHeight) {
- nodeToAppendOfCorrectHeight = nodeToAppend;
- break;
- }
- // assert 0 <= nodeToInsert.listHeight < curNode.listHeight
- if (curNode.kind !== 4 /* AstNodeKind.List */) {
- throw new Error('unexpected');
- }
- parents.push(curNode);
- // assert 2 <= curNode.childrenLength <= 3
- curNode = curNode.makeLastElementMutable();
- }
- // assert nodeToAppendOfCorrectHeight!.listHeight === curNode.listHeight
- for (let i = parents.length - 1; i >= 0; i--) {
- const parent = parents[i];
- if (nodeToAppendOfCorrectHeight) {
- // Can we take the element?
- if (parent.childrenLength >= 3) {
- // assert parent.childrenLength === 3 && parent.listHeight === nodeToAppendOfCorrectHeight.listHeight + 1
- // we need to split to maintain (2,3)-tree property.
- // Send the third element + the new element to the parent.
- nodeToAppendOfCorrectHeight = ListAstNode.create23(parent.unappendChild(), nodeToAppendOfCorrectHeight, null, false);
- }
- else {
- parent.appendChildOfSameHeight(nodeToAppendOfCorrectHeight);
- nodeToAppendOfCorrectHeight = undefined;
- }
- }
- else {
- parent.handleChildrenChanged();
- }
- }
- if (nodeToAppendOfCorrectHeight) {
- return ListAstNode.create23(list, nodeToAppendOfCorrectHeight, null, false);
- }
- else {
- return list;
- }
- }
- /**
- * Prepends the given node to the end of this (2,3) tree.
- * Returns the new root.
- */
- function prepend(list, nodeToAppend) {
- list = list.toMutable();
- let curNode = list;
- const parents = new Array();
- // assert nodeToInsert.listHeight <= curNode.listHeight
- while (nodeToAppend.listHeight !== curNode.listHeight) {
- // assert 0 <= nodeToInsert.listHeight < curNode.listHeight
- if (curNode.kind !== 4 /* AstNodeKind.List */) {
- throw new Error('unexpected');
- }
- parents.push(curNode);
- // assert 2 <= curNode.childrenFast.length <= 3
- curNode = curNode.makeFirstElementMutable();
- }
- let nodeToPrependOfCorrectHeight = nodeToAppend;
- // assert nodeToAppendOfCorrectHeight!.listHeight === curNode.listHeight
- for (let i = parents.length - 1; i >= 0; i--) {
- const parent = parents[i];
- if (nodeToPrependOfCorrectHeight) {
- // Can we take the element?
- if (parent.childrenLength >= 3) {
- // assert parent.childrenLength === 3 && parent.listHeight === nodeToAppendOfCorrectHeight.listHeight + 1
- // we need to split to maintain (2,3)-tree property.
- // Send the third element + the new element to the parent.
- nodeToPrependOfCorrectHeight = ListAstNode.create23(nodeToPrependOfCorrectHeight, parent.unprependChild(), null, false);
- }
- else {
- parent.prependChildOfSameHeight(nodeToPrependOfCorrectHeight);
- nodeToPrependOfCorrectHeight = undefined;
- }
- }
- else {
- parent.handleChildrenChanged();
- }
- }
- if (nodeToPrependOfCorrectHeight) {
- return ListAstNode.create23(nodeToPrependOfCorrectHeight, list, null, false);
- }
- else {
- return list;
- }
- }
|