编写go语言用到的编译器_如何在Go中编写编译器:快速指南

本文介绍了如何使用Go语言快速编写一个编译器,涵盖了从编译器概述、语言设计到词法分析器、解析器、抽象语法树(AST)、类型检查器和代码生成的构建过程。作者分享了简化版的编译器实现,旨在在避免复杂性的前提下提供编译器基础知识。
摘要由CSDN通过智能技术生成

编写go语言用到的编译器

by Joseph Livni

约瑟夫·利夫尼(Joseph Livni)

如何在Go中编写编译器:快速指南 (How to write a compiler in Go: a quick guide)

Compilers are awesome! ? ? ? They combine theory and application and touch on a lot of software related topics such as parsing and language construction. At their core, compilers are a program that make a program readable by the computer.

编译器很棒! ? ? ? 他们将理论和应用程序结合在一起,并涉及许多与软件相关的主题,例如解析和语言构造。 编译器的核心是使计算机可读的程序。

The inspiration for this came out of a compilers course I took this past Fall and my love for Go.

灵感来自于我去年秋天修读的编译器课程以及我对Go的热爱。

This is the guide I wish I had when starting my journey into compilers. There are a lot of books, videos, and tutorials on how to create compilers. The goal of this post is to strike a balance between providing a nontrivial example of some of the things a compiler can do while avoid getting stuck in the weeds. ?

这是我入门编译器时希望得到的指南。 有很多关于如何创建编译器的书籍,视频和教程。 这篇文章的目的是在提供一个简单的例子来说明编译器在避免陷入困境的同时可以做的一些事情之间取得平衡。 ?

The result will be a compiler that can execute a small made up language.To checkout and run the final project see the instructions below. ?

结果将是可以执行一种小型组合语言的编译器。要签出并运行最终项目,请参见以下说明。 ?

Note: Remember that Go is strict about absolute paths when running this

注意:请记住,运行此命令时Go严格遵守绝对路径

cd $GOPATH/src/github.com/Lebonescogit clone https://github.com/Lebonesco/go-compiler.gitcd go-compilergo test -vgo run main.go ./examples/math.bx
编译器概述 (Outline of the Compiler)
  • Lexer/Parser

    词法分析器/解析器

  • AST Generator

    AST发电机

  • Type Checker

    类型检查器

  • Code Generation

    代码生成

语言 (The Language)

The goal of this post is to get you familiar with compilers as quickly as a possible so we’ll keep the language simple. For Types we’ll work with strings, integers, and bools. It will have Statements that include func, if, else, let, and return. This should be enough to have fun working with some of the complexities of a compiler.

这篇文章的目的是使您尽快熟悉编译器,以便我们使语言保持简单。 对于Types,我们将使用stringsintegersboolsif不包含, if包含func 语句elseletreturn 。 这应该足以使您有趣地使用一些编译器。

The first compiler that I built, I completed over the course of two months and took up 1000’s of lines of code. I took some shortcuts in this post in order to show you the key fundamentals.

我构建的第一个编译器在两个月的时间内完成了工作,并占用了1000行代码。 为了使您了解关键的基础知识,我在本文中采取了一些捷径。

Two common components that our language is missing are classes and arrays. These add additional complications we don’t have time for right now. If it turns out that people really want to know how to handle these elements I’ll write a followup.

我们的语言缺少的两个常见组件是classesarrays 。 这些增加了我们现在没有时间的其他复杂性。 如果事实证明人们真的想知道如何处理这些元素,那么我将写一篇后续文章。

Some example code:

一些示例代码:

func add(a Int, b int) Int {    return a + b;}
func hello(name String) String {    return "hello:" + " " + name;}
let num = add(1, 2);let phrase = string hello("Jeff");let i = int 0;let result = "";
if (i == 2) {    result = hello("cat");} else {    result = hello("dog");}
PRINT(result);
快速设置 (Quick Setup)

The only outside package we need is gocc, which will help build the lexer and parser.

我们唯一需要的外部软件包是gocc 它将帮助构建词法分析器和解析器。

To get it run:

要使其运行:

go get github.com/goccmack/gocc

Make sure the bin folder where gocc is located is in your PATH :

确保gocc所在的bin文件夹在您的PATH

export PATH=$GOPATH/bin:$PATH

Note: If you’re having problems at this stage try running go env to make sure that your $GOROOT and $GOPATH are correctly assigned.

注意:如果在此阶段遇到问题,请尝试运行go env以确保正确分配了$GOROOT$GOPATH

