Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v1] Remove PartiQLValue from AST; refactor AST literals #1650

Merged
merged 7 commits into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 55 additions & 10 deletions partiql-ast/api/partiql-ast.api
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public final class org/partiql/ast/Ast {
public static final fun exprInCollection (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;Z)Lorg/partiql/ast/expr/ExprInCollection;
public static final fun exprIsType (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/DataType;Z)Lorg/partiql/ast/expr/ExprIsType;
public static final fun exprLike (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;Z)Lorg/partiql/ast/expr/ExprLike;
public static final fun exprLit (Lorg/partiql/value/PartiQLValue;)Lorg/partiql/ast/expr/ExprLit;
public static final fun exprLit (Lorg/partiql/ast/literal/Literal;)Lorg/partiql/ast/expr/ExprLit;
public static final fun exprMatch (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/graph/GraphMatch;)Lorg/partiql/ast/expr/ExprMatch;
public static final fun exprNot (Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprNot;
public static final fun exprNullIf (Lorg/partiql/ast/expr/Expr;Lorg/partiql/ast/expr/Expr;)Lorg/partiql/ast/expr/ExprNullIf;
Expand Down Expand Up @@ -233,6 +233,8 @@ public abstract class org/partiql/ast/AstRewriter : org/partiql/ast/AstVisitor {
public fun visitLet (Lorg/partiql/ast/Let;Ljava/lang/Object;)Lorg/partiql/ast/AstNode;
public synthetic fun visitLetBinding (Lorg/partiql/ast/Let$Binding;Ljava/lang/Object;)Ljava/lang/Object;
public fun visitLetBinding (Lorg/partiql/ast/Let$Binding;Ljava/lang/Object;)Lorg/partiql/ast/AstNode;
public synthetic fun visitLiteral (Lorg/partiql/ast/literal/Literal;Ljava/lang/Object;)Ljava/lang/Object;
public fun visitLiteral (Lorg/partiql/ast/literal/Literal;Ljava/lang/Object;)Lorg/partiql/ast/AstNode;
public synthetic fun visitPathStepAllElements (Lorg/partiql/ast/expr/PathStep$AllElements;Ljava/lang/Object;)Ljava/lang/Object;
public fun visitPathStepAllElements (Lorg/partiql/ast/expr/PathStep$AllElements;Ljava/lang/Object;)Lorg/partiql/ast/AstNode;
public synthetic fun visitPathStepAllFields (Lorg/partiql/ast/expr/PathStep$AllFields;Ljava/lang/Object;)Ljava/lang/Object;
Expand Down Expand Up @@ -348,6 +350,7 @@ public abstract class org/partiql/ast/AstVisitor {
public fun visitKeyValue (Lorg/partiql/ast/ddl/KeyValue;Ljava/lang/Object;)Ljava/lang/Object;
public fun visitLet (Lorg/partiql/ast/Let;Ljava/lang/Object;)Ljava/lang/Object;
public fun visitLetBinding (Lorg/partiql/ast/Let$Binding;Ljava/lang/Object;)Ljava/lang/Object;
public fun visitLiteral (Lorg/partiql/ast/literal/Literal;Ljava/lang/Object;)Ljava/lang/Object;
public fun visitNullable (Lorg/partiql/ast/ddl/AttributeConstraint$Null;Ljava/lang/Object;)Ljava/lang/Object;
public fun visitOrderBy (Lorg/partiql/ast/OrderBy;Ljava/lang/Object;)Ljava/lang/Object;
public fun visitPartitionBy (Lorg/partiql/ast/ddl/PartitionBy;Ljava/lang/Object;)Ljava/lang/Object;
Expand Down Expand Up @@ -1657,22 +1660,15 @@ public class org/partiql/ast/expr/ExprLike$Builder {
}

public class org/partiql/ast/expr/ExprLit : org/partiql/ast/expr/Expr {
public final field value Lorg/partiql/value/PartiQLValue;
public fun <init> (Lorg/partiql/value/PartiQLValue;)V
public field lit Lorg/partiql/ast/literal/Literal;
public fun <init> (Lorg/partiql/ast/literal/Literal;)V
public fun accept (Lorg/partiql/ast/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object;
public static fun builder ()Lorg/partiql/ast/expr/ExprLit$Builder;
protected fun canEqual (Ljava/lang/Object;)Z
public fun children ()Ljava/util/Collection;
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
}

public class org/partiql/ast/expr/ExprLit$Builder {
public fun build ()Lorg/partiql/ast/expr/ExprLit;
public fun toString ()Ljava/lang/String;
public fun value (Lorg/partiql/value/PartiQLValue;)Lorg/partiql/ast/expr/ExprLit$Builder;
}

public class org/partiql/ast/expr/ExprMatch : org/partiql/ast/expr/Expr {
public final field expr Lorg/partiql/ast/expr/Expr;
public final field pattern Lorg/partiql/ast/graph/GraphMatch;
Expand Down Expand Up @@ -2569,6 +2565,55 @@ public class org/partiql/ast/graph/GraphSelector$ShortestKGroup$Builder {
public fun toString ()Ljava/lang/String;
}

public abstract class org/partiql/ast/literal/Literal : org/partiql/ast/AstNode {
public fun <init> ()V
public fun accept (Lorg/partiql/ast/AstVisitor;Ljava/lang/Object;)Ljava/lang/Object;
public fun bigDecimalValue ()Ljava/math/BigDecimal;
public fun booleanValue ()Z
public fun children ()Ljava/util/Collection;
public fun dataType ()Lorg/partiql/ast/DataType;
public abstract fun kind ()Lorg/partiql/ast/literal/LiteralKind;
public static fun litApprox (Ljava/lang/String;)Lorg/partiql/ast/literal/Literal;
public static fun litBool (Z)Lorg/partiql/ast/literal/Literal;
public static fun litExact (Ljava/lang/String;)Lorg/partiql/ast/literal/Literal;
public static fun litExact (Ljava/math/BigDecimal;)Lorg/partiql/ast/literal/Literal;
public static fun litInt (I)Lorg/partiql/ast/literal/Literal;
public static fun litInt (J)Lorg/partiql/ast/literal/Literal;
public static fun litInt (Ljava/lang/String;)Lorg/partiql/ast/literal/Literal;
public static fun litInt (Ljava/math/BigInteger;)Lorg/partiql/ast/literal/Literal;
public static fun litMissing ()Lorg/partiql/ast/literal/Literal;
public static fun litNull ()Lorg/partiql/ast/literal/Literal;
public static fun litString (Ljava/lang/String;)Lorg/partiql/ast/literal/Literal;
public static fun litTypedString (Lorg/partiql/ast/DataType;Ljava/lang/String;)Lorg/partiql/ast/literal/Literal;
public fun numberValue ()Ljava/lang/String;
public fun stringValue ()Ljava/lang/String;
}

public class org/partiql/ast/literal/LiteralKind {
public static final field BOOLEAN I
public static final field MISSING I
public static final field NULL I
public static final field NUM_APPROX I
public static final field NUM_EXACT I
public static final field NUM_INT I
public static final field STRING I
public static final field TYPED_STRING I
public static final field UNKNOWN I
public static fun BOOLEAN ()Lorg/partiql/ast/literal/LiteralKind;
public static fun MISSING ()Lorg/partiql/ast/literal/LiteralKind;
public static fun NULL ()Lorg/partiql/ast/literal/LiteralKind;
public static fun NUM_APPROX ()Lorg/partiql/ast/literal/LiteralKind;
public static fun NUM_EXACT ()Lorg/partiql/ast/literal/LiteralKind;
public static fun NUM_INT ()Lorg/partiql/ast/literal/LiteralKind;
public static fun STRING ()Lorg/partiql/ast/literal/LiteralKind;
public static fun TYPED_STRING ()Lorg/partiql/ast/literal/LiteralKind;
public static fun UNKNOWN ()Lorg/partiql/ast/literal/LiteralKind;
protected fun canEqual (Ljava/lang/Object;)Z
public fun code ()I
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
}

public abstract class org/partiql/ast/sql/SqlBlock {
public static final field Companion Lorg/partiql/ast/sql/SqlBlock$Companion;
public field next Lorg/partiql/ast/sql/SqlBlock;
Expand Down
4 changes: 0 additions & 4 deletions partiql-ast/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ plugins {

dependencies {
api(Deps.ionElement)
api(project(":partiql-types"))
// TODO REMOVE ME ONCE PartiQLValue IS REMOVED
// THE AST NEEDS ITS OWN "VALUE" REPRESENTATION
api(project(":partiql-spi"))
Comment on lines -23 to -26
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(self-review) partiql-ast now will not depend on other packages. Had to add the spi dependency to partiql-parser following this change

compileOnly(Deps.lombok)
annotationProcessor(Deps.lombok)
}
Expand Down
5 changes: 5 additions & 0 deletions partiql-ast/src/main/java/org/partiql/ast/AstVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.partiql.ast.graph.GraphPattern;
import org.partiql.ast.graph.GraphQuantifier;
import org.partiql.ast.graph.GraphSelector;
import org.partiql.ast.literal.Literal;

// TODO docs
// Also include docs on how a library user could create a new variant for sum types and which methods to override
Expand Down Expand Up @@ -292,6 +293,10 @@ public R visitExprWindowOver(ExprWindow.Over node, C ctx) {
return defaultVisit(node, ctx);
}

public R visitLiteral(Literal node, C ctx) {
return defaultVisit(node, ctx);
}

public R visitQueryBody(QueryBody node, C ctx) {
return node.accept(this, ctx);
}
Expand Down
7 changes: 3 additions & 4 deletions partiql-ast/src/main/java/org/partiql/ast/Explain.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import lombok.Builder;
import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull;
import org.partiql.value.PartiQLValue;
import org.partiql.ast.literal.Literal;

import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -16,14 +16,13 @@
@Builder(builderClassName = "Builder")
@EqualsAndHashCode(callSuper = false)
public class Explain extends Statement {
// TODO get rid of PartiQLValue once https://github.com/partiql/partiql-lang-kotlin/issues/1589 is resolved
@NotNull
public final Map<String, PartiQLValue> options;
public final Map<String, Literal> options;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(self-review) model the EXPLAIN option map values w/ a Literal directly. Could also opt for ExprLit but wasn't sure

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Literal makes sense


@NotNull
public final Statement statement;

public Explain(@NotNull Map<String, PartiQLValue> options, @NotNull Statement statement) {
public Explain(@NotNull Map<String, Literal> options, @NotNull Statement statement) {
this.options = options;
this.statement = statement;
}
Expand Down
19 changes: 10 additions & 9 deletions partiql-ast/src/main/java/org/partiql/ast/expr/ExprLit.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
package org.partiql.ast.expr;

import lombok.Builder;
import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull;
import org.partiql.ast.AstNode;
import org.partiql.ast.AstVisitor;
import org.partiql.value.PartiQLValue;
import org.partiql.ast.literal.Literal;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
* TODO docs, equals, hashcode
*/
@Builder(builderClassName = "Builder")
@EqualsAndHashCode(callSuper = false)
public class ExprLit extends Expr {
@NotNull
public final PartiQLValue value; // This representation be changed in https://github.com/partiql/partiql-lang-kotlin/issues/1589
public Literal lit;

public ExprLit(@NotNull PartiQLValue value) {
this.value = value;
public ExprLit(@NotNull Literal lit) {
this.lit = lit;
}

@Override
@NotNull
@Override
public Collection<AstNode> children() {
return Collections.emptyList();
List<AstNode> kids = new ArrayList<>();
kids.add(lit);
return kids;
}

@Override
Expand Down
125 changes: 125 additions & 0 deletions partiql-ast/src/main/java/org/partiql/ast/literal/Literal.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package org.partiql.ast.literal;

import org.jetbrains.annotations.NotNull;
import org.partiql.ast.AstNode;
import org.partiql.ast.AstVisitor;
import org.partiql.ast.DataType;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;

public abstract class Literal extends AstNode {
Copy link
Member Author

@alancai98 alancai98 Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latest design currently models things a bit differently than before. Key summary is

  • Fat interface w/ a LiteralKind enum. Includes relevant constructors and property accessor methods. much like PType, Datum, AnyElement, and Calcite's SqlLiteral
  • Three numeric variants (approx, int, exact) with each containing just a string
    • The parser will still perform some type ascription
    • Modeling certain numerics with a string proved less cumbersome. E.g.
      • 123 vs 123. -- BigDecimal does not actually preserve whether there was a period or not
      • 123e0 vs 123.e0 -- BigDecimal for the mantissa of an approximate does not preserve whether there was a period or not
      • 123e0 vs 123.e+0 -- a long/integer for the exponent does not preserve whether there was an explicit +
    • Simplifying to just a string allows us to hold off on any eager computation
  • Date/time/timestamp represented as a typed string
    • The parser already performs some validations and basic type ascription.
    • One potential issue with eagerly converting the fields into java is all of the additional properties that would need added. E.g. date would need year, month, day. Time would need hour, minute, second, precision, timezone. Timestamp would need all of date and time's fields. We would need to at some point convert these AST fields to the plan's representation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One potential issue with eagerly converting the fields into java is all of the additional properties that would need added. E.g. date would need year, month, day. Time would need hour, minute, second, precision, timezone. Timestamp would need all of date and time's fields. We would need to at some point convert these AST fields to the plan's representation.

That's a good point, and I didn't think of that. We wouldn't be able to zero-pad and would need to insert nulls for those fields. Seems like a lot of extra work (on read aka scribe) when a (rule,string) is sufficient for capturing syntax which ultimately is the goal.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

capturing syntax which ultimately is the goal

Yeah that was my mindset w/ this latest refactor -- AST at the end of the day just captures syntax with some slight validation w/ the parser. I think most users are not actually performing computation directly on the AST. Computation (like in our case) will come later in the plan or even pushed all the way to the evaluator.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the flexibility PType provides with the fat interface, I explicitly wrote in the Javadocs that consumers of this should not author their own implementations. See

* Users should NOT author their own implementation. The current recommendation is to use the static methods
* (exposed by this interface) to instantiate a type.
.

Could add that to this API as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up changing this to not be an abstract class and a concrete class extending AstEnum (like DataType) -- #1650 (comment). So adding another implementation shouldn't be an issue anymore.

@NotNull
public abstract LiteralKind kind();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly a fat-interface but similar pattern! We are doing a tagged-union, and we don't actually want a LiteralKind enum and the code (aka tag) should go directly on the Literal (like we did with DataType).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try using the code directly on the Literal. I had ran into some slightly repetitive + confusing code pattern when using a LiteralKind:

val litText = when (lit.kind().code()) {
    LiteralKind.NULL -> "NULL"
    LiteralKind.MISSING -> "MISSING"
    LiteralKind.BOOLEAN -> lit.booleanValue().toString()
    ...
    else -> error("Unsupported literal kind ${lit.kind()}")
}

^ lit.kind().code() rather than just lit.code().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to be more like DataType in the lateset commit.


@Override
@NotNull
public Collection<AstNode> children() {
return Collections.emptyList();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a quick follow-up PR, could you please change this to

public List<AstNode> getChildren() {
    ...
}

This way it has ordered semantics and the getFoo while clunky (imo) plays better with kotlin accessors.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I'll track this as a followup in #1610.


@Override
public <R, C> R accept(@NotNull AstVisitor<R, C> visitor, C ctx) {
return visitor.visitLiteral(this, ctx);
}

// Factory methods
public static Literal litApprox(@NotNull String value) {
return new LiteralApprox(value);
}

public static Literal litBool(boolean value) {
return new LiteralBool(value);
}

public static Literal litExact(@NotNull BigDecimal value) {
if (value.scale() == 0) {
return new LiteralExact(value + ".");
} else {
return new LiteralExact(value.toString());
}
}

public static Literal litExact(@NotNull String value) {
return new LiteralExact(value);
}

public static Literal litInt(int value) {
return new LiteralInt(Integer.toString(value));
}

public static Literal litInt(long value) {
return new LiteralInt(Long.toString(value));
}

public static Literal litInt(@NotNull BigInteger value) {
return new LiteralInt(value.toString());
}

public static Literal litInt(@NotNull String value) {
return new LiteralInt(value);
}

public static Literal litNull() {
return new LiteralNull();
}

public static Literal litMissing() {
return new LiteralMissing();
}

public static Literal litString(@NotNull String value) {
return new LiteralString(value);
}

public static Literal litTypedString(@NotNull DataType type, @NotNull String value) {
return new LiteralTypedString(type, value);
}
alancai98 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to be as restrictive as possible, you can have explicit time(boolean withTimeZone, int precision, @NotNull String value) for the datetime values, rather than the typed string allowing all types.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think there's some value in having a public factory method to construct typed string literals w/ arguments DataType and String. For instance, if an API user had a DataType already (date, time, timetz, timestamp, timestamptz), they could simply call this factory method. If we only make the specific typed string methods public (e.g. time(boolean withTimeZone, int precision, @NotNull String value), then they would need to perform additional conversion logic.

We could always add such methods in the future.


// Value extraction
/**
* TODO docs
* Valid for just LiteralBool
*/
public boolean booleanValue() {
throw new UnsupportedOperationException();
}

/**
* TODO docs
* Valid for just LiteralApprox, LiteralInt, and LiteralExact
*/
@NotNull
public String numberValue() {
throw new UnsupportedOperationException();
}

/**
* TODO docs
* Valid for just LiteralInt and LiteralExact
*/
@NotNull
public BigDecimal bigDecimalValue() {
throw new UnsupportedOperationException();
}

/**
* TODO docs
* Valid for just LiteralString and LiteralTypedString
*/
@NotNull
public String stringValue() {
throw new UnsupportedOperationException();
}

/**
* TODO docs
* Valid for just LiteralTypedString
*/
@NotNull
public DataType dataType() {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.partiql.ast.literal;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discussed a different modeling with @RCHowell using just one class rather than three for the numerics (LiteralApprox, LiteralExact, LiteralInt) that used a enum/kind to distinguish between the three numeric types.

public class LiteralNumber extends Literal {
    @Nullable
    private final Long p1;

    @Nullable
    private final BigDecimal p2;

    @NotNull
    public final ParseContext kind;

    private LiteralNumber(@Nullable Long p1, @Nullable BigDecimal p2, @NotNull ParseContext kind) {
        this.p1 = p1;
        this.p2 = p2;
        this.kind = kind;
    }

    // Factory methods
    public static LiteralNumber integer(long value) {
        return new LiteralNumber(value, null, ParseContext.INTEGER);
    }

    public static LiteralNumber integer(int value) {
        return new LiteralNumber((long) value, null, ParseContext.INTEGER);
    }

    public static LiteralNumber exact(BigDecimal value) {
        return new LiteralNumber(null, value, ParseContext.EXACT);
    }

    public static LiteralNumber approx(BigDecimal value, long exponent) {
        return new LiteralNumber(exponent, value, ParseContext.APPROX);
    }

    public static LiteralNumber approx(BigDecimal value) {
        return new LiteralNumber(null, value, ParseContext.APPROX);
    }

    public static LiteralNumber approx(float value) {
        return approx(BigDecimal.valueOf(value));
    }

    public static LiteralNumber approx(double value) {
        return approx(BigDecimal.valueOf(value));
    }

    // Getting the value out
    @NotNull
    public BigDecimal getDecimal() {
        if (kind == ParseContext.INTEGER) {
            return p2;
        } else {
            throw new IllegalStateException("Unknown context: " + kind);
        }
    }

    public long getInteger() {
        if (kind == ParseContext.INTEGER) {
            return p1;
        } else {
            throw new IllegalStateException("Unknown context: " + kind);
        }
    }

    // similarly for getDouble

    // TODO if we keep this representation, change to extend `AstEnum`
    public enum ParseContext {
        INTEGER,
        EXACT,
        APPROX
    }

    @NotNull
    @Override
    public String getText() {
        switch (kind) {
            // Since they're nullable, can be slightly annoying to extract the value but it's internal code
            case INTEGER:
                assert p1 != null;
                return p1.toString();
            case EXACT:
                assert p2 != null;
                return p2.toString();
            case APPROX:
                assert p1 != null;
                assert p2 != null;
                return p2 + "e" + p1;
            default:
                throw new IllegalStateException("Unknown context: " + kind);
        }
    }
}

and if we wanted to internalize the enum, could add some additional methods

    public boolean isInteger() {
        return kind == ParseContext.INTEGER;
    }

    public boolean isExact() {
        return kind == ParseContext.EXACT;
    }

    public boolean isApprox() {
        return kind == ParseContext.APPROX;
    }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo, the customer code using these different modelings is pretty similar. But I feel like the internal implementation code is simpler if we break apart the LiteralNumeric class into separate classes since we don't have to do the null checks or casing on the enum/kind.

Using the single class with an enum

val v = when (lit) {
    is LiteralNumber -> {
        val kind = lit.kind
        when (kind) {
            LiteralNumber.ParseContext.EXACT -> {
                lit.decimal
            }
            LiteralNumber.ParseContext.INTEGER -> {
                lit.integer
            }
            LiteralNumber.ParseContext.APPROX -> {
                lit.double
            }
            else -> error("Unexpected numeric literal: $lit")
        }
    }
    else -> error("Unexpected literal: $lit")
}

Not using the enum directly but using helper methods

val v2 = when (lit) {
    is LiteralNumber -> {
        if (lit.isExact) {
            lit.decimal
        }
        else if (lit.isInteger) {
            lit.integer
        }
        else if (lit.isApprox) {
            lit.double
        } else {
            error("Unexpected numeric literal: $lit")
        }
    }
    else -> error("Unexpected literal: $lit")
}

Using current PR's three classes

val v3 = when (lit) {
    is LiteralInteger -> lit.integer
    is LiteralExact -> lit.decimal
    is LiteralApprox -> lit.double
    else -> error("Unexpected literal: $lit")
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sent a comment elsewhere, but we don't have to do the null checks. This is internal code which will throw a NPE if we have a bug whereas now it throws an assertion exception, again if we have a bug. The checks don't gain us anything because an exception is thrown either way.


import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull;

@EqualsAndHashCode(callSuper = false)
class LiteralApprox extends Literal {
@NotNull
String value;

LiteralApprox(@NotNull String value) {
this.value = value;
}

@NotNull
@Override
public String numberValue() {
return value;
}

@NotNull
@Override
public LiteralKind kind() {
return LiteralKind.NUM_APPROX();
}
}
24 changes: 24 additions & 0 deletions partiql-ast/src/main/java/org/partiql/ast/literal/LiteralBool.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.partiql.ast.literal;

import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull;

@EqualsAndHashCode(callSuper = false)
class LiteralBool extends Literal {
private final boolean value;

LiteralBool(boolean value) {
this.value = value;
}

@Override
public boolean booleanValue() {
return value;
}

@NotNull
@Override
public LiteralKind kind() {
return LiteralKind.BOOLEAN();
}
}
Loading
Loading