当前位置: 首页 > news >正文

Hive-技术补充-ANTLR语法编写

一、导读

我们学习一门语言,或外语或编程语言,是不是都是要先学语法,想想这些语言有哪些相同点

        1、中文、英语、日语......是不是都有 主谓宾 的规则

        2、c、java、python、js......是不是都有 数据类型 、循环 等语法或数据结构

虽然人们在过去的几十年里发明了多种编程语言,但基本的语言模式并不多。因为他们在设计时都会情不自禁的与自己脑海中的自然语言关联,并用数学符号表达了出来。因此如果你掌握了一门语言后再去学其他语言就会变的容易。总结下来有四种抽象的计算机语言模式:

        1、序列:即一列元素,例如一个数组初始化时给的一串值

        2、选择:在多种可选方案中做出选择,例如编程语言中的if语句

        3、词法符号依赖:一个词法符号往往成对出现,比如{} () []

        4、嵌套结构:一种自相似的语言结构,例如编程语言中的if嵌套 for嵌套

为了表达以上模式,我们先了解下巴克斯-诺尔范式(Backus Normal Form简称BNF)

二、巴克斯-诺尔范式

是一种用于表示上下文无关文法的语言,它是由约翰·巴科斯(John Backus)和彼得·诺尔(Peter Naur)首先引入的用来描述计算机语言语法的符号集。

几乎所有程序设计语言都是通过上下文无关文法来定义的。巴克斯-诺尔范式是上下文无关语法的一个例子。

而且上下文无关文法足够简单可以构造有效的分析算法来检验一个给定字串是否是由某个上下文无关文法产生,例如 LR 分析器和 LL 分析器。

三、从编程语言中提取语法

可以形象的理解为就像从一堆字符串中抽取一系列正则表达式来把他们完全表达出来,只是语法比正则表达式更丰富。

讨论语法的整体结构以及如何建立初始的语法框架,是任何编程语言项目的基础步骤。

语法由一个为该语法命名的头部定义和一些列可以相互引用的语言规则组成,例如:

grammar MyG;
rule1 : 《stuff》;
rule2 : 《more stuff》;
......

tuff 和 more stuff 为语言规则

下面我们来表达下常用的四种语言模式

1、序列模式

可以直接使用 类似 'RETR'的常量字符串来表示任意简单字符序列,如关键字、标点符号等
我们使用语法规则来为编程语的特定结构命名,就像我们编程时为多次相同功能命名的函数

+ 字符串表示一个或多个元素的情况

* 字符串表示0个或多个元素的情况

file : (row '\n')* ; //以一个 '\n' 作为终止符的序列
row : field(',',field)* ;  //以一个 ',' 作为分隔符的序列
field:INT ; //假设字段都是整数

file 规则使用带终止符的序列模式来匹配0或多个row\n 序列
row 规则使用带分隔符的序列来匹配一个field 后面跟着0个或多个','field 序列的情形
','隔开了所有field 。rwo规则匹配类似 1、1,2 以及 1,2,3 的序列 

2、选择模式

使用 | 表达编程语言中的选择模式,可以分割多个可选的语法结构,例如:

filed : INT | STRING ;

type: 'float' | 'int' | 'void'

stmt : node_stmt

        | edge_stmt

        | attr_stmt

        | id '=' id

        | subgraph

        ;

3、词法符号依赖模式

举个例子,如果我们在某个语句中看到了 [ ,就必须在同一个语句中找到与它匹配的 ] ,
通常[] 会把其他元素包裹起来,比如数组的初始化声明。
可以这样表示 :

vector : '[' INT+ ']' ;

4、嵌套模式

如果一条规则的定义中需要用到它自身,我们就需要一条嵌套规则,比如递归规则。下面时一条while循环的嵌套规则

stat : 'while' '(' expr ')' stat //匹配while语句
    | '{' stat* '}'                 //匹配 {} 中若干条语句组成的代码
    ......                         //其他语句
    ;

四、优先级处理

我们先来写一个包含乘法和加法的语法:

expr : expr '*' expr //匹配由 '*' 运算符连接的子表达式
    | expr '+' expr  //匹配由 '+' 运算符连接的子表达式
    | INT
    ;

当处理 1 + 1 和 2*2 时没有问题,但是当处理 1+1*2 时就会由问题,

ANTLR 通过优先选择位置靠前的备选分支来解决歧义问题,这隐式的运行我们指定运算符优先级

尽管如此,一些运算符(例如指数运算符)是从右向左结合的,所以我们需要在这样的运算符上使用assoc选项手工指定结合性,这样,输入的 2^3^4就能够被正确解释为 2^(3^4)

expr : <assoc=right> expr '^' expr //运算符是右结合的
    | INT
    ;

此外还可以使用优先级上升来解决这个问题

五、常见的词法结构

