JavaML 用于 Java 源代码的标记语言

小油菜的春天

小油菜的春天

2016-02-19 16:28

下面是个简单易学的JavaML 用于 Java 源代码的标记语言教程,图老师小编详细图解介绍包你轻松学会,喜欢的朋友赶紧get起来吧!

  传统的源代码简单文本表示对于编程人员来说很方便,但需要进行语法分析来揭示程序的深层结构。尽管某些复杂的软件工具通过分析源代码可以访问程序的结构,但许多像 grep 这样的轻量级编程辅助工具却仅仅依赖于源代码的词法结构。我说明的是一种新的 XML 应用程序,该应用程序提供了另一种 Java 源代码表示法。这种基于 XML 的表示法叫做 JavaML,对工具软件来说显得更加自然,它利用丰富的 XML 工具和技术,可以方便地对大量软件工程分析进行规范。使用 Jikes Java 编译器框架构建的强健的转换器,可将传统的源代码表示转换为 JavaML;而使用 XSLT 样式表,又可将 JavaML 转回到传统的文本格式。

  简介

  从第一种计算机编程语言开始,编程人员就已经开始将文本表示用作软件结构和计算过程的编码媒介。这些年来,技术已得到充分发展,使编译器的前端大大自动化了;编译器的前端就是执行词法分析和语法分析的那部分,是揭示以简单文本表示的编程语言的结构所必不可少的。借助于具有牢固基础的正规表达式的概念和语法,Lex/Flex 和 Yacc/Bison [42] 之类的工具使这些单调乏味的分析工作走向了自动化。正规表达式说明单个字符如何组合成表单记号,语法则描述更高级别的结构是如何由其他结构和原始表单记号递归构成的。这些过程一起将字符系列转换成一种叫做 抽象语法树 (AST) 的数据结构,这种数据结构可以更直接地反映程序的结构。

  源代码的文本表示有几种良好的特性。文本表示相当简明,并且与自然语言相似,往往比较容易阅读。文本还是一种通用的数据格式,因而可以方便地使用大量软件工具转换和处理源代码,这些软件工具包括文本编辑器、版本控制系统以及 grep, awk 和 wc 之类的命令流水线实用程序。

  然而,传统的源代码表示有许多问题。诸如 C++ 和 Perl 之类的当前十分流行的语言的语法结构,更加重了对语法分析能力的限制。尽管有许多工具的支持,但是为这些语言构造一个编译器的前端仍很困难。更为困难的也许是,要发展一种语言的语法,就常常需要处理脆弱的文法。这种局限性使得对一种正在发展的语言的处理复杂化了。

  文本表示和软件工具

  传统的源代码表示的最大局限性是,只有在语法分析后,才能搞清楚程序的结构。这一缺陷使得,某种语言专用的分析功能,在每一个工具中都要重复配置,只要这些工具在进行程序的词法分析之外,还要对程序进行语法推理。编译器必然需要与抽象结构树(AST)一起使用,而许多其他软件工程工具将从访问源代码的结构化表示中获益。遗憾的是,许多软件工程工具没有嵌入语法分析器,因而仅限于执行一些词法分析任务。

  有几种原因使得开发人员常常避免在工具软件中嵌入语法分析器。正如前面所提到的,对于语法结构复杂的语言,构建一个完整的编译器前端是具有挑战性的。虽然重复使用(例如重复使用语法定义)简化了实现的过程,但产生的抽象结构树(AST)并不总是直观的。抽象结构树(AST)通常反映的是奇特的人为技巧,而不是直接表示编程层次上的结构。此外,如果您的目标是使用词法信息就能完成得相当好的简单语法分析,嵌入编译器的前端无异于杀鸡用牛刀。

  如果希望转换源代码的格式,则会出现其他的复杂问题:抽象结构树(AST)的变化最终必定反映到传统的源代码表示中,因为后者是主要的长期存储格式。要从一个抽象结构树(AST)重新创建一种文本表示,最直接了当的方法就是不进行语法分析,但这样会产生不受欢迎的副作用,如缩排或空白的更改。开发人员所依赖的其他词法工具会分不清这些更改,例如,版本控制系统就无法区分一个有意义的修改和一个意外产生的无理修改。

  最后,在某种工具中使用语法分析器必定会把该工具定位于那种特定的语言,因而会减弱它的适用性和通用性。更糟的是,由于源程序没有标准的结构化的外部表示法,甚至对于以同一种编程语言为目标的不同工具来说,要对它们之间的互操作性进行支持,都是很困难的。

  这些复杂因素的最终结果是,开发人员往往不得不使用简单的、面向词法分析的工具,比如编辑器中的 grep 或者搜索并替换。这种方法牺牲了准确性:试想将一个局部变量从 result 重命名为 answer。借助简单的搜索并替换,每一处出现这个单词的地方都会替换,即使是注释、文本串或者毫不相干的实例字段中的字符,也不例外。

  一些开发人员采用的一种替代方法是,依靠集成开发环境 (IDE) 中提供的一套固定的工具,该开发环境能够通过一个集成的语言专用分析器访问其源程序的结构,但这种方法牺牲了灵活性。集成开发环境(IDE)一般只提供一套有限的功能,而且这些功能难于扩展。此外,使用现有的交互式环境,难以自动或批量分析和转换源代码。一些更高级的集成开发环境 (IDE) 如 IBM VisualAge for C++ [ 48],将应用程序编程接口提供给程序的表示法,虽然这也算是一种改进,但这项技术仍然有弊端,因为它不能将简单工具从一种复杂的环境中分离出来,此外还产生了对专有技术的依赖,这是我们所不希望看到的。

  解决方案

  隐藏在上述问题背后的一个基本问题是,源代码缺乏规范的结构化表示方法。我们需要有一种通用的格式来直接表示程序结构,以便于软件工具分析和处理。我们观察到的一个关键之处是,XML 这种可扩展标记语言(eXtensible Markup Language) [ 9],正好提供了这种功能,而且它是一种功能无比强大的源代码补充表示。

  在本文中,我要介绍 Java 标记语言,也即 JavaML ― 一种用于描述 Java 源程序的 XML 应用程序。JavaML 文档类型定义 (DTD) 规定了有效的 JavaML 文档的元素,以及这些元素组合的方式。在这些元素、元素的属性以及用这些元素编写的编程语言结构之间,有一种自然的对应关系。在 JavaML 文档中,源程序的结构反映在元素的嵌套中。借助这种表示,我们就可以利用处理和查询 XML 和 SGML 文档的大量工具,提供一种功能丰富的、开放式的基本结构,进行 Java 源代码的软件工程转换和分析。

  JavaML 很适合用作工具软件的规范的 Java 源代码表示。它保留了传统表示的大部分优点,而且克服了这些表示法的许多弱点。下一节讲述 Java 语言和 XML 的有关特性, Java 标记语言 (JavaML) 一节则详细描述标记语言,以及传统表示与 JavaML 之间的转换器的实现。 使用 XML 一节举了许多例子来说明,如何利用现有的 XML 和 SGML 工具,在 JavaML 提供的功能更为丰富的表示法的基础上,分析源代码和进行格式转换。 相关工作和 下一步工作 讲述相关的工作,并推荐完成更令人兴奋的下一步工作的途径。 附录 A 中有 JavaML 的完整的文档类型定义 (DTD),而已转换的源代码的更多实例可从作者 的 JavaML Web 页 [ 4 ] 上获得。

  背景

  Java 标记语言在 Java 和 XML 这两种技术之间架起了一座桥梁,它受到这两种技术的大量特性的影响,获益匪浅。

  Java 技术

  虽然基于 XML 的编程语言结构的表示法与语言无关,但是 Java 语言是试验这些理念和技术的最佳选择。

  Java 语言是一种流行的面向对象的编程语言,由 Sun Microsystems 在 90 年代中期开发 [ 3][ 25 ]。它是一个基于 Java 虚拟机 (JVM) 的、与操作平台无关的执行模型,由于用作万维网应用程序的编程语言而很快被广泛的接受。Java 语言将一种令人联想到 Smalltalk [ 26 ] 的简单对象模型,与 Algol 语言块结构、一种类似 C++ [ 49] 的语法,一种静态类型系统和一种由 Modula-2 [ 10] 产生的软件包系统结合在一起。

  在 Java 语言中,与在大多数其他面向对象 (OO) 的语言中一样,对程序语言进行解析所得到的基本单元是 类(class),类规定了一组对象的行为。每一个类可定义几种 方法,或者称之为行为,类似于函数或过程。一个类还可以定义 域,或称之为状态变量,这些域与类的 实例相关联,而这些实例叫做 对象。类可以从 超类继承行为和状态,从而形成互相关的类的层次结构,该层次结构允许在其顶部将相关的代码分解为类,从而有利于重复使用。行为是通过向目标接受者对象发送一个 消息来调用的,此消息就是执行一个为该类定义的方法的请求。选择执行什么方法来响应一个消息叫做 动态调用 ,依据的是接收消息的对象的运行时类。例如 ColoredBall 类的一个实例,可能用一种与 Ball 类的实例不同的方法响应 draw 消息。这种收到同一消息而做出不同响应的能力,主要得益于 Java 语言的可扩展性优势,而这种优势正是面向对象(OO)的团体所大力称道的。

  Java 语言在工业和教育领域都得到了广泛应用,并且仍然是一种广受欢迎的 Web 编程语言。与 C++ 不同,Java 类定义放在一个单独的自含式文件中,既没有单独的头文件也没有执行文件,并且 Java 语言基本上没有定义的次序相关性。在出现方法体时,它总是紧随方法特征声明之后定义。此外,Java 语言缺少集成处理器。这些特性合在一起,使 Java 源程序在语法上很简洁,从而使 Java 语言成为使用 XML 表示的最理想的语言。(这种方法对其他语言的适应能力将在 下一步工作 中进一步讨论。)

  XML:可扩展标记语言

  XML 是一种标准的可扩展标记语言 [ 9 ],是 SGML(标准通用标记语言)的子集 [ 37 ]。万维网联盟 (W3C) 将 XML 设计得既小巧又简单,同时仍保持与 SGML 兼容。尽管 HTML(超文本标记语言)当前是标准的 Web 文档语言,W3C 正在将 XML 定位为 HTML 替代语言。标记文档时,HTML 仅允许编程人员使用预先定义的一套固定标记,而 XML 却可以方便地使用用户定义的标记,来适应手头文档和数据的标记要求 [ 27][ 28]。

  XML 文档只是由使用标记符进行标记的文本简单地组成,这些标记符含在尖括号内。下面是一个简单例子:

  

