</pre>
<p>
-<font color=red>TODO: Unify this section with Selectors - it's the same syntax.</font>
+<span class="alert">TODO: Unify this section with Selectors - it's the same syntax.</span>
</p>
<h3 id="Composite_literals">Composite literals</h3>
</pre>
-<font color=red>
+<span class="alert">
TODO: Specify what happens to receivers.
-</font>
+</span>
<h3 id="Indexes">Indexes</h3>
operation,
or a field selector of an addressable struct operand.
A function result variable is not addressable.
-(<font color=red>TODO: remove this restriction.</font>)
+(<span class="alert">TODO: remove this restriction.</span>)
Given an operand of pointer type, the pointer indirection
operator <code>*</code> retrieves the value pointed
to by the operand.
</p>
<p>
-<font color=red>TODO: Probably in a separate section, communication semantics
-need to be presented regarding send, receive, select, and goroutines.</font>
+<span class="alert">TODO: Probably in a separate section, communication semantics
+need to be presented regarding send, receive, select, and goroutines.</span>
</p>
<h3 id="Method_expressions">Method expressions</h3>
</pre>
<p>
-<font color=red>
+<span class="alert">
TODO: perhaps ^ should be disallowed on non-uints instead of assuming twos complement.
Also it may be possible to make typed constants more like variables, at the cost of fewer
overflow etc. errors being caught.
-</font>
+</span>
</p>
</ol>
<p>
-<font color=red>
+<span class="alert">
TODO: Define when return is required.<br />
TODO: Language about result parameters needs to go into a section on
function/method invocation<br />
-</font>
+</span>
</p>
<h3 id="Break_statements">Break statements</h3>
<p>
is erroneous because the jump to label <code>L</code> skips
the creation of <code>v</code>.
-(<font color=red>TODO: Eliminate in favor of used and not set errors?</font>)
+(<span class="alert">TODO: Eliminate in favor of used and not set errors?</span>)
</p>
<h3 id="Fallthrough_statements">Fallthrough statements</h3>
<code>unsafe.Alignof(x[0])</code>, but at least 1.
</ol>
-<h2 id="Implementation_differences"><font color=red>Implementation differences - TODO</font></h2>
+<h2 id="Implementation_differences"><span class="alert">Implementation differences - TODO</span></h2>
<ul>
- <li><font color=red>Implementation does not honor the restriction on goto statements and targets (no intervening declarations).</font></li>
- <li><font color=red>Gccgo does not implement the blank identifier.</font></li>
- <li><font color=red>Method expressions are not implemented.</font></li>
+ <li><span class="alert">Implementation does not honor the restriction on goto statements and targets (no intervening declarations).</span></li>
+ <li><span class="alert">Gccgo does not implement the blank identifier.</span></li>
+ <li><span class="alert">Method expressions are not implemented.</span></li>
</ul>
+/*
+ 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.
+*/
+
+/* ------------------------------------------------------------------------- */
+/* Styles meant to help page authors achieve beauty. */
+
code, .code {
font-size: 100%;
font-family: monospace;
}
p.rule {
- font-style: italic
+ font-style: italic;
}
span.event {
- font-style: italic
+ font-style: italic;
+}
+
+span.alert {
+ color: #ff0000;
}
body {
border-top:1px solid #36C;
}
-pre{
+pre {
font-size: 9pt;
background-color: #f8f8ff;
margin: 1em 0 0 0;
margin: 0.5em 0px 1em 0px;
}
-/* Above this comment, styles meant to help page authors achieve beauty. */
-/* Below this comment, styles used in the boilerplate-ish parts of pages. */
+
+/* ------------------------------------------------------------------------- */
+/* Styles used in the boilerplate-ish parts of pages. */
div#content {
margin-left: 20%;
}
}
+
+/* ------------------------------------------------------------------------- */
+/* Styles used by go/printer Styler implementations. */
+
+span.comment {
+ color: #0000a0;
+}
+
+span.highlight {
+ background-color: #00ff00;
+}
// Write Go output: Go input with rewrites of C.xxx to _C_xxx.
fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n");
fmt.Fprintf(fgo1, "//line %s:1\n", srcfile);
- printer.Fprint(fgo1, p.AST, 0, 8);
+ printer.Fprint(fgo1, p.AST, 0, 8, nil);
// Write second Go output: definitions of _C_xxx.
// In a separate file so that the import of "unsafe" does not
for name, def := range p.Typedef {
fmt.Fprintf(fgo2, "type %s ", name);
- printer.Fprint(fgo2, def, 0, 8);
+ printer.Fprint(fgo2, def, 0, 8, nil);
fmt.Fprintf(fgo2, "\n");
}
fmt.Fprintf(fgo2, "type _C_void [0]byte\n");
for name, def := range p.Vardef {
fmt.Fprintf(fc, "#pragma dynld %s·_C_%s %s \"%s/%s_%s.so\"\n", p.Package, name, name, pkgroot, p.PackagePath, base);
fmt.Fprintf(fgo2, "var _C_%s ", name);
- printer.Fprint(fgo2, &ast.StarExpr{X: def.Go}, 0, 8);
+ printer.Fprint(fgo2, &ast.StarExpr{X: def.Go}, 0, 8, nil);
fmt.Fprintf(fgo2, "\n");
}
fmt.Fprintf(fc, "\n");
Name: &ast.Ident{Value: "_C_"+name},
Type: def.Go,
};
- printer.Fprint(fgo2, d, 0, 8);
+ printer.Fprint(fgo2, d, 0, 8, nil);
fmt.Fprintf(fgo2, "\n");
if name == "CString" || name == "GoString" {
const Pkg = "/pkg/" // name for auto-generated package documentation tree
-type delayTime struct {
- mutex sync.RWMutex;
- minutes int;
-}
-
+// ----------------------------------------------------------------------------
+// Support types
-func (dt *delayTime) set(minutes int) {
- dt.mutex.Lock();
- dt.minutes = minutes;
- dt.mutex.Unlock();
+// An RWValue wraps a value and permits mutually exclusive
+// access to it and records the time the value was last set.
+type RWValue struct {
+ mutex sync.RWMutex;
+ value interface{};
+ timestamp int64; // time of last set(), in seconds since epoch
}
-func (dt *delayTime) backoff(max int) {
- dt.mutex.Lock();
- dt.minutes *= 2;
- if dt.minutes > max {
- dt.minutes = max;
- }
- dt.mutex.Unlock();
+func (v *RWValue) set(value interface{}) {
+ v.mutex.Lock();
+ v.value = value;
+ v.timestamp = time.Seconds();
+ v.mutex.Unlock();
}
-func (dt *delayTime) get() int {
- dt.mutex.RLock();
- defer dt.mutex.RUnlock();
- return dt.minutes;
+func (v *RWValue) get() (interface{}, int64) {
+ v.mutex.RLock();
+ defer v.mutex.RUnlock();
+ return v.value, v.timestamp;
}
-type timeStamp struct {
- mutex sync.RWMutex;
- seconds int64;
-}
-
+// ----------------------------------------------------------------------------
+// Globals
-func (ts *timeStamp) set() {
- ts.mutex.Lock();
- ts.seconds = time.Seconds();
- ts.mutex.Unlock();
+type delayTime struct {
+ RWValue;
}
-func (ts *timeStamp) get() int64 {
- ts.mutex.RLock();
- defer ts.mutex.RUnlock();
- return ts.seconds;
+func (dt *delayTime) backoff(max int) {
+ dt.mutex.Lock();
+ v := dt.value.(int) * 2;
+ if v > max {
+ v = max;
+ }
+ dt.value = v;
+ dt.mutex.Unlock();
}
syncCmd = flag.String("sync", "", "sync command; disabled if empty");
syncMin = flag.Int("sync_minutes", 0, "sync interval in minutes; disabled if <= 0");
syncDelay delayTime; // actual sync delay in minutes; usually syncDelay == syncMin, but delay may back off exponentially
- syncTime timeStamp; // time of last p4 sync
+ syncTime RWValue; // time of last p4 sync
// layout control
tabwidth = flag.Int("tabwidth", 4, "tab width");
goroot = "/home/r/go-release/go";
}
flag.StringVar(&goroot, "goroot", goroot, "Go root directory");
- syncTime.set(); // have a reasonable initial value
+ syncTime.set(nil); // have a reasonable initial value (time is shown on web page)
}
}
+// ----------------------------------------------------------------------------
+// HTML formatting support
+
+// Styler implements a printer.Styler.
+type Styler struct {
+ highlight string;
+}
+
+
+func (s *Styler) LineTag(line int) (text []byte, tag printer.HtmlTag) {
+ tag = printer.HtmlTag{fmt.Sprintf(`<a id="L%d">`, line), "</a>"};
+ return;
+}
+
+
+func (s *Styler) Comment(c *ast.Comment, line []byte) (text []byte, tag printer.HtmlTag) {
+ text = line;
+ // minimal syntax-coloring of comments for now - people will want more
+ // (don't do anything more until there's a button to turn it on/off)
+ tag = printer.HtmlTag{`<span class="comment">`, "</span>"};
+ return;
+}
+
+
+func (s *Styler) BasicLit(x *ast.BasicLit) (text []byte, tag printer.HtmlTag) {
+ text = x.Value;
+ return;
+}
+
+
+func (s *Styler) Ident(id *ast.Ident) (text []byte, tag printer.HtmlTag) {
+ text = strings.Bytes(id.Value);
+ if s.highlight == id.Value {
+ tag = printer.HtmlTag{"<span class=highlight>", "</span>"};
+ }
+ return;
+}
+
+
+func (s *Styler) Token(tok token.Token) (text []byte, tag printer.HtmlTag) {
+ text = strings.Bytes(tok.String());
+ return;
+}
+
+
+
// ----------------------------------------------------------------------------
// Templates
// Write an AST-node to w; optionally html-escaped.
-func writeNode(w io.Writer, node interface{}, html bool) {
+func writeNode(w io.Writer, node interface{}, html bool, style printer.Styler) {
mode := printer.UseSpaces;
if html {
mode |= printer.GenHTML;
}
- printer.Fprint(w, node, mode, *tabwidth);
+ printer.Fprint(w, node, mode, *tabwidth, style);
}
case string:
writeText(w, strings.Bytes(v), html);
case ast.Decl:
- writeNode(w, v, html);
+ writeNode(w, v, html, nil);
case ast.Expr:
- writeNode(w, v, html);
+ writeNode(w, v, html, nil);
default:
if html {
var buf bytes.Buffer;
}
-var godocHtml *template.Template
-var packageHtml *template.Template
-var packageText *template.Template
-var parseerrorHtml *template.Template
-var parseerrorText *template.Template
+var (
+ godocHtml,
+ packageHtml,
+ packageText,
+ parseerrorHtml,
+ parseerrorText *template.Template;
+)
func readTemplates() {
// have to delay until after flags processing,
content interface{};
}
+ _, ts := syncTime.get();
d := Data{
title: title,
- timestamp: time.SecondsToLocalTime(syncTime.get()).String(),
+ timestamp: time.SecondsToLocalTime(ts).String(),
content: content,
};
}
-func serveGoSource(c *http.Conn, filename string) {
+func serveGoSource(c *http.Conn, filename string, style printer.Styler) {
path := pathutil.Join(goroot, filename);
prog, errors := parse(path, parser.ParseComments);
if errors != nil {
var buf bytes.Buffer;
fmt.Fprintln(&buf, "<pre>");
- writeNode(&buf, prog, true);
+ writeNode(&buf, prog, true, style);
fmt.Fprintln(&buf, "</pre>");
servePage(c, "Source file " + filename, buf.Bytes());
serveHtmlDoc(c, r, path);
case ext == ".go":
- serveGoSource(c, path);
+ serveGoSource(c, path, &Styler{highlight: r.FormValue("h")});
default:
// TODO:
args := []string{"/bin/sh", "-c", *syncCmd};
if exec(c, args) {
// sync succeeded
- syncTime.set();
+ syncTime.set(nil);
syncDelay.set(*syncMin); // revert to regular sync schedule
} else {
// sync failed - back off exponentially, but try at least once a day
go func() {
for {
dosync(nil, nil);
+ _, delay := syncDelay.get();
if *verbose {
- log.Stderrf("next sync in %dmin", syncDelay.get());
+ log.Stderrf("next sync in %dmin", delay);
}
- time.Sleep(int64(syncDelay.get())*(60*1e9));
+ time.Sleep(int64(delay)*60e9);
}
}();
}
+ // Start http server.
if err := http.ListenAndServe(*httpaddr, handler); err != nil {
log.Exitf("ListenAndServe %s: %v", *httpaddr, err);
}
}
var res bytes.Buffer;
- _, err = printer.Fprint(&res, file, printerMode(), *tabwidth);
+ _, err = printer.Fprint(&res, file, printerMode(), *tabwidth, nil);
if err != nil {
return err;
}
var noPos token.Position
-// A lineTag is a token.Position that is used to print
-// line tag id's of the form "L%d" where %d stands for
-// the line indicated by position.
-//
-type lineTag token.Position
+// An HtmlTag specifies a start and end tag.
+type HtmlTag struct {
+ Start, End string; // empty if tags are absent
+}
-// A htmlTag specifies a start and end tag.
-type htmlTag struct {
- start, end string; // empty if tags are absent
+// A Styler specifies the formatting line tags and elementary Go words.
+// A format consists of text and a (possibly empty) surrounding HTML tag.
+type Styler interface {
+ LineTag(line int) ([]byte, HtmlTag);
+ Comment(c *ast.Comment, line []byte) ([]byte, HtmlTag);
+ BasicLit(x *ast.BasicLit) ([]byte, HtmlTag);
+ Ident(id *ast.Ident) ([]byte, HtmlTag);
+ Token(tok token.Token) ([]byte, HtmlTag);
}
output io.Writer;
mode uint;
tabwidth int;
+ style Styler;
errors chan os.Error;
// Current state
last token.Position;
// HTML support
- tag htmlTag; // tag to be used around next item
lastTaggedLine int; // last line for which a line tag was written
// The list of comments; or nil.
}
-func (p *printer) init(output io.Writer, mode uint, tabwidth int) {
+func (p *printer) init(output io.Writer, mode uint, tabwidth int, style Styler) {
p.output = output;
p.mode = mode;
p.tabwidth = tabwidth;
+ p.style = style;
p.errors = make(chan os.Error);
p.buffer = make([]whiteSpace, 0, 16); // whitespace sequences are short
}
}
+func (p *printer) writeTaggedItem(data []byte, tag HtmlTag) {
+ // write start tag, if any
+ // (no html-escaping and no p.pos update for tags - use write0)
+ if tag.Start != "" {
+ p.write0(strings.Bytes(tag.Start));
+ }
+ p.write(data);
+ // write end tag, if any
+ if tag.End != "" {
+ p.write0(strings.Bytes(tag.End));
+ }
+}
+
+
// writeItem writes data at position pos. data is the text corresponding to
// a single lexical token, but may also be comment text. pos is the actual
// (or at least very accurately estimated) position of the data in the original
-// source text. The data may be tagged, depending on p.mode and the setLineTag
-// parameter. writeItem updates p.last to the position immediately following
-// the data.
+// source text. If tags are present and GenHTML is set, the tags are written
+// before and after the data. writeItem updates p.last to the position
+// immediately following the data.
//
-func (p *printer) writeItem(pos token.Position, data []byte, setLineTag bool) {
+func (p *printer) writeItem(pos token.Position, data []byte, tag HtmlTag) {
p.pos = pos;
if debug {
// do not update p.pos - use write0
p.write0(strings.Bytes(fmt.Sprintf("[%d:%d]", pos.Line, pos.Column)));
}
if p.mode & GenHTML != 0 {
- // no html-escaping and no p.pos update for tags - use write0
- if setLineTag && pos.Line > p.lastTaggedLine {
- // id's must be unique within a document: set
- // line tag only if line number has increased
- // (note: for now write complete start and end
- // tag - shorter versions seem to have issues
- // with Safari)
- p.tag.start = fmt.Sprintf(`<a id="L%d"></a>`, pos.Line);
+ // write line tag if on a new line
+ // TODO(gri): should write line tags on each line at the start
+ // will be more useful (e.g. to show line numbers)
+ if p.style != nil && pos.Line > p.lastTaggedLine {
+ p.writeTaggedItem(p.style.LineTag(pos.Line));
p.lastTaggedLine = pos.Line;
}
- // write start tag, if any
- if p.tag.start != "" {
- p.write0(strings.Bytes(p.tag.start));
- p.tag.start = ""; // tag consumed
- }
- p.write(data);
- // write end tag, if any
- if p.tag.end != "" {
- p.write0(strings.Bytes(p.tag.end));
- p.tag.end = ""; // tag consumed
- }
+ p.writeTaggedItem(data, tag);
} else {
p.write(data);
}
}
// write comment
- p.writeItem(comment.Pos(), text, false);
+ var tag HtmlTag;
+ if p.style != nil {
+ text, tag = p.style.Comment(comment, text);
+ }
+ p.writeItem(comment.Pos(), text, tag);
}
// printed, followed by the actual token.
//
func (p *printer) print(args ...) {
- setLineTag := false;
v := reflect.NewValue(args).(*reflect.StructValue);
for i := 0; i < v.NumField(); i++ {
f := v.Field(i);
next := p.pos; // estimated position of next item
var data []byte;
+ var tag HtmlTag;
switch x := f.Interface().(type) {
case whiteSpace:
if x == ignore {
p.buffer = p.buffer[0 : i+1];
p.buffer[i] = x;
case []byte:
+ // TODO(gri): remove this case once commentList
+ // handles comments correctly
data = x;
case string:
+ // TODO(gri): remove this case once fieldList
+ // handles comments correctly
data = strings.Bytes(x);
+ case *ast.Ident:
+ if p.style != nil {
+ data, tag = p.style.Ident(x);
+ } else {
+ data = strings.Bytes(x.Value);
+ }
+ case *ast.BasicLit:
+ if p.style != nil {
+ data, tag = p.style.BasicLit(x);
+ } else {
+ data = x.Value;
+ }
+ // escape all literals so they pass through unchanged
+ // (note that valid Go programs cannot contain esc ('\xff')
+ // bytes since they do not appear in legal UTF-8 sequences)
+ // TODO(gri): this this more efficiently.
+ data = strings.Bytes("\xff" + string(data) + "\xff");
case token.Token:
- data = strings.Bytes(x.String());
+ if p.style != nil {
+ data, tag = p.style.Token(x);
+ } else {
+ data = strings.Bytes(x.String());
+ }
case token.Position:
if x.IsValid() {
next = x; // accurate position of next item
}
- case lineTag:
- pos := token.Position(x);
- if pos.IsValid() {
- next = pos; // accurate position of next item
- setLineTag = true;
- }
- case htmlTag:
- p.tag = x; // tag surrounding next item
default:
panicln("print: unsupported argument type", f.Type().String());
}
// at the end of a file)
p.writeNewlines(next.Line - p.pos.Line);
- p.writeItem(next, data, setLineTag);
- setLineTag = false;
+ p.writeItem(next, data, tag);
}
}
}
func (p *printer) commentList(list []*ast.Comment) {
for i, c := range list {
t := c.Text;
+ // TODO(gri): this needs to be styled like normal comments
p.print(c.Pos(), t);
if t[1] == '/' && i+1 < len(list) {
//-style comment which is not at the end; print a newline
}
}
if isIncomplete {
+ // TODO(gri): this needs to be styled like normal comments
p.print("// contains unexported fields");
}
}
}
if isIncomplete {
+ // TODO(gri): this needs to be styled like normal comments
p.print("// contains unexported methods");
}
p.print("BadExpr");
case *ast.Ident:
- p.print(x.Value);
+ p.print(x);
case *ast.BinaryExpr:
p.binaryExpr(x, prec1);
}
case *ast.BasicLit:
- // escape all literals so they pass through unchanged
- // (note that valid Go programs cannot contain esc ('\xff')
- // bytes since they do not appear in legal UTF-8 sequences)
- p.print(esc, x.Value, esc);
+ p.print(x);
case *ast.StringList:
p.stringList(x.Strings);
func (p *printer) genDecl(d *ast.GenDecl, context declContext) {
p.leadComment(d.Doc);
- p.print(lineTag(d.Pos()), d.Tok, blank);
+ p.print(d.Pos(), d.Tok, blank);
if d.Lparen.IsValid() {
// group of parenthesized declarations
// test-print the statement and see if it would fit
var buf bytes.Buffer;
- _, err := Fprint(&buf, b.List[0], p.mode, p.tabwidth);
+ _, err := Fprint(&buf, b.List[0], p.mode, p.tabwidth, p.style);
if err != nil {
return false; // don't try
}
func (p *printer) funcDecl(d *ast.FuncDecl) {
p.leadComment(d.Doc);
- p.print(lineTag(d.Pos()), token.FUNC, blank);
+ p.print(d.Pos(), token.FUNC, blank);
if recv := d.Recv; recv != nil {
// method: print receiver
p.print(token.LPAREN);
// or assignment-compatible to ast.Expr, ast.Decl, or ast.Stmt. Printing
// is controlled by the mode and tabwidth parameters.
//
-func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int) (int, os.Error) {
+func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int, style Styler) (int, os.Error) {
// redirect output through a trimmer to eliminate trailing whitespace
// (Input to a tabwriter must be untrimmed since trailing tabs provide
// formatting information. The tabwriter could provide trimming
// setup printer and print node
var p printer;
- p.init(output, mode, tabwidth);
+ p.init(output, mode, tabwidth, style);
go func() {
switch n := node.(type) {
case ast.Expr:
// format source
var buf bytes.Buffer;
- if _, err := Fprint(&buf, prog, pmode, tabwidth); err != nil {
+ if _, err := Fprint(&buf, prog, pmode, tabwidth, nil); err != nil {
t.Error(err);
}
res := buf.Bytes();