wikipeg 6.1.2 → 6.1.3

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/HISTORY.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Release History
2
2
 
3
+ ## 6.1.3 (2026-06-03)
4
+ * Add array-transformation rules to --common-lang transformation,
5
+ which allows you to generate PHP and JavaScript parser from the same
6
+ grammar specification.
7
+ * Add --esm option to wikipeg binary to allow creation of a JavaScript
8
+ ESM module.
9
+ * Bug fix to parameter value assertions involving reference
10
+ parameters, which would previously incorrectly fail a type check.
11
+
3
12
  ## 6.1.2 (2026-05-07)
4
13
  * Update jasmine to 7.0.0-pre.1. This removes the reported security
5
14
  vulnerability in the glob package even though this is only used
package/README.md CHANGED
@@ -589,12 +589,17 @@ All parameters referenced in the grammar have an initial value and can
589
589
  be used before their first assignment.
590
590
 
591
591
  Parameters have a type detected at compile time: boolean, integer,
592
- string, reference, or labeled expression. Initial values for each type are:
592
+ or string. Initial values for each type are:
593
593
  - boolean: false
594
594
  - integer: 0
595
595
  - string: ""
596
- - reference: null
597
- - labeled expression: null (or the integer or string default value)
596
+
597
+ Parameters can also be a *reference* to a value of one of these types,
598
+ in which case they are initialized to `null`. Parameters can also be
599
+ set to the value of a labeled expression; these assignments are
600
+ ignored for the purpose of assigning a type and initial value. However,
601
+ if *every* assignment to the parameter is a labeled expression it is
602
+ given the initial value `null`.
598
603
 
599
604
  The parameter namespace is global, but the value of a parameter reverts
600
605
  to its previous value after termination of the assigning rule reference.
@@ -747,10 +752,39 @@ done to manage the memory cost of excessive caching of simple matches.
747
752
  For more predictable caching, you may wish to use the `noInlining`
748
753
  option.
749
754
 
750
- Requirements
751
- -------------
752
-
753
- * Node.js 6 or later
755
+ Making a release
756
+ ----------------
757
+ Each wikipeg release is made to the PHP ecosystem
758
+ (composer/packagist.org) and Node/JS ecosystem (npm/npmjs.org)
759
+ together.
760
+
761
+ 1. Begin by running `composer update-history` which will
762
+ update the [`HISTORY.md`](./HISTORY.md) with the next patch version.
763
+ Update the release number in `HISTORY.md` if this is to be a minor or
764
+ major release.
765
+
766
+ 2. Update the version number in [`package.json`](./package.json) and
767
+ at the top of [`lib/peg.js`](./lib/peg.js).
768
+
769
+ 3. Run `npm install --package-lock-only` to update the version number
770
+ in [`package-lock.json`](./package-lock.json).
771
+
772
+ 4. Commit these changes with the commit message "Release <version number>",
773
+ and push to gerrit. When merged, sign and tag the resulting commit
774
+ with the unprefixed version number; eg `git tag -s 6.1.3`. Push the
775
+ tag to the origin (`git push origin 6.1.3`) to complete the
776
+ PHP/composer/`packagist.org` release.
777
+
778
+ 5. With the tagged release as your local HEAD, `npm publish` to push
779
+ the release to node/npm/`npmjs.org`.
780
+
781
+ 6. Run `composer update-history` again, which should add an entry to
782
+ `HISTORY.md` for the next "not yet released" version. Add a `-git`
783
+ suffix to the version number in [`package.json`](./package.json) and
784
+ at the top of [`lib/peg.js`](./lib/peg.js).
785
+ Run `npm install --package-lock-only` to update `package-lock.json`.
786
+ Commit these changes with the commit message "Bump version after
787
+ release" and push to gerrit.
754
788
 
755
789
  Development
756
790
  -----------
package/bin/wikipeg CHANGED
@@ -33,6 +33,7 @@ Options:
33
33
  "module.exports")
34
34
  --php force PHP mode regardless of extension
35
35
  --class-name <name> specify generated PHP class name
