Skip to content

Commit

Permalink
1. Added double ?? escape to Mark pragma.
Browse files Browse the repository at this point in the history
2. Updated the docs.
  • Loading branch information
henry-luo committed Dec 12, 2018
1 parent 9661501 commit 9a409d7
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 107 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Could be represented in Mark as:

```text
{form // object with type-name 'form'
(?--comment?) // Mark pragma, like HTML comment
(?comment?) // Mark pragma, like HTML comment
{div class:"form-group" // nested Mark object
{label for:email // property 'for' and its value, both unquoted
"Email address:" // text needs to be quoted
Expand Down Expand Up @@ -162,11 +162,9 @@ console.log("Greeting from Mark: " + Mark.stringify(obj));
</script>
```

Note: /dist/mark.js has bundled all sub-modules and all dependencies with it, and is meant to run in browser. The entire script is about 14K after gzip. It supports latest browsers, including Chrome, Safari, Firefox, Edge.
Note: /dist/mark.js has bundled all sub-modules and all dependencies with it, and is meant to run in browser. The entire script is about 14K after gzip. It supports latest browsers, including Chrome, Safari, Firefox, Edge. (*Legacy browser IE is not supported.*)

*If you just want the core functional API, without the sub-modules, you can also use mark.core.js, which is only 7K after gzip. You can also refer to the package.json to create your own custom bundle with the sub-modules you need.*

*If you need to support legacy browsers, like IE11, which do not have proper ES6 support, you need to use /dist/mark.es5.js. IE < 11 are not supported.*
*If you just want the core functional API, without the sub-modules, you can also use mark.core.js, which is only 7K after gzip. You can also refer to the package.json to create your own custom bundle with the sub-modules you need.*

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion dist/mark.core.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/mark.es5.js

This file was deleted.

2 changes: 1 addition & 1 deletion dist/mark.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Could be represented in Mark as:

