Built-in datatypes: Added String
The parser now understands what a strings is and can take a string literal and curn out an Stringliteral AST nodes. git-svn-id: https://svn.tlawal.org/svn/monkey@56 f6afcba9-9ef1-4bdd-9b72-7484f5705bac
This commit is contained in:
parent
05d2c32b38
commit
2791a27f1f
10
ast/ast.go
10
ast/ast.go
@ -286,3 +286,13 @@ func (ce *CallExpression) String() string {
|
|||||||
|
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String Literal
|
||||||
|
type StringLiteral struct {
|
||||||
|
Token token.Token
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sl *StringLiteral) expression_node() {}
|
||||||
|
func (sl *StringLiteral) TokenLiteral() string { return sl.Token.Literal }
|
||||||
|
func (sl *StringLiteral) String() string { return sl.Token.Literal }
|
||||||
|
@ -62,6 +62,9 @@ func (l_lexer *Lexer) NextToken() token.Token {
|
|||||||
tok = new_token(token.LT, l_lexer.current_char)
|
tok = new_token(token.LT, l_lexer.current_char)
|
||||||
case '>':
|
case '>':
|
||||||
tok = new_token(token.GT, l_lexer.current_char)
|
tok = new_token(token.GT, l_lexer.current_char)
|
||||||
|
case '"':
|
||||||
|
tok.Type = token.STRING
|
||||||
|
tok.Literal = l_lexer.read_string()
|
||||||
case 0:
|
case 0:
|
||||||
tok.Literal = ""
|
tok.Literal = ""
|
||||||
tok.Type = token.EOF
|
tok.Type = token.EOF
|
||||||
@ -134,3 +137,19 @@ func (l_lexer *Lexer) read_number() string {
|
|||||||
func is_digit(ch byte) bool {
|
func is_digit(ch byte) bool {
|
||||||
return '0' <= ch && ch <= '9'
|
return '0' <= ch && ch <= '9'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read the current character until it encounters a closing '"' or end of input.
|
||||||
|
TODO: some additional thing that can be done at the lexer level with strings is to report an error when it
|
||||||
|
reaches the end of input without proper termination. Support for character escaping would be really neat.
|
||||||
|
*/
|
||||||
|
func (l_lexer *Lexer) read_string() string {
|
||||||
|
position := l_lexer.position + 1
|
||||||
|
for {
|
||||||
|
l_lexer.read_char()
|
||||||
|
if l_lexer.current_char == '"' || l_lexer.current_char == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l_lexer.input[position:l_lexer.position]
|
||||||
|
}
|
||||||
|
@ -25,6 +25,8 @@ func TestNextToken(t *testing.T) {
|
|||||||
|
|
||||||
10 == 10;
|
10 == 10;
|
||||||
10 != 9;
|
10 != 9;
|
||||||
|
"foobar"
|
||||||
|
"foo babr"
|
||||||
`
|
`
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
expectedType token.TokenType
|
expectedType token.TokenType
|
||||||
@ -111,6 +113,9 @@ func TestNextToken(t *testing.T) {
|
|||||||
{token.INT, "9"},
|
{token.INT, "9"},
|
||||||
{token.SEMICOLON, ";"},
|
{token.SEMICOLON, ";"},
|
||||||
|
|
||||||
|
{token.STRING, "foobar"},
|
||||||
|
{token.STRING, "foo babr"},
|
||||||
|
|
||||||
{token.EOF, ""},
|
{token.EOF, ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ const (
|
|||||||
RETURN_VALUE_OBJECT = "RETURN_VALUE"
|
RETURN_VALUE_OBJECT = "RETURN_VALUE"
|
||||||
ERROR_OBJECT = "ERROR"
|
ERROR_OBJECT = "ERROR"
|
||||||
FUNCTION_OBJECT = "FUNCTION"
|
FUNCTION_OBJECT = "FUNCTION"
|
||||||
|
STRING_OBJECT = "STRING"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Object interface {
|
type Object interface {
|
||||||
@ -101,3 +102,11 @@ func (f *Function) Inspect() string {
|
|||||||
|
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String
|
||||||
|
type String struct {
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *String) Type() ObjectType { return STRING_OBJECT }
|
||||||
|
func (s *String) Inspec() string { return s.Value }
|
||||||
|
@ -113,6 +113,9 @@ func New(l_lexer *lexer.Lexer) *Parser {
|
|||||||
// Call Expression
|
// Call Expression
|
||||||
l_parser.register_infix(token.LPAREN, l_parser.parse_call_expression)
|
l_parser.register_infix(token.LPAREN, l_parser.parse_call_expression)
|
||||||
|
|
||||||
|
// String
|
||||||
|
l_parser.register_prefix(token.STRING, l_parser.parse_string_literal)
|
||||||
|
|
||||||
return l_parser
|
return l_parser
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,3 +434,10 @@ func (l_parser *Parser) parse_call_arguments() []ast.Expression {
|
|||||||
}
|
}
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l_parser *Parser) parse_string_literal() ast.Expression {
|
||||||
|
return &ast.StringLiteral{
|
||||||
|
Token: l_parser.current_token,
|
||||||
|
Value: l_parser.current_token.Literal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -658,6 +658,24 @@ func TestLetStatements(l_test *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStringLiteralExpression(l_test *testing.T) {
|
||||||
|
input := `"hello world";`
|
||||||
|
l_lexer := lexer.New(input)
|
||||||
|
l_parser := New(l_lexer)
|
||||||
|
program := l_parser.ParseProgram()
|
||||||
|
check_parser_errors(l_test, l_parser)
|
||||||
|
|
||||||
|
statement := program.Statements[0].(*ast.ExpressionStatement)
|
||||||
|
literal, ok := statement.Expression.(*ast.StringLiteral)
|
||||||
|
if !ok {
|
||||||
|
l_test.Fatalf("expression not *ast.StringLiteral, got=%T", statement.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
if literal.Value != "hello world" {
|
||||||
|
l_test.Errorf("literal.Value not %q, got=%q", "hello world", literal.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
func check_parser_errors(l_test *testing.T, l_parser *Parser) {
|
func check_parser_errors(l_test *testing.T, l_parser *Parser) {
|
||||||
|
@ -15,6 +15,7 @@ const (
|
|||||||
// Identifiers and basic type literals
|
// Identifiers and basic type literals
|
||||||
IDENT = "IDENT"
|
IDENT = "IDENT"
|
||||||
INT = "INT"
|
INT = "INT"
|
||||||
|
STRING = "STRING"
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
ASSIGN = "="
|
ASSIGN = "="
|
||||||
|
Loading…
Reference in New Issue
Block a user