wikipeg 2.0.5 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -67,6 +67,8 @@ You can tweak the generated parser with several options:
67
67
  * `--extra-options-file` — file with additional options (in JSON format) to
68
68
  pass to `PEG.buildParser`
69
69
  * `--trace` — makes the parser trace its progress
70
+ * `--header-comment-file` — file containing a well-formatted comment, used
71
+ to customize the comment at the top of the generated file
70
72
 
71
73
  ### JavaScript API
72
74
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.5
1
+ 4.0.0
package/bin/wikipeg CHANGED
@@ -47,6 +47,11 @@ Options:
47
47
  to PEG.buildParser
48
48
  --extra-options-file <file> file with additional options (in JSON
49
49
  format) to pass to PEG.buildParser
50
+ --header-comment-file <file> file containing the comment to add at the top
51
+ of the generated file to document where it
52
+ comes from. Must contain all formatting
53
+ necessary for said comment in the target
54
+ language.
50
55
  -v, --version print version information and exit
51
56
  -h, --help print help and exit
52
57
  `);
@@ -210,6 +215,19 @@ while (args.length > 0 && isOption(args[0])) {
210
215
  addExtraOptions(options, json);
211
216
  break;
212
217
 
218
+ case "--header-comment-file":
219
+ nextArg();
220
+ if (args.length === 0) {
221
+ abort("Missing parameter of the --header-comment-file option.");
222
+ }
223
+ try {
224
+ var comment = fs.readFileSync(args[0]);
225
+ } catch(e) {
226
+ abort("Can't read from file \"" + args[0] + "\".");
227
+ }
228
+ options.headerComment = comment;
229
+ break;
230
+
213
231
  case "-v":
214
232
  case "--version":
215
233
  printVersion();
@@ -225,6 +225,11 @@ let javascript = {
225
225
  if (opts.params.length) {
226
226
  keyParts = keyParts.concat(opts.params);
227
227
  }
228
+ let storeRefs = opts.storeRefs.filter(function(part) {
229
+ return part !== '';
230
+ }).map(function(part) {
231
+ return ' ' + part;
232
+ }).join('\n');
228
233
  return {
229
234
  start: [
230
235
  `var key = [${keyParts.join(',')}].map(String).join(":");`,
@@ -241,7 +246,7 @@ let javascript = {
241
246
  ' nextPos: peg$currPos, ',
242
247
  ` result: ${opts.result}, `,
243
248
  '};',
244
- opts.storeRefs
249
+ storeRefs
245
250
  ].join('\n')
246
251
  };
247
252
  },
@@ -251,7 +256,8 @@ let javascript = {
251
256
  return ` if (cached.hasOwnProperty(${encName})) param_${name}.value = cached.$${name};`;
252
257
  },
253
258
 
254
- cacheStoreRef(name) {
259
+ cacheStoreRef(name, store) {
260
+ if (!store) return '';
255
261
  return `if (saved_${name} !== param_${name}.value) cached.$${name} = param_${name}.value;`;
256
262
  },
257
263
 
@@ -424,33 +424,38 @@ let php = {
424
424
  if (opts.params.length) {
425
425
  keyParts = keyParts.concat(opts.params);
426
426
  }
427
+ let storeRefs = opts.storeRefs.map(function(part) {
428
+ return ' ' + part;
429
+ }).join(',\n');
427
430
  return {
428
431
  start: [
429
432
  `$key = json_encode([${keyParts.join(',')}]);`,
430
433
  `$cached = $this->cache[$key] ?? null;`,
431
434
  `if ($cached) {`,
432
- ` $this->currPos = $cached['nextPos'];`,
435
+ ` $this->currPos = $cached->nextPos;`,
433
436
  opts.loadRefs,
434
- ` return $cached['result'];`,
437
+ ` return $cached->result;`,
435
438
  '}',
436
439
  opts.saveRefs,
437
440
  ].join('\n'),
438
441
  store: [
439
- `$cached = ['nextPos' => $this->currPos, 'result' => ${opts.result}];`,
440
- opts.storeRefs,
441
- `$this->cache[$key] = $cached;`
442
+ `$this->cache[$key] = new ${opts.className}CacheEntry(`,
443
+ ' $this->currPos,',
444
+ ` ${opts.result + (opts.storeRefs.length > 0 ? ',' : '')}`,
445
+ storeRefs,
446
+ `);`
442
447
  ].join('\n')
443
448
  };
444
449
  },
445
450
 
446
451
  cacheLoadRef(name) {
447
- let encName = php.stringify('$' + name);
448
- return `if (array_key_exists(${encName}, $cached)) $param_${name} = $cached[${encName}];`;
452
+ return `if ($cached->${name} !== self::$UNDEFINED) { $param_${name} = $cached->${name}; }`;
449
453
  },
450
454
 
451
- cacheStoreRef(name) {
452
- let encName = php.stringify('$' + name);
453
- return `if ($saved_${name} !== $param_${name}) $cached[${encName}] = $param_${name};`;
455
+ cacheStoreRef(name, store) {
456
+ return store ?
457
+ `$saved_${name} !== $param_${name} ? $param_${name} : self::$UNDEFINED` :
458
+ 'self::$UNDEFINED';
454
459
  },
455
460
 
456
461
  /**
@@ -80,6 +80,28 @@ function generateJavascript(ast, options) {
80
80
  language = js;
81
81
  }
82
82
 
83
+ var className = options.className || 'PEGParser';
84
+ var namespace = '';
85
+ var matches = className.match(/^(.*)\\([^\\]*)$/);
86
+ if (matches) {
87
+ className = matches[2];
88
+ namespace = `namespace ${matches[1]};`;
89
+ }
90
+
91
+ var refsSet = {};
92
+ var getRefs = visitor.build({
93
+ rule_ref: function(node) {
94
+ for (let i = 0; i < node.assignments.length; i++) {
95
+ let assignment = node.assignments[i];
96
+ if (assignment.isref) {
97
+ refsSet[assignment.name] = true;
98
+ }
99
+ }
100
+ }
101
+ });
102
+ getRefs(ast);
103
+ var references = Object.keys(refsSet);
104
+
83
105
  /**
84
106
  * The default init cache hook
85
107
  */
@@ -91,7 +113,6 @@ function generateJavascript(ast, options) {
91
113
  var generateCacheRule = language.generateCacheRule;
92
114
 
93
115
  function indent2(code) { return code.replace(/^(.+)$/gm, ' $1'); }
94
- function indent4(code) { return code.replace(/^(.+)$/gm, ' $1'); }
95
116
 
96
117
  /**
97
118
  * The Context class.
@@ -749,21 +770,23 @@ function generateJavascript(ast, options) {
749
770
  block.push(language.cacheLoadRef(name));
750
771
  }
751
772
  }
752
- return indent4(block.join('\n'));
773
+ return indent2(block.join('\n'));
753
774
  }
754
775
 
755
776
  /**
756
- * Get a block which determines which refs have changed, if any. Any changed
757
- * refs are stored in the `cached` object.
777
+ * Get the list of expressions or statements returned by cacheStoreRef() for
778
+ * refs which may have changed.
758
779
  */
759
780
  function getCacheStoreRefs(rule) {
760
- var block = [];
781
+ var store = {};
761
782
  for (let name in rule.passedParams) {
762
783
  if (rule.passedParams[name].type === 'reference') {
763
- block.push(language.cacheStoreRef(name));
784
+ store[name] = true;
764
785
  }
765
786
  }
766
- return indent2(block.join('\n'));
787
+ return references.map(function(name) {
788
+ return language.cacheStoreRef(name, store[name]);
789
+ });
767
790
  }
768
791
 
769
792
  /**
@@ -774,10 +797,10 @@ function generateJavascript(ast, options) {
774
797
  var parts = [];
775
798
  for (let name in rule.passedParams) {
776
799
  if (rule.passedParams[name].type === 'reference') {
777
- parts.push(indent2(language.cacheSaveRef(name)));
800
+ parts.push(language.cacheSaveRef(name));
778
801
  }
779
802
  }
780
- return indent2(parts.join('\n'));
803
+ return parts.join('\n');
781
804
  }
782
805
 
783
806
  function expandTemplate(template, vars) {
@@ -848,6 +871,7 @@ function generateJavascript(ast, options) {
848
871
  loadRefs: getCacheLoadRefs(node),
849
872
  saveRefs: getCacheSaveRefs(node),
850
873
  storeRefs: getCacheStoreRefs(node),
874
+ className: className,
851
875
  });
852
876
  body.push(cacheBits.start);
853
877
  }
@@ -1356,15 +1380,8 @@ function generateJavascript(ast, options) {
1356
1380
  }
1357
1381
  templateVars['/*GENERATED*/'] = generated.join('\n');
1358
1382
 
1359
- var className = options.className || 'PEGParser';
1360
- var matches = className.match(/^(.*)\\([^\\]*)$/);
1361
- if (matches) {
1362
- templateVars['CLASS_NAME'] = matches[2];
1363
- templateVars['/*NAMESPACE*/'] = `namespace ${matches[1]};`;
1364
- } else {
1365
- templateVars['CLASS_NAME'] = className;
1366
- templateVars['/*NAMESPACE*/'] = '';
1367
- }
1383
+ templateVars['CLASS_NAME'] = className;
1384
+ templateVars['/*NAMESPACE*/'] = namespace;
1368
1385
 
1369
1386
  var cacheInitCode = '';
1370
1387
  var cacheInitHook;
@@ -1377,9 +1394,35 @@ function generateJavascript(ast, options) {
1377
1394
  }
1378
1395
  templateVars['/*CACHE_INIT*/'] = cacheInitCode;
1379
1396
 
1397
+ var headerComment = '';
1398
+ if (options.headerComment) {
1399
+ headerComment = options.headerComment + '\n';
1400
+ } else {
1401
+ headerComment = '/*\n' +
1402
+ ' * Generated by WikiPEG\n' +
1403
+ ' */\n\n';
1404
+ }
1405
+ templateVars['/*HEADER_COMMENT*/'] = headerComment;
1406
+
1380
1407
  var template;
1381
1408
  if (options.language === 'php') {
1382
1409
  template = fs.readFileSync(__dirname + '/../../runtime/template.php', 'utf8');
1410
+ if (options.cache) {
1411
+ templateVars['/*CACHE_ENTRY_BEGIN*/'] = '';
1412
+ templateVars['/*CACHE_ENTRY_END*/'] = '';
1413
+ templateVars['/*CACHE_ENTRY_DECLARE*/'] = '';
1414
+ templateVars['/*CACHE_ENTRY_ARGS*/'] = '';
1415
+ templateVars['/*CACHE_ENTRY_INIT*/'] = '';
1416
+ references.forEach(function(name) {
1417
+ templateVars['/*CACHE_ENTRY_DECLARE*/'] += `\tpublic $${name};\n`;
1418
+ templateVars['/*CACHE_ENTRY_ARGS*/'] += `, $${name}`;
1419
+ templateVars['/*CACHE_ENTRY_INIT*/'] += `\t\t$this->${name} = $${name};\n`;
1420
+ });
1421
+ } else {
1422
+ template = template.replace(
1423
+ /\/\*CACHE_ENTRY_BEGIN\*\/[^]+?\/\*CACHE_ENTRY_END\*\//, ''
1424
+ );
1425
+ }
1383
1426
  } else {
1384
1427
  template = fs.readFileSync(__dirname + '/../../runtime/template.js', 'utf8');
1385
1428
  }
package/lib/parser.js CHANGED
@@ -5,6 +5,8 @@ module.exports = ( function () {
5
5
  * Generated by WikiPEG
6
6
  */
7
7
 
8
+
9
+
8
10
  function peg$subclass(child, parent) {
9
11
  function ctor() { this.constructor = child; }
10
12
  ctor.prototype = parent.prototype;
package/lib/peg.js CHANGED
@@ -5,7 +5,7 @@ var arrays = require("./utils/arrays"),
5
5
 
6
6
  var PEG = {
7
7
  /* WikiPEG version (uses semantic versioning). */
8
- VERSION: "2.0.5",
8
+ VERSION: "4.0.0",
9
9
 
10
10
  GrammarError: require("./grammar-error"),
11
11
  parser: require("./parser"),
@@ -1,9 +1,7 @@
1
1
  ( function () {
2
2
 
3
3
  "use strict";
4
- /*
5
- * Generated by WikiPEG
6
- */
4
+ /*HEADER_COMMENT*/
7
5
 
8
6
  function peg$subclass(child, parent) {
9
7
  function ctor() { this.constructor = child; }
@@ -1,53 +1,67 @@
1
1
  <?php
2
-
2
+ /*HEADER_COMMENT*/
3
3
  /*NAMESPACE*/
4
4
 
5
5
  /*INITIALIZER0*/
6
6
 
7
- class CLASS_NAME extends \WikiPEG\PEGParserBase {
8
- // initializer
9
- /*INITIALIZER*/
10
-
11
- // cache init
12
- /*CACHE_INIT*/
13
-
14
- // expectations
15
- protected $expectations = [
16
- /*EXPECTATIONS*/
17
- ];
18
-
19
- // actions
20
- /*ACTIONS*/
21
-
22
- // generated
23
- /*GENERATED*/
24
-
25
- public function parse($input, $options = []) {
26
- $this->initInternal($input, $options);
27
- $startRule = $options['startRule'] ?? '(DEFAULT)';
28
- $result = null;
29
-
30
- if (!empty($options['stream'])) {
31
- switch ($startRule) {
32
- /*STREAM_CASES*/
33
- default:
34
- throw new \WikiPEG\InternalError("Can't stream rule $startRule.");
35
- }
36
- } else {
37
- switch ($startRule) {
38
- /*START_CASES*/
39
- default:
40
- throw new \WikiPEG\InternalError("Can't start parsing from rule $startRule.");
41
- }
42
- }
43
-
44
- if ($result !== self::$FAILED && $this->currPos === $this->inputLength) {
45
- return $result;
46
- } else {
47
- if ($result !== self::$FAILED && $this->currPos < $this->inputLength) {
48
- $this->fail(/*END_EXPECTATION*/);
49
- }
50
- throw $this->buildParseException();
51
- }
52
- }
7
+ /*CACHE_ENTRY_BEGIN*/
8
+ class CLASS_NAMECacheEntry {
9
+ public $nextPos;
10
+ public $result;
11
+ /*CACHE_ENTRY_DECLARE*/
12
+
13
+ public function __construct( $nextPos, $result/*CACHE_ENTRY_ARGS*/ ) {
14
+ $this->nextPos = $nextPos;
15
+ $this->result = $result;
16
+ /*CACHE_ENTRY_INIT*/
17
+ }
18
+ }
19
+ /*CACHE_ENTRY_END*/
20
+
21
+ class CLASS_NAME extends \Wikimedia\WikiPEG\PEGParserBase {
22
+ // initializer
23
+ /*INITIALIZER*/
24
+
25
+ // cache init
26
+ /*CACHE_INIT*/
27
+
28
+ // expectations
29
+ protected $expectations = [
30
+ /*EXPECTATIONS*/
31
+ ];
32
+
33
+ // actions
34
+ /*ACTIONS*/
35
+
36
+ // generated
37
+ /*GENERATED*/
38
+
39
+ public function parse( $input, $options = [] ) {
40
+ $this->initInternal( $input, $options );
41
+ $startRule = $options['startRule'] ?? '(DEFAULT)';
42
+ $result = null;
43
+
44
+ if ( !empty( $options['stream'] ) ) {
45
+ switch ( $startRule ) {
46
+ /*STREAM_CASES*/
47
+ default:
48
+ throw new \Wikimedia\WikiPEG\InternalError( "Can't stream rule $startRule." );
49
+ }
50
+ } else {
51
+ switch ( $startRule ) {
52
+ /*START_CASES*/
53
+ default:
54
+ throw new \Wikimedia\WikiPEG\InternalError( "Can't start parsing from rule $startRule." );
55
+ }
56
+ }
57
+
58
+ if ( $result !== self::$FAILED && $this->currPos === $this->inputLength ) {
59
+ return $result;
60
+ } else {
61
+ if ( $result !== self::$FAILED && $this->currPos < $this->inputLength ) {
62
+ $this->fail( /*END_EXPECTATION*/ );
63
+ }
64
+ throw $this->buildParseException();
65
+ }
66
+ }
53
67
  }
package/package.json CHANGED
@@ -1,49 +1,49 @@
1
1
  {
2
- "name": "wikipeg",
3
- "version": "2.0.5",
4
- "description": "Parser generator for JavaScript and PHP",
5
- "license": "MIT",
6
- "homepage": "https://gerrit.wikimedia.org/r/plugins/gitiles/wikipeg/",
7
- "contributors": [
8
- {
9
- "name": "David Majda",
10
- "email": "david@majda.cz",
11
- "url": "http://majda.cz/"
12
- },
13
- {
14
- "name": "Tim Starling",
15
- "email": "tstarling@wikimedia.org"
16
- }
17
- ],
18
- "files": [
19
- "CHANGELOG.md",
20
- "LICENSE",
21
- "README.md",
22
- "VERSION",
23
- "bin/wikipeg",
24
- "examples/*.pegjs",
25
- "examples/*.pegphp",
26
- "lib/**/*.js",
27
- "lib/**/*.php",
28
- "src/**/*.php",
29
- "tools/build-browser.js",
30
- "package.json"
31
- ],
32
- "main": "lib/peg",
33
- "bin": "bin/wikipeg",
34
- "scripts": {
35
- "eslint": "make eslint",
36
- "test": "make eslint && make test"
37
- },
38
- "repository": {
39
- "type": "git",
40
- "url": "https://gerrit.wikimedia.org/r/wikipeg"
41
- },
42
- "devDependencies": {
43
- "jasmine-node": "3.0.0",
44
- "eslint": "5.14.1"
45
- },
46
- "engines": {
47
- "node": ">= 6.0.0"
48
- }
2
+ "name": "wikipeg",
3
+ "version": "4.0.0",
4
+ "description": "Parser generator for JavaScript and PHP",
5
+ "license": "MIT",
6
+ "homepage": "https://gerrit.wikimedia.org/r/plugins/gitiles/wikipeg/",
7
+ "contributors": [
8
+ {
9
+ "name": "David Majda",
10
+ "email": "david@majda.cz",
11
+ "url": "http://majda.cz/"
12
+ },
13
+ {
14
+ "name": "Tim Starling",
15
+ "email": "tstarling@wikimedia.org"
16
+ }
17
+ ],
18
+ "files": [
19
+ "CHANGELOG.md",
20
+ "LICENSE",
21
+ "README.md",
22
+ "VERSION",
23
+ "bin/wikipeg",
24
+ "examples/*.pegjs",
25
+ "examples/*.pegphp",
26
+ "lib/**/*.js",
27
+ "lib/**/*.php",
28
+ "src/**/*.php",
29
+ "tools/build-browser.js",
30
+ "package.json"
31
+ ],
32
+ "main": "lib/peg",
33
+ "bin": "bin/wikipeg",
34
+ "scripts": {
35
+ "eslint": "make eslint",
36
+ "test": "make eslint && make test"
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://gerrit.wikimedia.org/r/wikipeg"
41
+ },
42
+ "devDependencies": {
43
+ "eslint": "8.31.0",
44
+ "jasmine-node": "3.0.0"
45
+ },
46
+ "engines": {
47
+ "node": ">= 6.0.0"
48
+ }
49
49
  }
@@ -1,72 +1,73 @@
1
1
  <?php
2
2
 
3
- namespace WikiPEG;
3
+ namespace Wikimedia\WikiPEG;
4
+
5
+ use InvalidArgumentException;
4
6
 
5
7
  class DefaultTracer implements Tracer {
6
- private $indentLevel = 0;
8
+ private $indentLevel = 0;
7
9
 
8
- public function trace($event) {
9
- switch ($event['type']) {
10
- case 'rule.enter':
11
- $this->log($event);
12
- $this->indentLevel++;
13
- break;
10
+ public function trace( $event ) {
11
+ switch ( $event['type'] ) {
12
+ case 'rule.enter':
13
+ $this->log( $event );
14
+ $this->indentLevel++;
15
+ break;
14
16
 
15
- case 'rule.match':
16
- $this->indentLevel--;
17
- $this->log($event);
18
- break;
17
+ case 'rule.match':
18
+ $this->indentLevel--;
19
+ $this->log( $event );
20
+ break;
19
21
 
20
- case 'rule.fail':
21
- $this->indentLevel--;
22
- $this->log($event);
23
- break;
22
+ case 'rule.fail':
23
+ $this->indentLevel--;
24
+ $this->log( $event );
25
+ break;
24
26
 
25
- default:
26
- throw new \Exception("Invalid event type {$event['type']}");
27
- }
28
- }
27
+ default:
28
+ throw new InvalidArgumentException( "Invalid event type {$event['type']}" );
29
+ }
30
+ }
29
31
 
30
- private function log($event) {
31
- print
32
- str_pad(
33
- ''.$event['location'],
34
- 20
35
- )
36
- . str_pad($event['type'], 10) . ' '
37
- . str_repeat(' ', $this->indentLevel) . $event['rule']
38
- . $this->formatArgs($event['args'] ?? null)
39
- . "\n";
40
- }
32
+ private function log( $event ) {
33
+ print str_pad(
34
+ '' . $event['location'],
35
+ 20
36
+ )
37
+ . str_pad( $event['type'], 10 ) . ' '
38
+ . str_repeat( ' ', $this->indentLevel ) . $event['rule']
39
+ . $this->formatArgs( $event['args'] ?? null )
40
+ . "\n";
41
+ }
41
42
 
42
- private function formatArgs($argMap) {
43
- if (!$argMap) {
44
- return '';
45
- }
43
+ private function formatArgs( $argMap ) {
44
+ if ( !$argMap ) {
45
+ return '';
46
+ }
46
47
 
47
- $argParts = [];
48
- foreach ($argMap as $argName => $argValue) {
49
- if ($argName === '$silence') {
50
- continue;
51
- }
52
- if ($argName === '$boolParams') {
53
- $argParts[] = '0x' . base_convert($argValue, 10, 16);
54
- } else {
55
- $displayName = str_replace( '$param_', '', $argName);
56
- if ( $displayName[0] === '&' ) {
57
- $displayName = substr( $displayName, 1 );
58
- $ref = '&';
59
- } else {
60
- $ref = '';
61
- }
62
- $argParts[] = "$displayName=$ref" .
63
- json_encode( $argValue, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
64
- }
65
- }
66
- if ($argParts) {
67
- return '<' . implode(', ', $argParts) . '>';
68
- } else {
69
- return '';
70
- }
71
- }
48
+ $argParts = [];
49
+ foreach ( $argMap as $argName => $argValue ) {
50
+ if ( $argName === '$silence' ) {
51
+ continue;
52
+ }
53
+ if ( $argName === '$boolParams' ) {
54
+ $argParts[] = '0x' . base_convert( $argValue, 10, 16 );
55
+ } else {
56
+ $displayName = str_replace( '$param_', '', $argName );
57
+ if ( $displayName[0] === '&' ) {
58
+ $displayName = substr( $displayName, 1 );
59
+ $ref = '&';
60
+ } else {
61
+ $ref = '';
62
+ }
63
+ $argParts[] = "$displayName=$ref" .
64
+ json_encode( $argValue, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
65
+ }
66
+ }
67
+ if ( $argParts ) {
68
+ return '<' . implode( ', ', $argParts ) . '>';
69
+ } else {
70
+ return '';
71
+ }
72
+ }
72
73
  }