Skip to content

Commit ca67cbd

Browse files
authored
DCL Parser with AST Object Encapsulation (#173)
DCL Parser with AST Object Encapsulation (#173)
1 parent 41e6c49 commit ca67cbd

14 files changed

+1400
-8
lines changed

extension/src/astObjects/dclAtom.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Position, Range } from 'vscode';
2+
import { IDclFragment } from './dclInterfaces';
3+
4+
export class DclAtom implements IDclFragment {
5+
public symbol: string;
6+
public flatIndex: number;
7+
8+
private _line: number;
9+
get line(): number {
10+
return this._line;
11+
}
12+
13+
private _column: number;
14+
get column(): number {
15+
return this._column;
16+
}
17+
18+
constructor(line: number, column: number, value: string, flatIdx: number) {
19+
this._line = line;
20+
this._column = column;
21+
this.symbol = value;
22+
this.flatIndex = flatIdx;
23+
}
24+
25+
get isComment(): boolean {
26+
return this.symbol.startsWith('/');
27+
}
28+
29+
get isBlockComment(): boolean {
30+
return this.symbol.startsWith('/*');
31+
}
32+
33+
get isString(): boolean {
34+
return /^".*"$/.test(this.symbol);
35+
}
36+
37+
get range(): Range {
38+
return new Range(this.line, this.column, this.line, this.column + this.symbol.length);
39+
}
40+
41+
equal(atom: IDclFragment): boolean {
42+
return JSON.stringify(this) === JSON.stringify(atom);
43+
}
44+
45+
contains(pos: Position): boolean {
46+
return this.range.contains(pos);
47+
}
48+
49+
getAtomFromPosition(position: Position): IDclFragment {
50+
return this.contains(position) ? this : null;
51+
}
52+
53+
}
+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { Position, Range } from 'vscode';
2+
import { DclAtom } from './dclAtom';
3+
import { IDclContainer, IDclFragment } from './dclInterfaces';
4+
5+
export class DclAttribute implements IDclContainer {
6+
7+
get line(): number {
8+
return this.length === 0 ? -1 : this.firstAtom.line;
9+
}
10+
11+
get column(): number {
12+
return this.length === 0 ? -1 : this.firstAtom.column;
13+
}
14+
15+
get asAttribute(): DclAttribute {
16+
return this;
17+
}
18+
19+
get asContainer(): IDclContainer {
20+
return this;
21+
}
22+
23+
get isComment(): boolean {
24+
return false;
25+
}
26+
27+
get isBlockComment(): boolean {
28+
return false;
29+
}
30+
31+
get isString(): boolean {
32+
return false;
33+
}
34+
35+
get range(): Range {
36+
const lastAtom = this.atoms[this.length - 1];
37+
return new Range(this.firstAtom.range.start, lastAtom.range.end);
38+
}
39+
40+
equal(dclObject: IDclFragment): boolean {
41+
return JSON.stringify(this) === JSON.stringify(dclObject);
42+
}
43+
44+
contains(position: Position): boolean {
45+
return this.range.contains(position);
46+
}
47+
48+
getAtomFromPosition(position: Position): IDclFragment {
49+
if (this.contains(position)) {
50+
for (let i = 0; i < this.length; i++) {
51+
if (!this.atoms[i].contains(position)) {
52+
continue;
53+
}
54+
return this.atoms[i];
55+
}
56+
}
57+
return null;
58+
}
59+
60+
public atoms: Array<IDclFragment>;
61+
62+
get length(): number {
63+
return this.atoms.length;
64+
}
65+
66+
get firstAtom(): IDclFragment {
67+
return this.atoms[0];
68+
}
69+
70+
get lastAtom(): IDclFragment {
71+
return this.atoms[this.length - 1];
72+
}
73+
74+
get firstNonComment(): IDclFragment {
75+
return this.length === 0 ? null : this.firstAtom;
76+
}
77+
78+
getParentFrom(from: Position|IDclFragment, tilesOnly: boolean = false): IDclContainer {
79+
const pos = from instanceof Position ? from : from.range.start;
80+
if (this.contains(pos)) {
81+
return tilesOnly ? null : this;
82+
}
83+
return null;
84+
}
85+
86+
flatten(into?: Array<DclAtom>): Array<DclAtom> {
87+
if (!into) {
88+
into = [];
89+
}
90+
this.atoms.forEach(item => {
91+
into.push(item as DclAtom);
92+
});
93+
return into;
94+
}
95+
96+
97+
// Everything above this point is sequenced by IDclFragment & then IDclContainer contract
98+
// Everything below this point is unique to DclAttribute and not an interface requirement
99+
100+
101+
constructor(atoms: Array<DclAtom>) {
102+
this.atoms = [...atoms];
103+
}
104+
105+
get key(): IDclFragment {
106+
return this.length === 0 ? null : this.firstAtom;
107+
}
108+
get delineator(): IDclFragment {
109+
return this.length < 3 ? null : this.atoms[1];
110+
}
111+
get value(): IDclFragment {
112+
return this.length < 3 ? null : this.atoms[2];
113+
}
114+
115+
// Context: DCL Attributes are valid in 2 arrangements: "Key = Value;" OR "key;"
116+
// Any other arrangement should be considered a malformed syntax error.
117+
get isWellFormed(): boolean {
118+
const invalid = this.length < 2 // probably has key, but missing semi-colon
119+
|| this.length === 3 // probably missing equal or semi-colon
120+
|| this.length > 4 // exceeds possible key-value-pair structure
121+
|| this.lastAtom.symbol !== ';' // invalid attribute termination
122+
|| this.firstAtom.isString // strings are invalid keys
123+
|| (this.length === 4 && this.atoms[1].symbol !== '=');
124+
return !invalid;
125+
}
126+
127+
}
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Position, Range } from 'vscode';
2+
import { DclAtom } from './dclAtom';
3+
import { DclAttribute } from './dclAttribute';
4+
import { DclTile } from './dclTile';
5+
6+
export interface IDclFragment {
7+
readonly line: number;
8+
readonly column: number;
9+
readonly symbol?: string | undefined;
10+
readonly flatIndex?: number | undefined;
11+
readonly asTile?: DclTile | undefined;
12+
readonly asAttribute?: DclAttribute | undefined;
13+
readonly asContainer?: IDclContainer | undefined;
14+
readonly isComment: boolean;
15+
readonly isBlockComment: boolean;
16+
readonly isString: boolean;
17+
readonly range: Range;
18+
19+
equal(atom: IDclFragment): boolean;
20+
contains(position: Position): boolean;
21+
getAtomFromPosition(position: Position): IDclFragment;
22+
}
23+
24+
export interface IDclContainer extends IDclFragment {
25+
readonly atoms: Array<IDclFragment>;
26+
readonly length: number;
27+
readonly firstAtom: IDclFragment;
28+
readonly lastAtom: IDclFragment;
29+
readonly firstNonComment: IDclFragment;
30+
getParentFrom(position: Position|IDclFragment, tilesOnly: boolean): IDclContainer;
31+
flatten(into?: Array<DclAtom>): Array<DclAtom>;
32+
}

0 commit comments

Comments
 (0)