Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PHP parser based on PEG.js grammar #1152

Merged
merged 18 commits into from
Jun 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,23 @@ before_script:
script:
- |
if [[ ! -z "$WP_VERSION" ]] ; then
# Run the build because otherwise there will be a bunch of warnings about
# failed `stat` calls from `filemtime()`.
npm install || exit 1
npm run build || exit 1
phpunit || exit 1
# Make sure phpegjs parser is up to date
node bin/create-php-parser.js || exit 1
if ! git diff --quiet --exit-code lib/parser.php; then
echo 'ERROR: The PEG parser has been updated, but the generated PHP version'
echo ' (lib/parser.php) has not. Run `bin/create-php-parser.js` and'
echo ' commit the resulting changes to resolve this.'
sleep .2 # Otherwise Travis doesn't want to print the whole message
exit 1
fi
# Check parser syntax
php lib/parser.php || exit 1
# Run PHPUnit tests
RUN_PARSER_TESTS=1 phpunit || exit 1
WP_MULTISITE=1 phpunit || exit 1
fi
- |
Expand Down
24 changes: 24 additions & 0 deletions bin/create-php-parser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env node

const pegjs = require( 'pegjs' );
const phpegjs = require( 'phpegjs' );
const fs = require( 'fs' );
const path = require( 'path' );

const peg = fs.readFileSync( 'blocks/api/post.pegjs', 'utf8' );

const parser = pegjs.generate(
peg,
{
plugins: [ phpegjs ],
phpegjs: {
parserNamespace: null,
parserGlobalNamePrefix: 'Gutenberg_PEG_',
},
}
);

