eyeprolog 1.2.37 → 1.2.38

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.2.37",
6
+ "version": "1.2.38",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/parser.js CHANGED
@@ -606,7 +606,9 @@ class Parser {
606
606
  this.advance();
607
607
  return atom('{}');
608
608
  }
609
- const term = this.parseTerm(0, true);
609
+ // As with a parenthesized term, a current operator atom may be the entire
610
+ // curly-bracket content: `{*}` denotes {}(*), not an incomplete infix use.
611
+ const term = this.parseTerm(0, true, true, true);
610
612
  this.expect(TOK.RBRACE, '}');
611
613
  this.advance();
612
614
  return compound('{}', [term]);
@@ -732,7 +734,7 @@ class Parser {
732
734
  // an argument delimiter there is no operand for prefix syntax, so keep
733
735
  // the operator as an atom instead of reporting a misleading bad-term
734
736
  // error.
735
- if ([TOK.COMMA, TOK.RPAREN, TOK.RBRACKET, TOK.BAR, TOK.DOT].includes(this.token.type)) {
737
+ if ([TOK.COMMA, TOK.RPAREN, TOK.RBRACKET, TOK.RBRACE, TOK.BAR, TOK.DOT].includes(this.token.type)) {
736
738
  if (!allowOperatorAtom && this.token.type !== TOK.DOT) {
737
739
  throw new Error(`parse line ${this.token.line}: operator atom ${op} requires argument context or parentheses`);
738
740
  }
package/src/write.js CHANGED
@@ -227,7 +227,9 @@ function format(term, env, options, table, maxPriority = 1200, context = 'term')
227
227
  }
228
228
 
229
229
  if (!options.ignoreOps && resolved.name === '{}' && resolved.arity === 1) {
230
- return `{${format(resolved.args[0], env, options, table, 1200)}}`;
230
+ // A current operator atom is valid as the complete curly-bracket content,
231
+ // just as it is in a functional argument or list element.
232
+ return `{${format(resolved.args[0], env, options, table, 1200, 'argument')}}`;
231
233
  }
232
234
 
233
235
  if (!options.ignoreOps) {
@@ -9,6 +9,7 @@ answer(Binary, Octal, Hex, Character, Escaped, Curly, EmptyCurly, IntegerPart, F
9
9
  =(Character, 0'\n),
10
10
  =(Escaped, 'A\x42\\101\'),
11
11
  =(Curly, {pair(a, b)}),
12
+ {*} = {}(*),
12
13
  =(EmptyCurly, {}),
13
14
  IntegerPart is float_integer_part(-3.75),
14
15
  FractionalPart is float_fractional_part(-3.75).
@@ -38,7 +38,7 @@ import {
38
38
  variantTerms,
39
39
  parseProgramText,
40
40
  } from '../src/index.js';
41
- import { parseGoalText, parseNumberTokenText } from '../src/parser.js';
41
+ import { ISO_OPERATOR_DEFINITIONS, parseGoalText, parseNumberTokenText } from '../src/parser.js';
42
42
  import { compareTerms } from '../src/term.js';
43
43
  import { formatTermForWrite } from '../src/write.js';
44
44
  import { selectClauseCandidates } from '../src/program.js';
@@ -782,6 +782,38 @@ c4 ?- call((!;1)).
782
782
  assertNotIncludes(result.stdout, "'?-'", 'writeq(?-) has no quotes');
783
783
  },
784
784
  },
785
+ {
786
+ name: 'curly brackets accept ISO and custom operator atoms (issue #41)',
787
+ run: () => {
788
+ const operatorNames = [...new Set(ISO_OPERATOR_DEFINITIONS.map(([, , name]) => name))]
789
+ .filter((name) => name !== ',');
790
+ for (const name of operatorNames) {
791
+ const holder = parseGoalText(`holder({${name}})`);
792
+ const curly = holder.args[0];
793
+ assertEqual(curly.name, '{}', `curly functor for ${name}`);
794
+ assertEqual(curly.args[0].name, name, `curly operator atom ${name}`);
795
+ }
796
+
797
+ const customProgram = parseProgramText([
798
+ ':- op(100, fx, pre).',
799
+ ':- op(100, xf, post).',
800
+ ':- op(100, xfx, infix).',
801
+ 'custom({pre}, {post}, {infix}).',
802
+ '',
803
+ ].join('\n'));
804
+ const custom = customProgram.find((clause) => clause.head.name === 'custom').head;
805
+ assertEqual(
806
+ custom.args.map((curly) => curly.args[0].name).join(','),
807
+ 'pre,post,infix',
808
+ 'custom prefix, postfix, and infix operator atoms',
809
+ );
810
+
811
+ const repl = runCli([], { input: 'read(T).\n{*}.\nhalt.\n' });
812
+ assertEqual(repl.status, 0, 'curly operator REPL status');
813
+ assertIncludes(repl.stdout, 'T = {*}.', 'curly operator REPL answer');
814
+ assertNotIncludes(repl.stdout, '{(*)}', 'curly operator has no unnecessary parentheses');
815
+ },
816
+ },
785
817
  {
786
818
  name: 'normal parser rejects non-conforming bare operator operands',
787
819
  run: () => {
@@ -5197,12 +5197,14 @@ parsing of subsequent text, place them before their first use. ISO argument
5197
5197
  syntax also permits an atom that is currently an operator to appear directly
5198
5198
  as a functional argument or list element, so forms such as
5199
5199
  `current_op(Priority, Specifier, :-)` and `[:-,-]` are valid without quoting
5200
- or parenthesizing those operator atoms. Term output observes the same `arg`
5201
- rule: with `quoted(true)`, an operator atom is not quoted merely because it is
5202
- an operator when it occurs as a functional argument or list element. Thus
5203
- `writeq([:-,-])` emits `[:-,-]`, and `writeq(f(;,'|',';;'))` emits
5204
- `f(;,'|',';;')`; the bar stays quoted because ISO treats the unquoted `|`
5205
- token as a list separator rather than an atom. The ISO initial operator table also
5200
+ or parenthesizing those operator atoms. A current operator atom may likewise
5201
+ be the complete content of parentheses or curly brackets: `(+)` denotes the
5202
+ atom `+`, and `{*}` denotes the curly term `{}(*)`. Term output observes the
5203
+ same context rules: with `quoted(true)`, an operator atom is not quoted merely
5204
+ because it occurs as a functional argument, list element, or sole curly-bracket
5205
+ content. Thus `writeq({*})` emits `{*}`, `writeq([:-,-])` emits `[:-,-]`, and
5206
+ `writeq(f(;,'|',';;'))` emits `f(;,'|',';;')`; the bar stays quoted because
5207
+ ISO treats the unquoted `|` token as a list separator rather than an atom. The ISO initial operator table also
5206
5208
  contains `?-` at priority 1200 with specifier `fx`, so
5207
5209
  `current_op(1200, fx, ?-)` succeeds. EyeProlog's embedded quad syntax permits
5208
5210
  an optional label before the query marker (`Label ?- Query.`), so while quad