Cool, let’s dive into some code.

太酷了,让我们深入研究一些代码。

构建词法分析器 (Building the Lexer)

The lexer’s job is to read the program and output a stream of tokens that are consumed by the parser. Each Token contains the type that the token represents in the language and the string Literal of that token.

词法分析器的工作是读取程序并输出解析器使用的令牌流。 每个Token包含令牌用该语言表示的type以及该令牌的字符串Literal

To identify the pieces of the program we will be using regular expressions. gocc will then convert these regular expressions into a DFA (Deterministic Finite Automata) which can theoretically run in linear time.

为了识别程序的各个部分,我们将使用正则表达式。 然后,gocc会将这些正则表达式转换为DFA ( 确定性有限自动机 ),该DFA理论上可以在线性时间内运行。

The notation that we’ll be using is BNF (Backus–Naur form). Don’t confuse this with EBNF (extended Backus–Naur form) or ABNF (augmented Backus–Naur form) which have some added features. Keep this in mind when looking at other examples online that could be using other forms which provide more syntactic sugar.

我们将使用的表示法是BNF ( Backus–Naur形式 )。 不要将此与具有某些附加功能的EBNF ( 扩展Backus–Naur形式 )或ABNF ( 增强Backus–Naur形式 )相混淆。 在网上查看其他示例时,请牢记这一点,这些示例可能正在使用提供更多语法糖的其他形式。

Let’s start with the basics and define what strings and integers will look like.

让我们从基础开始,定义什么样的stringsintegers

Note that:

注意:

  • All token names must be lower case

    所有令牌名称必须小写
  • Any key preceded by ‘!’ will be ignored

    任何以“!”开头的键 将被忽略
  • Any key preceded by ‘_’ will not by turned into a token

    任何以“ _”开头的键都不会变成令牌
  • Any expression enclosed by ‘{‘ expression ‘}’ can be repeated 0 or more times

    '{' expression '}'内的任何表达式都可以重复0次或更多次

In the below example we are ignoring all white space and have defined an int and string_literal token.

在下面的示例中,我们忽略了所有空格,并定义了一个intstring_literal token

Every language has built in keywords that are reserved words that deliver special functionality. But, how will the lexer know whether a string is a keyword or a user created identifier? It handles this be giving preference to the order in which tokens are defined. Therefore, let’s define keywords next.

每种语言都有内置的keywords ,这些keywords是保留字词,可提供特殊功能。 但是,词法分析器如何知道stringkeyword还是用户创建的identifier ? 它通过优先考虑定义令牌的顺序来进行处理。 因此,让我们接下来定义keywords

We’ll finish this up by adding the punctuation necessary for expressions.

我们将通过添加表达式所需的标点符号来完成此操作。

Cool! Let’s see if this is actually doing what we want with some unit tests. Feel free to just paste this part into your IDE. ?

凉! 让我们看看这是否确实在完成一些单元测试所需的工作 。 随意将这部分粘贴到您的IDE中。 ?

Note: It’s generally good practice in Go to place test files in the relevant subdirectory, but for simplicity I’m placing all tests in the root.

注意:在Go中将测试文件放置在相关的子目录中通常是一个好习惯,但是为了简单起见,我将所有测试都放置在根目录中。

To test our scanner run:

要测试我们的扫描仪运行:

$ gocc grammer.bnf$ go test -v=== RUN   TestToken--- PASS: TestToken (0.00s)PASSok      github.com/Lebonesco/compiler       0.364s

Great, we now have a working Lexer ?

太好了,我们现在有一个工作的Lexer吗?

构建解析器 (Building the Parser)

Building the Parser is similar to how we built the Lexer. We will construct a set of element sequences that when correctly match a stream of tokens produce a grammar. This will also run in linear time by internally converting our NFA (Non-Deterministic Automaton) grammar to DFA (Deterministic Finite Automaton).

构建Parser类似于我们构建Lexer 。 我们将构建一组元素序列,当正确匹配令牌流时,它们会产生语法。 通过将我们的NFA ( 非确定性自动机 )语法内部转换为DFA ( 确定性有限自动机 ),这也将在线性时间内运行。

Let’s keep things simple. What actually is our program? Well, it’s a bunch of Statements in which each Statement can contain a set of Statements and/or Expressions. This seems like a good place to start our grammar.

让我们保持简单。 我们的程序到底是什么? 嗯,这是一堆Statements ,其中每个Statement可以包含一组Statements和/或Expressions 。 这似乎是开始我们的语法的好地方。

