Classworking工具箱源代码生成与字节码生成的结合.(DOC)

Classworking工具箱源代码生成与字节码生成的结合.(DOC)
Classworking工具箱源代码生成与字节码生成的结合.(DOC)

JiBX 1.0 采用类处理技术对类编译后生成的字节码进行了增强并且支持直接生成新类。字节码生成比工作在源代码级具有一些显著的优势,然而,有时它却在生成和调试应用程序时造成一些麻烦。即使不考虑方便的问题,一些开发者也是除了“源代码”之外什么也不信任。JiBx 2.0 的首席开发人员Dennis Sosnoski 要使JiBX 2.0 同时支持字节码生成技术和源代码生成技术。在这篇文章中,他讨论了源代码生成技术和字节码生成技术的不同之处并且对于如何协调二者给出了自己的看法。

类处理技术允许程序直接处理由Java? 源程序编译后生成的二进制类表示。我的 JiBX XML 数据绑定框架就是这样的一个例子,它采用类处理技术增强了 Java 类文件,给类文件添加了实现与 XML 文件相互转换的方法。直接处理二进制类具有许多优势,包括当源文件不可用时仍然可以对类进行修改。在大多数情况下,这种二进制方法都是非常有效的。

但是有时缺少源代码也有不利之处。例如:在调试过程中要使用源文件。Java 调试器是被设计为工作在源代码级,如果没有与字节码指令相匹配的 Java 源代码,调试器实际上是无用的。如果使用基于类处理技术的框架产生错误,在追踪这些错误时,缺少源文件就成问题了。而 JiBX 并不从类文件中删除调试信息——这样您仍然可以照常调试原始代码——但是不能通过添加的与 XML 相互转换的方法进行调试。除了调试的实用性问题外,很多开发者并不愿意信任一个框架在字节码级上处理他们的程序,并且他们自己不能方便地检查处理结果也让他们不舒服。

我为 JiBX 2.0 开发定的目标之一就是增加用源代码增强代替字节码增强的选项。在这篇文章里,我将对照地介绍处理这两种类型代码的难点和我实现时所采用的技术。同时,我也会讨论字节码操作的一些细节,这是我以前的文章中没有涉及到的,特别是在调用方法和控制流领域。

源代码的替代品

通常,编译器把 Java 源代码翻译成字节码指令序列。因此,大多数类处理库完全忽视了源代码并且只工作在字节码级上。惟一的例外情况是 Javassist 库,它允许使用 Java 源代码的一种形式将字节码插入方法中或者构造新的方法(请参阅参考资料,找到我早期关于Javassist 的Java programming dynamics文章)。

JiBX 2.0 对于源代码/字节码提供双重支持的可能性,有可能建立在 Javaassist 的源代码处理方法上:不断地产生源代码,然后,当直接增强类文件时,Javaassist 将源代码转换成字节码。但是 Javassist 对于源代码的支持是有限的,并且包含一些与标准 Java 源代码不同的特性(包括方法变量的引用方式)。其次,Javassist 比一些其他的字节码库(特别是 ASM 库,参看在文章“ Classworking toolkit: ASM Classworking” 中的讨论)速度慢。我认为字节码增强仍然是 JiBX 2.0 的主要目标,在某些情况下(如使用 JiBX 联合一种 IDE 自动汇编),可能需要重复进行字节码增强,所以速度也是至关重要的。最后,javassist CPL 证书与 JiBX 的 BSD 证书并不兼容。鉴于上述原因,我决定采用另外一种方法。

我计划使用一个策略原型代替代码生成的实现,这时,根据使用源代码策略或者字节码策略的不同,同类型的操作将做不同的翻译。字节码生成与 JiBX 1.X 的实现基本上是相同的(通过使用 ASM 库而不是 BCEL 库)。源代码生成是新功能,并且它的结构形式必须考虑到在操作层面上与字节码生成的兼容性。

回页首

代码形式比较

Java 源代码通常被编译成字节码,并且一些工具甚至可以将字节码(至少是由通常的编译器产生的文件形式)反编译成源代码。这两种代码形式之间的相互转换表明二者之间具有很高的兼容性。即使如此,使用源代码的编程技术与字节码的编程技术之间仍然存在实质的不同。在这一节,我将举例说明一些不同之处。

方法的参数和变量

Java 源代码通常把方法的参数当成一个特殊形式的本地变量,参数声明直接包含在函数声明中。这个原则有一个例外,虚方法使用一个特殊的第一参数,这个参数不显示在方法的参数列表中。这个隐藏参数是指向调用这个方法的类实例的 this 指针。

字节码对于方法参数的处理与本地变量相似。对于字节码,在方法执行的时候,每个参数占用堆栈结构的一个或者两个字。与源代码不同,字节码中的每个参数都是明确的——虚方法的 this 指针通常在堆栈结构中的 0 位置,接着是方法声明中明确定义的参数。不同的参数占用不同数量的堆栈结构位置,这取决于参数值类型的大小与标准字大小的比较。

源代码中一般的本地变量都定义在一个块中,这个块可能是一个完整的方法体或者是一个嵌套块。在字节码中遵循同样的原则。尽管不是明确的块,字节码中定义本地变量时,也要定义一个指令范围,在这个范围内,变量是有效的。同方法变量一样,本地变量占用堆栈结构中的字。在字节码中,为了使方法所需要的堆栈空间最小化,不同位置的不同本地变量可能会使用堆栈结构中的同一个字,只要变量的有效范围不重叠。

图 1 给出了一个简单方法的堆栈分配情况,包括本地变量。 long 型的值每个占用堆栈中的两个字,而 int 型和指针类型的值每个占用一个字。

图 1. 堆栈的使用

方法调用和堆栈使用

在 Java 源代码中,方法调用看起来非常简单:只要在方法名称后紧接着在括号里填入用逗号隔开的实参列表。实参是有位置的并且它必须与方法声明中相应的参数位置相同。如果方法返回一个结果值,则可以直接把这个值赋给一个本地变量、直接使用这个值或者不对它做任何处理。

而在相应的字节码中,情况就复杂多了。方法调用之前,必须根据参数声明,按照从左到右的顺序把对应的实参压入堆栈。虚方法调用(与静态调用相对)时,调用对象实例的指针必须在其他自变量之前被压入堆栈。当所有的自变量都在堆栈中时,方法可以被调用,并且调用结束后,在堆栈中的整个自变量列表将被方法的返回值代替。为了使堆栈状态有效,字节码必须考虑虚方法与静态方法调用的不同和返回值的问题。

我将举例说明堆栈的使用。清单 1 在一个类中定义了一个方法,这个方法与图 1 表示的相似。清单 2 给出了一个字节码的注释版本,在 main() 方法中调用清单 1 中类的power()。

清单 2 的粗体部分表示实际的 power() 方法调用的建立和返回处理。清单 1. 例子的源代码

public class PowerTest

{

private long power(long value, int power) {

long result;

if (power < 5) {

// just compute value inline for low loop count

result = 1;

for (int i = 0; i < power; i++) {

result *= value;

}

} else {

// split the computation using recursion for speed result = power(value, power/2);

result = result*result;

if ((power % 2) == 1) {

result *= value;

}

}

return result;

}

public static void main(String[] args) {

PowerTest inst = new PowerTest();

long value = Long.parseLong(args[0]);

int power = Integer.parseInt(args[1]);

System.out.println(value + " to the power " +

power + " is " + inst.power(value, power));

}

}

