diff options
author | 2021-03-16 16:08:11 -0400 | |
---|---|---|
committer | 2021-03-16 16:08:11 -0400 | |
commit | 588b086a4dc45e717341e18728fff65a90ddf46c (patch) | |
tree | fbabfff761add5217963a3d341d68392bea2cccf /src/parser.ts | |
parent | 174fc1d669fea1cf8be0d873fdb232fd0169abe6 (diff) | |
download | astro-588b086a4dc45e717341e18728fff65a90ddf46c.tar.gz astro-588b086a4dc45e717341e18728fff65a90ddf46c.tar.zst astro-588b086a4dc45e717341e18728fff65a90ddf46c.zip |
Bring compiler into Astro (#4)
* include source compiler
* Import from JS
* Conditionally use the instance contents
Co-authored-by: Fred K. Schott <fkschott@gmail.com>
Diffstat (limited to 'src/parser.ts')
-rw-r--r-- | src/parser.ts | 296 |
1 files changed, 0 insertions, 296 deletions
diff --git a/src/parser.ts b/src/parser.ts deleted file mode 100644 index d972e72ce..000000000 --- a/src/parser.ts +++ /dev/null @@ -1,296 +0,0 @@ -const [CHARS, TAG_START, TAG_END, END_TAG_START, EQ, EOF, UNKNOWN] = Array.from(new Array(20), (x, i) => i + 1); - -const voidTags = new Set(['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']); - -type Visitor = (tag: Tag) => Tag; - -interface State { - code: string; - index: number; - visitor: Visitor; - tagName?: string; -} - -interface Attribute { - name: string; - value?: string; - boolean: boolean; - start: number; - end: number; -} - -interface Text { - type: 0; - data: string; - start: number; - end: number; -} - -export interface Tag { - type: 1; - tagName: string; - attributes: Array<Attribute>; - children: Array<Tag | Text>; - void: boolean; - start: number; - end: number; -} - -interface Document { - children: Array<Tag | Text>; -} - -function stateChar(state: State) { - return state.code[state.index]; -} - -function stateNext(state: State) { - state.index++; - return stateChar(state); -} - -function stateRewind(state: State) { - state.index--; - return stateChar(state); -} - -function stateInBounds(state: State) { - return state.index < state.code.length; -} - -function createState(code: string, visitor: Visitor): State { - return { - code, - index: 0, - visitor, - }; -} - -function* _stringify(tag: Tag): Generator<string, void, unknown> { - yield '<'; - yield tag.tagName; - for (let attr of tag.attributes) { - yield ' '; - yield `"${attr.name}"`; - if (!attr.boolean) { - yield '='; - yield `"${attr.value}"`; - } - } - if (!tag.void) { - for (let child of tag.children) { - if (child.type === 0) { - yield child.data; - } else { - yield* _stringify(child); - } - } - } -} - -function stringify(tag: Tag) { - let out = ''; - for (let chunk of _stringify(tag)) { - out += chunk; - } - return out; -} - -function spliceSlice(str: string, index: number, count: number, add: string) { - // We cannot pass negative indexes directly to the 2nd slicing operation. - if (index < 0) { - index = str.length + index; - if (index < 0) { - index = 0; - } - } - - return str.slice(0, index) + (add || '') + str.slice(index + count); -} - -function replaceTag(state: State, tag: Tag) { - const origLen = tag.end - tag.start; - const html = stringify(tag); - const newLen = html.length; - const newCurIndex = tag.start + newLen; - - state.code = spliceSlice(state.code, tag.start, origLen, html); - state.index = newCurIndex; -} - -function consumeToken(state: State) { - do { - const c = stateNext(state); - - if (/\s/.test(c)) { - continue; - } - - if (c === '<') { - return TAG_START; - } - - if (c === '>') { - return TAG_END; - } - - if (c === '/') { - return END_TAG_START; - } - - if (/[a-zA-Z]/.test(c)) { - return CHARS; - } - - return UNKNOWN; - } while (stateInBounds(state)); - - return EOF; -} - -function consumeText(state: State): Text { - let start = state.index; - let data = ''; - let c = stateNext(state); - while (stateInBounds(state) && c !== '<') { - data += c; - c = stateNext(state); - } - - return { - type: 0, - data, - start, - end: state.index - 1, - }; -} - -function consumeTagName(state: State): string { - let name = ''; - let token = consumeToken(state); - while (token === CHARS) { - name += stateChar(state); - token = consumeToken(state); - } - return name.toLowerCase(); -} - -function consumeAttribute(state: State): Attribute { - let start = state.index; - let name = '', - token; - do { - name += stateChar(state).toLowerCase(); - token = consumeToken(state); - } while (token === CHARS); - - if (token !== EQ) { - stateRewind(state); - return { - name, - boolean: true, - start, - end: state.index - 1, - }; - } - - let value = ''; - do { - value += stateChar(state).toLowerCase(); - token = consumeToken(state); - } while (token === CHARS); - - return { - name, - value, - boolean: false, - start, - end: state.index - 1, - }; -} - -function consumeChildren(state: State): Array<Tag | Text> { - const children: Array<Tag | Text> = []; - - childLoop: while (stateInBounds(state)) { - const token = consumeToken(state); - switch (token) { - case TAG_START: { - const next = consumeToken(state); - if (next === END_TAG_START) { - consumeTagName(state); - consumeToken(state); // > - break childLoop; - } else { - stateRewind(state); - consumeTag(state); - } - break; - } - case CHARS: { - children.push(consumeText(state)); - break; - } - default: { - break; - } - } - } - - return children; -} - -function consumeTag(state: State): Tag { - const start = state.index - 1; - const tagName = consumeTagName(state); - const attributes: Array<Attribute> = []; - - let token = consumeToken(state); - - // Collect attributes - attrLoop: while (token !== TAG_END) { - switch (token) { - case CHARS: { - attributes.push(consumeAttribute(state)); - break; - } - default: { - break attrLoop; - } - } - - token = consumeToken(state); - } - - const children: Array<Tag | Text> = consumeChildren(state); - - const node: Tag = { - type: 1, - tagName, - attributes, - children, - void: voidTags.has(tagName), - start, - end: state.index - 1, - }; - - const replacement = state.visitor(node); - if (replacement !== node) { - replaceTag(state, node); - } - - return node; -} - -function consumeDocument(state: State): Document { - const children: Array<Tag | Text> = consumeChildren(state); - - return { - children, - }; -} - -export function preparse(code: string, visitor: Visitor) { - const state = createState(code, visitor); - consumeDocument(state); -} |