Skip to content

Instantly share code, notes, and snippets.

@expert0226
Last active February 13, 2020 08:31
Show Gist options
  • Save expert0226/00532bf7f646ed9618e70f21cc75fc76 to your computer and use it in GitHub Desktop.
Save expert0226/00532bf7f646ed9618e70f21cc75fc76 to your computer and use it in GitHub Desktop.
코드스파츠74 - 3일차 과제 제출: HTML Parser 에 Attribute 해석 기능 추가
// ===============
// === 개정 이력 ===
// ===============
// 2018-01-25 10:07
// 1. IIFE(Immediately Invoked Function Expression) 능력을 과시했으니, 이제 테스트 코드를 주석 걸기 쉽게하기 위해 명명된 함수로 변경
// 2018-01-24 20:07
// 1. textNode 함수를 순수 함수로 개선
// 2018-01-24 19:30
// 1. Attributes 를 children 에서 분리하여 별도의 배열로 저장
// 2. attrNode 함수를 순수 함수로 개선
// 주석? 선수(?!)끼리 왜 이러세요?
// 초보를 넘어 람보가 되고 싶다.
// 브라우저 개발자 도구에서 콘솔 지우기가 귀찮...
clear();
// 브라우저 개발자 도구에서 콘솔에서 "Uncaught SyntaxError: Identifier 'xxx' has already been declared" 가 귀찮...
{
'use strict';
const textNode = text => {
if(text && text.length) {
text = text.substr(text.indexOf('>') + 1);
text = text.trim();
return {type:'TEXT', text};
}
return null;
}
const attrNode = startTag => {
const attributes = [];
findIndexForFirstSpace = startTag.indexOf(" ");
if (findIndexForFirstSpace !== -1) {
const attrs = startTag.substr(findIndexForFirstSpace + 1).split(/['"] /g);
for(const attr of attrs) {
[name, text] = attr.split("=");
text = text.replace(/['"]/g, '')
attributes.push({type: 'ATTR', name, text});
}
}
return attributes;
}
const elementNode = (input, cursor, text, stack, stacks) => {
const char = input[cursor++];
let isBreak = false;
if (char === '<') {
if (!!text) {
const tx = textNode(text);
if(tx) stack.tag.children.push(textNode(text));
text = '';
}
if(input[cursor++] !== '/'){
let startTag = input.substring(cursor - 1, cursor = input.indexOf('>', cursor));
const isClose = input[cursor - 1] === '/';
if (isClose) {
startTag = startTag.substr(0, startTag.length - 1);
}
const findIndexForFirstSpace = startTag.indexOf(" ");
const name = findIndexForFirstSpace == -1? startTag: startTag.substr(0, findIndexForFirstSpace);
const tag = {name, type:'NODE', children:[]};
tag.attributes = attrNode(startTag);
cursor++;
stack.tag.children.push(tag);
if(!isClose){
stacks.push({tag, back:stack});
isBreak = true;
}
} else if (stack.tag.name == input.substring(cursor, input.indexOf('>', cursor))) {
stack = stack.back;
}
} else text += char;
return {cursor, text, isBreak, stack};
};
const parser = input => {
const result = {tag:{type:'ROOT', children:[]}}, stacks = [];
let cursor = 0, stack = result;
do {
let text = '';
while(cursor < input.length) {
const v = elementNode(input, cursor, text, stack, stacks);
({cursor, text, stack} = v);
if(v.isBreak) break;
}
} while(stack = stacks.pop());
return result.tag.children;
};
const testParser = _ => {
// ==========================
// === SI 업계의 전설같은 소문 ===
// ==========================
// 이거 보면 당장 도망가라
// 송금 먼저 전화 나중
// 유전주석 무전도망
// by http://emptydream.tistory.com/3757 [빈꿈님의 웹툰일기 필독 요망]
const testSet = [
`<div>test</div>`,
`<div>test<img/></div>`,
`<div>test<a>aa</a>bb</div>`,
`<div>
a
<a>b</a>
c
<img/>
d
</div>`,
`<div style="color: red">a</div>`,
`<div style="color: red; background: blue">a</div>`,
`<div style="color: red">a<span id="sp" class="span">b</span></div>`,
`<div id="base" style="width: 100%;">a<a>b</a>c<img/>d</div>`,
];
for (test of testSet) console.log(test, parser(test));
};
console.time("제발! 빨간 글씨 안 보이게 해주세요.\n총 테스트 시간");
testParser();
console.timeEnd("제발! 빨간 글씨 안 보이게 해주세요.\n총 테스트 시간");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment