commit 45420a626bbd8fa777c8cadbb7b24646db156a58
parent 8885f23423bb7f442000fab6261d9fb427b930e9
Author: bsandro <email@bsandro.tech>
Date: Mon, 13 Jun 2022 23:21:23 +0300
prefix operators: shriek(!) and minus(-)
Diffstat:
3 files changed, 94 insertions(+), 0 deletions(-)
diff --git a/ast/ast.go b/ast/ast.go
@@ -20,6 +20,24 @@ type Expression interface {
expressionNode()
}
+type PrefixExpression struct {
+ Token token.Token
+ Operator string
+ Right Expression
+}
+func (pe *PrefixExpression) expressionNode() {}
+func (pe *PrefixExpression) TokenLiteral() string {
+ return pe.Token.Literal
+}
+func (pe *PrefixExpression) String() string {
+ var out bytes.Buffer
+ out.WriteString("{")
+ out.WriteString(pe.Operator)
+ out.WriteString(pe.Right.String())
+ out.WriteString("}")
+ return out.String()
+}
+
type Identifier struct {
Token token.Token
Value string
diff --git a/parser/parser.go b/parser/parser.go
@@ -43,6 +43,8 @@ func New(l *lexer.Lexer) *Parser {
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)
p.registerPrefix(token.INT, p.parseIntegerLiteral)
+ p.registerPrefix(token.SHRIEK, p.parsePrefixExpression)
+ p.registerPrefix(token.MINUS, p.parsePrefixExpression)
return p
}
@@ -120,6 +122,7 @@ func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement {
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
+ p.noPrefixParseFnError(p.curToken.Type)
return nil
} else {
leftExp := prefix()
@@ -173,3 +176,18 @@ func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) {
func (p *Parser) registerInfix(tokenType token.TokenType, fn infixParseFn) {
p.infixParseFns[tokenType] = fn
}
+
+func (p *Parser) noPrefixParseFnError(t token.TokenType) {
+ msg := fmt.Sprintf("no prefix parse function for %s", t)
+ p.errors = append(p.errors, msg)
+}
+
+func (p *Parser) parsePrefixExpression() ast.Expression {
+ expression := &ast.PrefixExpression{
+ Token: p.curToken,
+ Operator: p.curToken.Literal,
+ }
+ p.nextToken()
+ expression.Right = p.parseExpression(PREFIX)
+ return expression
+}
diff --git a/parser/parser_test.go b/parser/parser_test.go
@@ -4,6 +4,7 @@ import (
"interp/ast"
"interp/lexer"
"testing"
+ "fmt"
)
func TestLetStatements(t *testing.T) {
@@ -165,3 +166,60 @@ func TestIntegerLiteralExpression(t *testing.T) {
t.Errorf("literal.TokenLiteral is not '8' but %s", literal.TokenLiteral())
}
}
+
+func TestParsingPrefixExpressions(t *testing.T) {
+ prefixTests := []struct{
+ input string
+ operator string
+ intValue int64
+ }{
+ {"!5;", "!", 5},
+ {"-15;", "-", 15},
+ }
+
+ for _, tt := range prefixTests {
+ l := lexer.New(tt.input)
+ p := New(l)
+ prg := p.ParseProgram()
+ checkParserErrors(t, p)
+
+ if len(prg.Statements) != 1 {
+ t.Fatalf("prg.Statements count is wrong: %d instead of 1", len(prg.Statements))
+ }
+
+ statement, ok := prg.Statements[0].(*ast.ExpressionStatement)
+ if !ok {
+ t.Fatalf("prg.Statements[0] is not ast.ExpressionStatement but %T", prg.Statements[0])
+ }
+
+ expr, ok := statement.Expression.(*ast.PrefixExpression)
+ if !ok {
+ t.Fatalf("statement.Expression is not PrefixExpression but %T", statement.Expression)
+ }
+
+ if expr.Operator != tt.operator {
+ t.Fatalf("exp.Operator is not %s but %s", tt.operator, expr.Operator)
+ }
+
+ if !testIntegerLiteral(t, expr.Right, tt.intValue) {
+ return
+ }
+ }
+}
+
+func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
+ integ, ok := il.(*ast.IntegerLiteral)
+ if !ok {
+ t.Errorf("il is not *ast.IntegerLiteral but %T", il)
+ return false
+ }
+ if integ.Value != value {
+ t.Errorf("integ.Value is not %d but %d", value, integ.Value)
+ return false
+ }
+ if integ.TokenLiteral() != fmt.Sprintf("%d", value) {
+ t.Errorf("integ.TokenLiteral not %d but %s", value, integ.TokenLiteral())
+ return false
+ }
+ return true
+}