Below is the beginning recursive hierarchy of the program. Statements is a sequence of zero or more Statements and Functions is a list of functions. Our languages requires functions to be defined before other Statement types. This will reduce some headache during the type checking phase. empty is a keyword in BNF that represents an empty space.

下面是程序的开始递归层次结构。 Statements是零个或多个Statements的序列。 FunctionsFunctions的列表。 我们的语言要求在其他Statement类型之前定义函数。 这将减少类型检查阶段的麻烦。 emptyBNF中的关键字,表示空白。

But wait! What is a Statement? It’s a unit of code that doesn’t return a value. This includes: if, let, and return statements. This is opposed to Expressions which do return values. We will get to those next.

可是等等! 什么是Statement ? 它是不返回值的代码单元。 这包括: ifletreturn语句。 这与确实返回值的Expressions相反。 我们接下来将介绍这些内容。

Below is our grammar for Statement and Function. A StatementBlock is a larger abstraction that encapsulates a list of Statements with braces { }.

下面是StatementFunction语法。 StatementBlock是一个较大的抽象,它用大括号{ }封装了一系列Statements

Next lets build out our Expression which handles all infix operations such as +, -, *, <, >, ==, and, and or.

接下来,让我们构建处理所有中缀操作(例如+-* & lt)的Expression ;&g t; , = =和,和或。

Almost done with a fully working grammar! Let’s wrap things up by defining our parameter insertion. Each function can accept any amount of values. When defining a function we need to label the argument types in the signature while a called function can accept any amount of Expressions.

几乎完全可以使用语法了! 让我们通过定义参数插入来总结一下。 每个function可以接受任意数量的值。 定义函数时,我们需要在签名中标记参数类型,而被调用函数可以接受任意数量的Expressions

Now that our parser is completed let’s add some code to our driver, main.go.

现在我们的解析器已经完成,让我们向驱动程序main.go添加一些代码。

As we progress through the compiler we will add more functionality to this driver.

随着编译器的进行,我们将向该驱动程序添加更多功能。

One of the things challenging about building a parser is that there’re many different ways to define the grammar. ?

构建解析器所面临的挑战之一是,有许多不同的方法来定义语法。 ?

We won’t really know how well we did until we get into the next section which uses the output we just generated. The difficulty of building the static type checker will be strongly influenced by our grammar design. Keep your fingers crossed ?.

在进入下一部分使用刚刚生成的输出的部分之前,我们不会真正知道我们的性能如何。 构建静态类型检查器的难度将受到我们语法设计的强烈影响。 手指交叉吗?

测试解析器 (Test Parser)

We’ll keep this simple because at this point our parser can still produce false positives. Once we start working on the AST we can check its accuracy.

我们将保持简单,因为此时解析器仍会产生误报。 一旦我们开始研究AST,我们就可以检查其准确性。

go test -v=== RUN   TestParser--- PASS: TestParser (0.00s)=== RUN   TestToken--- PASS: TestToken (0.00s)PASSok      github.com/Lebonesco/go-compiler        7.295s

Congrats ? ? ?! You now have a fully working Lexer and Parser. Time to move onto the AST (Abstract Syntax Tree).

恭喜 ? ? ?! 现在,您已经可以正常使用Lexer和Parser。 是时候进入AST (抽象语法树)了。

抽象语法树 (Abstract Syntax Tree)

The best way to understand an abstract syntax tree is in relation to a parse tree which is what we generated in the last post. A parse tree represents each part of the program that is matched in our grammar.

理解抽象语法树的最佳方法是与解析树相关,解析树是我们在上一篇文章中生成的。 语法分析树表示程序中与我们的语法匹配的每个部分。

By contrast, an AST only contains the information related to type checking and code generation, and skips any other extra content that is used while parsing the text.
相比之下,AST仅包含与类型检查和代码生成有关的信息,并且跳过解析文本时使用的任何其他额外内容。

Don’t worry if that definition doesn’t makes sense right now. I always learn best by actually coding, so let’s jump into it!

如果该定义现在没有意义,请不要担心。 我总是通过实际编码学习得最好,所以让我们开始吧!

Create a new directory and two new files. ast.go will contain our AST generating functions and types.go will have the tree node types.

创建一个新目录和两个新文件。 ast.go将包含我们的AST生成函数和types.go将具有树节点类型

mkdir astcd asttouch ast.gotouch types.go

Like with the parse tree, we will define our structure from top to bottom. Every node will either be a Statement or Expression. Go isn’t object oriented so we’ll use a composition pattern utilizing interface and struct to represent our node categories. Our AST will return a Program node that contains the rest of the program. From now on, any struct we created with a TokenLiteral() method is a node. If that node has a statementNode() method it will fit the Statement interface and if it has a expressionNode() method it’s an Expression.

像解析树一样,我们将从上到下定义我们的结构。 每个node将是一个StatementExpression 。 Go不是面向对象的,因此我们将使用通过interfacestruct表示node类别的合成模式。 我们的AST将返回一个包含该程序其余部分的“程序Program节点。 从现在开始,我们使用TokenLiteral()方法创建的任何结构都是一个node 。 如果该node具有statementNode()方法,则它将适合Statement接口;如果它具有expressionNode()方法,则它是一个Expression

In addition, we’ll be adding json tags to each struct field to make it easier when we convert our AST into json for testing purposes.

另外,我们将在每个struct字段中添加json标记,以简化将AST转换为json以进行测试时的操作。

Now let’s start building our Statement structs based off of the Statement and Node interfaces.

现在,让我们开始基于StatementNode接口构建Statement结构。

Note: json:"-" means that the field will be omitted from our json.

注意: json:"-"表示该字段将从json中省略。

Next we need some hooks to connect our nodes and parser. The code below allows our Statement nodes to fit the Node and Statement interfaces.

接下来,我们需要一些钩子来连接nodesparser 。 下面的代码允许我们的Statement节点适合NodeStatement接口。

We then need the hooks that will be called by the parser.

然后,我们需要解析器将调用的钩子。

As you can see, most of our code is checking and casting our input type.

如您所见, 我们的大多数代码都是检查并转换我们的输入类型。

These hooks will then be called by the parser in grammar.bnf. To access these functions we’ll need to import "github.com/Lebonesco/go-compiler/ast.

然后,解析器将在grammar.bnf调用这些挂钩。 要访问这些功能,我们需要import "github.com/Lebonesco/go-compiler/ast

Now when a piece of grammar is identified, it calls an AST hook and passes in the necessary data to construct a node.

现在,当识别出一条语法时,它会调用AST钩子,并传递必要的数据以构造一个node

As you could imagine, there is a lot of flexibility in how you want to generate your AST. There are design strategies that reduce the amount of unique nodes in the AST . However, having more node types will make your life easier when we get to the typechecker and code generation. ?

可以想象,在生成AST的方式上有很多灵活性。 有一些设计策略可以减少AST中唯一节点的数量。 然而,有更多的节点类型将使您的生活更轻松,当我们到了typecheckercode generation 。 ?

This part has a lot of code. However, it’s not very difficult and mostly all the same. The error handling in Go can feel a bit tedious, but in the long run it’ll be worth it when we make a silly mistake. Safety First ?

这部分有很多代码。 但是,这并不是很难,而且几乎都一样。 Go中的错误处理可能会有些乏味,但是从长远来看,当我们犯了一个愚蠢的错误时,这是​​值得的。 安全第一 ?

We’re not going to do anything too crazy with our error handling because I want to save on lines of code. However, if you feel so inclined you can add more descriptive and useful errors.

我们不会对错误处理做任何疯狂的事情,因为我想节省代码行。 但是,如果您倾向于这样做,则可以添加更多描述性和有用的错误。

Let’s move on to Expressions!

让我们继续进行Expressions

Fit them into the Node and Expression interfaces.

使它们适合NodeExpression接口。

And create the Expression hooks.

并创建Expression挂钩。

Finally, insert the hooks into the parser.

最后,将钩子插入parser

测试AST发生器 (Test AST Generator)

The test cases for the AST generator are the most tedious to write. But trust me, this is not a part you want to mess up on. If you have problems here, those bugs will rollover into your type checker and code generator. ?

AST生成器的测试用例编写起来最繁琐。 但是请相信我,这不是您想要弄混的部分。 如果您在此处遇到问题,则这些错误将累积到type checkercode generator 。 ?

In my opinion, if code doesn’t have tests it’s broken

我认为,如果代码没有测试,那就坏了

In our AST test we will construct what our final result should look like. To avoid comparing elements such as tokens, that could create false negatives, we convert our result and expected output into json before comparing with a deep comparison function, reflect.DeepEqual().

在AST测试中,我们将构造最终结果。 为了避免比较可能会产生假阴性的诸如tokens元素,在与深度比较函数reflect.DeepEqual() 比较之前,我们将结果和预期输出转换为json。

