lua-parse

动手写个词法解析器,词法解析非常简单其关键处理问题在于

  • 字符串
  • 数字
  • 关键字与符号

定义关键字及符号映射

src=lua-lexer.js

src=lua-token.js

src=lua-parser.js

drawJson
function drawAttrs(attrs) {
  let attrStr = "<ul class='attrs'>";
  for (let { key, val } of attrs) {
    attrStr += `<li class='attr'>${key}:${val}</li>`;
  }
  return attrStr + "</ul>";
}
function drawNode(node,idx) {
  let name = "";
  if (node?.constructor?.name) {
    name = node.constructor.name;
  }
  let startTag = "";
  let endName = "";
  if (name === "Object") {
    startTag = "{";
    endName = "}";
  }
  if (name === "Array") {
    startTag = "[";
    endName = "]";
  }
  let mini = "";
  if (endName !== "") {
    mini = `<span class="mini"> ${Object.keys(node)} </span>`;
    endName = `<span>${endName}</span>`;
  }
  if (node._n) {
    name = `${node._n}:${name}`;
  }

  let keys = Object.keys(node);
  let attrs = [];
  let childrenToken = [];
  for (let key of keys) {
    if (typeof node[key] !== "object" || node[key] == null) {
      attrs.push({ key, val: node[key] });
    } else {
      Object.defineProperty(node[key], "_n", {
        enumerable: false,
        value: key,
      });
      childrenToken.push(node[key]);
    }
  }
  let attrStr = "";
  if (attrs.length) {
    attrStr = drawAttrs(attrs);
  }
  let childrenStr = "";
  if (childrenToken.length) {
    childrenStr = drawTree(childrenToken,idx);
  }
  const printLocation = (node) => {
    if (node instanceof Node) {
      return `onclick="selectCode(event,${idx})" data-start=${node.location?.start?.offset} data-end=${node.location?.end?.offset}`;
    }
  };

  return `<li class='node' data-active="false"   ${printLocation(
    node
  )}>${name}${startTag}${attrStr}${childrenStr}${mini}${endName}</li>`;
}

function drawTree(nodes,idx) {
  let children = [];
  for (let node of nodes) {
    children.push(drawNode(node,idx));
  }
  return `<ul class="tree"  >${children.join("\n")}</ul>`;
}

function toggle(e) {
  if (e.dataset["active"] === "true") {
    e.dataset["active"] = false;
  } else {
    e.dataset["active"] = true;
  }
}
function drawJson(obj, dom, idx) {
  let str = drawTree([obj], idx);
  dom.addEventListener(
    "click",
    (e) => {
      if (e.target.tagName === "LI") {
        toggle(e.target);
      }
    },
    false
  );
  dom.innerHTML = `<div>
  <button onclick="expandAll(${idx})">展开所有</button>
  <button onclick="foldAll(${idx})">折叠所有</button></div>
  ${str}`;
}
window.drawJson = drawJson;
function drawCode(code, dom, idx) {
  let str = `<textarea id="code-${idx}" class="code" rows=6 cols=60 readonly>${code}</textarea>`;
  dom.innerHTML = str;
}
function expandAll(idx) {
  let nodes = document
    .getElementById(`draw-${idx}`)
    .querySelectorAll('[data-active="false"]');
  Array.from(nodes).forEach((node) => {
    node.dataset["active"] = true;
  });
}
function selectCode(event,idx) {
  let start = event.target.dataset["start"];
  let end = event.target.dataset["end"];
  let input = document.getElementById(`code-${idx}`);
  input.focus();
  input.setSelectionRange(start, end);
}
function foldAll(idx) {
  let nodes = document
    .getElementById(`draw-${idx}`)
    .querySelectorAll('[data-active="true"]');
  Array.from(nodes).forEach((node) => {
    node.dataset["active"] = false;
  });
}
window.drawCode = drawCode;
window.expandAll = expandAll;
window.foldAll = foldAll;
window.selectCode = selectCode;
function draw(code, ast, idx) {
  let e = document.getElementById(`draw-${idx}`);
  e.classList.add("box");
  let c = document.createElement("div");
  let j = document.createElement("div");
  c.classList.add("code");
  j.classList.add("json");
  e.appendChild(c);
  e.appendChild(j);
  drawJson(ast, j, idx);
  drawCode(code, c, idx);
}
window.draw = draw;
let idx = arguments[0];
let code = `
...
nil
true
false
123
1.123
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
draw(
  code,
  [
    parser.parseExp0(),
    parser.parseExp0(),
    parser.parseExp0(),
    parser.parseExp0(),
    parser.parseExp0(),
    parser.parseExp0(),
  ],
  idx
);
let idx = arguments[0];
let code = `
function ()  end
function (...) end
function (a,b,c) end
function (d,e,...) end
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
draw(
  code,
  [
    parser.parseExp0(),
    parser.parseExp0(),
    parser.parseExp0(),
    parser.parseExp0(),
  ],
  idx
);
let idx = arguments[0];
let code = `
{}
{["a"]=123}
{["a"]=123,["b"]=456,c=123}
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
draw(code, [parser.parseExp0(), parser.parseExp0(), parser.parseExp0()], idx);
let idx = arguments[0];
let code = `

a.b
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
draw(code, [parser.parseExp0()], idx);
let idx = arguments[0];
let code = `
1 or 2 or 3
1 ^ 2 ^ 3
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
draw(code, [parser.parseExp(), parser.parseExp()], idx);
let idx = arguments[0];
let code = `
-- 注释
;
break
::label::
goto preLabel
while 1 do end
repeat until 1
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
draw(
  code,
  [
    parser.parseState(),
    parser.parseState(),
    parser.parseState(),
    parser.parseState(),
    parser.parseState(),
    parser.parseState(),
  ],
  idx
);
let idx = arguments[0];
let code = `
if true then end
if true then  elseif true then end
if true then  elseif true then  else  end
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
draw(
  code,
  [parser.parseState(), parser.parseState(), parser.parseState()],
  idx
);
let idx = arguments[0];
let code = `
for i=10,1 do end
for i=10,1,2 do end
for i,v in "a" do end
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
draw(
  code,
  [parser.parseState(), parser.parseState(), parser.parseState()],
  idx
);
let idx = arguments[0];
let code = `
local a = 123
local a,b=1,2
local a
local function f() end
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
draw(
  code,
  [
    parser.parseState(),
    parser.parseState(),
    parser.parseState(),
    parser.parseState(),
  ],
  idx
);
let idx = arguments[0];
let code = `
name[1]
name.name1
name:name1(1)(2)(3)
("name")[1]
("name").name1
("name"):name(1)
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
draw(
  code,
  [
    parser.parseExp(),
    parser.parseExp(),
    parser.parseExp(),
    parser.parseExp(),
    parser.parseExp(),
    parser.parseExp(),
  ],
  idx
);
let idx = arguments[0];
let code = `
;
break
::label::
goto label
do print('hello') end
while true do print('hello') end
repeat print('hello') until false
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
let ast =  parser.parse();
console.log(ast)
draw(code,ast, idx);
let idx = arguments[0];
let code = `
local function f() end
`;
let lexer = new Lexer(code);
let parser = new Parser(lexer);
let ast =  parser.parse();
console.log(ast)
draw(code,ast, idx);