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 // The operator 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() + ")" } type InfixExpression struct { Token token.Token // The operator token Operator string Left Expression Right Expression } func (ie *InfixExpression) expressionNode() {} func (ie *InfixExpression) TokenLiteral() string { return ie.Token.Literal } func (ie *InfixExpression) String() string { return "(" + ie.Left.String() + " " + ie.Operator + " " + ie.Right.String() + ")" } type Boolean struct { Token token.Token Value bool } func (bl *Boolean) expressionNode() {} func (bl *Boolean) TokenLiteral() string { return bl.Token.Literal } func (bl *Boolean) String() string { return bl.Token.Literal }