comments
This commit is contained in:
23
ast/ast.go
23
ast/ast.go
@@ -391,7 +391,6 @@ type AssignmentStatement struct {
|
||||
func (as AssignmentStatement) TokenLiteral() string {
|
||||
return as.Token.Literal
|
||||
}
|
||||
|
||||
func (as AssignmentStatement) String() string {
|
||||
var out bytes.Buffer
|
||||
|
||||
@@ -403,5 +402,25 @@ func (as AssignmentStatement) String() string {
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func (as AssignmentStatement) statementNode() {}
|
||||
|
||||
// Comment a comment
|
||||
type Comment struct {
|
||||
Token token.Token // the token.COMMENT token
|
||||
Value string
|
||||
}
|
||||
|
||||
func (c *Comment) statementNode() {}
|
||||
|
||||
// TokenLiteral prints the literal value of the token associated with this node
|
||||
func (c *Comment) TokenLiteral() string { return c.Token.Literal }
|
||||
|
||||
// String returns a stringified version of the AST for debugging
|
||||
func (c *Comment) String() string {
|
||||
var out bytes.Buffer
|
||||
|
||||
out.WriteString(c.TokenLiteral() + " ")
|
||||
out.WriteString(c.Value)
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ type Lexer struct {
|
||||
position int // current position in input (point to current char)
|
||||
readPosition int // current reading position in input (after current char)
|
||||
ch byte // current char under exclamation
|
||||
prevCh byte // previous char read
|
||||
}
|
||||
|
||||
func New(input string) *Lexer {
|
||||
@@ -16,6 +17,7 @@ func New(input string) *Lexer {
|
||||
}
|
||||
|
||||
func (l *Lexer) readChar() {
|
||||
l.prevCh = l.ch
|
||||
if l.readPosition >= len(l.input) {
|
||||
l.ch = 0
|
||||
} else {
|
||||
@@ -39,6 +41,9 @@ func (l *Lexer) NextToken() token.Token {
|
||||
l.skipWhitespace()
|
||||
|
||||
switch l.ch {
|
||||
case '#':
|
||||
tok.Type = token.COMMENT
|
||||
tok.Literal = l.readLine()
|
||||
case '=':
|
||||
if l.peekChar() == '=' {
|
||||
ch := l.ch
|
||||
@@ -69,7 +74,13 @@ func (l *Lexer) NextToken() token.Token {
|
||||
}
|
||||
|
||||
case '/':
|
||||
tok = newToken(token.SLASH, l.ch)
|
||||
if l.peekChar() == '/' {
|
||||
l.readChar()
|
||||
tok.Type = token.COMMENT
|
||||
tok.Literal = l.readLine()
|
||||
} else {
|
||||
tok = newToken(token.SLASH, l.ch)
|
||||
}
|
||||
case '*':
|
||||
tok = newToken(token.ASTERISK, l.ch)
|
||||
case '<':
|
||||
@@ -165,3 +176,14 @@ func (l *Lexer) readString() string {
|
||||
}
|
||||
return l.input[position:l.position]
|
||||
}
|
||||
|
||||
func (l *Lexer) readLine() string {
|
||||
position := l.position + 1
|
||||
for {
|
||||
l.readChar()
|
||||
if l.ch == '\r' || l.ch == '\n' || l.ch == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return l.input[position:l.position]
|
||||
}
|
||||
|
||||
@@ -6,13 +6,15 @@ import (
|
||||
)
|
||||
|
||||
func TestNextToken(t *testing.T) {
|
||||
input := `let five = 5;
|
||||
input := `#!./monkey-lang
|
||||
let five = 5;
|
||||
let ten = 10;
|
||||
|
||||
let add = fn(x, y) {
|
||||
x + y;
|
||||
};
|
||||
|
||||
# this is a comment
|
||||
let result = add(five, ten);
|
||||
!-/*5;
|
||||
5 < 10 > 5;
|
||||
@@ -23,6 +25,7 @@ func TestNextToken(t *testing.T) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// this is another comment
|
||||
10 == 10;
|
||||
10 != 9;
|
||||
"foobar"
|
||||
@@ -35,6 +38,7 @@ func TestNextToken(t *testing.T) {
|
||||
expectedType token.TokenType
|
||||
expectedLiteral string
|
||||
}{
|
||||
{token.COMMENT, "!./monkey-lang"},
|
||||
{token.LET, "let"},
|
||||
{token.IDENT, "five"},
|
||||
{token.ASSIGN, "="},
|
||||
@@ -61,6 +65,7 @@ func TestNextToken(t *testing.T) {
|
||||
{token.SEMICOLON, ";"},
|
||||
{token.RBRACE, "}"},
|
||||
{token.SEMICOLON, ";"},
|
||||
{token.COMMENT, " this is a comment"},
|
||||
{token.LET, "let"},
|
||||
{token.IDENT, "result"},
|
||||
{token.ASSIGN, "="},
|
||||
@@ -106,7 +111,7 @@ func TestNextToken(t *testing.T) {
|
||||
{token.SEMICOLON, ";"},
|
||||
|
||||
{token.RBRACE, "}"},
|
||||
|
||||
{token.COMMENT, " this is another comment"},
|
||||
{token.INT, "10"},
|
||||
{token.EQ, "=="},
|
||||
{token.INT, "10"},
|
||||
|
||||
@@ -26,7 +26,7 @@ const (
|
||||
CLOSURE_OBJ = "CLOSURE"
|
||||
)
|
||||
|
||||
// Mutable is the interface for all immutable objects which must implement
|
||||
// Immutable is the interface for all immutable objects which must implement
|
||||
// the Clone() method used by binding names to values.
|
||||
type Immutable interface {
|
||||
Clone() Object
|
||||
|
||||
@@ -153,6 +153,8 @@ func (p *Parser) parseStatement() ast.Statement {
|
||||
}
|
||||
|
||||
switch p.curToken.Type {
|
||||
case token.COMMENT:
|
||||
return p.parseComment()
|
||||
case token.LET:
|
||||
return p.parseLetStatement()
|
||||
case token.RETURN:
|
||||
@@ -542,3 +544,7 @@ func (p *Parser) parseAssignmentStatement() ast.Statement {
|
||||
|
||||
return stmt
|
||||
}
|
||||
|
||||
func (p *Parser) parseComment() ast.Statement {
|
||||
return &ast.Comment{Token: p.curToken, Value: p.curToken.Literal}
|
||||
}
|
||||
|
||||
@@ -1126,3 +1126,48 @@ func testAssignmentStatement(t *testing.T, s ast.Statement, name string) bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func TestComments(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expectedComment string
|
||||
}{
|
||||
{"// foo", " foo"},
|
||||
{"#!monkey", "!monkey"},
|
||||
{"# foo", " foo"},
|
||||
{" # foo", " foo"},
|
||||
{" // let x = 1", " let x = 1"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
l := lexer.New(tt.input)
|
||||
p := New(l)
|
||||
program := p.ParseProgram()
|
||||
checkParserErrors(t, p)
|
||||
|
||||
if len(program.Statements) != 1 {
|
||||
t.Fatalf("program.Statements does not contain 1 statements. got=%d",
|
||||
len(program.Statements))
|
||||
}
|
||||
|
||||
comment := program.Statements[0]
|
||||
if !testComment(t, comment, tt.expectedComment) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testComment(t *testing.T, s ast.Statement, expected string) bool {
|
||||
comment, ok := s.(*ast.Comment)
|
||||
if !ok {
|
||||
t.Errorf("s not *ast.Comment. got=%T", s)
|
||||
return false
|
||||
}
|
||||
|
||||
if comment.Value != expected {
|
||||
t.Errorf("comment.Value not '%s'. got=%s", expected, comment.Value)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ const (
|
||||
ILLEGAL = "ILLEGAL"
|
||||
EOF = "EOF"
|
||||
|
||||
// COMMENT a line comment, e.g: # this is a comment
|
||||
COMMENT = "COMMENT"
|
||||
|
||||
// Identifiers + literals
|
||||
IDENT = "IDENT" // add, foobar, x, y
|
||||
INT = "INT" // 123456
|
||||
|
||||
Reference in New Issue
Block a user