All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.neuland.jade4j.parser.Parser Maven / Gradle / Ivy

There is a newer version: 1.3.2
Show newest version
package de.neuland.jade4j.parser;

import de.neuland.jade4j.exceptions.JadeParserException;
import de.neuland.jade4j.expression.ExpressionHandler;
import de.neuland.jade4j.lexer.Assignment;
import de.neuland.jade4j.lexer.Each;
import de.neuland.jade4j.lexer.Lexer;
import de.neuland.jade4j.lexer.token.*;
import de.neuland.jade4j.lexer.token.AttributeList;
import de.neuland.jade4j.parser.node.*;
import de.neuland.jade4j.template.TemplateLoader;
import de.neuland.jade4j.util.CharacterParser;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.Reader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Parser {

    public static final Pattern FILE_EXTENSION_PATTERN = Pattern.compile(".*\\.\\w+$");
    private Lexer lexer;
    private LinkedHashMap blocks = new LinkedHashMap();
    private String[] textOnlyTags = {"script", "style"};
    private Integer _spaces = null;
    private String basePath;
    private final TemplateLoader templateLoader;
    private ExpressionHandler expressionHandler;
    private Parser extending;
    private final String filename;
    private LinkedList contexts = new LinkedList();
    private CharacterParser characterParser;
    private int inMixin = 0;
    private HashMap mixins = new HashMap();
    private int inBlock = 0;
    private PathHelper pathHelper = new PathHelper();
    public Parser(String filename, String basePath, TemplateLoader templateLoader, ExpressionHandler expressionHandler) throws IOException {
        this.filename = filename;
        this.basePath = basePath;
        this.templateLoader = templateLoader;
        this.expressionHandler = expressionHandler;
        lexer = new Lexer(filename, templateLoader,expressionHandler);
        characterParser = new CharacterParser();
        getContexts().push(this);
    }
    public Parser(String src, String filename, String basePath, TemplateLoader templateLoader, ExpressionHandler expressionHandler) throws IOException {
        this.filename = filename;
        this.basePath = basePath;
        this.templateLoader = templateLoader;
        this.expressionHandler = expressionHandler;
        lexer = new Lexer(src,filename, templateLoader,expressionHandler);
        characterParser = new CharacterParser();
        getContexts().push(this);
    }

    public Node parse() {
        BlockNode block = new BlockNode();
        block.setLineNumber(lexer.getLineno());
        block.setFileName(filename);
        while (!(peek() instanceof Eos)) {
            if (peek() instanceof Newline) {
                advance();
            } else {
                Node expr = parseExpr();
                if (expr != null) {
                    block.push(expr);
                }
            }
        }
        if (extending != null) {
            getContexts().push(extending);
            Node rootNode = extending.parse();
            getContexts().pop();

            // hoist mixins
            Set keySet = this.mixins.keySet();
            for (String name : keySet) {
                rootNode.getNodes().push(this.mixins.get(name));
            }
            return rootNode;
        }

        return block;
    }

    private Node parseExpr() {
        Token token = peek();
        if (token instanceof Tag) {
            return parseTag();
        }
        if (token instanceof Mixin) {
            return parseMixin();
        }
        if (token instanceof Block) {
            return parseBlock();
        }
        if (token instanceof MixinBlock) {
            return parseMixinBlock();
        }
        if (token instanceof CaseToken) {
            return parseCase();
        }
        if (token instanceof ExtendsToken) {
            return parseExtends();
        }
        if (token instanceof Include) {
            return parseInclude();
        }
        if (token instanceof Doctype) {
            return parseDoctype();
        }
        if (token instanceof Filter) {
            return parseFilter();
        }
        if (token instanceof Comment) {
            return parseComment();
        }
        if (token instanceof Text) {
            return parseText();
        }
        if (token instanceof Each) {
            return parseEach();
        }
//        if (token instanceof Code) {
//            return parseCode();
//        }
        if (token instanceof Expression) {
            return parseCode();
        }
        if (token instanceof BlockCode) {
            return parseBlockCode();
        }
        if (token instanceof Call) {
            return parseCall();
        }
        if (token instanceof Interpolation) {
            return parseInterpolation();
        }
        if (token instanceof Yield) {
            return parseYield();
        }
        if (token instanceof CssClass || token instanceof CssId) {
            return parseCssClassOrId();
        }
        if (token instanceof While) {
            return parseWhile();
        }
        if (token instanceof If) {
            return parseConditional();
        }
        if (token instanceof Assignment) {
            return parseAssignment();
        }

        throw new JadeParserException(filename, lexer.getLineno(), templateLoader, token);
    }
    /**
     * block code
     */

    private Node parseBlockCode(){
      Token tok = this.expect(BlockCode.class);
      ExpressionNode node;
      Token body = this.peek();
      String text;
      if (body instanceof PipelessText) {
        this.advance();
        text = StringUtils.join(body.getValues(),"\n");
      } else {
        text = "";
      }
        node = new ExpressionNode();
        node.setValue(text);
        node.setLineNumber(tok.getLineNumber());
        return node;
    }

    private Node parseComment() {
        Token token = expect(Comment.class);

        Node block = this.parseTextBlock();
        if (block != null ) {
            BlockCommentNode node = new BlockCommentNode();
            node.setBlock(block);
            node.setBuffered(token.isBuffer());
            node.setLineNumber(token.getLineNumber());
            node.setFileName(filename);
            node.setValue(token.getValue());
            return node;
        } else {
            CommentNode node = new CommentNode();
            node.setBuffered(token.isBuffer());
            node.setLineNumber(token.getLineNumber());
            node.setFileName(filename);
            node.setValue(token.getValue());
            return node;
        }
    }

    private Node parseMixin() {
        Mixin mixinToken = (Mixin) expect(Mixin.class);
        MixinNode node = new MixinNode();
        node.setName(mixinToken.getValue());
        node.setLineNumber(mixinToken.getLineNumber());
        node.setFileName(filename);

        if (StringUtils.isNotBlank(mixinToken.getArguments())) {
            node.setArguments(mixinToken.getArguments());
        }
        List args = node.getArguments();

        String rest;

        if (args.size() > 0) {
            Matcher matcher = Pattern.compile("^\\.\\.\\.").matcher(args.get(args.size() - 1).trim());
            if (matcher.find(0)) {
                rest = args.remove(args.size() - 1).trim().replaceAll("^\\.\\.\\.", "");
                node.setRest(rest);

            }
        }

        if (peek() instanceof Indent) {
            this.inMixin++;
            node.setBlock(block());
            node.setCall(false);
            this.mixins.put(mixinToken.getValue(),node);
            this.inMixin--;
            return node;
        }else{
            node.setCall(true);
            return node;
        }
    }

    private Node parseCall() {
        Token token = expect(Call.class);
        Call callToken = (Call) token;
        MixinNode mixin = new MixinNode();
        mixin.setBlock(new BlockNode());
        mixin.setName(callToken.getValue());
        mixin.setLineNumber(callToken.getLineNumber());
        mixin.setFileName(filename);
        mixin.setCall(true);

        if (StringUtils.isNotBlank(callToken.getArguments())) {
            mixin.setArguments(callToken.getArguments());
        }


        this.tag(mixin);
        if(mixin.hasCodeNode()) {
            mixin.getBlock().push(mixin.getCodeNode());
            mixin.setCodeNode(null);
        }
        if(mixin.hasBlock() && mixin.getBlock().getNodes().isEmpty())
            mixin.setBlock(null);
        return mixin;
    }

    private Node parseCssClassOrId() {
        Token tok = advance();
        Tag div = new Tag("div", line());
        lexer.defer(div);
        lexer.defer(tok);
        return parseExpr();
    }
    private Node parseBlock() {
        Token token = expect(Block.class);
        Block block = (Block) token;
        String mode = block.getMode();
        String name = block.getValue().trim();

        this.inBlock++;
        BlockNode blockNode;
        if (peek() instanceof Indent) {
            blockNode = block();
        } else {
            blockNode = new BlockNode();
            blockNode.setLineNumber(block.getLineNumber());
            blockNode.setFileName(filename);
            LiteralNode node = new LiteralNode();
            node.setValue("");
            blockNode.push(node);
        }
        this.inBlock--;
        blockNode.setName(name);
        blockNode.setLineNumber(line());

        BlockNode prev;
        if(this.blocks.get(name)==null)
            prev = new BlockNode();
        else
            prev = this.blocks.get(name);


        if ("replace".equals(prev.getMode())) {
            this.blocks.put(name, prev);
            return prev;
        }
        LinkedList allNodes = new LinkedList();
        allNodes.addAll(prev.getPrepended());
        allNodes.addAll(blockNode.getNodes());
        allNodes.addAll(prev.getAppended());
        //ok


        if ("append".equals(mode)) {
            LinkedList appendedNodes = new LinkedList();
            if(prev.getParser() == this){
                appendedNodes.addAll(prev.getAppended());
                appendedNodes.addAll(blockNode.getNodes());
            }else{
                appendedNodes.addAll(blockNode.getNodes());
                appendedNodes.addAll(prev.getAppended());
            }
            prev.setAppended(appendedNodes);
        } else if ("prepend".equals(mode)) {
            LinkedList prependedNodes = new LinkedList();
            if(prev.getParser() == this){
                prependedNodes.addAll(blockNode.getNodes());
                prependedNodes.addAll(prev.getPrepended());
            }else{
                prependedNodes.addAll(prev.getPrepended());
                prependedNodes.addAll(blockNode.getNodes());
            }
            prev.setPrepended(prependedNodes);

        }

        blockNode.setNodes(allNodes);
        blockNode.setAppended(prev.getAppended());
        blockNode.setPrepended(prev.getPrepended());
        blockNode.setMode(mode);
        blockNode.setParser(this);
        blockNode.setSubBlock(this.inBlock>0);

        blocks.put(name, blockNode);
        return blockNode;
    }

    private Node parseMixinBlock(){
        Token tok = expect(MixinBlock.class);
        if(this.inMixin == 0){
            throw new JadeParserException(filename, lexer.getLineno(), templateLoader, "Anonymous blocks are not allowed unless they are part of a mixin.");
        }
        return new MixinBlockNode();
    }

    private Node parseInclude() {
        Token token = expect(Include.class);
        Include includeToken = (Include) token;
        String templateName = includeToken.getValue().trim();
        String path = pathHelper.resolvePath(filename,templateName,basePath,templateLoader.getExtension());

        try {
            if (includeToken.getFilter() != null) {
                Reader reader = templateLoader.getReader(path);
                FilterNode node = new FilterNode();
                node.setValue(includeToken.getFilter());
                node.setLineNumber(line());
                node.setFileName(filename);
                TextNode text = new TextNode();
                text.setValue(IOUtils.toString(reader));
                BlockNode block = new BlockNode();
                LinkedList nodes = new LinkedList();
                nodes.add(text);
                block.setNodes(nodes);
                if (block != null)
                    node.setTextBlock(block);
                else {
                    node.setTextBlock(new BlockNode());
                }
                return node;
            }
        } catch (IOException e) {
            throw new JadeParserException(filename, lexer.getLineno(), templateLoader, "the included file [" + templateName + "] could not be opened\n" + e.getMessage());
        }

        // non-jade
        String extension = FilenameUtils.getExtension(path);
        if (!templateLoader.getExtension().equals(extension)) {
            try {
                Reader reader = templateLoader.getReader(path);
                LiteralNode node = new LiteralNode();
                node.setLineNumber(lexer.getLineno());
                node.setFileName(filename);
                node.setValue(IOUtils.toString(reader));
                return node;
            } catch (IOException e) {
                throw new JadeParserException(filename, lexer.getLineno(), templateLoader, "the included file [" + templateName + "] could not be opened\n" + e.getMessage());
            }
        }

        Parser parser = createParser(templateName);
        parser.setBlocks(new LinkedHashMap(blocks));
        parser.setMixins(mixins);
        contexts.push(parser);
        Node ast = parser.parse();
        contexts.pop();
        ast.setFileName(path);
        if (peek() instanceof Indent && ast != null) {
            ((BlockNode) ast).getIncludeBlock().push(block());
        }

        return ast;
    }

    private Node parseExtends() {
        Token token = expect(ExtendsToken.class);
        ExtendsToken extendsToken = (ExtendsToken) token;
        String templateName = extendsToken.getValue().trim();

        Parser parser = createParser(templateName);

        parser.setBlocks(blocks);
        parser.setContexts(contexts);
        extending = parser;

        LiteralNode node = new LiteralNode();
        node.setValue("");
        return node;
    }

    private Parser createParser(String templateName) {
        templateName = ensureJadeExtension(templateName);
        try {
            return new Parser(pathHelper.resolvePath(filename,templateName, basePath, templateLoader.getExtension()), basePath, templateLoader, expressionHandler);
        } catch (IOException e) {
            throw new JadeParserException(filename, lexer.getLineno(), templateLoader, "the template [" + templateName
                    + "] could not be opened\n" + e.getMessage());
        }
    }

    private String ensureJadeExtension(String templateName) {
        if (!templateLoader.getExtension().equals(FilenameUtils.getExtension(templateName))) {
            return templateName + "."+templateLoader.getExtension();
        }
        return templateName;
    }

    private BlockNode parseYield() {
        advance();
        BlockNode block = new BlockNode();
        block.setLineNumber(lexer.getLineno());
        block.setFileName(filename);
        block.setYield(true);
        return block;
    }
    private Node parseInterpolation() {
        Token token = advance();
        String name = token.getValue();
        TagNode tagNode = new TagNode();
        tagNode.setLineNumber(lexer.getLineno());
        tagNode.setFileName(filename);
        tagNode.setName(name);
        tagNode.setBuffer(true);
        return this.tag(tagNode);
    }

    private Node blockExpansion() {
        if (peek() instanceof Colon) {
            Token token = expect(Colon.class);
            Colon colon = (Colon) token;
            BlockNode block = new BlockNode();
            block.setLineNumber(colon.getLineNumber());
            block.setFileName(filename);
            block.getNodes().add(parseExpr());
            return block;
        }
        return block();
    }

    private BlockNode block() {
        BlockNode block = new BlockNode();
        block.setLineNumber(lexer.getLineno());
        block.setFileName(filename);
        expect(Indent.class);
        while (!(peek() instanceof Outdent)) {
            if (peek() instanceof Newline) {
                advance();
            } else {
                Node parseExpr = this.parseExpr();
                parseExpr.setFileName(filename);
                if (parseExpr != null) {
                    block.push(parseExpr);
                }
            }
        }
        expect(Outdent.class);
        return block;
    }

    private List whenBlock() {
        expect(Indent.class);
        List caseConditionalNodes = new LinkedList();
        while (!(peek() instanceof Outdent) && !(peek() instanceof Eos)) {
            if (peek() instanceof Newline) {
                advance();
            } else {
                caseConditionalNodes.add(this.parseCaseCondition());
            }
        }
        if (peek() instanceof Outdent) {
            expect(Outdent.class);
        }
        return caseConditionalNodes;
    }

    private Node parseText() {
        Token tok = expect(Text.class);
        Node[] tokens = this.parseInlineTagsInText(tok.getValue());
        if (tokens.length == 1) return tokens[0];
        BlockNode node = new BlockNode();
        for (int i = 0; i < tokens.length; i++) {
          node.push(tokens[i]);
        }
        node.setValue(tok.getValue());
        node.setLineNumber(tok.getLineNumber());
        node.setFileName(filename);
        return node;
    }

    private Node parseEach() {
        Token token = expect(Each.class);
        Each eachToken = (Each) token;
        EachNode node = new EachNode();
        node.setValue(eachToken.getValue());
        node.setKey(eachToken.getKey());
        node.setCode(eachToken.getCode());
        node.setLineNumber(eachToken.getLineNumber());
        node.setFileName(filename);
        node.setBlock(block());
        if (peek() instanceof Else) {
            advance();
            node.setElseNode(block());
        }
        return node;
    }

    private Node parseWhile() {
        Token token = expect(While.class);
        While whileToken = (While) token;
        WhileNode node = new WhileNode();
        node.setValue(whileToken.getValue());
        node.setLineNumber(whileToken.getLineNumber());
        node.setFileName(filename);
        BlockNode block = block();
        if(block!=null)
            node.setBlock(block);
        else
            node.setBlock(new BlockNode());
        return node;
    }

    private Node parseAssignment() {
        Token token = expect(Assignment.class);
        Token assignmentToken = (Assignment) token;
        Node node = new AssigmentNode();
        node.setName(assignmentToken.getName());
        node.setValue(assignmentToken.getValue());
        node.setLineNumber(assignmentToken.getLineNumber());
        node.setFileName(filename);
        return node;
    }

    private Node parseTag() {
        Token token = advance();
        String name = token.getValue();
        TagNode tagNode = new TagNode();
        tagNode.setLineNumber(lexer.getLineno());
        tagNode.setFileName(filename);
        tagNode.setName(name);
        tagNode.setValue(name);
        tagNode.setSelfClosing(token.isSelfClosing());
        return this.tag(tagNode);
    }

    private Node tag(AttrsNode tagNode){
        // ast-filter look-ahead
        boolean seenAttrs = false;
        while (true) {
            Token incomingToken = peek();
            if (incomingToken instanceof CssId) {
                Token tok = advance();
                tagNode.setAttribute("id", tok.getValue(),false);
                continue;
            } else if (incomingToken instanceof CssClass) {
                Token tok = advance();
                tagNode.setAttribute("class", tok.getValue(),false);
                continue;
            } else if (incomingToken instanceof AttributeList) {
                if (seenAttrs) {
                    throw new JadeParserException(filename,line(),templateLoader,this.filename + ", line " + this.peek().getLineNumber() + ":\nYou should not have jade tags with multiple attributes.");
                    //console.warn(this.filename + ', line ' + this.peek().line + ':\nYou should not have jade tags with multiple attributes.');
                }
                seenAttrs = true;
                AttributeList tok = (AttributeList) advance();
                List attrs = tok.getAttributes();
                tagNode.setSelfClosing(tok.isSelfClosing());

                for (Attribute attr : attrs) {
                    String name = attr.getName();
                    Object value = attr.getValue();
                    tagNode.setAttribute(name, value, attr.isEscaped());
                }
                continue;
            } else if (incomingToken instanceof AttributesBlock) {
                Token tok = this.advance();
                tagNode.addAttributes(tok.getValue());
            } else {
                break;
            }
        }

        // check immediate '.'
        boolean dot = false;
        if (peek() instanceof Dot) {
            dot = true;
            tagNode.setTextOnly(true);
            advance();
        }

        // (text | code | ':')?
        if (peek() instanceof Text) {
            tagNode.getBlock().push(parseText());
        } else if (peek() instanceof Expression) {
            tagNode.setCodeNode(parseCode());
        } else if (peek() instanceof Colon) {
            Token next = advance();
            BlockNode block = new BlockNode();
            block.setLineNumber(next.getLineNumber());
            block.setFileName(filename);
            block.push(parseExpr());
            tagNode.setBlock(block);
        }

        // newline*
        while (peek() instanceof Newline) {
            advance();
        }
        if (tagNode.isTextOnly()) {
            Node block = this.parseTextBlock();
            if(block == null)
                block = new BlockNode();
            tagNode.setBlock(block);
        }else if(peek() instanceof Indent){
            BlockNode block = block();
//            if(!tagNode.hasBlock())
//                tagNode.setBlock(new BlockNode());
            for (int i = 0, len = block.getNodes().size(); i < len; ++i) {
                tagNode.getBlock().push(block.getNodes().get(i));
            }
        }
//        else{
//            if (Arrays.asList(textOnlyTags).contains(tagNode.getName())) {
//                tagNode.setTextOnly(true);
//            }
//        }
//
//        // script special-case
//        if ("script".equals(tagNode.getName())) {
//            String type = tagNode.getAttribute("type");
//            if (!dot && StringUtils.isNotBlank(type)) {
//                String cleanType = type.replaceAll("^['\"]|['\"]$", "");
//                if (!"text/javascript".equals(cleanType)) {
//                    tagNode.setTextOnly(false);
//                }
//            }
//        }
//
//        if (peek() instanceof Indent) {
//            if (tagNode.isTextOnly()) {
//                lexer.setPipeless(true);
//                tagNode.setTextNode(parseTextBlock());
//                lexer.setPipeless(false);
//            } else {
//                Node blockNode = block();
//                if (tagNode.hasBlock()) {
//                    tagNode.getBlock().getNodes().addAll(blockNode.getNodes());
//                } else {
//                    tagNode.setBlock(blockNode);
//                }
//            }
//        }

        return tagNode;

    }
    private Node[] parseInlineTagsInText(String str) {
        int line = this.line();
        Matcher matcher = Pattern.compile("(\\\\)?#\\[((?:.|\\n)*)$").matcher(str);
        TextNode text = new TextNode();
        text.setLineNumber(line);
        text.setFileName(filename);
        if (matcher.find(0) && matcher.groupCount()>1) {
            if (matcher.group(1) != null) { // escape
                text.setValue(str.substring(0, matcher.start()) + "#[");//Not sure if Matcher.end() is correct
                Node[] rest = this.parseInlineTagsInText(matcher.group(2));
                if (rest[0] instanceof TextNode) {
                    text.setValue(text.getValue() + rest[0].getValue());
                    rest = ArrayUtils.remove(rest,0);
                }
                Node[] textNodes = {text};
                return ArrayUtils.addAll(textNodes, rest);
            } else {
                text.setValue(str.substring(0, matcher.start()));//Not sure if Matcher.end() is correct
                Node[] textNodes = {text};
                Node[] buffer = textNodes;
                String rest = matcher.group(2);
                CharacterParser.Match range = null;
                try {
                    range = characterParser.parseMax(rest);
                } catch (CharacterParser.SyntaxError syntaxError) {
                    throw new JadeParserException(this.filename,line,templateLoader," See "+matcher.group(0));
                }
                Parser inner = null;
                try {
                    inner = new Parser(range.getSrc(), this.filename,this.basePath, this.templateLoader,this.expressionHandler); //Need to be reviewed
                } catch (IOException e) {
                    throw new JadeParserException(this.filename,line,templateLoader,"Could not parse text");
                }
                buffer = ArrayUtils.add(buffer,inner.parse());
                return ArrayUtils.addAll(buffer, this.parseInlineTagsInText(rest.substring(range.getEnd() + 1)));
            }
        } else {
            text.setValue(str);
            Node[] textNodes = {text};
            return textNodes;
        }
    }

    private Node parseTextBlock() {
        BlockNode blockNode = new BlockNode();
        blockNode.setLineNumber(line());
        blockNode.setFileName(filename);
        Token body  = peek();
        if(!(body instanceof PipelessText)){
            return null;
        }
        this.advance();
        ArrayList values = body.getValues();
        Node[] textNodes = {};
        for (String value : values) {
            textNodes = ArrayUtils.addAll(textNodes,parseInlineTagsInText(value));
        }
        blockNode.setNodes(new LinkedList(Arrays.asList(textNodes)));
        return blockNode;
    }

    private Node parseConditional() {
        If conditionalToken = (If) expect(If.class);
        ConditionalNode conditional = new ConditionalNode();
        conditional.setLineNumber(conditionalToken.getLineNumber());
        conditional.setFileName(filename);

        List conditions = conditional.getConditions();

        IfConditionNode main = new IfConditionNode(conditionalToken.getValue(), conditionalToken.getLineNumber());
        main.setInverse(conditionalToken.isInverseCondition());
        main.setBlock(block());
        conditions.add(main);

        while (peek() instanceof ElseIf) {
            ElseIf token = (ElseIf) expect(ElseIf.class);
            IfConditionNode elseIf = new IfConditionNode(token.getValue(), token.getLineNumber());
            elseIf.setBlock(block());
            conditions.add(elseIf);
        }

        if (peek() instanceof Else) {
            Else token = (Else) expect(Else.class);
            IfConditionNode elseNode = new IfConditionNode(null, token.getLineNumber());
            elseNode.setDefault(true);
            elseNode.setBlock(block());
            conditions.add(elseNode);
        }

        return conditional;
    }
//    var block = new nodes.Block;
//    block.line = this.line();
//    block.filename = this.filename;
//    this.expect('indent');
//    while ('outdent' != this.peek().type) {
//      switch (this.peek().type) {
//        case 'comment':
//        case 'newline':
//          this.advance();
//          break;
//        case 'when':
//          block.push(this.parseWhen());
//          break;
//        case 'default':
//          block.push(this.parseDefault());
//          break;
//        default:
//          throw new Error('Unexpected token "' + this.peek().type
//                          + '", expected "when", "default" or "newline"');
//      }
//    }
//    this.expect('outdent');
//
//    node.block = block;
//
//    return node;
    private Node parseBlockExpansion(){
      if (this.peek() instanceof Colon) {
        this.advance();
          BlockNode blockNode = new BlockNode();
          blockNode.push(this.parseExpr());
          return blockNode;
      } else {
        return this.block();
      }
    }

    private Node parseCase() {
        String val = expect(CaseToken.class).getValue();
        Node node = new CaseNode();
        node.setValue(val);
        node.setLineNumber(line());

        Node block = new BlockNode();
        block.setLineNumber(line());
        block.setFileName(filename);
        expect(Indent.class);
        while (!(peek() instanceof Outdent)) {
            if (peek() instanceof Comment) {
                advance();
            } else if (peek() instanceof Newline) {
                advance();
            } else if (peek() instanceof When) {
                block.push(parseWhen());
            } else if (peek() instanceof Default) {
                block.push(parseDefault());
            } else {
                throw new JadeParserException(filename,lexer.getLineno(),templateLoader,"Unexpected token \"" + this.peek() + "\", expected \"when\", \"default\" or \"newline\"");
            }
        }
        expect(Outdent.class);
        node.setBlock(block);
        return node;
    }
    /**
     * when
     */

    private Node parseWhen(){
      String val = this.expect(When.class).getValue();
        CaseNode.When when = new CaseNode.When();
        when.setValue(val);
        if (!(this.peek() instanceof Newline)) {
          when.setBlock(this.parseBlockExpansion());
        }
        return when;

    }

    /**
     * default
     */

    private Node parseDefault(){
        expect(Default.class);
        Node when = new CaseNode.When();
        when.setValue("default");
        when.setBlock(this.parseBlockExpansion());
        return when;
    }

    private CaseConditionNode parseCaseCondition() {
        CaseConditionNode node = new CaseConditionNode();
        Token token = null;
        if (peek() instanceof When) {
            token = expect(When.class);
        } else {
            token = expect(Default.class);
            node.setDefault(true);
        }
        node.setLineNumber(token.getLineNumber());
        node.setFileName(filename);
        node.setValue(token.getValue());
        node.setBlock(blockExpansion());
        return node;
    }

    private Node parseCode() {
        Token token = expect(Expression.class);
        Expression expressionToken = (Expression) token;
        ExpressionNode codeNode = new ExpressionNode();
        codeNode.setValue(expressionToken.getValue());
        codeNode.setBuffer(expressionToken.isBuffer());
        codeNode.setEscape(expressionToken.isEscape());
        codeNode.setLineNumber(expressionToken.getLineNumber());
        codeNode.setFileName(filename);
        boolean block = false;
//        int i = 1;
//        while (lookahead(i) != null && lookahead(i) instanceof Newline)
//            ++i;
        block = peek() instanceof Indent;
        if (block) {
            codeNode.setBlock((BlockNode) block());
        }
        return codeNode;
    }

    private Node parseDoctype() {
        Token token = expect(Doctype.class);
        Doctype doctype = (Doctype) token;
        DoctypeNode doctypeNode = new DoctypeNode();
        doctypeNode.setValue(doctype.getValue());
        return doctypeNode;
    }

    // var tok = this.expect('code')
    // , node = new nodes.Code(tok.val, tok.buffer, tok.escape)
    // , block
    // , i = 1;
    // node.line = this.line();
    // while (this.lookahead(i) && 'newline' == this.lookahead(i).type) ++i;
    // block = 'indent' == this.lookahead(i).type;
    // if (block) {
    // this.skip(i-1);
    // node.block = this.block();
    // }
    // return node;

    private Node parseFilter() {
        Token token = expect(Filter.class);
        Filter filterToken = (Filter) token;
        AttributeList attr = (AttributeList) accept(AttributeList.class);
        lexer.setPipeless(true);
        Node tNode = parseTextBlock();
        lexer.setPipeless(false);

        FilterNode node = new FilterNode();
        node.setValue(filterToken.getValue());
        node.setLineNumber(line());
        node.setFileName(filename);
        if(tNode!=null)
            node.setTextBlock(tNode);
        else{
            node.setTextBlock(new BlockNode());
        }
        if (attr != null) {
            node.setAttributes(convertToNodeAttributes(attr));
        }
        return node;
    }

    private Node parseASTFilter() {
        Token token = expect(Filter.class);
        Filter filterToken = (Filter) token;
        AttributeList attr = (AttributeList) accept(AttributeList.class);

        token = expect(Colon.class);

        FilterNode node = new FilterNode();
        node.setValue(filterToken.getValue());
        node.setBlock(block());
        node.setLineNumber(line());
        node.setFileName(filename);
        node.setAttributes(convertToNodeAttributes(attr));
        return node;
    }

    private List convertToNodeAttributes(AttributeList attr) {
        List attributes = attr.getAttributes();
        List attributeNodes = new LinkedList();
        for (Attribute attribute : attributes) {
            attributeNodes.add(new Attr(attribute.getName(),attribute.getValue(),attribute.isEscaped()));
        }
        return attributeNodes;
    }

    private Token lookahead(int i) {
        return lexer.lookahead(i);
    }

    private Token peek() {
        return lookahead(1);
    }

    private void skip(int n) {
        while (n > 0) {
            lexer.advance();
            n = n - 1;
        }
    }

    private Token advance() {
        return lexer.advance();
    }

    @SuppressWarnings("rawtypes")
    private Token accept(Class clazz) {
        if (this.peek().getClass().equals(clazz)) {
            return lexer.advance();
        }
        return null;
    }

    private int line() {
        return lexer.getLineno();
    }

    @SuppressWarnings("rawtypes")
    private Token expect(Class expectedTokenClass) {
        Token t = this.peek();
        if (t.getClass().equals(expectedTokenClass)) {
            return advance();
        } else {
            throw new JadeParserException(filename, lexer.getLineno(), templateLoader, expectedTokenClass, t.getClass());
        }
    }

    public Map getBlocks() {
        return blocks;
    }

    public void setBlocks(LinkedHashMap blocks) {
        this.blocks = blocks;
    }

    public LinkedList getContexts() {
        return contexts;
    }

    public void setContexts(LinkedList contexts) {
        this.contexts = contexts;
    }

    public void setMixins(HashMap mixins) {
        this.mixins = mixins;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy