diff --git a/dict_test.go b/dict_test.go
index 6a8778e..1f293d1 100644
--- a/dict_test.go
+++ b/dict_test.go
@@ -24,7 +24,7 @@ func TestDictParser(t *testing.T) {
 		/*  1 */ {`{}`, map[any]any{}, nil},
 		/*  2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)},
 		/*  3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
-		/*  4 */ {`{1:"one",2:"two",3:"three"}.2`, "three", nil},
+		/*  4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
 		/*  5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
 		/*  6 */ {`{1:"one"} + {2:"two"}`, map[any]any{1: "one", 2: "two"}, nil},
 		/*  7 */ {`2 in {1:"one", 2:"two"}`, true, nil},
diff --git a/list_test.go b/list_test.go
index 9552384..5bd9137 100644
--- a/list_test.go
+++ b/list_test.go
@@ -33,7 +33,7 @@ func TestListParser(t *testing.T) {
 		/*  11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
 		/*  12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil},
 		/*  13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
-		/*  14 */ {`[1,2,3].1`, int64(2), nil},
+		/*  14 */ {`[1,2,3][1]`, int64(2), nil},
 		/*  15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil},
 		/*  16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil},
 		/*  17 */ {`list=["one","two","three"]; list.10`, nil, errors.New(`[1:36] index 10 out of bounds`)},
@@ -43,6 +43,8 @@ func TestListParser(t *testing.T) {
 		/*  21 */ {`"b" in ["a", "b", "c"]`, true, nil},
 		/*  22 */ {`a=[1,2]; (a)<<3`, []any{1, 2, 3}, nil},
 		/*  23 */ {`a=[1,2]; (a)<<3; 1`, []any{1, 2}, nil},
+		/*  24 */ {`["a","b","c","d"][1]`, "b", nil},
+		/*  25 */ {`["a","b","c","d"][1,1]`, nil, errors.New(`[1:19] one index only is allowed`)},
 
 		// /*  8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
 		// /*  9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
@@ -61,8 +63,6 @@ func TestListParser(t *testing.T) {
 		var gotErr error
 
 		ctx := NewSimpleStore()
-		// ctx.SetVar("var1", int64(123))
-		// ctx.SetVar("var2", "abc")
 		ImportMathFuncs(ctx)
 		parser := NewParser(ctx)
 
diff --git a/operand-list.go b/operand-list.go
index d6a8784..e3d39e1 100644
--- a/operand-list.go
+++ b/operand-list.go
@@ -83,12 +83,12 @@ func (list *ListType) indexDeepCmp(target any) (index int) {
 
 // -------- list term
 func newListTermA(args ...*term) *term {
-	return newListTerm(args)
+	return newListTerm(0, 0, args)
 }
 
-func newListTerm(args []*term) *term {
+func newListTerm(row, col int, args []*term) *term {
 	return &term{
-		tk:       *NewValueToken(0, 0, SymList, "[]", args),
+		tk:       *NewValueToken(row, col, SymList, "[]", args),
 		parent:   nil,
 		children: nil,
 		position: posLeaf,
diff --git a/operator-dot.go b/operator-dot.go
index cd3a226..a82bf6b 100644
--- a/operator-dot.go
+++ b/operator-dot.go
@@ -17,7 +17,7 @@ func newDotTerm(tk *Token) (inst *term) {
 	}
 }
 
-func verifyIndex(ctx ExprContext, indexTerm *term, maxValue int) (index int, err error) {
+func verifyDotIndex(ctx ExprContext, indexTerm *term, maxValue int) (index int, err error) {
 	var v int
 	var indexValue any
 	if indexValue, err = indexTerm.compute(ctx); err == nil {
@@ -51,12 +51,12 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
 	case *ListType:
 		var index int
 		array := ([]any)(*unboxedValue)
-		if index, err = verifyIndex(ctx, indexTerm, len(array)); err == nil {
+		if index, err = verifyDotIndex(ctx, indexTerm, len(array)); err == nil {
 			v = array[index]
 		}
 	case string:
 		var index int
-		if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
+		if index, err = verifyDotIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
 			v = string(unboxedValue[index])
 		}
 	case *DictType:
diff --git a/operator-index.go b/operator-index.go
new file mode 100644
index 0000000..f8bd4b5
--- /dev/null
+++ b/operator-index.go
@@ -0,0 +1,91 @@
+// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
+// All rights reserved.
+
+// operator-index.go
+package expr
+
+// -------- index term
+func newIndexTerm(tk *Token) (inst *term) {
+	return &term{
+		tk:       *tk,
+		children: make([]*term, 0, 2),
+		position: posInfix,
+		priority: priDot,
+		evalFunc: evalIndex,
+	}
+}
+
+func verifyKey(indexTerm *term, indexList *ListType) (index any, err error) {
+	if len(*indexList) != 1 {
+		err = indexTerm.Errorf("one index only is allowed")
+	} else {
+		index = (*indexList)[0]
+	}
+	return
+}
+
+func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) {
+	var v int
+
+	if len(*indexList) != 1 {
+		err = indexTerm.Errorf("one index only is allowed")
+	} else if v, err = toInt((*indexList)[0], "index expression"); err == nil {
+		if v < 0 && v >= -maxValue {
+			v = maxValue + v
+		}
+		if v >= 0 && v < maxValue {
+			index = v
+		} else {
+			err = indexTerm.Errorf("index %d out of bounds", v)
+		}
+	}
+	return
+}
+
+func evalIndex(ctx ExprContext, self *term) (v any, err error) {
+	var leftValue, rightValue any
+	var indexList *ListType
+	var ok bool
+
+	if err = self.checkOperands(); err != nil {
+		return
+	}
+	if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
+		return
+	}
+
+	if indexList, ok = rightValue.(*ListType); !ok {
+		err = self.Errorf("invalid index expression")
+		return
+	}
+	indexTerm := self.children[1]
+
+	switch unboxedValue := leftValue.(type) {
+	case *ListType:
+		var index int
+		if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
+			v = (*unboxedValue)[index]
+		}
+	case string:
+		var index int
+		if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
+			v = string(unboxedValue[index])
+		}
+	case *DictType:
+		var ok bool
+		var indexValue any
+		if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
+			if v, ok = (*unboxedValue)[indexValue]; !ok {
+				err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
+			}
+		}
+	default:
+		err = self.errIncompatibleTypes(leftValue, rightValue)
+	}
+	return
+}
+
+// init
+func init() {
+	registerTermConstructor(SymIndex, newIndexTerm)
+}
diff --git a/operator-sum.go b/operator-sum.go
index e0bc003..af824eb 100644
--- a/operator-sum.go
+++ b/operator-sum.go
@@ -38,23 +38,6 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
 			rightInt, _ := rightValue.(int64)
 			v = leftInt + rightInt
 		}
-		// } else if IsList(leftValue) || IsList(rightValue) {
-		// 	var leftList, rightList *ListType
-		// 	var ok bool
-		// 	if leftList, ok = leftValue.(*ListType); !ok {
-		// 		leftList = &ListType{leftValue}
-		// 	}
-		// 	if rightList, ok = rightValue.(*ListType); !ok {
-		// 		rightList = &ListType{rightValue}
-		// 	}
-		// 	sumList := make(ListType, 0, len(*leftList)+len(*rightList))
-		// 	for _, item := range *leftList {
-		// 		sumList = append(sumList, item)
-		// 	}
-		// 	for _, item := range *rightList {
-		// 		sumList = append(sumList, item)
-		// 	}
-		// 	v = &sumList
 	} else if IsList(leftValue) && IsList(rightValue) {
 		var leftList, rightList *ListType
 		leftList, _ = leftValue.(*ListType)
diff --git a/parser.go b/parser.go
index 3428060..cb7315b 100644
--- a/parser.go
+++ b/parser.go
@@ -155,6 +155,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
 }
 
 func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
+	r, c := scanner.lastPos()
 	args := make([]*term, 0)
 	lastSym := SymUnknown
 	itemExpected := false
@@ -179,7 +180,7 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term
 		if lastSym != SymClosedSquare {
 			err = scanner.Previous().ErrorExpectedGot("]")
 		} else {
-			subtree = newListTerm(args)
+			subtree = newListTerm(r, c, args)
 		}
 	}
 	return
@@ -301,7 +302,7 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
 		startRow = tk.row
 		startCol = tk.col
 	} else if !defaultCase {
-		filterList = newListTerm(make([]*term, 0))
+		filterList = newListTerm(startRow, startCol, make([]*term, 0))
 	}
 
 	if tk.Sym == SymOpenBrace {
@@ -402,7 +403,19 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
 		case SymOpenSquare:
 			var listTerm *term
 			if listTerm, err = self.parseList(scanner, allowVarRef); err == nil {
-				err = tree.addTerm(listTerm)
+				var sym = SymUnknown
+				if currentTerm != nil {
+					sym = currentTerm.symbol()
+				}
+				if sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression {
+					indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
+					indexTerm := newTerm(indexTk)
+					if err = tree.addTerm(indexTerm); err == nil {
+						err = tree.addTerm(listTerm)
+					}
+				} else {
+					err = tree.addTerm(listTerm)
+				}
 				currentTerm = listTerm
 			}
 		case SymOpenBrace:
diff --git a/scanner.go b/scanner.go
index cb977f2..b88ee98 100644
--- a/scanner.go
+++ b/scanner.go
@@ -74,6 +74,14 @@ func (self *scanner) unreadChar() (err error) {
 	return
 }
 
+func (self *scanner) lastPos() (r, c int) {
+	if self.prev != nil {
+		r = self.prev.row
+		c = self.prev.col
+	}
+	return
+}
+
 func (self *scanner) Previous() *Token {
 	return self.prev
 }
diff --git a/symbol.go b/symbol.go
index ede606a..3004c24 100644
--- a/symbol.go
+++ b/symbol.go
@@ -85,6 +85,7 @@ const (
 	SymFuncDef
 	SymList
 	SymDict
+	SymIndex
 	SymExpression
 	SymSelector     // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
 	SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"