Add null literal
This commit is contained in:
@@ -134,6 +134,15 @@ func (i *Identifier) expressionNode() {}
|
||||
func (i *Identifier) TokenLiteral() string { return i.Token.Literal }
|
||||
func (i *Identifier) String() string { return i.Value }
|
||||
|
||||
// Null represents a null value
|
||||
type Null struct {
|
||||
Token token.Token
|
||||
}
|
||||
|
||||
func (n *Null) expressionNode() {}
|
||||
func (n *Null) TokenLiteral() string { return n.Token.Literal }
|
||||
func (n *Null) String() string { return n.Token.Literal }
|
||||
|
||||
type Boolean struct {
|
||||
Token token.Token
|
||||
Value bool
|
||||
|
||||
@@ -155,6 +155,9 @@ func (c *Compiler) Compile(node ast.Node) error {
|
||||
integer := &object.Integer{Value: node.Value}
|
||||
c.emit(code.OpConstant, c.addConstant(integer))
|
||||
|
||||
case *ast.Null:
|
||||
c.emit(code.OpNull)
|
||||
|
||||
case *ast.Boolean:
|
||||
if node.Value {
|
||||
c.emit(code.OpTrue)
|
||||
|
||||
@@ -100,6 +100,14 @@ func TestBooleanExpressions(t *testing.T) {
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "null",
|
||||
expectedConstants: []interface{}{},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpNull),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "1 > 2",
|
||||
expectedConstants: []interface{}{1, 2},
|
||||
|
||||
@@ -87,6 +87,9 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
|
||||
case *ast.Boolean:
|
||||
return nativeBoolToBooleanObject(node.Value)
|
||||
|
||||
case *ast.Null:
|
||||
return NULL
|
||||
|
||||
case *ast.PrefixExpression:
|
||||
right := Eval(node.Right, env)
|
||||
if isError(right) {
|
||||
|
||||
@@ -623,6 +623,11 @@ func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestNullExpression(t *testing.T) {
|
||||
evaluated := testEval("null")
|
||||
testNullObject(t, evaluated)
|
||||
}
|
||||
|
||||
func testBooleanObject(t *testing.T, obj object.Object, expected bool) bool {
|
||||
result, ok := obj.(*object.Boolean)
|
||||
if !ok {
|
||||
|
||||
@@ -64,6 +64,7 @@ func New(l *lexer.Lexer) *Parser {
|
||||
p.registerPrefix(token.MINUS, p.parsePrefixExpression)
|
||||
p.registerPrefix(token.TRUE, p.parseBoolean)
|
||||
p.registerPrefix(token.FALSE, p.parseBoolean)
|
||||
p.registerPrefix(token.NULL, p.parseNull)
|
||||
p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
|
||||
p.registerPrefix(token.IF, p.parseIfExpression)
|
||||
p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral)
|
||||
@@ -552,3 +553,7 @@ func (p *Parser) parseAssignmentStatement() ast.Statement {
|
||||
func (p *Parser) parseComment() ast.Statement {
|
||||
return &ast.Comment{Token: p.curToken, Value: p.curToken.Literal}
|
||||
}
|
||||
|
||||
func (p *Parser) parseNull() ast.Expression {
|
||||
return &ast.Null{Token: p.curToken}
|
||||
}
|
||||
|
||||
@@ -458,6 +458,29 @@ func TestIfExpression(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNullExpression(t *testing.T) {
|
||||
l := lexer.New("null")
|
||||
p := New(l)
|
||||
program := p.ParseProgram()
|
||||
checkParserErrors(t, p)
|
||||
|
||||
if len(program.Statements) != 1 {
|
||||
t.Fatalf("program has not enough statements. got=%d",
|
||||
len(program.Statements))
|
||||
}
|
||||
|
||||
stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
|
||||
if !ok {
|
||||
t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
|
||||
program.Statements[0])
|
||||
}
|
||||
|
||||
_, ok = stmt.Expression.(*ast.Null)
|
||||
if !ok {
|
||||
t.Fatalf("exp not *ast.Null. got=%T", stmt.Expression)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIfElseExpression(t *testing.T) {
|
||||
input := `if (x < y) { x } else { y }`
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ const (
|
||||
LET = "LET"
|
||||
TRUE = "TRUE"
|
||||
FALSE = "FALSE"
|
||||
NULL = "NULL"
|
||||
IF = "IF"
|
||||
ELSE = "ELSE"
|
||||
RETURN = "RETURN"
|
||||
@@ -64,6 +65,7 @@ var keywords = map[string]TokenType{
|
||||
"true": TRUE,
|
||||
"false": FALSE,
|
||||
"if": IF,
|
||||
"null": NULL,
|
||||
"else": ELSE,
|
||||
"return": RETURN,
|
||||
"while": WHILE,
|
||||
|
||||
@@ -233,6 +233,7 @@ func TestBooleanExpressions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"true", true},
|
||||
{"false", false},
|
||||
{"null", nil},
|
||||
{"!true", false},
|
||||
{"!false", true},
|
||||
{"!5", false},
|
||||
|
||||
Reference in New Issue
Block a user