...

Source file src/github.com/noirbizarre/gonja/parser/variable.go

Documentation: github.com/noirbizarre/gonja/parser

     1  package parser
     2  
     3  import (
     4  	// "fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/noirbizarre/gonja/nodes"
     9  	"github.com/noirbizarre/gonja/tokens"
    10  	log "github.com/sirupsen/logrus"
    11  )
    12  
    13  func (p *Parser) parseNumber() (nodes.Expression, error) {
    14  	log.WithFields(log.Fields{
    15  		"current": p.Current(),
    16  	}).Trace("parseNumber")
    17  	t := p.Match(tokens.Integer, tokens.Float)
    18  	if t == nil {
    19  		return nil, p.Error("Expected a number", t)
    20  	}
    21  
    22  	if t.Type == tokens.Integer {
    23  		i, err := strconv.Atoi(t.Val)
    24  		if err != nil {
    25  			return nil, p.Error(err.Error(), t)
    26  		}
    27  		nr := &nodes.Integer{
    28  			Location: t,
    29  			Val:      i,
    30  		}
    31  		return nr, nil
    32  	} else {
    33  		f, err := strconv.ParseFloat(t.Val, 64)
    34  		if err != nil {
    35  			return nil, p.Error(err.Error(), t)
    36  		}
    37  		fr := &nodes.Float{
    38  			Location: t,
    39  			Val:      f,
    40  		}
    41  		return fr, nil
    42  	}
    43  }
    44  
    45  func (p *Parser) parseString() (nodes.Expression, error) {
    46  	log.WithFields(log.Fields{
    47  		"current": p.Current(),
    48  	}).Trace("parseString")
    49  	t := p.Match(tokens.String)
    50  	if t == nil {
    51  		return nil, p.Error("Expected a string", t)
    52  	}
    53  	str := strconv.Quote(t.Val)
    54  	replaced := strings.Replace(str, `\\`, "\\", -1)
    55  	newstr, err := strconv.Unquote(replaced)
    56  	if err != nil {
    57  		return nil, p.Error(err.Error(), t)
    58  	}
    59  	sr := &nodes.String{
    60  		Location: t,
    61  		Val:      newstr,
    62  	}
    63  	return sr, nil
    64  }
    65  
    66  func (p *Parser) parseCollection() (nodes.Expression, error) {
    67  	switch p.Current().Type {
    68  	case tokens.Lbracket:
    69  		return p.parseList()
    70  	case tokens.Lparen:
    71  		return p.parseTuple()
    72  	case tokens.Lbrace:
    73  		return p.parseDict()
    74  	default:
    75  		return nil, nil
    76  	}
    77  }
    78  
    79  func (p *Parser) parseList() (nodes.Expression, error) {
    80  	log.WithFields(log.Fields{
    81  		"current": p.Current(),
    82  	}).Trace("parseList")
    83  	t := p.Match(tokens.Lbracket)
    84  	if t == nil {
    85  		return nil, p.Error("Expected [", t)
    86  	}
    87  
    88  	if p.Match(tokens.Rbracket) != nil {
    89  		// Empty list
    90  		return &nodes.List{t, []nodes.Expression{}}, nil
    91  	}
    92  
    93  	expr, err := p.ParseExpression()
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	list := []nodes.Expression{expr}
    98  
    99  	for p.Match(tokens.Comma) != nil {
   100  		if p.Peek(tokens.Rbracket) != nil {
   101  			// Trailing coma
   102  			break
   103  		}
   104  		expr, err := p.ParseExpression()
   105  		if err != nil {
   106  			return nil, err
   107  		}
   108  		if expr == nil {
   109  			return nil, p.Error("Expected a value", p.Current())
   110  		}
   111  		list = append(list, expr)
   112  	}
   113  
   114  	if p.Match(tokens.Rbracket) == nil {
   115  		return nil, p.Error("Expected ]", p.Current())
   116  	}
   117  
   118  	return &nodes.List{t, list}, nil
   119  }
   120  
   121  func (p *Parser) parseTuple() (nodes.Expression, error) {
   122  	log.WithFields(log.Fields{
   123  		"current": p.Current(),
   124  	}).Trace("parseTuple")
   125  	t := p.Match(tokens.Lparen)
   126  	if t == nil {
   127  		return nil, p.Error("Expected (", t)
   128  	}
   129  	expr, err := p.ParseExpression()
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	list := []nodes.Expression{expr}
   134  
   135  	trailingComa := false
   136  
   137  	for p.Match(tokens.Comma) != nil {
   138  		if p.Peek(tokens.Rparen) != nil {
   139  			// Trailing coma
   140  			trailingComa = true
   141  			break
   142  		}
   143  		expr, err := p.ParseExpression()
   144  		if err != nil {
   145  			return nil, err
   146  		}
   147  		if expr == nil {
   148  			return nil, p.Error("Expected a value", p.Current())
   149  		}
   150  		list = append(list, expr)
   151  	}
   152  
   153  	if p.Match(tokens.Rparen) == nil {
   154  		return nil, p.Error("Unbalanced parenthesis", t)
   155  		// return nil, p.Error("Expected )", p.Current())
   156  	}
   157  
   158  	if len(list) > 1 || trailingComa {
   159  		return &nodes.Tuple{t, list}, nil
   160  	} else {
   161  		return expr, nil
   162  	}
   163  }
   164  
   165  func (p *Parser) parsePair() (*nodes.Pair, error) {
   166  	log.WithFields(log.Fields{
   167  		"current": p.Current(),
   168  	}).Trace("parsePair")
   169  	key, err := p.ParseExpression()
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	if p.Match(tokens.Colon) == nil {
   175  		return nil, p.Error("Expected \":\"", p.Current())
   176  	}
   177  	value, err := p.ParseExpression()
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	return &nodes.Pair{
   182  		Key:   key,
   183  		Value: value,
   184  	}, nil
   185  }
   186  
   187  func (p *Parser) parseDict() (nodes.Expression, error) {
   188  	log.WithFields(log.Fields{
   189  		"current": p.Current(),
   190  	}).Trace("parseDict")
   191  	t := p.Match(tokens.Lbrace)
   192  	if t == nil {
   193  		return nil, p.Error("Expected {", t)
   194  	}
   195  
   196  	dict := &nodes.Dict{
   197  		Token: t,
   198  		Pairs: []*nodes.Pair{},
   199  	}
   200  
   201  	if p.Peek(tokens.Rbrace) == nil {
   202  		pair, err := p.parsePair()
   203  		if err != nil {
   204  			return nil, err
   205  		}
   206  		dict.Pairs = append(dict.Pairs, pair)
   207  	}
   208  
   209  	for p.Match(tokens.Comma) != nil {
   210  		pair, err := p.parsePair()
   211  		if err != nil {
   212  			return nil, err
   213  		}
   214  		dict.Pairs = append(dict.Pairs, pair)
   215  	}
   216  
   217  	if p.Match(tokens.Rbrace) == nil {
   218  		return nil, p.Error("Expected }", p.Current())
   219  	}
   220  
   221  	return dict, nil
   222  }
   223  
   224  func (p *Parser) ParseVariable() (nodes.Expression, error) {
   225  	log.WithFields(log.Fields{
   226  		"current": p.Current(),
   227  	}).Trace("ParseVariable")
   228  
   229  	t := p.Match(tokens.Name)
   230  	if t == nil {
   231  		return nil, p.Error("Expected an identifier.", t)
   232  	}
   233  
   234  	switch t.Val {
   235  	case "true", "True":
   236  		br := &nodes.Bool{
   237  			Location: t,
   238  			Val:      true,
   239  		}
   240  		return br, nil
   241  	case "false", "False":
   242  		br := &nodes.Bool{
   243  			Location: t,
   244  			Val:      false,
   245  		}
   246  		return br, nil
   247  	}
   248  
   249  	var variable nodes.Node = &nodes.Name{t}
   250  
   251  	for !p.Stream.EOF() {
   252  		if dot := p.Match(tokens.Dot); dot != nil {
   253  			getattr := &nodes.Getattr{
   254  				Location: dot,
   255  				Node:     variable,
   256  			}
   257  			tok := p.Match(tokens.Name, tokens.Integer)
   258  			switch tok.Type {
   259  			case tokens.Name:
   260  				getattr.Attr = tok.Val
   261  			case tokens.Integer:
   262  				i, err := strconv.Atoi(tok.Val)
   263  				if err != nil {
   264  					return nil, p.Error(err.Error(), tok)
   265  				}
   266  				getattr.Index = i
   267  			default:
   268  				return nil, p.Error("This token is not allowed within a variable name.", p.Current())
   269  			}
   270  			variable = getattr
   271  			continue
   272  		} else if bracket := p.Match(tokens.Lbracket); bracket != nil {
   273  			getitem := &nodes.Getitem{
   274  				Location: dot,
   275  				Node:     variable,
   276  			}
   277  			tok := p.Match(tokens.String, tokens.Integer)
   278  			switch tok.Type {
   279  			case tokens.String:
   280  				getitem.Arg = tok.Val
   281  			case tokens.Integer:
   282  				i, err := strconv.Atoi(tok.Val)
   283  				if err != nil {
   284  					return nil, p.Error(err.Error(), tok)
   285  				}
   286  				getitem.Index = i
   287  			default:
   288  				return nil, p.Error("This token is not allowed within a variable name.", p.Current())
   289  			}
   290  			variable = getitem
   291  			if p.Match(tokens.Rbracket) == nil {
   292  				return nil, p.Error("Unbalanced bracket", bracket)
   293  			}
   294  			continue
   295  
   296  		} else if lparen := p.Match(tokens.Lparen); lparen != nil {
   297  			call := &nodes.Call{
   298  				Location: lparen,
   299  				Func:     variable,
   300  				Args:     []nodes.Expression{},
   301  				Kwargs:   map[string]nodes.Expression{},
   302  			}
   303  			// if p.Peek(tokens.VariableEnd) != nil {
   304  			// 	return nil, p.Error("Filter parameter required after '('.", nil)
   305  			// }
   306  
   307  			for p.Match(tokens.Comma) != nil || p.Match(tokens.Rparen) == nil {
   308  				// TODO: Handle multiple args and kwargs
   309  				v, err := p.ParseExpression()
   310  				if err != nil {
   311  					return nil, err
   312  				}
   313  
   314  				if p.Match(tokens.Assign) != nil {
   315  					key := v.Position().Val
   316  					value, errValue := p.ParseExpression()
   317  					if errValue != nil {
   318  						return nil, errValue
   319  					}
   320  					call.Kwargs[key] = value
   321  				} else {
   322  					call.Args = append(call.Args, v)
   323  				}
   324  			}
   325  			variable = call
   326  			// We're done parsing the function call, next variable part
   327  			continue
   328  		}
   329  
   330  		// No dot or function call? Then we're done with the variable parsing
   331  		break
   332  	}
   333  
   334  	return variable, nil
   335  }
   336  
   337  // IDENT | IDENT.(IDENT|NUMBER)...
   338  func (p *Parser) ParseVariableOrLiteral() (nodes.Expression, error) {
   339  	log.WithFields(log.Fields{
   340  		"current": p.Current(),
   341  	}).Trace("ParseVariableOrLiteral")
   342  	t := p.Current()
   343  
   344  	if t == nil {
   345  		return nil, p.Error("Unexpected EOF, expected a number, string, keyword or identifier.", p.Current())
   346  	}
   347  
   348  	// Is first part a number or a string, there's nothing to resolve (because there's only to return the value then)
   349  	switch t.Type {
   350  	case tokens.Integer, tokens.Float:
   351  		return p.parseNumber()
   352  
   353  	case tokens.String:
   354  		return p.parseString()
   355  
   356  	case tokens.Lparen, tokens.Lbrace, tokens.Lbracket:
   357  		return p.parseCollection()
   358  
   359  	case tokens.Name:
   360  		return p.ParseVariable()
   361  
   362  	default:
   363  		return nil, p.Error("Expected either a number, string, keyword or identifier.", t)
   364  	}
   365  }
   366  

View as plain text