清单 2. 添加了注释的方法调用字节码

// create and initialize class instance (using default constructor)

new PowerTest

dup

invokespecial PowerTest.

// store reference (duplicated before initializer call) to "inst"

astore_1

// load first command line argument value string from array

aload_0

iconst_0

aaload

// convert and store value to "value"

invokestatic Long.parseLong

lstore_2

// load second command line argument value string from array

aload_0

iconst_1

aaload

// convert and store value to "power"

invokestatic Integer.parseInt

istore %4

// call power() and save result value to "result"

aload_1

lload_2

iload %4

invokespecial PowerTest.power

lstore %5

...

虽然字节码的堆栈操作增加了复杂度,但是它也具有一些源代码所不具备的灵活性。例如:字节码可以处理那些不止一次被用到的值,采用在堆栈中复制它们的方式。在源代码中要获得同样的效果,则需要定义一个本地变量来保存这个值。可以构造许多操作类型以利用字节码所提供的使用堆栈的灵活性,而且 JiBX 1.X 代码产生时很大程度上就是利用这种灵活性。

执行流程

在字节码中控制程序执行的正常流程也比在源代码中要复杂一些。Java 平台提供条件执行(使用 if),三种风格的循环(for、do 和 while),一种开关结构(switch)。在字节码级上,只有两种不同的基本结构,一种对应于 switch 语句,另外一种是分支。然而,分支语句具有许多变化形式,这些变化形式足以弥补基本结构的稀少。

为了演示基本分支操作,清单 3 显示了清单 1 中的 power() 方法。这个例子包含几个分支,三个分支语句的字节码显示为粗体。第一个分支是一个 if_icmpge 条件转移语句。这个分支使用堆栈顶端的两个字,从第二个字中减去第一个字并且如果减的结果为非负值则转到此分支。第二个分支是无条件转移 goto。它对堆栈没有影响,在任何时候都转向目标分支。第三个分支是一个 if_icmpne 条件转移语句。这个分支使用堆栈顶端的两个字,从第二个字从减去第一个字并且如果减的结果为非零值则转到此分支。

清单 3. 添加了注释的具有分支的字节码

// check if "power" less than 5

iload_3

iconst_5

if_icmpge 29

// initialize "result" to 1 and "i" to 0

lconst_1

lstore %4

iconst_0

istore %6

// jump to end if "i" greater than or equal to "power"

11: iload %6

iload_3

if_icmpge 59

// multiply "result" value by "value"

lload %4

lload_1

lmul

lstore %4

// increment "i" value and loop back to test

iinc %6 1

goto 11

// make recursive call for half the "power"

29: aload_0

lload_1

iload_3

iconst_2

idiv

invokespecial PowerTest.power

// square the returned "result" value

lstore %4

lload %4

lload %4

lmul

lstore %4

// check for odd "power" value

iload_3

iconst_2

irem

iconst_1

if_icmpne 59

// odd "power", multiple "result" again for final value

lload %4

lload_1

lmul

lstore %4

// return "result" value

59: lload %4

lreturn

清单 3 演示了 Java 条件执行(if 语句)和一种形式的循环(for 语句)是如何被翻译成字节码的。 Java 源代码中的另外一种循环结构的处理方式与 for 相似。 switch 语句就复杂多了,在字节码中,有时采用正常的条件分支,有时采用两种基于表的条件分支之一。

生成机制

仅仅通过提供两个完全独立的代码生成实现来支持源代码和字节码生成的合并是一种自然的方法。这种方法的确有用,但是显而易见,这将涉及很多重复的工作(在开始的时候和维护的过程中都有)。

我想要避免这种重复的工作。与其重复所有的生成代码,我宁愿实现一种策略类型方式,这种方式可以同时处理这两种类型的代码。这种方式使用公用代码控制生成过程,调用策略实现方法为一种特定的操作生成合适的代码。我将用一个简单的例子阐明这种技术

利用树生成代码

在上个月的专栏中,我描述了 JiBX 1.X 绑定编译器如何基于代码生成树结构产生字节码,这种树结构是根据绑定的定义构建的。采用基于树的方法处理代码生成具有如下优点:

回页首

所有的代码按顺序生成——不需要回退到前面生成的代码序列插入新生成的字节码。这就使实际代码生成变得相对简单。

虽然 JiBX 2.0 比 JiBX 1.X 更直接地依赖绑定定义,但是它依然沿用了 JiBX 1.X 使用树表示的工作原理。为了同时支持源代码和字节码生成,JiBX 2.0 添加了一个策略层,这个层加在由树生成的抽象操作序列和实际生成的代码之间。为了演示这个策略层是如何工作的,我将仔细讲解图 2(上个月文章中的一个例子)中的 JiBX 1.X 模型的代码生成部分,揭示字节码和源代码如何使用相同的抽象操作序列。

图2. 代码生成模型的例子

抽象操作

清单 4 为图 2 的左下部分给出了实现从 XML 到 Java 对象的解编排所需的抽象操作列表。清单的顶端部分完成的是一个 Customer 实例的解编排,底部完成的是一个 Name 实例的解编排。 JiBX 通常将解编排的代码变成虚方法添加到类中,添加的虚方法的名称以“JiBX”开头。在清单 4 中,我列出了 Customer 解编排方法的一部分抽象操作序列和Name 解编排方法的完整抽象操作序列。

清单 4. 名称域解编排逻辑操作

load or create object from "name" field, save to local

call Name unmarshalling method

store reference from local variable to "name" field

unmarshal "street1" field value from "street" element

unmarshal "city" field value from "city" element

...

// Name unmarshalling method

call unmarshalling context method to push instance on stack

生成字节码

清单 5 显示了清单 4 中的抽象操作列表产生的字节码。字节码假定添加到类的解编排方法都是只有一个参数的虚方法,JiBX 解编排上下文被用来解释 XML 输入文档。因为它们是虚方法,所以堆栈的第一个值(也就是偏移量为 0 的位置)填入的是对象实例的引用;堆栈的第二个值是解编排上下文的引用,后面紧接着是代码中的本地变量。

清单 5. 名称域解编排字节码

// load or create object from "name" field, save to local

aload_0

getfield name

dup

astore_3

ifnonnull 20

aload_1

invokestatic Name.JiBX_binding1_newinstance_1_0

astore_3

// call Name unmarshalling method

20: aload_3

aload_1

invokevirtual Name.JiBX_binding1_unmarshal_1_0

// store reference from local variable to "name" field

aload_0

aload_3

putfield name

// unmarshal "street1" field value from "street" element

aload_0

aload_1

aconst_null

ldc "street"

invokevirtual org.jibx...UnmarshallingContext.parseElementText

putfield street1

// unmarshal "city" field value from "city" element

aload_0

aload_1

aconst_null

ldc "city"

invokevirtual org.jibx...UnmarshallingContext.parseElementText

putfield city

...

// Name.JiBX_binding1_unmarshal_1_0 method

// call unmarshalling context method to push instance on stack

aload_1

aload_0

