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 CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

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
Loading ..................

Console