在词法角度上,不同编程语言的外观十分相似。也就是我们只需要描述标识符和整数一次,稍加改动,就可以将它运用于大多数编程语言中。

词法分析器也是使用不同的规则来描述繁多的语言结构。和语法分析器不同的是它是通过输入的字符流来识别特定语言结构

ANTLR运行词法规则和语法规则写在同一个文件中。不过词法分析和语法分析是两个不同的阶段,ANTLR是通过这种方式区分的:词法规则以大写字母开头,语法规则以小写字母开头。

例如 ID 是要给词法规则 expr 是一个语法规则

下面让我们一起来构造些常见词法符号的词法规则:

1、匹配标识符

ID : ('a'..'z'|'A'..'Z')+ ; //匹配1个或多个大小写字母
ID : [a-zA-Z]+ ; //匹配1个或多个大小写字母

有时不同的规则会起到冲突,比如

grammar KeywordTest;
enumDef : 'enum' '{'...'}';
......
FOR : 'for';
ID : [a-zA-Z]+ ; //不会匹配'enum'和'for'

按照语法ID规则也可以匹配到'enum'和'for' ,ANTLR词法分析器解决歧义的方式优先使用靠前的词法规则。

2、匹配数字

INT : '0'..'9'+; //匹配1个或多个数字
INT : [0-9]+;     //匹配1个或多个数字

不过浮点数要复杂的多:

FLOAT : DIGIT+'.' DIGIT* //匹配 1.39 3.1569 等...
    | '.' DIGIT+          //匹配 .1 .3654 等...
    ;
    
fragment
DIGIT : [0-9]; //匹配单个数字

这里我们使用了一条辅助规则 DIGIT,这样就不用重复书写了。将一条规则声明为fragment可以告诉ANTLR该规则不是词法符号,它只会被其他词法规则使用。

3、匹配字符串常量

STRING : '"' .*? '"'; //匹配"..." 间的任意文本

其中 . 匹配任意的单个字符。*表示0或多个字符 ? 是通过正则表达式提供了对贪婪子规则的支持

但是还不够完善,因为字符串中不应该再出现双引号。如果要加双引号必须加上转义符 \
 

STRING : '"' (ESC|.)*? '"';
fragment
ESC : '\\"' | '\\\\' ; //双字符序列 \" 和 \\

4、匹配注释和空白字符

当词法分析器匹配到我们定义的规则后,会把这些词法符号放入词法符号流,输送给语法分析器。

语法分析器检查词法符号流的语法结构,但是如果其中有注释或空白字符时,会十分容易出错,因此我们需要在词法匹配时就将它去除

assign : ID (WS|COMMENT)? '=' (WS|COMMENT)? expr (WS|COMMENT) ? ;

我们再使用 skip 指令通知词法分析为i将它丢弃
LINE_COMMENT : '//' .*? '\r'? '\n' -> skip ; //匹配 "//" 任意字符序列 '\n' 
COMMENT : '/*' .*? '*/' -> skip ; //匹配 "/*" 任意字符序列 '*/' 

注意windows上的换行符是 '\r\n' 而 linux上的换行符是 '\n' (因此注意windows上的文本文件上传到linux后每行会多一个\r符号)

另外词法分析器可以接收多种位于 -> 之后的指令 ,skip 只是其中一种 ,例如 channel 指令代表一个隐藏通道 

下面我们看下 空白字符的处理 

大多数编程语言将空白字符看作词法符号间的分隔符,并将它们忽略 比如 int a = 1 ; 

(python是个例外,它使用空白字符来表达某些语法的目的:换行符代表一行命令的终止,特定数量的缩进指明嵌套的层级)

下列规则告诉 ANTLR 丢弃空白字符

WS : (' '|'\t'|'\r'|'\n')+ -> skip ; //匹配一个或多个空白字符并将它们丢弃

WS : [ \t\r\n]+ -> skip ; //匹配一个或多个空白字符并将它们丢弃

六、词法分析器和语法分析器的界限

词法和语法规则在一份文件中,且词法规则可以包含递归,这使得词法分析器变的和语法分析器一样强大,甚至可以匹配一些语法结构。

划定词法分析器和语法分析器的界限不仅是语言的职责,更是语言编写的应用程序的职责。

词法分析器应该匹配并丢弃任何语法分析器无须知晓的东西。比如注释和空白字符。

由词法分析器来匹配类似标识符、关键字、字符串、数字的常见词法符号。语法分析器的层级更高,所以我们不应当让它处理将数字组合成整数这样的事情,
这会加重它的负担。

将语法分析器无需区分的词法结构归为同一个词法类型符号。

例如:如果我们的程序对待整数和浮点数的方式是一致的,那就可以把它们都归纳为 NUMBER 类型的词法符号。

同样的将可以以相同方式处理的实体归为一类,