invokevirtual org.jibx...UnmarshallingContext.pushObject

// unmarshal "firstName" field value from "first-name" element

aload_0

aload_1

aconst_null

ldc "first-name"

invokevirtual org.jibx...UnmarshallingContext.parseElementText

putfield firstName

// unmarshal "lastName" field value from "last-name" element

aload_0

aload_1

aconst_null

ldc "last-name"

invokevirtual org.jibx...UnmarshallingContext.parseElementText

putfield lastName

// call unmarshalling context method to pop instance from stack

aload_1

invokevirtual org.jibx...UnmarshallingContext.popObject

return

生成源代码

清单 6 显示了抽象操作列表生成的 Java 源代码,这组抽象操作与生成清单 5 中字节码的抽象操作相同。源代码中变量名“ctx”指的是每种方法的解编排上下文参数。

清单 6. 名称域解编排源代码

if (local1 == null) {

local1 = Name.JiBX_binding1_newinstance_1_0();

}

// call Name unmarshalling method

local1.JiBX_binding1_unmarshal_1_0(ctx);

// store reference from local variable to "name" field

name = local1;

// unmarshal "street1" field value from "street" element

street1 = ctx.parseElementText(null, "street");

// unmarshal "city" field value from "city" element

city = ctx.parseElementText(null, "city");

...

// Name.JiBX_binding1_unmarshal_1_0 method

// call unmarshalling context method to push instance on stack

ctx.pushObject(this);

// unmarshal "firstName" field value from "first-name" element

firstName = ctx.parseElementText(null, "first-name");

// unmarshal "lastName" field value from "last-name" element

lastName = ctx.parseElementText(null, "last-name");

// call unmarshalling context method to pop instance from stack

ctx.popObject();

从这个简单的例子可以很容易地看出,如何从抽象操作同时实现字节码和源代码的生成。虽然支持完全灵活的 JiBX 数据绑定涉及到更复杂的东西,但是原理是相同的。

检查改变

JiBX 1.X 绑定编译器检查每个绑定类,检查类中名称与实现绑定方法的模式相匹配的方法。如果绑定编译器发现名称符合的方法,它就认为在绑定编译器以前运行的时候已经添加了这些方法。当构造一个新方法时,在真正把这个新方法添加到类之前,绑定编译器首先检查是否与一个已经存在的绑定方法(在绑定编译器执行前就已经存在于类中,或者是在绑定编译器先前的执行过程中添加进来的)相匹配。如果找到相匹配的方法,则用已经存在的方法代

回页首

替新构造的方法。如果类中原来存在的一个绑定方法没有被绑定编译器使用,则从类中把它删除。这种方法匹配方式不仅使 JiBX 添加的代码总量最小化,而且在绑定重编译的时候避免了不必要的对类文件的改变。

在 JiBX 1.X 实现时,这种方法匹配方式具有一些局限性。特别是,它不能匹配相互递归调用的方法,也就是每个方法都调用其他的方法。对于 JiBX 2.0,我希望能够克服这种局限性。然而,在这篇文章中我只简单谈谈下面两个问题:第一,字节码方法比较通常是如何工作的;第二,我计划如何实现相应的源代码方法比较。这不是指源代码与字节码的比较——对于这个问题涉及到的更多,但是幸运的是 JiBX 2.0 的使用者并不需要关心这个问题,使用者只需要指定某个特定的类是使用字节码还是源代码修改。

字节码方法比较

JiBX 1.X 的字节码方法匹配基于对方法签名和方法的二进制字节码指令序列的比较。因为绑定编译器经常为一个绑定组件生成相同的字节码指令序列,这种匹配过程像预期的那样有效,甚至对于那些调用处于同一个类或者其他类中的其他方法的方法也同样有效,只要在调用之前检查了被调用方法的重复性。JiBX 代码生成所使用的基于树的方法总能按照深度优先顺序构造方法;如果在方法调用图中没有循环(方法的相互调用),重复检查是有效的。源代码方法比较

源代码方法匹配需要比字节码方法匹配多做一些工作。其中一种方式是匹配一个方法的Java 语言标志(包括源代码)序列,这是与字节码比较方法签名和指令序列的方法最相近的方式。与字节码生成相同,绑定编译器总是对一个绑定组件生成相同的源代码标志序列。如果一个新构造的方法的标志序列与一个现存方法的标志序列相匹配,则这两个方法是相同的,就用已经存在的方法代替新构造的方法。

在源代码中添加和删除方法也比在字节码中复杂。在字节码中,一个类描述中的方法排列顺序没有任何特别的意义。在字节码中,可以增加和删除方法而丝毫也不影响用户代码;并且生成的方法与从用户代码编译生成的代码本质上是相同的。另一方面,在源代码中,用户代码可能会含有注释和格式,这些注释和格式虽然不影响代码的含义但是对于用户来说,它们仍然非常重要。任何对原始形式的用户源代码的曲解都将造成麻烦。

框架所使用的在用户代码中添加生成的源代码的标准技术是:定义一个定界符将生成的代码从原始代码中分离。JiBX 2.0 采用一种更灵活的方式:开始时,在 Java 源文件中已经存在的用户方法后添加新生成的方法,但是如果生成的方法被后来的绑定编译修改了,则直接替换掉原来的代码。这种方法使用户能够对生成的代码进行移动和对格式进行重新调整,而不会影响到方法匹配过程。

当代码需要扫描大量的源代码文件时,效率也是一个潜在的重要因素。JiBX 能够避免这个问题,因为它要求绑定类编译后的版本必须存在,即使是在生成代码增强的时候。绑定编译器通常能够轻易地检查到 JiBX 生成的源代码方法名,不需要处理每个未处理过的源代码文件。只有那些添加了 JiBX 方法的源代码文件才需要扫描,这样,在比较时就能够记录和使用每个方法标志序列。

回页首

下期讲述泛型

这个月,我着重讲述了允许用户在绑定代码时选择源代码增强和字节码增强所涉及到的问题,绑定的代码是由我的 JiBX XML 数据绑定框架生成的。到目前为止,我得出这样的结论:使用正确的架构,这种选择是能够实现的,并且这毫无疑问会给用户带来很多的好处。我希望明年年初将会开发出支持这项特性的 JiBX 2.0 bata 版。

下个月,我将着重讲述 JiBX 2.0 中类处理技术的另一个方面的改变。Java 5 增加了对语言泛型的支持,使开发者既可以在编译时检查集合类型安全性,又可以隐藏使用集合所涉及的运行时重塑(尽管以一个输入有时会非常杂乱的语法为代价)。我完全不是因为泛型有助于书写清晰的代码而感到欣喜,而是我对于使用附加类型信息非常感兴趣,这种附加类型信息会被加入编译的类文件中。在下一期中,我将深入讲述泛型方面的内容,并且向您阐明如何在运行时使用反射访问泛型信息。

(注:素材和资料部分来自网络,供参考。请预览后才下载,期待你的好评与关注!)

中间代码生成实验报告材料