```text
{form // object with type-name 'form'
(?--comment?) // Mark pragma, like HTML comment
(?comment?) // Mark pragma, like HTML comment
{div class:"form-group" // nested Mark object
{label for:email // property 'for' and its value, both unquoted
"Email address:" // text needs to be quoted
Expand Down
72 changes: 28 additions & 44 deletions docs/mark-grammar.html
Original file line number Diff line number Diff line change
Expand Up @@ -2471,12 +2471,12 @@ <h2 id='mark_pragma'>mark_pragma</h2>
<div class="rule">
<h2 id='general_pragma'>general_pragma</h2>
<div onclick='svgBlockClick(event)'>
<svg class="railroad-diagram" width="501" height="110" viewBox="0 0 501 110">
<svg class="railroad-diagram" width="417" height="110" viewBox="0 0 417 110">
<g transform="translate(.5 .5)">
<path d="M 20 31 v 20 m 10 -20 v 20 m -10 -10 h 20.5"></path>
<g>
<path d="M40 41h0"></path>
<path d="M460 41h0"></path>
<path d="M376 41h0"></path>
<path d="M40 41h10"></path>
<g>
<path d="M50 41h0"></path>
Expand All @@ -2487,70 +2487,56 @@ <h2 id='general_pragma'>general_pragma</h2>
<path d="M86 41h10"></path>
<g>
<path d="M96 41h0"></path>
<path d="M404 41h0"></path>
<path d="M320 41h0"></path>
<path d="M96 41a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10"></path>
<g>
<path d="M116 21h268"></path>
<path d="M116 21h184"></path>
</g>
<path d="M384 21a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path>
<path d="M300 21a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path>
<path d="M96 41h20"></path>
<g>
<path d="M116 41h0"></path>
<path d="M384 41h0"></path>
<path d="M300 41h0"></path>
<path d="M116 41h10"></path>
<g>
<path d="M126 41h0"></path>
<path d="M374 41h0"></path>
<path d="M290 41h0"></path>
<path d="M126 41h20"></path>
<g>
<path d="M146 41h42"></path>
<path d="M312 41h42"></path>
<rect x="188" y="30" width="124" height="22"></rect>
<text x="250" y="45">char&#95;no&#95;qmark</text>
<path d="M146 41h0"></path>
<path d="M270 41h0"></path>
<rect x="146" y="30" width="124" height="22"></rect>
<text x="208" y="45">char&#95;no&#95;qmark</text>
</g>
<path d="M354 41h20"></path>
<path d="M270 41h20"></path>
<path d="M126 41a10 10 0 0 1 10 10v10a10 10 0 0 0 10 10"></path>
<g>
<path d="M146 71h0"></path>
<path d="M354 71h0"></path>
<path d="M146 71h10"></path>
<g>
<path d="M156 71h0"></path>
<path d="M184 71h0"></path>
<rect x="156" y="60" width="28" height="22" rx="10" ry="10"></rect>
<text x="170" y="75">?</text>
</g>
<path d="M184 71h10"></path>
<path d="M194 71h10"></path>
<g>
<path d="M204 71h0"></path>
<path d="M344 71h0"></path>
<rect x="204" y="60" width="140" height="22"></rect>
<text x="274" y="75">char&#95;no&#95;bracket</text>
</g>
<path d="M344 71h10"></path>
<path d="M146 71h44"></path>
<path d="M226 71h44"></path>
<rect x="190" y="60" width="36" height="22" rx="10" ry="10"></rect>
<text x="208" y="75">??</text>
</g>
<path d="M354 71a10 10 0 0 0 10 -10v-10a10 10 0 0 1 10 -10"></path>
<path d="M270 71a10 10 0 0 0 10 -10v-10a10 10 0 0 1 10 -10"></path>
</g>
<path d="M374 41h10"></path>
<path d="M290 41h10"></path>
<path d="M126 41a10 10 0 0 0 -10 10v29a10 10 0 0 0 10 10"></path>
<g>
<path d="M126 90h248"></path>
<path d="M126 90h164"></path>
</g>
<path d="M374 90a10 10 0 0 0 10 -10v-29a10 10 0 0 0 -10 -10"></path>
<path d="M290 90a10 10 0 0 0 10 -10v-29a10 10 0 0 0 -10 -10"></path>
</g>
<path d="M384 41h20"></path>
<path d="M300 41h20"></path>
</g>
<path d="M404 41h10"></path>
<path d="M320 41h10"></path>
<g>
<path d="M414 41h0"></path>
<path d="M450 41h0"></path>
<rect x="414" y="30" width="36" height="22" rx="10" ry="10"></rect>
<text x="432" y="45">?)</text>
<path d="M330 41h0"></path>
<path d="M366 41h0"></path>
<rect x="330" y="30" width="36" height="22" rx="10" ry="10"></rect>
<text x="348" y="45">?)</text>
</g>
<path d="M450 41h10"></path>
<path d="M366 41h10"></path>
</g>
<path d="M 460 41 h 20 m -10 -10 v 20 m 10 -20 v 20"></path>
<path d="M 376 41 h 20 m -10 -10 v 20 m 10 -20 v 20"></path>
</g>
</svg>

Expand All @@ -2562,7 +2548,6 @@ <h2 id='general_pragma'>general_pragma</h2>
<div>
References:
<a href="#char_no_qmark">char_no_qmark</a>
<a href="#char_no_bracket">char_no_bracket</a>
</div>
</div>
<div class="rule">
Expand Down Expand Up @@ -2721,7 +2706,6 @@ <h2 id='char_no_bracket'>char_no_bracket</h2>
</div>
<div>
Used by:
<a href="#general_pragma">general_pragma</a>
<a href="#paired_pragma">paired_pragma</a>
</div>
<div>
Expand Down
50 changes: 29 additions & 21 deletions docs/mark-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,27 +81,35 @@ The constructed Mark object is just a simple POJO. So basically:

Besides the above POJO behaviors, there are some additional prototype functions defined to work with the data model:

- `length()`: returns the number of content items stored in the Mark object.
- `contents()`: returns the list of content items stored in the Mark object.
- `parent()`: returns the parent object of current Mark object.
- `source(options)`: shorthand for stringifying the Mark object.
- `text()`: returns a string, which is the concatenation of all descendant text content items.
- `filter(callback, thisArg)`: similar to JS `Array.prototype.filter` that iterates through the content items.
- `map(callback, thisArg)`: similar to JS `Array.prototype.map` that iterates through the content items.
- `reduce(callback)`: similar to JS `Array.prototype.reduce` that iterates through the content items.
- `every(callback, thisArg)`: similar to JS `Array.prototype.every` that iterates through the content items.
- `some(callback, thisArg)`: similar to JS `Array.prototype.some` that iterates through the content items.
- `each(callback, thisArg)`: similar to JS `Array.prototype.forEach` that iterates through the content items.

When these API functions are overridden by properties of same name, you can still call them from the `Mark.prototype`, e.g.`Mark.prototype.length.call(markObj)`.
- `.length`: when `length` property is not defined on the object, it returns the number of content items stored in the Mark object; otherwise, it returns the value of the `length` property. You can use `Mark.lengthOf()` to get the content length, when `length` property is defined on the object.
- `.contents()`: returns the list of content items stored in the Mark object.
- `.parent()`: returns the parent object of current Mark object.
- `.source(options)`: shorthand for stringifying current Mark object.
- `.text()`: returns a string, which is the concatenation of all descendant text content items.

As Mark object is array-like, most functional JS code that works with array-like data, can be work with Mark object without change. The following API functions are directly mapped to those from `Array.prototype`.

- `.filter(callback, thisArg)`: mapped to JS `Array.prototype.filter`.
- `.map(callback, thisArg)`: mapped to JS `Array.prototype.map`.
- `.reduce(callback)`: mapped to JS `Array.prototype.reduce`.
- `.every(callback, thisArg)`: mapped to JS `Array.prototype.every`.
- `.some(callback, thisArg)`: mapped to JS `Array.prototype.some`.
- `.each(callback, thisArg)`: mapped to JS `Array.prototype.forEach`.
- `.forEach(callback, thisArg)`: mapped to JS `Array.prototype.forEach`.
- `.includes(searchElement, fromIndex)`: mapped to JS `Array.prototype.includes`.
- `.indexOf(searchElement, fromIndex)`: mapped to JS `Array.prototype.indexOf`.
- `.lastIndexOf(callback, thisArg)`: mapped to JS `Array.prototype.lastIndexOf`.
- `.slice(begin, end)`: mapped to JS `Array.prototype.slice`.

When these API functions are overridden by properties of same name, you can still call them from the `Mark.prototype`, e.g.`Mark.prototype.contents.call(markObj)`.

### 3.2 Static API

There are a few important API functions defined on the static Mark object:

- `Mark.lengthOf(markObj)`: returns the content length of a Mark object.
- `Mark.parse('string', options)`: parses a string into Mark object. It takes an optional parameter `options`.
- `Mark.stringify(markObj, options)`: serialize the Mark object back into string. It takes an optional parameter `options`.
- `options.omitComma`: tells whether comma between properties and array items should be omitted in the output. Default: `false`.
- `options.space`: may be used to control spacing in the final string. If it is a number, successive levels in the stringification will each be indented by this many space characters (up to 10). If it is a string, successive levels will be indented by this string (or the first 10 characters of it).

Mark does not support `reviver` function defined in `JSON.parse()`, and `replacer` function defined in `JSON.stringify()`. They are not structured nor secure way to serialize and deserialize custom data types.
Expand All @@ -112,23 +120,23 @@ Mutative API functions are now separated into its own sub-module `mark.mutate.js

