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 }