一、实验目的 通过在实验二的基础上,增加中间代码生成部分,使程序能够对实验二中的识别出的赋值语句,if语句和while语句进行语义分析,生成四元式中间代码。 二、实验方法 实验程序由c语言完成,在Turboc 2.0环境中调试通过。 语义分析程序的基本做法是对文法中的每个产生式分别编写一个语义分析子程序,当程序语法部分进行推倒或规约时,就分别调用各自的语义分析程序。当语法分析结束时,语义分析也就结束了。 在本实验程序中,当语法分析部分识别出语确的句子时,就进入content函数(当语法分析识别出不正确的句子时,不进入content函数,也就是不进行语义分析),然后根据句子的类型进行分类,进入不同的语义处理部分。 对于赋值语句,关键是产生正确的处理算术表达式E的四元式。 程序中的ec函数的功能就是产生算术表达式的四元式,在ec函数中使用了两个栈idshed,opshed,分别是算术表达式的数据栈和符号栈。每次提取一个数字和一个算符,然后将算符与与栈顶算符进行优先级比较,优先级高则将单前数字和算符进栈,低或者相等的话则将当前栈顶元素进行合并,产生四元式。直至整个算术表达式结束。其中还有一些细节问题,具体的做法可以参看程序。 对于实验给定的if语句的文法格式,条件判断式C只中可能是>或者<=两种关系,不可能是布尔表达式,这样程序就简单的多了。 通过ec函数可以产生条件判断式C中的E的四元式,然后只要加上转向四元式就可以了。本实验程序中只给出真出口的转向四元式,没有给出假出口的转向四元式,这在实际中是不可以的,但在

本实验中,实际上是对每条独立的语句进行语法分析,给出假出口转向四元式实际上意义不大,而且假出口转向语句的转移目标必须要到整个语句分析结束以后才可以知道,这样就要建立栈,然后回填,这样会使程序复杂很多,所以没有加上假出口转向四元式。 对于while语句,具体的做法和if语句差不多,所不同的是当while语句结束时,要多出一条无条件转向四元式,重新转到条件判断式C的第一条四元式。当要产生无条件转向四元式时,它的转向目标C的第一条四元式已经产生了,所以具体的做起来是不太困难的。只要记下当前while中的C的第一条四元式的位置,填上就可以了。 整个程序的结束是当读入“. ”时,程序就中止。 程序中还有很多细节问题,具体的可以后面的附录:程序的完整代码。 三、测试程序 ff:=6+6*6-; if sl>89+56*67 then f:=7*7+4; ff:=6+6*6-6%4+8; if sl+78*76>89*56+67 then while a-7>98+45*45 do f:=7*7+4; . 四、运行结果 首先对测试程序进行语法分析,识别出正确的句子,当识别出正确的句子时,就对当前句子进行语义分析,而语法不正确的句子不进行语义分析。 ff:=6+6*6- Error(4):Except ID or NUM ; Error(2):Syntax error if sl>89+56*67 then f:=7*7+4; success!!! (1) [ *, 56, 67, T1 ]

20个代码生成框架

20个代码生成框架 11.1 CodeSmith http: 官方论坛: http: 版权形式:30天试用 开源:否需要先注册确认后才能下载 1.2 MyGenerator MyGenerator是又一个国外很不错的代码生成工具,有人觉得比CodeSmith 简单、好用。 所有api可以在帮助菜单中找到。 http: 官方论坛: 版权形式: 免费 开源:否 1.3 NHibernate. http: 官方论坛: 版权形式:

免费 开源:否 1.4湛蓝.Net代码生成器 http: 官方论坛: http: 版权形式: 免费 开源:否 1.5动软.NET代码自动生成器 一款人气很旺的免费C#代码生成器 http: 官方论坛: 版权形式: 免费 开源:否 1.6 CodePlus 专为sql server c#语言设计的代码生成器,功能还是很强大http: 官方论坛:

版权形式: 需要少量的注册费用 开源:否下载地址很神秘 1.7 CodeMaker http: 官方论坛: 版权形式: 免费 开源:否 https://www.360docs.net/doc/c23070597.html,代码生成器 可以使用本工具生成https://www.360docs.net/doc/c23070597.html,和C#语言的代码,以及三层架构与ORM架构代码,并且使用的ORM持久化组件是开源的,您可以在本软件的安装目录下找到它 官方论坛: 版权形式: 免费 开源:否 1.9 BMW业务模型及代码生成器 一款人气很旺的免费C#代码生成器

