github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/eval/compiler.go (about)

     1  package eval
     2  
     3  //go:generate ./boilerplate.py
     4  
     5  import (
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/u-root/u-root/cmds/core/elvish/parse"
    10  	"github.com/u-root/u-root/cmds/core/elvish/util"
    11  )
    12  
    13  // compiler maintains the set of states needed when compiling a single source
    14  // file.
    15  type compiler struct {
    16  	// Builtin namespace.
    17  	builtin staticNs
    18  	// Lexical namespaces.
    19  	scopes []staticNs
    20  	// Variables captured from outer scopes.
    21  	capture staticNs
    22  	// Position of what is being compiled.
    23  	begin, end int
    24  	// Information about the source.
    25  	srcMeta *Source
    26  }
    27  
    28  func compile(b, g staticNs, n *parse.Chunk, src *Source) (op Op, err error) {
    29  	cp := &compiler{b, []staticNs{g}, make(staticNs), 0, 0, src}
    30  	defer util.Catch(&err)
    31  	return cp.chunkOp(n), nil
    32  }
    33  
    34  func (cp *compiler) compiling(n parse.Node) {
    35  	cp.begin, cp.end = n.Begin(), n.End()
    36  }
    37  
    38  func (cp *compiler) errorpf(begin, end int, format string, args ...interface{}) {
    39  	throw(&CompilationError{fmt.Sprintf(format, args...),
    40  		*util.NewSourceRange(cp.srcMeta.describePath(), cp.srcMeta.code, begin, end)})
    41  }
    42  
    43  func (cp *compiler) errorf(format string, args ...interface{}) {
    44  	cp.errorpf(cp.begin, cp.end, format, args...)
    45  }
    46  
    47  func (cp *compiler) thisScope() staticNs {
    48  	return cp.scopes[len(cp.scopes)-1]
    49  }
    50  
    51  func (cp *compiler) pushScope() staticNs {
    52  	sc := make(staticNs)
    53  	cp.scopes = append(cp.scopes, sc)
    54  	return sc
    55  }
    56  
    57  func (cp *compiler) popScope() {
    58  	cp.scopes[len(cp.scopes)-1] = make(staticNs)
    59  	cp.scopes = cp.scopes[:len(cp.scopes)-1]
    60  }
    61  
    62  func (cp *compiler) registerVariableGetQname(qname string) bool {
    63  	_, ns, name := ParseVariableRef(qname)
    64  	return cp.registerVariableGet(ns, name)
    65  }
    66  
    67  func (cp *compiler) registerVariableGet(ns, name string) bool {
    68  	switch ns {
    69  	case "", "local", "up":
    70  		// Handled below
    71  	case "e", "E":
    72  		return true
    73  	default:
    74  		return cp.registerModAccess(ns)
    75  	}
    76  	// Find in local scope
    77  	if ns == "" || ns == "local" {
    78  		if cp.thisScope().has(name) {
    79  			return true
    80  		}
    81  	}
    82  	// Find in upper scopes
    83  	if ns == "" || ns == "up" {
    84  		for i := len(cp.scopes) - 2; i >= 0; i-- {
    85  			if cp.scopes[i].has(name) {
    86  				// Existing name: record capture and return.
    87  				cp.capture.set(name)
    88  				return true
    89  			}
    90  		}
    91  	}
    92  	// Find in builtin scope
    93  	if ns == "" || ns == "builtin" {
    94  		if cp.builtin.has(name) {
    95  			return true
    96  		}
    97  	}
    98  	return false
    99  }
   100  
   101  func (cp *compiler) registerVariableSetQname(qname string) bool {
   102  	_, ns, name := ParseVariableRef(qname)
   103  	return cp.registerVariableSet(ns, name)
   104  }
   105  
   106  func (cp *compiler) registerVariableSet(ns, name string) bool {
   107  	switch ns {
   108  	case "local":
   109  		cp.thisScope().set(name)
   110  		return true
   111  	case "up":
   112  		for i := len(cp.scopes) - 2; i >= 0; i-- {
   113  			if cp.scopes[i].has(name) {
   114  				// Existing name: record capture and return.
   115  				cp.capture.set(name)
   116  				return true
   117  			}
   118  		}
   119  		return false
   120  	case "builtin":
   121  		cp.errorf("cannot set builtin variable")
   122  		return false
   123  	case "":
   124  		if cp.thisScope().has(name) {
   125  			// A name on current scope. Do nothing.
   126  			return true
   127  		}
   128  		// Walk up the upper scopes
   129  		for i := len(cp.scopes) - 2; i >= 0; i-- {
   130  			if cp.scopes[i].has(name) {
   131  				// Existing name. Do nothing
   132  				cp.capture.set(name)
   133  				return true
   134  			}
   135  		}
   136  		// New name. Register on this scope!
   137  		cp.thisScope().set(name)
   138  		return true
   139  	case "e", "E":
   140  		// Special namespaces, do nothing
   141  		return true
   142  	default:
   143  		return cp.registerModAccess(ns)
   144  	}
   145  }
   146  
   147  func (cp *compiler) registerModAccess(name string) bool {
   148  	if strings.ContainsRune(name, ':') {
   149  		name = name[:strings.IndexByte(name, ':')]
   150  	}
   151  	return cp.registerVariableGet("", name+NsSuffix)
   152  }