Skip to content

Commit 0ed8d7e

Browse files
committed
feat!: sync with upstream phpdoc-parser v2.0-v2.1
BREAKING CHANGES: - Merge QuoteAwareConstExprStringNode into ConstExprStringNode: quoteType is now a required constructor parameter (SINGLE_QUOTED/DOUBLE_QUOTED) - Remove QuoteAwareConstExprStringNode class - Remove quoteAwareConstExprString parameter from ConstExprParser and TypeParser constructors (always quote-aware now) New features: - Add ParserConfig class for centralized parser configuration - Add Comment class for inline comments in type expressions - Add TOKEN_COMMENT to Lexer for // comment tokenization - Add skipNewLineTokensAndConsumeComments() to TokenIterator - Add flushComments() to TokenIterator - Add COMMENTS attribute constant - Speculative union/intersection parsing across newlines with comments (enrichTypeOnUnionOrIntersection pattern from upstream) Internal changes: - TokenIterator savepoints now preserve comment state - All tryConsumeTokenType(TOKEN_PHPDOC_EOL) calls in TypeParser replaced with skipNewLineTokensAndConsumeComments() - Transpiler uses ConstExprStringNode.value for property names instead of toString() to avoid quoted output
1 parent 9243723 commit 0ed8d7e

14 files changed

Lines changed: 507 additions & 204 deletions

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export { AbstractNodeVisitor } from './phpdoc-parser/ast/abstract-node-visitor';
22
export { BaseNode } from './phpdoc-parser/ast/base-node';
3+
export { Comment } from './phpdoc-parser/ast/comment';
34
export { ConstExprArrayItemNode } from './phpdoc-parser/ast/const-expr/const-expr-array-item-node';
45
export { ConstExprArrayNode } from './phpdoc-parser/ast/const-expr/const-expr-array-node';
56
export { ConstExprFalseNode } from './phpdoc-parser/ast/const-expr/const-expr-false-node';
@@ -10,7 +11,6 @@ export { ConstExprNullNode } from './phpdoc-parser/ast/const-expr/const-expr-nul
1011
export { ConstExprStringNode } from './phpdoc-parser/ast/const-expr/const-expr-string-node';
1112
export { ConstExprTrueNode } from './phpdoc-parser/ast/const-expr/const-expr-true-node';
1213
export { ConstFetchNode } from './phpdoc-parser/ast/const-expr/const-fetch-node';
13-
export { QuoteAwareConstExprStringNode } from './phpdoc-parser/ast/const-expr/quote-aware-const-expr-string-node';
1414
export type { Node } from './phpdoc-parser/ast/node';
1515
export { NodeTraverser } from './phpdoc-parser/ast/node-traverser';
1616
export type { NodeVisitor } from './phpdoc-parser/ast/node-visitor';
@@ -79,6 +79,7 @@ export { PhpDocParser } from './phpdoc-parser/parser/php-doc-parser';
7979
export { StringUnescaper } from './phpdoc-parser/parser/string-unescaper';
8080
export { TokenIterator } from './phpdoc-parser/parser/token-iterator';
8181
export { TypeParser } from './phpdoc-parser/parser/type-parser';
82+
export { ParserConfig } from './phpdoc-parser/parser-config';
8283
export { Printer } from './phpdoc-parser/printer/printer';
8384
export * from './phpdoc-parser/transpiler/helpers';
8485
export * from './phpdoc-parser/transpiler/php-doc-to-typescript-type-transpiler';

src/phpdoc-parser/ast/comment.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export class Comment {
2+
public text: string;
3+
4+
public startLine: number;
5+
6+
public startIndex: number;
7+
8+
constructor(text: string, startLine: number = -1, startIndex: number = -1) {
9+
this.text = text;
10+
this.startLine = startLine;
11+
this.startIndex = startIndex;
12+
}
13+
14+
getReformattedText(): string {
15+
return this.text.trim();
16+
}
17+
}

src/phpdoc-parser/ast/const-expr/const-expr-node.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import type { ConstExprNullNode } from './const-expr-null-node';
88
import type { ConstExprStringNode } from './const-expr-string-node';
99
import type { ConstExprTrueNode } from './const-expr-true-node';
1010
import type { ConstFetchNode } from './const-fetch-node';
11-
import type { QuoteAwareConstExprStringNode } from './quote-aware-const-expr-string-node';
1211

