From 434f7c529858ff93449ffdd4990270ba4f4ea3b4 Mon Sep 17 00:00:00 2001 From: ryotaro oda Date: Sat, 7 Dec 2019 22:13:45 +0900 Subject: [PATCH] feat: ast --- ast/ast.go | 329 ++++++++++++++++++++++++++++++++++++++++++++++++ ast/ast_test.go | 37 ++++++ 2 files changed, 366 insertions(+) create mode 100644 ast/ast.go create mode 100644 ast/ast_test.go diff --git a/ast/ast.go b/ast/ast.go new file mode 100644 index 0000000..36ebfff --- /dev/null +++ b/ast/ast.go @@ -0,0 +1,329 @@ +package ast + +import ( + "bytes" + "strings" + + "github.com/0daryo/ody/token" +) + +// The base Node interface +type Node interface { + TokenLiteral() string + String() string +} + +// All statement nodes implement this +type Statement interface { + Node + statementNode() +} + +// All expression nodes implement this +type Expression interface { + Node + expressionNode() +} + +type Program struct { + Statements []Statement +} + +func (p *Program) TokenLiteral() string { + if len(p.Statements) > 0 { + return p.Statements[0].TokenLiteral() + } else { + return "" + } +} + +func (p *Program) String() string { + var out bytes.Buffer + for _, s := range p.Statements { + out.WriteString(s.String()) + } + return out.String() +} + +// Statements +type LetStatement struct { + Token token.Token // the token.LET token + Name *Identifier + Value Expression +} + +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() {} +func (ls *LetStatement) TokenLiteral() string { return ls.Token.Literal } + +// Expressions +type Identifier struct { + Token token.Token // the token.IDENT token + Value string +} + +func (i *Identifier) expressionNode() {} +func (i *Identifier) TokenLiteral() string { return i.Token.Literal } + +type ReturnStatement struct { + Token token.Token + ReturnValue Expression +} + +func (rs *ReturnStatement) statementNode() {} +func (rs *ReturnStatement) TokenLiteral() string { + return rs.Token.Literal +} + +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() +} + +type ExpressionStatement struct { + Token token.Token + Expression Expression +} + +func (es *ExpressionStatement) statementNode() {} +func (es *ExpressionStatement) TokenLiteral() string { + return es.Token.Literal +} + +func (es *ExpressionStatement) String() string { + if es.Expression != nil { + return es.Expression.String() + } + return "" +} +func (i *Identifier) String() string { + return i.Value +} + +type IntegerLiteral struct { + Token token.Token + Value int64 +} + +func (il *IntegerLiteral) expressionNode() {} +func (il *IntegerLiteral) TokenLiteral() string { return il.Token.Literal } +func (il *IntegerLiteral) String() string { return il.Token.Literal } + +type PrefixExpression struct { + Token token.Token + Operator string //ex) -, ! + 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 InfixExpression struct { + Token token.Token + Left Expression + Operator string //ex) -, !=,+ + Right Expression +} + +func (oe *InfixExpression) expressionNode() {} +func (oe *InfixExpression) TokenLiteral() string { return oe.Token.Literal } +func (oe *InfixExpression) String() string { + var out bytes.Buffer + out.WriteString("(") + out.WriteString(oe.Left.String()) + out.WriteString(" " + oe.Operator + " ") + out.WriteString(oe.Right.String()) + out.WriteString(")") + return out.String() +} + +type Boolean struct { + Token token.Token + Value bool +} + +func (b *Boolean) expressionNode() {} +func (b *Boolean) TokenLiteral() string { return b.Token.Literal } +func (b *Boolean) String() string { return b.Token.Literal } + +type IfExpression struct { + Token token.Token + Condition Expression + Consequence *BlockStatement + Alternative *BlockStatement +} + +func (ie *IfExpression) expressionNode() {} +func (ie *IfExpression) TokenLiteral() string { return ie.Token.Literal } +func (ie *IfExpression) String() string { + var out bytes.Buffer + out.WriteString("if") + out.WriteString(ie.Condition.String()) + out.WriteString(" ") + out.WriteString(ie.Consequence.String()) + + if ie.Alternative != nil { + out.WriteString("else ") + out.WriteString(ie.Alternative.String()) + } + return out.String() +} + +type BlockStatement struct { + Token token.Token + Statements []Statement +} + +func (bs *BlockStatement) statementNode() {} +func (bs *BlockStatement) TokenLiteral() string { return bs.Token.Literal } +func (bs *BlockStatement) String() string { + var out bytes.Buffer + for _, s := range bs.Statements { + out.WriteString(s.String()) + } + return out.String() +} + +type FunctionLiteral struct { + Token token.Token + Parameters []*Identifier + Body *BlockStatement +} + +func (fl *FunctionLiteral) expressionNode() {} +func (fl *FunctionLiteral) TokenLiteral() string { return fl.Token.Literal } +func (fl *FunctionLiteral) String() string { + var out bytes.Buffer + + params := []string{} + for _, p := range fl.Parameters { + params = append(params, p.String()) + } + out.WriteString(fl.TokenLiteral()) + out.WriteString("(") + out.WriteString(strings.Join(params, ", ")) + out.WriteString(") ") + out.WriteString(fl.Body.String()) + return out.String() +} + +type CallExpression struct { + Token token.Token + Function Expression + Arguments []Expression +} + +func (ce *CallExpression) expressionNode() {} +func (ce *CallExpression) TokenLiteral() string { + return ce.Token.Literal +} +func (ce *CallExpression) String() string { + var out bytes.Buffer + args := []string{} + for _, a := range ce.Arguments { + args = append(args, a.String()) + } + out.WriteString(ce.Function.String()) + out.WriteString("(") + out.WriteString(strings.Join(args, ", ")) + out.WriteString(")") + + return out.String() +} + +type StringLiteral struct { + Token token.Token + Value string +} + +func (sl *StringLiteral) expressionNode() {} +func (sl *StringLiteral) TokenLiteral() string { return sl.Token.Literal } +func (sl *StringLiteral) String() string { return sl.Token.Literal } + +type ArrayLiteral struct { + Token token.Token + Elements []Expression +} + +func (al *ArrayLiteral) expressionNode() {} +func (al *ArrayLiteral) TokenLiteral() string { return al.Token.Literal } +func (al *ArrayLiteral) String() string { + var out bytes.Buffer + + elements := []string{} + for _, el := range al.Elements { + elements = append(elements, el.String()) + } + out.WriteString("[") + out.WriteString(strings.Join(elements, ", ")) + out.WriteString("]") + + return out.String() +} + +type IndexExpression struct { + Token token.Token + Left Expression + Index Expression +} + +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 HashLiteral struct { + Token token.Token + Pairs map[Expression]Expression +} + +func (hl *HashLiteral) expressionNode() {} +func (hl *HashLiteral) TokenLiteral() string { return hl.Token.Literal } +func (hl *HashLiteral) String() string { + var out bytes.Buffer + + pairs := []string{} + for key, value := range hl.Pairs { + pairs = append(pairs, key.String()+":"+value.String()) + } + + out.WriteString("{") + out.WriteString(strings.Join(pairs, ", ")) + out.WriteString("}") + + return out.String() +} diff --git a/ast/ast_test.go b/ast/ast_test.go new file mode 100644 index 0000000..f47f1e4 --- /dev/null +++ b/ast/ast_test.go @@ -0,0 +1,37 @@ +package ast + +import ( + "testing" + + "github.com/0daryo/ody/token" +) + +func TestString(t *testing.T) { + 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", + }, + }, + }, + } + if program.String() != "let myVar = anotherVar;" { + t.Errorf("program.String() wrong. got=%q, program.String()", program.String()) + } +}