cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

Looking for quick-add? Try the external resource search, it's quicker and gives you access to the most recent version of thousands of libraries. ☝️

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Looking for quick-add? Try the external resource search, it's quicker and gives you access to the most recent version of thousands of libraries. ☝️

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
               <div id="css-table">
  <div id="col-1">
   <ul>
    <li><a href="#col-1-tab-1">Example 6<br>&nbsp;</a></li>
   </ul>
   <div id="col-1-tab-1">
    <article>
    <h1>matched pairs&mdash;recursion</h1>
     <p class="title">
      <b>pattern syntax:</b>
     </p>
     <textarea id="syntax" wrap="off"></textarea>
     <p class="title">
      <b>input string:</b>
     </p>
     <textarea id="input" wrap="off"></textarea>
     <p>
      <button id="exec" value="exec">exec()</button>
     </p>
    </article>
   </div>
  </div>
  <div id="col-2">
   <ul>
    <li><a href="#col-2-tab-1">matched<br>pairs
    </a></li>
    <li><a href="#col-2-tab-2">unmatched<br>pairs
    </a></li>
    <li><a href="#col-2-tab-3">internal<br>pairs
    </a></li>
    <li><a href="#col-2-tab-4">inserted<br>text
    </a></li>
    <li><a href="#col-2-tab-5">HTML<br>tags
    </a></li>
   </ul>

   <div id="col-2-tab-1">
    <article>
     <div class="example">
      <div class="discuss">
       <p>
        This is the basic pattern syntax for matching nested pairs.
        <code>L</code>
        and
        <code>R</code>
        can be most anything as long as the
        <code>L</code>
        rule can't match any of the same phrases as the
        <code>R</code>
        rule.
       </p>
       <p>
        Try changing the parentheses,
        <code>()</code>
        to brackets
        <code>{}</code>
        .<br>
        <button id="matched-brackets">do it</button>
        for me.
       </p>
       <p>
        Or even something more complicated.<br>
        <button id="matched-html-comments">do it</button>
        for me.
       </p>
      </div>
      <div id="matched-here" class="result"></div>
     </div>
    </article>
   </div>

   <div id="col-2-tab-2">
    <article>
     <div class="example">
      <div class="discuss">
       <p>What happens if the brackets are not matched? If you were adventurous with the previous tab you might have seen
        this already. But try it with some unbalanced pairs.</p>
       <p>
        If the parentheses are just unbalanced it will find the longest match to the first occurrence of any matched pairs.<br>
        <button id="long-unbalanced">do it</button>
        for me.
       </p>
       <p>
        But if there are no matched pairs at all&mdash;out of luck.<br>
        <button id="no-pairs">do it</button> for me.
       </p>
       <p>Often you will want to succeed only if the entire string is balanced.
       For that we need use anchors to match the beginning and
        ending of the string as well.<br>
        <button id="anchors-unmatched">do it</button> with unmatched pairs.<br>
        <button id="anchors-matched">do it</button> with matched pairs.
        </p>
      </div>
      <div id="unmatched-here" class="result"></div>
     </div>
    </article>
   </div>

   <div id="col-2-tab-3">
    <article>
     <div class="example">
      <div class="discuss">
       <p>In case you haven't noticed it yet, the simple formula for balancing matched pairs is missing something important.
        The parentheses set up here are nested with equal numbers of left and right parentheses. But you don't get a match to
        the entire string. It misses the internal pairs.</p>
       <p>
        To match pairs within pairs we need a slight modification. Between left and right we need to match <i>one or more</i>
        pairs. That looks like this:<br>
        <button id="internal">do it</button>
        for me.
       </p>
       <p>
        As long as the numbers of left and right parentheses are the same, it should work.<br>
        <button id="internal-more">do it</button>
        again.
       </p>
      </div>
      <div id="internal-here" class="result"></div>
     </div>
    </article>
   </div>

   <div id="col-2-tab-4">
    <article>
     <div class="example">
      <div class="discuss">
       <p>Now that you've got matching of paired brackets its a fairly easy matter to insert text between the brackets. You
        want to allow text after the left and before right brackets.
       The text is optional and can be most anything you like as long as it can't match either of the brackets.
       Here text is any printing ASCII characters except the brackets themselves.
       </p>
       <p>
        But if you really need a bracket in your text, with just a little extra effort you can escape it. 
        Here I've escaped it with the backslash, <code>\</code>, character.<br>
        <button id="escape-it">do it</button> escape it for me.
       </p>
       <p>
        or quote it:<br>
        <button id="quote-it">do it</button>
        quote it for me.
       </p>
       <p>Note that with both the escaped and quoted text it is important to put them as the first alternate characters.
        Don't forget the first-match-wins rule.</p>
      </div>
      <div id="text-here" class="result"></div>
     </div>
    </article>
   </div>

   <div id="col-2-tab-5">
    <article>
     <div class="example">
      <div class="discuss">
       <p>
        If you have been following along with the previous tabbed examples, it is just a short step to match HTML tags. I'll
        keep it simple here, not allowing for attributes, self-closing tags or specific tag names. Also, the text between tags
        cannot contain the
        <code>&lt;</code>
        character and no provisions are made to escape it. I just want a demonstration of the basics without obscuring them with
        too many details.
       </p>
       <p>
       I'm sure you will have noticed by now that there is no check that the name in a closing tag matches that of
        its paired, opening tag. That's a toughy. It requires some advanced features, but it can be done with <b>apg-exp</b>
        and here it is.<br>
        <button id="parent-mode">show me</button>
       </p>
       Don't believe it? Try it again with tag names that don't match.<br>
        <button id="parent-mode2">try it</button> now.
       <p>
       </p>
       I haven't talked about back references or parent-mode.
       You need them both for this example.
       But if you are interested in the details, check out the references in the "Where to Go Next" section.
       <p>
        </p>
      </div>
      <div id="html-tags-here" class="result"></div>
     </div>
    </article>
   </div>
  </div>
 </div>

            
          
