Bladeren bron

Extending the interpreter -- Parsing IndexExpression
e.g. array[index]

simon 3 jaren geleden
bovenliggende
commit
ab01de43bb
3 gewijzigde bestanden met toevoegingen van 73 en 4 verwijderingen
  1. 27 1
      ast/ast.go
  2. 15 0
      parser/parser.go
  3. 31 3
      parser/parser_test.go

+ 27 - 1
ast/ast.go

@@ -323,7 +323,7 @@ func (ce *CallExpression) String() string {
 
 func (ce *CallExpression) expressionNode() {}
 
-// ArrayLiteral 数组表达式  <expression>[<expression>]
+// ArrayLiteral 数组表达式  [<expression>]
 type ArrayLiteral struct {
 	Token   token.Token // the '[' token
 	Element []Expression
@@ -346,3 +346,29 @@ func (al *ArrayLiteral) String() string {
 }
 
 func (al *ArrayLiteral) expressionNode() {}
+
+// IndexExpression <expression>[<expression>]
+// e.g   let myArray = [1,2,3,4]
+//
+//	myArray[2];
+type IndexExpression struct {
+	Token token.Token // the [ token
+	Left  Expression
+	Index Expression
+}
+
+func (ie *IndexExpression) TokenLiteral() string { return ie.Token.Literal }
+
+func (ie *IndexExpression) String() string {
+	var out bytes.Buffer
+
+	out.WriteString("(")
+	out.WriteString(ie.Left.String())
+	out.WriteString("[")
+	out.WriteString(ie.Index.String())
+	out.WriteString("])")
+
+	return out.String()
+}
+
+func (ie *IndexExpression) expressionNode() {}

+ 15 - 0
parser/parser.go

@@ -25,6 +25,7 @@ const (
 	PRODUCT     // *
 	PREFIX      // -X OR !X
 	CALL        // myFunction(X)
+	INDEX       // array[index]
 )
 
 // precedences 指派 token 类型的优先级
@@ -38,6 +39,7 @@ var precedences = map[token.TypeToken]int{
 	token.ASTERISK: PRODUCT,
 	token.SLASH:    PRODUCT,
 	token.LPAREN:   CALL,
+	token.LBRACKET: INDEX,
 }
 
 type (
@@ -85,6 +87,7 @@ func New(l *lexer.Lexer) *Parser {
 	p.registerInfix(token.GT, p.parseInfixExpression)
 	p.registerInfix(token.LT, p.parseInfixExpression)
 	p.registerInfix(token.LPAREN, p.parseCallExpression)
+	p.registerInfix(token.LBRACKET, p.parseIndexExpression)
 
 	// Read two tokens, so curToken and peekToken are both set
 	p.nextToken()
@@ -220,6 +223,18 @@ func (p *Parser) parseCallExpression(left ast.Expression) ast.Expression {
 	exp.Arguments = p.parseExpressionList(token.RPAREN)
 	return exp
 }
+func (p *Parser) parseIndexExpression(left ast.Expression) ast.Expression {
+	defer untrace(trace("parseIndexExpression"))
+	exp := &ast.IndexExpression{Token: p.curToken, Left: left}
+
+	p.nextToken()
+	exp.Index = p.parseExpression(LOWEST)
+
+	if !p.expectPeek(token.RBRACKET) {
+		return nil
+	}
+	return exp
+}
 func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
 	defer untrace(trace("parseInfixExpression"))
 	exp := &ast.InfixExpression{

+ 31 - 3
parser/parser_test.go

@@ -338,6 +338,7 @@ func TestParsingInfixExpressions(t *testing.T) {
 	}
 }
 
+// 操作符 优先级测试
 func TestOperatorPrecedenceParsing(t *testing.T) {
 	tests := []struct {
 		input    string
@@ -416,10 +417,17 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
 		{
 			"!(true == true)", "(!(true == true))",
 		},
+		{
+			"a * [1, 2, 3, 4][b * c] *d",
+			"((a * ([1, 2, 3, 4][(b * c)])) * d)",
+		},
+		{
+			"add(a * b[2], b[1], 2 * [1, 2][1])",
+			"add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))",
+		},
 	}
 
 	for _, tt := range tests {
-		fmt.Printf("=====================================parse %s, begin============================\n", tt.input)
 		l := lexer.New(tt.input)
 		p := New(l)
 		program := p.ParseProgram()
@@ -427,9 +435,8 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
 
 		actual := program.String()
 		if actual != tt.expected {
-			t.Errorf("exptected=%q, got=%q", tt.expected, actual)
+			t.Errorf("exptected=%q,\n got=%q", tt.expected, actual)
 		}
-		fmt.Printf("=====================================parse %s, end============================\n", tt.input)
 	}
 }
 
@@ -732,3 +739,24 @@ func TestArrayLiteralsParsing(t *testing.T) {
 	testInfixExpression(t, array.Element[1], 2, "*", 2)
 	testInfixExpression(t, array.Element[2], 3, "+", 3)
 }
+
+func TestIndexExpressionParsing(t *testing.T) {
+	input := "myArray[1+1]"
+
+	l := lexer.New(input)
+	p := New(l)
+	program := p.ParseProgram()
+	checkParseErrors(t, p)
+
+	stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+	indexExp, ok := stmt.Expression.(*ast.IndexExpression)
+	if !ok {
+		t.Fatalf("exp not *ast.IndexExpression. got=%T", stmt.Expression)
+	}
+	if !testIdentifier(t, indexExp, "myArray") {
+		return
+	}
+	if !testInfixExpression(t, indexExp.Index, 1, "+", 1) {
+		return
+	}
+}