Skip to content

Commit

Permalink
refactor: Introduce numeric literal specific type seperate to non lit…
Browse files Browse the repository at this point in the history
…eral number/bigint types
  • Loading branch information
tristanmenzel committed Oct 8, 2024
1 parent fcb12c1 commit 23a7355
Show file tree
Hide file tree
Showing 20 changed files with 2,304 additions and 117 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"dev:tealscript": "tsx src/cli.ts build examples/tealscript/example.algo.ts",
"dev:approvals": "tsx src/cli.ts build tests/approvals --output-awst --output-awst-json --dry-run",
"dev:expected-output": "tsx src/cli.ts build tests/expected-output --dry-run",
"dev:testing": "tsx src/cli.ts build tests/approvals/array-literals.algo.ts --output-awst --output-awst-json --dry-run",
"dev:testing": "tsx src/cli.ts build tests/approvals/named-types.algo.ts --output-awst --output-awst-json --log-level=info",
"audit": "better-npm-audit audit",
"format": "prettier --write .",
"lint": "eslint \"src/**/*.ts\"",
Expand Down
15 changes: 12 additions & 3 deletions src/awst/intrinsic-factory.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { DeliberateAny } from '../typescript-helpers'
import { bigIntToUint8Array } from '../util'
import { nodeFactory } from './node-factory'
import type * as awst from './nodes'
import * as awst from './nodes'
import type { Expression } from './nodes'
import { BytesEncoding } from './nodes'
import type { SourceLocation } from './source-location'
import * as wtypes from './wtypes'

Expand Down Expand Up @@ -62,7 +64,14 @@ export const intrinsicFactory = {
wtype: wtypes.uint64WType,
})
},
itob({ value, sourceLocation }: { value: awst.Expression; sourceLocation: SourceLocation }): awst.IntrinsicCall {
itob({ value, sourceLocation }: { value: awst.Expression; sourceLocation: SourceLocation }): awst.Expression {
if (value instanceof awst.IntegerConstant) {
return nodeFactory.bytesConstant({
sourceLocation,
value: bigIntToUint8Array(value.value),
encoding: BytesEncoding.base16,
})
}
return nodeFactory.intrinsicCall({
sourceLocation,
stackArgs: [value],
Expand All @@ -80,4 +89,4 @@ export const intrinsicFactory = {
opCode: 'btoi',
})
},
} satisfies Record<string, (args: DeliberateAny) => awst.IntrinsicCall>
} satisfies Record<string, (args: DeliberateAny) => awst.Expression>
16 changes: 8 additions & 8 deletions src/awst/node-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,40 +57,40 @@ const explicitNodeFactory = {
},
uInt64Constant(props: { value: bigint; tealAlias?: string; sourceLocation: SourceLocation }): IntegerConstant {
return new IntegerConstant({
wtype: wtypes.uint64WType,
...props,
wtype: wtypes.uint64WType,
tealAlias: props.tealAlias ?? null,
})
},
bigUIntConstant(props: { value: bigint; sourceLocation: SourceLocation }): IntegerConstant {
return new IntegerConstant({
wtype: wtypes.biguintWType,
...props,
wtype: wtypes.biguintWType,
tealAlias: null,
})
},
not(props: { expr: Expression; sourceLocation: SourceLocation }): Not {
return new Not({
wtype: boolWType,
...props,
wtype: boolWType,
})
},
uInt64BinaryOperation(props: Omit<Props<UInt64BinaryOperation>, 'wtype'>): UInt64BinaryOperation {
return new UInt64BinaryOperation({
wtype: wtypes.uint64WType,
...props,
wtype: wtypes.uint64WType,
})
},
bigUIntBinaryOperation(props: Omit<Props<BigUIntBinaryOperation>, 'wtype'>): BigUIntBinaryOperation {
return new BigUIntBinaryOperation({
wtype: wtypes.biguintWType,
...props,
wtype: wtypes.biguintWType,
})
},
numericComparisonExpression(props: Omit<Props<NumericComparisonExpression>, 'wtype'>): NumericComparisonExpression {
return new NumericComparisonExpression({
wtype: boolWType,
...props,
wtype: boolWType,
})
},
bytesComparisonExpression(props: Omit<Props<BytesComparisonExpression>, 'wtype'>): BytesComparisonExpression {
Expand All @@ -100,14 +100,14 @@ const explicitNodeFactory = {
props.sourceLocation,
)
return new BytesComparisonExpression({
wtype: boolWType,
...props,
wtype: boolWType,
})
},
boolConstant(props: { value: boolean; sourceLocation: SourceLocation }): BoolConstant {
return new BoolConstant({
wtype: wtypes.boolWType,
...props,
wtype: wtypes.boolWType,
})
},
singleEvaluation({ source }: { source: Expression }) {
Expand Down
17 changes: 14 additions & 3 deletions src/awst/wtypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,18 @@ export abstract class ARC4Type extends WType {
}
}

export class WStructType extends WType {}
export class WStructType extends WType {
fields: Record<string, WType>

constructor({ fields, name }: { fields: Record<string, WType>; name: string }) {
super({
name,
scalarType: null,
immutable: true,
})
this.fields = fields
}
}

