- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在编写一个语法来解析 HTSQL 语法,并且无法解决如何处理段和除运算符的 /
字符的重用问题。 described grammar不是很正式,所以我一直在关注 Python 实现的确切输出,粗略地看,它似乎是一个手写的解析器,而不是使用解析器生成器——作为引用,我目前使用的解析器生成器是CL-YACC
与 CL-LEX
. (FWIW 完整的 is here ,虽然可能有点过时了。)
我遇到的一个歧义是由于 "/1"
被解析为 '(:COLLECT (:INTEGER "1"))'
,但是"/1/2"
被解析为 '(:COLLECT (:OPERATOR/(:INTEGER "1") (:INTEGER "2")))'
,即一个是段分隔符,一个是除法; "/1//2"
又是 '(:COLLECT (:OPERATOR/(:INTEGER "1") (:COLLECT (:INTEGER "2"))))'
.
因此问题是,我如何在语法规范中处理这个问题而不求助于切换到手动解析器?切换到不同的解析器生成器类(而不是 LALR(1))会有帮助吗?
到目前为止,我已经尝试了语法的不同变体,但是整个语法的优先级是固定的这一事实也干扰了对斜线的两种解释。我尝试的另一种方法是在词法分析器中消除歧义,即以不同方式处理第一个斜杠(在每个“组”中)并返回不同的符号,例如DIV
- 但是我找不到一个好的规则,并且以某种方式怀疑它纯粹是通过查看词法结构而存在的。
最后,我很想通过完全偏离给定的解析器来解决这个问题,以使我的生活更轻松。您是否认为这更可取,因为具有可预测的语法更容易理解?
图表 1,用于检查解析树的 Python 脚本:
import htsql
application = htsql.HTSQL("sqlite:///htsql_demo.sqlite")
global y
y = None
def p(string):
global y
with application:
y = htsql.core.syn.parse.parse(string)
return y
def l(name):
result = []
for c in name:
if c.isupper() and result:
result.append("-")
result.append(c)
return "".join(result)
def keyword(name):
return ":{}".format(name.upper())
def n(expression):
name = expression.__class__.__name__
name = name[:name.find("Syntax")]
return keyword(l(name))
def t(expression):
arguments = [n(expression)]
d = expression.__dict__
if "identifier" in d:
arguments.append(t(expression.identifier))
if "text" in d:
arguments.append("\"{}\"".format(expression.text))
if "symbol" in d:
if not isinstance(expression, (htsql.core.syn.syntax.ProjectSyntax, htsql.core.syn.syntax.FilterSyntax, htsql.core.syn.syntax.CollectSyntax, htsql.core.syn.syntax.DetachSyntax)):
arguments.append(expression.symbol)
if "arm" in d:
arguments.append(t(expression.arm))
if "larm" in d:
arguments.append(t(expression.larm))
if "rarm" in d:
arguments.append(t(expression.rarm))
if "arms" in d:
arguments.extend(t(x) for x in expression.arms)
if "rarms" in d:
arguments.extend(t(x) for x in expression.rarms)
return "({})".format(" ".join(arguments))
# t(p("/school"))
# '(:COLLECT (:IDENTIFIER "school"))
# t(p("/'school'"))
# '(:COLLECT (:STRING "school"))
图表 2,我当前的解析器,它没有正确处理这个问题:
(defpackage #:cl-htsql
(:use #:cl #:alexandria #:cl-lex #:yacc)
(:import-from #:arnesi #:with-collector))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun maybe-intern (name &optional (package NIL package-p))
"If NAME is a SYMBOL, return it, otherwise INTERN it."
(cond
((symbolp name)
name)
(package-p
(intern name package))
(T
(intern name))))
(defmacro define-lexer (name &body patterns)
"Shortcut for DEFINE-STRING-LEXER."
`(define-string-lexer ,name
,@(mapcar
(lambda (pattern)
(etypecase pattern
((or symbol string)
(let ((symbol (maybe-intern pattern))
(pattern (string pattern)))
`(,pattern (return (values ',symbol ',symbol)))))
(list
(destructuring-bind (pattern &optional symbol value) pattern
(let* ((symbol (or symbol (intern pattern)))
(value (or value symbol)))
(etypecase symbol
(list
`(,pattern ,symbol))
(symbol
`(,pattern (return (values ',symbol ',value))))))))))
patterns))))
;; parser are results are to be treated immutable
(define-lexer string-lexer
/
("\\|" \|)
("\\&" &)
<=
>=
==
=
!==
!=
!~
!
~
<
>
@
("\\?" ?)
("\\." \.)
("\\(" \()
("\\)" \))
("\\+" +)
-
("\\*" *)
\:
("-?0|[1-9][0-9]*(\\.[0-9]*)?([eE][+-]?[0-9]+)?"
(return (cond
((find #\e $@)
(values 'float $@))
((find #\. $@)
(values 'decimal $@))
(T
(values 'integer $@)))))
("([^\"\\.\\?~\'=<>\\(\\)@\\|\\&/:])+" (return (values 'name $@)))
("\'([^\\\']|\\.)*?\'" (return (values 'string (string-trim "\'" $@))))
("\"([^\\\"]|\\.)*?\"" (return (values 'string (string-trim "\"" $@)))))
(define-parser *expression-parser*
(:muffle-conflicts (44 0))
(:start-symbol query)
(:terminals (|\|| #+(or)div & ! |.| ? / = != !== !~ ~ < > == <= >= \( \) + - * @ name integer decimal float string))
(:precedence ((:left @) (:left ~) (:left |.|) (:left + -) (:left * div) (:left = != == !== ~ !~ < <= > >=) (:left !) (:left &) (:left |\||) (:left ?) (:left /)))
(query
segment)
(segment
(/ segment (lambda (x y) (declare (ignore x)) (if (eq y :skip) '(:skip) `(:collect ,y))))
skip
group)
(skip
((constantly :skip)))
(group
(\( segment \) (lambda (x y z) (declare (ignore x z)) `(:group ,y)))
sieve)
(sieve
(segment ? segment (lambda (x y z) (declare (ignore y)) `(:filter ,x ,z)))
or)
(or
(segment |\|| segment (lambda (x y z) `(:operator ,y ,x ,z)))
and)
(and
(segment & segment (lambda (x y z) `(:operator ,y ,x ,z)))
not)
(not
(! segment (lambda (x y) `(:prefix ,x ,y)))
comparison)
(comparison
(segment = segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment != segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment ~ segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment !~ segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment == segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment !== segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment < segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment <= segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment > segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment >= segment (lambda (x y z) `(:operator ,y ,x ,z)))
addition)
(addition
(segment + segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment - segment (lambda (x y z) `(:operator ,y ,x ,z)))
multiplication)
(multiplication
(segment * segment (lambda (x y z) `(:operator ,y ,x ,z)))
(segment / segment (lambda (x y z) (declare (ignore y)) `(:operator / ,x ,z)))
composition)
(composition
(segment |.| segment (lambda (x y z) (declare (ignore y)) `(:compose ,x ,z)))
attach)
(attach
(segment @ segment (lambda (x y z) (declare (ignore y)) `(:attach ,x ,z)))
detach)
(detach
(@ segment (lambda (x y) (declare (ignore x)) `(:detach ,y)))
term)
(term
(name (lambda (x) `(:identifier ,x)))
(string (lambda (x) `(:string ,x)))
(number (lambda (x) `(:integer ,x)))
(integer (lambda (x) `(:integer ,x)))
(decimal (lambda (x) `(:decimal ,x)))
(float (lambda (x) `(:float ,x)))))
(defun make-lexer-for-source (source)
"Make a lexer for the SOURCE, either a STRING or a STREAM."
(etypecase source
(string (string-lexer source))
(stream
(flet ((ignore (c)
(declare (ignore c))))
(stream-lexer #'read-line #'string-lexer #'ignore #'ignore)))))
(defun lex-source (source)
"Debug helper to lex a SOURCE into a list of tokens."
(let ((lexer (make-lexer-for-source source)))
(loop
for (x y) = (multiple-value-list (funcall lexer))
while x
collect (list x y))))
(define-condition htsql-parse-error (simple-error) ())
(defun translate-yacc-error (error)
(make-condition
'htsql-parse-error
:format-control "Couldn't parse HTSQL query: ~A."
:format-arguments (list error)))
(defun parse-htsql-query (source)
"Parse SOURCE into a syntax tree. The SOURCE may be either a STRING or
a STREAM."
(handler-case
(parse-with-lexer
(make-lexer-for-source source)
*expression-parser*)
(yacc-parse-error (error)
(error (translate-yacc-error error)))))
;; > (parse-htsql-query "/1/")
;; (:OPERATOR / (:COLLECT (:INTEGER "1")) :SKIP)
;; > (parse-htsql-query "/1/2")
;; (:OPERATOR / (:COLLECT (:INTEGER "1")) (:INTEGER "2"))
最佳答案
如果查看运算符列表,您会发现还有另一种情况,其中相同的符号用作二元运算符和一元运算符,但优先级不同:一元减号。这在表达式语言中很正常,而 yacc 和大多数 yacc 衍生产品提供了一个解决方案:产生式的显式优先级声明。在 yacc 中,你可能会这样写
%token UNARY_MINUS UNARY_SLASH
%left UNARY_SLASH
%left '-' '+'
%left '/' '*'
%left UNARY_MINUS
%%
expr: '/' expr %prec UNARY_SLASH
| expr '*' expr
| expr '/' expr
| expr '+' expr
| expr '-' expr
| '-' expr %prec UNARY_MINUS
我不知道 CL-YACC 是否提供了等效的;我没有在文档中找到任何内容。
您没有义务使用优先声明,我什至不相信它们是个好主意。以明确的方式说出您的意思只是稍微复杂一点:
term: ID | NUMBER | ... | '(' expr0 ')'
expr0: '/' expr0
| expr1
expr1: expr1 '+' expr2
| expr1 '-' expr2
| expr2
expr2: expr2 '/' expr3
| expr2 '*' expr3
| expr3
expr3: '-' expr3
| term
以上是明确的,根本不需要优先声明。
(非正式的)HTSQL 语法说“段”运算符的语法是 /T
而不是 /expr
,但我没有看到任何迹象T
可能是。在我看来,T
是一个术语或者可能是一个 lexpr (T := expr
),这使得 1/2
不太可能成为候选者。但是,正如您所说,这是非正式的。
关于python - 解析 HTSQL 时处理语法歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37372801/
虽然我正在运行以测试此 thread 中的一些代码我发现了一件奇怪的事情,如果你考虑下面的程序 import java.util.ArrayList; import java.util.List; p
我只是很好奇为什么使用 using 指令以这种方式设计它。对于 1) struct 被视为命名空间,对于 2) 它不是: struct foo { using type0 = int; }; na
我需要在 ANTLR 中匹配一条消息,该消息包含由/分隔的 2 个字段第一个字段可以有 1..3 数字,第二个字段可以有 1..2 数字, 这不起作用 msg: f1 '/' f2; f1: DIGI
我想定义一个带有 x, y, z 坐标的 3D 矢量。可以使用(+)运算符添加向量,并可以使用length函数计算长度 如果我想编译它,我会收到以下错误: It could refer to eith
所以,今天我一直在测试 Java 的重载技术,但遇到了我无法解释的歧义。基本上,当存在带有原语及其相应包装器的可变参数方法时,编译器会提示并且无法决定选择哪一个,我不明白为什么?人类很容易决定而不是编
发现: Ambiguity while overloading the cast operator 和 C++ Operator Ambiguity 没有帮助 情况: 在容器类的内部我有: opera
我正在使用以下 XPath 片段 ancestor::contribution[1]/preceding-sibling::contribution[@speaker-reference][1] 我的
在尝试依赖可变参数模板实现一些事情时,我偶然发现了一些我无法解释的事情。我将问题归结为以下代码片段: template struct A {}; template class Z, typenam
我最近对 LALR 的研究足以写一篇 LALR generator ,并且我正在尝试为其构造一个 java 或 c# 风格的语法(其开头指定为 here )。 我知道编写解析器生成器需要额外的努力
在阅读 wikipedia article关于修订控制的文章,我发现了一些似乎定义不明确的术语,并且想知道它们是如何在现实世界中实际使用/应用的。具体来说: “主线”与“基线” “分支”与“流” “结
我有以下代码: class A{ public void print(A a){ System.out.println("A 1"); } } class B exte
考虑我试过的以下查询,有两个表,Orders 和 Customers,每个表都有列名称 CustomerID,当我尝试显示两个 CustomerID 时,只有一列显示,我不明白为什么会这样,或者我是理
我正在为基本汇编语言编写一个简单的组合词法分析器/解析器。我的问题是,在解析操作码时,我需要解析一些十六进制,它是指令计数器,可能是立即值等,以及实际的操作码。 基本上,当解析像 add 这样的东西时
我正在尝试在 mysql 中跨两个不同的数据库进行标题搜索,以匹配来自不同来源的数据。在 db1 或 db2 中,标题有时会在一个数据库中以“第一个标题”开头,而在另一个数据库中则以“第一个标题”开头
我对 token 级别的歧义有疑问。 问题看起来是这样的。我的代码看起来像这样,因此标记 t_UN1 具有更高的优先级。 t_ignore = ' \t\v\r' # whitespace ....
在 Main() 的最后一行,我在尝试执行指针操作时遇到错误,但我实际上想做的是调用我在 Foo 类中定义的乘法运算符。如果这不是调用乘法的语法,那是什么? namespace test {
在此page它说: The "00" specifier causes the value to be rounded to the nearest digit preceding the decim
我正在使用 C++ Builder 并使用 TDateTime 数据类型创建 GUI 应用程序。 当我在 TDateTime 对象上使用 MonthOf() 方法时,我得到的值比 TDateTime
$7.3.3/14 (C++03) struct A { int x(); }; struct B : A { }; struct C : A { using A::x; int x(in
我一直在开发一种适配器类,当我在 clang 下遇到问题。当定义了左值引用和右值引用的转换运算符时,您会在尝试从您的类中移出时遇到歧义编译错误(当这样的代码应该没问题时,如 operator cons
我是一名优秀的程序员,十分优秀!