fs.writeFileSync(
path.join( __dirname, '..', 'lib', 'parser.php' ),
parser
);
112 changes: 89 additions & 23 deletions blocks/api/post.pegjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{

/** <?php
// The `maybeJSON` function is not needed in PHP because its return semantics
// are the same as `json_decode`
?> **/

function maybeJSON( s ) {
try {
return JSON.parse( s );
Expand All @@ -22,50 +27,111 @@ WP_Block
/ WP_Block_Html

WP_Block_Void
= "<!--" WS+ "wp:" blockName:WP_Block_Name WS+ attrs:(a:WP_Block_Attributes WS+ { return a })? "/-->"
{ return {
blockName: blockName,
attrs: attrs,
rawContent: ''
} }
= "<!--" WS+ "wp:" blockName:WP_Block_Name WS+ attrs:(a:WP_Block_Attributes WS+ {
/** <?php return $a; ?> **/
return a;
})? "/-->"
{
/** <?php
return array(
'blockName' => $blockName,
'attrs' => $attrs,
'rawContent' => '',
);
?> **/

return {
blockName: blockName,
attrs: attrs,
rawContent: ''
};
}

WP_Block_Balanced
= s:WP_Block_Start ts:(!WP_Block_End c:Any { return c })* e:WP_Block_End
& { return s.blockName === e.blockName }
{ return {
blockName: s.blockName,
attrs: s.attrs,
rawContent: ts.join( '' ),
} }
= s:WP_Block_Start ts:(!WP_Block_End c:Any {
/** <?php return $c; ?> **/
return c;
})* e:WP_Block_End & {
/** <?php return $s['blockName'] === $e['blockName']; ?> **/
return s.blockName === e.blockName;
}
{
/** <?php
return array(
'blockName' => $s['blockName'],
'attrs' => $s['attrs'],
'rawContent' => implode( '', $ts ),
);
?> **/

return {
blockName: s.blockName,
attrs: s.attrs,
rawContent: ts.join( '' )
};
}

WP_Block_Html
= ts:(!WP_Block_Balanced !WP_Block_Void c:Any { return c })+
= ts:(!WP_Block_Balanced !WP_Block_Void c:Any {
/** <?php return $c; ?> **/
return c;
})+
{
/** <?php
return array(
'attrs' => array(),
'rawContent' => implode( '', $ts ),
);
?> **/

return {
attrs: {},
rawContent: ts.join( '' )
}
}

WP_Block_Start
= "<!--" WS+ "wp:" blockName:WP_Block_Name WS+ attrs:(a:WP_Block_Attributes WS+ { return a })? "-->"
{ return {
blockName: blockName,
attrs: attrs
} }
= "<!--" WS+ "wp:" blockName:WP_Block_Name WS+ attrs:(a:WP_Block_Attributes WS+ {
/** <?php return $a; ?> **/
return a;
})? "-->"
{
/** <?php
return array(
'blockName' => $blockName,
'attrs' => $attrs,
);
?> **/

return {
blockName: blockName,
attrs: attrs
};
}

WP_Block_End
= "<!--" WS+ "/wp:" blockName:WP_Block_Name WS+ "-->"
{ return {
blockName: blockName
} }
{
/** <?php
return array(
'blockName' => $blockName,
);
?> **/

return {
blockName: blockName
};
}

WP_Block_Name
= $(ASCII_Letter (ASCII_AlphaNumeric / "/" ASCII_AlphaNumeric)*)

WP_Block_Attributes
= attrs:$("{" (!("}" WS+ """/"? "-->") .)* "}")
{ return maybeJSON( attrs ) }
{
/** <?php return json_decode( $attrs, true ); ?> **/
return maybeJSON( attrs );
}

ASCII_AlphaNumeric
= ASCII_Letter
Expand Down
17 changes: 10 additions & 7 deletions blocks/test/fixtures/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ and serialization logic.
Each test is made up of three fixture files:

1. `fixture-name.html`: The initial post content.
2. `fixture-name.json`: The **expected** parsed representation of the block(s)
inside the post content, along with their attributes and any nested content.
The contents of this file are compared against the **actual** parsed block
object(s).
3. `fixture-name.serialized.html`: The **expected** result of calling
2. `fixture-name.parsed.json`: The **expected** output of the PEG parser for
this content (checked against the **actual** output of both the JS and PHP
versions of the parser).
3. `fixture-name.json`: The **expected** representation of the block(s) inside
the post content, along with their attributes and any nested content. The
contents of this file are compared against the **actual** block object(s).
4. `fixture-name.serialized.html`: The **expected** result of calling
`serialize` on the parsed block object(s). The contents of this file are
compared against the **actual** re-serialized post content. This final step
simulates opening and re-saving a post.
Expand All @@ -22,8 +24,9 @@ the parsing and serialization of that block. These fixtures must be named like

1. `core-image.html` (or `core-image-specific-test-name.html`). Must contain a
`<!-- wp:core/image -->` block.
2. `core-image.json` (or `core-image-specific-test-name.html`).
3. `core-image.serialized.html` (or
2. `core-image.parsed.json` (or `core-image-specific-test-name.parsed.json`).
3. `core-image.json` (or `core-image-specific-test-name.json`).
4. `core-image.serialized.html` (or
`core-image-specific-test-name.serialized.html`).

Ideally all important attributes and features of the block should be tested
Expand Down
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-button-center.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/button",
"attrs": {
"align": "center"
},
"rawContent": "\n<div class=\"aligncenter wp-block-button\"><a href=\"https://github.com/WordPress/gutenberg\">Help build Gutenberg</a></div>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
11 changes: 11 additions & 0 deletions blocks/test/fixtures/core-code.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"blockName": "core/code",
"attrs": null,
"rawContent": "\n<pre class=\"wp-block-code\"><code>export default function MyButton() {\n\treturn &lt;Button&gt;Click Me!&lt;/Button&gt;;\n}</code></pre>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-cover-image.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/cover-image",
"attrs": {
"url": "https://cldup.com/uuUqE_dXzy.jpg"
},
"rawContent": "\n<section class=\"wp-block-cover-image\">\n\t<section class=\"cover-image\" style=\"background-image:url(https://cldup.com/uuUqE_dXzy.jpg);\">\n\t\t<h2>Guten Berg!</h2>\n\t</section>\n</section>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-embed.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/embed",
"attrs": {
"url": "https://example.com/"
},
"rawContent": "\n<figure class=\"wp-block-embed\">\n https://example.com/\n <figcaption>Embedded content from an example URL</figcaption>\n</figure>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-embedanimoto.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/embedanimoto",
"attrs": {
"url": "https://animoto.com/"
},
"rawContent": "\n<figure class=\"wp-block-embedanimoto\">\n https://animoto.com/\n <figcaption>Embedded content from animoto</figcaption>\n</figure>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-embedcloudup.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/embedcloudup",
"attrs": {
"url": "https://cloudup.com/"
},
"rawContent": "\n<figure class=\"wp-block-embedcloudup\">\n https://cloudup.com/\n <figcaption>Embedded content from cloudup</figcaption>\n</figure>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-embedcollegehumor.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/embedcollegehumor",
"attrs": {
"url": "https://collegehumor.com/"
},
"rawContent": "\n<figure class=\"wp-block-embedcollegehumor\">\n https://collegehumor.com/\n <figcaption>Embedded content from collegehumor</figcaption>\n</figure>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-embeddailymotion.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/embeddailymotion",
"attrs": {
"url": "https://dailymotion.com/"
},
"rawContent": "\n<figure class=\"wp-block-embeddailymotion\">\n https://dailymotion.com/\n <figcaption>Embedded content from dailymotion</figcaption>\n</figure>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-embedfacebook.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/embedfacebook",
"attrs": {
"url": "https://facebook.com/"
},
"rawContent": "\n<figure class=\"wp-block-embedfacebook\">\n https://facebook.com/\n <figcaption>Embedded content from facebook</figcaption>\n</figure>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-embedflickr.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/embedflickr",
"attrs": {
"url": "https://flickr.com/"
},
"rawContent": "\n<figure class=\"wp-block-embedflickr\">\n https://flickr.com/\n <figcaption>Embedded content from flickr</figcaption>\n</figure>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-embedfunnyordie.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/embedfunnyordie",
"attrs": {
"url": "https://funnyordie.com/"
},
"rawContent": "\n<figure class=\"wp-block-embedfunnyordie\">\n https://funnyordie.com/\n <figcaption>Embedded content from funnyordie</figcaption>\n</figure>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
13 changes: 13 additions & 0 deletions blocks/test/fixtures/core-embedhulu.parsed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"blockName": "core/embedhulu",
"attrs": {
"url": "https://hulu.com/"
},
"rawContent": "\n<figure class=\"wp-block-embedhulu\">\n https://hulu.com/\n <figcaption>Embedded content from hulu</figcaption>\n</figure>\n"
},
{
"attrs": {},
"rawContent": "\n"
}
]
Loading