wcc.js
Version:
Compiler for wxml and wxss files.
600 lines (574 loc) • 19.3 kB
JavaScript
const babelParser = require("@babel/parser");
const error = require('./error.js');
const babelTypes = require('@babel/types');
const util = require('./util.js');
const OUT_SEPARATOR = 'OUT_SEPARATOR';
const MEET_SEPARATOR_1 = 'MEET_SEPARATOR_1'; //{
const MEET_SEPARATOR_2 = 'MEET_SEPARATOR_2'; //{{
const IN_SEPARATOR = 'IN_SEPARATOR';
const MEET_SEPARATOR_3 = 'MEET_SEPARATOR_3'; // }
const MEET_SEPARATOR_4 = 'MEET_SEPARATOR_4'; // }}
function getSection(cItems, startIdx, endIdx) {
let start = cItems[startIdx];
let end = cItems[endIdx];
let setion = {
start: start,
end: end
};
let data = [];
for (let i = startIdx; i <= endIdx; ++i) {
data.push(cItems[i].c);
}
setion.str = data.join('');
return setion;
}
function text2Items(text = '', startLine = 1, startCol = 1) {
let cItems = [];
let baseLine = startLine;
let baseCol = startCol;
for (let i = 0; i < text.length; ++i) {
cItems.push({
c: text[i],
loc: {
line: baseLine,
col: baseCol
}
});
if (text[i] === '\n') {
baseLine++;
baseCol = 0;
}
baseCol++;
}
return cItems;
}
function _state_OUT_SEPARATOR(store) {
let item = store.cItems[store.index];
let c = item.c;
if (c === '{') {
store.state = MEET_SEPARATOR_1;
}
}
function _state_MEET_SEPARATOR_1(store) {
let item = store.cItems[store.index];
let c = item.c;
if (c === '{') {
store.state = MEET_SEPARATOR_2;
} else {
store.state = OUT_SEPARATOR;
}
}
function _state_MEET_SEPARATOR_2(store) {
let item = store.cItems[store.index];
let c = item.c;
if (store.index - 2 > store.sectionStart) {
let section = getSection(store.cItems, store.sectionStart, store.index - 3);
store.bindings.push({
type: 'static',
section: section
});
}
store.sectionStart = store.index;
store.meetDQ = false;
store.meetSQ = false;
if (c === '}') {
store.state = MEET_SEPARATOR_3;
} else {
store.state = IN_SEPARATOR;
store.index--;
}
}
function _state_IN_SEPARATOR(store) {
let item = store.cItems[store.index];
let c = item.c;
if (c === `\\`) {
//转义字符
store.index++;
if (store.index < store.cItems.length) {
store.index++;
}
} else if (c === `"`) {
if (!store.meetSQ) {
store.meetDQ = !store.meetDQ;
}
} else if (c === `'`) {
if (!store.meetDQ) {
store.meetSQ = !store.meetSQ;
}
} else if (c === ';') {
if (!store.meetDQ && !store.meetSQ) {
let option = store.option;
let message = ``;
let code = -1;
message = `unexpected \`${c}\``;
return makeWccError(option, {line: item.loc.line, col: item.loc.col}, code, message);
}
} else if( c === '='){
if(!store.meetDQ && !store.meetSQ){
let preItem = store.cItems[store.index - 1];
let postItem = store.cItems[store.index + 1];
if( ( preItem && ( preItem.c === '=' || preItem.c === '!' || preItem.c === '<' || preItem.c === '>' ) ) ){
// ==x 或者 !=x
}else if( (postItem && postItem.c === '=') ){
// x==
}else{
let option = store.option;
let message = ``;
let code = -1;
message = `unexpected \`${c}\``;
return makeWccError(option, {line: item.loc.line, col: item.loc.col}, code, message);
}
}
} else if (c === '}') {
if(!store.meetDQ && !store.meetSQ){
store.state = MEET_SEPARATOR_3;
}
}
}
function _state_MEET_SEPARATOR_3(store) {
let item = store.cItems[store.index];
let c = item.c;
if (c === '}') {
store.state = MEET_SEPARATOR_4;
} else {
store.state = IN_SEPARATOR;
}
}
function _state_MEET_SEPARATOR_4(store) {
let item = store.cItems[store.index];
let c = item.c;
if (store.index - 2 > store.sectionStart) {
let section = getSection(store.cItems, store.sectionStart, store.index - 3);
store.bindings.push({
type: 'bind',
section: section
});
}
store.sectionStart = store.index;
if (c === '{') {
store.state = MEET_SEPARATOR_1;
} else {
store.state = OUT_SEPARATOR;
store.index--;
}
}
function parseBinding(text = '', option = {}) {
if (!text) {
return [];
}
let startLine = option.loc.line || 1;
let startCol = option.loc.col || 1;
let path = option.path || '';
let cItems = text2Items(text, startLine, startCol);
//make the store
let store = {
text: text,
path: path,
state: OUT_SEPARATOR,
cItems: cItems,
bindings: [],
sectionStart: 0,
index: 0,
option: option,
meetDQ: false,
meetSQ: false
};
while (store.index < store.cItems.length) {
let item = store.cItems[store.index];
let res;
if (store.state === OUT_SEPARATOR) {
res = _state_OUT_SEPARATOR(store);
} else if (store.state === MEET_SEPARATOR_1) {
res = _state_MEET_SEPARATOR_1(store);
} else if (store.state === MEET_SEPARATOR_2) {
res = _state_MEET_SEPARATOR_2(store);
} else if (store.state === IN_SEPARATOR) {
res = _state_IN_SEPARATOR(store);
} else if (store.state === MEET_SEPARATOR_3) {
res = _state_MEET_SEPARATOR_3(store);
} else if (store.state === MEET_SEPARATOR_4) {
res = _state_MEET_SEPARATOR_4(store);
} else {
let message = `unknow expression state for ${text}`;
res = makeWccError(store.option, {line: item.loc.line, col: item.loc.col}, error.CODE.Z_PARSE, message);
}
if (res instanceof error.WccError) {
return res;
}
store.index++;
}
//剩余字符串处理
if (store.state === OUT_SEPARATOR) {
let section = getSection(store.cItems, store.sectionStart, store.index - 1);
store.bindings.push({
type: 'static',
section: section
});
} else if (store.state === MEET_SEPARATOR_1) {
let section = getSection(store.cItems, store.sectionStart, store.index - 1);
store.bindings.push({
type: 'static',
section: section
});
} else if (store.state === MEET_SEPARATOR_2) {
let section = getSection(store.cItems, store.sectionStart, store.index - 1);
let message = `did you forget }}, \' or \".\n`;
return makeWccError(store.option, {line: section.start.loc.line, col: section.start.loc.col}, -1, message);
} else if (store.state === IN_SEPARATOR) {
let section = getSection(store.cItems, store.sectionStart, store.index - 1);
let message = `did you forget }}, \' or \".\n`;
return makeWccError(store.option, {line: section.start.loc.line, col: section.start.loc.col}, -1, message);
} else if (store.state === MEET_SEPARATOR_3) {
let section = getSection(store.cItems, store.sectionStart, store.index - 1);
let message = `did you forget }}, \' or \".\n`;
return makeWccError(store.option, {line: section.start.loc.line, col: section.start.loc.col}, -1, message);
} else if (store.state === MEET_SEPARATOR_4) {
if (store.sectionStart < store.index - 2) {
let section = getSection(store.cItems, store.sectionStart, store.index - 3);
store.bindings.push({
type: 'bind',
section: section
});
}
}
return store.bindings;
}
exports.parse = function (text, option = {}) {
if (option.isStatic) {
option.node.isStatic = true;
//只能是静态的,直接返回
if(option.wcc.cmd.ds){
return `[3, '${util.escapeTxt(text)}'], [\"${option.path}\", ${option.loc.line}, ${option.loc.col}]`;
}else{
return `[3, '${util.escapeTxt(text)}']`;
}
}
let bindings = parseBinding(text, option);
if (bindings instanceof error.WccError) {
return bindings;
}
//没有数据绑定,长度为1,类型为static
if (bindings.length === 1 && bindings[0].type === 'static') {
option.node.isStatic = true;
if(option.wcc.cmd.ds){
return `[3, '${util.escapeTxt(bindings[0].section.str)}'], [\"${option.path}\", ${option.loc.line}, ${option.loc.col}]`;
}else{
return `[3, '${util.escapeTxt(bindings[0].section.str)}']`;
}
}
//存在数据绑定
let parseRes = [];
for (let i = 0; i < bindings.length; ++i) {
let exp = parseExp(bindings[i], option);
if (exp instanceof error.WccError) {
return exp;
}
parseRes.push(exp);
}
let res = ``;
if (parseRes.length > 1) {
//大于一个解析结果
option.node.isStatic = false;
res = `[a, ${parseRes.join(",")}]`;
} else if (parseRes.length === 1 && option.type === 'text') {
//一个文本节点
option.node.isStatic = false;
res = `[a, ${parseRes[0]}]`;
} else if (parseRes.length === 1 && !(option.type === 'text')) {
//一个非文本节点
option.node.isStatic = false;
res = parseRes[0];
} else {
// length 为0
option.node.isStatic = true;
res = `[3, '${util.escapeTxt(text)}']`;
}
if(option.wcc.cmd.ds){
res = `${res}, [\"${option.path}\", ${option.loc.line}, ${option.loc.col}]`;
}
return res;
}
function parseExp(binding, option) {
if (binding.type === 'static') {
return `[3, '${util.escapeTxt(binding.section.str)}']`;
} else if (binding.type === 'bind') {
let ast;
let sholdWrapAsObject = option.sholdWrapAsObject;
let path = option.path;
let txt = binding.section.str;
let loc = binding.section.start.loc;
try {
if (sholdWrapAsObject) {
txt = `obj = {${txt}}`
}
ast = babelParser.parse(txt, {
plugins: ['objectRestSpread']
});
if (sholdWrapAsObject) {
ast = ast.program.body[0].expression.right;
}
} catch (err) {
let message = err.message.replace(', expected ";"', '');
return makeWccError(option, loc, error.CODE.BABEL_PARSE_EXPRESSIO, message);
}
return walk(ast, binding, option, false);
} else {
return ``;
}
}
function makeWccError(option, loc = {
line: -1,
col: -1
}, code, message) {
if (option.type === 'attribute') {
message = `${option.path}:${option.node.name.start.loc.line}:${option.node.name.start.loc.col}: Bad attr \`${option.node.name.str}\` with message: ${message} at ${loc.line}:${loc.col}.\n`;
return (new error.WccError(code, message));
} else if (option.type === 'text') {
message = `${option.path}:${option.node.value.start.loc.line}:${option.node.value.start.loc.col}: Bad value with message: ${message} at ${loc.line}:${loc.col}.\n`;
return (new error.WccError(code, message));
}else{
message = `${option.path}:${option.node.value.start.loc.line}:${option.node.value.start.loc.col}: unknow binding type at ${loc.line}:${loc.col}.\n`;
return (new error.WccError(code, message));
}
}
function walk(node, binding, option, isStatic) {
if (node) {
if (babelTypes.isFile(node)) {
return walk(node.program, binding, option, isStatic);
} else if (babelTypes.isProgram(node)) {
if (node.body && node.body.length === 1) {
return walk(node.body[0], binding, option, isStatic);
} else if (node.body && node.body.length > 1) {
return makeWccError(option, {}, -1, 'too much expression');
} else if (node.directives && node.directives.length === 1) {
/**
{{"hello"}} 会被解析为directives属性的type为Directive的节点,节点的value的type为DirectiveLiteral
*/
return walk(node.directives[0], binding, option, isStatic);
} else if (node.directives && node.directives.length > 1) {
return makeWccError(option, {}, error.CODE.TOO_MUCH_AST_DIRECTIVES, 'too much expression');
} else {
return '';
}
} else if (babelTypes.isExpressionStatement(node)) {
return walk(node.expression, binding, option, isStatic);
} else if (babelTypes.isIdentifier(node)) {
if (isStatic) {
return `[3, "${node.name}"]`;
} else {
return `[[7],[3, "${node.name}"]]`;
}
} else if (babelTypes.isRegExpLiteral(node) || babelTypes.isBooleanLiteral(node) || babelTypes.isNumericLiteral(node)) {
return `[1, ${(node.value)}]`;
} else if(babelTypes.isNullLiteral(node)){
return `[1, null]`;
} else if (babelTypes.isStringLiteral(node)) {
return `[1, "${util.escapeTxt(node.value)}"]`;
} else if (babelTypes.isMemberExpression(node)) {
/*
对象取属性值: a.b 或者 a['b']
computed: 属性是否需要求值
*/
let objectRes = walk(node.object, binding, option, isStatic);
if (objectRes instanceof error.WccError) {
return objectRes;
}
let propertyRes = walk(node.property, binding, option, !node.computed);
if (propertyRes instanceof error.WccError) {
return propertyRes;
}
return `[[6],${objectRes},${propertyRes}]`;
} else if (babelTypes.isBinaryExpression(node)) {
/*
二元表达式
*/
let leftRes = walk(node.left, binding, option, isStatic);
if (leftRes instanceof error.WccError) {
return leftRes;
}
let rightRes = walk(node.right, binding, option, isStatic);
if (rightRes instanceof error.WccError) {
return rightRes;
}
return `[[2, "${node.operator}"], ${leftRes}, ${rightRes}]`;
} else if (babelTypes.isLogicalExpression(node)) {
/*
逻辑表达式
*/
let leftRes = walk(node.left, binding, option, isStatic);
if (leftRes instanceof error.WccError) {
return leftRes;
}
let rightRes = walk(node.right, binding, option, isStatic);
if (rightRes instanceof error.WccError) {
return rightRes;
}
return `[[2, "${node.operator}"],${leftRes},${rightRes}]`;
} else if (babelTypes.isUnaryExpression(node)) {
/*
一元表达式,只有一个操作数
*/
let argumentRes = walk(node.argument, binding, option, isStatic);
if (argumentRes instanceof error.WccError) {
return argumentRes;
}
return `[[2, "${node.operator}"], ${argumentRes}]`;
} else if (babelTypes.isArrayExpression(node)) {
/*
数组
第一项是[4]
第二项是一个数组,数组第一项是[5],
*/
if (node.elements.length) {
//数组长度可能为1, 2, 3...
let arrRes = node.elements.reduce(function (preRes, curNode) {
if (preRes instanceof error.WccError) {
return preRes;
} else {
let curRes = walk(curNode, binding, option, isStatic);
if (curRes instanceof error.WccError) {
return curRes;
} else {
return `[[5], ${preRes} ${preRes && ','}${curRes}]`;
}
}
}, '');
if (arrRes instanceof error.WccError) {
return arrRes;
}
return `[[4], ${arrRes}]`;
} else {
//数组为空
return `[[4], [[5]]]`;
}
} else if (babelTypes.isSpreadElement(node)) {
/**
template的析构赋值
<template is="msgItem" data="{{...item}}" />
*/
let argumentRes = walk(node.argument, binding, option, isStatic);
if (argumentRes instanceof error.WccError) {
return argumentRes;
}
return `[[10], ${argumentRes}]`;
} else if (babelTypes.isConditionalExpression(node)) {
/**
三元运算符 a ? b : c
*/
let consequentRes = walk(node.consequent, binding, option, isStatic);
if (consequentRes instanceof error.WccError) {
return consequentRes;
}
let alternateRes = walk(node.alternate, binding, option, isStatic);
if (alternateRes instanceof error.WccError) {
return alternateRes;
}
let testRes = walk(node.test, binding, option, isStatic);
if (testRes instanceof error.WccError) {
return testRes;
}
return `[[2,'?:'],${testRes},${consequentRes},${alternateRes}]`;
} else if (babelTypes.isObjectExpression(node)) {
/**
对象表达式
*/
if (node.properties.length === 1) {
/**
只有一个属性,继续walk,最后返回的是[[8], 'xxx', xxx]
*/
return walk(node.properties[0], binding, option, isStatic);
} else if (node.properties.length > 1) {
/**
多于一个属性,返回[[9], [xxx], [xxx]]
*/
let res = ''
let props = node.properties || [];
//先取前两个
let prop0Res = walk(props[0], binding, option, isStatic);
if (prop0Res instanceof error.WccError) {
return prop0Res;
}
let prop1Res = walk(props[1], binding, option, isStatic);
if (prop1Res instanceof error.WccError) {
return prop1Res;
}
res = `[[9], ${prop0Res}, ${prop1Res}]`;
//大于两个,后面继续合并为依赖
for (let i = 2, len = props.length; i < len; i++) {
let propRes = walk(props[i], binding, option, isStatic);
if (propRes instanceof error.WccError) {
return propRes;
}
res = `[[9], ${res}, ${propRes}]`;
}
return res;
} else {
return ``;
}
} else if (babelTypes.isDirective(node)) {
return walk(node.value, binding, option, isStatic);
} else if (babelTypes.isThisExpression(node)) {
/**
this表达式
*/
return `[[7], [3, 'this']]`;
} else if (babelTypes.isObjectProperty(node)) {
/**
对象属性
*/
if (node.key.name && typeof node.value === 'object') {
let valueRes = walk(node.value, binding, option, isStatic);
if (valueRes instanceof error.WccError) {
return valueRes;
}
return `[[8], "${node.key.name}", ${valueRes}]`;
} else {
return ``;
}
} else if (babelTypes.isDirective(node)) {
return walk(node.value, binding, option, isStatic);
} else if (babelTypes.isDirectiveLiteral(node)) {
return `[1, "${node.value}"]`;
} else if (babelTypes.isCallExpression(node)) {
/**
* 函数调用,例如wxs函数调用
* 调用的函数
* 函数的参数为一个数组
*/
let callee = node.callee;
let arguments = node.arguments || [];
let res = ``;
let calleeRes = walk(callee, binding, option, isStatic);
if (calleeRes instanceof error.WccError) {
return calleeRes;
}
if (arguments.length) {
let argumentsRes = arguments.reduce((preRes, curNode) => {
if (preRes instanceof error.WccError) {
return preRes;
}
let curRes = walk(curNode, binding, option, isStatic);
if (curRes instanceof error.WccError) {
return curRes;
}
return `[[5], ${preRes} ${preRes && ','}${curRes}]`;
}, '');
if (argumentsRes instanceof error.WccError) {
return argumentsRes;
}
res = `[[12], ${calleeRes}, ${argumentsRes}]`;
} else {
res = `[[12], ${calleeRes}, [ [5] ] ]`;
}
return res;
} else {
//非法的表达式
return makeWccError(option, {
line: binding.section.start.loc.line + node.loc.start.line - 1,
col: binding.section.start.loc.col + node.loc.start.column
}, error.CODE.WRONG_EXPRESSION_TYPE, 'unknow expression');
}
} else {
return ``;
}
}