diff --git a/README.md b/README.md index 82ef402..1cd1c19 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,12 @@ t = true f = false // Function literals -let f = add(x, y) { return a + b } -println(f(1,2)) +let f1 = add(x, y) { return x + y } +println(f1(1,2)) + +//short-arrow function literals +let f2 = (x, y) -> x + y +println(f2(1,2)) ``` ### Variables @@ -951,28 +955,56 @@ println(hash) ## Comprehensions -Monkey also support list(array,string, range, tuple) or map comprehensions, please see following examples: +Monkey support list(array,string, range, tuple) comprehensions. +list comprehension will return an array. +please see following examples: ```swift //array comprehension x = [[word.upper(), word.lower(), word.title()] for word in ["hello", "world", "good", "morning"]] -println(x) +println(x) //result: [["HELLO", "hello", "Hello"], ["WORLD", "world", "World"], ["GOOD", "good", "Good"], ["MORNING", "morning", "Morning"]] //string comprehension (here string is treated like an array) y = [ c.upper() for c in "huanghaifeng" where $_ % 2 != 0] //$_ is the index -println(y) +println(y) //result: ["U", "N", "H", "I", "E", "G"] //range comprehension w = [i + 1 for i in 2..10] -println(w) +println(w) //result: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] //tuple comprehension v = [x+1 for x in (12,34,56)] -println(v) +println(v) //result: [13, 35, 57] //hash comprehension z = [v * 10 for k,v in {"key1"=>10, "key2"=>20, "key3"=>30}] -println(z) +println(z) //result: [100, 200, 300] +``` + +Monkey also support hash comprehension. +hash comprehension will return a hash. +please see following examples: + +```swift +//hash comprehension (from hash) +z1 = { v:k for k,v in {"key1"=>10, "key2"=>20, "key3"=>30}} //reverse key-value pair +println(z1) // result: {10 => "key1", 20 => "key2", 30 => "key3"}. Order may differ + +//hash comprehension (from array) +z2 = {x:x**2 for x in [1,2,3]} +println(z2) // result: {1 => 1, 2 => 4, 3 => 9}. Order may differ + +//hash comprehension (from .. range) +z3 = {x:x**2 for x in 5..7} +println(z3) // result: {5 => 25, 6 => 36, 7 => 49}. Order may differ + +//hash comprehension (from string) +z4 = {x:x.upper() for x in "hi"} +println(z4) // result: {"h" => "H", "i" => "I"}. Order may differ + +//hash comprehension (from tuple) +z5 = {x+1:x+2 for x in (1,2,3)} +println(z5) // result: {4 => 5, 2 => 3, 3 => 4}. Order may differ ``` ## grep and map diff --git a/README_cn.md b/README_cn.md index 79e3948..8224a2c 100644 --- a/README_cn.md +++ b/README_cn.md @@ -149,8 +149,12 @@ t = true f = false // Function literals -let f = add(x, y) { return a + b } -println(f(1,2)) +let f1 = add(x, y) { return x + y } +println(f1(1,2)) + +//short-arrow function literals +let f2 = (x, y) -> x + y +println(f2(1,2)) ``` ### 变量 @@ -930,28 +934,55 @@ println(hash) ## 列表推导(Comprehensions) -Monkey也支持列表推导(列表可以为数组,字符串,Range,Tuple, 哈希)。请看下面的例子: +Monkey支持列表推导(列表可以为数组,字符串,Range,Tuple, 哈希)。 +列表推导的返回值均为数组。请看下面的例子: ```swift //数组 x = [[word.upper(), word.lower(), word.title()] for word in ["hello", "world", "good", "morning"]] -println(x) +println(x) //结果:[["HELLO", "hello", "Hello"], ["WORLD", "world", "World"], ["GOOD", "good", "Good"], ["MORNING", "morning", "Morning"]] + //字符串 y = [ c.upper() for c in "huanghaifeng" where $_ % 2 != 0] //$_ is the index -println(y) +println(y) //结果:["U", "N", "H", "I", "E", "G"] //范围 w = [i + 1 for i in 1..10] -println(w) +println(w) //结果:[2, 3, 4, 5, 6, 7, 8, 9, 10, 11] //tuple v = [x+1 for x in (12,34,56)] -println(v) +println(v) //结果:[13, 35, 57] //哈希 z = [v * 10 for k,v in {"key1"=>10, "key2"=>20, "key3"=>30}] -println(z) +println(z) //结果:[100, 200, 300] +``` + +Monkey同时也支持哈希推导。 +哈希推导的返回值均为哈希。请看下面的例子: + +```swift +//哈希推导 (from hash) +z1 = { v:k for k,v in {"key1"=>10, "key2"=>20, "key3"=>30}} //reverse key-value pair +println(z1) // 结果: {10 => "key1", 20 => "key2", 30 => "key3"}, 顺序可能不同 + +//哈希推导 (from array) +z2 = {x:x**2 for x in [1,2,3]} +println(z2) // 结果: {1 => 1, 2 => 4, 3 => 9}, 顺序可能不同 + +//哈希推导 (from .. range) +z3 = {x:x**2 for x in 5..7} +println(z3) // 结果: {5 => 25, 6 => 36, 7 => 49}, 顺序可能不同 + +//哈希推导 (from string) +z4 = {x:x.upper() for x in "hi"} +println(z4) // 结果: {"h" => "H", "i" => "I"}, 顺序可能不同 + +//哈希推导 (from tuple) +z5 = {x+1:x+2 for x in (1,2,3)} +println(z5) // 结果: {4 => 5, 2 => 3, 3 => 4}, 顺序可能不同 ``` ## Grep和map diff --git a/examples/comprehension.my b/examples/comprehension.my new file mode 100644 index 0000000..dd77cb0 --- /dev/null +++ b/examples/comprehension.my @@ -0,0 +1,41 @@ +//hash comprehension (from hash) +z1 = { v:k for k,v in {"key1"=>10, "key2"=>20, "key3"=>30}} //reverse key-value pair +println(z1) + +//hash comprehension (from array) +z2 = {x:x**2 for x in [1,2,3]} +println(z2) + +//hash comprehension (from .. range) +z3 = {x:x**2 for x in 5..7} +println(z3) + +//hash comprehension (from string) +z4 = {x:x.upper() for x in "hi"} +println(z4) + +//hash comprehension (from tuple) +z5 = {x+1:x+2 for x in (1,2,3)} +println(z5) + + +println("==================================") +// +x = [[word.upper(), word.lower(), word.title()] for word in ["hello", "world", "good", "morning"]] +println(x) + +//ַ +y = [ c.upper() for c in "huanghaifeng" where $_ % 2 != 0] //$_ is the index +println(y) + +//Χ +w = [i + 1 for i in 1..10] +println(w) + +//tuple +v = [x+1 for x in (12,34,56)] +println(v) + +//ϣ +z = [v * 10 for k,v in {"key1"=>10, "key2"=>20, "key3"=>30}] +println(z) \ No newline at end of file diff --git a/examples/demo.my b/examples/demo.my index e6de090..ae10d76 100644 --- a/examples/demo.my +++ b/examples/demo.my @@ -238,7 +238,7 @@ aChan1.send("Hello Channel!") # spawn and 'chan' example 2 let aa = [7, 2, 8, -9, 4, 0] -let nn = len(aa) / 2 +let nn = int(len(aa) / 2) //because '/' will always return float, so here we need to convert the result to int let aChan2 = chan() let cc = fn(aArr, c) { diff --git a/examples/linq.my b/examples/linq.my index d35b0e3..e130a75 100644 --- a/examples/linq.my +++ b/examples/linq.my @@ -763,9 +763,9 @@ if result == 0 { //test 'sequenceEqual' println() -let se1=[1, 2, 2, 3, 1], se11=[4,6] -let se2=[1, -1, 100], se22=[1, -1, 100] -let se3=[], se33=[] +let se1,se11=[1, 2, 2, 3, 1], [4,6] +let se2,se22=[1, -1, 100], [1, -1, 100] +let se3,se33=[], [] result = linq.from(se1).sequenceEqual(linq.from(se11)) if result { println("[1, 2, 2, 3, 1] sequenceEqual([4,6]) returns true") @@ -879,11 +879,11 @@ println('[1,2,3,4,5,6,7,8,9,10] sumInts()={result}') //test 'sumFloats' println() -let farr1 = [1., 2., 2., 3., 1.] +let farr1 = [1.0, 2.0, 2.0, 3.0, 1.0] result = linq.from(farr1).sumFloats() -println("[1., 2., 2., 3., 1.] sumFloats() returns", result) +println("[1.0, 2.0, 2.0, 3.0, 1.0] sumFloats() returns", result) -let farr2 = [1.] +let farr2 = [1.0] result = linq.from(farr2).sumFloats() println("[1.] sumFloats() returns", result) diff --git a/examples/test_program.my b/examples/test_program.my index 8646bad..45d9f5a 100644 --- a/examples/test_program.my +++ b/examples/test_program.my @@ -24,7 +24,7 @@ do { y = y + 1 } -z = "a b c d e f".split().map(fn(x) { +z = "a b c d e f".split(" ").map(fn(x) { println(x.upper()) x.upper() }) diff --git a/src/monkey/ast/ast.go b/src/monkey/ast/ast.go index 89c6049..e5f88e2 100644 --- a/src/monkey/ast/ast.go +++ b/src/monkey/ast/ast.go @@ -160,7 +160,7 @@ func (fal *ForEachArrayLoop) String() string { out.WriteString(" in ") out.WriteString(fal.Value.String()) if fal.Cond != nil { - out.WriteString(" WHERE ") + out.WriteString(" where ") out.WriteString(fal.Cond.String()) } out.WriteString(" { ") @@ -198,7 +198,7 @@ func (fml *ForEachMapLoop) String() string { out.WriteString(" in ") out.WriteString(fml.X.String()) if fml.Cond != nil { - out.WriteString(" WHERE ") + out.WriteString(" where ") out.WriteString(fml.Cond.String()) } out.WriteString(" { ") @@ -266,7 +266,7 @@ func (fdr *ForEachDotRange) String() string { out.WriteString(" .. ") out.WriteString(fdr.EndIdx.String()) if fdr.Cond != nil { - out.WriteString(" WHERE ") + out.WriteString(" where ") out.WriteString(fdr.Cond.String()) } out.WriteString(" { ") @@ -1905,8 +1905,7 @@ func (e *EnumLiteral) String() string { /////////////////////////////////////////////////////////// // List Comprehension(for array & string) // /////////////////////////////////////////////////////////// -//[ x+1 for x in arr ] -//[ str for str in strs ] +// [ Expr for Var in Value ] ---> Value could be array or string type ListComprehension struct { Token token.Token Var string @@ -1939,10 +1938,10 @@ func (lc *ListComprehension) String() string { out.WriteString(" in ") out.WriteString(lc.Value.String()) if lc.Cond != nil { - out.WriteString(" WHERE ") + out.WriteString(" where ") out.WriteString(lc.Cond.String()) } - out.WriteString("] ") + out.WriteString(" ]") return out.String() } @@ -1950,14 +1949,14 @@ func (lc *ListComprehension) String() string { /////////////////////////////////////////////////////////// // List Comprehension(for range) // /////////////////////////////////////////////////////////// -//[exp for i in start..end ] +//[Expr for Var in StartIdx..EndIdx ] type ListRangeComprehension struct { Token token.Token Var string StartIdx Expression EndIdx Expression Cond Expression //conditional clause(nil if there is no 'WHERE' clause) - Expr Expression + Expr Expression //the result expression } func (lc *ListRangeComprehension) Pos() token.Position { @@ -1986,19 +1985,19 @@ func (lc *ListRangeComprehension) String() string { out.WriteString("..") out.WriteString(lc.EndIdx.String()) if lc.Cond != nil { - out.WriteString(" WHERE ") + out.WriteString(" where ") out.WriteString(lc.Cond.String()) } - out.WriteString("] ") + out.WriteString(" ]") return out.String() } /////////////////////////////////////////////////////////// -// Map Comprehension // +// LIST Map Comprehension // /////////////////////////////////////////////////////////// -//[ expr for k,v in hash ] -type MapComprehension struct { +//[ Expr for Key,Value in X ] +type ListMapComprehension struct { Token token.Token Key string Value string @@ -2007,21 +2006,21 @@ type MapComprehension struct { Expr Expression //the result expression } -func (mc *MapComprehension) Pos() token.Position { +func (mc *ListMapComprehension) Pos() token.Position { return mc.Token.Pos } -func (mc *MapComprehension) End() token.Position { +func (mc *ListMapComprehension) End() token.Position { if mc.Cond != nil { return mc.Cond.End() } return mc.Expr.End() } -func (mc *MapComprehension) expressionNode() {} -func (mc *MapComprehension) TokenLiteral() string { return mc.Token.Literal } +func (mc *ListMapComprehension) expressionNode() {} +func (mc *ListMapComprehension) TokenLiteral() string { return mc.Token.Literal } -func (mc *MapComprehension) String() string { +func (mc *ListMapComprehension) String() string { var out bytes.Buffer out.WriteString("[ ") @@ -2031,10 +2030,155 @@ func (mc *MapComprehension) String() string { out.WriteString(" in ") out.WriteString(mc.X.String()) if mc.Cond != nil { - out.WriteString(" WHERE ") + out.WriteString(" where ") out.WriteString(mc.Cond.String()) } - out.WriteString("]") + out.WriteString(" ]") + + return out.String() +} + +/////////////////////////////////////////////////////////// +// Hash Comprehension(for array & string) // +/////////////////////////////////////////////////////////// +//{ KeyExpr:ValExpr for Var in Value } -->Value could be array or string +type HashComprehension struct { + Token token.Token + Var string + Value Expression //value(array or string) to range over + Cond Expression //conditional clause(nil if there is no 'WHERE' clause) + KeyExpr Expression //the result Key expression + ValExpr Expression //the result Value expression +} + +func (hc *HashComprehension) Pos() token.Position { + return hc.Token.Pos +} + +func (hc *HashComprehension) End() token.Position { + if hc.Cond != nil { + return hc.Cond.End() + } + return hc.Value.End() +} + +func (hc *HashComprehension) expressionNode() {} +func (hc *HashComprehension) TokenLiteral() string { return hc.Token.Literal } + +func (hc *HashComprehension) String() string { + var out bytes.Buffer + + out.WriteString("{ ") + out.WriteString(hc.KeyExpr.String()) + out.WriteString(" : ") + out.WriteString(hc.ValExpr.String()) + out.WriteString(" for ") + out.WriteString(hc.Var) + out.WriteString(" in ") + out.WriteString(hc.Value.String()) + if hc.Cond != nil { + out.WriteString(" where ") + out.WriteString(hc.Cond.String()) + } + out.WriteString(" }") + + return out.String() +} + +/////////////////////////////////////////////////////////// +// Hash Comprehension(for range) // +/////////////////////////////////////////////////////////// +//{ KeyExp:ValExp for Var in StartIdx..EndIdx } +type HashRangeComprehension struct { + Token token.Token + Var string + StartIdx Expression + EndIdx Expression + Cond Expression //conditional clause(nil if there is no 'WHERE' clause) + KeyExpr Expression //the result Key expression + ValExpr Expression //the result Value expression +} + +func (hc *HashRangeComprehension) Pos() token.Position { + return hc.Token.Pos +} + +func (hc *HashRangeComprehension) End() token.Position { + if hc.Cond != nil { + return hc.Cond.End() + } + return hc.EndIdx.End() +} + +func (hc *HashRangeComprehension) expressionNode() {} +func (hc *HashRangeComprehension) TokenLiteral() string { return hc.Token.Literal } + +func (hc *HashRangeComprehension) String() string { + var out bytes.Buffer + + out.WriteString("{ ") + out.WriteString(hc.KeyExpr.String()) + out.WriteString(" : ") + out.WriteString(hc.ValExpr.String()) + out.WriteString(" for ") + out.WriteString(hc.Var) + out.WriteString(" in ") + out.WriteString(hc.StartIdx.String()) + out.WriteString("..") + out.WriteString(hc.EndIdx.String()) + if hc.Cond != nil { + out.WriteString(" where ") + out.WriteString(hc.Cond.String()) + } + out.WriteString(" }") + + return out.String() +} + +/////////////////////////////////////////////////////////// +// Hash Map Comprehension // +/////////////////////////////////////////////////////////// +//{ KeyExpr:ValExpr for Key,Value in X } +type HashMapComprehension struct { + Token token.Token + Key string + Value string + X Expression //value(hash) to range over + Cond Expression //Conditional clause(nil if there is no 'WHERE' clause) + KeyExpr Expression //the result Key expression + ValExpr Expression //the result Value expression +} + +func (mc *HashMapComprehension) Pos() token.Position { + return mc.Token.Pos +} + +func (mc *HashMapComprehension) End() token.Position { + if mc.Cond != nil { + return mc.Cond.End() + } + return mc.X.End() +} + +func (mc *HashMapComprehension) expressionNode() {} +func (mc *HashMapComprehension) TokenLiteral() string { return mc.Token.Literal } + +func (mc *HashMapComprehension) String() string { + var out bytes.Buffer + + out.WriteString("{ ") + out.WriteString(mc.KeyExpr.String()) + out.WriteString(" : ") + out.WriteString(mc.ValExpr.String()) + out.WriteString(" for ") + out.WriteString(mc.Key + ", " + mc.Value) + out.WriteString(" in ") + out.WriteString(mc.X.String()) + if mc.Cond != nil { + out.WriteString(" where ") + out.WriteString(mc.Cond.String()) + } + out.WriteString(" }") return out.String() } diff --git a/src/monkey/eval/eval.go b/src/monkey/eval/eval.go index 518a16b..a3cb854 100644 --- a/src/monkey/eval/eval.go +++ b/src/monkey/eval/eval.go @@ -147,8 +147,14 @@ func Eval(node ast.Node, scope *Scope) Object { return evalListComprehension(node, scope) case *ast.ListRangeComprehension: return evalListRangeComprehension(node, scope) - case *ast.MapComprehension: - return evalMapComprehension(node, scope) + case *ast.ListMapComprehension: + return evalListMapComprehension(node, scope) + case *ast.HashComprehension: + return evalHashComprehension(node, scope) + case *ast.HashRangeComprehension: + return evalHashRangeComprehension(node, scope) + case *ast.HashMapComprehension: + return evalHashMapComprehension(node, scope) case *ast.BreakExpression: return BREAK case *ast.ContinueExpression: @@ -1630,9 +1636,9 @@ func evalMapExpression(me *ast.MapExpr, scope *Scope) Object { return result } -//[x+1 for x in arr ] -//[ str for str in strs ] -//[ x for x in tuple ] +//[ x+1 for x in arr ] +//[ str for str in strs ] +//[ x for x in tuple ] func evalListComprehension(lc *ast.ListComprehension, scope *Scope) Object { innerScope := NewScope(scope) aValue := Eval(lc.Value, innerScope) @@ -1688,7 +1694,7 @@ func evalListComprehension(lc *ast.ListComprehension, scope *Scope) Object { return ret } -//[x for x in a..b ] +//[ x for x in a..b ] //Almost same as evalForEachDotRangeExpression() function func evalListRangeComprehension(lc *ast.ListRangeComprehension, scope *Scope) Object { innerScope := NewScope(scope) @@ -1773,8 +1779,8 @@ func evalListRangeComprehension(lc *ast.ListRangeComprehension, scope *Scope) Ob return ret } -//[ expr for k,v in hash ] -func evalMapComprehension(mc *ast.MapComprehension, scope *Scope) Object { +//[ expr for k,v in hash ] +func evalListMapComprehension(mc *ast.ListMapComprehension, scope *Scope) Object { innerScope := NewScope(scope) aValue := Eval(mc.X, innerScope) if aValue.Type() == ERROR_OBJ { @@ -1818,6 +1824,223 @@ func evalMapComprehension(mc *ast.MapComprehension, scope *Scope) Object { return ret } +//{ k:v for x in arr } +//{ k:v for str in strs } +//{ k:v for x in tuple } +//Almost same as evalListComprehension +func evalHashComprehension(hc *ast.HashComprehension, scope *Scope) Object { + innerScope := NewScope(scope) + aValue := Eval(hc.Value, innerScope) + if aValue.Type() == ERROR_OBJ { + return aValue + } + + _, ok := aValue.(Iterable) //must be listable + if !ok { + panic(NewError(hc.Pos().Sline(), NOTITERABLE)) + } + + var members []Object + if aValue.Type() == STRING_OBJ { + aStr, _ := aValue.(*String) + runes := []rune(aStr.String) + for _, rune := range runes { + members = append(members, NewString(string(rune))) + } + } else if aValue.Type() == ARRAY_OBJ { + arr, _ := aValue.(*Array) + members = arr.Members + } else if aValue.Type() == TUPLE_OBJ { + tuple, _ := aValue.(*Tuple) + members = tuple.Members + } + + ret := &Hash{Pairs: make(map[HashKey]HashPair)} + + for idx, value := range members { + newSubScope := NewScope(innerScope) + newSubScope.Set("$_", NewInteger(int64(idx))) + newSubScope.Set(hc.Var, value) + if hc.Cond != nil { + cond := Eval(hc.Cond, newSubScope) + if cond.Type() == ERROR_OBJ { + return cond + } + + if !IsTrue(cond) { + continue + } + } + + keyResult := Eval(hc.KeyExpr, newSubScope) + if keyResult.Type() == ERROR_OBJ { + return keyResult + } + + valueResult := Eval(hc.ValExpr, newSubScope) + if valueResult.Type() == ERROR_OBJ { + return valueResult + } + + if hashable, ok := keyResult.(Hashable); ok { + ret.Pairs[hashable.HashKey()] = HashPair{Key: keyResult, Value: valueResult} + } else { + panic(NewError("", KEYERROR, keyResult.Type())) + } + } + + return ret +} + +//{ k:v for x in a..b } +//Almost same as evalListRangeComprehension() function +func evalHashRangeComprehension(hc *ast.HashRangeComprehension, scope *Scope) Object { + innerScope := NewScope(scope) + + startIdx := Eval(hc.StartIdx, innerScope) + endIdx := Eval(hc.EndIdx, innerScope) + + var j int64 + arr := &Array{} + + switch startIdx.(type) { + case *Integer: + startVal := startIdx.(*Integer).Int64 + if endIdx.Type() != INTEGER_OBJ { + panic(NewError(hc.Pos().Sline(), RANGETYPEERROR, INTEGER_OBJ, endIdx.Type())) + } + endVal := endIdx.(*Integer).Int64 + + if startVal >= endVal { + for j = startVal; j >= endVal; j = j - 1 { + arr.Members = append(arr.Members, NewInteger(j)) + } + } else { + for j = startVal; j <= endVal; j = j + 1 { + arr.Members = append(arr.Members, NewInteger(j)) + } + } + case *String: + startVal := startIdx.(*String).String + if endIdx.Type() != STRING_OBJ { + panic(NewError(hc.Pos().Sline(), RANGETYPEERROR, STRING_OBJ, endIdx.Type())) + } + endVal := endIdx.(*String).String + + //only support single character with lowercase + alphabet := "0123456789abcdefghijklmnopqrstuvwxyz" + + //convert to int for easy comparation + leftByte := []int32(strings.ToLower(startVal))[0] + rightByte := []int32(strings.ToLower(endVal))[0] + if leftByte >= rightByte { // z -> a + for i := len(alphabet) - 1; i >= 0; i-- { + v := int32(alphabet[i]) + if v <= leftByte && v >= rightByte { + arr.Members = append(arr.Members, NewString(string(v))) + } + } + } else { // a -> z + for _, v := range alphabet { + if v >= leftByte && v <= rightByte { + arr.Members = append(arr.Members, NewString(string(v))) + } + } + } + } + + ret := &Hash{Pairs: make(map[HashKey]HashPair)} + + for idx, value := range arr.Members { + newSubScope := NewScope(innerScope) + newSubScope.Set("$_", NewInteger(int64(idx))) + newSubScope.Set(hc.Var, value) + if hc.Cond != nil { + cond := Eval(hc.Cond, newSubScope) + if cond.Type() == ERROR_OBJ { + return cond + } + + if !IsTrue(cond) { + continue + } + } + + keyResult := Eval(hc.KeyExpr, newSubScope) + if keyResult.Type() == ERROR_OBJ { + return keyResult + } + + valueResult := Eval(hc.ValExpr, newSubScope) + if valueResult.Type() == ERROR_OBJ { + return valueResult + } + + if hashable, ok := keyResult.(Hashable); ok { + ret.Pairs[hashable.HashKey()] = HashPair{Key: keyResult, Value: valueResult} + } else { + panic(NewError("", KEYERROR, keyResult.Type())) + } + } + + return ret +} + +//{ k:v for k,v in hash } +//Almost same as evalListMapComprehension +func evalHashMapComprehension(mc *ast.HashMapComprehension, scope *Scope) Object { + innerScope := NewScope(scope) + aValue := Eval(mc.X, innerScope) + if aValue.Type() == ERROR_OBJ { + return aValue + } + + _, ok := aValue.(Iterable) //must be listable + if !ok { + panic(NewError(mc.Pos().Sline(), NOTITERABLE)) + } + + //must be a *Hash, if not, panic + hash, _ := aValue.(*Hash) + + ret := &Hash{Pairs: make(map[HashKey]HashPair)} + + for _, pair := range hash.Pairs { + newSubScope := NewScope(innerScope) + newSubScope.Set(mc.Key, pair.Key) + newSubScope.Set(mc.Value, pair.Value) + + if mc.Cond != nil { + cond := Eval(mc.Cond, newSubScope) + if cond.Type() == ERROR_OBJ { + return cond + } + + if !IsTrue(cond) { + continue + } + } + + keyResult := Eval(mc.KeyExpr, newSubScope) + if keyResult.Type() == ERROR_OBJ { + return keyResult + } + + valueResult := Eval(mc.ValExpr, newSubScope) + if valueResult.Type() == ERROR_OBJ { + return valueResult + } + + if hashable, ok := keyResult.(Hashable); ok { + ret.Pairs[hashable.HashKey()] = HashPair{Key: keyResult, Value: valueResult} + } else { + panic(NewError("", KEYERROR, keyResult.Type())) + } + } + + return ret +} + func evalCaseExpression(ce *ast.CaseExpr, scope *Scope) Object { rv := Eval(ce.Expr, scope) //case expression if rv.Type() == ERROR_OBJ { diff --git a/src/monkey/parser/parser.go b/src/monkey/parser/parser.go index 1a32be2..7c5b5d5 100644 --- a/src/monkey/parser/parser.go +++ b/src/monkey/parser/parser.go @@ -1103,23 +1103,143 @@ func (p *Parser) parseIndexExpression(arr ast.Expression) ast.Expression { } func (p *Parser) parseHashExpression() ast.Expression { - hash := &ast.HashLiteral{Token: p.curToken} - hash.Pairs = make(map[ast.Expression]ast.Expression) - if p.peekTokenIs(token.RBRACE) { + curToken := p.curToken //save current token + + if p.peekTokenIs(token.RBRACE) { //empty hash p.nextToken() + hash := &ast.HashLiteral{Token: curToken} + hash.Pairs = make(map[ast.Expression]ast.Expression) + return hash } - for !p.curTokenIs(token.RBRACE) { - p.nextToken() - key := p.parseExpression(LOWEST) - if !p.expectPeek(token.FATARROW) { + + p.nextToken() //skip the '{' + keyExpr := p.parseExpression(SLICE) //note the precedence + + if p.peekTokenIs(token.COLON) { //a hash comprehension + p.nextToken() //skip current token + p.nextToken() //skip the ':' + + valueExpr := p.parseExpression(LOWEST) + if !p.expectPeek(token.FOR) { return nil } + + if !p.expectPeek(token.IDENT) { //must be an identifier + return nil + } + + //{ k:k+1 for k in arr } -----> k is a variable in an array + //{ k:k+1 for k,v in hash } -----> k is a key in a hash + keyOrVariable := p.curToken.Literal + + if p.peekTokenIs(token.COMMA) { //hash map comprehension + return p.parseHashMapComprehension(curToken, keyOrVariable, keyExpr, valueExpr, token.RBRACE) + } + + // hash list comprehension + return p.parseHashListComprehension(curToken, keyOrVariable, keyExpr, valueExpr, token.RBRACE) + + } else if p.peekTokenIs(token.FATARROW) { //a hash + hash := &ast.HashLiteral{Token: curToken} + hash.Pairs = make(map[ast.Expression]ast.Expression) + + p.nextToken() //skip current token + p.nextToken() //skip the '=>' + + hash.Pairs[keyExpr] = p.parseExpression(LOWEST) + p.nextToken() //skip current token + for !p.curTokenIs(token.RBRACE) { + p.nextToken() //skip the ',' + + key := p.parseExpression(SLICE) + if !p.expectPeek(token.FATARROW) { + return nil + } + + p.nextToken() //skip the '=>' + hash.Pairs[key] = p.parseExpression(LOWEST) + p.nextToken() + } + return hash + } else { + msg := fmt.Sprintf("Syntax Error: %v - expected next token to be ':' or '=>', got %s instead", p.peekToken.Pos, p.peekToken.Type) + p.errors = append(p.errors, msg) + return nil + } + + return nil +} + +func (p *Parser) parseHashMapComprehension(curToken token.Token, key string, keyExpr ast.Expression, valueExpr ast.Expression, closure token.TokenType) ast.Expression { + if !p.expectPeek(token.COMMA) { + return nil + } + + if !p.expectPeek(token.IDENT) { + return nil + } + value := p.curToken.Literal + + if !p.expectPeek(token.IN) { + return nil + } + p.nextToken() + + X := p.parseExpression(LOWEST) + + var aCond ast.Expression + if p.peekTokenIs(token.WHERE) { p.nextToken() - hash.Pairs[key] = p.parseExpression(LOWEST) p.nextToken() + aCond = p.parseExpression(LOWEST) } - return hash + + if !p.expectPeek(closure) { + return nil + } + + result := &ast.HashMapComprehension{Token: curToken, Key: key, Value: value, X:X, Cond: aCond, KeyExpr:keyExpr, ValExpr:valueExpr} + return result +} + +func (p *Parser) parseHashListComprehension(curToken token.Token, variable string, keyExpr ast.Expression, valueExpr ast.Expression, closure token.TokenType) ast.Expression { + var isRange bool = false + + if !p.expectPeek(token.IN) { + return nil + } + p.nextToken() + + aValue1 := p.parseExpression(LOWEST) + + var aValue2 ast.Expression + if p.peekTokenIs(token.DOTDOT) { + isRange = true + p.nextToken() + p.nextToken() + aValue2 = p.parseExpression(DOTDOT) + } + + var aCond ast.Expression + if p.peekTokenIs(token.WHERE) { + p.nextToken() + p.nextToken() + aCond = p.parseExpression(LOWEST) + } + + if !p.expectPeek(closure) { + return nil + } + + var result ast.Expression + if !isRange { + result = &ast.HashComprehension{Token: curToken, Var: variable, Value: aValue1, Cond: aCond, KeyExpr:keyExpr, ValExpr:valueExpr} + } else { + result = &ast.HashRangeComprehension{Token: curToken, Var: variable, StartIdx: aValue1, EndIdx: aValue2, Cond: aCond, KeyExpr:keyExpr, ValExpr:valueExpr} + } + + return result } func (p *Parser) parseStructExpression() ast.Expression { @@ -1159,14 +1279,14 @@ func (p *Parser) parseArrayExpression() ast.Expression { temp, b := p.parseExpressionArrayEx([]ast.Expression{}, token.RBRACKET) if b { //list comprehension or map comprehension p.nextToken() //skip 'for' - if !p.expectPeek(token.IDENT) { + if !p.expectPeek(token.IDENT) { //must be an identifier return nil } variable := p.curToken.Literal if p.peekTokenIs(token.COMMA) { //map comprehension - return p.parseMapComprehension(curToken, temp[0], variable, token.RBRACKET) //here 'variable' is the key of the map + return p.parseListMapComprehension(curToken, temp[0], variable, token.RBRACKET) //here 'variable' is the key of the map } //list comprehension @@ -1217,7 +1337,7 @@ func (p *Parser) parseListComprehension(curToken token.Token, expr ast.Expressio return result } -func (p *Parser) parseMapComprehension(curToken token.Token, expr ast.Expression, variable string, closure token.TokenType) ast.Expression { +func (p *Parser) parseListMapComprehension(curToken token.Token, expr ast.Expression, variable string, closure token.TokenType) ast.Expression { if !p.expectPeek(token.COMMA) { return nil @@ -1246,7 +1366,7 @@ func (p *Parser) parseMapComprehension(curToken token.Token, expr ast.Expression return nil } - result := &ast.MapComprehension{Token: curToken, Key: variable, Value: Value, X:X, Cond: aCond, Expr: expr} + result := &ast.ListMapComprehension{Token: curToken, Key: variable, Value: Value, X:X, Cond: aCond, Expr: expr} return result }