36
+ --esm generate ESM module instead of CommonJS
36
37
  --cache make generated parser cache results
37
38
  --allowed-start-rules <rules> comma-separated list of rules the generated
38
39
  parser will be allowed to start parsing
@@ -122,6 +123,7 @@ function readStream(inputStream, callback) {
122
123
 
123
124
  /* This makes the generated parser a CommonJS module by default. */
124
125
  var exportVar = "module.exports";
126
+ var sourceSuffix = "";
125
127
  var options = {
126
128
  cache: false,
127
129
  output: "source",
@@ -142,6 +144,11 @@ while (args.length > 0 && isOption(args[0])) {
142
144
  options.language = "php";
143
145
  break;
144
146
 
147
+ case "--esm":
148
+ exportVar = "const api";
149
+ sourceSuffix = "\nexport default api;";
150
+ break;
151
+
145
152
  case '--common-lang':
146
153
  options.commonLang = true;
147
154
  break;
@@ -318,6 +325,7 @@ readStream(inputStream, function(input) {
318
325
 
319
326
  if (options.language === 'javascript') {
320
327
  outputStream.write(exportVar !== "" ? `${exportVar} = ${source}\n` : `${source}\n`);
328
+ outputStream.write(sourceSuffix);
321
329
  } else {
322
330
  outputStream.write(source + "\n");
323
331
  }
@@ -10,7 +10,7 @@ function analyzeParams(ast, options) {
10
10
  var boolIndex = 0;
11
11
  var refScopeIndex = 0;
12
12
 
13
- function registerParamType(name, type, location) {
13
+ function registerParamType(name, isref, type, location) {
14
14
  var paramInfo = getParamInfo(name);
15
15
  if (
16
16
  (paramInfo.type === 'labeled' && type === 'boolean') ||
@@ -18,12 +18,27 @@ function analyzeParams(ast, options) {
18
18
  ){
19
19
  throw new GrammarError("Labeled expression parameters can't also be boolean: " + name, location);
20
20
  }
21
+ if (paramInfo.isref === undefined ) {
22
+ paramInfo.isref = isref;
23
+ paramInfo.isrefLocation = location;
24
+ } else if (isref !== undefined ) {
25
+ if ( paramInfo.isref !== isref) {
26
+ throw new GrammarError(
27
+ "Reference type conflict in parameter " + name +
28
+ " (previous def at " + paramInfo.isrefLocation + ")",
29
+ location);
30
+ }
31
+ }
21
32
  if (paramInfo.type !== undefined && paramInfo.type !== 'labeled') {
22
33
  if (paramInfo.type !== type && type !== 'labeled') {
23
- throw new GrammarError("Type conflict in parameter " + name, location);
34
+ throw new GrammarError(
35
+ "Type conflict in parameter " + name +
36
+ " (previous def at " + paramInfo.typeLocation + ")",
37
+ location);
24
38
  }
25
39
  } else {
26
40
  paramInfo.type = type;
41
+ paramInfo.typeLocation = location;
27
42
  if (type === 'boolean') {
28
43
  if (boolIndex > 31) {
29
44
  throw new GrammarError("A maximum of 32 boolean parameters may be defined", location);
@@ -118,14 +133,12 @@ function analyzeParams(ast, options) {
118
133
  let assignment = node.assignments[i];
119
134
  let type;
120
135
 
121
- if (assignment.isref) {
122
- type = 'reference';
123
- } else if (assignment.type === 'increment') {
136
+ if (assignment.type === 'increment') {
124
137
  type = 'integer';
125
138
  } else {
126
139
  type = assignment.type;
127
140
  }
128
- registerParamType(assignment.name, type, node.location);
141
+ registerParamType(assignment.name, assignment.isref || false, type, node.location);
129
142
  assignment.paramInfo = getParamInfo(assignment.name);
130
143
 
131
144
  let refScope = null;
@@ -142,12 +155,12 @@ function analyzeParams(ast, options) {
142
155
  },
143
156
  parameter_and: function(node) {
144
157
  if (node.assert) {
145
- registerParamType(node.parameter, node.assert.type, node.location);
158
+ registerParamType(node.parameter, undefined, node.assert.type, node.location);
146
159
  }
147
160
  },
148
161
  parameter_not: function(node) {
149
162
  if (node.assert) {
150
- registerParamType(node.parameter, node.assert.type, node.location);
163
+ registerParamType(node.parameter, undefined, node.assert.type, node.location);
151
164
  }
152
165
  },
153
166
  })(ast);
@@ -337,7 +337,7 @@ function generateJavascript(ast, options) {
337
337
  let rule = currentRule;
338
338
  // Add reference variables
339
339
  for (let name in rule.passedParams) {
340
- if (rule.passedParams[name].type === 'reference') {
340
+ if (rule.passedParams[name].isref) {
341
341
  saved.refs.push({reg: allocReg([]), name: name});
342
342
  }
343
343
  }
@@ -595,7 +595,7 @@ function generateJavascript(ast, options) {
595
595
  function makeActionFunc(code, context) {
596
596
  var argNames = [];
597
597
  Object.keys(context.env).forEach(function(argName) {
598
- if (context.envTypes[argName] === 'reference') {
598
+ if (context.envTypes[argName].isref) {
599
599
  argNames.push(language.refArgActionDeclarator(argName));
600
600
  } else {
601
601
  argNames.push(language.valueArgActionDeclarator(argName));
@@ -641,7 +641,7 @@ function generateJavascript(ast, options) {
641
641
  */
642
642
  function makeParamExpression(info) {
643
643
  if (!currentRule || !currentRule.passedParams[info.name]) {
644
- return makeInitialParamValue(info, false);
644
+ return makeInitialParamValue(info);
645
645
  }
646
646
  if (info.type === undefined) {
647
647
  throw new Error("Undefined parameter type");
@@ -649,7 +649,7 @@ function generateJavascript(ast, options) {
649
649
  return [
650
650
  '/*', info.name, '*/',
651
651
  '(', language.boolParams, ' & 0x', (1 << info.index).toString(16), ') !== 0'].join('');
652
- } else if (info.type === 'reference') {
652
+ } else if (info.isref) {
653
653
  return language.refParamValue(info.name);
654
654
  } else {
655
655
  return language.paramArgName(info.name);
@@ -660,7 +660,7 @@ function generateJavascript(ast, options) {
660
660
  * Return an expression which gives the parameter reference object
661
661
  */
662
662
  function makeParamRefExpression(info) {
663
- if (info.type !== 'reference') {
663
+ if (!info.isref) {
664
664
  throw new Error('Cannot make reference object for non-reference parameter ' + info.name);
665
665
  }
666
666
  return language.paramArgName(info.name);
@@ -669,20 +669,19 @@ function generateJavascript(ast, options) {
669
669
  /**
670
670
  * Get an expression giving the initial value of a parameter
671
671
  */
672
- function makeInitialParamValue(info, isref) {
672
+ function makeInitialParamValue(info, refIsNull) {
673
673
  let type = info.type;
674
+ let isref = info.isref;
675
+ if (isref) {
676
+ let val = refIsNull ? 'null' : makeInitialParamValue({type:type});
677
+ return language.newRef(val);
678
+ }
674
679
  if (type === 'boolean') {
675
680
  return 'false';
676
681
  } else if (type === 'integer') {
677
682
  return '0';
678
683
  } else if (type === 'string') {
679
684
  return '""';
680
- } else if (type === 'reference') {
681
- if (isref) {
682
- return language.newRef('null');
683
- } else {
684
- return 'null';
685
- }
686
685
  } else if (type === 'labeled') {
687
686
  return 'null';
688
687
  } else {
@@ -861,10 +860,11 @@ function generateJavascript(ast, options) {
861
860
 
862
861
  for (let name in rule.passedParams) {
863
862
  let type = rule.passedParams[name].type;
864
- if (type === undefined || type === 'boolean') {
865
- continue;
866
- } else if (type === 'reference') {
863
+ let isref = rule.passedParams[name].isref;
864
+ if (isref) {
867
865
  args.push(language.refParamArgDeclarator(name));
866
+ } else if (type === undefined || type === 'boolean') {
867
+ continue;
868
868
  } else {
869
869
  args.push(language.paramArgName(name));
870
870
  }
@@ -889,18 +889,7 @@ function generateJavascript(ast, options) {
889
889
  args.push('0');
890
890
  } else {
891
891
  let paramName = language.paramNameFromArg(argName);
892
- let type = rule.passedParams[paramName].type;
893
- if (type === 'integer') {
894
- args.push('0');
895
- } else if (type === 'boolean') {
896
- args.push('false');
897
- } else if (type === 'string') {
898
- args.push('""');
899
- } else if (type === 'reference') {
900
- args.push(language.newRef('null'));
901
- } else {
902
- throw new Error('Unknown param type: ' + type);
903
- }
892
+ args.push(makeInitialParamValue(rule.passedParams[paramName], true));
904
893
  }
905
894
  }
906
895
  return args;
@@ -926,10 +915,11 @@ function generateJavascript(ast, options) {
926
915
 
927
916
  for (let name in rule.passedParams) {
928
917
  let type = rule.passedParams[name].type;
929
- if (type === undefined || type === 'boolean') {
930
- continue;
931
- } else if (type === 'reference') {
918
+ let isref = rule.passedParams[name].isref;
919
+ if (isref) {
932
920
  args.push(language.refParamValue(name));
921
+ } else if (type === undefined || type === 'boolean') {
922
+ continue;
933
923
  } else {
934
924
  args.push(language.paramArgName(name));
935
925
  }
@@ -945,7 +935,7 @@ function generateJavascript(ast, options) {
945
935
  function getCacheLoadRefs(rule) {
946
936
  var block = [];
947
937
  for (let name in rule.passedParams) {
948
- if (rule.passedParams[name].type === 'reference') {
938
+ if (rule.passedParams[name].isref) {
949
939
  block.push(language.cacheLoadRef(name));
950
940
  }
951
941
  }
@@ -1323,7 +1313,7 @@ function generateJavascript(ast, options) {
1323
1313
  var subresult = recurse(node.expression, newContext);
1324
1314
  subresult.block.push(`// ${node.label} <- ${reg}`);
1325
1315
  context.env[node.label] = reg;
1326
- context.envTypes[node.label] = 'value';
1316
+ context.envTypes[node.label] = { type: 'value' };
1327
1317
  return subresult;
1328
1318
  },
1329
1319
 
@@ -1334,7 +1324,7 @@ function generateJavascript(ast, options) {
1334
1324
  } else {
1335
1325
  context.env[node.label] = makeParamExpression(node.paramInfo);
1336
1326
  }
1337
- context.envTypes[node.label] = node.paramInfo.type;
1327
+ context.envTypes[node.label] = node.paramInfo;
1338
1328
  result.condition = 'true';
1339
1329
  result.expression = language.assertionSuccess;
1340
1330
  return result;
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
 
3
- var visitor = require("../visitor");
3
+ var visitor = require("../visitor");
4
+ var transformArray = require("../../utils/transform-array");
5
+
4
6
 
5
7
  /**
6
8
  * Perform a few simple transformations to convert a special subset of PHP to
@@ -14,6 +16,7 @@ function transformCommonLang(ast, options) {
14
16
 
15
17
  function transform(code) {
16
18
  if (options.language === 'javascript') {
19
+ code = transformArray.parse(code);
17
20
  code = code.replace(/\$this->/g, '');
18
21
  code = code.replace(/\$(?=\w+)/g, '');
19
22
  }
package/lib/peg.js CHANGED
@@ -4,7 +4,7 @@ var objects = require("./utils/objects");
4
4
 
5
5
  var PEG = {
6
6
  /* WikiPEG version (uses semantic versioning). */
7
- VERSION: "6.1.2",
7
+ VERSION: "6.1.3",
8
8
 
9
9
  GrammarError: require("./grammar-error"),
10
10
  parser: require("./parser"),