| {{- /* |
| -------------------------------------------------------------------------------- |
| Template file for use with tools/src/cmd/gen to generate the constant data that |
| is held in a core::intrinsic::TableData. |
| |
| To update the generated file, run: |
| ./tools/run gen |
| |
| See: |
| * tools/src/cmd/gen for structures used by this template |
| * https://golang.org/pkg/text/template/ for documentation on the template syntax |
| -------------------------------------------------------------------------------- |
| */ -}} |
| |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "Data" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- $I := $.Intrinsics -}} |
| |
| namespace { |
| |
| using ConstEvalFunctionIndex = tint::core::intrinsic::ConstEvalFunctionIndex; |
| using IntrinsicInfo = tint::core::intrinsic::IntrinsicInfo; |
| using MatchState = tint::core::intrinsic::MatchState; |
| using Number = tint::core::intrinsic::Number; |
| using NumberMatcher = tint::core::intrinsic::NumberMatcher; |
| using NumberMatcherIndex = tint::core::intrinsic::NumberMatcherIndex; |
| using NumberMatcherIndicesIndex = tint::core::intrinsic::NumberMatcherIndicesIndex; |
| using OverloadFlag = tint::core::intrinsic::OverloadFlag; |
| using OverloadFlags = tint::core::intrinsic::OverloadFlags; |
| using OverloadIndex = tint::core::intrinsic::OverloadIndex; |
| using OverloadInfo = tint::core::intrinsic::OverloadInfo; |
| using ParameterIndex = tint::core::intrinsic::ParameterIndex; |
| using ParameterInfo = tint::core::intrinsic::ParameterInfo; |
| using StringStream = tint::StringStream; |
| using TemplateNumberIndex = tint::core::intrinsic::TemplateNumberIndex; |
| using TemplateNumberInfo = tint::core::intrinsic::TemplateNumberInfo; |
| using TemplateTypeIndex = tint::core::intrinsic::TemplateTypeIndex; |
| using TemplateTypeInfo = tint::core::intrinsic::TemplateTypeInfo; |
| using Type = tint::core::type::Type; |
| using TypeMatcher = tint::core::intrinsic::TypeMatcher; |
| using TypeMatcherIndex = tint::core::intrinsic::TypeMatcherIndex; |
| using TypeMatcherIndicesIndex = tint::core::intrinsic::TypeMatcherIndicesIndex; |
| |
| template<size_t N> |
| using TemplateNumberMatcher = tint::core::intrinsic::TemplateNumberMatcher<N>; |
| |
| template<size_t N> |
| using TemplateTypeMatcher = tint::core::intrinsic::TemplateTypeMatcher<N>; |
| |
| // clang-format off |
| |
| {{ with $I.Sem -}} |
| {{ range .Types -}} |
| {{ template "Type" . }} |
| {{ end -}} |
| {{ range .TypeMatchers -}} |
| {{ template "TypeMatcher" . }} |
| {{ end -}} |
| {{ range .EnumMatchers -}} |
| {{ template "EnumMatcher" . }} |
| {{ end -}} |
| {{- end -}} |
| |
| {{- with $I.Table -}} |
| {{- template "Matchers" $I }} |
| |
| constexpr TypeMatcherIndex kTypeMatcherIndices[] = { |
| {{- range $i, $idx := .TypeMatcherIndices }} |
| /* [{{$i}}] */ TypeMatcherIndex({{$idx}}), |
| {{- end }} |
| }; |
| |
| static_assert(TypeMatcherIndex::CanIndex(kTypeMatcherIndices), |
| "TypeMatcherIndex is not large enough to index kTypeMatcherIndices"); |
| |
| constexpr NumberMatcherIndex kNumberMatcherIndices[] = { |
| {{- range $i, $idx := .NumberMatcherIndices }} |
| /* [{{$i}}] */ NumberMatcherIndex({{$idx}}), |
| {{- end }} |
| }; |
| |
| static_assert(NumberMatcherIndex::CanIndex(kNumberMatcherIndices), |
| "NumberMatcherIndex is not large enough to index kNumberMatcherIndices"); |
| |
| constexpr ParameterInfo kParameters[] = { |
| {{- range $i, $p := .Parameters }} |
| { |
| /* [{{$i}}] */ |
| /* usage */ ParameterUsage:: |
| {{- if $p.Usage }}k{{PascalCase $p.Usage}} |
| {{- else }}kNone |
| {{- end }}, |
| /* type_matcher_indices */ TypeMatcherIndicesIndex({{$p.TypeMatcherIndicesOffset}}), |
| /* number_matcher_indices */ |
| {{- if ge $p.NumberMatcherIndicesOffset 0 }} NumberMatcherIndicesIndex({{$p.NumberMatcherIndicesOffset}}) |
| {{- else }} NumberMatcherIndicesIndex(/* invalid */) |
| {{- end }}, |
| }, |
| {{- end }} |
| }; |
| |
| static_assert(ParameterIndex::CanIndex(kParameters), |
| "ParameterIndex is not large enough to index kParameters"); |
| |
| constexpr TemplateTypeInfo kTemplateTypes[] = { |
| {{- range $i, $o := .TemplateTypes }} |
| { |
| /* [{{$i}}] */ |
| /* name */ "{{$o.Name}}", |
| /* matcher_index */ |
| {{- if ge $o.MatcherIndex 0 }} TypeMatcherIndex({{$o.MatcherIndex}}) |
| {{- else }} TypeMatcherIndex(/* invalid */) |
| {{- end }}, |
| }, |
| {{- end }} |
| }; |
| |
| static_assert(TemplateTypeIndex::CanIndex(kTemplateTypes), |
| "TemplateTypeIndex is not large enough to index kTemplateTypes"); |
| |
| constexpr TemplateNumberInfo kTemplateNumbers[] = { |
| {{- range $i, $o := .TemplateNumbers }} |
| { |
| /* [{{$i}}] */ |
| /* name */ "{{$o.Name}}", |
| /* matcher_index */ |
| {{- if ge $o.MatcherIndex 0 }} NumberMatcherIndex({{$o.MatcherIndex}}) |
| {{- else }} NumberMatcherIndex(/* invalid */) |
| {{- end }}, |
| }, |
| {{- end }} |
| }; |
| |
| static_assert(TemplateNumberIndex::CanIndex(kTemplateNumbers), |
| "TemplateNumberIndex is not large enough to index kTemplateNumbers"); |
| |
| constexpr constant::Eval::Function kConstEvalFunctions[] = { |
| {{- range $i, $f := .ConstEvalFunctions }} |
| /* [{{$i}}] */ &constant::Eval::{{template "ExpandName" $f}}, |
| {{- end }} |
| }; |
| |
| static_assert(ConstEvalFunctionIndex::CanIndex(kConstEvalFunctions), |
| "ConstEvalFunctionIndex is not large enough to index kConstEvalFunctions"); |
| |
| constexpr OverloadInfo kOverloads[] = { |
| {{- range $i, $o := .Overloads }} |
| { |
| /* [{{$i}}] */ |
| /* flags */ OverloadFlags(OverloadFlag::kIs{{Title $o.Kind}} |
| {{- range $i, $u := $o.CanBeUsedInStage.List -}} |
| , OverloadFlag::kSupports{{Title $u}}Pipeline |
| {{- end }} |
| {{- if $o.MustUse}}, OverloadFlag::kMustUse{{end}} |
| {{- if $o.IsDeprecated}}, OverloadFlag::kIsDeprecated{{end -}} |
| ), |
| /* num_parameters */ {{$o.NumParameters}}, |
| /* num_template_types */ {{$o.NumTemplateTypes}}, |
| /* num_template_numbers */ {{$o.NumTemplateNumbers}}, |
| /* template_types */ |
| {{- if ge $o.TemplateTypesOffset 0 }} TemplateTypeIndex({{$o.TemplateTypesOffset}}) |
| {{- else }} TemplateTypeIndex(/* invalid */) |
| {{- end }}, |
| /* template_numbers */ |
| {{- if ge $o.TemplateNumbersOffset 0 }} TemplateNumberIndex({{$o.TemplateNumbersOffset}}) |
| {{- else }} TemplateNumberIndex(/* invalid */) |
| {{- end }}, |
| /* parameters */ |
| {{- if ge $o.ParametersOffset 0 }} ParameterIndex({{$o.ParametersOffset}}) |
| {{- else }} ParameterIndex(/* invalid */) |
| {{- end }}, |
| /* return_type_matcher_indices */ |
| {{- if ge $o.ReturnTypeMatcherIndicesOffset 0 }} TypeMatcherIndicesIndex({{$o.ReturnTypeMatcherIndicesOffset}}) |
| {{- else }} TypeMatcherIndicesIndex(/* invalid */) |
| {{- end }}, |
| /* return_number_matcher_indices */ |
| {{- if ge $o.ReturnNumberMatcherIndicesOffset 0 }} NumberMatcherIndicesIndex({{$o.ReturnNumberMatcherIndicesOffset}}) |
| {{- else }} NumberMatcherIndicesIndex(/* invalid */) |
| {{- end }}, |
| /* const_eval_fn */ |
| {{- if ge $o.ConstEvalFunctionOffset 0 }} ConstEvalFunctionIndex({{$o.ConstEvalFunctionOffset}}) |
| {{- else }} ConstEvalFunctionIndex(/* invalid */) |
| {{- end }}, |
| }, |
| {{- end }} |
| }; |
| |
| static_assert(OverloadIndex::CanIndex(kOverloads), |
| "OverloadIndex is not large enough to index kOverloads"); |
| |
| constexpr IntrinsicInfo kBuiltins[] = { |
| {{- range $i, $b := .Builtins }} |
| { |
| /* [{{$i}}] */ |
| {{- range $b.OverloadDescriptions }} |
| /* {{.}} */ |
| {{- end }} |
| /* num overloads */ {{$b.NumOverloads}}, |
| /* overloads */ OverloadIndex({{$b.OverloadsOffset}}), |
| }, |
| {{- end }} |
| }; |
| |
| constexpr IntrinsicInfo kUnaryOperators[] = { |
| {{- range $i, $o := .UnaryOperators }} |
| { |
| /* [{{$i}}] */ |
| {{- range $o.OverloadDescriptions }} |
| /* {{.}} */ |
| {{- end }} |
| /* num overloads */ {{$o.NumOverloads}}, |
| /* overloads */ OverloadIndex({{$o.OverloadsOffset}}), |
| }, |
| {{- end }} |
| }; |
| |
| {{- range $i, $o := .UnaryOperators }} |
| constexpr uint8_t kUnaryOperator{{ template "ExpandName" $o.Name}} = {{$i}}; |
| {{- end }} |
| |
| constexpr IntrinsicInfo kBinaryOperators[] = { |
| {{- range $i, $o := .BinaryOperators }} |
| { |
| /* [{{$i}}] */ |
| {{- range $o.OverloadDescriptions }} |
| /* {{.}} */ |
| {{- end }} |
| /* num overloads */ {{$o.NumOverloads}}, |
| /* overloads */ OverloadIndex({{$o.OverloadsOffset}}), |
| }, |
| {{- end }} |
| }; |
| |
| {{- range $i, $o := .BinaryOperators }} |
| constexpr uint8_t kBinaryOperator{{ template "ExpandName" $o.Name}} = {{$i}}; |
| {{- end }} |
| |
| constexpr IntrinsicInfo kConstructorsAndConverters[] = { |
| {{- range $i, $o := .ConstructorsAndConverters }} |
| { |
| /* [{{$i}}] */ |
| {{- range $o.OverloadDescriptions }} |
| /* {{.}} */ |
| {{- end }} |
| /* num overloads */ {{$o.NumOverloads}}, |
| /* overloads */ OverloadIndex({{$o.OverloadsOffset}}), |
| }, |
| {{- end }} |
| }; |
| |
| // clang-format on |
| |
| } // anonymous namespace |
| |
| const core::intrinsic::TableData {{$.Name}}{ |
| /* template_types */ kTemplateTypes, |
| /* template_numbers */ kTemplateNumbers, |
| /* type_matcher_indices */ kTypeMatcherIndices, |
| /* number_matcher_indices */ kNumberMatcherIndices, |
| /* type_matchers */ kTypeMatchers, |
| /* number_matchers */ kNumberMatchers, |
| /* parameters */ kParameters, |
| /* overloads */ kOverloads, |
| /* const_eval_functions */ kConstEvalFunctions, |
| /* ctor_conv */ kConstructorsAndConverters, |
| /* builtins */ kBuiltins, |
| /* binary_plus */ kBinaryOperators[kBinaryOperatorPlus], |
| /* binary_minus */ kBinaryOperators[kBinaryOperatorMinus], |
| /* binary_star */ kBinaryOperators[kBinaryOperatorStar], |
| /* binary_divide */ kBinaryOperators[kBinaryOperatorDivide], |
| /* binary_modulo */ kBinaryOperators[kBinaryOperatorModulo], |
| /* binary_xor */ kBinaryOperators[kBinaryOperatorXor], |
| /* binary_and */ kBinaryOperators[kBinaryOperatorAnd], |
| /* binary_or */ kBinaryOperators[kBinaryOperatorOr], |
| /* binary_logical_and */ kBinaryOperators[kBinaryOperatorLogicalAnd], |
| /* binary_logical_or */ kBinaryOperators[kBinaryOperatorLogicalOr], |
| /* binary_equal */ kBinaryOperators[kBinaryOperatorEqual], |
| /* binary_not_equal */ kBinaryOperators[kBinaryOperatorNotEqual], |
| /* binary_less_than */ kBinaryOperators[kBinaryOperatorLessThan], |
| /* binary_greater_than */ kBinaryOperators[kBinaryOperatorGreaterThan], |
| /* binary_less_than_equal */ kBinaryOperators[kBinaryOperatorLessThanEqual], |
| /* binary_greater_than_equal */ kBinaryOperators[kBinaryOperatorGreaterThanEqual], |
| /* binary_shift_left */ kBinaryOperators[kBinaryOperatorShiftLeft], |
| /* binary_shift_right */ kBinaryOperators[kBinaryOperatorShiftRight], |
| /* unary_not */ kUnaryOperators[kUnaryOperatorNot], |
| /* unary_complement */ kUnaryOperators[kUnaryOperatorComplement], |
| /* unary_minus */ kUnaryOperators[kUnaryOperatorMinus], |
| }; |
| |
| {{ end -}} |
| {{- end -}} |
| |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "Type" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- $name := PascalCase .Name -}} |
| /// TypeMatcher for 'type {{.Name}}' |
| constexpr TypeMatcher k{{$name}}Matcher { |
| /* match */ [](MatchState& state, const Type* ty) -> const Type* { |
| {{- range .TemplateParams }} |
| {{- template "DeclareLocalTemplateParam" . }} |
| {{- end }} |
| if (!Match{{$name}}(state, ty{{range .TemplateParams}}, {{.GetName}}{{end}})) { |
| return nullptr; |
| } |
| {{- range .TemplateParams }} |
| {{.Name}} = {{ template "MatchTemplateParam" .}}({{.Name}}); |
| if ({{ template "IsTemplateParamInvalid" .}}) { |
| return nullptr; |
| } |
| {{- end }} |
| return Build{{$name}}(state{{range .TemplateParams}}, {{.GetName}}{{end}}); |
| }, |
| /* string */ [](MatchState*{{if .TemplateParams}} state{{end}}) -> std::string { |
| {{- range .TemplateParams }} |
| {{- template "DeclareLocalTemplateParamName" . }} |
| {{- end }} |
| |
| {{- if .DisplayName }} |
| StringStream ss; |
| ss{{range SplitDisplayName .DisplayName}} << {{.}}{{end}}; |
| return ss.str(); |
| {{- else if .TemplateParams }} |
| return "{{.Name}}<"{{template "AppendTemplateParamNames" .TemplateParams}} + ">"; |
| {{- else }} |
| return "{{.Name}}"; |
| {{- end }} |
| } |
| }; |
| |
| {{ end -}} |
| |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "TypeMatcher" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- $name := PascalCase .Name -}} |
| /// TypeMatcher for 'match {{.Name}}' |
| constexpr TypeMatcher k{{$name}}Matcher { |
| /* match */ [](MatchState& state, const Type* ty) -> const Type* { |
| {{- range .PrecedenceSortedTypes }} |
| if (Match{{PascalCase .Name}}(state, ty)) { |
| return Build{{PascalCase .Name}}(state); |
| } |
| {{- end }} |
| return nullptr; |
| }, |
| /* string */ [](MatchState*) -> std::string { |
| StringStream ss; |
| // Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support |
| // template arguments, nor can they match sub-types. As such, they have no use for the MatchState. |
| ss |
| {{- range .Types -}} |
| {{- if IsFirstIn . $.Types }} << k{{PascalCase .Name}}Matcher.string(nullptr) |
| {{- else if IsLastIn . $.Types }} << " or " << k{{PascalCase .Name}}Matcher.string(nullptr) |
| {{- else }} << ", " << k{{PascalCase .Name}}Matcher.string(nullptr) |
| {{- end -}} |
| {{- end -}}; |
| return ss.str(); |
| } |
| }; |
| {{ end -}} |
| |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "EnumMatcher" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- $class := PascalCase .Name -}} |
| {{- $enum := PascalCase .Enum.Name -}} |
| /// EnumMatcher for 'match {{.Name}}' |
| constexpr NumberMatcher k{{$class}}Matcher { |
| {{ if eq 1 (len .Options) -}} |
| {{- $option := index .Options 0 }} |
| {{- $entry := printf "k%v" (PascalCase $option.Name) -}} |
| /* match */ [](MatchState&, Number number) -> Number { |
| if (number.IsAny() || number.Value() == static_cast<uint32_t>({{$enum}}::{{$entry}})) { |
| return Number(static_cast<uint32_t>({{$enum}}::{{$entry}})); |
| } |
| return Number::invalid; |
| }, |
| {{- else -}} |
| /* match */ [](MatchState&, Number number) -> Number { |
| switch (static_cast<{{$enum}}>(number.Value())) { |
| {{- range .Options }} |
| case {{$enum}}::k{{PascalCase .Name}}: |
| {{- end }} |
| return number; |
| default: |
| return Number::invalid; |
| } |
| }, |
| {{- end }} |
| /* string */ [](MatchState*) -> std::string { |
| return " |
| {{- range .Options -}} |
| {{- if IsFirstIn . $.Options }}{{.Name}} |
| {{- else if IsLastIn . $.Options }} or {{.Name}} |
| {{- else }}, {{.Name}} |
| {{- end -}} |
| {{- end -}} |
| "; |
| } |
| }; |
| {{ end -}} |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "Matchers" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| /// Type and number matchers |
| {{- $t_names := Map -}} |
| {{- $n_names := Map -}} |
| {{- range Iterate $.Sem.MaxTemplateTypes -}} |
| {{- $name := printf "TemplateTypeMatcher<%v>::matcher" . -}} |
| {{- $t_names.Put . $name }} |
| {{- end }} |
| {{- range Iterate $.Sem.MaxTemplateNumbers -}} |
| {{- $name := printf "TemplateNumberMatcher<%v>::matcher" . -}} |
| {{- $n_names.Put . $name }} |
| {{- end }} |
| {{- range $.Sem.Types -}} |
| {{- $name := printf "k%vMatcher" (PascalCase .Name) -}} |
| {{- $t_names.Put . $name }} |
| {{- end }} |
| {{- range $.Sem.TypeMatchers -}} |
| {{- $name := printf "k%vMatcher" (PascalCase .Name) -}} |
| {{- $t_names.Put . $name }} |
| {{- end }} |
| {{- range $.Sem.EnumMatchers -}} |
| {{- $name := printf "k%vMatcher" (PascalCase .Name) -}} |
| {{- $n_names.Put . $name }} |
| {{- end }} |
| |
| /// The template types, types, and type matchers |
| constexpr TypeMatcher kTypeMatchers[] = { |
| {{- range $i, $m := $.Table.TMatchers }} |
| /* [{{$i}}] */ |
| {{- if $m }} {{$t_names.Get $m}}, |
| {{- else }} {{$t_names.Get $i}}, |
| {{- end }} |
| {{- end }} |
| }; |
| |
| /// The template numbers, and number matchers |
| constexpr NumberMatcher kNumberMatchers[] = { |
| {{- range $i, $m := $.Table.NMatchers }} |
| /* [{{$i}}] */ |
| {{- if $m }} {{$n_names.Get $m}}, |
| {{- else }} {{$n_names.Get $i}}, |
| {{- end }} |
| {{- end }} |
| }; |
| |
| {{- end -}} |
| |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "DeclareLocalTemplateParam" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- if IsTemplateTypeParam . }} |
| const Type* {{.Name}} = nullptr; |
| {{- else if IsTemplateNumberParam . }} |
| Number {{.Name}} = Number::invalid; |
| {{- else if IsTemplateEnumParam . }} |
| Number {{.Name}} = Number::invalid; |
| {{- end -}} |
| {{- end -}} |
| |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "DeclareLocalTemplateParamName" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- if IsTemplateTypeParam . }} |
| const std::string {{.Name}} = state->TypeName(); |
| {{- else if IsTemplateNumberParam . }} |
| const std::string {{.Name}} = state->NumName(); |
| {{- else if IsTemplateEnumParam . }} |
| const std::string {{.Name}} = state->NumName(); |
| {{- end -}} |
| {{- end -}} |
| |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "MatchTemplateParam" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- if IsTemplateTypeParam . -}} |
| state.Type |
| {{- else if IsTemplateNumberParam . -}} |
| state.Num |
| {{- else if IsTemplateEnumParam . -}} |
| state.Num |
| {{- end -}} |
| {{- end -}} |
| |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "IsTemplateParamInvalid" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- if IsTemplateTypeParam . -}} |
| {{.Name}} == nullptr |
| {{- else if IsTemplateNumberParam . -}} |
| !{{.Name}}.IsValid() |
| {{- else if IsTemplateEnumParam . -}} |
| !{{.Name}}.IsValid() |
| {{- end -}} |
| {{- end -}} |
| |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "AppendTemplateParamNames" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- range $i, $ := . -}} |
| {{- if $i }} + ", " + {{.Name}} |
| {{- else }} + {{.Name}} |
| {{- end -}} |
| {{- end -}} |
| {{- end -}} |
| |
| |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- define "ExpandName" -}} |
| {{- /* ------------------------------------------------------------------ */ -}} |
| {{- if eq . "<<" -}}ShiftLeft |
| {{- else if eq . "&" -}}And |
| {{- else if eq . "|" -}}Or |
| {{- else if eq . "^" -}}Xor |
| {{- else if eq . "&&" -}}LogicalAnd |
| {{- else if eq . "||" -}}LogicalOr |
| {{- else if eq . "==" -}}Equal |
| {{- else if eq . "!" -}}Not |
| {{- else if eq . "!=" -}}NotEqual |
| {{- else if eq . "~" -}}Complement |
| {{- else if eq . "<" -}}LessThan |
| {{- else if eq . ">" -}}GreaterThan |
| {{- else if eq . "<=" -}}LessThanEqual |
| {{- else if eq . ">=" -}}GreaterThanEqual |
| {{- else if eq . "<<" -}}ShiftLeft |
| {{- else if eq . ">>" -}}ShiftRight |
| {{- else if eq . "+" -}}Plus |
| {{- else if eq . "-" -}}Minus |
| {{- else if eq . "*" -}}Star |
| {{- else if eq . "/" -}}Divide |
| {{- else if eq . "%" -}}Modulo |
| {{- else -}}{{.}} |
| {{- end -}} |
| {{- end -}} |