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

Extended string template #3587

1 change: 1 addition & 0 deletions runtime/ast/elementtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,5 @@ const (
ElementTypeForceExpression
ElementTypePathExpression
ElementTypeAttachExpression
ElementTypeStringTemplateExpression
)
5 changes: 3 additions & 2 deletions runtime/ast/elementtype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 58 additions & 0 deletions runtime/ast/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,64 @@ func (*StringExpression) precedence() precedence {
return precedenceLiteral
}

// StringTemplateExpression

type StringTemplateExpression struct {
Values []string
Expressions []Expression
Range
}

var _ Expression = &StringTemplateExpression{}

func NewStringTemplateExpression(gauge common.MemoryGauge, values []string, exprs []Expression, exprRange Range) *StringTemplateExpression {
// STRINGTODO: change to be similar to array memory usage?
common.UseMemory(gauge, common.StringExpressionMemoryUsage)
return &StringTemplateExpression{
Values: values,
Expressions: exprs,
Range: exprRange,
}
}

var _ Element = &StringExpression{}
var _ Expression = &StringExpression{}

func (*StringTemplateExpression) ElementType() ElementType {
return ElementTypeStringTemplateExpression
}

func (*StringTemplateExpression) isExpression() {}

func (*StringTemplateExpression) isIfStatementTest() {}

func (*StringTemplateExpression) Walk(_ func(Element)) {
// NO-OP
}

func (e *StringTemplateExpression) String() string {
return Prettier(e)
}

func (e *StringTemplateExpression) Doc() prettier.Doc {
return prettier.Text(QuoteString("String template"))
}

func (e *StringTemplateExpression) MarshalJSON() ([]byte, error) {
type Alias StringTemplateExpression
return json.Marshal(&struct {
*Alias
Type string
}{
Type: "StringTemplateExpression",
Alias: (*Alias)(e),
})
}

func (*StringTemplateExpression) precedence() precedence {
return precedenceLiteral
}

// IntegerExpression

