Browse Source

§2.6 preparing the AST for the Pratt parser.

Frederic G. MARAND 5 years ago
parent
commit
b69b615e71
6 changed files with 126 additions and 3 deletions
  1. 16 1
      ast/ast.go
  2. 40 0
      ast/ast_string_test.go
  3. 27 0
      ast/expression.go
  4. 4 0
      ast/identifier.go
  5. 20 1
      ast/let.go
  6. 19 1
      ast/return.go

+ 16 - 1
ast/ast.go

@@ -1,9 +1,13 @@
 package ast
 
-// Node is the interface implemented by every node in the AST.
+import "bytes"
+
+// Node is the interface implemented by every node in the AST. It extends the
+// fmt.Stringer interface, allowing simple printing of nodes.
 type Node interface {
 	// TokenLiteral returns the literal value of the token it's associated to.
 	TokenLiteral() string
+	String() string
 }
 
 // Statement extends Node and is only a way to differentiate between expressions
@@ -26,6 +30,17 @@ type Program struct {
 	Statements []Statement
 }
 
+// String satisfies the Node and fmt.Stringer interfaces.
+func (p *Program) String() string {
+	var out bytes.Buffer
+
+	for _, s := range p.Statements {
+		out.WriteString(s.String())
+	}
+
+	return out.String()
+}
+
 // TokenLiteral returns the string contents of the first statement token, which
 // can be an empty string if the program is empty.
 func (p *Program) TokenLiteral() string {

+ 40 - 0
ast/ast_string_test.go

@@ -0,0 +1,40 @@
+package ast
+
+import (
+	"testing"
+
+	"code.osinet.fr/fgm/waiig15/token"
+)
+
+func TestString(t *testing.T) {
+	// Build a program made of a single "let myBra = anotherVar;" statement.
+	program := &Program{
+		Statements: []Statement{
+			&LetStatement{
+				Token: token.Token{
+					Type:    token.LET,
+					Literal: "let",
+				},
+				Name: &Identifier{
+					Token: token.Token{
+						Type:    token.IDENT,
+						Literal: "myVar",
+					},
+					Value: "myVar",
+				},
+				Value: &Identifier{
+					Token: token.Token{
+						Type:    token.IDENT,
+						Literal: "anotherVar",
+					},
+					Value: "anotherVar",
+				},
+			},
+		},
+	}
+
+	programString := program.String()
+	if programString != "let myVar = anotherVar;" {
+		t.Errorf("program.String() wrong, got=%q", programString)
+	}
+}

+ 27 - 0
ast/expression.go

@@ -0,0 +1,27 @@
+package ast
+
+import "code.osinet.fr/fgm/waiig15/token"
+
+// ExpressionStatement fulfills the Node and Statement interfaces.
+// It represents a statement made of a bare expression like:
+//   x + 10;
+type ExpressionStatement struct {
+	Token      token.Token // the first token of the expression
+	Expression Expression
+}
+
+// String satisfies the Node and fmt.Stringer interfaces.
+func (es *ExpressionStatement) String() string {
+	if es.Expression != nil {
+		return es.Expression.String()
+	}
+
+	return "";
+}
+
+func (es *ExpressionStatement) statementNode() {}
+
+// TokenLiteral satisfies the Node interface.
+func (es *ExpressionStatement) TokenLiteral() string {
+	return es.Token.Literal
+}

+ 4 - 0
ast/identifier.go

@@ -8,6 +8,10 @@ type Identifier struct {
 	Value string      // The identifier string.
 }
 
+func (i *Identifier) String() string {
+	return i.Value
+}
+
 func (i *Identifier) expressionNode() {}
 
 // TokenLiteral satisfies the Node interface.

+ 20 - 1
ast/let.go

@@ -1,6 +1,9 @@
 package ast
 
-import "code.osinet.fr/fgm/waiig15/token"
+import (
+	"code.osinet.fr/fgm/waiig15/token"
+	"bytes"
+)
 
 // LetStatement is the Node type for Let statements.
 type LetStatement struct {
@@ -9,6 +12,22 @@ type LetStatement struct {
 	Value Expression
 }
 
+// String implements Node and fmt.Stringer.
+func (ls *LetStatement) String() string {
+	var out bytes.Buffer
+
+	out.WriteString(ls.TokenLiteral() + " ")
+	out.WriteString(ls.Name.String())
+	out.WriteString(" = ")
+	if ls.Value != nil {
+		out.WriteString(ls.Value.String())
+	}
+
+	out.WriteString(";")
+
+	return out.String()
+}
+
 func (ls *LetStatement) statementNode() {}
 
 // TokenLiteral satisfies the Node interface.

+ 19 - 1
ast/return.go

@@ -1,6 +1,9 @@
 package ast
 
-import "code.osinet.fr/fgm/waiig15/token"
+import (
+	"code.osinet.fr/fgm/waiig15/token"
+	"bytes"
+)
 
 // ReturnStatement fulfills the Node and Statement interfaces.
 type ReturnStatement struct {
@@ -9,6 +12,21 @@ type ReturnStatement struct {
 	ReturnValue Expression
 }
 
+// String satisfies the Node and fmt.Stringer interfaces.
+func (rs *ReturnStatement) String() string {
+	var out bytes.Buffer
+
+	out.WriteString(rs.TokenLiteral() + " ")
+
+	if rs.ReturnValue != nil {
+		out.WriteString(rs.ReturnValue.String())
+	}
+
+	out.WriteString(";")
+
+	return out.String()
+}
+
 func (rs *ReturnStatement) statementNode() {}
 
 // TokenLiteral satisfies the Node interface.