Dict literal now accepts expressions as keys. Key values are computed at evalutetion time and still must be integer or string
This commit is contained in:
+8
-1
@@ -30,7 +30,14 @@ func evalDict(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
if param, err = tree.Compute(ctx); err != nil {
|
||||
break
|
||||
}
|
||||
items[key] = param
|
||||
var keyValue any
|
||||
if keyValue, err = (key.(*scan.Term)).Compute(ctx); err == nil {
|
||||
if kern.IsInteger(keyValue) || kern.IsString(keyValue) {
|
||||
items[keyValue] = param
|
||||
} else {
|
||||
err = key.(*scan.Term).Errorf("dict key can be integer or string, got %s", kern.TypeName(keyValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
v = &items
|
||||
|
||||
@@ -257,24 +257,41 @@ func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (su
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) {
|
||||
tk := parser.Next(scanner)
|
||||
if tk.Sym == scan.SymError {
|
||||
err = tk.Error()
|
||||
return
|
||||
}
|
||||
if tk.Sym == scan.SymClosedBrace || tk.Sym == scan.SymEos {
|
||||
return
|
||||
}
|
||||
if tk.Sym == scan.SymInteger || tk.Sym == scan.SymString {
|
||||
tkSep := parser.Next(scanner)
|
||||
if tkSep.Sym != scan.SymColon {
|
||||
// func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) {
|
||||
// tk := parser.Next(scanner)
|
||||
// if tk.Sym == scan.SymError {
|
||||
// err = tk.Error()
|
||||
// return
|
||||
// }
|
||||
// if tk.Sym == scan.SymClosedBrace || tk.Sym == scan.SymEos {
|
||||
// return
|
||||
// }
|
||||
// if tk.Sym == scan.SymInteger || tk.Sym == scan.SymString || tk.Sym == scan.SymIdentifier {
|
||||
// tkSep := parser.Next(scanner)
|
||||
// if tkSep.Sym != scan.SymColon {
|
||||
// err = tkSep.ErrorExpectedGot(":")
|
||||
// } else {
|
||||
// key = tk.Value
|
||||
// }
|
||||
// } else {
|
||||
// err = tk.ErrorExpectedGot("dictionary-key or }")
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (parser *parser) parseDictKey(scanner *scan.Scanner) (key *scan.Term, err error) {
|
||||
var keyTree *scan.Ast
|
||||
if keyTree, err = parser.parseItem(scanner, parserNoFlags, scan.SymColon, scan.SymClosedBrace); err == nil {
|
||||
key = keyTree.Root()
|
||||
tkSep := scanner.Previous()
|
||||
sym := tkSep.Sym
|
||||
if sym == scan.SymClosedBrace || sym == scan.SymEos {
|
||||
if key != nil {
|
||||
err = tkSep.ErrorExpectedGot(":")
|
||||
}
|
||||
} else if sym != scan.SymColon {
|
||||
err = tkSep.ErrorExpectedGot(":")
|
||||
} else {
|
||||
key = tk.Value
|
||||
}
|
||||
} else {
|
||||
err = tk.ErrorExpectedGot("dictionary-key or }")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -284,11 +301,11 @@ func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext)
|
||||
lastSym := scan.SymUnknown
|
||||
itemExpected := false
|
||||
for lastSym != scan.SymClosedBrace && lastSym != scan.SymEos {
|
||||
var subTree *scan.Ast
|
||||
var valueTree *scan.Ast
|
||||
var key any
|
||||
if key, err = parser.parseDictKey(scanner); err != nil {
|
||||
break
|
||||
} else if key == nil {
|
||||
} else if key.(*scan.Term) == nil {
|
||||
tk := scanner.Previous()
|
||||
lastSym = tk.Sym
|
||||
if itemExpected {
|
||||
@@ -296,9 +313,9 @@ func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext)
|
||||
}
|
||||
break
|
||||
}
|
||||
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedBrace); err == nil {
|
||||
if subTree.Root() != nil {
|
||||
args[key] = subTree.Root()
|
||||
if valueTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedBrace); err == nil {
|
||||
if valueTree.Root() != nil {
|
||||
args[key] = valueTree.Root()
|
||||
} else /*if key != nil*/ {
|
||||
prev := scanner.Previous()
|
||||
err = prev.ErrorExpectedGot("dictionary-value")
|
||||
@@ -464,7 +481,7 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
||||
selectorTerm = nil
|
||||
continue
|
||||
} else {
|
||||
err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.Source())
|
||||
err = tk.ErrorExpectedOneOfGot(termSymbols...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ func (ast *Ast) insert(tree, node *Term) (root *Term, err error) {
|
||||
root = node
|
||||
tree.SetParent(node)
|
||||
} else {
|
||||
err = node.Errorf("two adjacent operators: %q and %q", tree, node)
|
||||
err = node.Errorf("two adjacent operators: %q and %q", tree, node.Source())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -188,25 +188,6 @@ func StringEndsWithOperator(s string) bool {
|
||||
return endingOperator(s) != SymNone
|
||||
}
|
||||
|
||||
// func endingOperator(s string) (sym Symbol) {
|
||||
// var matchLength = 0
|
||||
// sym = SymNone
|
||||
// lower := strings.TrimRight(strings.ToLower(s), " \t")
|
||||
// for symbol, spec := range symbolMap {
|
||||
// if strings.HasSuffix(lower, spec.repr) {
|
||||
// if len(spec.repr) > matchLength {
|
||||
// matchLength = len(spec.repr)
|
||||
// if spec.kind == symClassOperator && (spec.opType == PosInfix || spec.opType == PosPrefix) {
|
||||
// sym = symbol
|
||||
// } else {
|
||||
// sym = SymNone
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func endingOperator(s string) (sym Symbol) {
|
||||
var matchLength = 0
|
||||
var repr string
|
||||
|
||||
@@ -136,3 +136,7 @@ func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (e
|
||||
err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
|
||||
return
|
||||
}
|
||||
|
||||
func (tk *Token) ErrorExpectedOneOfGot(expected ...Symbol) (err error) {
|
||||
return tk.ErrorExpectedGotStringWithPrefix("expected one of ", SymListToString(expected, true), SymToString(tk.Sym))
|
||||
}
|
||||
|
||||
+8
-3
@@ -39,11 +39,16 @@ func TestDictParser(t *testing.T) {
|
||||
/* 16 */ {`D={1:"a"}; D.2`, nil, `[1:15] key 2 not found`},
|
||||
/* 17 */ {`D={1:"a"}; D."2"`, nil, `[1:17] key "2" not found`},
|
||||
/* 18 */ {`D={"a":1, "b":2}; D.a = 10; D.a`, int64(10), nil},
|
||||
// /* 19 */ {`D={"a": {"one": 1}}; D.a."one" = 10; D["a"]["one"]`, int64(10), nil},
|
||||
/* 19 */ {`k="a"; D={k: 10}`, kern.NewDict(map[any]any{"a": int64(10)}), nil},
|
||||
/* 20 */ {`k=[<1,2>]; D={k: 10}`, nil, `[1:16] dict key can be integer or string, got lisked-list`},
|
||||
/* 21 */ {`f=func(n){"x"+n}; d{f(5):10}`, nil, `[0:0] two adjacent operators: "d" and "{}"`},
|
||||
/* 22 */ {`f=func(n){"x"+n}; d={f(5):10}`, kern.NewDict(map[any]any{"x5": int64(10)}), nil},
|
||||
/* 23 */ {`f=func(n){"x"+n}; d={f(5); "z":10}`, nil, "[1:27] expected one of `:`, `}`, got `;`"},
|
||||
/* 24 */ {`f=func(n){"x"+n}; d={f(5) but "z":10}`, kern.NewDict(map[any]any{"z": int64(10)}), nil},
|
||||
}
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 16)
|
||||
runTestSuite(t, section, inputs)
|
||||
runTestSuiteSpec(t, section, inputs, 1, 23, 24)
|
||||
// runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
func TestAccessSubFields(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user