Skip to content

Commit

Permalink
Merge pull request #3660 from onflow/feature/string-templates
Browse files Browse the repository at this point in the history
Merge `string-templates` branch to `master`
  • Loading branch information
SupunS authored Oct 30, 2024
2 parents 085280e + 1210573 commit 1b27cc3
Show file tree
Hide file tree
Showing 38 changed files with 2,196 additions and 149 deletions.
1 change: 1 addition & 0 deletions 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 ast/elementtype_string.go

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

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

// StringTemplateExpression

type StringTemplateExpression struct {
// Values and Expressions are assumed to be interleaved, V[0] + E[0] + V[1] + ... + E[n-1] + V[n]
// this is enforced in the parser e.g. "a\(b)c" will be parsed as follows
// Values: []string{"a","c"}
// Expressions: []Expression{b}
Values []string
Expressions []Expression
Range
}

var _ Expression = &StringTemplateExpression{}

func NewStringTemplateExpression(
gauge common.MemoryGauge,
values []string,
exprs []Expression,
exprRange Range,
) *StringTemplateExpression {
common.UseMemory(gauge, common.NewStringTemplateExpressionMemoryUsage(len(values)+len(exprs)))
if len(values) != len(exprs)+1 {
// assert string template alternating structure
panic(errors.NewUnreachableError())
}
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 (e *StringTemplateExpression) Walk(walkChild func(Element)) {
walkExpressions(walkChild, e.Expressions)
}

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

func (e *StringTemplateExpression) Doc() prettier.Doc {
if len(e.Expressions) == 0 {
return prettier.Text(QuoteString(e.Values[0]))
}

// TODO: must reproduce expressions as literals
panic("not implemented")
}

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 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 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
48 changes: 48 additions & 0 deletions ast/string_template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Cadence - The resource-oriented smart contract programming language
*
* Copyright Flow Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ast

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/turbolent/prettier"
)

func TestStringTemplate_Doc(t *testing.T) {

t.Parallel()

stmt := &StringTemplateExpression{
Values: []string{
"abc",
},
Expressions: []Expression{},
Range: Range{
StartPos: Position{Offset: 4, Line: 2, Column: 3},
EndPos: Position{Offset: 11, Line: 2, Column: 10},
},
}

assert.Equal(t,
prettier.Text("\"abc\""),
stmt.Doc(),
)
}
4 changes: 4 additions & 0 deletions 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
1 change: 1 addition & 0 deletions common/memorykind.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ const (
MemoryKindIntegerExpression
MemoryKindFixedPointExpression
MemoryKindArrayExpression
MemoryKindStringTemplateExpression
MemoryKindDictionaryExpression
MemoryKindIdentifierExpression
MemoryKindInvocationExpression
Expand Down
Loading

0 comments on commit 1b27cc3

Please sign in to comment.