我知道 the grammars of C and C++ are context-sensitive ,特别是您需要在 C 中使用“lexer hack”。另一方面,我的印象是,尽管这两种语言之间存在相当大的相似性,但您可以仅使用 2 个前瞻标记来解析 Java。
您需要对 C 进行哪些更改以使其更易于解析?
我之所以这么问,是因为我看到的所有关于 C 上下文敏感性的示例在技术上都是允许的,但非常奇怪。例如,
foo (a);
可以使用参数 a 调用 void 函数 foo。或者,可以将 a 声明为 foo 类型的对象,但您也可以轻松摆脱括号。部分原因是因为 C grammar 的“直接声明符”产生规则实现了声明函数和变量的双重目的。
另一方面,Java grammar变量声明和函数声明有单独的生产规则。如果你写
foo a;
那么你知道它是一个变量声明,并且 foo 可以明确地被解析为一个类型名。如果类 foo 尚未在当前范围内的某处定义,则这可能不是有效代码,但这是语义分析的工作,可以在以后的编译器传递中执行。
我看到它说 C 由于 typedef 而难以解析,但您也可以在 Java 中声明自己的类型。除了 direct_declarator 之外,还有哪些 C 语法规则有问题?
最佳答案
解析 C++ 变得越来越难。解析 Java 变得同样困难。
查看 SO answer discussing why C (and C++) is "hard" to parse .简短的总结是 C 和 C++ 语法 本质上是模棱两可的。他们会给你多个解析,你必须使用上下文来解决歧义。然后人们会错误地假设您必须在解析时解决歧义;不是这样,见下文。如果您在解析时坚持解决歧义,那么您的解析器会变得更加复杂并且更难构建;但这种复杂性是自找的。
IIRC,Java 1.4 的“显而易见”的 LALR(1) 语法没有歧义,因此解析起来“容易”。我不太确定现代 Java 至少没有长途本地歧义。总是存在决定“...>>”是关闭两个模板还是“右移运算符”的问题。我怀疑 modern Java does not parse with LALR(1) anymore .
但是,对于这两种语言,人们可以通过使用强解析器(或弱解析器和上下文收集黑客,如 C 和 C++ 前端现在大多使用的那样)来解决解析问题。 C 和 C++ 具有预处理器的额外复杂性。这些在实践中比看起来更复杂。一种说法是 C 和 C++ 解析器太难了,必须手工编写。 It isn't true; you can build Java and C++ parsers just fine with GLR parser generators.
但解析并不是真正的问题所在。
解析后,您将希望对 AST/解析树进行一些操作。在实践中,您需要知道,对于每个标识符,它的定义是什么以及它在哪里使用(“名称和类型解析”,草率地,构建符号表)。事实证明,这比让解析器正确工作要多得多,还要加上继承、接口(interface)、重载和模板,而且所有这些的语义都是用非正式的自然语言编写的,分布在几十到几百页上,这一事实令人困惑的语言标准。 C++在这里真的很糟糕。从这个角度来看,Java 7 和 8 变得非常糟糕。 (并且符号表并不是您所需要的全部;请参阅我的简历以获取有关“解析后的生活”的更长的文章)。
大多数人都在为纯解析部分苦苦挣扎(通常永远不会完成;检查 SO 本身是否有很多关于如何为真正的语言构建工作解析器的问题),所以他们在解析后看不到生活。然后我们得到关于什么是难以解析的民间定理,并且没有关于该阶段之后会发生什么的信号。
修复 C++ 语法无济于事。
关于更改 C++ 语法:您会发现您需要修补很多地方以处理任何 C++ 语法中的各种本地和实际歧义。如果您坚持,following list might be a good starting place .我认为如果您不是 C++ 标准委员会,那么这样做是没有意义的。如果你这样做了,并使用它构建了一个编译器,那么没有理智的人会使用它。为了方便构建解析器的人,对现有 C++ 应用程序的投资太多了;此外,他们的痛苦已经结束,现有的解析器工作正常。
您可能想编写自己的解析器。好没问题;只是不要指望社区的其他人让你改变他们必须使用的语言来让你更容易。他们都希望对他们来说更容易,那就是使用记录和实现的语言。
关于java - 是什么让 Java 比 C 更容易解析?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26330425/
我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我正在使用ruby1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象