!
            
              /* Pen-specific styles */
* {
	box-sizing: border-box;
}

body {
	color: #000;
	font-size: .8rem;
	line-height: 150%;
}

h1 {
	font-size: 1.75rem;
	margin: 0 0 0.75rem 0;
	font-family: 'Open Sans', sans-serif;
	font-weight: 700;
}

h2 {
	font-family: 'Open Sans', sans-serif;
	font-weight: 700;
}

article {
	font-family: 'Open Sans', sans-serif;
	font-weight: 400;
}

b {
	font-family: 'Open Sans', sans-serif;
	font-weight: 700;
}

i {
	font-family: 'Open Sans', sans-serif;
	font-style: italic;
}

textarea {
	width: 100%;
}

button {
	cursor: pointer;
	cursor: hand;
}

p.title {
	margin: 0px 0px 3px 0px;
}

a.link {
	color: #4284B0;
}

a.link:visited {
	color: #9c4be7;
}

/* Pattern styles */
#css-table {
	display: table;
	width: 100%;
}

#col-1 {
	display: table-cell;
	width: 30%;
	border: 1px solid #4284B0;
}

#col-2 {
	display: table-cell;
	width: 70%;
	border-width: 1px 0px;
	border-style: solid;
	border-color: #4284B0;
}

textarea#syntax {
	height: 10rem;
	font-family: monospace;
}

textarea#input {
	height: 4rem;
	font-family: monospace;
}

.example {
	display: table;
	width: 100%;
}

.discuss {
	display: table-cell;
	width: 40%;
	/*width: 25rem;
  float: left;
	*/
}

.result {
	display: table-cell;
	width: 60%;
}

button {
	width: 5rem;
}
            
          
!
            
              var exp, result, htmlResult;
var strings = {};
var $syntax, $input;

// set up for the matched pairs tab
var matchedPairs = function() {
  $syntax.val(strings.grammar1);
  $input.val(strings.input11);
  htmlResult = $("#matched-here");
  htmlResult.html("");
}

// set up for the unmatched pairs tab
var unmatchedPairs = function() {
  $syntax.val(strings.grammar1);
  $input.val(strings.input21);
  htmlResult = $("#unmatched-here");
  htmlResult.html("");
}

// set up for pairs within pairs tab
var insertedPairs = function() {
  $syntax.val(strings.grammar1);
  $input.val(strings.input31);
  htmlResult = $("#internal-here");
  htmlResult.html("");
}

