Skip to content

Instantly share code, notes, and snippets.

@yangtaeho
Last active January 29, 2018 18:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yangtaeho/37046e0eaebc451652aa9f1b6f0f2b72 to your computer and use it in GitHub Desktop.
Save yangtaeho/37046e0eaebc451652aa9f1b6f0f2b72 to your computer and use it in GitHub Desktop.
codeSpitz 74-3 attribute 파서 추가(장황주의)
// ==== space test ====
let logL = {};
logL.deb = false;
logL.inf = false;
const attrStrTest = 'class= "test clazz" readonly title=test!! class="test clazz" style= "width : 150px ;height:50px"';
//getAttrList(attrStrTest);
const getAttrList = (attrStr) => {
const TYPE_KEY = 'KEY';
const TYPE_VALUE = 'VAL';
const attr = JSON.stringify({key:'',value:''});
const itemTemp = JSON.stringify({type:'',value:''});
// ==== base method ========
const setItem = (type,val) => {
const item = JSON.parse(itemTemp);
item.type = type;
item.value = val;
return item;
};
const setKeyStack = (char,text,stack,cursor) => {
const tempCharList = text.split(' ');
if(tempCharList.includes("") || tempCharList.length > 1){
const keyList = tempCharList.filter((val)=>{
if(val == '') {
return false;
}
return val;
},[]);
keyList.map((val)=>{
logL.inf && console.log(cursor,'SET KEY s ====> ', char, val);
stack.push(setItem(TYPE_KEY,val));
},stack);
} else {
logL.inf && console.log(cursor,'SET KEY o ====> ', char, text);
stack.push(setItem(TYPE_KEY,text));
//cursor += text;
}
text = '';
return {char,text,stack,cursor};
};
/**
* 제거한 space 갯수만큼 cursor 를 전진시킨다.
*/
const getCorrectCursor = (str,cursor) => {
if (str[cursor] !== ' ') {
//logL.deb && console.log(cursor,'getCorrectCursor throw',str[cursor],str);
str = str.substring(cursor,str.length);
return {cursor,str};
}
for(let i = cursor; cursor < str.length; i++) {
//logL.deb && console.log(cursor,'getCorrectCursor befor',str,i,str.length);
if (str[i] === ' ') {
const tstr = str.substring(i,str.length);
//logL.deb && console.log(cursor,'getCorrectCursor ing..',tstr,i);
} else {
cursor = i;
str = str.substring(cursor,str.length);
//logL.deb && console.log(cursor,'getCorrectCursor break',str,i);
break;
}
}
//logL.deb && console.log(cursor,'getCorrectCursor done',str);
return {cursor:cursor,tempT:str};
};
const getAttrVal = (cursor,attrStr,delim) => {
//logL.deb && console.log(cursor,'getAttrVal req',attrStr[cursor],delim,"currAttr",attrStr.substring(cursor));
const currAttr = attrStr.substring(cursor+1, cursor = attrStr.indexOf(delim,cursor+1));
//logL.deb && console.log(cursor,'getAttrVal res',attrStr[cursor],delim,currAttr,attrStr.substring(cursor));
return {cursor,currAttr};
};
// ================
const parseKeyValStack = (attrStr) => {
let text = '';
let cursor = 0;
let stack = [];
while (cursor < attrStr.length) {
//console.log('attrStr[cursor] : ', attrStr[cursor]);
let char = attrStr[cursor];
if (char === '=') {
//logL.deb && console.log(cursor,'setKeyStack before', char, text/* , JSON.stringify(stack) */);
const v = setKeyStack(char,text,stack,cursor);
({char,text,stack,cursor} = v);
//logL.deb && console.log(cursor,'setKeyStack after', char, text/* , JSON.stringify(stack) */);
let nextChar = attrStr[++cursor];
//logL.deb && console.log(cursor,'get nextChar', nextChar);
// 공백인 경우에 대한 처리
var tempT, tempAddCursor;
if (nextChar === ' ') {
//공백 문자에 대해 커서 당기는 처리
//logL.deb && console.log(cursor,'getCorrectCursor befor',nextChar, attrStr[cursor]);
const v = getCorrectCursor(attrStr,cursor);
({cursor} = v);
nextChar = attrStr[cursor];
//logL.deb && console.log(cursor,'getCorrectCursor after',nextChar, attrStr[cursor]);
}
if (nextChar === '"' || nextChar === '\'') {
//logL.deb && console.log(cursor,'proc. quotation ====> ', char, nextChar, attrStr.substring(cursor, attrStr.length));
const v = getAttrVal(cursor,attrStr,nextChar);
const currAttr = v.currAttr;
cursor = v.cursor;
logL.inf && console.log(cursor,'SET VALUE """ ====> ', char, currAttr);
stack.push({type:TYPE_VALUE,value:currAttr});
} else {
//logL.deb && console.log(cursor,'proc. no quotation ====> ', char, nextChar, attrStr.substring(cursor, attrStr.length));
const nextCursor = attrStr.indexOf(' ',cursor);
const currAttr = attrStr.substring(cursor, nextCursor);
cursor = nextCursor;
logL.inf && console.log(cursor,'SET VALUE " " ====> ', char, currAttr);
stack.push({type:TYPE_VALUE,value:currAttr});
}
} else {
text += char;
}
cursor++;
}
return stack;
};
const main = (attrStr) => {
let attrs = []; //최종 결과 넣을 곳
const stack = parseKeyValStack(attrStr);
//console.log(stack);//logL.debug
for (let i = 0, len = stack.length; i < len; i++) {
const item = stack[i];
////logL.deb && console.log('최종 파스... item', i, item);
let res = JSON.parse(attr);
if (item.type == TYPE_VALUE) {
console.log('아마도 에러..?');
throw '형이 여기서 왜 나와?';
}
if (item.type == TYPE_KEY) {
res.key = item.value;
const nextItem = stack[i+1];
if (nextItem.type == TYPE_VALUE) {
res.value = nextItem.value;
i++;
} else {
res.value = true; //우항이 없는 속성은 true 처리
}
}
////logL.deb && console.log('최종 파스... res', i, res);
attrs.push(res);
}
logL.deb && console.log("result ============\n",attrs);
return attrs;
};
return main(attrStr);
};
// ==== for main test data ====
const deb = true; //debug
const cmd = 'c';
var dat = {
a:'<div>test</div>',
b:'<div>a<a>b</a>c<img/>d</div>',
c:'<div class="test clazz" readonly title=test!! style= "width : 150px ;height:50px">test</div>',
d:'<div style="width:150px;height:50px">test<div><img/>a<a>b</a>c<img/>d</div></div>',
e:'<div><div>test</div>a<img/>test<div>test2</div><div>test3</div><div>test4</div></div>',
};
//class="test clazz" readonly title=test!! style= "width : 150px ;height:50px"
// ================================================================================================
//logic
const setTextNode = (text,target) => {
if (text.legnth) {
//어딘가에 삽입..
target.push({type:'TEXT',text});
}
};
const parseAttr = (attrStr) => getAttrList(attrStr); //space.js
const setElementNode =(input, cursor, text, stack, childrenStacks) => {
let isBreak = false;
let char = input[cursor++];
if (char === '<') {
//시작하는 태그 구간
deb && console.log(cursor,', input[cursor]=',input[cursor]);
if (input[cursor] !== '/') {
setTextNode(text,stack.tag.children); // 기존 노드 처리..
text = ''; // 새로 시작하므로 리셋
let tagStr = input.substring(cursor, cursor = input.indexOf('>',cursor));
deb && console.log(cursor,', tagStr=',tagStr);
const hasAttr = tagStr.indexOf(' ') > -1;
let nodeNm = tagStr.substring(0, (hasAttr ? tagStr.indexOf(' ') : tagStr.length));
let attrStr = hasAttr ? tagStr.substring(tagStr.indexOf(' ') + 1, tagStr.length) : '';
let children = [];
// 닫힌 태그 처리 <img/> 등
const isClose = input[cursor - 1] == '/';
if (isClose) {
nodeNm = nodeNm.substr(0,nodeNm.length - 1);
children = null;
}
deb && console.log(cursor,', hasAttr=',hasAttr);
deb && console.log(cursor,', nodeNm=',nodeNm);
deb && console.log(cursor,', attrStr=',attrStr);
const tag = {name:nodeNm, type:'NODE', attr:parseAttr(attrStr), children:children};
cursor++;
stack.tag.children.push(tag);
if (!isClose) {
childrenStacks.push({tag,back:stack}); //현재 태그를 넣고 돌아갈 태그를 스택에 넣음
isBreak = true;
}
deb && console.log(cursor,', input[cursor]=',input[cursor]);
//let text = input.substring(cursor, input.indexOf('<',cursor)-1);
//text = input.substring(cursor, input.indexOf('<',cursor)-1);
} else {
const stackTagName = stack.tag.name;
const currTagName = input.substring(cursor + 1, cursor = input.indexOf('>',cursor));
deb && console.log(cursor, 'stackTagName', stackTagName, 'currTagName', currTagName);
if (stackTagName == currTagName) {
stack = stack.back;
} else {
console.log('??');
}
deb && console.log(cursor,'end tag ==> input[cursor]=',input[cursor],'text=',text);
}
} else {
text += char;
}
deb && console.log('==== run result ====\n' ,' cursor=',cursor ,',char=', char ,',text=',text ,'\n========================');
return {cursor, text, stack, isBreak};
};
const parser = input => {
const result = {tag:{type:'ROOT',children:[]}};
const childrenStacks = [];
let stack = result;
let cursor = 0;
deb && console.log('==== run start ====\n'
,' input=', input
,',input.length=', input.length
,'\n========================'
);
do {
let text = '';
while (cursor < input.length) {
const ele = setElementNode(input, cursor, text, stack, childrenStacks);
({cursor, text, stack} = ele);
if(ele.isBreak) {
break;
}
}
} while (stack = childrenStacks.pop());
return result;
};
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="attribute-parser.js"></script>
<script src="code.js"></script>
</head>
<body>
<div id="te">
</div>
<script>
function log(title,data){
const res = `
${JSON.stringify(data)}
`;
const brEle = document.createElement('BR');
document.getElementById('te').append('==== html ====');
document.getElementById('te').append(brEle.cloneNode());
document.getElementById('te').append(title);
document.getElementById('te').append(brEle.cloneNode());
document.getElementById('te').append('==== res ====');
document.getElementById('te').append(brEle.cloneNode());
document.getElementById('te').append(res);
document.getElementById('te').append(brEle.cloneNode());
document.getElementById('te').append(brEle.cloneNode());
};
function main(cmd){
document.getElementById('te').innerHTML = '';
if (cmd) {
log(dat[cmd],parser(dat[cmd]));
} else {
log(dat['a'],parser(dat['a']));
log(dat['b'],parser(dat['b']));
log(dat['c'],parser(dat['c']));
log(dat['d'],parser(dat['d']));
log(dat['e'],parser(dat['e']));
}
}
main(cmd);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment