code: func (p *parser) parseRawBlock() *ast.BlockStatement {
  	// OPEN_RAW_BLOCK
  	tok := p.shift()
  
  	result := ast.NewBlockStatement(tok.Pos, tok.Line)
  
  	// helperName param* hash?
  	result.Expression = p.parseExpression(tok)
  
  	openName := result.Expression.Canonical()
  
  	// CLOSE_RAW_BLOCK
  	tok = p.shift()
  	if tok.Kind != lexer.TokenCloseRawBlock {
  		errExpected(lexer.TokenCloseRawBlock, tok)
  	}
  
  	// content
  	// @todo Is content mandatory in a raw block ?
  	content := p.parseContent()
  
  	program := ast.NewProgram(tok.Pos, tok.Line)
  	program.AddStatement(content)
  
  	result.Program = program
  
  	// OPEN_END_RAW_BLOCK
  	tok = p.shift()
  	if tok.Kind != lexer.TokenOpenEndRawBlock {
  		// should never happen as it is caught by lexer
  		errExpected(lexer.TokenOpenEndRawBlock, tok)
  	}
  
  	// helperName
  	endID := p.parseHelperName()
  
  	closeName, ok := ast.HelperNameStr(endID)
  	if !ok {
  		errNode(endID, "Erroneous closing expression")
  	}
  
  	if openName != closeName {
  		errNode(endID, fmt.Sprintf("%s doesn't match %s", openName, closeName))
  	}
  
  	// CLOSE_RAW_BLOCK
  	tok = p.shift()
  	if tok.Kind != lexer.TokenCloseRawBlock {
  		errExpected(lexer.TokenCloseRawBlock, tok)
  	}
  
  	return result
  }
docs: // rawBlock : openRawBlock content endRawBlock
  // openRawBlock : OPEN_RAW_BLOCK helperName param* hash? CLOSE_RAW_BLOCK
  // endRawBlock : OPEN_END_RAW_BLOCK helperName CLOSE_RAW_BLOCK