Skip to content

Commit

Permalink
Closes #43
Browse files Browse the repository at this point in the history
  • Loading branch information
thiagodp committed Jun 18, 2019
1 parent eb84c6a commit 616355b
Show file tree
Hide file tree
Showing 94 changed files with 762 additions and 324 deletions.
23 changes: 20 additions & 3 deletions __tests__/lexer/TagLexerTest.spec.ts
@@ -1,5 +1,6 @@
import { TagLexer } from "../../modules/lexer/TagLexer";
import { TagLexer, TagSubLexer } from "../../modules/lexer/TagLexer";
import { NodeTypes } from "../../modules/req/NodeTypes";
import { ReservedTags } from "concordialang-types/dist";

/**
* @author Thiago Delgado Pinto
Expand Down Expand Up @@ -36,7 +37,7 @@ describe( 'TagLexerTest', () => {
let names = lexer.analyze( line ).nodes.map( ( node ) => node.name );
expect( names ).toEqual( [ "hello", "world" ] );
} );

it( 'detects a tag with content', () => {
let line = '@hello( world )';
let r = lexer.analyze( line );
Expand Down Expand Up @@ -100,7 +101,23 @@ describe( 'TagLexerTest', () => {
expect( nodeOne.content ).toEqual( [ 'bar' ] );
let nodeTwo = r.nodes[ 1 ];
expect( nodeTwo.name ).toEqual( 'a' );
expect( nodeTwo.content ).toEqual( [ 'b', 'c' ] );
expect( nodeTwo.content ).toEqual( [ 'b', 'c' ] );
} );



it( 'detects subtypes', () => {

const l = new TagLexer( [
new TagSubLexer( ReservedTags.IGNORE, [ ReservedTags.IGNORE ] )
] );

const r = l.analyze( '@ignore' );
expect( r ).toBeDefined();
expect( r.errors ).toHaveLength( 0 );
expect( r.nodes ).toHaveLength( 1 );

expect( r.nodes[ 0 ].subType ).toEqual( ReservedTags.IGNORE );
} );

} );
3 changes: 1 addition & 2 deletions __tests__/semantic/TestCaseSSATest.spec.ts
@@ -1,7 +1,6 @@
import { join } from 'path';
import { FileInfo, Import, Location, Feature, Document, TestCase, Tag } from 'concordialang-types';
import { FileInfo, Import, Location, Feature, Document, TestCase, Tag, ReservedTags } from 'concordialang-types';
import { NodeTypes } from '../../modules/req/NodeTypes';
import { ReservedTags } from '../../modules/req/ReservedTags';
import { AugmentedSpec } from '../../modules/ast/AugmentedSpec';
import { TestCaseSSA } from '../../modules/semantic/TestCaseSSA';

Expand Down
4 changes: 2 additions & 2 deletions __tests__/testcase/TCGenTest.spec.ts
Expand Up @@ -103,7 +103,7 @@ describe( 'TCGenTest', () => {
]
);

expect( tc.shoudFail ).toBeFalsy();
expect( tc.shouldFail ).toBeFalsy();

} );

Expand Down Expand Up @@ -168,7 +168,7 @@ describe( 'TCGenTest', () => {
]
);

expect( tc.shoudFail ).toBeTruthy();
expect( tc.shouldFail ).toBeTruthy();

// ---

Expand Down
2 changes: 2 additions & 0 deletions data/pt.json
Expand Up @@ -31,6 +31,8 @@
"tagImportance": [ "importance" ],
"tagIgnore": [ "ignore" ],
"tagGenerated": [ "generated" ],
"tagFail": [ "fail" ],
"tagGenerateOnlyValidValues": [ "generate-only-valid-values" ],

"language": [ "language" ],

Expand Down
2 changes: 2 additions & 0 deletions dist/data/pt.json
Expand Up @@ -31,6 +31,8 @@
"tagImportance": [ "importance" ],
"tagIgnore": [ "ignore" ],
"tagGenerated": [ "generated" ],
"tagFail": [ "fail" ],
"tagGenerateOnlyValidValues": [ "generate-only-valid-values" ],

"language": [ "language" ],

Expand Down
4 changes: 2 additions & 2 deletions dist/modules/app/TCGenController.js
Expand Up @@ -8,6 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const concordialang_types_1 = require("concordialang-types");
const PreTestCaseGenerator_1 = require("../testscenario/PreTestCaseGenerator");
const TSGen_1 = require("../testscenario/TSGen");
const VariantSelectionStrategy_1 = require("../selection/VariantSelectionStrategy");
Expand All @@ -20,7 +21,6 @@ const util_1 = require("util");
const RuntimeException_1 = require("../req/RuntimeException");
const fs_1 = require("fs");
const Defaults_1 = require("./Defaults");
const ReservedTags_1 = require("../req/ReservedTags");
const Warning_1 = require("../req/Warning");
const DataTestCaseMix_1 = require("../testcase/DataTestCaseMix");
class TCGenController {
Expand Down Expand Up @@ -154,7 +154,7 @@ class TCGenController {
case Defaults_1.VariantSelectionOptions.FIRST:
return new VariantSelectionStrategy_1.FirstVariantSelectionStrategy();
case Defaults_1.VariantSelectionOptions.FIRST_MOST_IMPORTANT:
return new VariantSelectionStrategy_1.FirstMostImportantVariantSelectionStrategy(options.importance, [ReservedTags_1.ReservedTags.IMPORTANCE]);
return new VariantSelectionStrategy_1.FirstMostImportantVariantSelectionStrategy(options.importance, [concordialang_types_1.ReservedTags.IMPORTANCE]);
case Defaults_1.VariantSelectionOptions.ALL:
return new VariantSelectionStrategy_1.AllVariantsSelectionStrategy();
default: {
Expand Down
2 changes: 2 additions & 0 deletions dist/modules/dict/EnglishKeywordDictionary.js
Expand Up @@ -38,6 +38,8 @@ class EnglishKeywordDictionary {
this.tagImportance = ['importance'];
this.tagIgnore = ['ignore'];
this.tagGenerated = ['generated'];
this.tagFail = ['fail'];
this.tagGenerateOnlyValidValues = ['generate-only-valid-values'];
// Also available in Gherkin
this.language = ['language'];
this.feature = ['feature', 'story', 'user story'];
Expand Down
32 changes: 25 additions & 7 deletions dist/modules/lexer/Lexer.js
@@ -1,5 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const concordialang_types_1 = require("concordialang-types");
const VariantLexer_1 = require("./VariantLexer");
const TestEventLexer_1 = require("../lexer/TestEventLexer");
const DatabasePropertyLexer_1 = require("./DatabasePropertyLexer");
Expand Down Expand Up @@ -52,16 +53,28 @@ class Lexer {
this._lexers = [];
this._lexersMap = new Map(); // iterable in insertion order
this._lastLexer = null;
this._subLexers = [];
this._inLongString = false;
this._mustRecognizeAsText = false;
const dictionary = this.loadDictionary(_defaultLanguage); // may throw Error
if (!dictionary) {
throw new Error('Cannot load a dictionary for the language: ' + _defaultLanguage);
}
this._subLexers = [
new TagLexer_1.TagSubLexer(concordialang_types_1.ReservedTags.IGNORE, dictionary.tagIgnore),
new TagLexer_1.TagSubLexer(concordialang_types_1.ReservedTags.GENERATED, dictionary.tagGenerated),
new TagLexer_1.TagSubLexer(concordialang_types_1.ReservedTags.FAIL, dictionary.tagFail),
new TagLexer_1.TagSubLexer(concordialang_types_1.ReservedTags.SCENARIO, dictionary.tagScenario),
new TagLexer_1.TagSubLexer(concordialang_types_1.ReservedTags.VARIANT, dictionary.tagVariant),
new TagLexer_1.TagSubLexer(concordialang_types_1.ReservedTags.FEATURE, dictionary.tagFeature),
new TagLexer_1.TagSubLexer(concordialang_types_1.ReservedTags.GENERATE_ONLY_VALID_VALUES, dictionary.tagGenerateOnlyValidValues),
new TagLexer_1.TagSubLexer(concordialang_types_1.ReservedTags.IMPORTANCE, dictionary.tagImportance),
new TagLexer_1.TagSubLexer(concordialang_types_1.ReservedTags.GLOBAL, dictionary.tagGlobal)
];
this._lexers = [
new LongStringLexer_1.LongStringLexer(),
new LanguageLexer_1.LanguageLexer(dictionary.language),
new TagLexer_1.TagLexer(),
new TagLexer_1.TagLexer(this._subLexers),
new ImportLexer_1.ImportLexer(dictionary.import),
new FeatureLexer_1.FeatureLexer(dictionary.feature),
new BackgroundLexer_1.BackgroundLexer(dictionary.background),
Expand Down Expand Up @@ -160,7 +173,6 @@ class Lexer {
return false;
}
let result;
let node;
// Analyze with lexers of the suggested node types
if (this._lastLexer !== null) {
const suggestedNodeTypes = this._lastLexer.suggestedNextNodeTypes();
Expand Down Expand Up @@ -251,13 +263,12 @@ class Lexer {
|| {};
for (let lexer of this._lexers) {
if (this.isAWordBasedLexer(lexer)) {
let nodeType = lexer.affectedKeyword();
let words = dict[nodeType];
if (words) {
lexer.updateWords(words);
}
this.updateKeywordBasedLexer(lexer, dict);
}
}
for (let subLexer of this._subLexers) {
this.updateKeywordBasedLexer(subLexer, dict);
}
return dict;
}
/**
Expand All @@ -273,5 +284,12 @@ class Lexer {
isAWordBasedLexer(obj) {
return obj.updateWords !== undefined;
}
updateKeywordBasedLexer(kbl, dict) {
const nodeType = kbl.affectedKeyword();
const words = dict[nodeType];
if (words) {
kbl.updateWords(words);
}
}
}
exports.Lexer = Lexer;
47 changes: 46 additions & 1 deletion dist/modules/lexer/TagLexer.js
Expand Up @@ -11,13 +11,23 @@ const XRegExp = require('xregexp');
* @author Thiago Delgado Pinto
*/
class TagLexer {
constructor(_subLexers = []) {
this._subLexers = _subLexers;
}
/** @inheritDoc */
nodeType() {
return NodeTypes_1.NodeTypes.TAG;
}
/** @inheritDoc */
suggestedNextNodeTypes() {
return [NodeTypes_1.NodeTypes.TAG, NodeTypes_1.NodeTypes.VARIANT, NodeTypes_1.NodeTypes.FEATURE, NodeTypes_1.NodeTypes.SCENARIO];
return [
NodeTypes_1.NodeTypes.TAG,
NodeTypes_1.NodeTypes.VARIANT,
NodeTypes_1.NodeTypes.FEATURE,
NodeTypes_1.NodeTypes.SCENARIO,
NodeTypes_1.NodeTypes.UI_ELEMENT,
NodeTypes_1.NodeTypes.UI_PROPERTY
];
}
/** @inheritDoc */
analyze(line, lineNumber) {
Expand Down Expand Up @@ -67,9 +77,44 @@ class TagLexer {
name: result[1],
content: content
};
// Try to decide what subtype the tag has.
// An undefined subtype is valid and it means that the tag is not a reserved tag.
for (let subLexer of this._subLexers) {
if (subLexer.containsName(node.name)) {
node.subType = subLexer.affectedKeyword();
}
}
nodes.push(node);
}
return { nodes: nodes, errors: errors };
}
}
exports.TagLexer = TagLexer;
/**
* Allows to compare a tag name against a set of words in order to detect its subtype.
*
* @author Thiago Delgado Pinto
*/
class TagSubLexer {
constructor(_affectedKeyword, _words) {
this._affectedKeyword = _affectedKeyword;
this._words = _words;
}
/** @inheritDoc */
affectedKeyword() {
return this._affectedKeyword;
}
/** @inheritDoc */
updateWords(words) {
this._words = words.map(w => w.toLowerCase());
}
/**
* Compares if the tag's name is in the set of words.
*
* @param name Name to compare
*/
containsName(name) {
return this._words.indexOf(name.toLowerCase()) >= 0;
}
}
exports.TagSubLexer = TagSubLexer;
4 changes: 2 additions & 2 deletions dist/modules/parser/AfterAllParser.js
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const SyntaticException_1 = require("../req/SyntaticException");
const SyntacticException_1 = require("../req/SyntacticException");
const TypeChecking_1 = require("../util/TypeChecking");
/**
* AfterAll parser
Expand All @@ -12,7 +12,7 @@ class AfterAllParser {
analyze(node, context, it, errors) {
// Check whether a similar node was already declared
if (TypeChecking_1.isDefined(context.doc.afterAll)) {
let e = new SyntaticException_1.SyntaticException('Event already declared: After All', node.location);
let e = new SyntacticException_1.SyntacticException('Event already declared: After All', node.location);
errors.push(e);
return false;
}
Expand Down
6 changes: 3 additions & 3 deletions dist/modules/parser/AfterEachScenarioParser.js
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const SyntaticException_1 = require("../req/SyntaticException");
const SyntacticException_1 = require("../req/SyntacticException");
const TypeChecking_1 = require("../util/TypeChecking");
/**
* AfterEachScenario parser
Expand All @@ -12,13 +12,13 @@ class AfterEachScenarioParser {
analyze(node, context, it, errors) {
// Check whether a Feature was declared
if (!context.doc.feature) {
let e = new SyntaticException_1.SyntaticException('The event After Each Scenario must be declared after a Feature', node.location);
let e = new SyntacticException_1.SyntacticException('The event After Each Scenario must be declared after a Feature', node.location);
errors.push(e);
return false;
}
// Check whether a similar node was already declared
if (TypeChecking_1.isDefined(context.doc.afterEachScenario)) {
let e = new SyntaticException_1.SyntaticException('Event already declared: After Each Scenario', node.location);
let e = new SyntacticException_1.SyntacticException('Event already declared: After Each Scenario', node.location);
errors.push(e);
return false;
}
Expand Down
6 changes: 3 additions & 3 deletions dist/modules/parser/AfterFeatureParser.js
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const SyntaticException_1 = require("../req/SyntaticException");
const SyntacticException_1 = require("../req/SyntacticException");
const TypeChecking_1 = require("../util/TypeChecking");
/**
* AfterFeature parser
Expand All @@ -12,13 +12,13 @@ class AfterFeatureParser {
analyze(node, context, it, errors) {
// Check whether a Feature was declared
if (!context.doc.feature) {
let e = new SyntaticException_1.SyntaticException('The event After Feature must be declared after a Feature', node.location);
let e = new SyntacticException_1.SyntacticException('The event After Feature must be declared after a Feature', node.location);
errors.push(e);
return false;
}
// Check whether a similar node was already declared
if (TypeChecking_1.isDefined(context.doc.afterFeature)) {
let e = new SyntaticException_1.SyntaticException('Event already declared: After Feature', node.location);
let e = new SyntacticException_1.SyntacticException('Event already declared: After Feature', node.location);
errors.push(e);
return false;
}
Expand Down
8 changes: 4 additions & 4 deletions dist/modules/parser/BackgroundParser.js
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const SyntaticException_1 = require("../req/SyntaticException");
const SyntacticException_1 = require("../req/SyntacticException");
const TypeChecking_1 = require("../util/TypeChecking");
/**
* Background parser
Expand All @@ -12,18 +12,18 @@ class BackgroundParser {
analyze(node, context, it, errors) {
// Checks if a feature has been declared before it
if (!context.doc.feature) {
let e = new SyntaticException_1.SyntaticException('A background must be declared after a feature.', node.location);
let e = new SyntacticException_1.SyntacticException('A background must be declared after a feature.', node.location);
errors.push(e);
return false;
}
let feature = context.doc.feature;
if (feature.background) {
let e = new SyntaticException_1.SyntaticException('A feature cannot have more than one background.', node.location);
let e = new SyntacticException_1.SyntacticException('A feature cannot have more than one background.', node.location);
errors.push(e);
return false;
}
if (TypeChecking_1.isDefined(feature.scenarios) && feature.scenarios.length > 0) {
let e = new SyntaticException_1.SyntaticException('A background must be declared before a scenario.', node.location);
let e = new SyntacticException_1.SyntacticException('A background must be declared before a scenario.', node.location);
errors.push(e);
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions dist/modules/parser/BeforeAllParser.js
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const SyntaticException_1 = require("../req/SyntaticException");
const SyntacticException_1 = require("../req/SyntacticException");
const TypeChecking_1 = require("../util/TypeChecking");
/**
* BeforeAll parser
Expand All @@ -12,7 +12,7 @@ class BeforeAllParser {
analyze(node, context, it, errors) {
// Check whether a similar node was already declared
if (TypeChecking_1.isDefined(context.doc.beforeAll)) {
let e = new SyntaticException_1.SyntaticException('Event already declared: Before All', node.location);
let e = new SyntacticException_1.SyntacticException('Event already declared: Before All', node.location);
errors.push(e);
return false;
}
Expand Down

0 comments on commit 616355b

Please sign in to comment.