Skip to content

Commit

Permalink
Error-trace shows a bag (multiset) like a sequence.
Browse files Browse the repository at this point in the history
Fixes Github issue #250
#250

[Bug]
  • Loading branch information
lemmy committed Oct 26, 2022
1 parent aa94ba2 commit 1d9161c
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 260 deletions.
72 changes: 21 additions & 51 deletions src/model/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,10 @@ export abstract class CollectionValue extends Value {
readonly items: Value[],
readonly prefix: string,
readonly postfix: string,
readonly delim = ', ',
toStr?: (v: Value) => string
) {
super(key, makeCollectionValueString(items, prefix, postfix, ', ', toStr || (v => v.str)));
super(key, makeCollectionValueString(items, prefix, postfix, delim, toStr || (v => v.str)));
}

addDeletedItems(items: Value[]): void {
Expand Down Expand Up @@ -248,7 +249,7 @@ export abstract class CollectionValue extends Value {
}
const subIndent = indent + ' ';
const fmtFunc = (v: Value) => this.formatKey(subIndent, v) + '' + v.format(subIndent);
const body = makeCollectionValueString(this.items, '', '', ',\n', fmtFunc);
const body = makeCollectionValueString(this.items, '', '', this.delim + '\n', fmtFunc);
return `${this.prefix}\n${body}\n${indent}${this.postfix}`;
}

