Skip to content

Commit bb4df9e

Browse files
committed
feat: add Printer feature to allow generate PHP code from AST
1 parent 9a47013 commit bb4df9e

File tree

4 files changed

+603
-0
lines changed

4 files changed

+603
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "feat: add Printer feature to allow generate PHP code from AST",
4+
"packageName": "@rightcapital/phpdoc-parser",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* This class is converted via GPT based on this file:
3+
* https://github.com/phpstan/phpdoc-parser/blob/1.23.x/src/Printer/DiffElem.php
4+
*
5+
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
6+
*
7+
* Copyright (c) 2011, Nikita Popov
8+
* All rights reserved.
9+
*
10+
* Implements the Myers diff algorithm.
11+
*
12+
*/
13+
export class DiffElem<EleType> {
14+
public old: EleType;
15+
16+
public new: EleType;
17+
18+
public constructor(
19+
public type: DiffElemType,
20+
oldEle: EleType,
21+
newEle: EleType,
22+
) {
23+
this.type = type;
24+
this.old = oldEle;
25+
this.new = newEle;
26+
}
27+
}
28+
29+
export enum DiffElemType {
30+
KEEP = 1,
31+
REMOVE = 2,
32+
ADD = 3,
33+
REPLACE = 4,
34+
}

src/phpdoc-parser/printer/differ.ts

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/* eslint-disable no-param-reassign */
2+
/* eslint-disable no-plusplus */
3+
import { DiffElem, DiffElemType } from './diff-elem';
4+
5+
/**
6+
* Inspired by https://github.com/nikic/PHP-Parser/tree/36a6dcd04e7b0285e8f0868f44bd4927802f7df1
7+
*
8+
* Copyright (c) 2011, Nikita Popov
9+
* All rights reserved.
10+
*
11+
* Implements the Myers diff algorithm.
12+
*
13+
* Myers, Eugene W. "An O (ND) difference algorithm and its variations."
14+
* Algorithmica 1.1 (1986): 251-266.
15+
*
16+
* @template T
17+
*/
18+
export class Differ<T> {
19+
private isEqual: (a: T, b: T) => boolean;
20+
21+
constructor(isEqual: (a: T, b: T) => boolean) {
22+
this.isEqual = isEqual;
23+
}
24+
25+
public diff(old: T[], newElements: T[]): DiffElem<T>[] {
26+
const [trace, x, y] = this.calculateTrace(old, newElements);
27+
return this.extractDiff(trace, x, y, old, newElements);
28+
}
29+
30+
public diffWithReplacements(old: T[], newElements: T[]): DiffElem<T>[] {
31+
return this.coalesceReplacements(this.diff(old, newElements));
32+
}
33+
34+
private calculateTrace(
35+
old: T[],
36+
newElements: T[],
37+
): [Array<{ [key: number]: number }>, number, number] {
38+
const n = old.length;
39+
const m = newElements.length;
40+
const max = n + m;
41+
const v: { [key: number]: number } = { 1: 0 };
42+
const trace: Array<{ [key: number]: number }> = [];
43+
for (let d = 0; d <= max; d++) {
44+
trace.push({ ...v });
45+
for (let k = -d; k <= d; k += 2) {
46+
let x: number;
47+
if (k === -d || (k !== d && v[k - 1] < v[k + 1])) {
48+
x = v[k + 1];
49+
} else {
50+
x = v[k - 1] + 1;
51+
}
52+
53+
let y = x - k;
54+
while (x < n && y < m && this.isEqual(old[x], newElements[y])) {
55+
x++;
56+
y++;
57+
}
58+
59+
v[k] = x;
60+
if (x >= n && y >= m) {
61+
return [trace, x, y];
62+
}
63+
}
64+
}
65+
throw new Error('Should not happen');
66+
}
67+
68+
private extractDiff(
69+
trace: Array<{ [key: number]: number }>,
70+
x: number,
71+
y: number,
72+
old: T[],
73+
newElements: T[],
74+
): DiffElem<T>[] {
75+
const result: DiffElem<T>[] = [];
76+
for (let d = trace.length - 1; d >= 0; d--) {
77+
const v = trace[d];
78+
const k = x - y;
79+
80+
let prevK: number;
81+
if (k === -d || (k !== d && v[k - 1] < v[k + 1])) {
82+
prevK = k + 1;
83+
} else {
84+
prevK = k - 1;
85+
}
86+
87+
const prevX = v[prevK];
88+
const prevY = prevX - prevK;
89+
90+
while (x > prevX && y > prevY) {
91+
result.push(
92+
new DiffElem(DiffElemType.KEEP, old[x - 1], newElements[y - 1]),
93+
);
94+
x--;
95+
y--;
96+
}
97+
98+
if (d === 0) {
99+
break;
100+
}
101+
102+
while (x > prevX) {
103+
result.push(new DiffElem(DiffElemType.REMOVE, old[x - 1], null));
104+
x--;
105+
}
106+
107+
while (y > prevY) {
108+
result.push(new DiffElem(DiffElemType.ADD, null, newElements[y - 1]));
109+
y--;
110+
}
111+
}
112+
return result.reverse();
113+
}
114+
115+
private coalesceReplacements(diff: DiffElem<T>[]): DiffElem<T>[] {
116+
const newDiff: DiffElem<T>[] = [];
117+
const c = diff.length;
118+
for (let i = 0; i < c; i++) {
119+
const diffType = diff[i].type;
120+
if (diffType !== DiffElemType.REMOVE) {
121+
newDiff.push(diff[i]);
122+
// eslint-disable-next-line no-continue
123+
continue;
124+
}
125+
126+
let j = i;
127+
while (j < c && diff[j].type === DiffElemType.REMOVE) {
128+
j++;
129+
}
130+
131+
let k = j;
132+
while (k < c && diff[k].type === DiffElemType.ADD) {
133+
k++;
134+
}
135+
136+
if (j - i === k - j) {
137+
const len = j - i;
138+
for (let n = 0; n < len; n++) {
139+
newDiff.push(
140+
new DiffElem<T>(
141+
DiffElemType.REPLACE,
142+
diff[i + n].old,
143+
diff[j + n].new,
144+
),
145+
);
146+
}
147+
} else {
148+
for (; i < k; i++) {
149+
newDiff.push(diff[i]);
150+
}
151+
}
152+
i = k - 1;
153+
}
154+
return newDiff;
155+
}
156+
}

0 commit comments

Comments
 (0)