145 lines
3.3 KiB
Go
145 lines
3.3 KiB
Go
package ast
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"code.jmug.me/jmug/interpreter-in-go/pkg/token"
|
|
)
|
|
|
|
type Node interface {
|
|
TokenLiteral() string
|
|
String() string
|
|
}
|
|
|
|
type Statement interface {
|
|
Node
|
|
statementNode()
|
|
}
|
|
|
|
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()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (p *Program) String() string {
|
|
var out bytes.Buffer
|
|
for _, stmt := range p.Statements {
|
|
out.WriteString(stmt.String())
|
|
}
|
|
return out.String()
|
|
}
|
|
|
|
type LetStatement struct {
|
|
Token token.Token // TODO: This is a little redundant, figure out if I can get rid of it.
|
|
Name *Identifier
|
|
Value Expression
|
|
}
|
|
|
|
func (ls *LetStatement) statementNode() {}
|
|
func (ls *LetStatement) TokenLiteral() string {
|
|
return ls.Token.Literal
|
|
}
|
|
func (ls *LetStatement) String() string {
|
|
var out bytes.Buffer
|
|
out.WriteString(ls.TokenLiteral() + " ")
|
|
out.WriteString(ls.Name.String() + " = ")
|
|
if ls.Value != nil {
|
|
out.WriteString(ls.Value.String())
|
|
}
|
|
out.WriteString(";")
|
|
return out.String()
|
|
}
|
|
|
|
type ReturnStatement struct {
|
|
Token token.Token // TODO: This is a little redundant, figure out if I can get rid of it.
|
|
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()
|
|
}
|
|
|
|
// ExpressionStatement is a simple wrapper of an expression in a statement
|
|
// This is common in scripting languages and allows you to have a source line
|
|
// that is solely an expression, think of the Python REPL and how you can
|
|
// type `1 + 1` and get a result.
|
|
type ExpressionStatement struct {
|
|
Token token.Token // The first token in the expression.
|
|
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 ""
|
|
}
|
|
|
|
// Identifier is treated as an expression because in certain
|
|
// circumstances they can return values (think `let some = other` where `other`
|
|
// is actually an expression returning a value) and this makes them easier to
|
|
// handle (according to the author).
|
|
type Identifier struct {
|
|
Token token.Token
|
|
Value string
|
|
}
|
|
|
|
func (i *Identifier) expressionNode() {}
|
|
func (i *Identifier) TokenLiteral() string {
|
|
return i.Token.Literal
|
|
}
|
|
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
|
|
Right Expression
|
|
}
|
|
|
|
func (pe *PrefixExpression) expressionNode() {}
|
|
func (pe *PrefixExpression) TokenLiteral() string {
|
|
return pe.Token.Literal
|
|
}
|
|
func (pe *PrefixExpression) String() string {
|
|
return "(" + pe.Operator + pe.Right.String() + ")"
|
|
}
|