The mutative API functions defined on a Mark object are:

- `set(key, value)`: if the `key` is numeric, then it sets the indexed content item of the Mark object; otherwise, it sets a named property of the Mark object.
- `push(item, ...)`: pushes item(s) at the end of the contents of current Mark object. However, unlike JS `Array.prototype.push`, which returns the new array length, this function returns the current this object, so that the function call can be chained.
- `pop()`: pop an item from the end of contents.
- `splice(index, cnt, item, ...)`: remove `cnt` of items starting at the `index`, and then insert given item(s), similar to `Array.splice(...)`.
- `.set(key, value)`: if the `key` is numeric, then it sets the indexed content item of the Mark object; otherwise, it sets a named property of the Mark object.
- `.push(item, ...)`: pushes item(s) at the end of the contents of current Mark object. However, unlike JS `Array.prototype.push`, which returns the new array length, this function returns the current this object, so that the function call can be chained.
- `.pop()`: pop an item from the end of contents.
- `.splice(index, cnt, item, ...)`: remove `cnt` of items starting at the `index`, and then insert given item(s), similar to `Array.splice(...)`.

The mutative API function defined on a Mark pragma:

- `set(value)`: sets the content in the pragma, and returns the pragma itself.
- `.set(value)`: sets the content in the pragma, and returns the pragma itself.

### 3.3 Converter API
### 3.4 Converter API

These are additional prototype functions implemented in `mark.converter.js`, for mapping Mark into other formats:

- `markObj.html(options)`: serializes the Mark object into HTML. `options` parameter is same as that of `Mark.stringify()`.
- `markObj.xml(options)`: serializes the Mark object into XML. `options` parameter is same as that of `Mark.stringify()`.

### 3.4 Selector API
### 3.5 Selector API

These are additional prototype functions implemented in `mark.selector.js`, for processing the constructed data model using special selectors, like CSS selector:

Expand Down
12 changes: 6 additions & 6 deletions docs/mark-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Comparing to a JSON object, a Mark object has two extensions:
- A **type name**, which corresponds to class name or type name of an object. In JavaScript, that is `obj.constructor.name`. In HTML and XML, that's the element name.
- Optional list of **content** values following the named properties, which corresponds to child nodes in markup documents like HTML and XML.

### 1.1 Properties
### 1.1 Object Properties

```BNF
properties ::= (property ','?)*
Expand All @@ -47,7 +47,7 @@ continue_identifier ::= begin_identifier | digit | '-' | '.'
- Property keys **must be unique** (for both Mark and JSON object).
- Comma between properties is optional, and last property can have trailing comma.

### 1.2 Contents
### 1.2 Object Contents

```BNF
contents ::= (text | binary | json_object | mark_object | mark_pragma)*
Expand All @@ -58,15 +58,15 @@ contents ::= (text | binary | json_object | mark_object | mark_pragma)*

