Hashes
This commit is contained in:
@@ -67,6 +67,7 @@ func New(l *lexer.Lexer) *Parser {
|
||||
p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral)
|
||||
p.registerPrefix(token.STRING, p.parseStringLiteral)
|
||||
p.registerPrefix(token.LBRACKET, p.parseArrayLiteral)
|
||||
p.registerPrefix(token.LBRACE, p.parseHashLiteral)
|
||||
|
||||
p.infixParseFns = make(map[token.TokenType]infixParseFn)
|
||||
p.registerInfix(token.PLUS, p.parseInfixExpression)
|
||||
@@ -458,3 +459,32 @@ func (p *Parser) parseIndexExpression(left ast.Expression) ast.Expression {
|
||||
|
||||
return exp
|
||||
}
|
||||
|
||||
func (p *Parser) parseHashLiteral() ast.Expression {
|
||||
hash := &ast.HashLiteral{Token: p.curToken}
|
||||
hash.Pairs = make(map[ast.Expression]ast.Expression)
|
||||
|
||||
for !p.peekTokenIs(token.RBRACE) {
|
||||
p.nextToken()
|
||||
key := p.parseExpression(LOWEST)
|
||||
|
||||
if !p.expectPeek(token.COLON) {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.nextToken()
|
||||
value := p.parseExpression(LOWEST)
|
||||
|
||||
hash.Pairs[key] = value
|
||||
|
||||
if !p.peekTokenIs(token.RBRACE) && !p.expectPeek(token.COMMA) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if !p.expectPeek(token.RBRACE) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return hash
|
||||
}
|
||||
|
||||
@@ -743,6 +743,108 @@ func TestParsingIndexExpressions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsingHashLiteralsStringKeys(t *testing.T) {
|
||||
input := `{"one": 1, "two": 2, "three": 3}`
|
||||
|
||||
l := lexer.New(input)
|
||||
p := New(l)
|
||||
program := p.ParseProgram()
|
||||
checkParserErrors(t, p)
|
||||
|
||||
stmt := program.Statements[0].(*ast.ExpressionStatement)
|
||||
hash, ok := stmt.Expression.(*ast.HashLiteral)
|
||||
if !ok {
|
||||
t.Fatalf("exp not *ast.HashLiteral. got=%T", stmt.Expression)
|
||||
}
|
||||
|
||||
if len(hash.Pairs) != 3 {
|
||||
t.Fatalf("hash.Pairs has wrong length. got=%d", len(hash.Pairs))
|
||||
}
|
||||
|
||||
expected := map[string]int64{
|
||||
"one": 1,
|
||||
"two": 2,
|
||||
"three": 3,
|
||||
}
|
||||
|
||||
for key, value := range hash.Pairs {
|
||||
literal, ok := key.(*ast.StringLiteral)
|
||||
if !ok {
|
||||
t.Errorf("key is not ast.StringLiteral. got=%T", key)
|
||||
}
|
||||
|
||||
expectedValue := expected[literal.String()]
|
||||
|
||||
testIntegerLiteral(t, value, expectedValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsingEmptyHashLiteral(t *testing.T) {
|
||||
input := "{}"
|
||||
|
||||
l := lexer.New(input)
|
||||
p := New(l)
|
||||
program := p.ParseProgram()
|
||||
checkParserErrors(t, p)
|
||||
|
||||
stmt := program.Statements[0].(*ast.ExpressionStatement)
|
||||
hash, ok := stmt.Expression.(*ast.HashLiteral)
|
||||
if !ok {
|
||||
t.Fatalf("exp not *ast.HashLiteral. got=%T", stmt.Expression)
|
||||
}
|
||||
|
||||
if len(hash.Pairs) != 0 {
|
||||
t.Errorf("hash.Pairs has wrong length. got=%d", len(hash.Pairs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsingHashLiteralsWithExpressions(t *testing.T) {
|
||||
input := `{"one": 0 + 1, "two": 10 - 8, "three": 15 / 5}`
|
||||
|
||||
l := lexer.New(input)
|
||||
p := New(l)
|
||||
program := p.ParseProgram()
|
||||
checkParserErrors(t, p)
|
||||
|
||||
stmt := program.Statements[0].(*ast.ExpressionStatement)
|
||||
hash, ok := stmt.Expression.(*ast.HashLiteral)
|
||||
if !ok {
|
||||
t.Fatalf("exp not *ast.HashLiteral. got=%T", stmt.Expression)
|
||||
}
|
||||
|
||||
if len(hash.Pairs) != 3 {
|
||||
t.Fatalf("hash.Pairs has wrong length. got=%d", len(hash.Pairs))
|
||||
}
|
||||
|
||||
tests := map[string]func(expression ast.Expression){
|
||||
"one": func(e ast.Expression) {
|
||||
testInfixExpression(t, e, 0, "+", 1)
|
||||
},
|
||||
"two": func(e ast.Expression) {
|
||||
testInfixExpression(t, e, 10, "-", 8)
|
||||
},
|
||||
"three": func(e ast.Expression) {
|
||||
testInfixExpression(t, e, 15, "/", 5)
|
||||
},
|
||||
}
|
||||
|
||||
for key, value := range hash.Pairs {
|
||||
literal, ok := key.(*ast.StringLiteral)
|
||||
if !ok {
|
||||
t.Errorf("key is not ast.StringLiteral. got=%T", key)
|
||||
continue
|
||||
}
|
||||
|
||||
testFunc, ok := tests[literal.String()]
|
||||
if !ok {
|
||||
t.Errorf("No test function for key %q found", literal.String())
|
||||
continue
|
||||
}
|
||||
|
||||
testFunc(value)
|
||||
}
|
||||
}
|
||||
|
||||
func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
|
||||
if s.TokenLiteral() != "let" {
|
||||
t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
|
||||
|
||||
Reference in New Issue
Block a user