官方论坛: 版权形式: 免费 开源:否 1.10飞鹰CoolCoder 专门为采用nhibernate做关系对象影射架构的系统提供代码的工具,简单易用,虽然不提供源码,我们可以用反编译工具对其反编译看源码。这是个很不错的学习机会。 官方论坛: 版权形式: 免费 开源:否 1.11 AutoCoder自动代码生成器 AutoCoder自动代码生成器是一个根据模板自动生成代码的代码生成工具,根据模板的不同,可以生成任何语言(如: ASP、C#、C++BUILDER、DELPHI、JAV A、JSP、PHP、V B、https://www.360docs.net/doc/c23070597.html,……),不同层次结构(B/S、C/S、n-tiger……),基于不同数据库(ORACL E、MSSQL、MYSQL、

编译原理综合性实验报告-分析中间代码生成程序分析

编译原理综合性实验报告-分析中间代码生成程序分析XXXXXX计算机系综合性实验 实验报告 课程名称编译原理实验学期 XXXX 至 XXXX 学年第 X 学期学生所在系部计算机系年级 X 专业班级 XXXXXX 学生姓名 XXX 学号 XXXXXXXXXXXX 任课教师XXX 实验成绩 计算机系制 《编译原理》课程综合性实验报告 开课实验室: 年月日实验题目分析中间代码生成程序 一、实验目的 分析PL/0编译程序的总体结构、代码生成的方法和过程;具体写出一条语句的中间代码生成过程。 二、设备与环境 PC兼容机、Windows操作系统、Turbo Pascal软件等。 三、实验内容 1. 分析PL/0程序的Block子程序,理清PL/0程序结构和语句格式。画出Block 子程序的流程图,写出至少两条PL/0程序语句的语法格式。 2. 分析PL/0程序的Block子程序和Gen子程序,了解代码生成的方法和过程。 使用概要算法来描述语句的代码生成过程。 3. 自己编写一个简单的PL/0程序,能够正确通过编译,得到中间代码。列出自

己编写的源程序和编译后得到的中间代码。 4. 从中选择一个语句或表达式,写出代码生成的过程。要求从自己的源程序中 选择一条语句,结合这条语句写出语义分析和代码生成过程。在描述这个过程中,要说清楚每个功能有哪个子程序的哪条语句来完成,说清楚语句和参数的含义和功能。 四、实验结果及分析 (一)Block子程序分析 1.常量声明的分析: 常量声明部分的语法结构定义为如下形式: -> const ; -> [;] ->id = C 其中C可以是常量标识符或字符串或整数(可带符号)或实数(可带符号)。 常量声明分析程序的主要任务是: (1).扫描整个常量声明部分。 (2).为被声明的常量标识符建立符号表项。 (3).检查重复的声明。 2.变量声明部分的分析: 变量声明部分的语法结构定义为如下形式: -> var -> [;] ->:T ->id[,]

语义分析与中间代码生成程序的设计原理与实现技术__实验报告与源代码_北京交通大学

语义分析及中间代码生成程序设计原理与实现技术 XXX 1028XXX2 计科1XXX班 1.程序功能描述 完成以下描述赋值语句和算术表达式文法的语法制导生成中间代码四元式的过 程。 G[A]:A→V:=E E→E+T∣E-T∣ T→T*F∣T/F∣F F→(E)∣i V→i 说明:终结符号i 为用户定义的简单变量,即标识符的定义。 2. 设计要求 (1)给出每一产生式对应的语义动作;(2)设计中间代码四元式的结构(暂不与符号表有关)。(3)输入串应是词法分析的输出二元式序列,即某算术表达式“实验项目一”的输出结果。输出为输入串的四元式序列中间文件。(4)设计两个测试用例(尽可能完备),并给出程序执行结果四元式序列。 3.主要数据结构描述: 本程序采用的是算符优先文法,文法以及算符优先矩阵是根据第四次实验来修改的,所以主要的数据结构也跟第四次差不多,主要为文法的表示,FirstVT集和LastVT 集以及算符优先矩阵:

算符优先矩阵采用二维字符数组表示的: char mtr[9][9]; //算符优先矩阵 4.程序结构描述: 本程序一共有8功能函数: void get(); //获取文法 void print(); //打印文法 void fun(); //求FirstVT 和LastVT void matrix(); //求算符优先矩阵 void test(); //测试文法 int cmp(char a,char b); 比较两个运算符的优先级 1 0 -1 void out(char now,int avg1,int avg2); //打印四元式 int ope(char op,int a,int b); //定义四元式计算方法 5.实验代码 详见附件 6.程序测试 6.1 功能测试 程序运行显示如下功能菜单:

编译方法实验报告(中间代码生成器的设计)

编译方法实验报告 2011年10月

一、实验目的 熟悉算术表达式的语法分析与中间代码生成原理。 二、实验内容 (1)设计语法制导翻译生成表达式的四元式的算法; (2)编写代码并上机调试运行通过。 输入——算术表达式; 输出——语法分析结果; 相应的四元式序列。 (3)设计LL(1)分析法或LR(0)分析法的属性翻译文法,并根据这些属性翻译文法,使用扩展的语法分析器实现语法制导翻译。 三、实验原理及基本步骤 ●算术表达式文法: G(E): E →E ω0 T | T T →T ω1 F | F F → i | (E) ●文法变换: G’(E) E →T {ω0 T} T →F {ω1 F} F → i | (E) ●属性翻译文法: E →T {ω0“push(SYN,w)” T “QUAT”} T →F {ω1“push(SYN, w)” F “QUAT”} F →i “push(SEM, entry(w))” | (E) 其中: push(SYN, w) —当前单词w入算符栈SYN; push(SEM, entry(w)) —当前w在符号表中的入口值压入语义栈SEM; QUA T —生成四元式函数 i.T = newtemp; ii.QT[j] =( SYN[k], SEM[s-1], SEM[s], T); j++; iii.pop( SYN, _ ); pop( SEM, _ ); pop( SEM, _ ); push( SEM, T ); ●递归下降子程序: 数据结构:SYN —算符栈; SEM —语义栈;

四、数据结构设计 使用递归的结构进行四元式的设计,同时,运用堆栈结构将四元式的输出序列打印出来 while ( exp[i]=='+' || exp[i]=='-'){ syn[++i_syn]=exp[i]; //push(SYN,w) i++; //read(w) T(); quat();} while ( exp[i]=='*' || exp[i]=='/'){ syn[++i_syn]=exp[i]; //push(SYN,w) i++; //read(w) F(); quat();} void quat(){ strcpy(qt[j],"(, , , )");

编译原理实验 中间代码生成

实验四中间代码生成 一.实验目的: 掌握中间代码的四种形式(逆波兰式、语法树、三元式、四元式)。 二.实验内容: 1、逆波兰式定义:将运算对象写在前面,而把运算符号写在后面。用这种表示法表示的表 达式也称做后缀式。 2、抽象(语法)树:运算对象作为叶子结点,运算符作为内部结点。 3、三元式:形式序号:(op,arg1,arg2) 4、四元式:形式(op,arg1,arg2,result) 三、以逆波兰式为例的实验设计思想及算法 (1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。 (2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。 (3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。 (4)如果不是数字,该字符则是运算符,此时需比较优先关系。 做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将此运算符栈顶的运算符从栈中弹出,将该字符入栈。 (5)重复上述操作(1)-(2)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。 四、程序代码: //这是一个由中缀式生成后缀式的程序 #include<> #include<> #include<> #include<> #define maxbuffer 64 void main() { char display_out(char out_ch[maxbuffer], char ch[32]); //int caculate_array(char out_ch[32]); static int i=0; static int j=0; char ch[maxbuffer],s[maxbuffer],out[maxbuffer]; cout<<"请输入中缀表达式: ";

代码生成器

代码生成器使用规则 1.创建一个表,红色为必须字段,该sql执行前去掉行前的tab符号 DROP TABLE IF EXISTS `books`; CREATE TABLE `books` ( `id` varchar(64) NOT NULL COMMENT '编号', `bookid` varchar(30) default NULL COMMENT '书号', `name` varchar(100) default NULL COMMENT '书名', `author` varchar(100) default NULL COMMENT '作者', `price` float default NULL COMMENT '单价', `number` int(11) default NULL COMMENT '存库', `publish` varchar(30) default NULL COMMENT '出版社', `img` varchar(30) default 'upload/default.jpg' COMMENT '封面', `create_by` varchar(64) default NULL COMMENT '创建者', `create_date` datetime default NULL COMMENT '创建时间', `update_by` varchar(64) default NULL COMMENT '更新者', `update_date` datetime default NULL COMMENT '更新时间', `remarks` varchar(255) default NULL COMMENT '备注信息', `del_flag` char(1) NOT NULL COMMENT '删除标志', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 2.修改Generate类,修改如下 String moduleName = "books"; // 模块名,例:sys String subModuleName = ""; // 子模块名(可选) String className = "books"; // 类名,例:user String classAuthor = "石义良"; // 类作者,例:ThinkGem String functionName = "图书"; // 功能名,例:用户 // 是否启用生成工具 Boolean isEnable = true; 右击,run as java application 3.刷新整个项目,将com.thinkgem.jeesite.modules.books.web.BooksController中的 return"books/booksList"; return"books/booksForm"; 修改为 return"modules/books/booksList"; return"modules/books/booksForm"; 4.将/JeeSite/src/main/java/com/thinkgem/jeesite/modules/books/entity/Books.java中的父类修改IdEntity,并删除id成员,添加其他非红色字段成员及get、set方法。并将表映射为books表。 5.以thinkgem身份登录,在菜单管理中添加菜单:

20个代码生成框架

20个代码生成框架 1 1.1 CodeSmith 一款人气很旺国外的基于模板的dotnet代码生成器 官方网站:http://https://www.360docs.net/doc/c23070597.html, 官方论坛:http://https://www.360docs.net/doc/c23070597.html,/default.aspx 版权形式:30天试用 开源:否 需要先注册确认后才能下载 1.2 MyGenerator MyGenerator是又一个国外很不错的代码生成工具,有人觉得比CodeSmith简单、好用。 所有api可以在帮助菜单中找到。 官方网站:http://https://www.360docs.net/doc/c23070597.html,/portal/default.aspx 官方论坛: 版权形式:免费 开源:否 1.3 NHibernate. NHibernate是Hibernate公司在Java自动生成领域取得巨大成功后推出的一款ORM工具. 官方网站:http://https://www.360docs.net/doc/c23070597.html,/ 官方论坛: 版权形式:免费 开源:否 1.4 湛蓝.Net代码生成器

一款基于软件自动生成理念,能直接生成应用的dotnet代码生成器 官方网站:http://https://www.360docs.net/doc/c23070597.html, 官方论坛:http://https://www.360docs.net/doc/c23070597.html,/ 版权形式:免费 开源:否 1.5 动软.NET代码自动生成器 一款人气很旺的免费C#代码生成器 官方网站:http://https://www.360docs.net/doc/c23070597.html, 官方论坛: 版权形式:免费 开源:否 1.6 CodePlus 专为sql server c#语言设计的代码生成器,功能还是很强大 官方网站:http://https://www.360docs.net/doc/c23070597.html, 官方论坛: 版权形式:需要少量的注册费用 开源:否 下载地址很神秘 1.7 CodeMaker asp,jsp,php代码生成工具,自动生成维护数据库信息的动态网页的代码生成器。它可以帮助ASP、JSP、PHP开发人员快速的开发简单的数据库维护程序。无需任何编码,只需将数据库结构导入到CodeMaker中并做简单的设置,CodeMaker即可生成完整的数据库操作页面。用CodeMaker可以简单快速的创建网站后台维护程序。提高开发效率数十倍以

中间代码生成程序(三地址)

#include #include #include using namespace std; stack state; stack symbol; //stackval; stack symbol2; char sen[50]; char sym[12][6]={//符号表{'s','e','e','s','e','e'}, {'e','s','e','e','e','a'}, {'r','r','s','r','r','r'}, {'r','r','r','r','r','r'}, {'s','e','e','s','e','e'}, {'r','r','r','r','r','r'}, {'s','e','e','s','e','e'}, {'s','e','e','s','e','e'}, {'e','s','e','e','s','e'}, {'r','r','s','r','r','r'}, {'r','r','r','r','r','r'}, {'r','r','r','r','r','r'} }; char snum[12][6]={//数字表{5,1,1,4,2,1}, {3,6,5,3,2,0}, {2,2,7,2,2,2}, {4,4,4,4,4,4}, {5,1,1,4,2,1}, {6,6,6,6,6,6}, {5,1,1,4,2,1}, {5,1,1,4,2,1}, {3,6,5,3,11,4}, {1,1,7,1,1,1}, {3,3,3,3,3,3}, {5,5,5,5,5,5} }; int go2[12][3]={//goto表 {1,2,3}, {0,0,0}, {0,0,0}, {0,0,0}, {8,2,3},

实验 6 简单中间代码生成

实验 6 简单中间代码生成 1、实验目的: 综合运用所学知识,集成词法分析、符号表管理等程序的成果,在语法分析和文法属性计算的基础上,完成中间代码的生成工作。让学生全面了解一个编译器工作的全过程,真正全面掌握编译的思想和方法。 2、实验的基本原理 对于一个给定文法,通过改写文法,使其满足LR(1)文法的要求,根据语义确定文法符号的属性,确定语义规则或翻译方案;根据文法特点构造LR(1)分析表,进而构造语法分析和属性计算程序。分析程序在分析表的驱动下,完成对给定的句子进行语法分析和属性计算工作,最后生成三地址中间代码序列。 3、实验内容及要求 a.实验所用文法如下。 statmt → id = exp exp → exp addop term | term addop →+ | - term→ term mulop factor | factor mulop → * | / factor → ( exp ) | id | num 其中id和num为实验二中定义的标识符和常数,因此这里还需要一个小的词法分析器来得到id和num。 b.构造文法LR(1)项目集,构造ACTION和GOTO矩阵,确认文法满足LR(1) 文法要求。 c.按一般高级语言赋值语句的计算要求定义文法的属性和语义规则,属性计算的结果 是将给定赋值语句翻译成三地址代码序列,并输出此序列。 d.从数据文件中读出赋值语句,并对其进行语法分析,对合法语句,输出其翻译结果。 e.实验数据文件中应该有多个语句,可能有正确的也应该有错误的语句;语句中的表 达式有形式简单的也应该有复杂的。每个表达式写在一行,以$结束。 4、实验步骤 准备好用于实验的赋值语句序列,并存储在文件中。 a.编写单词分析子程序,能从源程序中分离出单词(包括标识符和常数);词法分析 器以子程序形式出现,当需要进行词法分析时进行调用;

中间代码生成具体实验过程含代码

实验三中间代码生成 学号:1152185;姓名:马小军 实验目的 1.了解并掌握中间代码的生成过程和作用 2.了解并掌握四元式 3.体会属性文法在中间代码生成过程中的作用 。 实验环境 Windows7操作系统vs2010编程环境 实验内容 从文件中读入表达式,输出其四元式的结果序列 本程序只能生成赋值语句及算数表达式中间代码的四元式不能生成逻辑表达式及其他复杂语句中间代码的四元式 实验原理 三、以逆波兰式为例的实验设计思想及算法 (1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。 (2)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符数字,则分析到该数字串的结束并将该数字存入数组。 (3)如果不是数字,该字符则是运算符,此时需比较优先关系。 做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将此运算符栈顶的运算符从栈中弹出,将该字符入栈。 (4)重复上述操作(2)-(3)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为四元式。 下面给出算法流程图

实验步骤 打开并运行软件 根据提示输入要分析的源程序(文件目录下有写好的文件源文件1.txt输入即可) 运行输出结果 例如将以下源文件放入test.txt 运行结果 a:=b*c+b*d 思考 同样的思路对算法进行适当改动就可以生成其它形式的中间代码 【其他部分】 设计原理和算法思想参考 《程序设计语言编译原理》第三版国防工业出版社作者陈火旺等

程序代码自动生成系统的设计与实现

万方数据

万方数据

万方数据

万方数据

基于敏捷开发的高校网络评教系统 作者:吴衡, WU Heng 作者单位:天水师范学院物理与信息科学学院,甘肃天水,741001 刊名: 计算技术与自动化 英文刊名:Computing Technology and Automation 年,卷(期):2011,30(4) 被引用次数:1次 参考文献(8条) 1.丁增富;葛信勇构建教学质量监控体系努力提高教学质量[期刊论文]-高等农业教育 2004(03) 2.陈莉和谐校园构建于素质教育双效联动[期刊论文]-中国市场 2007(2-3) 3.成奋华;金敏基于敏捷过程的IT项目范围管理的研究与应用[期刊论文]-计算机技术与发展 2010(10) 4.徐诚斌;王金平MVC在ThinkPHP框架中的应用研究 2011(03) 5.赵国栋;黄永中开源软件在高校的应用与推广策略研究[期刊论文]-中国资源综合利用 2007(01) 6.马文龙;高宝成用php实现基于MVC模式的Web应用程序开发 2008(07) 7.原晓林基于B/S教学管理系统的开发与研究[期刊论文]-山西警官高等专科学校学报 2009(04) 8.蓝蔚青;曹剑敏;张帆高校学生网络评教系统的构建与完善[期刊论文]-高等农业教育 2006(06) 引证文献(1条) 1.蒋建洪电子商务系统协同开发实践教学研究[期刊论文]-中国教育信息化·基础教育 2013(5) 本文链接:https://www.360docs.net/doc/c23070597.html,/Periodical_jsjsyzdh201104028.aspx

编译原理-分析中间代码生成程序

实验报告 课程名称编译原理 实验学期至学年第学期学生所在系部 年级专业班级 学生姓名学号 任课教师 实验成绩 计算机学院制

开课实验室:年月日 实验题目分析中间代码生成程序 一、实验目的 分析PL/0编译程序的总体结构、代码生成的方法和过程;具体写出一条语句的中间代码生成过程。 二、设备与环境 PC兼容机、Windows操作系统、Turbo Pascal软件等。 三、实验内容 1.分析PL/0程序的Block子程序,理清PL/0程序结构和语句格式。画出Block 子程序的流程图,写出至少两条PL/0程序语句的语法格式。 2.分析PL/0程序的Block子程序和Gen子程序,了解代码生成的方法和过程。 使用概要算法来描述语句的代码生成过程。 3.自己编写一个简单的PL/0程序,能够正确通过编译,得到中间代码。列出 自己编写的源程序和编译后得到的中间代码。 4.从中选择一个语句或表达式,写出代码生成的过程。要求从自己的源程序中 选择一条语句,结合这条语句写出语义分析和代码生成过程。在描述这个过 程中,要说清楚每个功能有哪个子程序的哪条语句来完成,说清楚语句和参 数的含义和功能。 四、实验结果及分析 (一)程序标注 levmax = 3; { max depth of block nesting } { 最大允许的块嵌套层数} { 语法分析过程block } { 参数:lev:这一次语法分析所在的层次} { tx:符号表指针} { fsys:用于出错恢复的单词集合} procedure block(lev, tx: integer; fsys: symset); var dx: integer; { data allocation index } { 数据段内存分配指针,指向下一个被分配空间在数据段中的偏移位置} tx0: integer; { initial table index } { 记录本层开始时符号表位置} cx0: integer; { initial code index } { 记录本层开始时代码段分配位置}

语义分析及中间代码生成程序设计原理与实现技术--实验报告及源代码北京交通大学

语义分析及中间代码生成程序设计原理与实现技术--实验 报告及源代码北京交通大学 语义分析及中间代码生成程序设计原理与实现技术 XXX 1028XXX2 计科1XXX班 1. 程序功能描述 完成以下描述赋值语句和算术表达式文法的语法制导生成中间代码四元式的过程。 G[A]:A?V:=E E?E+T?E-T? T?T*F?T/F?F F?(E)?i V?i 说明:终结符号i 为用户定义的简单变量,即标识符的定义。 2. 设计要求 (1)给出每一产生式对应的语义动作;(2)设计中间代码四元式的结构(暂不与符号表有关)。(3)输入串应是词法分析的输出二元式序列,即某算术表达式“实验项目一”的输出结果。输出为输入串的四元式序列中间文件。(4)设计两个测试用例(尽可能完备),并给出程序执行结果四元式序列。 3. 主要数据结构描述: 本程序采用的是算符优先文法,文法以及算符优先矩阵是根据第四次实验来修改 的,所以主要的数据结构也跟第四次差不多,主要为文法的表示,FirstVT集和LastVT

集以及算符优先矩阵: struct info{ char left; vector right; vector first; vector last; }; 算符优先矩阵采用二维字符数组表示的: char mtr[9][9]; //算符优先矩阵 4. 程序结构描述: 本程序一共有8功能函数: void get(); //获取文法 void print(); //打印文法 void fun(); //求FirstVT 和 LastVT void matrix(); //求算符优先矩阵 void test(); //测试文法 int cmp(char a,char b); 比较两个运算符的优先级 1 0 -1 void out(char now,int avg1,int avg2); //打印四元式 a,int b); //定义四元式计算方法 int ope(char op,int 5. 实验代码详见附件 6. 程序测试 6.1 功能测试 程序运行显示如下功能菜单:

从MATLAB代码生成独立c语言代码

Matlab 用help lsqcurvefit MATLAB Coder可以从MATLAB代码生成独立的、可读性强、可移植的C/C++代码。 使用MATLAB Coder产生代码的3个步骤:准备用于产生代码的MATLAB算法;检查MATLAB 代码的兼容性(有些matlab代码语句并不能生成c/c++代码);产生最终使用的源代码或MEX。 利用MATLAB Coder生成c++代码,并在vs2008中验证: 一个简单的例子,两数相乘: 1、安装matlab2011a或者更新版本; 2、简单生成一个foo.m文件; function c = foo(a, b)%#codegen %This function muliplies a and b c = a * b 其中,%#codegen可以防止出现警告错误 3、在命令窗口,输入mex -setpu,选中一个存在的编译器; 4、在命令窗口输入coder(图形界面),回车,弹出MATLAB Coder Project对话框; 5、在New选项卡Name中输入一个工程名foo.prj;点击Ok,弹出MATLAB Coder MEX Function 对话框; 6、在Overview选项卡中,点击Add files,弹出对话框,选中foo.m打开; 7、单击变量a,选择Define by Example…,弹出MATLAB Coder Define by Example对话框,在MATLAB Expression中输入5,点击OK;同样变量b也进行相应操作,输入6; 8、选中Build选项卡,Output type中选择c/c++ Static Library;选中Generate code only; 9、点击More settings,GeneralàLangu age选择C++;Interface选项中去掉所有选项;Close; 10、点击Build,进行编译;点击View report,弹出Code Generation Report对话框,此时,变量a、b、c会显示相应的变量信息; 11、利用vs2008建立一个控制台应用程序,将生成的相关文件foo.h、foo.cpp、rtwtypes.h、foo_types.h拷到相关目录下并添加到应用程序中; 12、在foo.cpp文件中添加#include “stdafx.h”;

中间代码生成实验报告

实验程序由c语言完成,在Turboc 2.0环境中调试通过。 语义分析程序的基本做法是对文法中的每个产生式分别编写一个语义分析子程序,当程序语法部分进行推倒或规约时,就分别调用各自的语义分析程序。当语法分析结束时,语义分析也就结束了。 在本实验程序中,当语法分析部分识别出语法正确的句子时,就进入content函数(当语法分析识别出不正确的句子时,不进入content函数,也就是不进行语义分析),然后根据句子的类型进行分类,进入不同的语义处理部分。 对于赋值语句,关键是产生正确的处理算术表达式E的四元式。程序中的ec函数的功能就是产生算术表达式的四元式,在ec函数中使用了两个栈idshed,opshed,分别是算术表达式的数据栈和符号栈。每次提取一个数字和一个算符,然后将算符与与栈顶算符进行优先级比较,优先级高则将单前数字和算符进栈,低或者相等的话则将当前栈顶元素进行合并,产生四元式。直至整个算术表达式结束。其中还有一些细节问题,具体的做法可以参看程序。 对于实验给定的if语句的文法格式,条件判断式C只中可能是>或者<=两种关系,不可能是布尔表达式,这样程序就简单的多了。 通过ec函数可以产生条件判断式C中的E的四元式,然后只要加上转向四元式就可以了。本实验程序中只给出真出口的转向四元式,没有给出假出口的转向四元式,这在实际中是不可以的,但在本实验中,实际上是对每条独立的语句进行语法分析,给出假出口转向四元式实际上意义不大,而且假出口转向语句的转移目标必须要到整个语句分析结束以后才可以知道,这样就要建立栈,然后回填,这样会使程序复杂很多,所以没有加上假出口转向四元式。 对于while语句,具体的做法和if语句差不多,所不同的是当while语句结束时,要多出一条无条件转向四元式,重新转到条件判断式C的第一条四元式。当要产生无条件转向四元式时,它的转向目标C的第一条四元式已经产生了,所以具体的做起来是不太困难的。只要记下当前while中的C的第一条四元式的位置,填上就可以了。

目标程序生成

实验八目标程序生成 【实验目的】 ?了解目标代码生成阶段在编译处理过程中的功能和作用 ?了解常用的三种目标代码形式及其优缺点 ?了解虚拟机及其指令系统 ?深入理解并掌握有中间代码向目标代码转换的过程和原理 【实验学时】 4学时 【实验要求】 ?熟练掌握虚拟机的指令系统 ?理解并掌握指令选择的方法 ?理解多寄存器分配的原则和方法 ?熟练掌握基本语句从四元式中间代码形式到目标代码的翻译原理和方法 ?独立完成目标代码生成程序 【实验原理】 一、四元式到目标代码的转换分析 1.取ARG结构值对应的目标代码 四元式中间代码的操作分量和目标量以ARG结构给出,在生成目标代码的过程中,首先要根据ARG结构取得对应的值或者地址,存入累加寄存器ac中,再生成运算的目标代码。从ARG结构取值的过程如下表所示:

取ARG结构值对应的目标代码示意表 2.取变量的绝对地址对应的目标代码如下表所示: 取变量的绝对地址对应的目标代码示意表 二、关键问题的处理 1.标号和跳转的处理:处理标号和跳转,需要用到标号地址表;表的结构为: 中间代码标号目标代码地址下一项 (1) 遇到标号定位时:设标号为L,应转向的目标代码为p1,分为两种情况: ?在标号地址表中没有L项,则填写表项(L,p1,NULL),并链入表尾; ?在标号地址表中有L项(L,addr,Next),则根据当前pc,回填addr对应的目标代码。 (2) 遇到跳转代码时:设要跳到的标号为L,这条语句对应的目标地址为p2,分为两种 情况: ?在标号地址表中没有L项,则构造一个表项(L,p2,NULL),链入表尾; ?在标号地址表中有L项(L,p1,next),则从中取出L的代码地址p1,直接生成目标代码。 2.形实参结合的处理: (1) 形参为值参: ?实参是常数值:将常数值送入相应存储单元; ?实参是直接变量:找到变量的存储地址,取值,送相应存储单元; ?实参是间接变量:此时变量的存储单元存放的是地址。找到变量的存储地址,取内容作为地址,再取内容,得到实参值,送相应存储单元;

中间代码生成具体实验过程含代码

实验三中间代码生成 学号:;姓名:马小军 实验目的 1.了解并掌握中间代码的生成过程和作用 2.了解并掌握四元式 3.体会属性文法在中间代码生成过程中的作用 。 实验环境 Windows7操作系统vs2010编程环境 实验内容 从文件中读入表达式,输出其四元式的结果序列 本程序只能生成赋值语句及算数表达式中间代码的四元式不能生成逻辑表达式及其他复杂语句中间代码的四元式 实验原理 三、以逆波兰式为例的实验设计思想及算法 (1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。 (2)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符数字,则分析到该数字串的结束并将该数字存入数组。 (3)如果不是数字,该字符则是运算符,此时需比较优先关系。 做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将此运算符栈顶的运算符从栈中弹出,将该字符入栈。 (4)重复上述操作(2)-(3)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为四元式。 下面给出算法流程图

实验步骤 打开并运行软件 根据提示输入要分析的源程序(文件目录下有写好的文件源文件1.txt输入即可) 运行输出结果 例如将以下源文件放入test.txt 运行结果 a:=b*c+b*d 思考 同样的思路对算法进行适当改动就可以生成其它形式的中间代码 【其他部分】 设计原理和算法思想参考 《程序设计语言编译原理》第三版国防工业出版社作者陈火旺等

附录代码 #include #include #include #include using namespace std; #define MAX 100 int m=0,sum=0;//sum用??于?¨2计?算?运?算?符¤?的ì?个?数oy //m用??于?¨2标à¨o记?输o?入¨?表à¨a达??式o?中D字á?符¤?的ì?个?数oy char JG='A'; char str[MAX];//用??于?¨2存??输o?入¨?表à¨a达??式o? int token=0;//左á¨?括¤?§号?的ì?标à¨o志? /***********用??于?¨2更¨1改?计?算?后¨?数oy组á¨|中D的ì?值|ì**************/ void change(int e) { int f=e+2; char ch=str[f]; if(ch>='A'&&ch<='Z') { for(int l=0;l='A'&&str[e]<='Z') { for(int i=0;i

实验四 中间代码生成

实验四中间代码生成 一、实验目的: 了解中间代码的产生过程。 二、实验学时: 2学时。 三、实验内容 在实验三的基础上,生成表达式的中间代码(可用任一种中间代码表示形式)。 四、实验方法 对实验三的代码加以改造(属性文法参照书171页表7.3),使之输出对应的中间代码(而不是表达式的值)。 五、处理程序例 例: 正确源程序例: 23+(45+4)* 40 结果应为:(三地址代码形式) T1:=45+4 T2:=T1*40 T3:=23+T2 附:实验三自下而上语法分析及属性计算 一、实验目的: 通过本实验掌握LR分析器的构造过程,并根据语法制导翻译,掌握属性文法的自下而上计算的过程。 二、实验学时: 4学时。 三、实验内容 根据给出的简单表达式的语法构成规则(见五),编制LR分析程序,要求能对用给定的语法规则书写的源程序进行语法分析和语义分析。 对于正确的表达式,给出表达式的值。 对于错误的表达式,给出出错位置。 四、实验方法 采用LR分析法。 首先给出S-属性文法的定义(为简便起见,每个文法符号只设置一个综合属性,即该文法符号所代表的表达式的值。属性文法的定义可参照书137页表6.1),并将其改造成用LR分析实现时的语义分析动作(可参照书145页表6.5)。 接下来给出LR分析表。 然后程序的具体实现: LR分析表可用二维数组(或其他)实现。

添加一个val栈作为语义分析实现的工具。 编写总控程序,实现语法分析和语义分析的过程。注:对于整数的识别可以借助实验1。 五、文法定义 简单的表达式文法如下: E->E+T|E-T|T T->T*F|T/F|F F->(E)|i 上式中,i 为整数。 六、处理程序例 例1: 正确源程序例: 23+(45+4)* 40 分析结果应为:正确的表达式。其值为:1983 例2: 错误源程序例: 5+(56+)-24 分析结果应为:错误的表达式:出错位置为)

相关文档
最新文档