Skip to content

Commit

Permalink
FEAT: working on index operator - lexer and parser execute, evaluator…
Browse files Browse the repository at this point in the history
… not yet - also smth is wrong with precedence parsing
  • Loading branch information
MKaczkow committed Oct 14, 2024
1 parent ac21e6e commit 2c66057
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 0 deletions.
2 changes: 2 additions & 0 deletions monkey/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ is represented as:

### built-in functions
* design choice - should they be evalueated in top-level environment or in their own environment? (`object.Environment`)
* `index operator` is treated like infix operator, with `someArray` as left operand and `index` as right operand, e.g. `myArray[0]` means `myArray` is left operand and `0` is right operand

### compiler
* goal is turning `source code` into `bytecode` and then use VM to execute it
Expand All @@ -120,6 +121,7 @@ is represented as:
- [x] CI for monkey interpreter
- [ ] 'talk-me-through' for monkey interpreter chapter 3
- [ ] chapter 4
- [ ] find and fix bug in precedence parsing (or tests)
- [ ] compiler
- [ ] chapter 1
- [ ] CI for monkey compiler
Expand Down
22 changes: 22 additions & 0 deletions monkey/interpreter/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,28 @@ func (al *ArrayLiteral) String() string {
return out.String()
}

type IndexExpression struct {
Token token.Token // "[" token
Left Expression
Index Expression
// syntactically both Left and Index are *just* expressions
// but semantically Index expression should produce an integer
}

func (ie *IndexExpression) expressionNode() {}
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()
}

type BlockStatement struct {
Token token.Token
Statements []Statement
Expand Down
16 changes: 16 additions & 0 deletions monkey/interpreter/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
PRODUCT // *
PREFIX // -X or !X
CALL // myFunction(X)
INDEX // someArray[index]
)

var precedences = map[token.TokenType]int{
Expand All @@ -30,6 +31,7 @@ var precedences = map[token.TokenType]int{
token.SLASH: PRODUCT,
token.ASTERISK: PRODUCT,
token.LPAREN: CALL,
token.LBRACKET: INDEX,
}

type (
Expand Down Expand Up @@ -79,6 +81,7 @@ func New(l *lexer.Lexer) *Parser {
p.registerInfix(token.LT, p.parseInfixExpression)
p.registerInfix(token.GT, 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()
Expand Down Expand Up @@ -307,6 +310,19 @@ func (p *Parser) parseArrayLiteral() ast.Expression {
return array
}

func (p *Parser) parseIndexExpression(left ast.Expression) ast.Expression {
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) parseBlockStatement() *ast.BlockStatement {
block := &ast.BlockStatement{Token: p.curToken}
block.Statements = []ast.Statement{}
Expand Down
34 changes: 34 additions & 0 deletions monkey/interpreter/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,13 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
// FAIL
//
// temporarily fixed, but this is a #TODO: find out why this was happening
//
// (2024-10-14)
// another failures, something is likely wrong with precedence
// --- FAIL: TestOperatorPrecedenceParsing (0.00s)
// parser_test.go:372: expected="((a * ([1, 2, 3, 4][(b * c)])) * d)", got="((a * ([1, 2, 3, 4][(b * c)]) * d)"
// parser_test.go:372: expected="add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))", got="add((a * (b[2]), (b[1], (2 * ([1, 2][1])) "
// FAIL
tests := []struct {
input string
expected string
Expand Down Expand Up @@ -351,6 +358,14 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
"add(a + b + c * d / f + g)",
"add((((a + b) + ((c * d) / f)) + g)) ",
},
{
"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 {
Expand Down Expand Up @@ -530,6 +545,25 @@ func TestParsingArrayLiterals(t *testing.T) {
testInfixExpression(t, array.Elements[2], 3, "+", 3)
}

func TestParsingIndexExpressions(t *testing.T) {
input := "myArray[1 + 1]"
l := lexer.New(input)
p := New(l)
program := p.ParseProgram()
checkParserErrors(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.Left, "myArray") {
return
}
if !testInfixExpression(t, indexExp.Index, 1, "+", 1) {
return
}
}

func TestFunctionLiteralParsing(t *testing.T) {
input := `fn(x, y) { x + y; }`

Expand Down

0 comments on commit 2c66057

Please sign in to comment.