?xml version="1.0"?!DOCTYPE email SYSTEM "email.dtd"emailheadtoMom/totoDad/tofromGreg/fromsubjectMy trip/subject/headbody encoding="ascii"The weather is terrific!/body/email

  email 是 email 元素的开启标记,本例结尾的 /email 是对应的关闭标记。文本和其他嵌套的标记可出现在开启和关闭标记结构之间。出现空元素是允许的;空元素以专门的形式缩略,将开启标记和关闭标记合并在一起: tag-name/。在上述文档中,email 元素包含两个直接子元素:head 和 body。此外, XML 开启标记可以与元素的属性/值对相关联,例如,上面 body 元素的 encoding 属性值为 ascii。若要一个 XML 文档是 合式 的,则该文档必须完全遵循 XML 文档要求的大量的语法规则(例如标记必须配对并正确嵌套,属性值必须格式正确并放在引号内,等等)。

  XML 文档的一个更严格的特点是 有效性 。当且仅当一个 XML 文档既是合式的,又符合其指定的 文档类型定义 (或称 DTD )时,该 XML 文档才是有效的。文档类型定义是一种形式描述,用于一类 XML 文档使用的特定语言的语法,它定义所有已允许的元素名称,并描述每一类元素可以拥有的属性,同时,它还限制一个有效的 XML 文档内的嵌套结构。就下面的文档定义类型 (DTD) 而言,前面的 XML 示例是有效的:

  

!-- email DTD --!ENTITY % encoding-attribute"encoding (ascii|mime) #REQUIRED"!ELEMENT email (head,body)!ELEMENT head (to+,from,subject?)!ELEMENT to (#PCDATA)!ELEMENT from (#PCDATA)!ELEMENT subject (#PCDATA)!ELEMENT body (#PCDATA)!ATTLIST bodyencrypted (yes|no) #IMPLIED%encoding-attribute;

  按照此文档类型定义 (DTD),有六种元素类型。email 元素必须正好包含一个 head,后面正好跟一个 body 元素。Head 也同样必须包含一个或多个 to 元素,然后是一个 from 元素,后面跟一个可选的 subject 元素。元素的次序必须是指定的次序。这些元素中的每一个都可以包含文本(又称 已分析的字符数据 或 PCDATA)。文档定义类型 (DTD) 中的单一 ATTLIST 声明规定,body 元素 可以 为 encrypted 属性指定一个值,并且 必须为 encoding 属性指定 ascii 或 mime。encoding-attribute 的 ENTITY 声明(在 DTD 顶部)是一种分解出冗余文本的简单方法;即引号之间给出的文本,可按原样替换到下面的 ATTLIST 声明中(并且,重要的是,它可以用在多个 ATTLIST 中)。

  如果上述准则有任何一个未得到满足,则声明遵循此文档定义类型(DTD)的 XML 文档无效。例如,如果某个 email 文档中缺少 from 元素,则该文档是无效的,虽然它可能仍然是合式的。

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/webkaifa/)

  当您在 XML 中制作数据模型时,主要的设计决策是在嵌套元素或使用属性之间作出选择。在上例中,如果我们这样选择的话,本来也可以把 head 中包含的全部元素都合并到 email 元素的各个属性中去。在使用属性和嵌套元素之间有一些重要区别:

属性/值对是无序的,而嵌套的子元素具有规定的次序。 属性的值可以只包含字符数据,不可以包括其他标记,而嵌套的子元素可以随意地进一步嵌套。 只能给每个属性一个值,而一个父元素可以包含多个属于同一类的元素(例如,我们可以让多个 to 元素包含在 head 中)。

  虽然上面的区别有时可以决定使用这种技术还是那种技术,但最初的决策往往是一个爱好问题。但是,以后使用由此产生的文档的经验,可能会启发人们重新去考察这个决策,以便使文档中所需的某些操作变得容易或简单。

  XML 的另一种有用的数据模型编制特性是,通过 id 属性给元素附上一个唯一标识符,然后其他元素的 idref 就可以引用这些元素。一个合式的 XML 文档,必须让每一个 idref 值与文档中的一个给定 id 匹配。id/idref 链接描述一些优势,这些优势能够使 XML 表示通用的定向图,而不只是表示树目录。

  一些工具(如 Emacs 编辑模式、基于结构的编辑器、DTD 语法分析器、验证实用程序、查询系统、格式转换和样式语言,以及许多其他工具)能够很好地支持 XML,这在一定程度上是由于 XML 是从 SGML 继承而来的。许多其他 W3C comments都与 XML 有关,包括级联样式表 [ 8]、XSL(可扩展样式表语言)[ 19]、XSLT (用于转换的 XSL)[ 14]、XPath [ 16 ] 和 DOM(文档对象模型)[ 2]。

  Java 标记语言 (JavaML)

  Java 标记语言提供了一套完整的 Java 源代码自描述表示。与传统的基于字符的程序表示不同,JavaML 以基于 XML 语法的元素嵌套方式,直接反映了软件产品的结构。此外,通过使用 XML 的 id 和 idref 链接,JavaML 还在程序图中表现出了更多的侧面。

  由于 XML 是一种基于文本的表示,所以它保留了传统源代码表示的许多优点。而 JavaML 是一种 XML 的应用程序,所以它易于进行语法分析;并且,现有的所有用于 XML 的工具,都可用于以 JavaML 表示的 Java 源代码。JavaML 的各种工具可以利用现有的基础结构和规范表示法来增强其互操作性。

  可能的方法

  虽然使用 XML 应用程序来编制源代码的基本方法是相当直接的,但到底使用哪种可能的标记语言,仍然有一个很大的设计空间。最明显的可能性是直接将 XML 用作一个典型的抽象语法树文本转储格式,该语法树源于对源代码的语法分析。现考虑下面这个简单的 Java 程序:

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/webkaifa/)

  

import java.applet.*;import java.awt.*;public class FirstAppletextends Applet {public void paint(Graphics g) {g.drawString("FirstApplet", 25, 50);}}

  执行上例中抽象语法树明显的(但极不令人满意的)转换,可能会产生 仅第一行代码 的如下 XML 形式:

  

compilation-unitImportDeclarationsoptImportDeclarationsImportDeclarationTypeImportOnDemandDeclarationimportNameQualifiedNameNameSimpleNamejava/SimpleName/Name.NameSimpleNameapplet/SimpleName/NameQualifiedName/Name. * ;/TypeImportOnDemandDeclaration/ImportDeclaration/ImportDeclarations/ImportDeclarationsopt.../compilation-unit

  毫无疑问,这种转换与理想相去甚远:它冗长得令人无法接受,并且它给出了底层语法的许多令人乏味的细节,这些语法原本是用来分析传统源代码表示的。

  另一种可能性是直接标记 Java 源程序而不更改程序的文本(即仅添加标记)。这种方法可能把 FirstApplet.java 实现转换为:

  

java-source-programimport-declarationimport java.applet.*;/import-declarationimport-declarationimport java.awt.*;/import-declarationclass-declarationmodifierspublic/modifiers classclass-nameFirstApplet/class-nameextendssuperclassApplet/superclass {method-definitionmodifierspublic/modifiersreturn-typevoid/return-typemethod-namepaint/method-name(formal-argumentstypeGraphics/typenameg/name/formal-arguments)statements{g.drawString("FirstApplet", 25, 50);} /statements/method-definition}/class-declaration/java-source-program

  这一格式朝着一种更有用的标记语言的方向迈了一大步。我们已经肯定地向源代码添加了值,而转回传统的表示法也很容易:只要删除所有 tag,并留下元素的内容(这种删除标记的方法,与 stripsgml [ 31 ] 实用程序的作法,如出一辙)。虽然这种表示似乎对许多任务有用,但它仍然存在一些问题。首先,编码的许多详细信息包含在元素的文本内容中,如果我们想确定要导入什么软件包, XML 查询将需要对声明导入的元素进行词法分析。这种分析用起来不方便,而且没有利用 XML 提供的能力。也许更为重要的一点是,上述的 XML 表示法保留了传统源代码的人为因素,而另一种表示法则可能允许我们不考虑语法细节进行抽象,使我们自己从这些语法重负中完全解脱出来。

  选择的表示

  我选择原型 JavaML 表示,旨在制定 Java 语言(实际上包括类似的面向对象)的编程语言结构模型,而不依赖特定语言的语法。人们可以很容易想到有一个 Smalltalk 标记语言(SmalltalkML),与此想法非常类似,甚至可以想到应该有一个面向对象标记语言(OOML),它既可以转换成传统的 Java 源代码,又可以转换成 Smalltalk 的外部定义格式。心中有了这个目标,于是首先按这些结构原则设计出 JavaML,然后对它不断地进行提炼,使它在功能和可读性方面都堪称优秀的标记语言。

  JavaML 由 附录 A 中的文档类型定义 (DTD) 来确定,但最好还是用示例来说明。对于上面列出的 FirstApplet.java 源代码,我们给出如图 1 所示的 JavaML 程序。

  图 1.转换成 JavaML 的 FirstApplet.java

  

