Add null literal
Some checks failed
Build / build (push) Successful in 1m30s
Test / build (push) Has been cancelled

This commit is contained in:
Chuck Smith
2024-03-19 16:33:13 -04:00
parent 60d27f09d7
commit 97eeae0c1a
9 changed files with 59 additions and 0 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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},

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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}
}

View File

@@ -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 }`

View File

@@ -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,

View File

@@ -233,6 +233,7 @@ func TestBooleanExpressions(t *testing.T) {
tests := []vmTestCase{
{"true", true},
{"false", false},
{"null", nil},
{"!true", false},
{"!false", true},
{"!5", false},