export class WTuple extends WType {
types: WType[]
Expand All @@ -136,14 +147,14 @@ export class WTuple extends WType {
}
}
export class WArray extends WType {
readonly itemType: WType
readonly elementType: WType
constructor(props: { itemType: WType; immutable: boolean }) {
super({
name: 'WArray',
scalarType: null,
immutable: props.immutable,
})
this.itemType = props.itemType
this.elementType = props.itemType
}
}

Expand Down
26 changes: 18 additions & 8 deletions src/awst_build/base-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ import type { PType } from './ptypes'
import {
ArrayLiteralPType,
ArrayPType,
BigIntPType,
BigIntLiteralPType,
bigIntPType,
biguintPType,
boolPType,
NumberPType,
numberPType,
NumericLiteralPType,
ObjectPType,
TransientType,
TuplePType,
Expand Down Expand Up @@ -68,7 +70,9 @@ export abstract class BaseVisitor implements Visitor<Expressions, NodeBuilder> {

visitBigIntLiteral(node: ts.BigIntLiteral): InstanceBuilder {
const literalValue = BigInt(node.text.slice(0, -1))
return new BigIntLiteralExpressionBuilder(literalValue, new BigIntPType({ literalValue }), this.sourceLocation(node))
const ptype = this.context.getPTypeForNode(node)
invariant(ptype instanceof TransientType, 'Literals should resolve to transient PTypes')
return new BigIntLiteralExpressionBuilder(literalValue, ptype, this.sourceLocation(node))
}

visitRegularExpressionLiteral(node: ts.RegularExpressionLiteral): InstanceBuilder {
Expand Down Expand Up @@ -97,7 +101,9 @@ export abstract class BaseVisitor implements Visitor<Expressions, NodeBuilder> {

visitNumericLiteral(node: ts.NumericLiteral): InstanceBuilder {
const literalValue = BigInt(node.text)
return new BigIntLiteralExpressionBuilder(literalValue, new NumberPType({ literalValue }), this.sourceLocation(node))
const ptype = this.context.getPTypeForNode(node)
invariant(ptype instanceof TransientType, 'Literals should resolve to transient PTypes')
return new BigIntLiteralExpressionBuilder(literalValue, ptype, this.sourceLocation(node))
}

visitIdentifier(node: ts.Identifier): NodeBuilder {
Expand Down Expand Up @@ -490,15 +496,19 @@ export abstract class BaseVisitor implements Visitor<Expressions, NodeBuilder> {
return targetType
}
if (
sourceType instanceof NumberPType ||
(sourceType instanceof UnionPType && sourceType.types.every((t) => t.equals(uint64PType) || t instanceof NumberPType))
sourceType instanceof NumericLiteralPType ||
sourceType.equals(numberPType) ||
(sourceType instanceof UnionPType &&
sourceType.types.every((t) => t.equals(uint64PType) || t instanceof NumericLiteralPType || sourceType.equals(numberPType)))
) {
// Narrow `uint64 | number` or `number` to target type
return targetType
}
if (
sourceType instanceof BigIntPType ||
(sourceType instanceof UnionPType && sourceType.types.every((t) => t.equals(biguintPType) || t instanceof BigIntPType))
sourceType.equals(bigIntPType) ||
sourceType instanceof BigIntLiteralPType ||
(sourceType instanceof UnionPType &&
sourceType.types.every((t) => t.equals(biguintPType) || t instanceof BigIntLiteralPType || t.equals(bigIntPType)))
) {
// Narrow `biguint | bigint` or `bigint` to target type
return targetType
Expand Down
4 changes: 2 additions & 2 deletions src/awst_build/eb/arc4/arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { uint64WType } from '../../../awst/wtypes'
import { logger } from '../../../logger'
import { codeInvariant, invariant } from '../../../util'
import type { PType } from '../../ptypes'
import { IterableIteratorType, NumberPType, TuplePType, uint64PType } from '../../ptypes'
import { IterableIteratorType, NumericLiteralPType, TuplePType, uint64PType } from '../../ptypes'
import {
ARC4EncodedType,
DynamicArrayConstructor,
Expand Down Expand Up @@ -65,7 +65,7 @@ export class StaticArrayConstructorBuilder extends NodeBuilder {
})
codeInvariant(elementType instanceof ARC4EncodedType, 'Element type must be an ARC4 encoded type', sourceLocation)
codeInvariant(
arraySize instanceof NumberPType && arraySize.literalValue !== undefined,
arraySize instanceof NumericLiteralPType,
`Array size type parameter of ${this.typeDescription} must be a literal number. Inferred type is ${arraySize.name}`,
sourceLocation,
)
Expand Down
4 changes: 2 additions & 2 deletions src/awst_build/eb/arc4/uint-n-constructor-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { IntegerConstant } from '../../../awst/nodes'
import type { SourceLocation } from '../../../awst/source-location'
import { bigIntToUint8Array, codeInvariant, invariant } from '../../../util'
import type { PType } from '../../ptypes'
import { biguintPType, NumberPType, uint64PType } from '../../ptypes'
import { biguintPType, NumericLiteralPType, uint64PType } from '../../ptypes'
import { UintNType } from '../../ptypes/arc4-types'
import { instanceEb } from '../../type-registry'
import type { InstanceBuilder } from '../index'
Expand All @@ -30,7 +30,7 @@ export class UintNConstructorBuilder extends NodeBuilder {
callLocation: sourceLocation,
})
codeInvariant(
size instanceof NumberPType && size.literalValue !== undefined,
size instanceof NumericLiteralPType,
`Generic type of ${this.typeDescription} must be a literal number. Inferred type is ${size.name}`,
sourceLocation,
)
Expand Down
41 changes: 31 additions & 10 deletions src/awst_build/eb/biguint-expression-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,60 @@ import type { awst } from '../../awst'
import { intrinsicFactory } from '../../awst/intrinsic-factory'
import { nodeFactory } from '../../awst/node-factory'
import type { Expression } from '../../awst/nodes'
import { BigUIntBinaryOperator, BigUIntPostfixUnaryOperator, NumericComparison } from '../../awst/nodes'
import { BigUIntBinaryOperator, BigUIntPostfixUnaryOperator, IntegerConstant, NumericComparison } from '../../awst/nodes'
import type { SourceLocation } from '../../awst/source-location'
import { CodeError, NotSupported } from '../../errors'
import { NotSupported } from '../../errors'
import { logger } from '../../logger'
import { tryConvertEnum } from '../../util'
import type { InstanceType, PType } from '../ptypes'
import { BigUintFunction, biguintPType } from '../ptypes'
import { BigUintFunction, biguintPType, uint64PType } from '../ptypes'
import { BooleanExpressionBuilder } from './boolean-expression-builder'
import type { InstanceBuilder } from './index'
import { BuilderBinaryOp, BuilderComparisonOp, BuilderUnaryOp, FunctionBuilder, InstanceExpressionBuilder } from './index'
import { LiteralExpressionBuilder } from './literal-expression-builder'
import { UInt64ExpressionBuilder } from './uint64-expression-builder'
import { requireExpressionOfType } from './util'
import { parseFunctionArgs } from './util/arg-parsing'

export class BigUintFunctionBuilder extends FunctionBuilder {
readonly ptype = BigUintFunction

call(args: Array<InstanceBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
if (args.length === 0) {
const {
args: [initialValue],
} = parseFunctionArgs({
args,
typeArgs,
genericTypeArgs: 0,
callLocation: sourceLocation,
funcName: 'BigUInt',
argSpec: (a) => [a.optional(uint64PType, biguintPType)],
})
if (!initialValue) {
return new BigUintExpressionBuilder(
nodeFactory.bigUIntConstant({
sourceLocation,
value: 0n,
}),
)
}
if (args.length === 1) {
const [arg0] = args
if (arg0 instanceof LiteralExpressionBuilder) {
return arg0.resolveToPType(biguintPType)
if (initialValue.ptype.equals(uint64PType)) {
const expr = initialValue.resolve()
if (expr instanceof IntegerConstant) {
return new BigUintExpressionBuilder(
nodeFactory.bigUIntConstant({
...expr,
}),
)
}
return new BigUintExpressionBuilder(
nodeFactory.reinterpretCast({
expr: initialValue.toBytes(sourceLocation),
sourceLocation,
wtype: biguintPType.wtype,
}),
)
}
throw CodeError.unexpectedUnhandledArgs({ sourceLocation })
return initialValue
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/awst_build/eb/bytes-expression-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { CodeError, wrapInCodeError } from '../../errors'
import { logger } from '../../logger'
import { base32ToUint8Array, base64ToUint8Array, hexToUint8Array, uint8ArrayToUtf8, utf8ToUint8Array } from '../../util'
import type { InstanceType, PType } from '../ptypes'
import { BytesFunction, bytesPType, NumberPType, stringPType, uint64PType } from '../ptypes'
import { BytesFunction, bytesPType, NumericLiteralPType, stringPType, uint64PType } from '../ptypes'
import { instanceEb } from '../type-registry'
import type { BuilderComparisonOp, InstanceBuilder, NodeBuilder } from './index'
import { BuilderUnaryOp, FunctionBuilder, InstanceExpressionBuilder, ParameterlessFunctionBuilder } from './index'
Expand Down Expand Up @@ -129,7 +129,7 @@ export class BytesExpressionBuilder extends InstanceExpressionBuilder<InstanceTy
sourceLocation,
`The '~' ${this.typeDescription} operator coerces the target value to a number type. Use {bytes expression}.bitwiseInvert() instead`,
)
return new BigIntLiteralExpressionBuilder(0n, new NumberPType({ literalValue: 0n }), sourceLocation)
return new BigIntLiteralExpressionBuilder(0n, new NumericLiteralPType({ literalValue: 0n }), sourceLocation)
}
return super.prefixUnaryOp(op, sourceLocation)
}
Expand Down
Loading

0 comments on commit 23a7355

Please sign in to comment.