From 28e2f9d27544a839fe2843eb3d6adced614945fe Mon Sep 17 00:00:00 2001 From: Daniel Marcotte Date: Sat, 23 May 2015 11:51:56 -0700 Subject: [PATCH] handlebars: Add support for block params See https://github.com/wycats/handlebars.js/pull/906 --- .../handlebars/parsing/_HbLexer.java | 345 +++++++++--------- resources/messages/HbBundle.properties | 2 + .../handlebars/parsing/HbParsing.java | 35 +- .../handlebars/parsing/HbTokenTypes.java | 2 + .../handlebars/parsing/handlebars.flex | 8 +- test/data/parser/BlockWithBlockParams.hbs | 1 + test/data/parser/BlockWithBlockParams.txt | 33 ++ .../parser/InverseBlockWithBlockParams.hbs | 1 + .../parser/InverseBlockWithBlockParams.txt | 33 ++ .../handlebars/parsing/HbParserSpecTest.java | 10 +- .../parsing/HbTokenizerSpecTest.java | 19 +- 11 files changed, 316 insertions(+), 173 deletions(-) create mode 100644 test/data/parser/BlockWithBlockParams.hbs create mode 100644 test/data/parser/BlockWithBlockParams.txt create mode 100644 test/data/parser/InverseBlockWithBlockParams.hbs create mode 100644 test/data/parser/InverseBlockWithBlockParams.txt diff --git a/gen/com/dmarcotte/handlebars/parsing/_HbLexer.java b/gen/com/dmarcotte/handlebars/parsing/_HbLexer.java index bfec0a0..b919930 100644 --- a/gen/com/dmarcotte/handlebars/parsing/_HbLexer.java +++ b/gen/com/dmarcotte/handlebars/parsing/_HbLexer.java @@ -1,9 +1,9 @@ -/* The following code was generated by JFlex 1.4.3 on 7/26/15 1:26 PM */ +/* The following code was generated by JFlex 1.4.3 on 7/26/15 1:40 PM */ // We base our lexer directly on the official handlebars.l lexer definition, // making some modifications to account for Jison/JFlex syntax and functionality differences // -// Revision ported: https://github.com/wycats/handlebars.js/blob/14b7ef9066d107dc83deedc8e6791947811cc764/src/handlebars.l +// Revision ported: https://github.com/wycats/handlebars.js/blob/b8a9f7264d3b6ac48514272bf35291736cedad00/src/handlebars.l package com.dmarcotte.handlebars.parsing; @@ -17,7 +17,7 @@ /** * This class is a scanner generated by * JFlex 1.4.3 - * on 7/26/15 1:26 PM from the specification file + * on 7/26/15 1:40 PM from the specification file * handlebars.flex */ final class _HbLexer implements FlexLexer { @@ -50,11 +50,11 @@ final class _HbLexer implements FlexLexer { * Translates characters to character classes */ private static final String ZZ_CMAP_PACKED = - "\11\0\1\1\1\2\1\22\1\1\1\1\22\0\1\1\1\16\1\23"+ - "\1\13\1\0\1\37\1\15\1\24\1\6\1\7\3\37\1\17\1\21"+ - "\1\5\12\36\1\0\2\37\1\20\1\12\1\0\1\25\32\0\1\40"+ - "\1\4\1\41\1\14\1\0\1\37\1\35\3\0\1\26\1\34\5\0"+ - "\1\27\5\0\1\32\1\30\1\31\1\33\5\0\1\3\1\37\1\10"+ + "\11\0\1\1\1\2\1\23\1\1\1\1\22\0\1\1\1\16\1\24"+ + "\1\13\1\0\1\40\1\15\1\25\1\6\1\7\3\40\1\17\1\21"+ + "\1\5\12\37\1\0\2\40\1\20\1\12\1\0\1\26\32\0\1\41"+ + "\1\4\1\42\1\14\1\0\1\40\1\36\3\0\1\27\1\35\5\0"+ + "\1\30\5\0\1\33\1\31\1\32\1\34\5\0\1\3\1\22\1\10"+ "\1\11\uff81\0"; /** @@ -70,18 +70,18 @@ final class _HbLexer implements FlexLexer { private static final String ZZ_ACTION_PACKED_0 = "\1\1\1\2\2\0\1\3\1\4\1\0\2\1\1\5"+ "\1\2\2\5\1\6\1\7\1\10\3\5\1\11\1\6"+ - "\2\5\1\12\5\5\1\2\1\5\1\13\2\3\1\4"+ - "\2\5\1\1\1\14\1\0\1\15\1\16\1\17\3\0"+ - "\1\20\1\21\2\0\1\22\5\0\1\23\1\15\1\0"+ - "\1\24\1\3\1\25\1\4\1\25\1\0\1\1\1\14"+ - "\1\26\1\27\1\16\1\30\1\31\1\32\1\16\1\33"+ - "\1\34\1\0\1\17\4\0\1\24\2\35\1\4\1\0"+ - "\1\1\1\36\1\26\1\0\1\37\1\34\3\0\1\40"+ - "\1\41\1\1\1\42\1\43\1\44\1\45\1\0\1\40"+ - "\1\46\1\47"; + "\1\12\2\5\1\13\6\5\1\2\1\5\1\14\2\3"+ + "\1\4\2\5\1\1\1\15\1\0\1\16\1\17\1\20"+ + "\3\0\1\21\1\22\2\0\1\23\6\0\1\24\1\16"+ + "\1\0\1\25\1\3\1\26\1\4\1\26\1\0\1\1"+ + "\1\15\1\27\1\30\1\17\1\31\1\32\1\33\1\17"+ + "\1\34\1\35\1\0\1\20\3\0\1\16\1\0\1\25"+ + "\2\36\1\4\1\0\1\1\1\37\1\27\1\0\1\40"+ + "\1\35\4\0\1\41\1\42\1\43\1\1\1\44\1\45"+ + "\1\46\1\47\1\0\1\42\1\50\1\51"; private static int [] zzUnpackAction() { - int [] result = new int[107]; + int [] result = new int[113]; int offset = 0; offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); return result; @@ -106,23 +106,24 @@ private static int zzUnpackAction(String packed, int offset, int [] result) { private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); private static final String ZZ_ROWMAP_PACKED_0 = - "\0\0\0\42\0\104\0\146\0\210\0\252\0\314\0\356"+ - "\0\u0110\0\u0132\0\u0154\0\u0176\0\u0198\0\u0198\0\u0198\0\u0198"+ - "\0\u01ba\0\u01dc\0\u01fe\0\u0198\0\u0220\0\u0242\0\u0264\0\u0198"+ - "\0\u0286\0\u02a8\0\u02ca\0\u02ec\0\u030e\0\u0330\0\u0352\0\u0198"+ - "\0\u0374\0\u0396\0\u03b8\0\u03da\0\u03fc\0\u041e\0\u0440\0\u0132"+ - "\0\u0198\0\u0462\0\u0484\0\u04a6\0\u04c8\0\u02ec\0\u0198\0\u0198"+ - "\0\u0242\0\u04ea\0\u0198\0\u0264\0\u050c\0\u052e\0\u0550\0\u0572"+ - "\0\u0198\0\u0594\0\u030e\0\u05b6\0\u05d8\0\u05fa\0\u061c\0\u0198"+ - "\0\u063e\0\u0660\0\u0198\0\u0682\0\u0198\0\u06a4\0\u0198\0\u0198"+ - "\0\u0198\0\u0198\0\u06c6\0\u06e8\0\u070a\0\u0198\0\u072c\0\u074e"+ - "\0\u0770\0\u0792\0\u07b4\0\u05fa\0\u0198\0\u07d6\0\u03da\0\u07f8"+ - "\0\u081a\0\u0198\0\u083c\0\u0198\0\u0198\0\u085e\0\u0880\0\u08a2"+ - "\0\u08c4\0\u0198\0\u08e6\0\u0198\0\u0198\0\u0198\0\u0198\0\u0908"+ - "\0\u0198\0\u0198\0\u0198"; + "\0\0\0\43\0\106\0\151\0\214\0\257\0\322\0\365"+ + "\0\u0118\0\u013b\0\u015e\0\u0181\0\u01a4\0\u01a4\0\u01a4\0\u01a4"+ + "\0\u01c7\0\u01ea\0\u020d\0\u01a4\0\u0230\0\u01a4\0\u0253\0\u0276"+ + "\0\u01a4\0\u0299\0\u02bc\0\u02df\0\u0302\0\u0325\0\u0348\0\u036b"+ + "\0\u038e\0\u01a4\0\u03b1\0\u03d4\0\u03f7\0\u041a\0\u043d\0\u0460"+ + "\0\u0483\0\u013b\0\u01a4\0\u04a6\0\u04c9\0\u04ec\0\u050f\0\u0325"+ + "\0\u01a4\0\u01a4\0\u0253\0\u0532\0\u01a4\0\u0276\0\u0555\0\u0578"+ + "\0\u059b\0\u05be\0\u05e1\0\u01a4\0\u0604\0\u0348\0\u0627\0\u064a"+ + "\0\u066d\0\u0690\0\u01a4\0\u06b3\0\u06d6\0\u01a4\0\u06f9\0\u01a4"+ + "\0\u071c\0\u01a4\0\u01a4\0\u01a4\0\u01a4\0\u073f\0\u0762\0\u0785"+ + "\0\u01a4\0\u07a8\0\u07cb\0\u07ee\0\u0811\0\u0834\0\u0857\0\u066d"+ + "\0\u01a4\0\u087a\0\u041a\0\u089d\0\u08c0\0\u01a4\0\u08e3\0\u01a4"+ + "\0\u01a4\0\u0906\0\u0929\0\u094c\0\u0811\0\u01a4\0\u096f\0\u01a4"+ + "\0\u0992\0\u01a4\0\u01a4\0\u01a4\0\u01a4\0\u09b5\0\u01a4\0\u01a4"+ + "\0\u01a4"; private static int [] zzUnpackRowMap() { - int [] result = new int[107]; + int [] result = new int[113]; int offset = 0; offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); return result; @@ -145,62 +146,67 @@ private static int zzUnpackRowMap(String packed, int offset, int [] result) { private static final int [] ZZ_TRANS = zzUnpackTrans(); private static final String ZZ_TRANS_PACKED_0 = - "\3\1\1\11\36\1\1\12\2\13\1\14\1\15\1\16"+ + "\3\1\1\11\37\1\1\12\2\13\1\14\1\15\1\16"+ "\1\17\1\20\1\21\1\22\5\15\1\23\1\24\1\25"+ - "\1\13\1\26\1\27\1\30\1\31\2\12\1\32\2\12"+ - "\1\33\1\12\1\34\1\15\1\35\2\15\2\36\1\37"+ - "\1\40\36\15\2\36\37\15\10\41\1\42\31\41\17\6"+ - "\1\43\22\6\1\15\2\36\5\15\1\44\6\15\1\45"+ - "\22\15\3\10\1\46\36\10\3\1\1\47\36\1\1\50"+ - "\2\51\2\0\1\51\1\0\3\51\5\0\1\50\3\51"+ - "\3\0\11\50\4\0\2\13\17\0\1\13\22\0\1\52"+ - "\110\0\1\53\1\54\40\0\1\55\31\0\1\50\2\51"+ - "\2\0\1\51\1\0\3\51\5\0\1\50\3\51\3\0"+ - "\10\50\1\56\4\0\2\57\5\0\2\57\7\0\1\60"+ - "\1\57\17\0\4\61\1\62\16\61\1\63\16\61\4\64"+ - "\1\65\17\64\1\63\15\64\1\50\2\51\2\0\1\51"+ - "\1\0\3\51\5\0\1\50\3\51\3\0\1\50\1\66"+ - "\7\50\3\0\1\50\2\51\2\0\1\51\1\0\3\51"+ - "\5\0\1\50\3\51\3\0\4\50\1\67\4\50\3\0"+ - "\1\50\2\51\2\0\1\51\1\0\3\51\5\0\1\50"+ - "\3\51\3\0\7\50\1\70\1\50\3\0\1\50\2\71"+ - "\2\0\1\51\1\0\2\71\1\51\5\0\1\50\1\51"+ - "\1\72\1\71\3\0\10\50\1\56\3\0\41\73\1\60"+ - "\1\0\2\36\42\0\1\74\36\0\10\41\1\75\41\41"+ - "\1\76\31\41\17\6\1\77\22\6\10\0\1\100\50\0"+ - "\1\101\22\0\3\10\1\102\36\10\3\0\1\103\41\0"+ - "\1\104\1\0\1\105\3\0\1\106\1\107\1\110\1\111"+ - "\1\112\1\113\33\0\1\114\41\0\1\115\41\0\1\116"+ - "\31\0\2\61\1\0\37\61\2\64\1\0\37\64\1\50"+ - "\2\51\2\0\1\51\1\0\3\51\5\0\1\50\3\51"+ - "\3\0\2\50\1\117\6\50\3\0\1\50\2\51\2\0"+ - "\1\51\1\0\3\51\5\0\1\50\3\51\3\0\5\50"+ - "\1\120\3\50\3\0\1\50\2\51\2\0\1\51\1\0"+ - "\3\51\5\0\1\50\3\51\3\0\1\50\1\121\7\50"+ - "\41\0\1\122\3\0\3\74\1\123\36\74\10\41\1\124"+ - "\31\41\10\0\1\125\31\0\10\6\1\126\6\6\1\77"+ - "\22\6\10\0\1\127\31\0\3\10\1\130\36\10\3\0"+ - "\1\131\41\0\1\132\1\0\1\105\4\0\1\107\1\110"+ - "\1\111\1\112\43\0\1\133\32\0\1\134\41\0\1\135"+ - "\31\0\1\50\2\51\2\0\1\51\1\0\3\51\5\0"+ - "\1\50\3\51\3\0\1\136\10\50\3\0\1\50\2\51"+ - "\2\0\1\51\1\0\3\51\5\0\1\50\3\51\3\0"+ - "\1\137\10\50\3\0\1\50\2\51\2\0\1\51\1\0"+ - "\3\51\5\0\1\50\3\51\3\0\2\50\1\140\6\50"+ - "\4\0\2\71\4\0\2\71\11\0\1\71\13\0\1\122"+ - "\3\0\3\74\1\141\36\74\10\6\1\142\6\6\1\43"+ - "\22\6\3\10\1\143\36\10\5\0\1\144\53\0\1\145"+ - "\22\0\1\50\2\146\2\0\1\51\1\0\2\146\1\51"+ - "\5\0\1\50\2\51\1\146\3\0\11\50\3\0\1\50"+ - "\2\147\2\0\1\51\1\0\2\147\1\51\5\0\1\50"+ - "\2\51\1\147\3\0\11\50\3\0\1\50\2\51\2\0"+ - "\1\51\1\0\3\51\5\0\1\50\3\51\3\0\1\150"+ - "\10\50\6\0\1\151\36\0\3\10\1\143\1\10\1\152"+ - "\34\10\1\50\2\153\2\0\1\51\1\0\2\153\1\51"+ - "\5\0\1\50\2\51\1\153\3\0\11\50\3\0"; + "\1\26\1\13\1\27\1\30\1\31\1\32\2\12\1\33"+ + "\2\12\1\34\1\35\1\36\1\15\1\37\2\15\2\40"+ + "\1\41\1\42\37\15\2\40\40\15\10\43\1\44\32\43"+ + "\17\6\1\45\23\6\1\15\2\40\5\15\1\46\6\15"+ + "\1\47\23\15\3\10\1\50\37\10\3\1\1\51\37\1"+ + "\1\52\2\53\2\0\1\53\1\0\3\53\5\0\1\52"+ + "\4\53\3\0\11\52\4\0\2\13\20\0\1\13\22\0"+ + "\1\54\112\0\1\55\1\56\41\0\1\57\32\0\1\52"+ + "\2\53\2\0\1\53\1\0\3\53\5\0\1\52\4\53"+ + "\3\0\10\52\1\60\4\0\2\61\5\0\2\61\7\0"+ + "\1\62\2\61\17\0\4\63\1\64\17\63\1\65\16\63"+ + "\4\66\1\67\20\66\1\65\15\66\1\52\2\53\2\0"+ + "\1\53\1\0\3\53\5\0\1\52\4\53\3\0\1\52"+ + "\1\70\7\52\3\0\1\52\2\53\2\0\1\53\1\0"+ + "\3\53\5\0\1\52\4\53\3\0\4\52\1\71\4\52"+ + "\3\0\1\52\2\53\2\0\1\53\1\0\3\53\5\0"+ + "\1\52\4\53\3\0\7\52\1\72\1\52\3\0\1\52"+ + "\2\53\2\0\1\53\1\0\3\53\5\0\1\52\4\53"+ + "\3\0\2\52\1\73\6\52\3\0\1\52\2\74\2\0"+ + "\1\53\1\0\2\74\1\53\5\0\1\52\1\53\1\75"+ + "\1\53\1\74\3\0\10\52\1\60\3\0\42\76\1\62"+ + "\1\0\2\40\43\0\1\77\37\0\10\43\1\100\42\43"+ + "\1\101\32\43\17\6\1\102\23\6\10\0\1\103\51\0"+ + "\1\104\23\0\3\10\1\105\37\10\3\0\1\106\42\0"+ + "\1\107\1\0\1\110\3\0\1\111\1\112\1\113\1\114"+ + "\1\115\1\116\34\0\1\117\42\0\1\120\42\0\1\121"+ + "\32\0\2\63\1\0\40\63\2\66\1\0\40\66\1\52"+ + "\2\53\2\0\1\53\1\0\3\53\5\0\1\52\4\53"+ + "\3\0\2\52\1\122\6\52\3\0\1\52\2\53\2\0"+ + "\1\53\1\0\3\53\5\0\1\52\4\53\3\0\5\52"+ + "\1\123\3\52\3\0\1\52\2\53\2\0\1\53\1\0"+ + "\3\53\5\0\1\52\4\53\3\0\1\52\1\124\7\52"+ + "\3\0\1\52\2\125\2\0\1\53\1\0\3\53\5\0"+ + "\1\52\3\53\1\125\3\0\11\52\42\0\1\126\3\0"+ + "\3\77\1\127\37\77\10\43\1\130\32\43\10\0\1\131"+ + "\32\0\10\6\1\132\6\6\1\102\23\6\10\0\1\133"+ + "\32\0\3\10\1\134\37\10\3\0\1\135\42\0\1\136"+ + "\1\0\1\110\4\0\1\112\1\113\1\114\1\115\44\0"+ + "\1\137\33\0\1\140\42\0\1\141\32\0\1\52\2\53"+ + "\2\0\1\53\1\0\3\53\5\0\1\52\4\53\3\0"+ + "\1\142\10\52\3\0\1\52\2\53\2\0\1\53\1\0"+ + "\3\53\5\0\1\52\4\53\3\0\1\143\10\52\3\0"+ + "\1\52\2\53\2\0\1\53\1\0\3\53\5\0\1\52"+ + "\4\53\3\0\2\52\1\144\6\52\4\0\2\145\17\0"+ + "\1\146\1\145\20\0\2\74\4\0\2\74\12\0\1\74"+ + "\13\0\1\126\3\0\3\77\1\147\37\77\10\6\1\150"+ + "\6\6\1\45\23\6\3\10\1\151\37\10\5\0\1\152"+ + "\54\0\1\153\23\0\1\52\2\154\2\0\1\53\1\0"+ + "\2\154\1\53\5\0\1\52\3\53\1\154\3\0\11\52"+ + "\3\0\1\52\2\155\2\0\1\53\1\0\2\155\1\53"+ + "\5\0\1\52\3\53\1\155\3\0\11\52\3\0\1\52"+ + "\2\53\2\0\1\53\1\0\3\53\5\0\1\52\4\53"+ + "\3\0\1\156\10\52\6\0\1\157\37\0\3\10\1\151"+ + "\1\10\1\160\35\10\1\52\2\161\2\0\1\53\1\0"+ + "\2\161\1\53\5\0\1\52\3\53\1\161\3\0\11\52"+ + "\3\0"; private static int [] zzUnpackTrans() { - int [] result = new int[2346]; + int [] result = new int[2520]; int offset = 0; offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); return result; @@ -242,15 +248,16 @@ private static int zzUnpackTrans(String packed, int offset, int [] result) { private static final String ZZ_ATTRIBUTE_PACKED_0 = "\2\1\2\0\2\1\1\0\5\1\4\11\3\1\1\11"+ - "\3\1\1\11\7\1\1\11\7\1\1\0\1\11\2\1"+ - "\3\0\2\11\2\0\1\11\5\0\1\11\1\1\1\0"+ - "\4\1\1\11\1\0\1\1\1\11\1\1\1\11\1\1"+ - "\4\11\2\1\1\0\1\11\4\0\2\1\1\11\1\1"+ - "\1\0\2\1\1\11\1\0\2\11\3\0\1\1\1\11"+ - "\1\1\4\11\1\0\3\11"; + "\1\1\1\11\2\1\1\11\10\1\1\11\7\1\1\0"+ + "\1\11\2\1\3\0\2\11\2\0\1\11\6\0\1\11"+ + "\1\1\1\0\4\1\1\11\1\0\1\1\1\11\1\1"+ + "\1\11\1\1\4\11\2\1\1\0\1\11\3\0\1\1"+ + "\1\0\2\1\1\11\1\1\1\0\2\1\1\11\1\0"+ + "\2\11\4\0\1\11\1\1\1\11\1\1\4\11\1\0"+ + "\3\11"; private static int [] zzUnpackAttribute() { - int [] result = new int[107]; + int [] result = new int[113]; int offset = 0; offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); return result; @@ -570,141 +577,149 @@ else if (zzAtEOF) { case 2: { return HbTokenTypes.WHITE_SPACE; } - case 40: break; - case 14: + case 42: break; + case 15: { return HbTokenTypes.OPEN; } - case 41: break; - case 23: + case 43: break; + case 24: { return HbTokenTypes.OPEN_ENDBLOCK; } - case 42: break; + case 44: break; case 17: - { return HbTokenTypes.ID; - } - case 43: break; - case 16: // lookahead expression with fixed base length zzMarkedPos = zzStartRead + 1; { return HbTokenTypes.ID; } - case 44: break; - case 13: + case 45: break; + case 14: // lookahead expression with fixed lookahead length yypushback(1); { return HbTokenTypes.ID; } - case 45: break; - case 33: + case 46: break; + case 18: + { return HbTokenTypes.ID; + } + case 47: break; + case 35: { yypushback(4); yybegin(comment_end); return HbTokenTypes.COMMENT_CONTENT; } - case 46: break; + case 48: break; case 5: { return HbTokenTypes.INVALID; } - case 47: break; - case 15: + case 49: break; + case 16: { yypopState(); return HbTokenTypes.CLOSE; } - case 48: break; - case 22: + case 50: break; + case 23: { return HbTokenTypes.OPEN_UNESCAPED; } - case 49: break; - case 11: + case 51: break; + case 12: { return HbTokenTypes.ESCAPE_CHAR; } - case 50: break; + case 52: break; case 3: { return HbTokenTypes.UNCLOSED_COMMENT; } - case 51: break; - case 25: + case 53: break; + case 26: { return HbTokenTypes.OPEN_BLOCK; } - case 52: break; - case 19: + case 54: break; + case 20: // lookahead expression with fixed lookahead length yypushback(1); { return HbTokenTypes.NUMBER; } - case 53: break; - case 30: + case 55: break; + case 31: { return HbTokenTypes.OPEN_RAW_BLOCK; } - case 54: break; - case 36: + case 56: break; + case 38: // lookahead expression with fixed base length zzMarkedPos = zzStartRead + 4; { return HbTokenTypes.ELSE; } - case 55: break; + case 57: break; + case 33: + { return HbTokenTypes.OPEN_BLOCK_PARAMS; + } + case 58: break; case 8: { return HbTokenTypes.CLOSE_SEXPR; } - case 56: break; + case 59: break; case 4: { yypopState(); return HbTokenTypes.UNCLOSED_COMMENT; } - case 57: break; - case 31: + case 60: break; + case 32: { yypopState(); yypushState(raw); return HbTokenTypes.CLOSE_RAW_BLOCK; } - case 58: break; + case 61: break; case 9: { return HbTokenTypes.EQUALS; } - case 59: break; - case 10: + case 62: break; + case 11: { return HbTokenTypes.DATA_PREFIX; } - case 60: break; - case 26: + case 63: break; + case 27: { return HbTokenTypes.OPEN_INVERSE; } - case 61: break; - case 20: + case 64: break; + case 21: { // otherwise, if the remaining text just contains the one escaped mustache, then it's all CONTENT return HbTokenTypes.CONTENT; } - case 62: break; - case 34: + case 65: break; + case 36: { return HbTokenTypes.END_RAW_BLOCK; } - case 63: break; + case 66: break; + case 10: + { return HbTokenTypes.CLOSE_BLOCK_PARAMS; + } + case 67: break; case 1: { return HbTokenTypes.CONTENT; } - case 64: break; - case 28: - { yypopState(); return HbTokenTypes.CLOSE_UNESCAPED; - } - case 65: break; - case 35: + case 68: break; + case 37: { yypopState(); yypushState(comment_block); return HbTokenTypes.COMMENT_OPEN; } - case 66: break; - case 27: + case 69: break; + case 29: + { yypopState(); return HbTokenTypes.CLOSE_UNESCAPED; + } + case 70: break; + case 28: { yypopState(); yypushState(comment); return HbTokenTypes.COMMENT_OPEN; } - case 67: break; - case 21: + case 71: break; + case 22: { yypopState(); return HbTokenTypes.COMMENT_CLOSE; } - case 68: break; - case 39: + case 72: break; + case 41: // lookahead expression with fixed base length zzMarkedPos = zzStartRead + 5; { return HbTokenTypes.BOOLEAN; } - case 69: break; - case 37: + case 73: break; + case 39: // lookahead expression with fixed base length zzMarkedPos = zzStartRead + 4; { return HbTokenTypes.BOOLEAN; } - case 70: break; - case 38: + case 74: break; + case 40: { // backtrack over the END_RAW_BLOCK we picked up at the end of this string yypushback(5); @@ -720,16 +735,16 @@ else if (zzAtEOF) { } } } - case 71: break; + case 75: break; case 7: { return HbTokenTypes.OPEN_SEXPR; } - case 72: break; + case 76: break; case 6: { return HbTokenTypes.SEP; } - case 73: break; - case 12: + case 77: break; + case 13: { // backtrack over any stache characters at the end of this string while (yylength() > 0 && yytext().subSequence(yylength() - 1, yylength()).toString().equals("{")) { yypushback(1); @@ -755,12 +770,12 @@ else if (zzAtEOF) { } } } - case 74: break; - case 18: + case 78: break; + case 19: { return HbTokenTypes.STRING; } - case 75: break; - case 29: + case 79: break; + case 30: { // backtrack over any extra stache characters at the end of this string while (yylength() > 2 && yytext().subSequence(yylength() - 3, yylength()).toString().equals("}}}")) { yypushback(1); @@ -770,8 +785,8 @@ else if (zzAtEOF) { yybegin(comment_end); return HbTokenTypes.COMMENT_CONTENT; } - case 76: break; - case 32: + case 80: break; + case 34: { // grab everything up to the next open stache // backtrack over any stache characters or escape characters at the end of this string while (yylength() > 0 @@ -783,11 +798,11 @@ else if (zzAtEOF) { yypopState(); return HbTokenTypes.CONTENT; } - case 77: break; - case 24: + case 81: break; + case 25: { return HbTokenTypes.OPEN_PARTIAL; } - case 78: break; + case 82: break; default: if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { zzAtEOF = true; diff --git a/resources/messages/HbBundle.properties b/resources/messages/HbBundle.properties index 196f6df..73fe733 100644 --- a/resources/messages/HbBundle.properties +++ b/resources/messages/HbBundle.properties @@ -48,6 +48,8 @@ hb.parsing.element.expected.open_partial=Expected Open Partial "{{>" hb.parsing.element.expected.open_unescaped=Expected Open Unescaped "{{{" hb.parsing.element.expected.open_sexpr=Expected Open Subexpression "(" hb.parsing.element.expected.close_sexpr=Expected Close Subexpression ")" +hb.parsing.element.expected.open_block_params=Expected Open Block Param "as |" +hb.parsing.element.expected.close_block_params=Expected Close Block Param "|" hb.parsing.element.expected.outer_element_type=Expected Handlebars Content hb.parsing.element.expected.separator=Expected a Separator "/" or "." hb.parsing.element.expected.string=Expected a String diff --git a/src/com/dmarcotte/handlebars/parsing/HbParsing.java b/src/com/dmarcotte/handlebars/parsing/HbParsing.java index 7c72c0e..543c0f6 100644 --- a/src/com/dmarcotte/handlebars/parsing/HbParsing.java +++ b/src/com/dmarcotte/handlebars/parsing/HbParsing.java @@ -11,7 +11,7 @@ /** * The parser is based directly on Handlebars.yy - * (taken from the following revision: https://github.com/wycats/handlebars.js/blob/eee2c4d4f29e233280907bc89a32556de66fe783/src/handlebars.yy) + * (taken from the following revision: https://github.com/wycats/handlebars.js/blob/b8a9f7264d3b6ac48514272bf35291736cedad00/src/handlebars.yy) *