// set for inserted text tab
var addedText = function() {
  $syntax.val(strings.grammar4);
  $input.val(strings.input4);
  htmlResult = $("#text-here");
  htmlResult.html("");
}

// set up for HTML tags tab
var htmlTags = function() {
  $syntax.val(strings.grammar5);
  $input.val(strings.input5);
  htmlResult = $("#html-tags-here");
  htmlResult.html("");
}

// the button-click functions
var execMB = function() {
  $syntax.val(strings.grammar12);
  $input.val(strings.input12);
  exec();
}
var execMHC = function() {
  $syntax.val(strings.grammar13);
  $input.val(strings.input13);
  exec();
}
var execAU = function() {
  $syntax.val(strings.grammar31);
  $input.val(strings.input33);
  exec();
}
var execAM = function() {
  $syntax.val(strings.grammar31);
  $input.val(strings.input34);
  exec();
}
var execLU = function() {
  $input.val(strings.input22);
  exec();
}
var execNP = function() {
  $input.val(strings.input23);
  exec();
}
var execI = function() {
  $syntax.val(strings.grammar3);
  $input.val(strings.input31);
  exec();
}
var execIM = function() {
  $syntax.val(strings.grammar3);
  $input.val(strings.input32);
  exec();
}
var execEI = function() {
  $syntax.val(strings.grammar41);
  $input.val(strings.input41);
  exec();
}
var execQI = function() {
  $syntax.val(strings.grammar42);
  $input.val(strings.input42);
  exec();
}
var execPM = function() {
  $syntax.val(strings.grammar51);
  $input.val(strings.input51);
  exec();
}
var execPM2 = function() {
  $syntax.val(strings.grammar51);
  $input.val(strings.input52);
  exec();
}

// execute the pattern match
var exec = function() {
  try {
    exp = new ApgExp($syntax.val());
    result = exp.exec($input.val());
    if (result) {
      html = result.toHtml();
    } else {
      html = "<h3>result: null</h3>";
    }
  } catch (e) {
    if (e.name === "ApgExpError") {
      html = "<h3>ApgExpError</h3>";
      html += e.toHtml();
    } else if(e instanceof Error){
      html = "<h3>Error</h3>";
      html += e.message;
    }else{
      html = "<h3>unknown exception</h3>";
      html += e;
    }
  }
  htmlResult.html(html);
}