1312
export class ConstExprNode extends BaseNode {
1413
public getNodeType(): string {
@@ -31,10 +30,6 @@ export class ConstExprNode extends BaseNode {
3130
return this.getNodeType() === 'ConstExprFalseNode';
3231
}
3332

34-
public isQuoteAwareConstExprStringNode(): this is QuoteAwareConstExprStringNode {
35-
return this.getNodeType() === 'QuoteAwareConstExprStringNode';
36-
}
37-
3833
public isConstExprNullNode(): this is ConstExprNullNode {
3934
return this.getNodeType() === 'ConstExprNullNode';
4035
}

src/phpdoc-parser/ast/const-expr/const-expr-string-node.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,55 @@
11
import { ConstExprNode } from './const-expr-node';
22

33
export class ConstExprStringNode extends ConstExprNode {
4-
constructor(public value: string) {
4+
public static readonly SINGLE_QUOTED = 1;
5+
6+
public static readonly DOUBLE_QUOTED = 2;
7+
8+
constructor(
9+
public value: string,
10+
public quoteType: number,
11+
) {
512
super();
613
}
714

815
public toString(): string {
9-
return this.value;
16+
if (this.quoteType === ConstExprStringNode.SINGLE_QUOTED) {
17+
return `'${this.value.replace(/[\\']/g, '\\$&')}'`;
18+
}
19+
20+
return `"${this.escapeDoubleQuotedString()}"`;
21+
}
22+
23+
private escapeDoubleQuotedString(): string {
24+
// eslint-disable-next-line no-control-regex
25+
let escaped = this.value.replace(/["\n\r\t\x0B\f\v\\$]/g, '\\$&');
26+
27+
const regexes = [
28+
// eslint-disable-next-line no-control-regex
29+
/[\x00-\x08\x0E-\x1F]/g,
30+
/[\xC0-\xC1]/g,
31+
/[\xF5-\xFF]/g,
32+
/\xE0[\x80-\x9F]/g,
33+
/\xF0[\x80-\x8F]/g,
34+
/[\xC2-\xDF](?![\x80-\xBF])/g,
35+
/[\xE0-\xEF](?![\x80-\xBF]{2})/g,
36+
/[\xF0-\xF4](?![\x80-\xBF]{3})/g,
37+
// eslint-disable-next-line no-control-regex
38+
/(?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF]/g,
39+
/(?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]|[\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF]/g,
40+
/(?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF])/g,
41+
/(?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2})/g,
42+
/(?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF])/g,
43+
];
44+
45+
for (const regex of regexes) {
46+
escaped = escaped.replace(regex, (match) => {
47+
const hex = match.charCodeAt(0).toString(16);
48+
return `\\x${'0'.repeat(2 - hex.length) + hex}`;
49+
});
50+
}
51+
52+
return escaped;
1053
}
1154

1255
public getNodeType(): string {

src/phpdoc-parser/ast/const-expr/quote-aware-const-expr-string-node.ts

Lines changed: 0 additions & 59 deletions
This file was deleted.

src/phpdoc-parser/ast/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,6 @@ export enum Attribute {
4545
END_INDEX = 'endIndex',
4646

4747
ORIGINAL_NODE = 'originalNode',
48+
49+
COMMENTS = 'comments',
4850
}

src/phpdoc-parser/lexer/lexer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ export class Lexer {
6969

7070
public static TOKEN_ARROW = 'TOKEN_ARROW';
7171

72+
public static TOKEN_COMMENT = 'TOKEN_COMMENT';
73+
7274
public static TOKEN_LABELS = {
7375
[Lexer.TOKEN_REFERENCE]: '&',
7476
[Lexer.TOKEN_UNION]: '|',
@@ -84,6 +86,7 @@ export class Lexer {
8486
[Lexer.TOKEN_OPEN_CURLY_BRACKET]: '{',
8587
[Lexer.TOKEN_CLOSE_CURLY_BRACKET]: '}',
8688
[Lexer.TOKEN_COMMA]: ',',
89+
[Lexer.TOKEN_COMMENT]: '//',
8790
[Lexer.TOKEN_COLON]: ':',
8891
[Lexer.TOKEN_VARIADIC]: '...',
8992
[Lexer.TOKEN_DOUBLE_COLON]: '::',
@@ -160,6 +163,7 @@ export class Lexer {
160163
[Lexer.TOKEN_OPEN_CURLY_BRACKET]: '\\{',
161164
[Lexer.TOKEN_CLOSE_CURLY_BRACKET]: '\\}',
162165
[Lexer.TOKEN_COMMA]: ',',
166+
[Lexer.TOKEN_COMMENT]: '\\/\\/[^\\r\\n]*(?=\\n|\\r|\\*\\/)',
163167
[Lexer.TOKEN_VARIADIC]: '\\.\\.\\.',
164168
[Lexer.TOKEN_DOUBLE_COLON]: '::',
165169
[Lexer.TOKEN_DOUBLE_ARROW]: '=>',

src/phpdoc-parser/parser-config.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export class ParserConfig {
2+
public useLinesAttributes: boolean;
3+
4+
public useIndexAttributes: boolean;
5+
6+
public useCommentsAttributes: boolean;
7+
8+
constructor(
9+
usedAttributes: {
10+
lines?: boolean;
11+
indexes?: boolean;
12+
comments?: boolean;
13+
} = {},
14+
) {
15+
this.useLinesAttributes = usedAttributes.lines ?? false;
16+
this.useIndexAttributes = usedAttributes.indexes ?? false;
17+
this.useCommentsAttributes = usedAttributes.comments ?? false;
18+
}
19+
}

src/phpdoc-parser/parser/const-expr-parser.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { ConstExprNullNode } from '../ast/const-expr/const-expr-null-node';
88
import { ConstExprStringNode } from '../ast/const-expr/const-expr-string-node';
99
import { ConstExprTrueNode } from '../ast/const-expr/const-expr-true-node';
1010
import { ConstFetchNode } from '../ast/const-expr/const-fetch-node';
11-
import { QuoteAwareConstExprStringNode } from '../ast/const-expr/quote-aware-const-expr-string-node';
1211
import { Attribute } from '../ast/types';
1312
import { Lexer } from '../lexer/lexer';
1413
import { ParserException } from './parser-exception';
@@ -25,7 +24,6 @@ export class ConstExprParser {
2524
*/
2625
constructor(
2726
private unescapeStrings: boolean = false,
28-
private quoteAwareConstExprString: boolean = false,
2927
usedAttributes: { lines?: boolean; indexes?: boolean } = {},
3028
) {
3129
this.useLinesAttributes = usedAttributes.lines ?? false;
@@ -76,23 +74,14 @@ export class ConstExprParser {
7674
}
7775
tokens.next();
7876

79-
if (this.quoteAwareConstExprString) {
80-
return this.enrichWithAttributes(
81-
tokens,
82-
new QuoteAwareConstExprStringNode(
83-
value,
84-
type === Lexer.TOKEN_SINGLE_QUOTED_STRING
85-
? QuoteAwareConstExprStringNode.SINGLE_QUOTED
86-
: QuoteAwareConstExprStringNode.DOUBLE_QUOTED,
87-
),
88-
startLine,
89-
startIndex,
90-
);
91-
}
92-
9377
return this.enrichWithAttributes(
9478
tokens,
95-
new ConstExprStringNode(value),
79+
new ConstExprStringNode(
80+
value,
81+
type === Lexer.TOKEN_SINGLE_QUOTED_STRING
82+
? ConstExprStringNode.SINGLE_QUOTED
83+
: ConstExprStringNode.DOUBLE_QUOTED,
84+
),
9685
startLine,
9786
startIndex,
9887
);

src/phpdoc-parser/parser/token-iterator.ts

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Comment } from '../ast/comment';
12
import { Lexer } from '../lexer/lexer';
23
import { ParserException } from './parser-exception';
34

@@ -6,7 +7,9 @@ export class TokenIterator {
67

78
private index: number;
89

9-
private savePoints: number[] = [];
10+
private comments: Comment[] = [];
11+
12+
private savePoints: Array<[number, Comment[]]> = [];
1013

1114
private skippedTokenTypes: string[] = [Lexer.TOKEN_HORIZONTAL_WS];
1215

@@ -158,6 +161,47 @@ export class TokenIterator {
158161
return true;
159162
}
160163

164+
public flushComments(): Comment[] {
165+
const res = this.comments;
166+
this.comments = [];
167+
return res;
168+
}
169+
170+
public skipNewLineTokensAndConsumeComments(): void {
171+
if (this.currentTokenType() === Lexer.TOKEN_COMMENT) {
172+
this.comments.push(
173+
new Comment(
174+
this.currentTokenValue(),
175+
this.currentTokenLine(),
176+
this.currentTokenIndex(),
177+
),
178+
);
179+
this.next();
180+
}
181+
182+
if (!this.isCurrentTokenType(Lexer.TOKEN_PHPDOC_EOL)) {
183+
return;
184+
}
185+
186+
let foundNewLine: boolean;
187+
do {
188+
foundNewLine = this.tryConsumeTokenType(Lexer.TOKEN_PHPDOC_EOL);
189+
if (this.currentTokenType() !== Lexer.TOKEN_COMMENT) {
190+
// eslint-disable-next-line no-continue
191+
continue;
192+
}
193+
194+
this.comments.push(
195+
new Comment(
196+
this.currentTokenValue(),
197+
this.currentTokenLine(),
198+
this.currentTokenIndex(),
199+
),
200+
);
201+
this.next();
202+
} while (foundNewLine);
203+
}
204+
161205
private detectNewline(): void {
162206
const value = this.currentTokenValue();
163207
if (value.substring(0, 2) === '\r\n') {
@@ -228,17 +272,19 @@ export class TokenIterator {
228272
}
229273

230274
public pushSavePoint(): void {
231-
this.savePoints.push(this.index);
275+
this.savePoints.push([this.index, [...this.comments]]);
232276
}
233277

234278
public dropSavePoint(): void {
235279
this.savePoints.pop();
236280
}
237281

238282
public rollback(): void {
239-
const index = this.savePoints.pop();
240-
// assert(index !== null);
241-
this.index = index;
283+
const savepoint = this.savePoints.pop();
284+
if (savepoint === undefined) {
285+
throw new Error('No save point to rollback to');
286+
}
287+
[this.index, this.comments] = savepoint;
242288
}
243289

244290
throwError(

0 commit comments

Comments
 (0)