## 2. Mark Pragma

Mark pragma is a sequence of characters enclosed in brackets `( ... )`. It has two syntax forms.
Mark pragma is a sequence of characters enclosed in brackets `( ... )`. It has two syntax forms. The general form is quoted in `(? ... ?)`, and the pair form is quoted in `( ... )`.

The general form is quoted in `(? ... ?)`, and the pair form is quoted in `( ... )`. Paired form can contain embedded brackets, as long as they are balanced.
In the general form, character `?` needs to be escaped with double `??`.

Outer brackets are delimiters, whereas embedded brackets are part of the pragma content.
In paired form, no character needs to be escaped. It can contain embedded brackets, as long as they are balanced. Outer brackets are delimiters, whereas embedded brackets are part of the pragma content.

```BNF
mark_pragma ::= general_pragma | paired_pragma
general_pragma ::= '(?' (char_no_qmark | ('?' char_no_bracket))* '?)'
general_pragma ::= '(?' (char_no_qmark | '??')* '?)'
paired_pragma ::= '(' (char_no_bracket | mark_pragma)* ')'
```

Expand Down
2 changes: 1 addition & 1 deletion docs/mark.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ hex ::= [0-9a-fA-F]
/*-- Mark pragma -----------------------------------------------*/
mark_pragma ::= general_pragma | paired_pragma

general_pragma ::= '(?' (char_no_qmark | ('?' char_no_bracket))* '?)'
general_pragma ::= '(?' (char_no_qmark | '??')* '?)'

paired_pragma ::= '(' (char_no_bracket | paired_pragma)* ')'

Expand Down
51 changes: 30 additions & 21 deletions mark.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ var MARK = (function() {
};

// Mark object API functions
var api = {
let ap = Array.prototype;
let api = {
// object 'properties': just use JS Object.keys(), Object.values(), Object.entries() to work with the properties

// return the content items
Expand All @@ -119,17 +120,17 @@ var MARK = (function() {
},

// as Mark is array-like, we can simply inherit all functional methods from Array.prototype
filter: Array.prototype.filter,
map: Array.prototype.map,
reduce: Array.prototype.reduce,
every: Array.prototype.every,
some: Array.prototype.some,
each: Array.prototype.forEach,
forEach: Array.prototype.forEach,
includes: Array.prototype.includes,
indexOf: Array.prototype.indexOf,
lastIndexOf: Array.prototype.lastIndexOf,
slice: Array.prototype.slice,
filter: ap.filter,
map: ap.map,
reduce: ap.reduce,
every: ap.every,
some: ap.some,
each: ap.forEach,
forEach: ap.forEach,
includes: ap.includes,
indexOf: ap.indexOf,
lastIndexOf: ap.lastIndexOf,
slice: ap.slice,

// conversion APIs
source: function(options) {
Expand Down Expand Up @@ -543,18 +544,27 @@ MARK.parse = (function() {
pragma = function() {
let prag = '', level = 0;
next(); // skip starting '('
if (ch === '?') {
if (ch === '?') { // normal pragma
next(); // skip '?'
while (ch) {
if (ch === '?' && text[at] === ')') {
// end of pragma
at++; next(); // skip ending ')'
return MARK.pragma(prag);
if (ch === '?') {
if (text[at] === ')') {
// end of pragma
at++; next(); // skip ending ')'
return MARK.pragma(prag);
}
else if (text[at] === '?') { // escape for '?'
at++;
}
else {
error("'?' should be escaped in Mark pragma");
}
}
// else - normal char
prag += ch; next();
}
} else {
}
else { // paired pragma
while (ch) {
if (ch === ')') {
if (level) { level--; } // embedded (...)
Expand Down Expand Up @@ -1053,8 +1063,7 @@ MARK.stringify = function(obj, options) {
buffer = "{";
var nonEmpty = false;
if (!value.constructor) { // assume Mark pragma
// todo: should escape '{','}' in $pragma
return value[$pragma] ? (value[$paired] ? '('+ value[$pragma] +')' : '(?'+ value[$pragma] +'?)') : 'null'/* unknown object */;
return value[$pragma] ? (value[$paired] ? '('+ value[$pragma] +')' : '(?'+ value[$pragma].replace(/\?/g, '??') +'?)') : '(??)';
}
// Mark or JSON object
objStack.push(value);
Expand Down Expand Up @@ -1096,7 +1105,7 @@ MARK.stringify = function(obj, options) {
}

objStack.pop();
if (nonEmpty ) {
if (nonEmpty) {
// buffer = buffer.substring(0, buffer.length-1) + indent(objStack.length) + "}";
if (length && indentStep) { buffer += indent(objStack.length); }
buffer += "}";
Expand Down
Loading

0 comments on commit 9a409d7

Please sign in to comment.