mirror of
https://github.com/golang/go
synced 2024-11-24 19:10:15 -07:00
docs: fold the prog.sh scripting from makehtml into htmlgen itself.
This allows us to drop some crufty scripting and provides a firmer footing for building better tools for preparing documents with source code inside. Also eliminate line numbers from the examples and text. R=golang-dev, adg CC=golang-dev https://golang.org/cl/4650069
This commit is contained in:
parent
a342006207
commit
9cf37c3723
File diff suppressed because it is too large
Load Diff
@ -28,7 +28,7 @@ Hello, World
|
|||||||
|
|
||||||
Let's start in the usual way:
|
Let's start in the usual way:
|
||||||
|
|
||||||
--PROG progs/helloworld.go /package/ END
|
!src progs/helloworld.go /package/ $
|
||||||
|
|
||||||
Every Go source file declares, using a "package" statement, which package it's part of.
|
Every Go source file declares, using a "package" statement, which package it's part of.
|
||||||
It may also import other packages to use their facilities.
|
It may also import other packages to use their facilities.
|
||||||
@ -107,13 +107,13 @@ Echo
|
|||||||
|
|
||||||
Next up, here's a version of the Unix utility "echo(1)":
|
Next up, here's a version of the Unix utility "echo(1)":
|
||||||
|
|
||||||
--PROG progs/echo.go /package/ END
|
!src progs/echo.go /package/ $
|
||||||
|
|
||||||
This program is small but it's doing a number of new things. In the last example,
|
This program is small but it's doing a number of new things. In the last example,
|
||||||
we saw "func" introduce a function. The keywords "var", "const", and "type"
|
we saw "func" introduce a function. The keywords "var", "const", and "type"
|
||||||
(not used yet) also introduce declarations, as does "import".
|
(not used yet) also introduce declarations, as does "import".
|
||||||
Notice that we can group declarations of the same sort into
|
Notice that we can group declarations of the same sort into
|
||||||
parenthesized lists, one item per line, as on lines 7-10 and 14-17.
|
parenthesized lists, one item per line, as in the "import" and "const" clauses here.
|
||||||
But it's not necessary to do so; we could have said
|
But it's not necessary to do so; we could have said
|
||||||
|
|
||||||
const Space = " "
|
const Space = " "
|
||||||
@ -163,7 +163,7 @@ or we could go even shorter and write the idiom
|
|||||||
The ":=" operator is used a lot in Go to represent an initializing declaration.
|
The ":=" operator is used a lot in Go to represent an initializing declaration.
|
||||||
There's one in the "for" clause on the next line:
|
There's one in the "for" clause on the next line:
|
||||||
|
|
||||||
--PROG progs/echo.go /for/
|
!src progs/echo.go /for/
|
||||||
|
|
||||||
The "flag" package has parsed the arguments and left the non-flag arguments
|
The "flag" package has parsed the arguments and left the non-flag arguments
|
||||||
in a list that can be iterated over in the obvious way.
|
in a list that can be iterated over in the obvious way.
|
||||||
@ -210,7 +210,7 @@ Once you've built a string <i>value</i>, you can't change it, although
|
|||||||
of course you can change a string <i>variable</i> simply by
|
of course you can change a string <i>variable</i> simply by
|
||||||
reassigning it. This snippet from "strings.go" is legal code:
|
reassigning it. This snippet from "strings.go" is legal code:
|
||||||
|
|
||||||
--PROG progs/strings.go /hello/ /ciao/
|
!src progs/strings.go /hello/ /ciao/
|
||||||
|
|
||||||
However the following statements are illegal because they would modify
|
However the following statements are illegal because they would modify
|
||||||
a "string" value:
|
a "string" value:
|
||||||
@ -269,7 +269,7 @@ will slice the whole array.
|
|||||||
|
|
||||||
Using slices one can write this function (from "sum.go"):
|
Using slices one can write this function (from "sum.go"):
|
||||||
|
|
||||||
--PROG progs/sum.go /sum/ /^}/
|
!src progs/sum.go /sum/ /^}/
|
||||||
|
|
||||||
Note how the return type ("int") is defined for "sum" by stating it
|
Note how the return type ("int") is defined for "sum" by stating it
|
||||||
after the parameter list.
|
after the parameter list.
|
||||||
@ -386,7 +386,7 @@ An I/O Package
|
|||||||
Next we'll look at a simple package for doing file I/O with an
|
Next we'll look at a simple package for doing file I/O with an
|
||||||
open/close/read/write interface. Here's the start of "file.go":
|
open/close/read/write interface. Here's the start of "file.go":
|
||||||
|
|
||||||
--PROG progs/file.go /package/ /^}/
|
!src progs/file.go /package/ /^}/
|
||||||
|
|
||||||
The first few lines declare the name of the
|
The first few lines declare the name of the
|
||||||
package—"file"—and then import two packages. The "os"
|
package—"file"—and then import two packages. The "os"
|
||||||
@ -416,7 +416,7 @@ will soon give it some exported, upper-case methods.
|
|||||||
|
|
||||||
First, though, here is a factory to create a "File":
|
First, though, here is a factory to create a "File":
|
||||||
|
|
||||||
--PROG progs/file.go /newFile/ /^}/
|
!src progs/file.go /newFile/ /^}/
|
||||||
|
|
||||||
This returns a pointer to a new "File" structure with the file descriptor and name
|
This returns a pointer to a new "File" structure with the file descriptor and name
|
||||||
filled in. This code uses Go's notion of a ''composite literal'', analogous to
|
filled in. This code uses Go's notion of a ''composite literal'', analogous to
|
||||||
@ -433,12 +433,12 @@ composite literal, as is done here on line 21.
|
|||||||
|
|
||||||
We can use the factory to construct some familiar, exported variables of type "*File":
|
We can use the factory to construct some familiar, exported variables of type "*File":
|
||||||
|
|
||||||
--PROG progs/file.go /var/ /^.$/
|
!src progs/file.go /var/ /^.$/
|
||||||
|
|
||||||
The "newFile" function was not exported because it's internal. The proper,
|
The "newFile" function was not exported because it's internal. The proper,
|
||||||
exported factory to use is "OpenFile" (we'll explain that name in a moment):
|
exported factory to use is "OpenFile" (we'll explain that name in a moment):
|
||||||
|
|
||||||
--PROG progs/file.go /func.OpenFile/ /^}/
|
!src progs/file.go /func.OpenFile/ /^}/
|
||||||
|
|
||||||
There are a number of new things in these few lines. First, "OpenFile" returns
|
There are a number of new things in these few lines. First, "OpenFile" returns
|
||||||
multiple values, a "File" and an error (more about errors in a moment).
|
multiple values, a "File" and an error (more about errors in a moment).
|
||||||
@ -468,9 +468,9 @@ the implementation of our "Open" and "Create"; they're trivial
|
|||||||
wrappers that eliminate common errors by capturing
|
wrappers that eliminate common errors by capturing
|
||||||
the tricky standard arguments to open and, especially, to create a file:
|
the tricky standard arguments to open and, especially, to create a file:
|
||||||
|
|
||||||
--PROG progs/file.go /^const/ /^}/
|
!src progs/file.go /^const/ /^}/
|
||||||
|
|
||||||
--PROG progs/file.go /func.Create/ /^}/
|
!src progs/file.go /func.Create/ /^}/
|
||||||
|
|
||||||
Back to our main story.
|
Back to our main story.
|
||||||
Now that we can build "Files", we can write methods for them. To declare
|
Now that we can build "Files", we can write methods for them. To declare
|
||||||
@ -479,7 +479,7 @@ of that type, placed
|
|||||||
in parentheses before the function name. Here are some methods for "*File",
|
in parentheses before the function name. Here are some methods for "*File",
|
||||||
each of which declares a receiver variable "file".
|
each of which declares a receiver variable "file".
|
||||||
|
|
||||||
--PROG progs/file.go /Close/ END
|
!src progs/file.go /Close/ $
|
||||||
|
|
||||||
There is no implicit "this" and the receiver variable must be used to access
|
There is no implicit "this" and the receiver variable must be used to access
|
||||||
members of the structure. Methods are not declared within
|
members of the structure. Methods are not declared within
|
||||||
@ -496,7 +496,7 @@ set of such error values.
|
|||||||
|
|
||||||
We can now use our new package:
|
We can now use our new package:
|
||||||
|
|
||||||
--PROG progs/helloworld3.go /package/ END
|
!src progs/helloworld3.go /package/ $
|
||||||
|
|
||||||
The ''"./"'' in the import of ''"./file"'' tells the compiler
|
The ''"./"'' in the import of ''"./file"'' tells the compiler
|
||||||
to use our own package rather than
|
to use our own package rather than
|
||||||
@ -520,12 +520,12 @@ Rotting cats
|
|||||||
Building on the "file" package, here's a simple version of the Unix utility "cat(1)",
|
Building on the "file" package, here's a simple version of the Unix utility "cat(1)",
|
||||||
"progs/cat.go":
|
"progs/cat.go":
|
||||||
|
|
||||||
--PROG progs/cat.go /package/ END
|
!src progs/cat.go /package/ $
|
||||||
|
|
||||||
By now this should be easy to follow, but the "switch" statement introduces some
|
By now this should be easy to follow, but the "switch" statement introduces some
|
||||||
new features. Like a "for" loop, an "if" or "switch" can include an
|
new features. Like a "for" loop, an "if" or "switch" can include an
|
||||||
initialization statement. The "switch" on line 18 uses one to create variables
|
initialization statement. The "switch" statement in "cat" uses one to create variables
|
||||||
"nr" and "er" to hold the return values from the call to "f.Read". (The "if" on line 25
|
"nr" and "er" to hold the return values from the call to "f.Read". (The "if" a few lines later
|
||||||
has the same idea.) The "switch" statement is general: it evaluates the cases
|
has the same idea.) The "switch" statement is general: it evaluates the cases
|
||||||
from top to bottom looking for the first case that matches the value; the
|
from top to bottom looking for the first case that matches the value; the
|
||||||
case expressions don't need to be constants or even integers, as long as
|
case expressions don't need to be constants or even integers, as long as
|
||||||
@ -537,7 +537,7 @@ in a "for" statement, a missing value means "true". In fact, such a "switch"
|
|||||||
is a form of "if-else" chain. While we're here, it should be mentioned that in
|
is a form of "if-else" chain. While we're here, it should be mentioned that in
|
||||||
"switch" statements each "case" has an implicit "break".
|
"switch" statements each "case" has an implicit "break".
|
||||||
|
|
||||||
Line 25 calls "Write" by slicing the incoming buffer, which is itself a slice.
|
The argument to "file.Stdout.Write" is created by slicing the array "buf".
|
||||||
Slices provide the standard Go way to handle I/O buffers.
|
Slices provide the standard Go way to handle I/O buffers.
|
||||||
|
|
||||||
Now let's make a variant of "cat" that optionally does "rot13" on its input.
|
Now let's make a variant of "cat" that optionally does "rot13" on its input.
|
||||||
@ -548,7 +548,7 @@ The "cat" subroutine uses only two methods of "f": "Read" and "String",
|
|||||||
so let's start by defining an interface that has exactly those two methods.
|
so let's start by defining an interface that has exactly those two methods.
|
||||||
Here is code from "progs/cat_rot13.go":
|
Here is code from "progs/cat_rot13.go":
|
||||||
|
|
||||||
--PROG progs/cat_rot13.go /type.reader/ /^}/
|
!src progs/cat_rot13.go /type.reader/ /^}/
|
||||||
|
|
||||||
Any type that has the two methods of "reader"—regardless of whatever
|
Any type that has the two methods of "reader"—regardless of whatever
|
||||||
other methods the type may also have—is said to <i>implement</i> the
|
other methods the type may also have—is said to <i>implement</i> the
|
||||||
@ -560,34 +560,32 @@ existing "reader" and does "rot13" on the data. To do this, we just define
|
|||||||
the type and implement the methods and with no other bookkeeping,
|
the type and implement the methods and with no other bookkeeping,
|
||||||
we have a second implementation of the "reader" interface.
|
we have a second implementation of the "reader" interface.
|
||||||
|
|
||||||
--PROG progs/cat_rot13.go /type.rotate13/ /end.of.rotate13/
|
!src progs/cat_rot13.go /type.rotate13/ /end.of.rotate13/
|
||||||
|
|
||||||
(The "rot13" function called on line 42 is trivial and not worth reproducing here.)
|
(The "rot13" function called in "Read" is trivial and not worth reproducing here.)
|
||||||
|
|
||||||
To use the new feature, we define a flag:
|
To use the new feature, we define a flag:
|
||||||
|
|
||||||
--PROG progs/cat_rot13.go /rot13Flag/
|
!src progs/cat_rot13.go /rot13Flag/
|
||||||
|
|
||||||
and use it from within a mostly unchanged "cat" function:
|
and use it from within a mostly unchanged "cat" function:
|
||||||
|
|
||||||
--PROG progs/cat_rot13.go /func.cat/ /^}/
|
!src progs/cat_rot13.go /func.cat/ /^}/
|
||||||
|
|
||||||
(We could also do the wrapping in "main" and leave "cat" mostly alone, except
|
(We could also do the wrapping in "main" and leave "cat" mostly alone, except
|
||||||
for changing the type of the argument; consider that an exercise.)
|
for changing the type of the argument; consider that an exercise.)
|
||||||
Lines 56 through 58 set it all up: If the "rot13" flag is true, wrap the "reader"
|
The "if" at the top of "cat" sets it all up: If the "rot13" flag is true, wrap the "reader"
|
||||||
we received into a "rotate13" and proceed. Note that the interface variables
|
we received into a "rotate13" and proceed. Note that the interface variables
|
||||||
are values, not pointers: the argument is of type "reader", not "*reader",
|
are values, not pointers: the argument is of type "reader", not "*reader",
|
||||||
even though under the covers it holds a pointer to a "struct".
|
even though under the covers it holds a pointer to a "struct".
|
||||||
|
|
||||||
Here it is in action:
|
Here it is in action:
|
||||||
|
|
||||||
<pre>
|
|
||||||
$ echo abcdefghijklmnopqrstuvwxyz | ./cat
|
$ echo abcdefghijklmnopqrstuvwxyz | ./cat
|
||||||
abcdefghijklmnopqrstuvwxyz
|
abcdefghijklmnopqrstuvwxyz
|
||||||
$ echo abcdefghijklmnopqrstuvwxyz | ./cat --rot13
|
$ echo abcdefghijklmnopqrstuvwxyz | ./cat --rot13
|
||||||
nopqrstuvwxyzabcdefghijklm
|
nopqrstuvwxyzabcdefghijklm
|
||||||
$
|
$
|
||||||
</pre>
|
|
||||||
|
|
||||||
Fans of dependency injection may take cheer from how easily interfaces
|
Fans of dependency injection may take cheer from how easily interfaces
|
||||||
allow us to substitute the implementation of a file descriptor.
|
allow us to substitute the implementation of a file descriptor.
|
||||||
@ -601,9 +599,7 @@ as we saw with "rot13". The type "file.File" implements "reader"; it could also
|
|||||||
implement a "writer", or any other interface built from its methods that
|
implement a "writer", or any other interface built from its methods that
|
||||||
fits the current situation. Consider the <i>empty interface</i>
|
fits the current situation. Consider the <i>empty interface</i>
|
||||||
|
|
||||||
<pre>
|
|
||||||
type Empty interface {}
|
type Empty interface {}
|
||||||
</pre>
|
|
||||||
|
|
||||||
<i>Every</i> type implements the empty interface, which makes it
|
<i>Every</i> type implements the empty interface, which makes it
|
||||||
useful for things like containers.
|
useful for things like containers.
|
||||||
@ -618,17 +614,17 @@ same interface variable.
|
|||||||
|
|
||||||
As an example, consider this simple sort algorithm taken from "progs/sort.go":
|
As an example, consider this simple sort algorithm taken from "progs/sort.go":
|
||||||
|
|
||||||
--PROG progs/sort.go /func.Sort/ /^}/
|
!src progs/sort.go /func.Sort/ /^}/
|
||||||
|
|
||||||
The code needs only three methods, which we wrap into sort's "Interface":
|
The code needs only three methods, which we wrap into sort's "Interface":
|
||||||
|
|
||||||
--PROG progs/sort.go /interface/ /^}/
|
!src progs/sort.go /interface/ /^}/
|
||||||
|
|
||||||
We can apply "Sort" to any type that implements "Len", "Less", and "Swap".
|
We can apply "Sort" to any type that implements "Len", "Less", and "Swap".
|
||||||
The "sort" package includes the necessary methods to allow sorting of
|
The "sort" package includes the necessary methods to allow sorting of
|
||||||
arrays of integers, strings, etc.; here's the code for arrays of "int"
|
arrays of integers, strings, etc.; here's the code for arrays of "int"
|
||||||
|
|
||||||
--PROG progs/sort.go /type.*IntSlice/ /Swap/
|
!src progs/sort.go /type.*IntSlice/ /Swap/
|
||||||
|
|
||||||
Here we see methods defined for non-"struct" types. You can define methods
|
Here we see methods defined for non-"struct" types. You can define methods
|
||||||
for any type you define and name in your package.
|
for any type you define and name in your package.
|
||||||
@ -637,12 +633,12 @@ And now a routine to test it out, from "progs/sortmain.go". This
|
|||||||
uses a function in the "sort" package, omitted here for brevity,
|
uses a function in the "sort" package, omitted here for brevity,
|
||||||
to test that the result is sorted.
|
to test that the result is sorted.
|
||||||
|
|
||||||
--PROG progs/sortmain.go /func.ints/ /^}/
|
!src progs/sortmain.go /func.ints/ /^}/
|
||||||
|
|
||||||
If we have a new type we want to be able to sort, all we need to do is
|
If we have a new type we want to be able to sort, all we need to do is
|
||||||
to implement the three methods for that type, like this:
|
to implement the three methods for that type, like this:
|
||||||
|
|
||||||
--PROG progs/sortmain.go /type.day/ /Swap/
|
!src progs/sortmain.go /type.day/ /Swap/
|
||||||
|
|
||||||
|
|
||||||
Printing
|
Printing
|
||||||
@ -675,7 +671,7 @@ argument. It's easier in many cases in Go. Instead of "%llud" you
|
|||||||
can just say "%d"; "Printf" knows the size and signedness of the
|
can just say "%d"; "Printf" knows the size and signedness of the
|
||||||
integer and can do the right thing for you. The snippet
|
integer and can do the right thing for you. The snippet
|
||||||
|
|
||||||
--PROG progs/print.go 'NR==10' 'NR==11'
|
!src progs/print.go 10 11
|
||||||
|
|
||||||
prints
|
prints
|
||||||
|
|
||||||
@ -684,7 +680,7 @@ prints
|
|||||||
In fact, if you're lazy the format "%v" will print, in a simple
|
In fact, if you're lazy the format "%v" will print, in a simple
|
||||||
appropriate style, any value, even an array or structure. The output of
|
appropriate style, any value, even an array or structure. The output of
|
||||||
|
|
||||||
--PROG progs/print.go 'NR==14' 'NR==20'
|
!src progs/print.go 14 20
|
||||||
|
|
||||||
is
|
is
|
||||||
|
|
||||||
@ -697,7 +693,7 @@ of "%v" while "Println" inserts spaces between arguments
|
|||||||
and adds a newline. The output of each of these two lines is identical
|
and adds a newline. The output of each of these two lines is identical
|
||||||
to that of the "Printf" call above.
|
to that of the "Printf" call above.
|
||||||
|
|
||||||
--PROG progs/print.go 'NR==21' 'NR==22'
|
!src progs/print.go 21 22
|
||||||
|
|
||||||
If you have your own type you'd like "Printf" or "Print" to format,
|
If you have your own type you'd like "Printf" or "Print" to format,
|
||||||
just give it a "String" method that returns a string. The print
|
just give it a "String" method that returns a string. The print
|
||||||
@ -705,7 +701,7 @@ routines will examine the value to inquire whether it implements
|
|||||||
the method and if so, use it rather than some other formatting.
|
the method and if so, use it rather than some other formatting.
|
||||||
Here's a simple example.
|
Here's a simple example.
|
||||||
|
|
||||||
--PROG progs/print_string.go 'NR==9' END
|
!src progs/print_string.go 9 $
|
||||||
|
|
||||||
Since "*testType" has a "String" method, the
|
Since "*testType" has a "String" method, the
|
||||||
default formatter for that type will use it and produce the output
|
default formatter for that type will use it and produce the output
|
||||||
@ -803,7 +799,7 @@ coordinates the communication; as with maps and slices, use
|
|||||||
|
|
||||||
Here is the first function in "progs/sieve.go":
|
Here is the first function in "progs/sieve.go":
|
||||||
|
|
||||||
--PROG progs/sieve.go /Send/ /^}/
|
!src progs/sieve.go /Send/ /^}/
|
||||||
|
|
||||||
The "generate" function sends the sequence 2, 3, 4, 5, ... to its
|
The "generate" function sends the sequence 2, 3, 4, 5, ... to its
|
||||||
argument channel, "ch", using the binary communications operator "<-".
|
argument channel, "ch", using the binary communications operator "<-".
|
||||||
@ -815,7 +811,7 @@ channel, and a prime number. It copies values from the input to the
|
|||||||
output, discarding anything divisible by the prime. The unary communications
|
output, discarding anything divisible by the prime. The unary communications
|
||||||
operator "<-" (receive) retrieves the next value on the channel.
|
operator "<-" (receive) retrieves the next value on the channel.
|
||||||
|
|
||||||
--PROG progs/sieve.go /Copy.the/ /^}/
|
!src progs/sieve.go /Copy.the/ /^}/
|
||||||
|
|
||||||
The generator and filters execute concurrently. Go has
|
The generator and filters execute concurrently. Go has
|
||||||
its own model of process/threads/light-weight processes/coroutines,
|
its own model of process/threads/light-weight processes/coroutines,
|
||||||
@ -838,9 +834,9 @@ on which it can report back:
|
|||||||
Back to our prime sieve. Here's how the sieve pipeline is stitched
|
Back to our prime sieve. Here's how the sieve pipeline is stitched
|
||||||
together:
|
together:
|
||||||
|
|
||||||
--PROG progs/sieve.go /func.main/ /^}/
|
!src progs/sieve.go /func.main/ /^}/
|
||||||
|
|
||||||
Line 29 creates the initial channel to pass to "generate", which it
|
The first line of "main" creates the initial channel to pass to "generate", which it
|
||||||
then starts up. As each prime pops out of the channel, a new "filter"
|
then starts up. As each prime pops out of the channel, a new "filter"
|
||||||
is added to the pipeline and <i>its</i> output becomes the new value
|
is added to the pipeline and <i>its</i> output becomes the new value
|
||||||
of "ch".
|
of "ch".
|
||||||
@ -849,30 +845,30 @@ The sieve program can be tweaked to use a pattern common
|
|||||||
in this style of programming. Here is a variant version
|
in this style of programming. Here is a variant version
|
||||||
of "generate", from "progs/sieve1.go":
|
of "generate", from "progs/sieve1.go":
|
||||||
|
|
||||||
--PROG progs/sieve1.go /func.generate/ /^}/
|
!src progs/sieve1.go /func.generate/ /^}/
|
||||||
|
|
||||||
This version does all the setup internally. It creates the output
|
This version does all the setup internally. It creates the output
|
||||||
channel, launches a goroutine running a function literal, and
|
channel, launches a goroutine running a function literal, and
|
||||||
returns the channel to the caller. It is a factory for concurrent
|
returns the channel to the caller. It is a factory for concurrent
|
||||||
execution, starting the goroutine and returning its connection.
|
execution, starting the goroutine and returning its connection.
|
||||||
|
|
||||||
The function literal notation (lines 12-16) allows us to construct an
|
The function literal notation used in the "go" statement allows us to construct an
|
||||||
anonymous function and invoke it on the spot. Notice that the local
|
anonymous function and invoke it on the spot. Notice that the local
|
||||||
variable "ch" is available to the function literal and lives on even
|
variable "ch" is available to the function literal and lives on even
|
||||||
after "generate" returns.
|
after "generate" returns.
|
||||||
|
|
||||||
The same change can be made to "filter":
|
The same change can be made to "filter":
|
||||||
|
|
||||||
--PROG progs/sieve1.go /func.filter/ /^}/
|
!src progs/sieve1.go /func.filter/ /^}/
|
||||||
|
|
||||||
The "sieve" function's main loop becomes simpler and clearer as a
|
The "sieve" function's main loop becomes simpler and clearer as a
|
||||||
result, and while we're at it let's turn it into a factory too:
|
result, and while we're at it let's turn it into a factory too:
|
||||||
|
|
||||||
--PROG progs/sieve1.go /func.sieve/ /^}/
|
!src progs/sieve1.go /func.sieve/ /^}/
|
||||||
|
|
||||||
Now "main"'s interface to the prime sieve is a channel of primes:
|
Now "main"'s interface to the prime sieve is a channel of primes:
|
||||||
|
|
||||||
--PROG progs/sieve1.go /func.main/ /^}/
|
!src progs/sieve1.go /func.main/ /^}/
|
||||||
|
|
||||||
Multiplexing
|
Multiplexing
|
||||||
----
|
----
|
||||||
@ -884,41 +880,41 @@ A realistic client-server program is a lot of code, so here is a very simple sub
|
|||||||
to illustrate the idea. It starts by defining a "request" type, which embeds a channel
|
to illustrate the idea. It starts by defining a "request" type, which embeds a channel
|
||||||
that will be used for the reply.
|
that will be used for the reply.
|
||||||
|
|
||||||
--PROG progs/server.go /type.request/ /^}/
|
!src progs/server.go /type.request/ /^}/
|
||||||
|
|
||||||
The server will be trivial: it will do simple binary operations on integers. Here's the
|
The server will be trivial: it will do simple binary operations on integers. Here's the
|
||||||
code that invokes the operation and responds to the request:
|
code that invokes the operation and responds to the request:
|
||||||
|
|
||||||
--PROG progs/server.go /type.binOp/ /^}/
|
!src progs/server.go /type.binOp/ /^}/
|
||||||
|
|
||||||
Line 14 defines the name "binOp" to be a function taking two integers and
|
The type declaration makes "binOp" represent a function taking two integers and
|
||||||
returning a third.
|
returning a third.
|
||||||
|
|
||||||
The "server" routine loops forever, receiving requests and, to avoid blocking due to
|
The "server" routine loops forever, receiving requests and, to avoid blocking due to
|
||||||
a long-running operation, starting a goroutine to do the actual work.
|
a long-running operation, starting a goroutine to do the actual work.
|
||||||
|
|
||||||
--PROG progs/server.go /func.server/ /^}/
|
!src progs/server.go /func.server/ /^}/
|
||||||
|
|
||||||
We construct a server in a familiar way, starting it and returning a channel
|
We construct a server in a familiar way, starting it and returning a channel
|
||||||
connected to it:
|
connected to it:
|
||||||
|
|
||||||
--PROG progs/server.go /func.startServer/ /^}/
|
!src progs/server.go /func.startServer/ /^}/
|
||||||
|
|
||||||
Here's a simple test. It starts a server with an addition operator and sends out
|
Here's a simple test. It starts a server with an addition operator and sends out
|
||||||
"N" requests without waiting for the replies. Only after all the requests are sent
|
"N" requests without waiting for the replies. Only after all the requests are sent
|
||||||
does it check the results.
|
does it check the results.
|
||||||
|
|
||||||
--PROG progs/server.go /func.main/ /^}/
|
!src progs/server.go /func.main/ /^}/
|
||||||
|
|
||||||
One annoyance with this program is that it doesn't shut down the server cleanly; when "main" returns
|
One annoyance with this program is that it doesn't shut down the server cleanly; when "main" returns
|
||||||
there are a number of lingering goroutines blocked on communication. To solve this,
|
there are a number of lingering goroutines blocked on communication. To solve this,
|
||||||
we can provide a second, "quit" channel to the server:
|
we can provide a second, "quit" channel to the server:
|
||||||
|
|
||||||
--PROG progs/server1.go /func.startServer/ /^}/
|
!src progs/server1.go /func.startServer/ /^}/
|
||||||
|
|
||||||
It passes the quit channel to the "server" function, which uses it like this:
|
It passes the quit channel to the "server" function, which uses it like this:
|
||||||
|
|
||||||
--PROG progs/server1.go /func.server/ /^}/
|
!src progs/server1.go /func.server/ /^}/
|
||||||
|
|
||||||
Inside "server", the "select" statement chooses which of the multiple communications
|
Inside "server", the "select" statement chooses which of the multiple communications
|
||||||
listed by its cases can proceed. If all are blocked, it waits until one can proceed; if
|
listed by its cases can proceed. If all are blocked, it waits until one can proceed; if
|
||||||
@ -930,9 +926,9 @@ returns, terminating its execution.
|
|||||||
All that's left is to strobe the "quit" channel
|
All that's left is to strobe the "quit" channel
|
||||||
at the end of main:
|
at the end of main:
|
||||||
|
|
||||||
--PROG progs/server1.go /adder,.quit/
|
!src progs/server1.go /adder,.quit/
|
||||||
...
|
...
|
||||||
--PROG progs/server1.go /quit....true/
|
!src progs/server1.go /quit....true/
|
||||||
|
|
||||||
There's a lot more to Go programming and concurrent programming in general but this
|
There's a lot more to Go programming and concurrent programming in general but this
|
||||||
quick tour should give you some of the basics.
|
quick tour should give you some of the basics.
|
||||||
|
157
doc/htmlgen.go
157
doc/htmlgen.go
@ -2,46 +2,80 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Process plain text into HTML.
|
// If --html is set, process plain text into HTML.
|
||||||
// - h2's are made from lines followed by a line "----\n"
|
// - h2's are made from lines followed by a line "----\n"
|
||||||
// - tab-indented blocks become <pre> blocks
|
// - tab-indented blocks become <pre> blocks with the first tab deleted
|
||||||
// - blank lines become <p> marks (except inside <pre> tags)
|
// - blank lines become <p> marks (except inside <pre> tags)
|
||||||
// - "quoted strings" become <code>quoted strings</code>
|
// - "quoted strings" become <code>quoted strings</code>
|
||||||
|
|
||||||
|
// Lines beginning !src define pieces of program source to be
|
||||||
|
// extracted from other files and injected as <pre> blocks.
|
||||||
|
// The syntax is simple: 1, 2, or 3 space-separated arguments:
|
||||||
|
//
|
||||||
|
// Whole file:
|
||||||
|
// !src foo.go
|
||||||
|
// One line (here the signature of main):
|
||||||
|
// !src foo.go /^func.main/
|
||||||
|
// Block of text, determined by start and end (here the body of main):
|
||||||
|
// !src foo.go /^func.main/ /^}/
|
||||||
|
//
|
||||||
|
// Patterns can be /regular.expression/, a decimal number, or $
|
||||||
|
// to signify the end of the file.
|
||||||
|
// TODO: the regular expression cannot contain spaces; does this matter?
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"template"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
lines = make([][]byte, 0, 2000) // probably big enough; grows if not
|
html = flag.Bool("html", true, "process text into HTML")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// lines holds the input and is reworked in place during processing.
|
||||||
|
lines = make([][]byte, 0, 20000)
|
||||||
|
|
||||||
empty = []byte("")
|
empty = []byte("")
|
||||||
newline = []byte("\n")
|
newline = []byte("\n")
|
||||||
tab = []byte("\t")
|
tab = []byte("\t")
|
||||||
quote = []byte(`"`)
|
quote = []byte(`"`)
|
||||||
indent = []byte{' ', ' ', ' ', ' '}
|
indent = []byte(" ")
|
||||||
|
|
||||||
sectionMarker = []byte("----\n")
|
sectionMarker = []byte("----\n")
|
||||||
preStart = []byte("<pre>")
|
preStart = []byte("<pre>")
|
||||||
preEnd = []byte("</pre>\n")
|
preEnd = []byte("</pre>\n")
|
||||||
pp = []byte("<p>\n")
|
pp = []byte("<p>\n")
|
||||||
|
|
||||||
|
srcPrefix = []byte("!src")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
read()
|
read()
|
||||||
|
programs()
|
||||||
|
if *html {
|
||||||
headings()
|
headings()
|
||||||
coalesce(preStart, foldPre)
|
coalesce(preStart, foldPre)
|
||||||
coalesce(tab, foldTabs)
|
coalesce(tab, foldTabs)
|
||||||
paragraphs()
|
paragraphs()
|
||||||
quotes()
|
quotes()
|
||||||
|
}
|
||||||
write()
|
write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read turns standard input into a slice of lines.
|
||||||
func read() {
|
func read() {
|
||||||
b := bufio.NewReader(os.Stdin)
|
b := bufio.NewReader(os.Stdin)
|
||||||
for {
|
for {
|
||||||
@ -56,6 +90,7 @@ func read() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write puts the result on standard output.
|
||||||
func write() {
|
func write() {
|
||||||
b := bufio.NewWriter(os.Stdout)
|
b := bufio.NewWriter(os.Stdout)
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
@ -64,8 +99,104 @@ func write() {
|
|||||||
b.Flush()
|
b.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
// each time prefix is found on a line, call fold and replace
|
// programs injects source code from !src invocations.
|
||||||
// line with return value from fold.
|
func programs() {
|
||||||
|
nlines := make([][]byte, 0, len(lines)*3/2)
|
||||||
|
for _, line := range lines {
|
||||||
|
if bytes.HasPrefix(line, srcPrefix) {
|
||||||
|
line = trim(line)[len(srcPrefix):]
|
||||||
|
prog := srcCommand(string(line))
|
||||||
|
if *html {
|
||||||
|
nlines = append(nlines, []byte(fmt.Sprintf("<pre><!--%s\n-->", line)))
|
||||||
|
}
|
||||||
|
for _, l := range prog {
|
||||||
|
nlines = append(nlines, htmlEscape(l))
|
||||||
|
}
|
||||||
|
if *html {
|
||||||
|
nlines = append(nlines, preEnd)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nlines = append(nlines, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines = nlines
|
||||||
|
}
|
||||||
|
|
||||||
|
// srcCommand processes one !src invocation.
|
||||||
|
func srcCommand(command string) [][]byte {
|
||||||
|
// TODO: quoted args so we can have 'a b'?
|
||||||
|
args := strings.Fields(command)
|
||||||
|
if len(args) == 0 || len(args) > 3 {
|
||||||
|
log.Fatal("bad syntax for src command: %s", command)
|
||||||
|
}
|
||||||
|
file := args[0]
|
||||||
|
lines := bytes.SplitAfter(readFile(file), newline)
|
||||||
|
// File plus zero args: whole file:
|
||||||
|
// !src file.go
|
||||||
|
if len(args) == 1 {
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
start := match(file, 0, lines, string(args[1]))
|
||||||
|
// File plus one arg: one line:
|
||||||
|
// !src file.go /foo/
|
||||||
|
if len(args) == 2 {
|
||||||
|
return [][]byte{lines[start]}
|
||||||
|
}
|
||||||
|
// File plus two args: range:
|
||||||
|
// !src file.go /foo/ /^}/
|
||||||
|
end := match(file, start, lines, string(args[2]))
|
||||||
|
return lines[start : end+1] // +1 to include matched line.
|
||||||
|
}
|
||||||
|
|
||||||
|
// htmlEscape makes sure input is HTML clean, if necessary.
|
||||||
|
func htmlEscape(input []byte) []byte {
|
||||||
|
if !*html || bytes.IndexAny(input, `&"<>`) < 0 {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
var b bytes.Buffer
|
||||||
|
template.HTMLEscape(&b, input)
|
||||||
|
return b.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// readFile reads and returns a file as part of !src processing.
|
||||||
|
func readFile(name string) []byte {
|
||||||
|
file, err := ioutil.ReadFile(name)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// match identifies the input line that matches the pattern in a !src invocation.
|
||||||
|
// If start>0, match lines starting there rather than at the beginning.
|
||||||
|
func match(file string, start int, lines [][]byte, pattern string) int {
|
||||||
|
// $ matches the end of the file.
|
||||||
|
if pattern == "$" {
|
||||||
|
return len(lines) - 1
|
||||||
|
}
|
||||||
|
// Number matches the line.
|
||||||
|
if i, err := strconv.Atoi(pattern); err == nil {
|
||||||
|
return i - 1 // Lines are 1-indexed.
|
||||||
|
}
|
||||||
|
// /regexp/ matches the line that matches the regexp.
|
||||||
|
if len(pattern) > 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' {
|
||||||
|
re, err := regexp.Compile(pattern[1 : len(pattern)-1])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
for i := start; i < len(lines); i++ {
|
||||||
|
if re.Match(lines[i]) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Fatalf("%s: no match for %s", file, pattern)
|
||||||
|
}
|
||||||
|
log.Fatalf("unrecognized pattern: %s", pattern)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// coalesce combines lines. Each time prefix is found on a line,
|
||||||
|
// it calls fold and replaces the line with return value from fold.
|
||||||
func coalesce(prefix []byte, fold func(i int) (n int, line []byte)) {
|
func coalesce(prefix []byte, fold func(i int) (n int, line []byte)) {
|
||||||
j := 0 // output line number goes up by one each loop
|
j := 0 // output line number goes up by one each loop
|
||||||
for i := 0; i < len(lines); {
|
for i := 0; i < len(lines); {
|
||||||
@ -82,7 +213,7 @@ func coalesce(prefix []byte, fold func(i int) (n int, line []byte)) {
|
|||||||
lines = lines[0:j]
|
lines = lines[0:j]
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the <pre> block as a single slice
|
// foldPre returns the <pre> block as a single slice.
|
||||||
func foldPre(i int) (n int, line []byte) {
|
func foldPre(i int) (n int, line []byte) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
for i < len(lines) {
|
for i < len(lines) {
|
||||||
@ -96,7 +227,7 @@ func foldPre(i int) (n int, line []byte) {
|
|||||||
return n, buf.Bytes()
|
return n, buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the tab-indented block as a single <pre>-bounded slice
|
// foldTabs returns the tab-indented block as a single <pre>-bounded slice.
|
||||||
func foldTabs(i int) (n int, line []byte) {
|
func foldTabs(i int) (n int, line []byte) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
buf.WriteString("<pre>\n")
|
buf.WriteString("<pre>\n")
|
||||||
@ -104,7 +235,7 @@ func foldTabs(i int) (n int, line []byte) {
|
|||||||
if !bytes.HasPrefix(lines[i], tab) {
|
if !bytes.HasPrefix(lines[i], tab) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
buf.Write(lines[i])
|
buf.Write(lines[i][1:]) // delete leading tab.
|
||||||
n++
|
n++
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
@ -112,6 +243,7 @@ func foldTabs(i int) (n int, line []byte) {
|
|||||||
return n, buf.Bytes()
|
return n, buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// headings turns sections into HTML sections.
|
||||||
func headings() {
|
func headings() {
|
||||||
b := bufio.NewWriter(os.Stdout)
|
b := bufio.NewWriter(os.Stdout)
|
||||||
for i, l := range lines {
|
for i, l := range lines {
|
||||||
@ -123,6 +255,7 @@ func headings() {
|
|||||||
b.Flush()
|
b.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// paragraphs turns blank lines into paragraph marks.
|
||||||
func paragraphs() {
|
func paragraphs() {
|
||||||
for i, l := range lines {
|
for i, l := range lines {
|
||||||
if bytes.Equal(l, newline) {
|
if bytes.Equal(l, newline) {
|
||||||
@ -131,12 +264,14 @@ func paragraphs() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// quotes turns "x" in the file into <code>x</code>.
|
||||||
func quotes() {
|
func quotes() {
|
||||||
for i, l := range lines {
|
for i, l := range lines {
|
||||||
lines[i] = codeQuotes(l)
|
lines[i] = codeQuotes(l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// quotes turns "x" in the line into <code>x</code>.
|
||||||
func codeQuotes(l []byte) []byte {
|
func codeQuotes(l []byte) []byte {
|
||||||
if bytes.HasPrefix(l, preStart) {
|
if bytes.HasPrefix(l, preStart) {
|
||||||
return l
|
return l
|
||||||
@ -162,7 +297,7 @@ func codeQuotes(l []byte) []byte {
|
|||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop trailing newline
|
// trim drops the trailing newline, if present.
|
||||||
func trim(l []byte) []byte {
|
func trim(l []byte) []byte {
|
||||||
n := len(l)
|
n := len(l)
|
||||||
if n > 0 && l[n-1] == '\n' {
|
if n > 0 && l[n-1] == '\n' {
|
||||||
@ -171,7 +306,7 @@ func trim(l []byte) []byte {
|
|||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
// expand tabs to spaces. don't worry about columns.
|
// expandTabs expands tabs to spaces. It doesn't worry about columns.
|
||||||
func expandTabs(l []byte) []byte {
|
func expandTabs(l []byte) []byte {
|
||||||
return bytes.Replace(l, tab, indent, -1)
|
return bytes.Replace(l, tab, indent, -1)
|
||||||
}
|
}
|
||||||
|
16
doc/makehtml
16
doc/makehtml
@ -7,7 +7,6 @@ set -e
|
|||||||
|
|
||||||
TXT=${1:-go_tutorial.txt} # input file
|
TXT=${1:-go_tutorial.txt} # input file
|
||||||
HTML=$(basename $TXT .txt).html # output file (basename)
|
HTML=$(basename $TXT .txt).html # output file (basename)
|
||||||
TMP=TEMP.txt # input to htmlgen
|
|
||||||
|
|
||||||
if ! test -w $HTML
|
if ! test -w $HTML
|
||||||
then
|
then
|
||||||
@ -15,17 +14,4 @@ then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if grep -q '^--PROG' $TXT
|
make htmlgen && ./htmlgen < $TXT > $HTML
|
||||||
then
|
|
||||||
echo >&2 makehtml: processing PROG sections
|
|
||||||
<$TXT >$TMP awk '
|
|
||||||
/^--PROG/ { system("sh ./prog.sh "$2" "$3" "$4" "); getline }
|
|
||||||
/^/ {print}
|
|
||||||
'
|
|
||||||
else
|
|
||||||
cp $TXT $TMP
|
|
||||||
fi
|
|
||||||
|
|
||||||
make htmlgen && ./htmlgen < $TMP > $HTML
|
|
||||||
|
|
||||||
rm -f $TMP
|
|
||||||
|
72
doc/prog.sh
72
doc/prog.sh
@ -1,72 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style
|
|
||||||
# license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
# generate HTML for a program excerpt.
|
|
||||||
# first arg is file name
|
|
||||||
# second arg is awk pattern to match start line
|
|
||||||
# third arg is awk pattern to stop processing
|
|
||||||
#
|
|
||||||
# missing third arg means print one line
|
|
||||||
# third arg "END" means proces rest of file
|
|
||||||
# missing second arg means process whole file
|
|
||||||
#
|
|
||||||
# examples:
|
|
||||||
#
|
|
||||||
# prog.sh foo.go # whole file
|
|
||||||
# prog.sh foo.go "/^func.main/" # signature of main
|
|
||||||
# prog.sh foo.go "/^func.main/" "/^}/ # body of main
|
|
||||||
#
|
|
||||||
# non-blank lines are annotated with line number in file
|
|
||||||
|
|
||||||
# line numbers are printed %.2d to make them equal-width for nice formatting.
|
|
||||||
# the format gives a leading 0. the format %2d gives a leading space but
|
|
||||||
# that appears to confuse sanjay's makehtml formatter into bungling quotes
|
|
||||||
# because it makes some lines look indented.
|
|
||||||
|
|
||||||
echo "<pre> <!-- $* -->"
|
|
||||||
|
|
||||||
case $# in
|
|
||||||
3)
|
|
||||||
if test "$3" = "END" # $2 to end of file
|
|
||||||
then
|
|
||||||
awk '
|
|
||||||
function LINE() { printf("%.2d\t%s\n", NR, $0) }
|
|
||||||
BEGIN { printing = 0 }
|
|
||||||
'$2' { printing = 1; LINE(); getline }
|
|
||||||
printing { if($0 ~ /./) { LINE() } else { print "" } }
|
|
||||||
'
|
|
||||||
else # $2 through $3
|
|
||||||
awk '
|
|
||||||
function LINE() { printf("%.2d\t%s\n", NR, $0) }
|
|
||||||
BEGIN { printing = 0 }
|
|
||||||
'$2' { printing = 1; LINE(); getline }
|
|
||||||
'$3' && printing { if(printing) {printing = 0; LINE(); exit} }
|
|
||||||
printing { if($0 ~ /./) { LINE() } else { print "" } }
|
|
||||||
'
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
2) # one line
|
|
||||||
awk '
|
|
||||||
function LINE() { printf("%.2d\t%s\n", NR, $0) }
|
|
||||||
'$2' { LINE(); getline; exit }
|
|
||||||
'
|
|
||||||
;;
|
|
||||||
1) # whole file
|
|
||||||
awk '
|
|
||||||
function LINE() { printf("%.2d\t%s\n", NR, $0) }
|
|
||||||
{ if($0 ~ /./) { LINE() } else { print "" } }
|
|
||||||
'
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo >&2 usage: prog.sh file.go /func.main/ /^}/
|
|
||||||
esac <$1 |
|
|
||||||
sed '
|
|
||||||
s/&/\&/g
|
|
||||||
s/"/\"/g
|
|
||||||
s/</\</g
|
|
||||||
s/>/\>/g
|
|
||||||
'
|
|
||||||
|
|
||||||
echo '</pre>'
|
|
Loading…
Reference in New Issue
Block a user