github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/compile_value_test.go (about) 1 package eval_test 2 3 import ( 4 "testing" 5 6 . "src.elv.sh/pkg/eval" 7 "src.elv.sh/pkg/eval/errs" 8 "src.elv.sh/pkg/testutil" 9 10 . "src.elv.sh/pkg/eval/evaltest" 11 "src.elv.sh/pkg/eval/vals" 12 ) 13 14 func TestCompound(t *testing.T) { 15 Test(t, 16 That("put {fi,elvi}sh{1.0,1.1}").Puts( 17 "fish1.0", "fish1.1", "elvish1.0", "elvish1.1"), 18 19 // As a special case, an empty compound expression evaluates to an empty 20 // string. 21 That("put {}").Puts(""), 22 That("put [&k=][k]").Puts(""), 23 24 // TODO: Test the case where fsutil.GetHome returns an error. 25 26 // Error in any of the components throws an exception. 27 That("put a{[][1]}").Throws(ErrorWithType(errs.OutOfRange{}), "[][1]"), 28 // Error in concatenating the values throws an exception. 29 That("put []a").Throws(ErrorWithMessage("cannot concatenate list and string")), 30 // Error when applying tilde throws an exception. 31 That("put ~[]").Throws(ErrorWithMessage("tilde doesn't work on value of type list")), 32 ) 33 } 34 35 func TestIndexing(t *testing.T) { 36 Test(t, 37 That("put [a b c][2]").Puts("c"), 38 That("put [][0]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 39 That("put [&key=value][key]").Puts("value"), 40 That("put [&key=value][bad]").Throws( 41 vals.NoSuchKey("bad"), "[&key=value][bad]"), 42 ) 43 } 44 45 func TestListLiteral(t *testing.T) { 46 Test(t, 47 That("put [a b c]").Puts(vals.MakeList("a", "b", "c")), 48 That("put []").Puts(vals.EmptyList), 49 // List expression errors if an element expression errors. 50 That("put [ [][0] ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 51 ) 52 } 53 54 func TestMapLiteral(t *testing.T) { 55 Test(t, 56 That("put [&key=value]").Puts(vals.MakeMap("key", "value")), 57 That("put [&]").Puts(vals.EmptyMap), 58 // Map keys and values may evaluate to multiple values as long as their 59 // numbers match. 60 That("put [&{a b}={foo bar}]").Puts(vals.MakeMap("a", "foo", "b", "bar")), 61 // Map expression errors if a key or value expression errors. 62 That("put [ &[][0]=a ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 63 That("put [ &a=[][0] ]").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 64 // Map expression errors if number of keys and values in a single pair 65 // does not match. 66 That("put [&{a b}={foo bar lorem}]").Throws(ErrorWithMessage("2 keys but 3 values")), 67 ) 68 } 69 70 func TestStringLiteral(t *testing.T) { 71 Test(t, 72 That(`put 'such \"''literal'`).Puts(`such \"'literal`), 73 That(`put "much \n\033[31;1m$cool\033[m"`). 74 Puts("much \n\033[31;1m$cool\033[m"), 75 ) 76 } 77 78 func TestTilde(t *testing.T) { 79 home, cleanup := testutil.InTempHome() 80 testutil.MustCreateEmpty("file1") 81 testutil.MustCreateEmpty("file2") 82 defer cleanup() 83 84 Test(t, 85 // Tilde 86 // ----- 87 That("put ~").Puts(home), 88 That("put ~/src").Puts(home+"/src"), 89 // Make sure that tilde processing retains trailing slashes. 90 That("put ~/src/").Puts(home+"/src/"), 91 // Tilde and wildcard. 92 That("put ~/*").Puts(home+"/file1", home+"/file2"), 93 // TODO: Add regression test for #793. 94 // TODO: Add regression test for #1246. 95 ) 96 } 97 98 func TestOutputCapture(t *testing.T) { 99 Test(t, 100 // Output capture 101 That("put (put lorem ipsum)").Puts("lorem", "ipsum"), 102 That("put (print \"lorem\nipsum\")").Puts("lorem", "ipsum"), 103 // \r\n is also supported as a line separator 104 That(`print "lorem\r\nipsum\r\n" | all`).Puts("lorem", "ipsum"), 105 ) 106 } 107 108 func TestExceptionCapture(t *testing.T) { 109 Test(t, 110 // Exception capture 111 That("bool ?(nop); bool ?(e:false)").Puts(true, false), 112 ) 113 } 114 115 func TestVariableUse(t *testing.T) { 116 Test(t, 117 That("x = foo", "put $x").Puts("foo"), 118 // Must exist before use 119 That("put $x").DoesNotCompile(), 120 That("put $x[0]").DoesNotCompile(), 121 // Compounding 122 That("x = SHELL", "put 'WOW, SUCH '$x', MUCH COOL'\n"). 123 Puts("WOW, SUCH SHELL, MUCH COOL"), 124 // Splicing 125 That("x = [elvish rules]", "put $@x").Puts("elvish", "rules"), 126 127 // Variable namespace 128 // ------------------ 129 130 // Pseudo-namespace local: accesses the local scope. 131 That("x = outer; { local:x = inner; put $local:x }").Puts("inner"), 132 // Pseudo-namespace up: accesses upvalues. 133 That("x = outer; { local:x = inner; put $up:x }").Puts("outer"), 134 // Unqualified name prefers local: to up:. 135 That("x = outer; { local:x = inner; put $x }").Puts("inner"), 136 // Unqualified name resolves to upvalue if no local name exists. 137 That("x = outer; { put $x }").Puts("outer"), 138 // Unqualified name resolves to builtin if no local name or upvalue 139 // exists. 140 That("put $true").Puts(true), 141 // A name can be explicitly unqualified by having a leading colon. 142 That("x = val; put $:x").Puts("val"), 143 That("put $:true").Puts(true), 144 145 // Pseudo-namespace E: provides read-write access to environment 146 // variables. Colons inside the name are supported. 147 That("set-env a:b VAL; put $E:a:b").Puts("VAL"), 148 That("E:a:b = VAL2; get-env a:b").Puts("VAL2"), 149 150 // Pseudo-namespace e: provides readonly access to external commands. 151 // Only names ending in ~ are resolved, and resolution always succeeds 152 // regardless of whether the command actually exists. Colons inside the 153 // name are supported. 154 That("put $e:a:b~").Puts(NewExternalCmd("a:b")), 155 156 // A "normal" namespace access indexes the namespace as a variable. 157 That("ns: = (ns [&a= val]); put $ns:a").Puts("val"), 158 // Multi-level namespace access is supported. 159 That("ns: = (ns [&a:= (ns [&b= val])]); put $ns:a:b").Puts("val"), 160 // Multi-level namespace access can have a leading colon to signal that 161 // the first component is unqualified. 162 That("ns: = (ns [&a:= (ns [&b= val])]); put $:ns:a:b").Puts("val"), 163 // Multi-level namespace access can be combined with the local: 164 // pseudo-namespaces. 165 That("ns: = (ns [&a:= (ns [&b= val])]); put $local:ns:a:b").Puts("val"), 166 // Multi-level namespace access can be combined with the up: 167 // pseudo-namespaces. 168 That("ns: = (ns [&a:= (ns [&b= val])]); { put $up:ns:a:b }").Puts("val"), 169 ) 170 } 171 172 func TestClosure(t *testing.T) { 173 Test(t, 174 That("[]{ }").DoesNothing(), 175 That("[x]{put $x} foo").Puts("foo"), 176 177 // Assigning to captured variable 178 That("var x = lorem; []{set x = ipsum}; put $x").Puts("ipsum"), 179 That("var x = lorem; []{ put $x; set x = ipsum }; put $x"). 180 Puts("lorem", "ipsum"), 181 182 // Assigning to element of captured variable 183 That("x = a; { x = b }; put $x").Puts("b"), 184 That("x = [a]; { x[0] = b }; put $x[0]").Puts("b"), 185 186 // Shadowing 187 That("var x = ipsum; []{ var x = lorem; put $x }; put $x"). 188 Puts("lorem", "ipsum"), 189 190 // Shadowing by argument 191 That("var x = ipsum; [x]{ put $x; set x = BAD } lorem; put $x"). 192 Puts("lorem", "ipsum"), 193 194 // Closure captures new local variables every time 195 That("fn f []{ var x = (num 0); put { set x = (+ $x 1) } { put $x } }", 196 "var inc1 put1 = (f); $put1; $inc1; $put1", 197 "var inc2 put2 = (f); $put2; $inc2; $put2").Puts(0, 1, 0, 1), 198 199 // Rest argument. 200 That("[x @xs]{ put $x $xs } a b c").Puts("a", vals.MakeList("b", "c")), 201 That("[a @b c]{ put $a $b $c } a b c d"). 202 Puts("a", vals.MakeList("b", "c"), "d"), 203 // Options. 204 That("[a &k=v]{ put $a $k } foo &k=bar").Puts("foo", "bar"), 205 // Option default value. 206 That("[a &k=v]{ put $a $k } foo").Puts("foo", "v"), 207 208 // Argument name must be unqualified. 209 That("[a:b]{ }").DoesNotCompile(), 210 // Argument name must not be empty. 211 That("['']{ }").DoesNotCompile(), 212 That("[@]{ }").DoesNotCompile(), 213 // Option name must be unqualified. 214 That("[&a:b=1]{ }").DoesNotCompile(), 215 // Option name must not be empty. 216 That("[&''=b]{ }").DoesNotCompile(), 217 218 // Exception when evaluating option default value. 219 That("[&a=[][0]]{ }").Throws(ErrorWithType(errs.OutOfRange{}), "[][0]"), 220 // Option default value must be one value. 221 That("[&a=(put foo bar)]{ }").Throws( 222 errs.ArityMismatch{ 223 What: "option default value", ValidLow: 1, ValidHigh: 1, Actual: 2}, 224 "(put foo bar)"), 225 ) 226 }