Add ternary expressions

This commit is contained in:
Elara 2023-10-30 08:37:59 -07:00
parent 9d44c8d02c
commit c1a2e56f4f
5 changed files with 352 additions and 186 deletions

View File

@ -10,10 +10,10 @@
</div>
<div class="navbar-menu" id="navbarMenu">
<div class="navbar-end">
<a class='navbar-item #if(title == "Home"):is-active#!if' href="/">
<a class='navbar-item #(title == "Home" ? "is-active" : "")' href="/">
Home
</a>
<a class='navbar-item #if(title == "About"):is-active#!if' href="/about">
<a class='navbar-item #(title == "About" ? "is-active" : "")' href="/about">
About
</a>
</div>

View File

@ -199,3 +199,14 @@ func (l Logical) Pos() Position {
func (l Logical) Op() string {
return l.Value
}
type Ternary struct {
Condition Node
IfTrue Node
Else Node
Position Position
}
func (t Ternary) Pos() Position {
return t.Position
}

File diff suppressed because it is too large Load Diff

View File

@ -55,13 +55,15 @@ EndTag = "#!" name:Ident {
}, nil
}
ExprTag = "#(" expr:Expr ')' {
ExprTag = "#(" item:Item ')' {
return ast.ExprTag{
Value: expr.(ast.Node),
Value: item.(ast.Node),
Position: getPos(c),
}, nil
}
Item = Ternary / Expr
Expr = first:ExprSegment rest:(_ Logical _ ExprSegment)* {
restSlice := toAnySlice(rest)
if len(restSlice) == 0 {
@ -100,7 +102,7 @@ ExprSegment = first:Value rest:(_ Operator _ Value)* {
return out, nil
}
ParamList = '(' params:(Expr ( ',' _ Expr )* )? ')' {
ParamList = '(' params:(Item ( ',' _ Item )* )? ')' {
paramSlice := toAnySlice(params)
if len(paramSlice) == 0 {
return []ast.Node{}, nil
@ -121,6 +123,14 @@ Value = not:"!"? node:(MethodCall / FieldAccess / Index / String / RawString / F
}, nil
}
Ternary = cond:Expr _ '?' _ ifTrue:Value _ ':' _ elseVal:Value {
return ast.Ternary{
Condition: cond.(ast.Node),
IfTrue: ifTrue.(ast.Node),
Else: elseVal.(ast.Node),
}, nil
}
MethodCall = value:Value '.' name:Ident params:ParamList {
return ast.MethodCall{
Value: value.(ast.Node),

View File

@ -27,6 +27,7 @@ var (
ErrFuncTooManyReturns = errors.New("template functions can only have two return values")
ErrFuncNoReturns = errors.New("template functions must return at least one value")
ErrFuncSecondReturnType = errors.New("the second return value of a template function must be an error")
ErrTernaryCondBool = errors.New("ternary condition must be a boolean")
)
// HTML represents unescaped HTML strings
@ -215,6 +216,8 @@ func (t *Template) getValue(node ast.Node, local map[string]any) (any, error) {
return t.getField(node, local)
case ast.MethodCall:
return t.execMethodCall(node, local)
case ast.Ternary:
return t.evalTernary(node, local)
default:
return nil, nil
}
@ -427,6 +430,24 @@ func (t *Template) execFunc(fn reflect.Value, node ast.Node, args []ast.Node, lo
}
}
func (t *Template) evalTernary(tr ast.Ternary, local map[string]any) (any, error) {
condVal, err := t.getValue(tr.Condition, local)
if err != nil {
return nil, t.posError(tr.Condition, "%w", ErrTernaryCondBool)
}
cond, ok := condVal.(bool)
if !ok {
return nil, errors.New("ternary condition must be a boolean")
}
if cond {
return t.getValue(tr.IfTrue, local)
} else {
return t.getValue(tr.Else, local)
}
}
func (t *Template) posError(n ast.Node, format string, v ...any) error {
return ast.PosError(n, t.name, format, v...)
}