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 {
|
func (as AssignmentStatement) TokenLiteral() string {
|
||||||
return as.Token.Literal
|
return as.Token.Literal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (as AssignmentStatement) String() string {
|
func (as AssignmentStatement) String() string {
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
@@ -403,5 +402,25 @@ func (as AssignmentStatement) String() string {
|
|||||||
|
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (as AssignmentStatement) statementNode() {}
|
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)
|
position int // current position in input (point to current char)
|
||||||
readPosition int // current reading position in input (after current char)
|
readPosition int // current reading position in input (after current char)
|
||||||
ch byte // current char under exclamation
|
ch byte // current char under exclamation
|
||||||
|
prevCh byte // previous char read
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(input string) *Lexer {
|
func New(input string) *Lexer {
|
||||||
@@ -16,6 +17,7 @@ func New(input string) *Lexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *Lexer) readChar() {
|
func (l *Lexer) readChar() {
|
||||||
|
l.prevCh = l.ch
|
||||||
if l.readPosition >= len(l.input) {
|
if l.readPosition >= len(l.input) {
|
||||||
l.ch = 0
|
l.ch = 0
|
||||||
} else {
|
} else {
|
||||||
@@ -39,6 +41,9 @@ func (l *Lexer) NextToken() token.Token {
|
|||||||
l.skipWhitespace()
|
l.skipWhitespace()
|
||||||
|
|
||||||
switch l.ch {
|
switch l.ch {
|
||||||
|
case '#':
|
||||||
|
tok.Type = token.COMMENT
|
||||||
|
tok.Literal = l.readLine()
|
||||||
case '=':
|
case '=':
|
||||||
if l.peekChar() == '=' {
|
if l.peekChar() == '=' {
|
||||||
ch := l.ch
|
ch := l.ch
|
||||||
@@ -69,7 +74,13 @@ func (l *Lexer) NextToken() token.Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case '/':
|
case '/':
|
||||||
|
if l.peekChar() == '/' {
|
||||||
|
l.readChar()
|
||||||
|
tok.Type = token.COMMENT
|
||||||
|
tok.Literal = l.readLine()
|
||||||
|
} else {
|
||||||
tok = newToken(token.SLASH, l.ch)
|
tok = newToken(token.SLASH, l.ch)
|
||||||
|
}
|
||||||
case '*':
|
case '*':
|
||||||
tok = newToken(token.ASTERISK, l.ch)
|
tok = newToken(token.ASTERISK, l.ch)
|
||||||
case '<':
|
case '<':
|
||||||
@@ -165,3 +176,14 @@ func (l *Lexer) readString() string {
|
|||||||
}
|
}
|
||||||
return l.input[position:l.position]
|
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) {
|
func TestNextToken(t *testing.T) {
|
||||||
input := `let five = 5;
|
input := `#!./monkey-lang
|
||||||
|
let five = 5;
|
||||||
let ten = 10;
|
let ten = 10;
|
||||||
|
|
||||||
let add = fn(x, y) {
|
let add = fn(x, y) {
|
||||||
x + y;
|
x + y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# this is a comment
|
||||||
let result = add(five, ten);
|
let result = add(five, ten);
|
||||||
!-/*5;
|
!-/*5;
|
||||||
5 < 10 > 5;
|
5 < 10 > 5;
|
||||||
@@ -23,6 +25,7 @@ func TestNextToken(t *testing.T) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is another comment
|
||||||
10 == 10;
|
10 == 10;
|
||||||
10 != 9;
|
10 != 9;
|
||||||
"foobar"
|
"foobar"
|
||||||
@@ -35,6 +38,7 @@ func TestNextToken(t *testing.T) {
|
|||||||
expectedType token.TokenType
|
expectedType token.TokenType
|
||||||
expectedLiteral string
|
expectedLiteral string
|
||||||
}{
|
}{
|
||||||
|
{token.COMMENT, "!./monkey-lang"},
|
||||||
{token.LET, "let"},
|
{token.LET, "let"},
|
||||||
{token.IDENT, "five"},
|
{token.IDENT, "five"},
|
||||||
{token.ASSIGN, "="},
|
{token.ASSIGN, "="},
|
||||||
@@ -61,6 +65,7 @@ func TestNextToken(t *testing.T) {
|
|||||||
{token.SEMICOLON, ";"},
|
{token.SEMICOLON, ";"},
|
||||||
{token.RBRACE, "}"},
|
{token.RBRACE, "}"},
|
||||||
{token.SEMICOLON, ";"},
|
{token.SEMICOLON, ";"},
|
||||||
|
{token.COMMENT, " this is a comment"},
|
||||||
{token.LET, "let"},
|
{token.LET, "let"},
|
||||||
{token.IDENT, "result"},
|
{token.IDENT, "result"},
|
||||||
{token.ASSIGN, "="},
|
{token.ASSIGN, "="},
|
||||||
@@ -106,7 +111,7 @@ func TestNextToken(t *testing.T) {
|
|||||||
{token.SEMICOLON, ";"},
|
{token.SEMICOLON, ";"},
|
||||||
|
|
||||||
{token.RBRACE, "}"},
|
{token.RBRACE, "}"},
|
||||||
|
{token.COMMENT, " this is another comment"},
|
||||||
{token.INT, "10"},
|
{token.INT, "10"},
|
||||||
{token.EQ, "=="},
|
{token.EQ, "=="},
|
||||||
{token.INT, "10"},
|
{token.INT, "10"},
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const (
|
|||||||
CLOSURE_OBJ = "CLOSURE"
|
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.
|
// the Clone() method used by binding names to values.
|
||||||
type Immutable interface {
|
type Immutable interface {
|
||||||
Clone() Object
|
Clone() Object
|
||||||
|
|||||||
@@ -153,6 +153,8 @@ func (p *Parser) parseStatement() ast.Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch p.curToken.Type {
|
switch p.curToken.Type {
|
||||||
|
case token.COMMENT:
|
||||||
|
return p.parseComment()
|
||||||
case token.LET:
|
case token.LET:
|
||||||
return p.parseLetStatement()
|
return p.parseLetStatement()
|
||||||
case token.RETURN:
|
case token.RETURN:
|
||||||
@@ -542,3 +544,7 @@ func (p *Parser) parseAssignmentStatement() ast.Statement {
|
|||||||
|
|
||||||
return stmt
|
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
|
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"
|
ILLEGAL = "ILLEGAL"
|
||||||
EOF = "EOF"
|
EOF = "EOF"
|
||||||
|
|
||||||
|
// COMMENT a line comment, e.g: # this is a comment
|
||||||
|
COMMENT = "COMMENT"
|
||||||
|
|
||||||
// Identifiers + literals
|
// Identifiers + literals
|
||||||
IDENT = "IDENT" // add, foobar, x, y
|
IDENT = "IDENT" // add, foobar, x, y
|
||||||
INT = "INT" // 123456
|
INT = "INT" // 123456
|
||||||
|
|||||||
Reference in New Issue
Block a user