世界上第一个 Go 程序

未匹配的标注

本文为官方 Go Blog 的中文翻译,详见 翻译说明

Andrew Gerrand
2013 年 7 月 18 日

Brad Fitzpatrick 和我 (Andrew Gerrand) 最近开始重组 godoc, 在我看来这是最古老的 Go 程序之一. Robert Griesemer 于 2009 年初开始编写它, 而今天我们仍在使用它.

当我 tweeted 时, Dave Cheney 回答了一个 有趣的问题: 最早的 Go 程序是什么?Rob Pike 扒拉许久邮件, 在给 Robert 和Ken Thompson 的旧邮件中找到了它.

接下来是第一个 Go 程序. 它是由 Rob 于 2008 年 2 月撰写的, 当时团队只有 Rob, Robert 和Ken. 他们有一个可靠的功能列表 (在 此博客文章 中提到) 和粗略的语言规范. Ken 刚刚完成了 Go 编译器的第一个工作版本 (它不会产生本机代码, 而是将 Go 代码转译为 C 以便快速进行原型制作), 现在是时候尝试使用它编写程序了.

Rob 向 "Go team" 发送了邮件:

From: Rob 'Commander' Pike
Date: Wed, Feb 6, 2008 at 3:42 PM
To: Ken Thompson, Robert Griesemer
Subject: slist

it works now.

roro=% a.out
(defn foo (add 12 34))
return: icounter = 4440
roro=%

here's the code.
some ugly hackery to get around the lack of strings.

(程序输出中的 icounter 行是为调试而打印的已执行语句的数量.)

package main

// fake stuff
type char uint8;

// const char TESTSTRING[] = "(defn foo (add 'a 'b)).";

type Atom struct {
        string  *[100]char;
        integer int;
        next    *Slist;  /* in hash bucket */
}

type List struct {
        car     *Slist;
        cdr     *Slist;
}

type Slist struct {
        isatom          bool;
        isstring        bool;
        //union {
        atom    Atom;
        list    List;
        //} u;

        Free method();
        Print method();
        PrintOne method(doparen bool);
        String method(*char <-);
        Integer method(int <-);
        Car method(*Slist <-);
        Cdr method(*Slist <-);
}

method (this *Slist) Car(*Slist <-) {
        return this.list.car;
}

method (this *Slist) Cdr(*Slist <-) {
        return this.list.cdr;
}

method (this *Slist) String(*[100]char <-) {
        return this.atom.string;
}

method (this *Slist) Integer(int <-) {
        return this.atom.integer;
}

function OpenFile();
function Parse(*Slist <-);

//Slist* atom(char *s, int i);

var token int;
var peekc int = -1;
var lineno int32 = 1;

var input [100*1000]char;
var inputindex int = 0;
var tokenbuf [100]char;

var EOF int = -1;  // BUG should be const

function main(int32 <-) {
        var list *Slist;

        OpenFile();
        for ;; {
                list = Parse();
                if list == nil {
                        break;
                }
                list.Print();
                list.Free();
                break;
        }

        return 0;
}

method (slist *Slist) Free(<-) {
        if slist == nil {
                return;
        }
        if slist.isatom {
//              free(slist.String());
        } else {
                slist.Car().Free();
                slist.Cdr().Free();
        }
//      free(slist);
}

method (slist *Slist) PrintOne(<- doparen bool) {
        if slist == nil {
                return;
        }
        if slist.isatom {
                if slist.isstring {
                        print(slist.String());
                } else {
                        print(slist.Integer());
                }
        } else {
                if doparen {
                        print("(");
                }
                slist.Car().PrintOne(true);
                if slist.Cdr() != nil {
                        print(" ");
                        slist.Cdr().PrintOne(false);
                }
                if doparen {
                        print(")");
                }
        }
}

method (slist *Slist) Print() {
        slist.PrintOne(true);
        print ".";
}