Expand Down Expand Up @@ -292,17 +293,29 @@ export class SequenceValue extends CollectionValue {
* Represents a structure: [a |-> 'A', b |-> 34, c |-> <<TRUE, 2>>], [], etc.
*/
export class StructureValue extends CollectionValue {
constructor(key: ValueKey, items: Value[], preserveOrder = false) {
if (!preserveOrder) {
items.sort(StructureValue.compareItems);
}
super(key, items, '[', ']', StructureValue.itemToString);
constructor(
key: ValueKey,
items: Value[],
prefix = '[',
postfix = ']',
delim = ', ',
readonly itemSep = ' |-> ',
toStr = StructureValue.itemToString,
preserveOrder = false) {
super(key, items, prefix, postfix, delim, toStr);
if (!preserveOrder) {
items.sort(StructureValue.compareItems);
}
}

static itemToString(item: Value): string {
return `${item.key} |-> ${item.str}`;
}

static funcItemToString(item: Value): string {
return `${item.key} :> ${item.str}`;
}

static compareItems(a: Value, b: Value): number {
if (a.key < b.key) {
return -1;
Expand All @@ -318,50 +331,7 @@ export class StructureValue extends CollectionValue {
}

formatKey(indent: string, value: Value): string {
return `${indent}${value.key} |-> `;
}
}

/**
* Represents a simple function: (10 :> TRUE), ("foo" :> "bar"), etc
*/
export class SimpleFunctionItem extends Value {
constructor(
key: ValueKey,
readonly from: Value,
readonly to: Value
) {
super(key, `${from.str} :> ${to.str}`);
}

format(indent: string): string {
if (this.str.length <= VALUE_FORMAT_LENGTH_THRESHOLD || !(this.to instanceof CollectionValue)) {
return `(${this.str})`;
}
const body = this.to.format(indent + ' ');
return `(${this.from.str} :> ${body}\n${indent})`;
}
}

/**
* Represents a collection of merged simple functions: (10 :> TRUE),
* ("foo" :> "bar" @@ "baz" => 31), etc
*/
export class SimpleFunction extends Value {
readonly expandSingle = false;

constructor(
key: ValueKey,
readonly items: SimpleFunctionItem[]
) {
super(key, makeCollectionValueString(items, '(', ')', ' @@ ', (v => v.str)));
}

format(indent: string): string {
if (this.items.length === 1) {
return this.items[0].format(indent);
}
return super.format(indent);
return `${indent}${value.key}` + this.itemSep;
}
}

Expand Down
22 changes: 7 additions & 15 deletions src/parsers/tlcValues.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Value, StructureValue, SetValue, SequenceValue, ValueKey, SimpleFunction,
NameValue, SimpleFunctionItem } from '../model/check';
import { Value, StructureValue, SetValue, SequenceValue, ValueKey,
NameValue } from '../model/check';
import { ParsingError } from '../common';
import { Position } from 'vscode';

Expand Down Expand Up @@ -214,7 +214,7 @@ function parseValue(key: ValueKey, token: Token, tokenizer: Tokenizer): Value {
}
if (token.type === TokenType.FunctionStart) {
const items = parseCollectionItems(tokenizer, TokenType.FunctionEnd, TokenType.AtAt, parseFunctionItem);
return new SimpleFunction(key, items);
return new StructureValue(key, items, '(', ')', ' @@ ', ' :> ', StructureValue.funcItemToString);
}
throw new ParsingError(`Unexpected token at ${tokenizer.getPosition()}: ${token.str}`);
}
Expand Down Expand Up @@ -263,22 +263,14 @@ function parseStructureItem(_: ValueKey, token: Token, tokenizer: Tokenizer): Va
return parseValue(token.str, tokenizer.nextToken(), tokenizer);
}

function parseFunctionItem(key: ValueKey, tokenFrom: Token, tokenizer: Tokenizer): SimpleFunctionItem {
function parseFunctionItem(key: ValueKey, tokenFrom: Token, tokenizer: Tokenizer): Value {
if (tokenFrom === Token.END) {
console.log(`Unexpected function description end at ${tokenizer.getPosition()}`);
return new SimpleFunctionItem(key, UNKNOWN_FROM, UNKNOWN_TO);
throw new ParsingError(`Expected structure item at ${tokenizer.getPosition()}, found ${tokenFrom.str}`);
}
const from = parseValue('from', tokenFrom, tokenizer);
const tokenColon = tokenizer.nextToken();
if (tokenColon.type !== TokenType.ColonBracket) {
console.log(`Unexpected function description end at ${tokenizer.getPosition()}`);
return new SimpleFunctionItem(key, from, UNKNOWN_TO);
throw new ParsingError(`Expected function item separator at ${tokenizer.getPosition()}, found ${tokenColon.str}`);
}
const tokenTo = tokenizer.nextToken();
if (tokenTo === Token.END) {
console.log(`Unexpected function description end at ${tokenizer.getPosition()}`);
return new SimpleFunctionItem(key, from, UNKNOWN_TO);
}
const to = parseValue('to', tokenTo, tokenizer);
return new SimpleFunctionItem(key, from, to);
return parseValue(from.str, tokenizer.nextToken(), tokenizer);
}
2 changes: 1 addition & 1 deletion tests/suite/model/check.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ function setX(key: ValueKey, change: Change, ...items: Value[]) {
}

function structX(key: ValueKey, change: Change, ...items: Value[]) {
const value = new StructureValue(key, items, true);
const value = new StructureValue(key, items, '[', ']', ', ', ' |-> ', StructureValue.itemToString, true);
value.changeType = change;
return value;
}
186 changes: 7 additions & 179 deletions tests/suite/parsers/tlcValues.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as assert from 'assert';
import { before } from 'mocha';
import { parseVariableValue } from '../../../src/parsers/tlcValues';
import { Value } from '../../../src/model/check';
import { v, set, seq, struct, func, n, funcMerge, funcItem } from '../shortcuts';
import { v, set, seq, struct, n } from '../shortcuts';

const ROOT = 'root';

Expand Down Expand Up @@ -133,83 +133,6 @@ suite('TLC Values Output Parser Test Suite', () => {
assertValue(lines, expect);
});

test('Parses simple functions', () => {
assertValue(
['("foo" :> -7)'],
func(ROOT, v('from', '"foo"'), v('to', '-7')));
assertValue(
['(TRUE :> <<{FALSE}>>)'],
func(ROOT, v('from', 'TRUE'), seq('to', set(1, v(1, 'FALSE')))));
});

test('Parses simple functions with var-names and var-values', () => {
assertValue(
['(a1 :> 30)'],
func(ROOT, n('from', 'a1'), v('to', '30')));
assertValue(
['(foo :> bar)'],
func(ROOT, n('from', 'foo'), n('to', 'bar')));
});

test('Parses nested simple functions', () => {
assertValue(
['("foo" :> (TRUE :> (10 :> FALSE)))'],
func(ROOT,
v('from', '"foo"'),
func('to',
v('from', 'TRUE'),
func('to',
v('from', '10'),
v('to', 'FALSE')))));
});

test('Parses merged functions', () => {
assertValue(
['(1 :> 3 @@ 2 :> 5 @@ 3 :> 10)'],
funcMerge(ROOT,
funcItem(1, v('from', '1'), v('to', '3')),
funcItem(2, v('from', '2'), v('to', '5')),
funcItem(3, v('from', '3'), v('to', '10'))
)
);
});

test('Parses complex case', () => {
const lines = [
'{ 12,',
' [ key_1 |-> <<"one", "two">>,',
' key_2 |-> { 3, 4,',
' "five", TRUE},',
' key_3 |-> [',
' subkey_41 |-> <<',
' -299384>>',
' ]],',
'(TRUE :> {(10 :> <<"foo">>)} @@ FALSE :> "false")',
'<<{}>>,',
' "long long \\" string"',
'{<<',
'',
' [ foo |-> {TRUE}, bar |-> -2..5 ]',
'>>}}',
];
const expect = set(ROOT,
v(1, '12'),
struct(2,
seq('key_1', v(1, '"one"'), v(2, '"two"')),
set('key_2', v(1, '3'), v(2, '4'), v(3, '"five"'), v(4, 'TRUE')),
struct('key_3', seq('subkey_41', v(1, '-299384')))
),
funcMerge(3,
funcItem(1, v('from', 'TRUE'), set('to', func(1, v('from', '10'), seq('to', v(1, '"foo"'))))),
funcItem(2, v('from', 'FALSE'), v('to', '"false"'))
),
seq(4, set('1')),
v(5, '"long long \\" string"'),
set(6, seq(1, struct(1, set('foo', v(1, 'TRUE')), v('bar', '-2..5')))),
);
assertValue(lines, expect);
});

test('Formats simple values without keys', () => {
assertFormat(v('foo', '"bar"'), '', ['"bar"']);
assertFormat(v('bar', '10'), '', ['10']);
Expand All @@ -229,9 +152,9 @@ suite('TLC Values Output Parser Test Suite', () => {
seq(ROOT, v(1, '10'), v(2, '20'), struct(3), v(4, '"some long-long-long string to exceed threshold"')),
'', [
'<<',
' 10,',
' 20,',
' [],',
' 10, ',
' 20, ',
' [], ',
' "some long-long-long string to exceed threshold"',
'>>'
]
Expand All @@ -245,109 +168,14 @@ suite('TLC Values Output Parser Test Suite', () => {
set(ROOT, v(1, '10'), v(2, '20'), struct(3), v(4, '"some long-long-long string to exceed threshold"')),
'', [
'{',
' 10,',
' 20,',
' [],',
' 10, ',
' 20, ',
' [], ',
' "some long-long-long string to exceed threshold"',
'}'
]
);
});

test('Formats simple structs', () => {
assertFormat(struct(ROOT), ' ', ['[]']);
assertFormat(
struct(ROOT, v('bar', '10'), v('baz', '20')),
' ', [
'[bar |-> 10, baz |-> 20]'
]);
assertFormat(
struct(ROOT, v('bar', '10'), v('baz', '20'), seq('seq')),
'', [
'[',
' bar |-> 10,',
' baz |-> 20,',
' seq |-> <<>>',
']'
]
);
});

test('Formats simple functions', () => {
assertFormat(func(ROOT, v('from', 'foo'), v('to', '30')), ' ', ['(foo :> 30)']);
assertFormat(
func(ROOT,
v('from', 'foo'),
struct('to', v('bar', '10'), v('baz', '"some long-long-long string to exceed threshold"'))
), '', [
'(foo :> [',
' bar |-> 10,',
' baz |-> "some long-long-long string to exceed threshold"',
' ]',
')'
]
);
});

test('Formats merged functions', () => {
assertFormat(
funcMerge(ROOT,
funcItem(1, v('from', '1'), v('to', '3')),
funcItem(2, v('from', '2'), v('to', '5')),
funcItem(3, v('from', '3'), v('to', '10'))
), '', [
'(1 :> 3 @@ 2 :> 5 @@ 3 :> 10)'
]);
});

test('Formats with proper indentation', () => {
const value = set(ROOT,
v(1, '12'),
struct(2,
seq('key_1', v(1, '"one"'), v(2, '"two"')),
set('key_2', v(1, '3'), v(2, 'TRUE')),
struct('key_3',
seq('subkey_41',
v(1, '-299384'),
v(2, '"some long-long-long-long string to exceed threshold"')
)
)
),
funcMerge(3,
funcItem(1, v('from', 'TRUE'), set('to', func(1, v('from', '10'), seq('to', v(1, '"foo"'))))),
funcItem(2, v('from', 'FALSE'), v('to', '"false"'))
),
seq(4, set('1')),
v(5, '"long string"'),
set(6, seq(1, struct(1, set('foo', v(1, 'TRUE')), v('bar', '-2..5')))),
);
assertFormat(value, ' ', [
'{',
' 12,',
' [',
' key_1 |-> <<"one", "two">>,',
' key_2 |-> {3, TRUE},',
' key_3 |-> [',
' subkey_41 |-> <<',
' -299384,',
' "some long-long-long-long string to exceed threshold"',
' >>',
' ]',
' ],',
' (TRUE :> {(10 :> <<"foo">>)} @@ FALSE :> "false"),',
' <<{}>>,',
' "long string",',
' {',
' <<',
' [',
' bar |-> -2..5,',
' foo |-> {TRUE}',
' ]',
' >>',
' }',
' }'
]);
});
});

function assertValue(lines: string[], expected: Value, message?: string) {
Expand Down
Loading

0 comments on commit 1d9161c

Please sign in to comment.