aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-03-29 20:19:31 +0000
committerBobby <[email protected]>2024-03-29 20:19:31 +0000
commitcf6dd4b0e5efa1f8041d5570497a5228c737c454 (patch)
treec15fa426e3a2c74ff6aa5261452ebadfbb3e0fed
parentc6a3c37f268ba3189a4902b852121e979c70817f (diff)
downloadmana-cf6dd4b0e5efa1f8041d5570497a5228c737c454.tar.xz
mana-cf6dd4b0e5efa1f8041d5570497a5228c737c454.zip
Strings
-rw-r--r--ast/ast.go9
-rw-r--r--evaluator/evaluator.go3
-rw-r--r--evaluator/evaluator_test.go14
-rw-r--r--lexer/lexer.go15
-rw-r--r--lexer/lexer_test.go4
-rw-r--r--object/object.go8
-rw-r--r--parser/parser.go8
-rw-r--r--parser/parser_test.go19
-rw-r--r--tokens/tokens.go5
9 files changed, 83 insertions, 2 deletions
diff --git a/ast/ast.go b/ast/ast.go
index 346d96d..0fe7985 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -297,3 +297,12 @@ func (ce *CallExpression) String() string {
return out.String()
}
+
+type StringLiteral struct {
+ Token tokens.Token
+ Value string
+}
+
+func (sl *StringLiteral) expressionNode() {}
+func (sl *StringLiteral) TokenLiteral() string { return sl.Token.Literal }
+func (sl *StringLiteral) String() string { return sl.Token.Literal }
diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go
index 87bfc48..9129a98 100644
--- a/evaluator/evaluator.go
+++ b/evaluator/evaluator.go
@@ -83,6 +83,9 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
return &object.Function{Parameters: params, Body: body, Env: env}
+ case *ast.StringLiteral:
+ return &object.String{Value: node.Value}
+
case *ast.ReturnStatement:
val := Eval(node.ReturnValue, env)
if isError(val) {
diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go
index 75480dc..cf9e320 100644
--- a/evaluator/evaluator_test.go
+++ b/evaluator/evaluator_test.go
@@ -313,3 +313,17 @@ func testNullObject(t *testing.T, obj object.Object) bool {
}
return true
}
+
+func TestStringLiteral(t *testing.T) {
+ input := `"Hello World!"`
+
+ evaluated := testEval(input)
+ str, ok := evaluated.(*object.String)
+ if !ok {
+ t.Fatalf("object is not String. got=%T (%+v)", evaluated, evaluated)
+ }
+
+ if str.Value != "Hello World!" {
+ t.Errorf("String has wrong value. got=%q", str.Value)
+ }
+}
diff --git a/lexer/lexer.go b/lexer/lexer.go
index 589ed60..5774120 100644
--- a/lexer/lexer.go
+++ b/lexer/lexer.go
@@ -65,6 +65,9 @@ func (l *Lexer) NextToken() tokens.Token {
tok = newToken(tokens.LBRACE, l.ch)
case '}':
tok = newToken(tokens.RBRACE, l.ch)
+ case '"':
+ tok.Type = tokens.STRING
+ tok.Literal = l.readString()
case 0:
tok.Literal = ""
tok.Type = tokens.EOF
@@ -145,3 +148,15 @@ func (l *Lexer) readNumber() string {
}
return l.input[position:l.position]
}
+
+func (l *Lexer) readString() string {
+ position := l.position + 1
+ for {
+ l.readChar()
+ if l.ch == '"' || l.ch == 0 {
+ break
+ }
+ }
+
+ return l.input[position:l.position]
+}
diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go
index 8d2bbe5..7df8db2 100644
--- a/lexer/lexer_test.go
+++ b/lexer/lexer_test.go
@@ -27,6 +27,8 @@ func TestNextToken(t *testing.T) {
10 == 10;
10 != 9;
+ "foobar"
+ "foo bar"
`
var tests = []struct {
@@ -106,6 +108,8 @@ func TestNextToken(t *testing.T) {
{tokens.NOT_EQ, "!="},
{tokens.INT, "9"},
{tokens.SEMICOLON, ";"},
+ {tokens.STRING, "foobar"},
+ {tokens.STRING, "foo bar"},
{tokens.EOF, ""},
}
diff --git a/object/object.go b/object/object.go
index 793ff6b..731be0b 100644
--- a/object/object.go
+++ b/object/object.go
@@ -15,6 +15,7 @@ const (
NULL_OBJ = "NULL"
RETURN_VALUE_OBJ = "RETURN_VALUE"
FUNCTION_OBJ = "FUNCTION"
+ STRING_OBJ = "STRING"
ERROR_OBJ = "ERROR"
)
@@ -47,6 +48,10 @@ type Function struct {
Env *Environment
}
+type String struct {
+ Value string
+}
+
func (i *Integer) Type() ObjectType {
return INTEGER_OBJ
}
@@ -104,3 +109,6 @@ func (f *Function) Inspect() string {
func (e *Error) Type() ObjectType { return ERROR_OBJ }
func (e *Error) Inspect() string { return "ERROR:" + e.Message }
+
+func (s *String) Type() ObjectType { return STRING_OBJ }
+func (s *String) Inspect() string { return s.Value }
diff --git a/parser/parser.go b/parser/parser.go
index 3fe3d9f..6ca88b3 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -71,6 +71,7 @@ func New(l *lexer.Lexer) *Parser {
p.registerPrefix(tokens.IF, p.parseIfExpression)
p.registerPrefix(tokens.FUNCTION, p.parseFunctionLiteral)
p.registerPrefix(tokens.LPAREN, p.parseGroupedExpression)
+ p.registerPrefix(tokens.STRING, p.parseStringLiteral)
// Initialize the infix parse functions.
p.infixParseFns = make(map[tokens.TokenType]infixParseFn)
@@ -436,6 +437,13 @@ func (p *Parser) parseCallArguments() []ast.Expression {
return args
}
+func (p *Parser) parseStringLiteral() ast.Expression {
+ return &ast.StringLiteral{
+ Token: p.curToken,
+ Value: p.curToken.Literal,
+ }
+}
+
// curTokenIs returns true if the current token is of the given type.
func (p *Parser) curTokenIs(t tokens.TokenType) bool {
return p.curToken.Type == t
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 08539b7..3a574a7 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -772,3 +772,22 @@ func TestCallExpressionParsing(t *testing.T) {
testInfixExpression(t, exp.Arguments[1], 2, "*", 3)
testInfixExpression(t, exp.Arguments[2], 4, "+", 5)
}
+
+func TestStringLiteralExpression(t *testing.T) {
+ input := `"hello world";`
+
+ var l *lexer.Lexer = lexer.New(input)
+ var p *Parser = New(l)
+ var program *ast.Program = p.ParseProgram()
+ checkParserErrors(t, p)
+
+ stmt := program.Statements[0].(*ast.ExpressionStatement)
+ literal, ok := stmt.Expression.(*ast.StringLiteral)
+ if !ok {
+ t.Fatalf("exp not *ast.StringLiteral. got=%T", stmt.Expression)
+ }
+
+ if literal.Value != "hello world" {
+ t.Errorf("literal.Value not %q. got=%q", "hello world", literal.Value)
+ }
+}
diff --git a/tokens/tokens.go b/tokens/tokens.go
index 5a74cd4..64f44fa 100644
--- a/tokens/tokens.go
+++ b/tokens/tokens.go
@@ -12,8 +12,9 @@ const (
EOF = "EOF"
// Identifiers + literals
- IDENT = "IDENT"
- INT = "INT"
+ IDENT = "IDENT"
+ INT = "INT"
+ STRING = "STRING"
// Operators
ASSIGN = "="