code: func ParseModule(filename, body string) (*Module, error) {
  	ast, err := syntax.Parse(filename, body, syntax.RetainComments)
  	if err != nil {
  		return nil, err
  	}
  
  	m := &Module{docstring: extractDocstring(ast.Stmts)}
  	m.populateFromAST(filename, ast)
  
  	// emit adds a node to the module.
  	emit := func(name string, ast syntax.Node, n Node) {
  		n.populateFromAST(name, ast)
  		m.Nodes = append(m.Nodes, n)
  	}
  
  	// Walk over top-level statements and match them against patterns we recognize
  	// as relevant.
  	for _, stmt := range ast.Stmts {
  		switch st := stmt.(type) {
  		case *syntax.LoadStmt:
  			// A load(...) statement. Each imported symbol ends up in the module's
  			// namespace, so add corresponding ExternalReference nodes.
  			for i, nm := range st.To {
  				emit(nm.Name, st, &ExternalReference{
  					ExternalName: st.From[i].Name,
  					Module:       st.Module.Value.(string),
  				})
  			}
  
  		case *syntax.DefStmt:
  			// A function declaration: "def name(...)".
  			emit(st.Name.Name, st, &Function{
  				docstring: extractDocstring(st.Body),
  			})
  
  		case *syntax.AssignStmt:
  			// A top level assignment. We care only about <var> = ... (i.e. when LHS
  			// is a simple variable, not a tuple or anything like that).
  			if st.Op != syntax.EQ {
  				continue
  			}
  			lhs := matchSingleIdent(st.LHS)
  			if lhs == "" {
  				continue
  			}
  			if n := parseAssignmentRHS(st.RHS); n != nil {
  				emit(lhs, st, n)
  			}
  		}
  	}
  
  	return m, nil
  }
docs: // ParseModule parses a single Starlark module.
  //
  // Filename is only used when recording position information.