例如:如果语法分析器不关心xml的内容,词法分析器就可以将<>中的所有内容归为一个名为 TAG 的词法符号类型

相反,如果语法分析器需要把一种类型的文本拆开处理,那么词法分析器就应该将它的各个组成部分作为独立的词法符号输送个语法分析器

语法分析器和词法分析器是灵活的,受我们支配的,我们可以根据需求的不同来规定他们的功能和界限

比如我们有这样一个需求:

我们在代理服务器上有一份日志文件(ip 请求方法 状态码),日志内容如下
192.168.56.239 "GET /download/foo.html HTTP/1.0" 200
......

当我们大脑看到这些信息后很自认的就可以想出很多统计逻辑,不过我们的需求只是统计行数,

那我们就可以忽略到里面每行的的内容,只关注换行符即可

语法文件部分内容如下:
file : NL+ ;                     //匹配换行符序列的语法分析器
STUFF: ~'\n'+ -> skip ; //除 '\n' 之外的字符全部丢掉
NL : '\n' ;                       //将设定的换行符返回给语法分析器或者其他的调用者

接下来我们增加一个需求:

从日志文件中提取ip地址列表,这意味着我们需要一条匹配ip地址的词法规则,最好还有一些词法规则来匹配一行记录中的其他元素

语法文件部分内容如下:

file : row ;                        //匹配日志文件中全部行的语法规则
row : ip STRING INT NL ;            //匹配日志文件中的一行记录

IP : INT '.' INT '.' INT '.' INT ;  //匹配ip 例如 192.168.56.239
INT : [0-9]+ ;                         //匹配ip地址中的一个字节或者http的状态码
STRING : '"' .*? '"' ;                 //匹配http请求方法
NL : '\n';                            //匹配一行记录的终止符
WS : ' ' -> skip ;                    //忽略空格

我们也可以把IP词法规则转换成ip语法规则,只需要转换成小写即可

file : row ;                        //匹配日志文件中全部行的语法规则
row : ip STRING INT NL ;            //匹配日志文件中的一行记录
ip : INT '.' INT '.' INT '.' INT ;  //匹配ip 例如 192.168.56.239


INT : [0-9]+ ;                         //匹配ip地址中的一个字节或者http的状态码
STRING : '"' .*? '"' ;                 //匹配http请求方法
NL : '\n';                            //匹配一行记录的终止符
WS : ' ' -> skip ;                    //忽略空格

相关文章:

Hive-技术补充-ANTLR语法编写

一、导读 我们学习一门语言&#xff0c;或外语或编程语言&#xff0c;是不是都是要先学语法&#xff0c;想想这些语言有哪些相同点 1、中文、英语、日语......是不是都有 主谓宾 的规则 2、c、java、python、js......是不是都有 数据类型 、循环 等语法或数据结构 虽然人们在…...

6.使用个人用户登录域控的成员服务器,如何防止个人用户账号的用户策略生效?

&#xff08;1&#xff09;需求&#xff1a; &#xff08;2&#xff09;实战配置步骤 第一步:创建新的策略-并编辑策略 第二步&#xff1a;将策略应用到服务器处在OU 第三步&#xff1a;测试 &#xff08;1&#xff09;需求&#xff1a; 比如域控&#xff0c;或者加入域的…...

模拟算法

例题一 算法思路&#xff1a; 纯模拟。从前往后遍历整个字符串&#xff0c;找到问号之后&#xff0c;就⽤ a ~ z 的每⼀个字符去尝试替换即 可。 例题二 解法&#xff08;模拟 分情况讨论&#xff09;&#xff1a; 算法思路&#xff1a; 模拟 分情况讨论。 计算相邻两个…...

【数据结构刷题专题】—— 二叉树

二叉树 二叉树刷题框架 二叉树的定义&#xff1a; struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(NULL), right(NULL); };1 二叉树的遍历方式 【1】前序遍历 class Solution { public:void traversal(TreeNode* node, vector&…...

基于AWS云服务构建智能家居系统的最佳实践

在当今智能家居时代,构建一个安全、高性能、可扩展和灵活的智能家居系统已经成为许多公司的目标。亚马逊网络服务(AWS)提供了一系列云服务,可以帮助企业轻松构建和管理智能家居系统。本文将探讨如何利用AWS云服务构建一个智能家居系统,并分享相关的最佳实践。 系统架构概述 该…...

Java零基础-集合:Set接口

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…...

数据结构与算法-排序算法

1.顺序查找 def linear_search(iters, val):for i, v in enumerate(iters):if v val:return ireturn 2.二分查找 # 升序的二分查找 def binary_search(iters, val):left 0right len(iters)-1while left < right:mid (left right) // 2if iters[mid] val:return mid…...

SpringBoot 文件上传(三)

