diff --git a/src/model/check.ts b/src/model/check.ts index b9fa4fc4..74b1b712 100644 --- a/src/model/check.ts +++ b/src/model/check.ts @@ -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 { @@ -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}`; } @@ -292,17 +293,29 @@ export class SequenceValue extends CollectionValue { * Represents a structure: [a |-> 'A', b |-> 34, c |-> <>], [], 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; @@ -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; } } diff --git a/src/parsers/tlcValues.ts b/src/parsers/tlcValues.ts index df979e5e..5d02a51e 100644 --- a/src/parsers/tlcValues.ts +++ b/src/parsers/tlcValues.ts @@ -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'; @@ -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}`); } @@ -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); } diff --git a/tests/suite/model/check.test.ts b/tests/suite/model/check.test.ts index 8d38d18c..0f99e0da 100644 --- a/tests/suite/model/check.test.ts +++ b/tests/suite/model/check.test.ts @@ -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; } diff --git a/tests/suite/parsers/tlcValues.test.ts b/tests/suite/parsers/tlcValues.test.ts index c1f85165..588d52af 100644 --- a/tests/suite/parsers/tlcValues.test.ts +++ b/tests/suite/parsers/tlcValues.test.ts @@ -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'; @@ -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']); @@ -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"', '>>' ] @@ -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) { diff --git a/tests/suite/shortcuts.ts b/tests/suite/shortcuts.ts index a54b58b6..45c38a1f 100644 --- a/tests/suite/shortcuts.ts +++ b/tests/suite/shortcuts.ts @@ -1,9 +1,9 @@ import * as vscode from 'vscode'; import moment = require('moment'); import { Moment } from 'moment'; -import { Value, ValueKey, SetValue, SequenceValue, StructureValue, SimpleFunction, +import { Value, ValueKey, SetValue, SequenceValue, StructureValue, InitialStateStatItem, CoverageItem, ModelCheckResult, CheckState, CheckStatus, MessageLine, MessageSpan, - ErrorTraceItem, OutputLine, ModelCheckResultSource, SimpleFunctionItem, ErrorInfo, + ErrorTraceItem, OutputLine, ModelCheckResultSource, ErrorInfo, WarningInfo } from '../../src/model/check'; import { DCollection } from '../../src/diagnostic'; import { ROOT_CONTAINER_NAME } from '../../src/symbols/tlaSymbols'; @@ -30,18 +30,6 @@ export function struct(key: ValueKey, ...items: Value[]): StructureValue { return new StructureValue(key, items); } -export function funcItem(key: ValueKey, from: Value, to: Value): SimpleFunctionItem { - return new SimpleFunctionItem(key, from, to); -} - -export function func(key: ValueKey, from: Value, to: Value): SimpleFunction { - return new SimpleFunction(key, [ funcItem(1, from, to) ]); -} - -export function funcMerge(key: ValueKey, ...items: SimpleFunctionItem[]): SimpleFunction { - return new SimpleFunction(key, items); -} - export function pos(line: number, char: number): vscode.Position { return new vscode.Position(line, char); }