type IntegerExpression struct {
Expand Down
84 changes: 59 additions & 25 deletions runtime/ast/expression_extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ type StringExtractor interface {
ExtractString(extractor *ExpressionExtractor, expression *StringExpression) ExpressionExtraction
}

type StringTemplateExtractor interface {
ExtractStringTemplate(extractor *ExpressionExtractor, expression *StringTemplateExpression) ExpressionExtraction
}

type ArrayExtractor interface {
ExtractArray(extractor *ExpressionExtractor, expression *ArrayExpression) ExpressionExtraction
}
Expand Down Expand Up @@ -117,31 +121,32 @@ type AttachExtractor interface {
}

type ExpressionExtractor struct {
IndexExtractor IndexExtractor
ForceExtractor ForceExtractor
BoolExtractor BoolExtractor
NilExtractor NilExtractor
IntExtractor IntExtractor
FixedPointExtractor FixedPointExtractor
StringExtractor StringExtractor
ArrayExtractor ArrayExtractor
DictionaryExtractor DictionaryExtractor
IdentifierExtractor IdentifierExtractor
AttachExtractor AttachExtractor
MemoryGauge common.MemoryGauge
VoidExtractor VoidExtractor
UnaryExtractor UnaryExtractor
ConditionalExtractor ConditionalExtractor
InvocationExtractor InvocationExtractor
BinaryExtractor BinaryExtractor
FunctionExtractor FunctionExtractor
CastingExtractor CastingExtractor
CreateExtractor CreateExtractor
DestroyExtractor DestroyExtractor
ReferenceExtractor ReferenceExtractor
MemberExtractor MemberExtractor
PathExtractor PathExtractor
nextIdentifier int
IndexExtractor IndexExtractor
ForceExtractor ForceExtractor
BoolExtractor BoolExtractor
NilExtractor NilExtractor
IntExtractor IntExtractor
FixedPointExtractor FixedPointExtractor
StringExtractor StringExtractor
StringTemplateExtractor StringTemplateExtractor
ArrayExtractor ArrayExtractor
DictionaryExtractor DictionaryExtractor
IdentifierExtractor IdentifierExtractor
AttachExtractor AttachExtractor
MemoryGauge common.MemoryGauge
VoidExtractor VoidExtractor
UnaryExtractor UnaryExtractor
ConditionalExtractor ConditionalExtractor
InvocationExtractor InvocationExtractor
BinaryExtractor BinaryExtractor
FunctionExtractor FunctionExtractor
CastingExtractor CastingExtractor
CreateExtractor CreateExtractor
DestroyExtractor DestroyExtractor
ReferenceExtractor ReferenceExtractor
MemberExtractor MemberExtractor
PathExtractor PathExtractor
nextIdentifier int
}

var _ ExpressionVisitor[ExpressionExtraction] = &ExpressionExtractor{}
Expand Down Expand Up @@ -271,6 +276,35 @@ func (extractor *ExpressionExtractor) ExtractString(expression *StringExpression
return rewriteExpressionAsIs(expression)
}

func (extractor *ExpressionExtractor) VisitStringTemplateExpression(expression *StringTemplateExpression) ExpressionExtraction {

// delegate to child extractor, if any,
// or call default implementation

if extractor.StringTemplateExtractor != nil {
return extractor.StringTemplateExtractor.ExtractStringTemplate(extractor, expression)
}
return extractor.ExtractStringTemplate(expression)
}

func (extractor *ExpressionExtractor) ExtractStringTemplate(expression *StringTemplateExpression) ExpressionExtraction {

// copy the expression
newExpression := *expression

// rewrite all value expressions

rewrittenExpressions, extractedExpressions :=
extractor.VisitExpressions(expression.Expressions)

newExpression.Expressions = rewrittenExpressions

return ExpressionExtraction{
RewrittenExpression: &newExpression,
ExtractedExpressions: extractedExpressions,
}
}

func (extractor *ExpressionExtractor) VisitArrayExpression(expression *ArrayExpression) ExpressionExtraction {

// delegate to child extractor, if any,
Expand Down
1 change: 1 addition & 0 deletions runtime/ast/precedence.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const (
// - BoolExpression
// - NilExpression
// - StringExpression
// - StringTemplateExpression
// - IntegerExpression
// - FixedPointExpression
// - ArrayExpression
Expand Down
4 changes: 4 additions & 0 deletions runtime/ast/visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ type ExpressionVisitor[T any] interface {
VisitNilExpression(*NilExpression) T
VisitBoolExpression(*BoolExpression) T
VisitStringExpression(*StringExpression) T
VisitStringTemplateExpression(*StringTemplateExpression) T
VisitIntegerExpression(*IntegerExpression) T
VisitFixedPointExpression(*FixedPointExpression) T
VisitDictionaryExpression(*DictionaryExpression) T
Expand Down Expand Up @@ -219,6 +220,9 @@ func AcceptExpression[T any](expression Expression, visitor ExpressionVisitor[T]
case ElementTypeStringExpression:
return visitor.VisitStringExpression(expression.(*StringExpression))

case ElementTypeStringTemplateExpression:
return visitor.VisitStringTemplateExpression(expression.(*StringTemplateExpression))

case ElementTypeIntegerExpression:
return visitor.VisitIntegerExpression(expression.(*IntegerExpression))

Expand Down
5 changes: 5 additions & 0 deletions runtime/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ func (compiler *Compiler) VisitFunctionExpression(_ *ast.FunctionExpression) ir.
panic(errors.NewUnreachableError())
}

func (compiler *Compiler) VisitStringTemplateExpression(e *ast.StringTemplateExpression) ir.Expr {
// TODO
panic(errors.NewUnreachableError())
}

func (compiler *Compiler) VisitStringExpression(e *ast.StringExpression) ir.Expr {
return &ir.Const{
Constant: ir.String{
Expand Down
28 changes: 28 additions & 0 deletions runtime/interpreter/interpreter_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package interpreter

import (
"math/big"
"strings"
"time"

"github.com/onflow/atree"
Expand Down Expand Up @@ -957,6 +958,33 @@ func (interpreter *Interpreter) VisitStringExpression(expression *ast.StringExpr
return NewUnmeteredStringValue(expression.Value)
}

func (interpreter *Interpreter) VisitStringTemplateExpression(expression *ast.StringTemplateExpression) Value {
values := interpreter.visitExpressionsNonCopying(expression.Expressions)

templatesType := interpreter.Program.Elaboration.StringTemplateExpressionTypes(expression)
argumentTypes := templatesType.ArgumentTypes

var builder strings.Builder
for i, str := range expression.Values {
builder.WriteString(str)
if i < len(values) {
// STRINGTODO: is this how the conversion should happen?
s := values[i].String()
switch argumentTypes[i] {
case sema.StringType:
// remove quotations
s = s[1 : len(s)-1]
builder.WriteString(s)
default:
builder.WriteString(s)
}
}
}

// STRINGTODO: already metered as a string constant in parser?
return NewUnmeteredStringValue(builder.String())
}

func (interpreter *Interpreter) VisitArrayExpression(expression *ast.ArrayExpression) Value {
values := interpreter.visitExpressionsNonCopying(expression.Values)

Expand Down
Loading
Loading