* Methods mapping to expression in the grammar are commented with the part of the grammar they map to. *

@@ -295,7 +295,7 @@ private boolean parseOpenRawBlock(PsiBuilder builder) { /** * openBlock - * : OPEN_BLOCK sexpr CLOSE { $$ = new yy.MustacheNode($2[0], $2[1]); } + * : OPEN_BLOCK sexpr blockParams? CLOSE { $$ = new yy.MustacheNode($2[0], $2[1]); } * ; */ private boolean parseOpenBlock(PsiBuilder builder) { @@ -306,6 +306,7 @@ private boolean parseOpenBlock(PsiBuilder builder) { } if (parseSexpr(builder)) { + parseBlockParams(builder); parseLeafTokenGreedy(builder, CLOSE); } @@ -337,7 +338,7 @@ private boolean parseOpenInverseChain(PsiBuilder builder) { /** * openInverse - * : OPEN_INVERSE sexpr CLOSE + * : OPEN_INVERSE sexpr blockParams? CLOSE * ; */ private boolean parseOpenInverse(PsiBuilder builder) { @@ -348,6 +349,7 @@ private boolean parseOpenInverse(PsiBuilder builder) { } if (parseSexpr(builder)) { + parseBlockParams(builder); parseLeafTokenGreedy(builder, CLOSE); } @@ -773,6 +775,33 @@ private boolean parsePartialName(PsiBuilder builder) { return false; } + /** + * blockParams + * OPEN_BLOCK_PARAMS ID+ CLOSE_BLOCK_PARAMS + */ + private boolean parseBlockParams(PsiBuilder builder) { + PsiBuilder.Marker blockParamsMarker = builder.mark(); + if (parseLeafToken(builder, OPEN_BLOCK_PARAMS)) { + blockParamsMarker.drop(); + parseLeafToken(builder, ID); + // parse any additional IDs + while (true) { + PsiBuilder.Marker optionalIdMarker = builder.mark(); + if (parseLeafToken(builder, ID)) { + optionalIdMarker.drop(); + } else { + optionalIdMarker.rollbackTo(); + break; + } + } + parseLeafToken(builder, CLOSE_BLOCK_PARAMS); + return true; + } else { + blockParamsMarker.rollbackTo(); + return false; + } + } + /** * dataName * : DATA path diff --git a/src/com/dmarcotte/handlebars/parsing/HbTokenTypes.java b/src/com/dmarcotte/handlebars/parsing/HbTokenTypes.java index 874d777..9ff4cbc 100644 --- a/src/com/dmarcotte/handlebars/parsing/HbTokenTypes.java +++ b/src/com/dmarcotte/handlebars/parsing/HbTokenTypes.java @@ -46,6 +46,8 @@ private HbTokenTypes() { public static final IElementType OPEN_UNESCAPED = new HbElementType("OPEN_UNESCAPED", "hb.parsing.element.expected.open_unescaped"); public static final IElementType OPEN_SEXPR = new HbElementType("OPEN_SEXPR", "hb.parsing.element.expected.open_sexpr"); public static final IElementType CLOSE_SEXPR = new HbElementType("CLOSE_SEXPR", "hb.parsing.element.expected.close_sexpr"); + public static final IElementType OPEN_BLOCK_PARAMS = new HbElementType("OPEN_BLOCK_PARAMS", "hb.parsing.element.expected.open_block_params"); + public static final IElementType CLOSE_BLOCK_PARAMS = new HbElementType("CLOSE_BLOCK_PARAMS", "hb.parsing.element.expected.close_block_params"); public static final IElementType OPEN_RAW_BLOCK = new HbElementType("OPEN_RAW_BLOCK", "hb.parsing.element.expected.open_raw_block"); public static final IElementType END_RAW_BLOCK = new HbElementType("END_RAW_BLOCK", "hb.parsing.element.expected.end_raw_block"); public static final IElementType CLOSE_RAW_BLOCK = new HbElementType("CLOSE_RAW_BLOCK", "hb.parsing.element.expected.close_raw_block"); diff --git a/src/com/dmarcotte/handlebars/parsing/handlebars.flex b/src/com/dmarcotte/handlebars/parsing/handlebars.flex index 09256ea..f221ba9 100644 --- a/src/com/dmarcotte/handlebars/parsing/handlebars.flex +++ b/src/com/dmarcotte/handlebars/parsing/handlebars.flex @@ -1,7 +1,7 @@ // We base our lexer directly on the official handlebars.l lexer definition, // making some modifications to account for Jison/JFlex syntax and functionality differences // -// Revision ported: https://github.com/wycats/handlebars.js/blob/14b7ef9066d107dc83deedc8e6791947811cc764/src/handlebars.l +// Revision ported: https://github.com/wycats/handlebars.js/blob/b8a9f7264d3b6ac48514272bf35291736cedad00/src/handlebars.l package com.dmarcotte.handlebars.parsing; @@ -150,7 +150,7 @@ WhiteSpace = {LineTerminator} | [ \t\f] "{{"\~? { return HbTokenTypes.OPEN; } "=" { return HbTokenTypes.EQUALS; } - "."/[\~\}\t \n\x0B\f\r] { return HbTokenTypes.ID; } + "."/[\~\}\t| \n\x0B\f\r] { return HbTokenTypes.ID; } ".." { return HbTokenTypes.ID; } [\/.] { return HbTokenTypes.SEP; } [\t \n\x0B\f\r]* { return HbTokenTypes.WHITE_SPACE; } @@ -163,6 +163,8 @@ WhiteSpace = {LineTerminator} | [ \t\f] "true"/[}\)\t \n\x0B\f\r] { return HbTokenTypes.BOOLEAN; } "false"/[}\)\t \n\x0B\f\r] { return HbTokenTypes.BOOLEAN; } \-?[0-9]+(\.[0-9]+)?/[}\)\t \n\x0B\f\r] { return HbTokenTypes.NUMBER; } + "as"[\t \n\x0B\f\r]+"|" { return HbTokenTypes.OPEN_BLOCK_PARAMS; } + "|" { return HbTokenTypes.CLOSE_BLOCK_PARAMS; } /* ID is the inverse of control characters. Control characters ranges: @@ -172,7 +174,7 @@ WhiteSpace = {LineTerminator} | [ \t\f] [\[-\^`] [, \, ], ^, `, Exceptions in range: _ [\{-~] {, |, }, ~ */ - [^\t \n\x0B\f\r!\"#%-,\.\/;->@\[-\^`\{-~]+/[\~=}\)\t \n\x0B\f\r\/.] { return HbTokenTypes.ID; } + [^\t \n\x0B\f\r!\"#%-,\.\/;->@\[-\^`\{-~]+/[\~=}\)|\t \n\x0B\f\r\/.] { return HbTokenTypes.ID; } // TODO handlesbars.l extracts the id from within the square brackets. Fix it to match handlebars.l? "["[^\]]*"]" { return HbTokenTypes.ID; } } diff --git a/test/data/parser/BlockWithBlockParams.hbs b/test/data/parser/BlockWithBlockParams.hbs new file mode 100644 index 0000000..54824f5 --- /dev/null +++ b/test/data/parser/BlockWithBlockParams.hbs @@ -0,0 +1 @@ +{{#foo as |bar baz|}}content{{/foo}} \ No newline at end of file diff --git a/test/data/parser/BlockWithBlockParams.txt b/test/data/parser/BlockWithBlockParams.txt new file mode 100644 index 0000000..d9491d7 --- /dev/null +++ b/test/data/parser/BlockWithBlockParams.txt @@ -0,0 +1,33 @@ +HbFile:BlockWithBlockParams.hbs + HbStatementsImpl(STATEMENTS) + HbBlockWrapperImpl(BLOCK_WRAPPER) + HbOpenBlockMustacheImpl(OPEN_BLOCK_STACHE) + HbPsiElementImpl([Hb] OPEN_BLOCK) + PsiElement([Hb] OPEN_BLOCK)('{{#') + HbMustacheNameImpl(MUSTACHE_NAME) + HbPathImpl(PATH) + HbPsiElementImpl([Hb] ID) + PsiElement([Hb] ID)('foo') + PsiWhiteSpace(' ') + HbPsiElementImpl([Hb] OPEN_BLOCK_PARAMS) + PsiElement([Hb] OPEN_BLOCK_PARAMS)('as |') + HbPsiElementImpl([Hb] ID) + PsiElement([Hb] ID)('bar') + PsiWhiteSpace(' ') + HbPsiElementImpl([Hb] ID) + PsiElement([Hb] ID)('baz') + HbPsiElementImpl([Hb] CLOSE_BLOCK_PARAMS) + PsiElement([Hb] CLOSE_BLOCK_PARAMS)('|') + HbPsiElementImpl([Hb] CLOSE) + PsiElement([Hb] CLOSE)('}}') + HbStatementsImpl(STATEMENTS) + PsiElement([Hb] CONTENT)('content') + HbCloseBlockMustacheImpl(CLOSE_BLOCK_STACHE) + HbPsiElementImpl([Hb] OPEN_ENDBLOCK) + PsiElement([Hb] OPEN_ENDBLOCK)('{{/') + HbMustacheNameImpl(MUSTACHE_NAME) + HbPathImpl(PATH) + HbPsiElementImpl([Hb] ID) + PsiElement([Hb] ID)('foo') + HbPsiElementImpl([Hb] CLOSE) + PsiElement([Hb] CLOSE)('}}') \ No newline at end of file diff --git a/test/data/parser/InverseBlockWithBlockParams.hbs b/test/data/parser/InverseBlockWithBlockParams.hbs new file mode 100644 index 0000000..003b0ca --- /dev/null +++ b/test/data/parser/InverseBlockWithBlockParams.hbs @@ -0,0 +1 @@ +{{^foo as |bar baz|}}content{{/foo}} \ No newline at end of file diff --git a/test/data/parser/InverseBlockWithBlockParams.txt b/test/data/parser/InverseBlockWithBlockParams.txt new file mode 100644 index 0000000..1ce6a24 --- /dev/null +++ b/test/data/parser/InverseBlockWithBlockParams.txt @@ -0,0 +1,33 @@ +HbFile:InverseBlockWithBlockParams.hbs + HbStatementsImpl(STATEMENTS) + HbBlockWrapperImpl(BLOCK_WRAPPER) + HbOpenInverseBlockMustacheImpl(OPEN_INVERSE_BLOCK_STACHE) + HbPsiElementImpl([Hb] OPEN_INVERSE) + PsiElement([Hb] OPEN_INVERSE)('{{^') + HbMustacheNameImpl(MUSTACHE_NAME) + HbPathImpl(PATH) + HbPsiElementImpl([Hb] ID) + PsiElement([Hb] ID)('foo') + PsiWhiteSpace(' ') + HbPsiElementImpl([Hb] OPEN_BLOCK_PARAMS) + PsiElement([Hb] OPEN_BLOCK_PARAMS)('as |') + HbPsiElementImpl([Hb] ID) + PsiElement([Hb] ID)('bar') + PsiWhiteSpace(' ') + HbPsiElementImpl([Hb] ID) + PsiElement([Hb] ID)('baz') + HbPsiElementImpl([Hb] CLOSE_BLOCK_PARAMS) + PsiElement([Hb] CLOSE_BLOCK_PARAMS)('|') + HbPsiElementImpl([Hb] CLOSE) + PsiElement([Hb] CLOSE)('}}') + HbStatementsImpl(STATEMENTS) + PsiElement([Hb] CONTENT)('content') + HbCloseBlockMustacheImpl(CLOSE_BLOCK_STACHE) + HbPsiElementImpl([Hb] OPEN_ENDBLOCK) + PsiElement([Hb] OPEN_ENDBLOCK)('{{/') + HbMustacheNameImpl(MUSTACHE_NAME) + HbPathImpl(PATH) + HbPsiElementImpl([Hb] ID) + PsiElement([Hb] ID)('foo') + HbPsiElementImpl([Hb] CLOSE) + PsiElement([Hb] CLOSE)('}}') \ No newline at end of file diff --git a/test/src/com/dmarcotte/handlebars/parsing/HbParserSpecTest.java b/test/src/com/dmarcotte/handlebars/parsing/HbParserSpecTest.java index e127e9d..67eeaca 100644 --- a/test/src/com/dmarcotte/handlebars/parsing/HbParserSpecTest.java +++ b/test/src/com/dmarcotte/handlebars/parsing/HbParserSpecTest.java @@ -2,7 +2,7 @@ /** * Java representations of the validations in Handlebars spec/parser.js - * (Precise revision: https://github.com/wycats/handlebars.js/blob/4282668d47b90da0d00cf4c4a86977f18fc8cde4/spec/parser.js) + * (Precise revision: https://github.com/wycats/handlebars.js/blob/b8a9f7264d3b6ac48514272bf35291736cedad00/spec/parser.js) *

* The tests here should map pretty clearly by name to the `it "does something"` validations in parser.js. *

@@ -138,6 +138,14 @@ public void testOldStandaloneInverseSection() { doTest(true); } + public void testBlockWithBlockParams() { + doTest(true); + } + + public void testInverseBlockWithBlockParams() { + doTest(true); + } + /** * Note on the spec/parser.js porting: some tests at the end are omitted * because they make no sense in the context of the plugin diff --git a/test/src/com/dmarcotte/handlebars/parsing/HbTokenizerSpecTest.java b/test/src/com/dmarcotte/handlebars/parsing/HbTokenizerSpecTest.java index 4b588d0..9a728c0 100644 --- a/test/src/com/dmarcotte/handlebars/parsing/HbTokenizerSpecTest.java +++ b/test/src/com/dmarcotte/handlebars/parsing/HbTokenizerSpecTest.java @@ -5,7 +5,7 @@ /** * Java representation of the validations in the spec/tokenizer.js revision which corresponds * to the revision of handlesbars.l that our lexer is based on - * (https://github.com/wycats/handlebars.js/blob/b09333db7946d20ba7dbc6d32d5496ab8295b8e1/spec/tokenizer.js) + * (https://github.com/wycats/handlebars.js/blob/b8a9f7264d3b6ac48514272bf35291736cedad00/spec/tokenizer.js) *

* All the tests should be nearly identical except that we generate whitespace tokens to give IDEA a better picture * of the text, vs. the actual Handlebars lexer which can just toss whitespace out @@ -499,4 +499,21 @@ public void testTokenizesNestedSubexpressionLiterals() { TokenizerResult result = tokenize("{{foo (bar (lol true) false) (baz 1) (blah 'b') (blorg \"c\")}}"); result.shouldMatchTokenTypes(OPEN, ID, WHITE_SPACE, OPEN_SEXPR, ID, WHITE_SPACE, OPEN_SEXPR, ID, WHITE_SPACE, BOOLEAN, CLOSE_SEXPR, WHITE_SPACE, BOOLEAN, CLOSE_SEXPR, WHITE_SPACE, OPEN_SEXPR, ID, WHITE_SPACE, NUMBER, CLOSE_SEXPR, WHITE_SPACE, OPEN_SEXPR, ID, WHITE_SPACE, STRING, CLOSE_SEXPR, WHITE_SPACE, OPEN_SEXPR, ID, WHITE_SPACE, STRING, CLOSE_SEXPR, CLOSE); } + + /** + * tokenizes block params + */ + public void testTokenizesBlockParams() { + TokenizerResult result = tokenize("{{#foo as |bar|}}"); + result.shouldMatchTokenTypes(OPEN_BLOCK, ID, WHITE_SPACE, OPEN_BLOCK_PARAMS, ID, CLOSE_BLOCK_PARAMS, CLOSE); + + result = tokenize("{{#foo as |bar baz|}}"); + result.shouldMatchTokenTypes(OPEN_BLOCK, ID, WHITE_SPACE, OPEN_BLOCK_PARAMS, ID, WHITE_SPACE, ID, CLOSE_BLOCK_PARAMS, CLOSE); + + result = tokenize("{{#foo as | bar baz |}}"); + result.shouldMatchTokenTypes(OPEN_BLOCK, ID, WHITE_SPACE, OPEN_BLOCK_PARAMS, WHITE_SPACE, ID, WHITE_SPACE, ID, WHITE_SPACE, CLOSE_BLOCK_PARAMS, CLOSE); + + result = tokenize("{{#foo as as | bar baz |}}"); + result.shouldMatchTokenTypes(OPEN_BLOCK, ID, WHITE_SPACE, ID, WHITE_SPACE, OPEN_BLOCK_PARAMS, WHITE_SPACE, ID, WHITE_SPACE, ID, WHITE_SPACE, CLOSE_BLOCK_PARAMS, CLOSE); + } }