zoukankan      html  css  js  c++  java
  • 《游戏脚本高级编程》——这本书实际上是讲虚拟机...

     我当年入门编译原理的时候,是看了一本《游戏脚本高级编程》的书。这本书虽然写得拖泥带水,内容也特别浅,但正是这本书让我知道了如何从头到尾实际地写一个parser + 虚拟机,让我入了门。后来我在

    的指点下看了Parsing Techniques,自己写了一个正则表达式匹配和LR(K) Parser (类似于yacc)。但重要的是这本书让我理解了如何手写Parser(这是从不会到会的飞跃)。

    后来我自己写了一个功能更加全面的类似于javascript的函数式动态脚本语言顺带支持了面向对象,进一步加深了理解。

    再后来,我看了《Engineering a Compiler》,通过这本书全面的学习了后端优化知识。

    再再后来,我阅读了LLVM的代码,陆陆续续写了四五个compiler,目前自认为算是基本了解了这个领域。

    总的感觉是,虽然看书重要,但是如果不亲自实现一遍很难真的学会。

    (笔记)《游戏脚本高级编程》——第 1 章脚本编程概论

    _daydash 的博客 - CSDN 博客

    1. 怎样具体设计一个游戏呢?程序员使用一个编译器将设计文档中的具体说明编写成实际的功能程序,美术设计人员使用 Photoshop 和 3ds max 之类的图像处理和生成软件将概念艺术和框架转化为具体的图形,而音乐制作人员则利用一个 MIDI 生成器或其他的跟踪软件将他们脑海中的声音转化成音乐游戏。那么,问题来了,没有工具或器械能够将故事情节和游戏角色也 “输入” 进去。

    2. 解决的方法:

    ①让程序员在游戏引擎中手工编写出所有这些编码。以游戏的物品为例,为描述物体,要写一个结构体:

    typedef struct _Item
    {
        char * pstrName; //物品的名称
        int iType;  //物品的具体类型
        int iPrice; //在物品店的价格
        int iPower; //具有的能量数量
    }Item;

    其中 iType 是物品的类型,游戏引擎需要利用这个数据了解该物品被使用时应具有什么功能。类似这种描述物品功能的常量应该事先定义:

    const HEAL = 0;
    const MAGIC_RESTORE = 1;
    const ARMOR_REPAIR = 2;
    const TELEPORT = 3;

    这为选择物品类型提供了一个非常实用的方法。如果一个物品类型是 Heal,那么它存放的就是游戏玩家的健康值。下一个是魔力值,接着是修复装甲,最后是在一定的游戏条件下,游戏玩家由一个游戏世界快速跳到另一个游戏世界中。

    iPower 的作用是无论当你准备使用这个物品实现什么样的处理,该物品都应该达到这个数量或者这种程度。即如果物品想要存放 HP,而且 iPower=32,那么玩家就可以取出这 32 点生命值并使用它们。

    这样就可以定义很多物品了,物品的定义暂且忽略,不过会出现一个问题,那就是物品的平衡问题,该问题贯穿整个游戏开发,并不是一件小事情,要不断地进行修改,但是你在游戏引擎中修改后会花费大量的时间进行重新编译,更加糟糕的是,重新编译的代码中有 99.9% 的内容并没有改变。

    这种方法的弊端已经出现了:首先,当你直接将你的物品描述实现在游戏代码之中时,你就需要重新编译和它相关的所有内容,浪费了大量的时间;其次,是它的组织结构。RPG 的游戏引擎问题也很复杂。

    ②通过将逻辑和具体实现相互分离来改善这种方法:

    现在问题的关键是如何将游戏代码和游戏内容加以分离

    用第①种方法定义物品的代码是:

    ItemArray[1].pstrName = "Magic Potion Lv 6";
    ItemArray[1].iType = MAGIC_RESTORE;
    ItemArray[1].iPrice = 250;
    ItemArray[1].iPower = 60;

    现在,将等号右边的所有内容提取出来,存放到某个 ASCII 文件中:

    Magic Potion Lv 6
    MAGIC_RESTORE

    这两种方法表示形式相同,唯一的区别是它所封装的所有 C/C++ 代码都是被分离的,在经过以下步骤装载到游戏中:

    (1) 打开文件并确定物品描述数组里面的哪个索引是用来存储相关内容的。可采取循环方式

    (2) 读取第一个字符串并将其存放到变量 pstrName 中

    (3) 读取下一行,如果本行的内容是 HEAL,就将变量 iType 赋值为 HEAL,以此类推

    (4) 读取下一段内容,将其由字符串类型转化为整型并将其存放到变量 iPrice 中

    (5) 读取下一段内容,将其由字符串类型转化为整型并将其存放到变量 iPower 中

    (6)重复上面的 (1)~(5) 步,知道所有的物品都被赋值为止

    这样,每次运行程序的时候,程序将会在这个 ASCII 文件中读取数据,而不用重新加载程序代码以修改物品数据,这就是游戏脚本编程的概念。这个例子告诉我们所有类型的脚本编程背后的基本准则——如何避免硬编码

    3. 简单来说,硬编码是指当你想把物品直接写入到游戏引擎中时所做的工作。也就是一种以一种严格的、固定的和难以编写的方式来编写程序和数据

    4. 第②种方法已经可以满足大部分的需求了,但是却无法从根本上对这些问题加以解决,是因为我们迟早要用到一些独特而又复杂的物品,这样的系统所能实现的功能是非常有限的。但是如果对单个物品进行编码,比如加入 if 条件判断写入引擎,那么又陷入了前面硬编码的误区。于是,脚本就用到了。脚本可以使你真正在游戏引擎之外编码,然后再载入游戏引擎中执行

    5. 那么脚本是如何运行的呢?脚本和传统编程非常相似,唯一的区别在于运行时它们是怎样被加载和执行的。在了解脚本运行过程之前,先了解一下普通程序是如何运行的。

    用高级语言编写的代码最终都会转换为低级的,可被机器识别的形式。原因是机器并不能识别这种高级语言,需要编译器将 C/C++ 这种语言转化成为最低级的、字节流形式的纯代码,即转化成为机器码。但机器码难以理解,故一般被写成更加容易理解的汇编语言形式。

    一旦代码被编译了,一切都会变得更加迅速。编译器将所有编译的代码交给一个称为 linker(链接器)的程序,它将输入的大量的指令集封装成一个带有一定头信息的、简洁紧凑的可执行文件,并最终生成一个. EXE(或是其他操作系统使用的扩展名)文件。当你运行这个可执行文件时,操作系统将调用程序装载器 program loader(通常简写为 loader),它负责将代码. EXE 文件中抽取出来并将其装载到内存中。然后程序装载器将通知中央处理器内存中第一条需要处理的指令的地址,也称为程序入口点(如 C/C++ 主函数 main() 的入口地址)。接着,程序就可以执行了。

    简单来说,这基本上就是计算机科学背后的哲学体系:将问题和算法转化成为高级语言代码,将高级语言代码转换成为低级语言代码,让低级语言代码通过 CPU 加以执行,从而(有望)解决问题。

    那么理解脚本的运行过程就简单了不少。与上述过程不同的是,脚本编译器不能将源码转化为任何类型 CPU 的机器码,因为这些代码不会直接运行在 CPU 上,而是通过虚拟机运行的。虽然虚拟机和 CPU 非常相似,但它们二者仍有一些不同——虚拟机是软件而 CPU 是硬件。它们的基本工作是获取下一条将要执行的指令,分析指令将要完成的功能并执行该条执行。不同的是虚拟机只能识别它特定的那种汇编语言(字节码)。

    虚拟机的另一个特性是,至少在游戏脚本编程的内部,它通常不是孤立的。相反,它是一个特殊的 “模型”,可产生其他程序或与之集成。与 CPU 很相似,CPU 也需要和主板、RAM 等东西集成,单纯的 CPU 是没有什么用处的。与虚拟机集成的程序被称为主应用程序,我们最终也是为这个程序编写脚本。

    因此,一个脚本系统并不仅仅只是为自己设计一个高级的、C/C++ 形式的语言,它还需要创立一种低级的汇编语言,即虚拟机器码。——所以这本书实际上是讲虚拟机。。。nxgc

    除了一套运行环境,虚拟机还为运行的脚本和主应用程序提供一个通信通道,即交互界面

    主应用程序会提供具有一组功能函数的运行脚本,这些功能函数被称为 API

    6. 脚本系统的基本类型:

    时刻牢记:大型而又复杂的特征列表看起来的确很好,但是当你不需要它们的时候,它们就只会使你的程序结构凌乱,运行缓慢

    ①面向过程的语言系统和面向对象的语言系统:

    这些系统使用高级的、结构化的或者是面向对象的语言来编写脚本,这些脚本随后被编译成能够在虚拟机内部运行的虚拟机器码,或者不加编译,直接由解释器解释执行。系统使用的虚拟机或解释器将会与主应用程序相集成,使得主应用程序可以调用脚本并和脚本进行交互。特点是非常灵活、自由,事实上可以适用于任何一种主要的计算任务

    ②基于命令的语言系统:

    通常会像 LOGO 语言一样,非常具体,它们全部由接受一个到两个参数的针对具体程序的命令组成。如下面一段代码:

    MovePlayer 10, 20
    PlayerTalk "Something is hidden in these bushes..."
    PlayAnim SEARCH_BUSHES
    PlayerTalk "It's the red sword!"
    GetItem RED_SWORD
    形成这个虚拟语言的命令都是非常明确的,它们都针对于非常具体的某一种 RPG 游戏,都针对于非常具体的某一种 RPG 游戏,很少甚至没有什么灵活性。

    ③动态链接的模块系统:

    当一个复杂的脚本在一个虚拟机上运行时,它的运行速度明显要比纯粹的机器码直接在 CPU 上运行的速度慢得多。为了避免这个问题,许多游戏采用动态链接脚本模块。即一些 C/C++ 程序段,这些程序段像游戏本身一样被编译成纯粹的机器码,并且在运行时被链接、载入。由于使用常见的 C/C++ 语言编写,运行速度特别快,而且功能强大。

    动态链接模块通过游戏向它们提供的 API 接口和游戏进行通信。通过使用这种 API 接口,模块可以检索和修改游戏的状态信息,从而从外部控制游戏。通常被称为 mods

    ④编译型代码和解释型代码:

    编译型代码就是那些称为机器码的一系列指令,它们就是通过将那些人类可以识别的代码转化而成的机器可以识别的代码。

    解释性代码是通过解释器的程序来运行的。解释器是很难实现的,一方面,它们几乎要具有编译器所有的复杂语言分解功能函数;另一方面,它们又要足够迅速地解决这些问题从而能够达到实时的效果

    ⑤现有的脚本编程方法:

    (1)Ruby:

    是一种完全面向对象的脚本语言,侧重于系统管理方面的工作。具有一系列很好的高级特性,如垃圾收集、动态库装载,以及多线程。解释型语言

    (2)Lua:

    Lua 是轻量级的强大的扩展编程语言,面向过程的脚本设计系统。最明显的特征是可以用它编写的程序来对它自身进行拓展。因此,它的核心代码很少,通常是用户来实现附加的特征。也能很好地和 C/C++ 进行交互

    (3)Java:

    Java 虚拟机,JVM,可以很容易地调用本地接口 JNL 和 C/C++ 程序进行结合,因为它广泛应用于专业级的电子商务中,所以 JVM 对于编译脚本来说是最优的多线程运行环境,且这门语言本身也很灵活而且高度的面向对象


    (1 条消息)(笔记)《游戏脚本高级编程》——第 2 章脚本编程系统的应用(上)_daydash 的博客 - CSDN 博客

    本章内容:

    • 介绍脚本系统是如何用于解决下列问题的:
    • RPG 游戏中与内容相关的部分——非玩家角色和场景细节
    • RPG 游戏中的物品、武器和敌人
    • 第一人称射击游戏中的物品、谜题和机关
    • 第一人称射击游戏中敌人的行为

    1. 游戏引擎和内容是完全分离的,所以当游戏玩家购买游戏软件的时候,实际上购买了两个部分:一个编译过的游戏引擎和一系列可以对游戏功能进行扩展的脚本。这种结构称之为模块化结构。这种方法一种常见的应用是以 “情节(episode)” 的形式发布游戏。这就意味着游戏软件店在在销售游戏软件的时候只出售一部分游戏内容以及可以在上面运行游戏的游戏引擎。当游戏玩家完成了第一个游戏情节的时候,他们就可以以较少的费用下载或者购买另外的如 patches 和 add-ons 样式的游戏情节(这不就类似于 DLC 吗)。

    2. 角色扮演游戏(Role Playing Games,RPGs)

    RPG 游戏一般要比其他类型的游戏要使用更多的脚本,原因就在于 RPG 游戏的确适宜于使用脚本,它们确确实实需要大量的游戏内容。自然而然,RPG 的游戏开发人员就需要一个很好的方法以一种结构化的、有组织的方式来开发这些内容。

    下面将分析一下 RPG 游戏的传统内容,以便理解为什么脚本对于设计角色扮演的游戏来说那么适用。

    ①复杂而又有深度的故事情节:

    RPG 游戏看起来更像是一部交互式小说,这就意味着游戏中将会出现进行无穷行对话的人物角色和带有无数 “场景点” 的高度结构化的游戏场景。而在玩家冒险经历的每一个场景点,游戏都需要玩家在到达该处之前完成了哪些主要的事情以确定游戏世界中的现在状态以及将要发生的事情。解决的方法就是采用一组“标记(flags)” 来记录场景点或者是游戏世界中的现在状态。每个标记代表游戏中的一个事件,可以为 TRUE 或者 FALSE(当然可以不是布尔类型)。

    这套系统的实现方法有很多种。一种就是在引擎的源代码中直接建立标技术组,并向游戏脚本提供一个访问接口,这个接口允许脚本对于数组进行读写操作。这种方法将所有逻辑和功能都写在了脚本上,脚本只需通过接口访问数组就可以完成这套系统。第二种方法就是如果脚本系统能力很强的话,甚至可以将数组从引擎中剥离,直接将数组存储到脚本内部并访问,这种方法是最理想的,因为这让游戏逻辑和引擎进行了分离。不过前者也是可用的。

    ②非玩家角色(Non-Player Characters, NPCs)

    NPC 在游戏里也是至关重要的,他们可以为主角进行剧情的引导,或是和他们交换物品。因此,条件逻辑、循环以及读取游戏 flags 的能力等就会变得很关键。解决方法如下:

    一些简单的对话,只需在 NPC 和脚本之间建立一些一对一的对应关系。当有 NPC 问一些带有选项的问题时,系统会根据玩家应答的结果的不同而采用不同的对话策略。或者其他的一些不同的情况,使得 NPC 变得十分逼真和灵活,这就需要一种更加强大的描述语言来描述他们的行为。

    这种语言所需要的特征:

    (1) 应该具备基本的对话能力

    (2) 在对话中可以通知游戏引擎在什么时候加入动画

    (3) 能够查看玩家的物品清单

    (4) 能够提供一组定做的答案

    (5) 记录玩家和这个角色的交往历史。此外,理论上游戏玩家在这些单独的对话期间可能会退出游戏然后又重新进入,那么就不仅需要在游戏期间在内存上保留信息,在不同的游戏期间还要把它们保存到磁盘上。即无限期地保存这个 NPC 相关的变化信息

    (6) 能够改变 flags

    下面有段对话:

    (游戏玩家第一次和 NPC 进行对话)

    NPC:"Hey, you look familiar."(斜着眼睛盯着玩家的脸)

    Player:"Do I?I don't believe we've met."

    NPC:"Wait a sec- you're the guy who's gonna save the world from the vampires,right?"

    NPC:(如果游戏玩家说的是 Yes)"I knew it!Here, take this garlic!"(将大蒜交给游戏玩家)

    Player:"Thanks!"

    (游戏玩家再次和 NPC 进行对话)

    NPC:"Sorry, I don't have any more garlic.I gave you all I had last time we spoke."

    Player:"Well that sucks."(跺脚)

    (游戏玩家第三次和非玩家角色进行对话)

    NPC:"Dude I told you, I gave you all my garlic.Leave me alone!"

    Player:"But I ran out, and there's still like 10 more vampires that need to be valiantly defeated!"

    NPC:"Hmm...well, my brother lives in the next town over, and he owns a garlic processing plant.I'll tell him you're in the area,and to batch ready for you.Next time you're there,just talk to him, and he'll give you all the garlic you need."

    Player:"Thanks, mysterious garlic-dispensing stranger!"

    NPC:"My name's Gary."

    Player:"Whatever."

    (游戏玩家和 NPC 进行了三次以上的对话)

    NPC:"So,have you seen my brother yet?"

    这段对话可以用以下类似于 C/C++ 的脚本代码编写:

    static int iConverseCount = 0;
    static bool bIsPlayerHero = FALSE;
    main()
    {
        string strAnswer;
        if(iConverseCount == 0)
        {
            NPCTalk("Hey, you look familiar.");
    	PlayAnim(NPC, SQUINT);
    	PlayerTalk("Do I?I don't believe we've met.");
    	strAnswer = NPCAsk("Wait a sec- you're the guy who's gonna save the world from the vampires,right?", "Yes", "No");
    	if(iAnswer == "Yes")
    	{
    	    NPCTalk("I knew it!Here, take this garlic!");
    	    GiveItem(GARLIC, 4);
    	    PlayerTalk("Thanks!");
    	    bIsPlayerHero = TRUE;
    	}
    	else
    	{
    	    NPCTalk("Ah.My mistake.");
    	    bIsPlayerHero = FALSE;
    	{
        }
        else
        {
    	if(bIsPlayerHero)
    	{
    	    if(iConverseCount == 1)
    	    {
    		NPCTalk("Sorry, I don't have any more garlic.I gave you all I had last time we spoke.");
    		PlayerTalk("Well that sucks.");
    		PlayAnim(PLAYER, STAMP_FEET);
    	    }
    	    else if(iConverseCount == 2)
    	    {
    		NPCTalk("Dude I told you, I gave you all my garlic.Leave me alone!");
    		PlayerTalk("But I ran out, and there's still like 10 more vampires that need to be valiantly defeated!");
    		NPCTalk("Hmm...well, my brother lives in the next town over, and he owns a garlic processing plant.I'll tell him you're in the area,and to batch ready for you.Next time you're there,just talk to him, and he'll give you all the garlic you need.")
    		PlayerTalk("Thanks, mysterious garlic-dispensing stranger!");
    		NPCTalk("My name's Gary");
    		PlayerTalk("Whatever.");
    		SetGameFlag(GET_GARLIC_FROM_GARYS_BROTHER);
    	    }
    	    else
    	    {
    	        NPCTalk("So,have you seen my brother yet?");
    	    }
    	}
    	else
    	{
    	    NPCTalk("Hello again.");
    	{
        }
        iConverseCount ++;
    }

    会发现仅仅只是添加了一些新的特征,这个语言就突然变得和 C/C++ 语言相似了。同样的,在编写一个 RPG 游戏的过程中,数组、指针、动态资源分配等许多更加高级的语言特性也会大放异彩。通常有两种方法来设计脚本:①先设计一个类似于 C/C++ 的语法体系,然后在你需要的时候添加新内容;②同时设计语法和语言的总体结构。前者要比后者容易的多。采用 C/C++ 也会是你的系统更加统一和标准

    ③物品和武器:

    因为很多物品的行为都非常像是宏,可以采用基于指令的语言来编写它们。

    物品脚本和游戏脚本一般都需要完成一系列的任务。首先要提到的就是它的后台功能,即这个物品是攻击敌人的,还是治疗友军的,还是打开一道门,还是清理一个过道等等。这个功能只要简单地更改一些代码就可以实现。基于命令的语言可以满足。然而,另一个方面是玩家可以感受到这种武器或者物品的功能的具体表现,比如武器的动画效果等。基于命令的语言就不能满足这个需求了。

    解决方法:

    最好还是采用那些 C/C++ 样式的、面向对象的语言,因为它们允许物品和武器在脚本内部定义自己的图形效果并且直到最为细节的部分。整个过程很简单,只是涉及到至少要提供一组可供脚本调用的基本的图形程序。所有这些所必须必备的不过就是下面这些常见的东西——制作图形、绘制精灵,也可能是播放电影文件使得玩家可以预览动画效果,通常由 DirectX、OpenGL 以及 SDL 所提供的图形 API 的精选子集组成。

    下面举例写一个武器实例:

    武器名为 Fire Sword。Fire Sword 的功能是向敌人发射火球,对于冰系或水系的怪物尤其有效。相反,对火属性的敌人效果较差。同时,由于它的热量,当游戏玩家每次使用这个武器时也会对自身造成一定程度上的伤害。我们就可以通过编写一个火球的动画来完成这个想法。综上,从技术层面来考虑这个武器的功能:

    (1) 需要能够修改游戏中人物的统计数据,也就是他们的体力值。还要考虑属性的克制时的伤害结算

    (2) 需要能够真正在屏幕上看到由玩家所在位置到敌人所在位置发射的火球,以及爆炸声。需要处理动画和声音,因此需要条件逻辑和循环。基于命令的语言已经不适用了。此外,主应用程序还应提供一个基本的多媒体 API 使得脚本最起码能够在屏幕上绘制精灵和播放声音。

    (3)还要处理由宝剑发射出来的热量对于自身所造成的的轻微伤害。与 (1) 类似,仅仅涉及到一些数据的问题,这意味着需要能够访问到玩家的具体状态。

    以上任务 (2) 比较难以解决,其余两个只要基于命令的语言就可解决。这就排除了基于命令的语言而改用 C/C++ 体系的语言。代码如下:

    Player.HP -= 4;
     
    int Y = Player.OnScreenY;
    for(int X=Player.OnScreenY; X<Enemy.OnScreenX; X++)
        BlitSprite(FIREBALL, X, Y);
    PlaySound(KA_BOOM);
     
    if(Enemy.Type==ICE || Enemy.Type==WATER)
        Enemy.HP -= 16;
    else if(Enemy.Type == FIRE)
        Enemy.HP -= 4;
    else
        Enemy.HP -= 8;

    ④敌人:

    敌人最为主要的部分在于它同时利用了前面的两个概念:它具有 NPC 的特征或者是个性为中心的方面,同时它还具有物品和武器的功能以及破坏特性。因此敌人是这两个实体背后的概念相结合的过程。

    解决方法:

    RPG 的打斗场面的主体框架是循环的,再循环过程的每一次迭代过程中,游戏玩家和敌人都要对输入进行分析。当敌人执行相应的打斗脚本时,玩家要准备接受输入的数据;当玩家执行相应的打斗脚本时,敌人要准备接受输入的数据。代码如下:

    void Act()
    {
        int iWeakestPlayer, iLastAttacker;
        if(iHitPoints < 20)
    	if(rand() % 10 == 1)
    	    Flee();
        else
        {
    	iWeakestPlayer = GetWeakestPlayer();
    	if(Player[iWeakestPlayer].iHitPoints<20)
    	    Attack(iWeakestPlayer, METEOR_SHOWER);
    	else
    	{
    	    iLastAttacker = GetLastAttacker();
    	    switch(Player[iLastAttacker].iType)
    	    {
    		case NINJA:
    		{
    		    Attack(iLastAttacker, THROW_FIREBALL);
    		    break;
    		}
    		case MAGE:
    		{
    		    Attack(iLastAttacker, BROADSWORD);
    		    break;
    		}
    		case WARRIOR:
    		{
    		    Attack(iLastAttacker, SUMMON_DEMON);
    		    break;
    		}
    	    }
    	}
        }    
    }
    代码解释:首先,敌人脚本会根据它会被打败的可能性有多大。如果这个结果低于一定的临界值(上述代码是低于 20 个体力值),那么它就会模拟一个试图从打斗现场逃跑的动作。如果在随机 1~10 之间随到了 1,那么就会逃跑。然而,如果他觉着有必要接着继续打斗,那么它就会调用引擎提供的函数来确定哪个是对手中最弱的。如果敌人觉着玩家比较接近被打败的状态(上述代码是玩家体力值低于 20),那么就会进行 Meteor Shower 来将对手打败。如果这个最弱的敌人并不是十分的弱小以至于很容易被打败出局,敌人就会转去袭击那个最后攻击他的人,并会基于对手的类型选择一个具体的进攻策略。

    第2章干货内容不多


    我当年入门编译原理的时候,是看了一本《游戏脚本高级编程》的书。这本书虽然写得拖泥带水,内容也特别浅,但正是这本书让我知道了如何从头到尾实际地写一个parser + 虚拟机,让我入了门。后来我在

    的指点下看了Parsing Techniques,自己写了一个正则表达式匹配和LR(K) Parser (类似于yacc)。但重要的是这本书让我理解了如何手写Parser(这是从不会到会的飞跃)。

    后来我自己写了一个功能更加全面的类似于javascript的函数式动态脚本语言顺带支持了面向对象,进一步加深了理解。

    再后来,我看了《Engineering a Compiler》,通过这本书全面的学习了后端优化知识。

    再再后来,我阅读了LLVM的代码,陆陆续续写了四五个compiler,目前自认为算是基本了解了这个领域。

    总的感觉是,虽然看书重要,但是如果不亲自实现一遍很难真的学会。


    作者:Yong He
    链接:https://www.zhihu.com/question/25868417/answer/31891651
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    语法分析和语义分析:

  • 相关阅读:
    数据库水平切分的实现原理解析---分库,分表,主从,集群,负载均衡器
    现代 PHP 新特性系列(六) —— Zend Opcache
    Apache配置参数详解
    Git的常用命令
    PHP如何防止XSS攻击
    CSRF的防御实例(PHP)
    Web安全之CSRF攻击
    PHP五种常见的设计模式
    PHP四种基础算法详解
    php遍历文件的5种方式
  • 原文地址:https://www.cnblogs.com/cx2016/p/13124108.html
Copyright © 2011-2022 走看看