function Get(int <-) {
        var c int;

        if peekc >= 0 {
                c = peekc;
                peekc = -1;
        } else {
                c = convert(int, input[inputindex]);
                inputindex = inputindex + 1; // BUG should be incr one expr
                if c == '.' {
                        lineno = lineno + 1;
                }
                if c == '.' {
                        inputindex = inputindex - 1;
                        c = EOF;
                }
        }
        return c;
}

function WhiteSpace(bool <- c int) {
        return c == ' ' || c == '.' || c == '.' || c == '.';
}

function NextToken() {
        var i, c int;
        var backslash bool;

        tokenbuf[0] = '.';     // 清除上一个令牌
        c = Get();
        while WhiteSpace(c)  {
                c = Get();
        }
        switch c {
                case EOF:
                        token = EOF;
                case '(':
                case ')':
                        token = c;
                        break;
                case:
                        for i = 0; i < 100 - 1; {  // sizeof tokenbuf - 1
                                tokenbuf[i] = convert(char, c);
                                i = i + 1;
                                c = Get();
                                if c == EOF {
                                        break;
                                }
                                if WhiteSpace(c) || c == ')' {
                                        peekc = c;
                                        break;
                                }
                        }
                        if i >= 100 - 1 {  // sizeof tokenbuf - 1
                                panic "atom too long.";
                        }
                        tokenbuf[i] = '.';
                        if '0' <= tokenbuf[0] && tokenbuf[0] <= '9' {
                                token = '0';
                        } else {
                                token = 'A';
                        }
        }
}

function Expect(<- c int) {
        if token != c {
                print "parse error: expected ", c, ".";
                panic "parse";
        }
        NextToken();
}

// Parse a non-parenthesized list up to a closing paren or EOF
function ParseList(*Slist <-) {
        var slist, retval *Slist;

        slist = new(Slist);
        slist.list.car = nil;
        slist.list.cdr = nil;
        slist.isatom = false;
        slist.isstring = false;

        retval = slist;
        for ;; {
                slist.list.car = Parse();
                if token == ')' {       // empty cdr
                        break;
                }
                if token == EOF {       // empty cdr  BUG SHOULD USE ||
                        break;
                }
                slist.list.cdr = new(Slist);
                slist = slist.list.cdr;
        }
        return retval;
}

function atom(*Slist <- i int) {  // BUG: uses tokenbuf; should take argument
        var h, length int;
        var slist, tail *Slist;

        slist = new(Slist);
        if token == '0' {
                slist.atom.integer = i;
                slist.isstring = false;
        } else {
                slist.atom.string = new([100]char);
                var i int;
                for i = 0; ; i = i + 1 {
                        (*slist.atom.string)[i] = tokenbuf[i];
                        if tokenbuf[i] == '.' {
                                break;
                        }
                }
                //slist.atom.string = "hello"; // BUG! s; //= strdup(s);
                slist.isstring = true;
        }
        slist.isatom = true;
        return slist;
}

function atoi(int <-) {  // BUG: uses tokenbuf; should take argument
        var v int = 0;
        for i := 0; '0' <= tokenbuf[i] && tokenbuf[i] <= '9'; i = i + 1 {
                v = 10 * v + convert(int, tokenbuf[i] - '0');
        }
        return v;
}

function Parse(*Slist <-) {
        var slist *Slist;

        if token == EOF || token == ')' {
                return nil;
        }
        if token == '(' {
                NextToken();
                slist = ParseList();
                Expect(')');
                return slist;
        } else {
                // Atom
                switch token {
                        case EOF:
                                return nil;
                        case '0':
                                slist = atom(atoi());
                        case '"':
                        case 'A':
                                slist = atom(0);
                        case:
                                slist = nil;
                                print "unknown token"; //, token, tokenbuf;
                }
                NextToken();
                return slist;
        }
        return nil;
}

