• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

无信不立

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【词法分析】自定义动态语言处理框架

一、学习目的

项目研发过程中经常会需要将业务逻辑外置,需要将业务逻辑和代码分离。一般面对这样的需求有以下几种解决办法:

  • 引入一个规则引擎,比如Drools。
  • 利用java的javax.script.ScriptEngineManager调用javascript脚本。
  • 利用antlr这样的开源项目定义自己的业务领域语言。

 

二、词法分析

把程序中的词法单元分为四类:标识符(分为关键字和一般标识符)、数字、特殊字符、空白(空格、Tab、回车换行等)

 

三、参考sql语句的词法分析和语法分析

参考:https://segmentfault.com/a/1190000008120254

Druid 的代码里,代表语法分析和词法分析的类分别是 SQLParser 和 Lexer。并且, Parser 拥有一个 Lexer。

public class SQLParser {

    protected final Lexer lexer;

    protected String      dbType;

    public SQLParser(String sql, String dbType){
        this(new Lexer(sql), dbType);
        this.lexer.nextToken();
    }

    public SQLParser(String sql){
        this(sql, null);
    }

    public SQLParser(Lexer lexer){
        this(lexer, null);
    }

    public SQLParser(Lexer lexer, String dbType){
        this.lexer = lexer;
        this.dbType = dbType;
    }
}
View Code

Lexer 作为词法分析器,必然拥有其词汇表,在Lexer里,以 Keywords 表示。

Keywords 实际上是 key 为单词,value 为 Token 的字典型结构,其中 Token 是单词的类型,比如说,“select” 的 Token 类型就是 Select Token,而 “abc” 的 Token 类型,则是标识符,也表示为 Identifier Token。

而 MySqlLexer 类,除了沿用其父类的 Keywords 外,自己还有自己的 Keywords。可以理解为 Lexer 所维护的关键字集合,是通用的;而 MySqlLexer 除了有通用的关键字集合,也有属于 MySQL 数据库 SQL 方言的关键字集合。

Parser 是 Lexer 的使用者,站在 Parser 的角度看,它会怎么去使用 Lexer,或者说,Lexer 应该具备怎样的功能,才能满足 Parser 的使用需求。
Lexer 应该具备一个函数,能让使用者命令它解析一个单词,并且 Lexer 还必须提供一个函数,供使用者获取 Lexer 上一次解析到的单词以及单词的类型。
在 Lexer 中,nextToken() 这个方法提供了第一个需求,只要被调用,它就按顺序从 SQL 语句的开头到结尾,解析出下一个单词;token() 方法,则返回了上一次解析的单词的 Token 类型,如果 Token 类型是标识符(Identifier),Lexer 还提供了一个 stringVal() 方法,让使用者能拿到标识符的值。

走进 Lexer 的 nextToken() 方法,可以发现它的代码充斥着 if 语句和 switch 语句,因为解析单词的时候,是一个字符一个字符地解析,这就意味着,这个方法每次扫描一个字符,都必须判断单词是否结束,应该用什么方式来验证这个单词等等。这个过程,就是一个状态机运作的过程,每解析到一个字符,都要判断当前的状态,以决定应该进入下一个什么状态。

 

四、自定义动态语言处理框架

1、抽象的对象及作用

词法分析器

=>提供词汇表

=>提供语言解析

 

语法分析器

=>提供语法解析

=>构建语法树

 

语法树

=>一条自然语言构造成的语法树

 

语法树访问器

=>对语法树进行访问

 

2、各个角色之间的关系的伪代码

词法分析器

package com.spring.sxf.study.springtradedao.mysql.language;

/**
 * 词法分析器
 * 主要功能
 * 1、提供词汇表
 * 2、访问语句,提炼出词汇
 */
public interface Lexer {
    /**
     * 获取当前token
     * @return
     */
    Token token();

    /**
     * 寻找下一个token
     */
    void nextToken();

    /**
     * 获取被解析的语句
     * @return
     */
    String getText();

    /**
     * 设置要被解析的语句
     */
    void setText(String text);

}


package com.spring.sxf.study.springtradedao.mysql.language;

/**
 * 词汇表
 * 1、一般标识
 * 2、语法关键词汇
 */
public enum  Token {
}
View Code

语法分析器

package com.spring.sxf.study.springtradedao.mysql.language;

import java.util.ArrayList;
import java.util.List;

/**
 * 语法解析器
 * 主要功能
 * 1、根据语法访问词法分析器,组织语法树
 */
