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>
<div class="navbar-menu" id="navbarMenu"> <div class="navbar-menu" id="navbarMenu">
<div class="navbar-end"> <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 Home
</a> </a>
<a class='navbar-item #if(title == "About"):is-active#!if' href="/about"> <a class='navbar-item #(title == "About" ? "is-active" : "")' href="/about">
About About
</a> </a>
</div> </div>

View File

@ -199,3 +199,14 @@ func (l Logical) Pos() Position {
func (l Logical) Op() string { func (l Logical) Op() string {
return l.Value 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 }, nil
} }
ExprTag = "#(" expr:Expr ')' { ExprTag = "#(" item:Item ')' {
return ast.ExprTag{ return ast.ExprTag{
Value: expr.(ast.Node), Value: item.(ast.Node),
Position: getPos(c), Position: getPos(c),
}, nil }, nil
} }
Item = Ternary / Expr
Expr = first:ExprSegment rest:(_ Logical _ ExprSegment)* { Expr = first:ExprSegment rest:(_ Logical _ ExprSegment)* {
restSlice := toAnySlice(rest) restSlice := toAnySlice(rest)
if len(restSlice) == 0 { if len(restSlice) == 0 {
@ -100,7 +102,7 @@ ExprSegment = first:Value rest:(_ Operator _ Value)* {
return out, nil return out, nil
} }
ParamList = '(' params:(Expr ( ',' _ Expr )* )? ')' { ParamList = '(' params:(Item ( ',' _ Item )* )? ')' {
paramSlice := toAnySlice(params) paramSlice := toAnySlice(params)
if len(paramSlice) == 0 { if len(paramSlice) == 0 {
return []ast.Node{}, nil return []ast.Node{}, nil
@ -121,6 +123,14 @@ Value = not:"!"? node:(MethodCall / FieldAccess / Index / String / RawString / F
}, nil }, 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 { MethodCall = value:Value '.' name:Ident params:ParamList {
return ast.MethodCall{ return ast.MethodCall{
Value: value.(ast.Node), Value: value.(ast.Node),

View File

@ -27,6 +27,7 @@ var (
ErrFuncTooManyReturns = errors.New("template functions can only have two return values") ErrFuncTooManyReturns = errors.New("template functions can only have two return values")
ErrFuncNoReturns = errors.New("template functions must return at least one value") 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") 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 // 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) return t.getField(node, local)
case ast.MethodCall: case ast.MethodCall:
return t.execMethodCall(node, local) return t.execMethodCall(node, local)
case ast.Ternary:
return t.evalTernary(node, local)
default: default:
return nil, nil 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 { func (t *Template) posError(n ast.Node, format string, v ...any) error {
return ast.PosError(n, t.name, format, v...) return ast.PosError(n, t.name, format, v...)
} }