1 ?xml version="1.0" encoding="UTF-8"?2 !DOCTYPE java-source-program SYSTEM "java-ml.dtd"34 java-source-program name="FirstApplet.java"5 import module="java.applet.*"/6 import module="java.awt.*"/7 class name="FirstApplet" visibility="public"8 superclass class="Applet"/9 method name="paint" visibility="public" id="meth-15"10 type name="void" primitive="true"/11 formal-arguments12 formal-argument name="g" id="frmarg-13"13 type name="Graphics"//formal-argument14 /formal-arguments15 block16 send message="drawString"17 targetvar-ref name="g" idref="frmarg-13"//target18 arguments19 literal-string value="FirstApplet"/20 literal-number kind="integer" value="25"/21 literal-number kind="integer" value="50"/22 /arguments23 /send24 /block25 /method26 /class27 /java-source-program

  在 JavaML 中,诸如方法、超类、消息发送和字面值数字之类的概念,都是直接在文档内容的元素和属性中表示的。JavaML 的表示法用元素的嵌套来反映编程语言的结构。例如,文本串 FirstApplet 是消息发送的一部分,这样 literal-string 元素就嵌套在 send 元素内。如果像图 2a 和图 2b 那样,以可视形式给出源代码时,这种嵌套就更明显了。有关的更多例子,请参见 JavaML 网页 [ 4]。

  细心的读者会看到, JavaML 表示的源代码的长度约比传统的源代码长三倍。源代码变长是转化成自描述数据格式(如 XML)的基本代价。有一点很重要,即编程人员在某些任务(包括一般开发和程序编辑)中能够使用简明扼要的传统表示法,尽管 JavaML 可能是底层表示法。JavaML 是对传统源代码表示的补充,并且特别适合在工具软件中使用,同时仍然可由开发人员访问并直接读取。

  图 2a. 由 XML Notepad 实用程序 [ 44 ] 显示的 FirstApplet 示例 JavaML 表示法树形图

  图 2b. XML Spy [ 36 ]显示 的 FirstApplet 示例的 JavaML 表示法树形图

 

  设计决策

  JavaML 所提供的不只是源程序的结构。请注意,将图 1 第 17 行的形参 g 用作了消息发送的目标,该 var-ref 标记的 idref 属性向后指向所引用的 formal-argument 元素(通过其 id 属性)。(在一个文档内,为要引用的元素选择的 id 值必须是唯一的,以便每一个标识符都用一个整数标记,从而使其值各不相同。)这种链接是标准的 XML,这样 XML 工具就能够从一个变量的使用追溯到变量的定义,例如,可获得变量的类型信息。局部变量(即代码段内声明的变量)也有类似的链接,程序结构图的其他方面还可以有更多链接。尽管单一的 var-use 标记已足以指示在任何地方出现的一个变量,但 JavaML 能够在变量值的引用和用作左值的变量之间进行区分:var-ref 元素用于前者,var-set 用于后者。

  在整个 JavaML 中,除非元素值的结构比简单文本字符串更加复杂,否则程序员随时可以使用元素的属性。元素的属性可用于诸如 synchronized 和 final 之类的修饰符,也可用于诸如 public 或 private 之类的可视性设置,但属性不用于类型之类的特性,因为类型具有某种结构形式:类型可由一个基本名和一个维数组成,并且它还可以引用实现该类型的类的定义,如果您想这样做的话。如果,比方说,一个返回类型只是方法元素的一个属性值,那么最终用户将不得不对属性的值 "int[][]" 执行字符串处理(这是令人不能接受的),以确定该二维数组的基本类型是原始类型 int。实际上,类型编写为显式子元素,如 type name="int" dimensions="2"。

展开更多 50%)
分享

猜你喜欢

JavaML 用于 Java 源代码的标记语言

Web开发
JavaML 用于 Java 源代码的标记语言

谈Sun关于Java的开放源代码策略

编程语言 网络编程
谈Sun关于Java的开放源代码策略

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

标记语言——为

Web开发
标记语言——为

一个用java作的日历的源代码

编程语言 网络编程
一个用java作的日历的源代码

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

标记语言——图片替换

Web开发
标记语言——图片替换

Sun预计在年底之前将公开全部Java源代码

编程语言 网络编程
Sun预计在年底之前将公开全部Java源代码

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

IE7进阶你不知道的实用技巧

IE7进阶你不知道的实用技巧

更改应用程序标题栏图标

更改应用程序标题栏图标
下拉加载更多内容 ↓