Run Test:

运行测试:

go test -v=== RUN   TestAST--- PASS: TestAST (0.00s)=== RUN   TestParser--- PASS: TestParser (0.00s)=== RUN   TestToken--- PASS: TestToken (0.00s)PASSok      github.com/Lebonesco/go-compiler        9.020s

Half way to a working compiler! ? While you give this post some ? ? ? don’t forget to give yourself a hand for making it this far.

正常工作的编译器的一半! ? 你给这篇文章一些吗? ? ? 别忘了帮助自己做到这一点。

Let’s add some more code to the driver.

让我们向驱动程序添加更多代码。

Now onto my favorite part, the Type Checker.

现在到我最喜欢的部分, 类型检查器

类型检查器 (Type Checker)

Our type checker will make sure that users don’t write code that conflicts with our statically typed language.

我们的类型检查器将确保用户不会编写与我们的静态类型化语言冲突的代码。

The core backbone of our type checker will be a combination of data structures that track identifier types, initialization, and valid type operations. This can get vastly more complicated once we start dealing with different levels of scope and classes. However, we’re keeping it as simple as possible. ?

类型检查器的核心骨干将是跟踪标识符类型,初始化和有效类型操作的数据结构的组合。 一旦我们开始处理不同级别的范围和类,这将变得非常复杂。 但是,我们将其保持尽可能简单。 ?

To start:

开始:

touch checker_test.gomkdir checkercd checkertouch checker.gotouch environment.go

environment.go will contain all of our helper functions that will be used by our checker and code generator to access and manipulate our set of variables and corresponding types. Our environment is simple so this will be straight forward.

environment.go将包含我们的所有辅助函数,我们的检查器代码生成 将使用这些函数来访问和操纵我们的变量集和相应类型。 我们的环境很简单,所以这很简单。

We’ll begin by setting all of our constant values including operation types, variable types, and mapping of each type to its valid methods.

我们将从设置所有常量值开始,包括操作类型变量类型以及每种类型到其有效方法的映射

Note: If we had classes in our language our checker would handle this third part rather than us doing it by hand.

注意:如果我们有使用我们的语言编写的类,则检查器将处理此第三部分,而不是我们手工完成。

The rest of environment.go are basic getters and setters that handle identifiers and functions.

其余environment.go是基本的gettersetter该句柄标识和功能。

The foundation of our type checker will be a single dispatch function, checker(), that takes in a Node and fires the corresponding checker function

类型检查器的基础是单个调度函数checker() ,该函数接收一个Node并触发相应的检查器函数

I felt like saving lines of code so we’ll be using a global environment to store our variable types.

我感觉很想节省代码行,因此我们将使用全局环境来存储变量类型。

Note: This wouldn’t be possible if we allowed Block Statements and Functions to have their own scope or if we were abiding by best practices.

注意:如果我们允许Block StatementsFunctions具有自己的范围,或者我们遵循最佳实践,则这是不可能的。

Now eval Statements. Block Statements are the only statement in which we return a type in order to handle the case when it is inside a function. If there is a Return Statement inside the Block Statement its type is returned. Otherwise, the Nothing_Type is returned.

现在评估StatementsBlock Statements是我们返回类型的唯一语句,以便处理在函数内部的情况。 如果Block StatementReturn Statement ,则返回其类型。 否则,将返回Nothing_Type

Onto evaluating Expressions. The most complicated part will be evalFunctionCall() because it could either be a built in function such as PRINT() or user defined.

