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