// handle the tab-click event
var tabSetup = function(event, ui) {
  switch (ui.newPanel.selector) {
  default:
  case "#col-2-tab-1":
    matchedPairs()
    break;
  case "#col-2-tab-2":
    unmatchedPairs()
    break;
  case "#col-2-tab-3":
    insertedPairs()
    break;
  case "#col-2-tab-4":
    addedText()
    break;
  case "#col-2-tab-5":
    htmlTags()
    break;
  }
}
$(document).ready(function() {
  
  // SABNF syntax for simple matched parentheses
  strings.grammar1 = "P = L P R / L R\n";
  strings.grammar1 += 'L = "("\n';
  strings.grammar1 += 'R = ")"\n';

  // SABNF syntax for simple matched curly braces
  strings.grammar12 = "P = L P R / L R\n";
  strings.grammar12 += 'L = "{"\n';
  strings.grammar12 += 'R = "}"\n';

  // SABNF syntax for simple matched HTML comment tags
  strings.grammar13 = "P = L P R / L R\n";
  strings.grammar13 += 'L = "<!--"\n';
  strings.grammar13 += 'R = "-->"\n';

  // SABNF syntax for entire string matched parentheses
  strings.grammar31 = "";
  strings.grammar31 += "matched = %^ P %$\n";
  strings.grammar31 += "P = L P R / L R\n";
  strings.grammar31 += 'L = "("\n';
  strings.grammar31 += 'R = ")"\n';

  // SABNF syntax for internal pairs of matched parentheses
  strings.grammar3 = "P = L 1*P R / L R\n";
  strings.grammar3 += 'L = "("\n';
  strings.grammar3 += 'R = ")"\n';

  // SABNF syntax for internal pairs of matched parentheses
  // with text inbetween
  strings.grammar4  = "P    = L text 1*(P text) R / L text R\n";
  strings.grammar4 += 'L    = "<"\n';
  strings.grammar4 += 'R    = ">"\n';
  strings.grammar4 += 'text = *(%d32-59/ %d61 / %d63-126)\n';

  // SABNF syntax for internal pairs of matched angle brackets
  // with text inbetween & escaped brackets
  strings.grammar41  = "P       = L text 1*(P text) R / L text R\n";
  strings.grammar41 += 'L       = "<"\n';
  strings.grammar41 += 'R       = ">"\n';
  strings.grammar41 += 'text    = *(escaped / (%d32-59/ %d61 / %d63-126))\n';
  strings.grammar41 += 'escaped = "\\<" / "\\>"\n';

  // SABNF syntax for internal pairs of matched angle brackets 
  // with text inbetween & quoted strings with brackets
  strings.grammar42  = "P       = L text 1*(P text) R / L text R\n";
  strings.grammar42 += 'L       = "<"\n';
  strings.grammar42 += 'R       = ">"\n';
  strings.grammar42 += 'text    = *(quoted / (%d32-59/ %d61 / %d63-126))\n';
  strings.grammar42 += 'quoted  = %d34 qtext %d34\n';
  strings.grammar42 += 'qtext   = *(%d32-33 / %d35-126)\n';

  // SABNF syntax for internal pairs of named HTML tags 
  // with text inbetween
  strings.grammar5  = "P     = L text 1*(P text) R / L text R\n";
  strings.grammar5 += 'L     = "<" name ">"\n';
  strings.grammar5 += 'R     = "</" name ">"\n';
  strings.grammar5 += 'text  = *(%d32-59 / %d61-126 / %d9-10 / %d13)\n';
  strings.grammar5 += 'name = 1*(%d65-90 / %d97-122)\n';

  // SABNF syntax for internal pairs of named HTML tags
  // matching of opening and closing tags
  strings.grammar51  = '';
  strings.grammar51 += 'tags  = %^ P %$\n';
  strings.grammar51 += 'P     = "<" name ">" text 1*(P text) "</" \\%pname ">"\n';
  strings.grammar51 += '      / "<" name ">" text "</" \\%pname ">"\n';
  strings.grammar51 += 'text  = *(%d32-59 / %d61-126 / %d9-10 / %d13)\n';
  strings.grammar51 += 'name = 1*(%d65-90 / %d97-122)\n';

  // some sample input strings
  strings.input11 = "()";
  strings.input12 = "{{}}";
  strings.input13 = "<!--<!---->-->";

  strings.input21 = "(()";
  strings.input22 = "(((())()))";
  strings.input23 = "((";

  strings.input31 = "(()())";
  strings.input32 = "((()(())()()(((())))()()()))";
  strings.input33 = "(()";
  strings.input34 = "((()))";

  strings.input4 = "<up 1<up 2< middle 1 >between<middle 2>down 2>down 1>";
  strings.input41 = "<up 1<up \\<up 2< middle 1 >\\<between\\><middle 2>down down\\> 2>down 1>";
  strings.input42 = '<up 1<"<ignore quoted"< middle 1 >between<middle 2>"ignore quoted>">down 1>';

  strings.input5 = '<div>description <b>bold stuff</b>\n  <div> inner text </span>\n</div>';
  strings.input51 = '<div>description <b>bold stuff</b>\n  <article> inner text </article>\n</div>';
  strings.input52 = '<div>description <b>bold stuff</b>\n  <article> inner text </span>\n</div>';
  
  // jQuery values
  $syntax = $("#syntax");
  $input = $("#input");

  // page setup
  $("#col-1").tabs();
  $("#col-2").tabs();
  $("#exec").click(exec);
  $("#matched-brackets").click(execMB);
  $("#matched-html-comments").click(execMHC);
  $("#long-unbalanced").click(execLU);
  $("#no-pairs").click(execNP);
  $("#anchors-unmatched").click(execAU);
  $("#anchors-matched").click(execAM);
  $("#internal").click(execI);
  $("#internal-more").click(execIM);
  $("#escape-it").click(execEI);
  $("#quote-it").click(execQI);
  $("#parent-mode").click(execPM);
  $("#parent-mode2").click(execPM2);
  $("#col-2").on("tabsactivate", tabSetup);
  matchedPairs();
});

            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................

Console