评估Expressions 。 最复杂的部分是evalFunctionCall()因为它可以是内置函数(如PRINT()或用户定义的。

Note: Currently, there is only one builtin function. However, more could be easily added given the framework that we’ve built.

注意:当前,只有一个内置函数。 但是,鉴于我们已经建立的框架,可以轻松添加更多内容。

Awesome! Let’s add a small snippet to our driver.

太棒了! 让我们向驱动程序添加一个小片段。

测试类型检查器 (Test Type Checker)
go test -v=== RUN   TestAST--- PASS: TestAST (0.00s)=== RUN   TestOperations--- PASS: TestOperations (0.00s)=== RUN   TestIdents--- PASS: TestIdents (0.00s)=== RUN   TestFunctions--- PASS: TestFunctions (0.00s)=== RUN   TestParser--- PASS: TestParser (0.00s)=== RUN   TestToken--- PASS: TestToken (0.00s)PASSok      github.com/Lebonesco/go-compiler        9.020s

I made some deliberate choices to leave things out of this language such as classes, for loops, and function scope. Most of the complexities that arise from these occur in the checker and code generator. If I added those elements this post would be a lot lot longer. ?

我做出了一些有选择的选择,以使诸如此类, for loops和函数scope classes东西不被使用。 由此产生的大多数复杂性都发生在checkercode generator 。 如果我添加这些元素,那么这篇文章将长很多。 ?

代码生成 (Code Generation)

We have finally made it to the last stage! ? ? ?

我们终于到了最后阶段! ? ? ?

In order to handle the most cases with the least amount of hassle every expression will be assigned to a temporary variable. It might make our generated code look bloated, but it will solve for any nested expressions.

为了以最少的麻烦处理大多数情况,每个expression都会分配给一个临时变量。 它可能会使我们生成的代码显得过大,但是它将解决所有嵌套表达式。

Bloated code won’t have any impact on final program speed because the optimizer will remove any redundancy when we compile our final generated C++ code.

膨胀的代码不会对最终程序速度产生任何影响,因为当我们编译最终生成的C ++代码时,优化器将消除任何冗余。

Our generator will look similar to the type checker. The main difference is that we’ll be passing down a buffer to store the generated code.

我们的生成器看起来类似于类型检查器。 主要区别在于我们将向下传递一个buffer来存储生成的代码。

While I chose to compile into C++, you can substitute in any language . The main purpose of this Go Compiler Guide was to enable you to be able to understand the pieces well enough to go out and create your own.

当我选择编译成C ++时,您可以用任何语言替代。 这份《 Go Compiler Guide 》的主要目的是使您能够充分理解各个部分,以便自行创建。

To start:

开始:

touch gen_test.gomkdir gencd gentouch gen.go

We’ll begin by importing all of the necessary packages and defining three utility functions, write() to write generated code to a buffer, check()to do error handling, and freshTemp()to generate unique variable names for temporary variables we create on the fly.

我们将从导入所有必需的包并定义三个实用程序功能开始,包括 write()将生成的代码写入缓冲区, check()执行错误处理,以及freshTemp()为我们创建的临时变量生成唯一的变量名在飞行中。

Note: It’s generally bad practice to use panic()for normal error handling in Go, but I’m tired of writing if statements.

注意:在Go中使用panic()进行常规错误处理通常是一种不好的做法,但是我厌倦了编写if statements

Similar to the checker, our generator has a core dispatch function that accepts a Node and calls the corresponding gen function.

检查器类似,我们的生成器具有一个核心调度函数,该函数接受Node并调用相应的gen函数。

Let’s generate some Statements. genProgram() generates necessary headers and main() function.

让我们生成一些StatementsgenProgram()生成必要的标头和main()函数。

Generating Expressions will look very similar to the code above. The main difference is that a temp variable is returned representing that expression. This helps us handle more complex Expression nesting.

生成Expressions看起来与上面的代码非常相似。 主要区别在于,返回了一个代表该表达式的temp变量。 这有助于我们处理更复杂的Expression嵌套。

The final piece of code will be our C++ Builtin types. Without this nothing will work.

最后的代码将是我们的C ++ Builtin类型。 没有这个,一切都将无效。

测试代码生成器 (Test Code Generator)

汇集全部 (Bringing It All Together)

We’re now going to combine our lexer, parser, AST generator, type checker, and code generator into a final runnable program, main.go.

现在,我们将词法分析器解析器AST生成器类型检查器代码生成器组合到最终的可运行程序main.go

Note: I’m running this on a Windows so my C++ compiles into main.exe. If this doesn’t work for you remove the .exe extension.

注意:我正在Windows上运行它,因此我的C ++可以编译成main.exe 。 如果这对您不起作用,请删除.exe扩展名。

To find some test programs to run go to github.com/Lebonesco/go-compiler/examples.

要找到一些要运行的测试程序,请访问github.com/Lebonesco/go-compiler/examples

go run main.go ./example/function.bxhello Jeff3

And there you have it! We have completed a fully working compiler in Go!

在那里,您拥有了! 我们已经在Go中完成了一个可以正常工作的编译器!

Thank you for taking the time to read this article.

感谢您抽出宝贵的时间阅读本文。

If you found it helpful or interesting please let me know ???.

如果您觉得它有用或有趣,请告诉我???。

翻译自: https://www.freecodecamp.org/news/write-a-compiler-in-go-quick-guide-30d2f33ac6e0/

编写go语言用到的编译器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值