function OpenFile() {
        //strcpy(input, TESTSTRING);
        //inputindex = 0;
        // (defn foo (add 12 34)).
        inputindex = 0;
        peekc = -1;  // BUG
        EOF = -1;  // BUG
        i := 0;
        input[i] = '('; i = i + 1;
        input[i] = 'd'; i = i + 1;
        input[i] = 'e'; i = i + 1;
        input[i] = 'f'; i = i + 1;
        input[i] = 'n'; i = i + 1;
        input[i] = ' '; i = i + 1;
        input[i] = 'f'; i = i + 1;
        input[i] = 'o'; i = i + 1;
        input[i] = 'o'; i = i + 1;
        input[i] = ' '; i = i + 1;
        input[i] = '('; i = i + 1;
        input[i] = 'a'; i = i + 1;
        input[i] = 'd'; i = i + 1;
        input[i] = 'd'; i = i + 1;
        input[i] = ' '; i = i + 1;
        input[i] = '1'; i = i + 1;
        input[i] = '2'; i = i + 1;
        input[i] = ' '; i = i + 1;
        input[i] = '3'; i = i + 1;
        input[i] = '4'; i = i + 1;
        input[i] = ')'; i = i + 1;
        input[i] = ')'; i = i + 1;
        input[i] = '.'; i = i + 1;
        NextToken();
}

该程序解析并打印一个 S-expression. 它不需要用户输入也没有导入, 仅依赖于内置的 print 功能进行输出. 从字面上看, 它是在第一天写的, 工作但很基础的编译器. 许多东西没有实现甚至没有指定语言.

尽管如此, 该程序仍可以识别出语言如今的基本风格. 类型和变量声明, 控制流和包语句没有太大变化.

但是, 有许多差异和缺失的地方. 最重要的是缺少并发性和接口 - 从第一天起就被认为是必不可少的但还未设计的内容.

一个 func 是一个 function, 其签名在 参数之前 指定了返回值, 并用 <- 分隔它们, 我们现在将其用作通道发送/接收运算符. 例如, WhiteSpace 函数采用整数 c 并返回一个布尔值.

function WhiteSpace(bool <- c int)

该箭头是一种权宜之计, 直到出现了用于声明多个返回值的更好的语法.

方法与函数不同, 并且具有自己的关键字.
Methods were distinct from functions and had their own keyword.

method (this *Slist) Car(*Slist <-) {
    return this.list.car;
}

并且方法已在结构定义中预先声明, 尽管很快就发生了改变了.

type Slist struct {
    ...
    Car method(*Slist <-);
}

尽管符合规范, 但还是没有字符串. 必须将输入字符串构建为具有笨拙构造的 uint8 数组. (数组是基本的, 还没有设计切片, 更不用说实现了, 尽管有一个未实现的 "开放数组" 的概念.)

input[i] = '('; i = i + 1;
input[i] = 'd'; i = i + 1;
input[i] = 'e'; i = i + 1;
input[i] = 'f'; i = i + 1;
input[i] = 'n'; i = i + 1;
input[i] = ' '; i = i + 1;
...

panicprint 都是内置关键字, 不是预先声明的函数.

print "parse error: expected ", c, ".";
panic "parse";

还有许多其他小的差异; 看看如果你可以识别出来的话.

编写此程序不到两年, Go 就作为一个开源项目发布了. 回顾过去, 令人惊讶的是该语言已经发展和成熟了不少. (在此原型 Go 和我们今天所知道的 Go 之间进行的最后更改是消除分号.)

但更令人震惊的是, 我们对 编写 Go 代码学到了多少. 例如, Rob 将他的方法接收者称为 this, 但是现在我们使用较短的上下文特定名称. 有数百个更重要的示例, 直到今天, 我们仍在发现编写 Go 代码的更好方法. (查看 glog软件包 的巧妙技巧, 以处理 详细级别.)

我想期待明天我们又可以学会什么.

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/go-blog/first-g...

译文地址:https://learnku.com/docs/go-blog/first-g...

上一篇 下一篇
Summer
贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~