Skip to content

Commit

Permalink
DCL Parser with AST Object Encapsulation (#173)
Browse files Browse the repository at this point in the history
DCL Parser with AST Object Encapsulation (#173)
  • Loading branch information
JD-Howard authored Jan 13, 2022
1 parent 41e6c49 commit ca67cbd
Show file tree
Hide file tree
Showing 14 changed files with 1,400 additions and 8 deletions.
53 changes: 53 additions & 0 deletions extension/src/astObjects/dclAtom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Position, Range } from 'vscode';
import { IDclFragment } from './dclInterfaces';

export class DclAtom implements IDclFragment {
public symbol: string;
public flatIndex: number;

private _line: number;
get line(): number {
return this._line;
}

private _column: number;
get column(): number {
return this._column;
}

constructor(line: number, column: number, value: string, flatIdx: number) {
this._line = line;
this._column = column;
this.symbol = value;
this.flatIndex = flatIdx;
}

get isComment(): boolean {
return this.symbol.startsWith('/');
}

get isBlockComment(): boolean {
return this.symbol.startsWith('/*');
}

get isString(): boolean {
return /^".*"$/.test(this.symbol);
}

get range(): Range {
return new Range(this.line, this.column, this.line, this.column + this.symbol.length);
}

equal(atom: IDclFragment): boolean {
return JSON.stringify(this) === JSON.stringify(atom);
}

contains(pos: Position): boolean {
return this.range.contains(pos);
}

getAtomFromPosition(position: Position): IDclFragment {
return this.contains(position) ? this : null;
}

}
127 changes: 127 additions & 0 deletions extension/src/astObjects/dclAttribute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { Position, Range } from 'vscode';
import { DclAtom } from './dclAtom';
import { IDclContainer, IDclFragment } from './dclInterfaces';

export class DclAttribute implements IDclContainer {

get line(): number {
return this.length === 0 ? -1 : this.firstAtom.line;
}

get column(): number {
return this.length === 0 ? -1 : this.firstAtom.column;
}

get asAttribute(): DclAttribute {
return this;
}

get asContainer(): IDclContainer {
return this;
}

get isComment(): boolean {
return false;
}

get isBlockComment(): boolean {
return false;
}

get isString(): boolean {
return false;
}

get range(): Range {
const lastAtom = this.atoms[this.length - 1];
return new Range(this.firstAtom.range.start, lastAtom.range.end);
}

equal(dclObject: IDclFragment): boolean {
return JSON.stringify(this) === JSON.stringify(dclObject);
}

contains(position: Position): boolean {
return this.range.contains(position);
}

getAtomFromPosition(position: Position): IDclFragment {
if (this.contains(position)) {
for (let i = 0; i < this.length; i++) {
if (!this.atoms[i].contains(position)) {
continue;
}
return this.atoms[i];
}
}
return null;
}

public atoms: Array<IDclFragment>;

get length(): number {
return this.atoms.length;
}

get firstAtom(): IDclFragment {
return this.atoms[0];
}

get lastAtom(): IDclFragment {
return this.atoms[this.length - 1];
}

get firstNonComment(): IDclFragment {
return this.length === 0 ? null : this.firstAtom;
}

getParentFrom(from: Position|IDclFragment, tilesOnly: boolean = false): IDclContainer {
const pos = from instanceof Position ? from : from.range.start;
if (this.contains(pos)) {
return tilesOnly ? null : this;
}
return null;
}

flatten(into?: Array<DclAtom>): Array<DclAtom> {
if (!into) {
into = [];
}
this.atoms.forEach(item => {
into.push(item as DclAtom);
});
return into;
}


// Everything above this point is sequenced by IDclFragment & then IDclContainer contract
// Everything below this point is unique to DclAttribute and not an interface requirement


constructor(atoms: Array<DclAtom>) {
this.atoms = [...atoms];
}

get key(): IDclFragment {
return this.length === 0 ? null : this.firstAtom;
}
get delineator(): IDclFragment {
return this.length < 3 ? null : this.atoms[1];
}
get value(): IDclFragment {
return this.length < 3 ? null : this.atoms[2];
}

// Context: DCL Attributes are valid in 2 arrangements: "Key = Value;" OR "key;"
// Any other arrangement should be considered a malformed syntax error.
get isWellFormed(): boolean {
const invalid = this.length < 2 // probably has key, but missing semi-colon
|| this.length === 3 // probably missing equal or semi-colon
|| this.length > 4 // exceeds possible key-value-pair structure
|| this.lastAtom.symbol !== ';' // invalid attribute termination
|| this.firstAtom.isString // strings are invalid keys
|| (this.length === 4 && this.atoms[1].symbol !== '=');
return !invalid;
}

}
32 changes: 32 additions & 0 deletions extension/src/astObjects/dclInterfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Position, Range } from 'vscode';
import { DclAtom } from './dclAtom';
import { DclAttribute } from './dclAttribute';
import { DclTile } from './dclTile';

export interface IDclFragment {
readonly line: number;
readonly column: number;
readonly symbol?: string | undefined;
readonly flatIndex?: number | undefined;
readonly asTile?: DclTile | undefined;
readonly asAttribute?: DclAttribute | undefined;
readonly asContainer?: IDclContainer | undefined;
readonly isComment: boolean;
readonly isBlockComment: boolean;
readonly isString: boolean;
readonly range: Range;

equal(atom: IDclFragment): boolean;
contains(position: Position): boolean;
getAtomFromPosition(position: Position): IDclFragment;
}

export interface IDclContainer extends IDclFragment {
readonly atoms: Array<IDclFragment>;
readonly length: number;
readonly firstAtom: IDclFragment;
readonly lastAtom: IDclFragment;
readonly firstNonComment: IDclFragment;
getParentFrom(position: Position|IDclFragment, tilesOnly: boolean): IDclContainer;
flatten(into?: Array<DclAtom>): Array<DclAtom>;
}
Loading

0 comments on commit ca67cbd

Please sign in to comment.