之前讲解了如何接收文件以及如何保存到服务端的本地磁盘中&#xff1a; SpringBoot 文件上传&#xff08;一)-CSDN博客 SpringBoot 文件上传&#xff08;二&#xff09;-CSDN博客 这节讲解如何利用阿里云提供的OSS&#xff08;Object Storage Service)对象存储服务保存文件。…...

web渗透测试漏洞流程:红队目标信息收集之资产搜索引擎收集

web渗透测试漏洞流程 渗透测试信息收集---域名信息收集1.域名信息的科普1.1 域名的概念1.2 后缀分类1.3 多重域名的关系1.4 域名收集的作用1.5 DNS解析原理1.6 域名解析记录2. 域名信息的收集的方法2.1 基础方法-搜索引擎语法2.1.1 Google搜索引擎2.1.1.1 Google语法的基本使用…...

UI自动化_id 元素定位

## 导包selenium from selenium import webdriver import time1、创建浏览器驱动对象 driver webdriver.Chrome() 2、打开测试网站 driver.get("你公司的平台地址") 3、使浏览器窗口最大化 driver.maximize_window() 4、在用户名输入框中输入admin driver.find_ele…...

华为OD技术面算法题整理

LeetCode原题 简单 题目编号频次409. 最长回文串 - 力扣(LeetCode)3...

vmware虚拟机下ubuntu扩大磁盘容量

1、扩容&#xff1a; 可以直接在ubuntu setting界面里直接扩容&#xff0c;也可通过vmware命令&#xff0c;如下&#xff1a; vmware提供一个命令行工具&#xff0c;vmware-vdiskmanager.exe&#xff0c;位于vmware的安装目录下&#xff0c;比如 C:/Program Files/VMware/VMwar…...

秋招打卡算法题第一天

一年多没有刷过算法题了&#xff0c;已经不打算找计算机类工作了&#xff0c;但是思来想去&#xff0c;还是继续找吧。 1. 字符串最后一个单词的长度 public static void main(String[] args) {Scanner in new Scanner(System.in);while(in.hasNextInt()){String itemin.nextL…...

BC98 序列中删除指定数字

题目 描述 有一个整数序列&#xff08;可能有重复的整数&#xff09;&#xff0c;现删除指定的某一个整数&#xff0c;输出删除指定数字之后的序列&#xff0c;序列中未被删除数字的前后位置没有发生改变。 数据范围&#xff1a;序列长度和序列中的值都满足 1≤&#xfffd;≤…...

基于Java的学生体质健康管理系统的设计与实现(论文+源码)_kaic

摘 要 随着时代的进步&#xff0c;信息化也在逐渐深入融进我们生活的方方面面。其中也给健康管理带来了新的发展方向。通过对学生体质健康管理的研究与分析发现当下的管理系统还不够全面&#xff0c;系统的性能达不到使用者的要求。因此&#xff0c;本文结合Java的优势和流行性…...

【Linux系统】冯诺依曼与操作系统

什么是冯诺依曼体系结构&#xff1f; 如图即为冯诺依曼大致的体系结构图&#xff0c; 我们知道这些都是由我们的计算机硬件组成 输入设备&#xff1a;键盘&#xff0c; 鼠标&#xff0c; 摄像头&#xff0c; 话筒&#xff0c; 磁盘&#xff0c; 网卡... 输出设备&#xff1a…...

前端理论总结(html5)——form表单的新增特性/h5的新特性

form表单的新增特性 range&#xff1a;范围 color&#xff1a;取色器 url&#xff1a;对url进行验证 tel&#xff1a;对手机号格式验证 email&#xff1a;对邮箱格式验证 novalidate &#xff1a;提交表单时不验证 form 或 input 域 numbe…...

基于TensorFlow的花卉识别(算能杯)%%%

Anaconda Prompt 激活 TensorFlow CPU版本 conda activate tensorflow_cpu //配合PyCharm环境 直接使用TensorFlow1.数据分析 此次设计的主题为花卉识别&#xff0c;数据为TensorFlow的官方数据集flower_photos&#xff0c;包括5种花卉&#xff08;雏菊、蒲公英、玫瑰、向日葵…...

Android实现一周时间早中晚排班表

我们要做一个可以动态添加,修改一周早中晚时间排班表&#xff0c;需求图如下&#xff1a; one two 过程具体在这里不描述了&#xff0c;具体查看&#xff0c;https://github.com/yangxiansheng123/WorkingSchedule 上传数据格式&#xff1a; {"friday_plan":"…...

【Java八股面试系列】中间件-Redis

目录 Redis 什么是Redis Redis解决了什么问题 Redis的实现原理 数据结构 String 常用命令 应用场景 List(列表) 常用命令 应用场景 Hash(哈希) 常用命令 应用场景 set(集合) 常见命令​编辑 应用场景 Sorted Set(有序集合) 常见命令​编辑 应用场景 数据持…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...