selectors support for hash
This commit is contained in:
@@ -577,6 +577,36 @@ func TestHashLiterals(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHashSelectorExpressions(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
expected interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
`{"foo": 5}.foo`,
|
||||||
|
5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`{"foo": 5}.bar`,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`{}.foo`,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
evaluated := testEval(tt.input)
|
||||||
|
integer, ok := tt.expected.(int)
|
||||||
|
if ok {
|
||||||
|
testIntegerObject(t, evaluated, int64(integer))
|
||||||
|
} else {
|
||||||
|
testNullObject(t, evaluated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHashIndexExpressions(t *testing.T) {
|
func TestHashIndexExpressions(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
|
|||||||
5
examples/selectors.monkey
Normal file
5
examples/selectors.monkey
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
let d = {"foo": 1, "bar": 2}
|
||||||
|
|
||||||
|
assert(d.foo == 1, "d.foo != 1")
|
||||||
|
assert(d.bar == 2, "d.bar != 2")
|
||||||
|
assert(d.bogus == null, "d.bogus != null")
|
||||||
@@ -107,6 +107,8 @@ func (l *Lexer) NextToken() token.Token {
|
|||||||
tok = newToken(token.SEMICOLON, l.ch)
|
tok = newToken(token.SEMICOLON, l.ch)
|
||||||
case ',':
|
case ',':
|
||||||
tok = newToken(token.COMMA, l.ch)
|
tok = newToken(token.COMMA, l.ch)
|
||||||
|
case '.':
|
||||||
|
tok = newToken(token.DOT, l.ch)
|
||||||
case '(':
|
case '(':
|
||||||
tok = newToken(token.LPAREN, l.ch)
|
tok = newToken(token.LPAREN, l.ch)
|
||||||
case ')':
|
case ')':
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func TestNextToken(t *testing.T) {
|
|||||||
"foo bar"
|
"foo bar"
|
||||||
[1, 2];
|
[1, 2];
|
||||||
{"foo": "bar"}
|
{"foo": "bar"}
|
||||||
"foo \"bar\""
|
d.foo
|
||||||
`
|
`
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -138,7 +138,9 @@ func TestNextToken(t *testing.T) {
|
|||||||
{token.COLON, ":"},
|
{token.COLON, ":"},
|
||||||
{token.STRING, "bar"},
|
{token.STRING, "bar"},
|
||||||
{token.RBRACE, "}"},
|
{token.RBRACE, "}"},
|
||||||
{token.STRING, "foo \"bar\""},
|
{token.IDENT, "d"},
|
||||||
|
{token.DOT, "."},
|
||||||
|
{token.IDENT, "foo"},
|
||||||
{token.EOF, ""},
|
{token.EOF, ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ var precedences = map[token.TokenType]int{
|
|||||||
token.ASTERISK: PRODUCT,
|
token.ASTERISK: PRODUCT,
|
||||||
token.LPAREN: CALL,
|
token.LPAREN: CALL,
|
||||||
token.LBRACKET: INDEX,
|
token.LBRACKET: INDEX,
|
||||||
|
token.DOT: INDEX,
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -89,6 +90,7 @@ func New(l *lexer.Lexer) *Parser {
|
|||||||
p.registerInfix(token.LPAREN, p.parseCallExpression)
|
p.registerInfix(token.LPAREN, p.parseCallExpression)
|
||||||
p.registerInfix(token.LBRACKET, p.parseIndexExpression)
|
p.registerInfix(token.LBRACKET, p.parseIndexExpression)
|
||||||
p.registerInfix(token.ASSIGN, p.parseAssignmentExpression)
|
p.registerInfix(token.ASSIGN, p.parseAssignmentExpression)
|
||||||
|
p.registerInfix(token.DOT, p.parseSelectorExpression)
|
||||||
|
|
||||||
// Read two tokens, so curToken and peekToken are both set
|
// Read two tokens, so curToken and peekToken are both set
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
@@ -570,3 +572,9 @@ func (p *Parser) parseComment() ast.Statement {
|
|||||||
func (p *Parser) parseNull() ast.Expression {
|
func (p *Parser) parseNull() ast.Expression {
|
||||||
return &ast.Null{Token: p.curToken}
|
return &ast.Null{Token: p.curToken}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseSelectorExpression(expression ast.Expression) ast.Expression {
|
||||||
|
p.expectPeek(token.IDENT)
|
||||||
|
index := &ast.StringLiteral{Token: p.curToken, Value: p.curToken.Literal}
|
||||||
|
return &ast.IndexExpression{Left: expression, Index: index}
|
||||||
|
}
|
||||||
|
|||||||
@@ -358,6 +358,10 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
|
|||||||
"add(a * b[2], b[1], 2 * [1, 2][1])",
|
"add(a * b[2], b[1], 2 * [1, 2][1])",
|
||||||
"add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))",
|
"add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"d.foo * d.bar",
|
||||||
|
"((d[foo]) * (d[bar]))",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@@ -831,6 +835,40 @@ func TestParsingArrayLiterals(t *testing.T) {
|
|||||||
testInfixExpression(t, array.Elements[2], 3, "+", 3)
|
testInfixExpression(t, array.Elements[2], 3, "+", 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParsingSelectorExpressions(t *testing.T) {
|
||||||
|
input := "myHash.foo"
|
||||||
|
|
||||||
|
l := lexer.New(input)
|
||||||
|
p := New(l)
|
||||||
|
program := p.ParseProgram()
|
||||||
|
checkParserErrors(t, p)
|
||||||
|
stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
|
||||||
|
t.Logf("stmt: %#v", stmt)
|
||||||
|
|
||||||
|
exp, ok := stmt.Expression.(*ast.IndexExpression)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("exp not *ast.IndexExpression. got=%T", stmt.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
ident, ok := exp.Left.(*ast.Identifier)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("exp.Left not *ast.Identifier. got=%T", stmt.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testIdentifier(t, ident, "myHash") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
index, ok := exp.Index.(*ast.StringLiteral)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("exp.Index not *ast.StringLiteral. got=%T", stmt.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
if index.Value != "foo" {
|
||||||
|
t.Fatalf("index.Value != \"foo\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParsingIndexExpressions(t *testing.T) {
|
func TestParsingIndexExpressions(t *testing.T) {
|
||||||
input := "myArray[1 + 1]"
|
input := "myArray[1 + 1]"
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ const (
|
|||||||
COMMA = ","
|
COMMA = ","
|
||||||
SEMICOLON = ";"
|
SEMICOLON = ";"
|
||||||
COLON = ":"
|
COLON = ":"
|
||||||
|
DOT = "."
|
||||||
|
|
||||||
LPAREN = "("
|
LPAREN = "("
|
||||||
RPAREN = ")"
|
RPAREN = ")"
|
||||||
|
|||||||
@@ -328,6 +328,16 @@ func TestHashLiterals(t *testing.T) {
|
|||||||
runVmTests(t, tests)
|
runVmTests(t, tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSelectorExpressions(t *testing.T) {
|
||||||
|
tests := []vmTestCase{
|
||||||
|
{`{"foo": 5}.foo`, 5},
|
||||||
|
{`{"foo": 5}.bar`, nil},
|
||||||
|
{`{}.foo`, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
runVmTests(t, tests)
|
||||||
|
}
|
||||||
|
|
||||||
func TestIndexExpressions(t *testing.T) {
|
func TestIndexExpressions(t *testing.T) {
|
||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{"[1, 2, 3][1]", 2},
|
{"[1, 2, 3][1]", 2},
|
||||||
|
|||||||
Reference in New Issue
Block a user