}
fatal("parsing %s: %s", name, err)
}
- p.Package = p.AST.Name.Value
+ p.Package = p.AST.Name.Name()
// Find the import "C" line and get any extra C preamble.
// Delete the import "C" line along the way.
// The parser should take care of scoping in the future,
// so that we will be able to distinguish a "top-level C"
// from a local C.
- if l, ok := sel.X.(*ast.Ident); ok && l.Value == "C" {
+ if l, ok := sel.X.(*ast.Ident); ok && l.Name() == "C" {
i := len(p.Crefs)
if i >= cap(p.Crefs) {
new := make([]*Cref, 2*i)
}
p.Crefs = p.Crefs[0 : i+1]
p.Crefs[i] = &Cref{
- Name: sel.Sel.Value,
+ Name: sel.Sel.Name(),
Expr: n,
Context: context,
}
t.Go = name // publish before recursive calls
switch dt.Kind {
case "union", "class":
- c.typedef[name.Value] = c.Opaque(t.Size)
+ c.typedef[name.Name()] = c.Opaque(t.Size)
if t.C == "" {
t.C = fmt.Sprintf("typeof(unsigned char[%d])", t.Size)
}
t.C = csyntax
}
t.Align = align
- c.typedef[name.Value] = g
+ c.typedef[name.Name()] = g
}
case *dwarf.TypedefType:
sub := c.Type(dt.Type)
t.Size = sub.Size
t.Align = sub.Align
- if _, ok := c.typedef[name.Value]; !ok {
- c.typedef[name.Value] = sub.Go
+ if _, ok := c.typedef[name.Name()]; !ok {
+ c.typedef[name.Name()] = sub.Go
}
case *dwarf.UcharType:
}
s = strings.Join(strings.Split(s, " ", 0), "") // strip spaces
name := c.Ident("_C_" + s)
- c.typedef[name.Value] = t.Go
+ c.typedef[name.Name()] = t.Go
t.Go = name
}
}
}
// Identifier
-func (c *typeConv) Ident(s string) *ast.Ident { return &ast.Ident{Value: s} }
+func (c *typeConv) Ident(s string) *ast.Ident { return ast.NewIdent(s) }
// Opaque type of n bytes.
func (c *typeConv) Opaque(n int64) ast.Expr {
switch cref.Context {
case "const":
// This came from a #define and we'll output it later.
- *cref.Expr = &ast.Ident{Value: cref.Name}
+ *cref.Expr = ast.NewIdent(cref.Name)
break
case "call":
if !cref.TypeName {
// Is an actual function call.
pos := (*cref.Expr).Pos()
- *cref.Expr = &ast.Ident{Position: pos, Value: "_C_" + cref.Name}
+ *cref.Expr = &ast.Ident{Position: pos, Obj: ast.NewObj(ast.Err, pos, "_C_"+cref.Name)}
p.Funcdef[cref.Name] = cref.FuncType
break
}
// place the identifier for the value and add it to Enumdef so
// it will be declared as a constant in the later stage.
if cref.Type.EnumValues != nil {
- *cref.Expr = &ast.Ident{Value: cref.Name}
+ *cref.Expr = ast.NewIdent(cref.Name)
p.Enumdef[cref.Name] = cref.Type.EnumValues[cref.Name]
break
}
// Reference to C variable.
// We declare a pointer and arrange to have it filled in.
- *cref.Expr = &ast.StarExpr{X: &ast.Ident{Value: "_C_" + cref.Name}}
+ *cref.Expr = &ast.StarExpr{X: ast.NewIdent("_C_" + cref.Name)}
p.Vardef[cref.Name] = cref.Type
case "type":
if !cref.TypeName {
for name, def := range p.Funcdef {
// Go func declaration.
d := &ast.FuncDecl{
- Name: &ast.Ident{Value: "_C_" + name},
+ Name: ast.NewIdent("_C_" + name),
Type: def.Go,
}
printer.Fprint(fgo2, d)
if err != nil || file == nil {
return ""
}
- return file.Name.Value
+ return file.Name.Name()
}
// (left-over) "documentation" package somewhere in a package
// directory of different name, but this is very unlikely and
// against current conventions.
- (file.Name.Value == name || file.Name.Value == fakePkgName) &&
+ (file.Name.Name() == name || file.Name.Name() == fakePkgName) &&
file.Doc != nil {
// found documentation; extract a synopsys
text = firstSentence(doc.CommentText(file.Doc))
func (s *Styler) Ident(id *ast.Ident) (text []byte, tag printer.HTMLTag) {
- text = strings.Bytes(id.Value)
- if s.highlight == id.Value {
+ text = strings.Bytes(id.Name())
+ if s.highlight == id.Name() {
tag = printer.HTMLTag{"<span class=highlight>", "</span>"}
}
return
func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) {
if id != nil {
- lists, found := x.words[id.Value]
+ lists, found := x.words[id.Name()]
if !found {
lists = new(IndexResult)
- x.words[id.Value] = lists
+ x.words[id.Name()] = lists
}
if kind == Use || x.decl == nil {
}
dir, _ := pathutil.Split(path)
- pak := Pak{dir, file.Name.Value}
+ pak := Pak{dir, file.Name.Name()}
x.file = &File{path, pak}
ast.Walk(x, file)
}
func (s *snippetStyler) Ident(id *ast.Ident) (text []byte, tag printer.HTMLTag) {
- text = strings.Bytes(id.Value)
+ text = strings.Bytes(id.Name())
if s.highlight == id {
tag = printer.HTMLTag{"<span class=highlight>", "</span>"}
}
if s == nil {
s = &Snippet{
id.Pos().Line,
- fmt.Sprintf(`could not generate a snippet for <span class="highlight">%s</span>`, id.Value),
+ fmt.Sprintf(`could not generate a snippet for <span class="highlight">%s</span>`, id.Name()),
}
}
return
rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'α[β:len(α)] -> α[β:]')")
// debugging support
+ checks = flag.Bool("checks", false, "do semantic checks")
comments = flag.Bool("comments", true, "print comments")
trace = flag.Bool("trace", false, "print parse trace")
func initParserMode() {
parserMode = uint(0)
+ if *checks {
+ parserMode |= parser.CheckSemantics
+ }
if *comments {
parserMode |= parser.ParseComments
}
// times in the pattern, it must match the same expression
// each time.
if m != nil && pattern.Type() == identType {
- name := pattern.Interface().(*ast.Ident).Value
+ name := pattern.Interface().(*ast.Ident).Name()
if isWildcard(name) {
if old, ok := m[name]; ok {
return match(nil, old, val)
// Wildcard gets replaced with map value.
if m != nil && pattern.Type() == identType {
- name := pattern.Interface().(*ast.Ident).Value
+ name := pattern.Interface().(*ast.Ident).Name()
if isWildcard(name) {
if old, ok := m[name]; ok {
return subst(nil, old, nil)
}
case *ast.Ident:
- return ei.compileIdent(a.block, a.constant, callCtx, x.Value)
+ return ei.compileIdent(a.block, a.constant, callCtx, x.Name())
case *ast.IndexExpr:
l, r := a.compile(x.X, false), a.compile(x.Index, false)
if v == nil {
return nil
}
- return ei.compileSelectorExpr(v, x.Sel.Value)
+ return ei.compileSelectorExpr(v, x.Sel.Name())
case *ast.StarExpr:
// We pass down our call context because this could be
*/
func (a *stmtCompiler) defineVar(ident *ast.Ident, t Type) *Variable {
- v, prev := a.block.DefineVar(ident.Value, ident.Pos(), t)
+ v, prev := a.block.DefineVar(ident.Name(), ident.Pos(), t)
if prev != nil {
// TODO(austin) It's silly that we have to capture
// Pos() in a variable.
pos := prev.Pos()
if pos.IsValid() {
- a.diagAt(ident, "variable %s redeclared in this block\n\tprevious declaration at %s", ident.Value, &pos)
+ a.diagAt(ident, "variable %s redeclared in this block\n\tprevious declaration at %s", ident.Name(), &pos)
} else {
- a.diagAt(ident, "variable %s redeclared in this block", ident.Value)
+ a.diagAt(ident, "variable %s redeclared in this block", ident.Name())
}
return nil
}
}
// Declare and initialize v before compiling func
// so that body can refer to itself.
- c, prev := a.block.DefineConst(d.Name.Value, a.pos, decl.Type, decl.Type.Zero())
+ c, prev := a.block.DefineConst(d.Name.Name(), a.pos, decl.Type, decl.Type.Zero())
if prev != nil {
pos := prev.Pos()
if pos.IsValid() {
- a.diagAt(d.Name, "identifier %s redeclared in this block\n\tprevious declaration at %s", d.Name.Value, &pos)
+ a.diagAt(d.Name, "identifier %s redeclared in this block\n\tprevious declaration at %s", d.Name.Name(), &pos)
} else {
- a.diagAt(d.Name, "identifier %s redeclared in this block", d.Name.Value)
+ a.diagAt(d.Name, "identifier %s redeclared in this block", d.Name.Name())
}
}
fn := a.compileFunc(a.block, decl, d.Body)
func (a *stmtCompiler) compileLabeledStmt(s *ast.LabeledStmt) {
// Define label
- l, ok := a.labels[s.Label.Value]
+ l, ok := a.labels[s.Label.Name()]
if ok {
if l.resolved.IsValid() {
- a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Value, &l.resolved)
+ a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Name(), &l.resolved)
}
} else {
pc := badPC
- l = &label{name: s.Label.Value, gotoPC: &pc}
+ l = &label{name: s.Label.Name(), gotoPC: &pc}
a.labels[l.name] = l
}
l.desc = "regular label"
}
// Is this simply an assignment?
- if _, ok := a.block.defs[ident.Value]; ok {
+ if _, ok := a.block.defs[ident.Name()]; ok {
ident = nil
break
}
if name == nil && pred(l) {
return l
}
- if name != nil && l.name == name.Value {
+ if name != nil && l.name == name.Name() {
if !pred(l) {
a.diag("cannot %s to %s %s", errOp, l.desc, l.name)
return nil
if name == nil {
a.diag("%s outside %s", errOp, errCtx)
} else {
- a.diag("%s label %s not defined", errOp, name.Value)
+ a.diag("%s label %s not defined", errOp, name.Name())
}
return nil
}
pc = l.continuePC
case token.GOTO:
- l, ok := a.labels[s.Label.Value]
+ l, ok := a.labels[s.Label.Name()]
if !ok {
pc := badPC
- l = &label{name: s.Label.Value, desc: "unresolved label", gotoPC: &pc, used: s.Pos()}
+ l = &label{name: s.Label.Name(), desc: "unresolved label", gotoPC: &pc, used: s.Pos()}
a.labels[l.name] = l
}
defer bodyScope.exit()
for i, t := range decl.Type.In {
if decl.InNames[i] != nil {
- bodyScope.DefineVar(decl.InNames[i].Value, decl.InNames[i].Pos(), t)
+ bodyScope.DefineVar(decl.InNames[i].Name(), decl.InNames[i].Pos(), t)
} else {
bodyScope.DefineTemp(t)
}
}
for i, t := range decl.Type.Out {
if decl.OutNames[i] != nil {
- bodyScope.DefineVar(decl.OutNames[i].Value, decl.OutNames[i].Pos(), t)
+ bodyScope.DefineVar(decl.OutNames[i].Name(), decl.OutNames[i].Pos(), t)
} else {
bodyScope.DefineTemp(t)
}
s += ", "
}
if ns != nil && ns[i] != nil {
- s += ns[i].Value + " "
+ s += ns[i].Name() + " "
}
if t == nil {
// Some places use nil types to represent errors
func (t *FuncDecl) String() string {
s := "func"
if t.Name != nil {
- s += " " + t.Name.Value
+ s += " " + t.Name.Name()
}
s += funcTypeString(t.Type, t.InNames, t.OutNames)
return s
}
func (a *typeCompiler) compileIdent(x *ast.Ident, allowRec bool) Type {
- _, _, def := a.block.Lookup(x.Value)
+ _, _, def := a.block.Lookup(x.Name())
if def == nil {
- a.diagAt(x, "%s: undefined", x.Value)
+ a.diagAt(x, "%s: undefined", x.Name())
return nil
}
switch def := def.(type) {
case *Constant:
- a.diagAt(x, "constant %v used as type", x.Value)
+ a.diagAt(x, "constant %v used as type", x.Name())
return nil
case *Variable:
- a.diagAt(x, "variable %v used as type", x.Value)
+ a.diagAt(x, "variable %v used as type", x.Name())
return nil
case *NamedType:
if !allowRec && def.incomplete {
case Type:
return def
}
- log.Crashf("name %s has unknown type %T", x.Value, def)
+ log.Crashf("name %s has unknown type %T", x.Name(), def)
return nil
}
// Compute field name and check anonymous fields
var name string
if names[i] != nil {
- name = names[i].Value
+ name = names[i].Name()
} else {
if ts[i] == nil {
continue
}
if names[i] != nil {
- name := names[i].Value
+ name := names[i].Name()
methods[nm].Name = name
methods[nm].Type = ts[i].(*FuncType)
nm++
for _, spec := range decl.Specs {
spec := spec.(*ast.TypeSpec)
// Create incomplete type for this type
- nt := b.DefineType(spec.Name.Value, spec.Name.Pos(), nil)
+ nt := b.DefineType(spec.Name.Name(), spec.Name.Pos(), nil)
if nt != nil {
nt.(*NamedType).incomplete = true
}
if err != nil {
return nil, err
}
- if prog.Name.Value != pkgname {
- return nil, os.NewError(fmt.Sprintf("multiple packages found: %s, %s", prog.Name.Value, pkgname))
+ if prog.Name.Name() != pkgname {
+ return nil, os.NewError(fmt.Sprintf("multiple packages found: %s, %s", prog.Name.Name(), pkgname))
}
if mode == PackageClauseOnly {
return prog, nil
}
files[entry.Name] = src
if name == "" {
- name = src.Name.Value
+ name = src.Name.Name()
}
}
}
}
-// ----------------------------------------------------------------------------
-// Scope support
-
-func openScope(p *parser) *parser {
- p.topScope = ast.NewScope(p.topScope)
- return p
-}
-
-
-// Usage pattern: defer close(openScope(p));
-func close(p *parser) { p.topScope = p.topScope.Outer }
-
-
-func (p *parser) declare(ident *ast.Ident) {
- if !p.topScope.Declare(ident) {
- p.Error(p.pos, "'"+ident.Value+"' declared already")
- }
-}
-
-
-func (p *parser) declareList(idents []*ast.Ident) {
- for _, ident := range idents {
- p.declare(ident)
- }
-}
-
-
// ----------------------------------------------------------------------------
// Common productions
func (p *parser) parseIdent() *ast.Ident {
+ obj := ast.NewObj(ast.Err, p.pos, "")
if p.tok == token.IDENT {
- x := &ast.Ident{p.pos, string(p.lit)}
+ obj.Name = string(p.lit)
p.next()
- return x
+ } else {
+ p.expect(token.IDENT) // use expect() error handling
}
- p.expect(token.IDENT) // use expect() error handling
- return &ast.Ident{p.pos, ""}
+ return &ast.Ident{obj.Pos, obj}
}
if !isIdent {
pos := list.At(i).(ast.Expr).Pos()
p.errorExpected(pos, "identifier")
- idents[i] = &ast.Ident{pos, ""}
+ idents[i] = &ast.Ident{pos, ast.NewObj(ast.Err, pos, "")}
}
idents[i] = ident
}
defer un(trace(p, "BlockStmt"))
}
- defer close(openScope(p))
-
lbrace := p.expect(token.LBRACE)
list := p.parseStmtList()
rbrace := p.expect(token.RBRACE)
defer un(trace(p, "IfStmt"))
}
- // IfStmt block
- defer close(openScope(p))
-
pos := p.expect(token.IF)
s1, s2, _ := p.parseControlClause(false)
body := p.parseBlockStmt(nil)
defer un(trace(p, "CaseClause"))
}
- // CaseClause block
- defer close(openScope(p))
-
// SwitchCase
pos := p.pos
var x []ast.Expr
defer un(trace(p, "TypeCaseClause"))
}
- // TypeCaseClause block
- defer close(openScope(p))
-
// TypeSwitchCase
pos := p.pos
var types []ast.Expr
defer un(trace(p, "SwitchStmt"))
}
- // SwitchStmt block
- defer close(openScope(p))
-
pos := p.expect(token.SWITCH)
s1, s2, _ := p.parseControlClause(false)
defer un(trace(p, "CommClause"))
}
- // CommClause block
- defer close(openScope(p))
-
// CommCase
pos := p.pos
var tok token.Token
defer un(trace(p, "ForStmt"))
}
- // ForStmt block
- defer close(openScope(p))
-
pos := p.expect(token.FOR)
s1, s2, s3 := p.parseControlClause(true)
body := p.parseBlockStmt(nil)
var ident *ast.Ident
if p.tok == token.PERIOD {
- ident = &ast.Ident{p.pos, "."}
+ ident = &ast.Ident{p.pos, ast.NewObj(ast.Err, p.pos, ".")}
p.next()
} else if p.tok == token.IDENT {
ident = p.parseIdent()
defer un(trace(p, "File"))
}
- // file block
- defer close(openScope(p))
-
// package clause
doc := p.leadComment
pos := p.expect(token.PACKAGE)
// An Ident node represents an identifier.
Ident struct {
- token.Position // identifier position
- Value string // identifier string (e.g. foobar)
+ token.Position // identifier position
+ Obj *Object // denoted object
}
// An Ellipsis node stands for the "..." type in a
// A single string literal (common case) is represented by a BasicLit
// node; StringList nodes are used only if there are two or more string
// literals in a sequence.
+ // TODO(gri) Deprecated. StringLists are only created by exp/parser;
+ // Remove when exp/parser is removed.
//
StringList struct {
Strings []*BasicLit // list of strings, len(Strings) > 1
func (x *ChanType) exprNode() {}
+// ----------------------------------------------------------------------------
+// Convenience functions for Idents
+
+// NewIdent creates a new Ident without position and minimal object
+// information. Useful for ASTs generated by code other than the Go
+// parser.
+//
+func NewIdent(name string) *Ident { return &Ident{noPos, NewObj(Err, noPos, name)} }
+
+
// IsExported returns whether name is an exported Go symbol
// (i.e., whether it begins with an uppercase letter).
func IsExported(name string) bool {
return unicode.IsUpper(ch)
}
-// IsExported returns whether name is an exported Go symbol
+
+// IsExported returns whether id is an exported Go symbol
// (i.e., whether it begins with an uppercase letter).
-func (name *Ident) IsExported() bool { return IsExported(name.Value) }
+func (id *Ident) IsExported() bool { return id.Obj.IsExported() }
+
+
+// Name returns an identifier's name.
+func (id *Ident) Name() string { return id.Obj.Name }
+
-func (name *Ident) String() string {
- if name != nil {
- return name.Value
+func (id *Ident) String() string {
+ if id != nil && id.Obj != nil {
+ return id.Obj.Name
}
return "<nil>"
}
// TODO(gri) Should collect comments as well. For that the comment
// list should be changed back into a []*CommentGroup,
// otherwise need to modify the existing linked list.
- return &File{doc, noPos, &Ident{noPos, pkg.Name}, decls, nil}
+ return &File{doc, noPos, NewIdent(pkg.Name), decls, nil}
}
package ast
-// A Scope maintains the set of identifiers visible
-// in the scope and a link to the immediately surrounding
-// (outer) scope.
+import "go/token"
+
+type ObjKind int
+
+// The list of possible Object kinds.
+const (
+ Err ObjKind = iota // object kind unknown (forward reference or error)
+ Pkg // package
+ Con // constant
+ Typ // type
+ Var // variable
+ Fun // function or method
+)
+
+
+// An Object describes a language entity such as a package,
+// constant, type, variable, or function (incl. methods).
//
-// NOTE: WORK IN PROGRESS
+type Object struct {
+ Kind ObjKind
+ Pos token.Position // declaration position
+ Name string // declared name
+ Scope *Scope // scope in which the Object is declared
+}
+
+
+func NewObj(kind ObjKind, pos token.Position, name string) *Object {
+ return &Object{kind, pos, name, nil}
+}
+
+
+// IsExported returns whether obj is exported.
+func (obj *Object) IsExported() bool { return IsExported(obj.Name) }
+
+
+// A Scope maintains the set of named language entities visible
+// in the scope and a link to the immediately surrounding (outer)
+// scope.
//
type Scope struct {
- Outer *Scope
- Names map[string]*Ident
+ Outer *Scope
+ Objects map[string]*Object
}
// NewScope creates a new scope nested in the outer scope.
-func NewScope(outer *Scope) *Scope { return &Scope{outer, make(map[string]*Ident)} }
+func NewScope(outer *Scope) *Scope { return &Scope{outer, make(map[string]*Object)} }
-// Declare inserts an identifier into the scope s. If the
-// declaration succeeds, the result is true, if the identifier
-// exists already in the scope, the result is false.
-//
-func (s *Scope) Declare(ident *Ident) bool {
- if _, found := s.Names[ident.Value]; found {
- return false
+// Declare attempts to insert a named object into the scope s.
+// If the scope does not contain an object with that name yet,
+// Declare inserts the object, and the result is true. Otherwise,
+// the scope remains unchanged and the result is false.
+func (s *Scope) Declare(obj *Object) bool {
+ if obj.Name != "_" {
+ if _, found := s.Objects[obj.Name]; found {
+ return false
+ }
+ s.Objects[obj.Name] = obj
}
- s.Names[ident.Value] = ident
return true
}
-// Lookup looks up an identifier in the current scope chain.
-// If the identifier is found, it is returned; otherwise the
-// result is nil.
+// Lookup looks up an object in the current scope chain.
+// The result is nil if the object is not found.
//
-func (s *Scope) Lookup(name string) *Ident {
+func (s *Scope) Lookup(name string) *Object {
for ; s != nil; s = s.Outer {
- if ident, found := s.Names[name]; found {
- return ident
+ if obj, found := s.Objects[name]; found {
+ return obj
}
}
return nil
}
-
-
-// TODO(gri) Uncomment once this code is needed.
-/*
-var Universe = Scope {
- Names: map[string]*Ident {
- // basic types
- "bool": nil,
- "byte": nil,
- "int8": nil,
- "int16": nil,
- "int32": nil,
- "int64": nil,
- "uint8": nil,
- "uint16": nil,
- "uint32": nil,
- "uint64": nil,
- "float32": nil,
- "float64": nil,
- "string": nil,
-
- // convenience types
- "int": nil,
- "uint": nil,
- "uintptr": nil,
- "float": nil,
-
- // constants
- "false": nil,
- "true": nil,
- "iota": nil,
- "nil": nil,
-
- // functions
- "cap": nil,
- "len": nil,
- "new": nil,
- "make": nil,
- "panic": nil,
- "panicln": nil,
- "print": nil,
- "println": nil,
- }
-}
-*/
\ No newline at end of file
func (doc *docReader) addType(decl *ast.GenDecl) {
spec := decl.Specs[0].(*ast.TypeSpec)
- typ := doc.lookupTypeDoc(spec.Name.Value)
+ typ := doc.lookupTypeDoc(spec.Name.Name())
// typ should always be != nil since declared types
// are always named - be conservative and check
if typ != nil {
// if the type is not exported, the effect to
// a client is as if there were no type name
if t.IsExported() {
- return string(t.Value)
+ return string(t.Name())
}
case *ast.StarExpr:
return baseTypeName(t.X)
func (doc *docReader) addFunc(fun *ast.FuncDecl) {
- name := fun.Name.Value
+ name := fun.Name.Name()
// determine if it should be associated with a type
if fun.Recv != nil {
func NewFileDoc(file *ast.File) *PackageDoc {
var r docReader
- r.init(file.Name.Value)
+ r.init(file.Name.Name())
r.addFile(file)
return r.newDoc("", "", nil)
}
switch v := d.Specs[0].(type) {
case *ast.ValueSpec:
- return v.Names[0].Value
+ return v.Names[0].Name()
case *ast.TypeSpec:
- return v.Name.Value
+ return v.Name.Name()
}
return ""
if f.Recv != nil {
doc.Recv = f.Recv.Type
}
- doc.Name = f.Name.Value
+ doc.Name = f.Name.Name()
doc.Decl = f
d[i] = doc
i++
// sort by name
// pull blocks (name = "") up to top
// in original order
- if ni, nj := p[i].Type.Name.Value, p[j].Type.Name.Value; ni != nj {
+ if ni, nj := p[i].Type.Name.Name(), p[j].Type.Name.Name(); ni != nj {
return ni < nj
}
return p[i].order < p[j].order
switch v := d.(type) {
case *ast.ValueSpec:
for _, name := range v.Names {
- if match(name.Value, names) {
+ if match(name.Name(), names) {
return true
}
}
case *ast.TypeSpec:
- if match(v.Name.Value, names) {
+ if match(v.Name.Name(), names) {
return true
}
}
if err != nil {
return pkgs, err
}
- name := src.Name.Value
+ name := src.Name.Name()
pkg, found := pkgs[name]
if !found {
pkg = &ast.Package{name, path, make(map[string]*ast.File)}
PackageClauseOnly uint = 1 << iota // parsing stops after package clause
ImportsOnly // parsing stops after import declarations
ParseComments // parse comments and add them to AST
+ CheckSemantics // do semantic checks (only declarations for now)
Trace // print a trace of parsed productions
)
// Tracing/debugging
mode uint // parsing mode
+ check bool // == (mode & CheckSemantics != 0)
trace bool // == (mode & Trace != 0)
indent uint // indentation used for tracing output
func (p *parser) init(filename string, src []byte, mode uint) {
p.scanner.Init(filename, src, p, scannerMode(mode))
p.mode = mode
- p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
+ p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
+ p.check = mode&CheckSemantics != 0 // for convenience (p.check is used frequently)
p.next()
}
// ----------------------------------------------------------------------------
// Scope support
+// Usage pattern: defer closeScope(openScope(p));
func openScope(p *parser) *parser {
p.topScope = ast.NewScope(p.topScope)
return p
}
-// Usage pattern: defer close(openScope(p));
-func close(p *parser) { p.topScope = p.topScope.Outer }
+func closeScope(p *parser) { p.topScope = p.topScope.Outer }
-func (p *parser) declare(ident *ast.Ident) {
- if !p.topScope.Declare(ident) {
- p.Error(p.pos, "'"+ident.Value+"' declared already")
- }
-}
-
-
-func (p *parser) declareList(idents []*ast.Ident) {
- for _, ident := range idents {
- p.declare(ident)
+func (p *parser) parseIdent(kind ast.ObjKind) *ast.Ident {
+ obj := ast.NewObj(ast.Err, p.pos, "")
+ if p.tok == token.IDENT {
+ obj.Name = string(p.lit)
+ p.next()
+ } else {
+ p.expect(token.IDENT) // use expect() error handling
}
+ return &ast.Ident{obj.Pos, obj}
}
-// ----------------------------------------------------------------------------
-// Common productions
-
-func (p *parser) parseIdent() *ast.Ident {
+// TODO(gri) Separate parsing from declaration since an identifier's
+// scope often starts only after the type has been seen.
+func (p *parser) declIdent(kind ast.ObjKind) *ast.Ident {
+ obj := ast.NewObj(kind, p.pos, "")
if p.tok == token.IDENT {
- x := &ast.Ident{p.pos, string(p.lit)}
+ obj.Name = string(p.lit)
+ // TODO(gri) Consider reversing the conditionals below:
+ // always do the declaration but only report
+ // error if enabled (may be necessary to get
+ // search functionality in the presence of
+ // incorrect files).
+ if p.check && !p.topScope.Declare(obj) {
+ // TODO(gri) Declare could return already-declared
+ // object for a very good error message.
+ p.Error(obj.Pos, "'"+obj.Name+"' declared already")
+ }
p.next()
- return x
+ } else {
+ p.expect(token.IDENT) // use expect() error handling
}
- p.expect(token.IDENT) // use expect() error handling
- return &ast.Ident{p.pos, ""}
+ return &ast.Ident{obj.Pos, obj}
}
-func (p *parser) parseIdentList() []*ast.Ident {
+// TODO(gri) Separate parsing from declaration since an identifier's
+// scope often starts only after the type has been seen.
+func (p *parser) declIdentList(kind ast.ObjKind) []*ast.Ident {
if p.trace {
defer un(trace(p, "IdentList"))
}
var list vector.Vector
- list.Push(p.parseIdent())
+ list.Push(p.declIdent(kind))
for p.tok == token.COMMA {
p.next()
- list.Push(p.parseIdent())
+ list.Push(p.declIdent(kind))
}
// convert vector
}
+func (p *parser) findIdent() *ast.Ident {
+ pos := p.pos
+ name := ""
+ var obj *ast.Object
+ if p.tok == token.IDENT {
+ name = string(p.lit)
+ obj = p.topScope.Lookup(name)
+ p.next()
+ } else {
+ p.expect(token.IDENT) // use expect() error handling
+ }
+ if obj == nil {
+ obj = ast.NewObj(ast.Err, pos, name)
+ }
+ return &ast.Ident{obj.Pos, obj}
+}
+
+
+// ----------------------------------------------------------------------------
+// Common productions
+
func makeExprList(list *vector.Vector) []ast.Expr {
exprs := make([]ast.Expr, len(*list))
for i, x := range *list {
defer un(trace(p, "QualifiedIdent"))
}
- var x ast.Expr = p.parseIdent()
+ var x ast.Expr = p.findIdent()
if p.tok == token.PERIOD {
// first identifier is a package identifier
p.next()
- sel := p.parseIdent()
+ sel := p.findIdent()
x = &ast.SelectorExpr{x, sel}
}
return x
if !isIdent {
pos := x.(ast.Expr).Pos()
p.errorExpected(pos, "identifier")
- idents[i] = &ast.Ident{pos, ""}
+ idents[i] = &ast.Ident{pos, ast.NewObj(ast.Err, pos, "")}
}
idents[i] = ident
}
}
for p.tok != token.RPAREN && p.tok != token.EOF {
- idents := p.parseIdentList()
+ idents := p.declIdentList(ast.Var)
typ := p.parseParameterType(ellipsisOk)
list.Push(&ast.Field{nil, idents, typ, nil, nil})
if p.tok != token.COMMA {
var params []*ast.Field
p.expect(token.LPAREN)
+ openScope(p)
if p.tok != token.RPAREN {
params = p.parseParameterList(ellipsisOk)
}
+ closeScope(p)
p.expect(token.RPAREN)
return params
defer un(trace(p, "BlockStmt"))
}
- defer close(openScope(p))
+ defer closeScope(openScope(p))
lbrace := p.expect(token.LBRACE)
list := p.parseStmtList()
switch p.tok {
case token.IDENT:
- return p.parseIdent()
+ return p.findIdent()
case token.INT, token.FLOAT, token.CHAR, token.STRING:
x := &ast.BasicLit{p.pos, p.tok, p.lit}
p.expect(token.PERIOD)
if p.tok == token.IDENT {
// selector
- sel := p.parseIdent()
+ sel := p.findIdent()
return &ast.SelectorExpr{x, sel}
}
s := &ast.BranchStmt{p.pos, tok, nil}
p.expect(tok)
if tok != token.FALLTHROUGH && p.tok == token.IDENT {
- s.Label = p.parseIdent()
+ s.Label = p.findIdent()
}
p.expectSemi()
}
// IfStmt block
- defer close(openScope(p))
+ defer closeScope(openScope(p))
pos := p.expect(token.IF)
s1, s2, _ := p.parseControlClause(false)
}
// CaseClause block
- defer close(openScope(p))
+ defer closeScope(openScope(p))
// SwitchCase
pos := p.pos
}
// TypeCaseClause block
- defer close(openScope(p))
+ defer closeScope(openScope(p))
// TypeSwitchCase
pos := p.pos
}
// SwitchStmt block
- defer close(openScope(p))
+ defer closeScope(openScope(p))
pos := p.expect(token.SWITCH)
s1, s2, _ := p.parseControlClause(false)
}
// CommClause block
- defer close(openScope(p))
+ defer closeScope(openScope(p))
// CommCase
pos := p.pos
}
// ForStmt block
- defer close(openScope(p))
+ defer closeScope(openScope(p))
pos := p.expect(token.FOR)
s1, s2, s3 := p.parseControlClause(true)
var ident *ast.Ident
if p.tok == token.PERIOD {
- ident = &ast.Ident{p.pos, "."}
+ ident = &ast.Ident{p.pos, ast.NewObj(ast.Pkg, p.pos, ".")}
p.next()
} else if p.tok == token.IDENT {
- ident = p.parseIdent()
+ ident = p.declIdent(ast.Pkg)
}
var path []*ast.BasicLit
defer un(trace(p, "ConstSpec"))
}
- idents := p.parseIdentList()
+ idents := p.declIdentList(ast.Con)
typ := p.tryType()
var values []ast.Expr
if typ != nil || p.tok == token.ASSIGN {
defer un(trace(p, "TypeSpec"))
}
- ident := p.parseIdent()
+ ident := p.declIdent(ast.Typ)
typ := p.parseType()
p.expectSemi()
defer un(trace(p, "VarSpec"))
}
- idents := p.parseIdentList()
+ idents := p.declIdentList(ast.Var)
typ := p.tryType()
var values []ast.Expr
if typ == nil || p.tok == token.ASSIGN {
recv = p.parseReceiver()
}
- ident := p.parseIdent()
+ ident := p.declIdent(ast.Fun)
params, results := p.parseSignature()
var body *ast.BlockStmt
defer un(trace(p, "File"))
}
- // file block
- defer close(openScope(p))
-
// package clause
doc := p.leadComment
pos := p.expect(token.PACKAGE)
- ident := p.parseIdent()
+ ident := p.parseIdent(ast.Pkg) // package name is in no scope
p.expectSemi()
+ // file block
+ defer closeScope(openScope(p))
+
var decls []ast.Decl
// Don't bother parsing the rest if we had errors already.
if i > 0 {
size += 2 // ", "
}
- size += len(x.Value)
+ size += len(x.Name())
if size >= maxSize {
break
}
if p.Styler != nil {
data, tag = p.Styler.Ident(x)
} else {
- data = strings.Bytes(x.Value)
+ data = strings.Bytes(x.Name())
}
case *ast.BasicLit:
if p.Styler != nil {