public class LanguageParse {
    /**
     * 根据词法分析器解析出语法树
     *
     * @param lexer
     * @return
     */
    List<Node> parse(Lexer lexer){
        //解析SelectColumnNode
        SelectColumnNode selectColumnNode=parseSelect(lexer);
        //解析FromTableNode
        FromTableNode fromTableNode=parseFrom(lexer);
        //组装语法数
        List<Node> nodes=new ArrayList<>();
        nodes.add(new DefaultNode(selectColumnNode,fromTableNode));
        return nodes;
    }


    SelectColumnNode parseSelect(Lexer lexer){
        //解析该子节点
        return null;
    }

    FromTableNode parseFrom(Lexer lexer){
        //解析该子节点
        return null;
    }
}
View Code

语法树及语法树的节点

package com.spring.sxf.study.springtradedao.mysql.language;



/**
 * 语法树的节点
 * 1、根据语法解析器解析出语法树
 * 2、允许访问者进行访问
 */
public interface Node {
    /**
     * 允许访问器访问语法树
      * @param visitor
     */
   void  accept(Visitor visitor);
}


package com.spring.sxf.study.springtradedao.mysql.language;

import java.util.List;

/**
 * 查询类名的节点
 */
public  abstract  class SelectColumnNode extends NodeImpl{
    /**
     * 获取查询的列名集合
     * @return
     */
    abstract  List<String> getColumns();

    @Override
    void accept0(Visitor visitor) {
        //获取所有列名
        visitor.visit(this);
    }
}

package com.spring.sxf.study.springtradedao.mysql.language;

import java.util.List;

/**
 * 查询表名的节点
 */
public abstract class FromTableNode extends NodeImpl {
    /**
     * 获取查询的表名集合
     *
     * @return
     */
    abstract List<String> getTableName();

    @Override
    void accept0(Visitor visitor) {
        visitor.visit(this);
    }
}


package com.spring.sxf.study.springtradedao.mysql.language;

/**
 * 模拟一个语法树
 * 该语法树有两个子节点:SelectColumnNode和FromTableNode
 */
public class DefaultNode extends NodeImpl {

    private SelectColumnNode selectColumnNode;

    private FromTableNode fromTableNode;

    public DefaultNode(SelectColumnNode selectColumnNode, FromTableNode fromTableNode) {
        this.selectColumnNode = selectColumnNode;
        this.fromTableNode = fromTableNode;
    }

    @Override
    void accept0(Visitor visitor) {
        //访问自身
       visitor.visit(this);

        //访问孩子节点
        selectColumnNode.accept(visitor);
        fromTableNode.accept(visitor);
    }
}
View Code

访问器

package com.spring.sxf.study.springtradedao.mysql.language;

import java.util.List;

/**
 * 模拟一个语法树访问器
 */
public class DefaultNodeVisitor implements Visitor {

    private List<String> selectCloumns;
    private List<String> tableNames;

    @Override
    public void preVisit(Node x) {
        //执行前置操作
    }

    @Override
    public void endVisit(Node x) {
        //执行后置操作

    }

    @Override
    public void visit(SelectColumnNode selectColumnNode) {
        //解析列
        this.selectCloumns = selectColumnNode.getColumns();
    }

    @Override
    public void visit(FromTableNode fromTableNode) {
        //解析表名
        this.tableNames = fromTableNode.getTableName();
    }

    @Override
    public void visit(DefaultNode defaultNode) {
        //解析语法树根节点
    }

    List<String> getCloumns() {
        return this.selectCloumns;
    }

    List<String> getTableNames() {
        return this.tableNames;
    }
}
View Code

代码示意

package com.spring.sxf.study.springtradedao.mysql.language;

import java.util.List;

/**
 * 模拟各个抽象对象之间的关系
 */
public class Test {

    public static void main(String[] args) {
        //要被解析的自然语言
        String text = "select a,b from t_order";

        //step1:建立词法分析器
        Lexer lexer = new Lexer() {
            @Override
            public Token token() {
                return null;
            }

            @Override
            public void nextToken() {

            }

            @Override
            public String getText() {
                return null;
            }

            @Override
            public void setText(String text) {

            }
        };

        lexer.setText(text);

        //step2:建立语法分析器&并解析出语法树
        LanguageParse languageParse=new LanguageParse();
        List<Node> nodes= languageParse.parse(lexer);

        //step3:访问语法树,确认表名和列名
        DefaultNodeVisitor visitor=new DefaultNodeVisitor();
        nodes.get(0).accept(visitor);

        //获取列名
        visitor.getCloumns();
        //获取表名
        visitor.getTableNames();
    }
}
View Code

 

posted on 2019-12-01 10:36  无信不立  阅读(500)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3