-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
366 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()) | ||
} | ||
} |