Skip to content

Commit

Permalink
feat: Handle ARC4 bool and string
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanmenzel committed Nov 12, 2024
1 parent 350daf7 commit ce32257
Show file tree
Hide file tree
Showing 22 changed files with 1,573 additions and 414 deletions.
2 changes: 1 addition & 1 deletion packages/algo-ts/src/arc4/encoded-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ abstract class AbiEncoded implements BytesBacked {
}

export class Str extends AbiEncoded {
constructor(s: StringCompat) {
constructor(s?: StringCompat) {
super()
}
get native(): string {
Expand Down
4 changes: 2 additions & 2 deletions src/awst/node-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ const explicitNodeFactory = {
...props,
})
},
stringConstant(props: { value: string; sourceLocation: SourceLocation }): StringConstant {
stringConstant(props: { value: string; sourceLocation: SourceLocation; wtype?: wtypes.WType }): StringConstant {
return new StringConstant({
...props,
wtype: wtypes.stringWType,
wtype: props.wtype ?? wtypes.stringWType,
})
},
uInt64Constant({
Expand Down
2 changes: 1 addition & 1 deletion src/awst/to-code-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class ToCodeVisitor
return `${expression.base.accept(this)}.push(...${expression.other.accept(this)}`
}
visitARC4Decode(expression: nodes.ARC4Decode): string {
return `ARC4_DECODE(${expression.value})`
return `ARC4_DECODE(${expression.value.accept(this)})`
}
visitIntrinsicCall(expression: nodes.IntrinsicCall): string {
const immediates = expression.immediates.length ? `<${expression.immediates.map((i) => i).join(', ')}>` : ''
Expand Down
5 changes: 3 additions & 2 deletions src/awst_build/base-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export abstract class BaseVisitor implements Visitor<Expressions, NodeBuilder> {
const sourceLocation = this.sourceLocation(node)
const eb = this.baseAccept(node.expression)
const args = node.arguments?.map((a) => this.baseAccept(a)) ?? []
const typeArgs = node.typeArguments?.map((t) => this.context.getPTypeForNode(t)) ?? this.context.getPTypeForNode(node).getGenericArgs()
const typeArgs = this.context.getTypeParameters(node)
return eb.newCall(args, typeArgs, sourceLocation)
}

Expand Down Expand Up @@ -431,7 +431,8 @@ export abstract class BaseVisitor implements Visitor<Expressions, NodeBuilder> {
}

visitExpressionWithTypeArguments(node: ts.ExpressionWithTypeArguments): NodeBuilder {
throw new TodoError('ExpressionWithTypeArguments')
// Should be fine to ignore the type parameters as these can be inferred by the type checker
return this.baseAccept(node.expression)
}

visitAsExpression(node: ts.AsExpression): NodeBuilder {
Expand Down
4 changes: 2 additions & 2 deletions src/awst_build/context/awst-build-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export interface AwstBuildContext {
* Reflect generic type parameters for a call expression
* @param node
*/
getTypeParameters(node: ts.CallExpression): PType[]
getTypeParameters(node: ts.CallExpression | ts.NewExpression): PType[]

/**
* Resolve the given identifier to a unique variable name that accounts
Expand Down Expand Up @@ -155,7 +155,7 @@ class AwstBuildContextImpl implements AwstBuildContext {
return this.nameResolver.resolveUniqueName(node.text, symbol)
}

getTypeParameters(node: ts.CallExpression): PType[] {
getTypeParameters(node: ts.CallExpression | ts.NewExpression): PType[] {
return this.typeResolver.resolveTypeParameters(node, this.getSourceLocation(node))
}

Expand Down
15 changes: 8 additions & 7 deletions src/awst_build/eb/arc4/arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { base32ToUint8Array, codeInvariant, invariant } from '../../../util'
import type { PType } from '../../ptypes'
import { accountPType, bytesPType, IterableIteratorType, NumericLiteralPType, stringPType, TuplePType, uint64PType } from '../../ptypes'
import {
AddressClass,
arc4AddressAlias,
ARC4EncodedType,
DynamicArrayConstructor,
Expand All @@ -19,8 +20,8 @@ import {
StaticArrayType,
} from '../../ptypes/arc4-types'
import { instanceEb } from '../../type-registry'
import type { InstanceBuilder } from '../index'
import { FunctionBuilder, NodeBuilder } from '../index'
import type { InstanceBuilder, NodeBuilder } from '../index'
import { ClassBuilder, FunctionBuilder } from '../index'
import { IterableIteratorExpressionBuilder } from '../iterable-iterator-expression-builder'
import { AccountExpressionBuilder } from '../reference/account'
import { AtFunctionBuilder } from '../shared/at-function-builder'
Expand All @@ -30,7 +31,7 @@ import { requireExpressionOfType } from '../util'
import { parseFunctionArgs } from '../util/arg-parsing'
import { Arc4EncodedBaseExpressionBuilder } from './base'

export class DynamicArrayConstructorBuilder extends NodeBuilder {
export class DynamicArrayClassBuilder extends ClassBuilder {
readonly ptype = DynamicArrayConstructor

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
Expand Down Expand Up @@ -58,7 +59,7 @@ export class DynamicArrayConstructorBuilder extends NodeBuilder {
)
}
}
export class StaticArrayConstructorBuilder extends NodeBuilder {
export class StaticArrayClassBuilder extends ClassBuilder {
readonly ptype = StaticArrayConstructor

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
Expand Down Expand Up @@ -100,8 +101,8 @@ export class StaticArrayConstructorBuilder extends NodeBuilder {
)
}
}
export class AddressConstructorBuilder extends NodeBuilder {
readonly ptype = DynamicArrayConstructor
export class AddressClassBuilder extends ClassBuilder {
readonly ptype = AddressClass

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
const {
Expand All @@ -111,7 +112,7 @@ export class AddressConstructorBuilder extends NodeBuilder {
typeArgs,
callLocation: sourceLocation,
funcName: 'Address constructor',
genericTypeArgs: 2,
genericTypeArgs: 0,
argSpec: (a) => [a.optional(accountPType, stringPType, bytesPType)],
})
if (!accountOrAddressOrBytes) {
Expand Down
19 changes: 11 additions & 8 deletions src/awst_build/eb/arc4/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { wtypes } from '../../../awst/wtypes'
import { CodeError } from '../../../errors'
import type { PType } from '../../ptypes'
import type { ARC4EncodedType } from '../../ptypes/arc4-types'
import { instanceEb } from '../../type-registry'
import { BooleanExpressionBuilder } from '../boolean-expression-builder'
import { BytesExpressionBuilder } from '../bytes-expression-builder'
import type { InstanceBuilder, NodeBuilder } from '../index'
Expand All @@ -26,20 +27,22 @@ export class Arc4EncodedBaseExpressionBuilder<T extends ARC4EncodedType> extends
return super.compare(other, op, sourceLocation)
}

//
// compare(other: InstanceBuilder, op: BuilderBinaryOp, sourceLocation: SourceLocation): InstanceBuilder {
// switch (op) {
// case Bu
// }
// return super.binaryOp(other, op, sourceLocation);
// }

memberAccess(name: string, sourceLocation: SourceLocation): NodeBuilder {
switch (name) {
case 'bytes':
return new BytesExpressionBuilder(this.toBytes(sourceLocation))
case 'equals':
return new Arc4EqualsFunctionBuilder(this, sourceLocation)
case 'native':
if (this.ptype.nativeType === undefined) break
return instanceEb(
nodeFactory.aRC4Decode({
value: this.resolve(),
sourceLocation,
wtype: this.ptype.nativeType.wtypeOrThrow,
}),
this.ptype.nativeType,
)
}
return super.memberAccess(name, sourceLocation)
}
Expand Down
21 changes: 21 additions & 0 deletions src/awst_build/eb/arc4/bool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Expression } from '../../../awst/nodes'
import type { SourceLocation } from '../../../awst/source-location'
import type { PType } from '../../ptypes'
import { ARC4BoolClass, ARC4BooleanType, type ARC4EncodedType } from '../../ptypes/arc4-types'
import type { InstanceBuilder, NodeBuilder } from '../index'
import { ClassBuilder } from '../index'
import { Arc4EncodedBaseExpressionBuilder } from './base'

export class BoolClassBuilder extends ClassBuilder {
readonly ptype = ARC4BoolClass

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
throw new Error('Method not implemented.')
}
}

export class BoolExpressionBuilder extends Arc4EncodedBaseExpressionBuilder<ARC4EncodedType> {
constructor(expression: Expression) {
super(expression, ARC4BooleanType)
}
}
64 changes: 64 additions & 0 deletions src/awst_build/eb/arc4/string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { nodeFactory } from '../../../awst/node-factory'
import type { Expression } from '../../../awst/nodes'
import { StringConstant } from '../../../awst/nodes'
import type { SourceLocation } from '../../../awst/source-location'
import { wtypes } from '../../../awst/wtypes'
import type { PType } from '../../ptypes'
import { stringPType } from '../../ptypes'
import type { ARC4EncodedType } from '../../ptypes/arc4-types'
import { ARC4StrClass, ARC4StringType } from '../../ptypes/arc4-types'
import type { InstanceBuilder, NodeBuilder } from '../index'
import { ClassBuilder } from '../index'
import { parseFunctionArgs } from '../util/arg-parsing'
import { Arc4EncodedBaseExpressionBuilder } from './base'

export class StrClassBuilder extends ClassBuilder {
readonly ptype = ARC4StrClass

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
const {
args: [initialValue],
} = parseFunctionArgs({
args,
typeArgs,
genericTypeArgs: 0,
funcName: this.typeDescription,
callLocation: sourceLocation,
argSpec: (a) => [a.optional(stringPType)],
})

if (!initialValue) {
return new StrExpressionBuilder(
nodeFactory.stringConstant({
value: '',
sourceLocation: sourceLocation,
wtype: wtypes.arc4StringAliasWType,
}),
)
}
const expr = initialValue.resolve()
if (expr instanceof StringConstant) {
return new StrExpressionBuilder(
nodeFactory.stringConstant({
value: expr.value,
sourceLocation: sourceLocation,
wtype: wtypes.arc4StringAliasWType,
}),
)
} else {
return new StrExpressionBuilder(
nodeFactory.aRC4Encode({
value: expr,
wtype: wtypes.arc4StringAliasWType,
sourceLocation,
}),
)
}
}
}

export class StrExpressionBuilder extends Arc4EncodedBaseExpressionBuilder<ARC4EncodedType> {
constructor(expression: Expression) {
super(expression, ARC4StringType)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { intrinsicFactory } from '../../../awst/intrinsic-factory'
import { nodeFactory } from '../../../awst/node-factory'
import type { Expression } from '../../../awst/nodes'
import { IntegerConstant } from '../../../awst/nodes'
Expand All @@ -7,18 +6,15 @@ import { CodeError } from '../../../errors'
import { bigIntToUint8Array, codeInvariant, invariant } from '../../../util'
import type { PType } from '../../ptypes'
import { biguintPType, NumericLiteralPType, uint64PType } from '../../ptypes'
import { arc4ByteAlias, UintNType } from '../../ptypes/arc4-types'
import { instanceEb } from '../../type-registry'
import type { InstanceBuilder } from '../index'
import { NodeBuilder } from '../index'
import { arc4ByteAlias, ByteClass, UintNClass, UintNType } from '../../ptypes/arc4-types'
import type { InstanceBuilder, NodeBuilder } from '../index'
import { ClassBuilder } from '../index'
import { isValidLiteralForPType } from '../util'
import { parseFunctionArgs } from '../util/arg-parsing'
import { Arc4EncodedBaseExpressionBuilder } from './base'

export class UintNConstructorBuilder extends NodeBuilder {
get ptype(): undefined {
return undefined
}
export class UintNClassBuilder extends ClassBuilder {
readonly ptype = UintNClass

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
const {
Expand All @@ -43,18 +39,16 @@ export class UintNConstructorBuilder extends NodeBuilder {
}
}

export class ByteConstructorBuilder extends NodeBuilder {
get ptype(): undefined {
return undefined
}
export class ByteClassBuilder extends ClassBuilder {
readonly ptype = ByteClass

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
const {
args: [initialValueBuilder],
} = parseFunctionArgs({
args,
typeArgs,
genericTypeArgs: 1,
genericTypeArgs: 0,
funcName: 'Byte constructor',
argSpec: (a) => [a.optional()],
callLocation: sourceLocation,
Expand Down Expand Up @@ -132,17 +126,4 @@ export class UintNExpressionBuilder extends Arc4EncodedBaseExpressionBuilder<Uin
invariant(ptype instanceof UintNType, 'ptype must be instance of UIntNType')
super(expr, ptype)
}

memberAccess(name: string, sourceLocation: SourceLocation): NodeBuilder {
switch (name) {
case 'native':
if (this.ptype.n <= 64) {
return instanceEb(intrinsicFactory.btoi({ value: this._expr, sourceLocation }), uint64PType)
} else {
return instanceEb(nodeFactory.reinterpretCast({ expr: this._expr, sourceLocation, wtype: biguintPType.wtype }), biguintPType)
}
}

return super.memberAccess(name, sourceLocation)
}
}
12 changes: 11 additions & 1 deletion src/awst_build/eb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TupleItemExpression } from '../../awst/nodes'
import type { SourceLocation } from '../../awst/source-location'
import { CodeError, NotSupported } from '../../errors'
import { logger } from '../../logger'
import type { PType, PTypeOrClass } from '../ptypes'
import type { LibClassType, PType, PTypeOrClass } from '../ptypes'
import { instanceEb } from '../type-registry'

export enum BuilderComparisonOp {
Expand Down Expand Up @@ -178,6 +178,16 @@ export abstract class InstanceBuilder<TPType extends PType = PType> extends Node
}
}

export abstract class ClassBuilder extends NodeBuilder {
abstract readonly ptype: LibClassType

abstract newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder

call(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): NodeBuilder {
throw new CodeError(`${this.typeDescription} should be called with the \`new\` keyword`, { sourceLocation })
}
}

export abstract class FunctionBuilder extends NodeBuilder {
readonly ptype: PType | undefined = undefined

Expand Down
Loading

0 comments on commit ce32257

Please sign in to comment.