Add nil keyword

This commit is contained in:
Elara 2024-06-06 19:06:45 -07:00
parent f1a998c25b
commit d5c33f9e5d
5 changed files with 340 additions and 295 deletions

View File

@ -20,6 +20,14 @@ func (p Position) String() string {
return fmt.Sprintf("%s: line %d, col %d", p.Name, p.Line, p.Col) return fmt.Sprintf("%s: line %d, col %d", p.Name, p.Line, p.Col)
} }
type Nil struct {
Position Position
}
func (n Nil) Pos() Position {
return n.Position
}
type Tag struct { type Tag struct {
Name Ident Name Ident
Params []Node Params []Node

11
expr.go
View File

@ -54,6 +54,17 @@ func (t *Template) performOp(a, b reflect.Value, op ast.Operator) (any, error) {
default: default:
return nil, ast.PosError(op, "the in operator can only be used on strings, arrays, and slices (got %s and %s)", a.Type(), b.Type()) return nil, ast.PosError(op, "the in operator can only be used on strings, arrays, and slices (got %s and %s)", a.Type(), b.Type())
} }
} else if !b.IsValid() {
if op.Value != "==" {
return nil, ast.PosError(op, "invalid operator for nil value (expected ==, got %s)", op.Value)
}
switch a.Kind() {
case reflect.Chan, reflect.Slice, reflect.Map, reflect.Func, reflect.Interface, reflect.Pointer:
return a.IsNil(), nil
default:
return nil, ast.PosError(op, "values of type %s cannot be compared against nil", a.Type())
}
} else if b.CanConvert(a.Type()) { } else if b.CanConvert(a.Type()) {
b = b.Convert(a.Type()) b = b.Convert(a.Type())
} else { } else {

File diff suppressed because it is too large Load Diff

View File

@ -129,7 +129,7 @@ ParamList = '(' params:(Expr ( ',' _ Expr )* )? ')' {
return out, nil return out, nil
} }
Value = not:"!"? node:(MethodCall / FieldAccess / Index / String / RawString / Float / Integer / Bool / FuncCall / VariableOr / Ident / ParenExpr / Array / Map) { Value = not:"!"? node:(Nil / MethodCall / FieldAccess / Index / String / RawString / Float / Integer / Bool / FuncCall / VariableOr / Ident / ParenExpr / Array / Map) {
return ast.Value{ return ast.Value{
Node: node.(ast.Node), Node: node.(ast.Node),
Not: not != nil, Not: not != nil,
@ -288,6 +288,10 @@ ArithmeticOp = ('+' / '-' / '/' / '*' / '%') {
}, nil }, nil
} }
Nil = "nil" {
return ast.Nil{Position: getPos(c)}, nil
}
Text = . [^#]* { return ast.Text{Data: c.text, Position: getPos(c)}, nil } Text = . [^#]* { return ast.Text{Data: c.text, Position: getPos(c)}, nil }
_ "whitespace" ← [ \t\r\n]* _ "whitespace" ← [ \t\r\n]*

View File

@ -26,7 +26,7 @@ type Template struct {
// WriteOnSuccess indicates whether the output should only be written if generation fully succeeds. // WriteOnSuccess indicates whether the output should only be written if generation fully succeeds.
// This option buffers the output of the template, so it will use more memory. (default: false) // This option buffers the output of the template, so it will use more memory. (default: false)
WriteOnSuccess bool WriteOnSuccess bool
NilToZero bool NilToZero bool
tags map[string]Tag tags map[string]Tag
vars map[string]any vars map[string]any
@ -239,6 +239,8 @@ func (t *Template) getValue(node ast.Node, local map[string]any) (any, error) {
return t.convertArray(node, local) return t.convertArray(node, local)
case ast.Assignment: case ast.Assignment:
return node, t.handleAssignment(node, local) return node, t.handleAssignment(node, local)
case ast.Nil:
return nil, nil
default: default:
return nil, nil return nil, nil
} }