js 如何用函数的方式解析if表达式?

作者站长头像
站长
· 阅读数 10

我有一个表达式:

( a || b && c ) && ( d || e )

abcde都是之后会进行解析的函数,类型是() => boolean,这个可以不用管

我想把这个表达式解析成一个变量,这样我可以用函数调用的形式来得到这个表达式的结果。

目前我想到的变量格式是这样的:

interface Expression {
  type: 'and' | 'or',
  children: Array<Expression>
}

const res: Expression = {
  type: 'and',
  children: [
    {
      type: 'or',
      children: [
        { type: 'and', children: [a] }
        { type: 'and', children: [b, c] }
      ]
    },
    {
      type: 'or',
      children: [d, e]
    }
  ]
}

type'and'的表达式children中的每个嵌套表达式都要为true,此时这个表达式才是truetype'or'的表达式则是children里只要有一个为true就行

现在我遇到的主要问题是括号没想到啥好办法去处理,因为我想要的表达式变量格式是按实际运算顺序排列的,但是原始的表达式是从左到右按书写顺序排列的

或者有啥更好的方式能做到说用函数的方式解析if表达式(不要evalnew Function

回复
1个回答
avatar
test
2024-07-15

简单的二元表达式解析,在线地址, evaluator我就不写了具体可以看一下我的这个项目代码

const text = "( a || b && c ) && ( d || e )";
enum TokenType {
  Identifier = "Identifier",
  And = "And",
  OR = "OR",
  LPAREN = "LPAREN",
  RPAREN = "RPAREN",
  EOF = "EOF"
}
class Token {
  constructor(
    public readonly type: TokenType,
    public readonly literal: string
  ) {}
}
type Expression = Token | BinaryExpression;
class BinaryExpression {
  constructor(
    public readonly left: Expression,
    public readonly operator,
    public readonly right: Expression
  ) {}
}

const lexer = (s: string): Token[] => {
  const tokens: Token[] = [];
  const n = s.length;
  let idx = 0;
  const isLetter = (c: string) => {
    if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) {
      return true;
    }

    return false;
  };
  const readIdent = (): string => {
    let start = idx;
    while (isLetter(s[idx])) {
      ++idx;
    }

    return s.slice(start, idx);
  };

  while (idx < n) {
    const c = s[idx];
    switch (c) {
      case " ":
      case "\t":
      case "\r":
      case "\n":
        ++idx;
        break;
      case "(":
        tokens.push(new Token(TokenType.LPAREN, "("));
        ++idx;
        break;
      case ")":
        tokens.push(new Token(TokenType.RPAREN, ")"));
        ++idx;
        break;
      case "|":
        ++idx;
        if (s[idx] === "|") {
          tokens.push(new Token(TokenType.OR, "||"));
          ++idx;
        } else {
          throw new Error(`lexer: Bad character '${s[idx]}' at index ${idx}`);
        }
        break;
      case "&":
        ++idx;
        if (s[idx] === "&") {
          tokens.push(new Token(TokenType.And, "&&"));
          ++idx;
        } else {
          throw new Error(`lexer: Bad character '${s[idx]}' at index ${idx}`);
        }
        break;
      default: {
        if (isLetter(c)) {
          const ident = readIdent();
          tokens.push(new Token(TokenType.Identifier, ident));
        }
      }
    }
  }

  tokens.push(new Token(TokenType.EOF, ""));

  return tokens;
};

const parser = (tokens: Token[]): Expression => {
  let idx = 0;

  const matchToken = (type: TokenType): Token => {
    if (tokens[idx].type !== type) {
      throw new Error(
        `expected SyntaxType ${type}, got=${tokens[idx].type} at index ${idx}`
      );
    }

    const token = tokens[idx];
    ++idx;
    return token;
  };

  const getBinaryOperatorPrecedence = (token: Token): number => {
    switch (token.type) {
      case TokenType.OR:
        return 1;
      case TokenType.And:
        return 2;
      default:
        return 0;
    }
  };

  const parsePrimaryExpression = (): Expression => {
    const token = tokens[idx];
    switch (token.type) {
      case TokenType.Identifier:
        ++idx;
        return token;
      case TokenType.LPAREN: {
        ++idx;
        const expr = parseBinaryExpression(0);
        matchToken(TokenType.RPAREN);
        return expr;
      }
      default:
        throw new Error(`ParseExpression: unexpected NodeType '${token.type}'`);
    }
  };

  const parseBinaryExpression = (parentPrecedence: number): Expression => {
    let left = parsePrimaryExpression();

    while (true) {
      const operator = tokens[idx];
      const precedence = getBinaryOperatorPrecedence(operator);
      if (precedence <= parentPrecedence) {
        break;
      }

      ++idx;
      const right = parseBinaryExpression(precedence);
      left = new BinaryExpression(left, operator, right);
    }

    return left;
  };

  return parseBinaryExpression(0);
};
const tokens = lexer(text);
const ast = parser(tokens);
console.log(ast);
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容