aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Gerrand <adg@golang.org>2013-07-18 15:32:09 +1000
committerAndrew Gerrand <adg@golang.org>2013-07-18 15:32:09 +1000
commit70aea4431c21f510ac86a3b7a5dc1f5e61392190 (patch)
treecbac484e8b888516793cd7627f1c31fadaae5ead
parent74485dbaa4509d369b543f23d5dd659f6c77246c (diff)
go.blog: add "The first Go program"
R=r CC=rsc https://golang.org/cl/11499043
2 files changed, 445 insertions, 0 deletions
diff --git a/content/first-go-program.article b/content/first-go-program.article
new file mode 100644
index 0000000..e972ed1
--- /dev/null
+++ b/content/first-go-program.article
@@ -0,0 +1,123 @@
+The first Go program
+18 Jul 2013
+Tags: history
+
+Andrew Gerrand
+
+* Introduction
+
+Brad Fitzpatrick and I (Andrew Gerrand) recently started restructuring
+[[http://golang.org/cmd/godoc/][godoc]], and it occurred to me that it is one
+of the oldest Go programs.
+Robert Griesemer started writing it back in early 2009,
+and we're still using it today.
+
+When I [[https://twitter.com/enneff/status/357403054632484865][tweeted]] about
+this, Dave Cheney replied with an [[https://twitter.com/davecheney/status/357406479415914497][interesting question]]:
+what is the oldest Go program? Rob Pike dug into his mail and found it
+in an old message to Robert and Ken Thompson.
+
+What follows is the first Go program. It was written by Rob in February 2008,
+when the team was just Rob, Robert, and Ken. They had a solid feature list
+(mentioned in [[http://commandcenter.blogspot.com.au/2012/06/less-is-exponentially-more.html][this blog post]])
+and a rough language specfication. Ken had just finished the first working version of
+a Go compiler (it didn't produce native code, but rather transliterated Go code
+to C for fast prototyping) and it was time to try writing a program with it.
+
+Rob sent mail to the "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.
+
+
+(The `icounter` line in the program output is the number of executed
+statements, printed for debugging.)
+
+.code first-go-program/slist.go
+
+The program parses and prints an
+[[https://en.wikipedia.org/wiki/S-expression][S-expression]].
+It takes no user input and has no imports, relying only on the built-in
+`print` facility for output.
+It was written literally the first day there was a
+[[http://golang.org/change/8b8615138da3][working but rudimentary compiler]].
+Much of the language wasn't implemented and some of it wasn't even specified.
+
+Still, the basic flavor of the language today is recognizable in this program.
+Type and variable declarations, control flow, and package statements haven't
+changed much.
+
+But there are many differences and absences.
+Most significant are the lack of concurrency and interfaces—both
+considered essential since day 1 but not yet designed.
+
+A `func` was a `function`, and its signature specified return values
+_before_ arguments, separating them with `<-`, which we now use as the channel
+send/receive operator. For example, the `WhiteSpace` function takes the integer
+`c` and returns a boolean.
+
+ function WhiteSpace(bool <- c int)
+
+This arrow was a stop-gap measure until a better syntax arose for declaring
+multiple return values.
+
+Methods were distinct from functions and had their own keyword.
+
+ method (this *Slist) Car(*Slist <-) {
+ return this.list.car;
+ }
+
+And methods were pre-declared in the struct definition, although that changed soon.
+
+ type Slist struct {
+ ...
+ Car method(*Slist <-);
+ }
+
+There were no strings, although they were in the spec.
+To work around this, Rob had to build the input string as an `uint8` array with
+a clumsy construction. (Arrays were rudimentary and slices hadn't been designed
+yet, let alone implemented, although there was the unimplemented concept of an
+"open array".)
+
+ 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;
+ ...
+
+Both `panic` and `print` were built-in keywords, not pre-declared functions.
+
+ print "parse error: expected ", c, "\n";
+ panic "parse";
+
+And there are many other little differences; see if you can identify some others.
+
+Less than two years after this program was written, Go was released as an
+open source project. Looking back, it is striking how much the language has
+grown and matured. (The last thing to change between this proto-Go and the Go
+we know today was the elimination of semicolons.)
+
+But even more striking is how much we have learned about _writing_ Go code.
+For instance, Rob called his method receivers `this`, but now we use shorter
+context-specific names. There are hundreds of more significant examples
+and to this day we're still discovering better ways to write Go code.
+(Check out the [[https://github.com/golang/glog][glog package]]'s clever trick for
+[[https://github.com/golang/glog/blob/master/glog.go#L854][handling verbosity levels]].)
+
+I wonder what we'll learn tomorrow.
+
diff --git a/content/first-go-program/slist.go b/content/first-go-program/slist.go
new file mode 100644
index 0000000..bde7e2b
--- /dev/null
+++ b/content/first-go-program/slist.go
@@ -0,0 +1,322 @@
+// +build OMIT
+
+package main
+
+// fake stuff
+type char uint8;
+
+// const char TESTSTRING[] = "(defn foo (add 'a 'b))\n";
+
+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 "\n";
+}
+
+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 == '\n' {
+ lineno = lineno + 1;
+ }
+ if c == '\0' {
+ inputindex = inputindex - 1;
+ c = EOF;
+ }
+ }
+ return c;
+}
+
+function WhiteSpace(bool <- c int) {
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+function NextToken() {
+ var i, c int;
+ var backslash bool;
+
+ tokenbuf[0] = '\0'; // clear previous token
+ 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\n";
+ }
+ tokenbuf[i] = '\0';
+ if '0' <= tokenbuf[0] && tokenbuf[0] <= '9' {
+ token = '0';
+ } else {
+ token = 'A';
+ }
+ }
+}
+
+function Expect(<- c int) {
+ if token != c {
+ print "parse error: expected ", c, "\n";
+ 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] == '\0' {
+ 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))\n
+ 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] = '\n'; i = i + 1;
+ NextToken();
+}