factory-transpiler
Version:
Factory Transpiler for HTML
232 lines (215 loc) • 9.12 kB
text/typescript
import { IterableStream } from './iterable-stream';
import { Token, TokenType } from './tokenizer';
import os = require('os');
import { DomObject, StringBuilder } from './dom-object';
import { createContentTag, createTextObject, createSingtonTag } from './dom-object';
import { FactoryMismatchException, FactorySyntaxException, FactoryTokenException } from './exceptions';
export function createSyntaxTree(stream: IterableStream<Token>): DomObject {
const objects: DomObject[] = createDomObjects(stream, TokenType.EOF);
function debug(buffer: StringBuilder) {
buffer.append('DOM');
buffer.append(os.EOL);
for (let i = 0; i < objects.length; i++) {
const next: DomObject = objects[i];
if (i < objects.length - 1) {
next.buildTreeModel(buffer, '├── ', '│ ');
} else {
next.buildTreeModel(buffer, '└── ', ' ');
}
}
return buffer.toString();
}
return {
buildElement: () => {
let content: string = '';
objects.forEach((ref) => {
content += ref.buildElement() + os.EOL;
});
return content;
},
buildTreeModel: (stringBuilder) => debug(stringBuilder),
};
}
function createDomObjects(stream: IterableStream<Token>, returnOn: TokenType = TokenType.BRACES_CLOSED): DomObject[] {
const tags: DomObject[] = [];
let wasExited = false;
while (stream.hasEntriesLeft()) {
const tag: Token = stream.step();
if (tag == undefined) {
throw new FactoryTokenException(stream, 'found null, but expected tag, string or closing braces');
return tags;
} else if (tag.type === TokenType.TAG) {
const bracket: Token = stream.step();
if (bracket == undefined) {
throw new FactoryTokenException(stream, null, TokenType.PARENTHESES_OPEN);
}
if (bracket.type === TokenType.PARENTHESES_OPEN) {
const attributes: Token[] = convertToAttributes(stream);
const closingBracket: Token = stream.getCurrentEntry();
if (closingBracket == undefined) {
throw new FactoryTokenException(stream, null, TokenType.PARENTHESES_CLOSED);
}
if (closingBracket.type === TokenType.PARENTHESES_CLOSED) {
const braces: Token = stream.step();
if (braces != undefined && braces.type === TokenType.BRACES_OPEN) {
tags.push(createContentTag(tag, attributes, createDomObjects(stream)));
} else {
tags.push(createSingtonTag(tag, attributes));
if (braces != undefined) stream.stepBackwards();
}
} else {
throw new FactoryTokenException(stream, closingBracket, TokenType.PARENTHESES_CLOSED);
}
} else {
throw new FactoryTokenException(stream, bracket, TokenType.PARENTHESES_OPEN);
}
} else if (tag.type === returnOn) {
return tags;
} else if (tag.type === TokenType.STRING) {
tags.push(createTextObject(tag));
} else {
throw new FactoryTokenException(
stream,
'found ' + TokenType[tag.type] + ', but expected tag, string or closing braces'
);
}
}
throw new FactoryMismatchException(
stream,
'some tokens to parse where left, check your opening and closing braces'
);
}
function convertToAttributes(stream: IterableStream<Token>): Token[] {
const tokens: Token[] = [];
while (stream.hasEntriesLeft()) {
const token: Token = stream.step();
if (token == undefined) {
throw new FactoryTokenException(stream, null, TokenType.PARENTHESES_CLOSED);
}
if (token.type === TokenType.PARENTHESES_CLOSED) {
return tokens;
}
tokens.push(convertToAttribute(stream));
}
throw new FactorySyntaxException(stream, 'failed to find closing parentheses');
}
function convertToAttribute(stream: IterableStream<Token>): Token {
const start = stream.getCurrentEntry();
if (start == undefined) {
throw new FactoryTokenException(stream, 'found null, but expected opening bracket, tag or opening parentheses');
}
const startIndex = stream.index();
if (start.type === TokenType.BRACKET_OPEN) {
const content = stream.step();
if (content != undefined && content.type === TokenType.TAG) {
const closing = stream.step();
if (closing != undefined && closing.type === TokenType.BRACKET_CLOSED) {
const assign = convertToAttributeContent(stream);
let suffix = '';
if (assign) {
suffix = '=' + assign.data;
}
return {
type: TokenType.ATTRIBUTE,
data: '[' + content.data + ']' + suffix,
start: startIndex,
end: stream.index(),
};
} else {
throw new FactoryTokenException(stream, closing, TokenType.BRACKET_CLOSED);
}
} else {
throw new FactoryTokenException(stream, content, TokenType.TAG);
}
} else if (start.type === TokenType.PARENTHESES_OPEN) {
const content = stream.step();
if (content != undefined && content.type === TokenType.TAG) {
const closing = stream.step();
if (closing != undefined && closing.type === TokenType.PARENTHESES_CLOSED) {
const assign = convertToAttributeContent(stream);
let suffix = '';
if (assign) {
suffix = '=' + assign.data;
}
return {
type: TokenType.ATTRIBUTE,
data: '(' + content.data + ')' + suffix,
start: startIndex,
end: stream.index(),
};
} else {
throw new FactoryTokenException(stream, closing, TokenType.PARENTHESES_CLOSED);
}
} else {
throw new FactoryTokenException(stream, content, TokenType.TAG);
}
} else if (start.type === TokenType.TAG) {
const assign = convertToAttributeContent(stream);
let suffix = '';
if (assign) {
suffix = '=' + assign.data;
}
return {
type: TokenType.ATTRIBUTE,
data: start.data + suffix,
start: startIndex,
end: stream.index(),
};
} else if (start.type === TokenType.ASTERISK) {
const content = stream.step();
if (content != undefined && content.type === TokenType.TAG) {
const assign = convertToAttributeContent(stream);
let suffix = '';
if (assign) {
suffix = '=' + assign.data;
}
return {
type: TokenType.ATTRIBUTE,
data: '*' + content.data + suffix,
start: startIndex,
end: stream.index(),
};
} else {
throw new FactoryTokenException(stream, content, TokenType.TAG);
}
} else if (start.type === TokenType.HASH) {
const content = stream.step();
if (content != undefined && content.type === TokenType.TAG) {
const assign = convertToAttributeContent(stream);
let suffix = '';
if (assign) {
suffix = '=' + assign.data;
}
return {
type: TokenType.ATTRIBUTE,
data: '#' + content.data + suffix,
start: startIndex,
end: stream.index(),
};
} else {
throw new FactoryTokenException(stream, content, TokenType.TAG);
}
} else {
throw new FactoryTokenException(
stream,
`found ${TokenType[start.type]}, but expected opening bracket, tag or opening parentheses`
);
}
}
function convertToAttributeContent(stream: IterableStream<Token>): Token | undefined {
const assign = stream.step();
if (assign == undefined) {
throw new FactoryTokenException(stream, null, TokenType.ASSIGN);
}
if (assign.type === TokenType.ASSIGN) {
const string = stream.step();
if (string != undefined && string.type === TokenType.STRING) {
return string;
} else {
throw new FactoryTokenException(stream, string, TokenType.STRING);
}
} else {
stream.stepBackwards();
return undefined;
}
}