public INode Build(INodeFactory nodeFactory, PseudoList defblocks) { if (defblocks == null) { throw new ArgumentNullException(nameof(defblocks)); } _nodeFactory = nodeFactory ?? throw new ArgumentNullException(nameof(nodeFactory)); _defblocks = defblocks.ToDictionary( x => x.GetSingleKeywordArgument <Symbol>(":name").Name, x => x.AsPseudoList()); var topBlocks = _defblocks .Values .Where(x => x.GetSingleArgumentAsBool(":is-top") == true) .ToList(); if (topBlocks.Count == 0) { throw new BuildingException("No top defblocks defined."); } if (topBlocks.Count > 1) { throw new BuildingException("More than one top defblock defined."); } var topBlock = topBlocks[0]; var topBlockContent = topBlock.GetFreeArguments(); var result = this.BuildContent(topBlockContent); return(result.Head.GetNode()); }
private CliExecutorArgumentDescriptor ExtractArgumentDescriptor(PseudoList subForm) { var alias = subForm.GetSingleKeywordArgument <Symbol>(":alias").Name.ToLowerInvariant(); var description = subForm.GetSingleKeywordArgument <StringAtom>(":description", true)?.Value; var docSubstitution = subForm.GetSingleKeywordArgument <StringAtom>(":doc-subst", true)?.Value; IList <string> values = null; if (subForm.GetCarSymbolName().ToLowerInvariant() == "multi-text") { values = subForm .GetAllKeywordArguments(":values") .Cast <StringAtom>() .Select(x => x.Value) .ToList(); } var isMandatory = subForm.GetSingleArgumentAsBool(":is-mandatory") ?? false; var allowsMultiple = subForm.GetSingleArgumentAsBool(":allows-multiple") ?? false; var argumentDescriptor = new CliExecutorArgumentDescriptor( alias, values, isMandatory, allowsMultiple, description, docSubstitution); return(argumentDescriptor); }
public void AddElement_ArgumentIsValid_AddsElement() { // Arrange var elements = new Element[] { Nil.Instance, True.Instance, Symbol.Create("aha"), Symbol.Create(":key"), }; var pseudoList = new PseudoList(elements); // Act var anotherSymbol = Symbol.Create("another"); pseudoList.AddElement(anotherSymbol); // Assert var expectedList = new List <Element>(elements) { anotherSymbol }; CollectionAssert.AreEqual(expectedList, pseudoList); }
public PseudoList Read(IList <IToken> tokens) { var list = new PseudoList(); var index = 0; this.ReadPseudoListContent(list, tokens, ref index, 0); return(list); }
public CliExecutorDescriptorBuilder(string executorGrammar) { ILexer lexer = new TinyLispLexer(); var tokens = lexer.Lexize(executorGrammar); ITinyLispPseudoReader reader = new TinyLispPseudoReader(); _form = reader.Read(tokens); }
public void GetAllKeywordArguments_PseudoListIsNull_ThrowsArgumentNullException() { // Arrange PseudoList pseudoList = null; // Act var ex = Assert.Throws <ArgumentNullException>(() => pseudoList.GetAllKeywordArguments(":key")); // Assert Assert.That(ex.ParamName, Is.EqualTo("shouldBePseudoList")); }
public void GetMultipleFreeArgumentSets_ArgumentIsNull_ThrowsArgumentNullException() { // Arrange PseudoList pseudoList = null; // Act var ex = Assert.Throws <ArgumentNullException>(() => pseudoList.GetMultipleFreeArgumentSets()); // Assert Assert.That(ex.ParamName, Is.EqualTo("shouldBePseudoList")); }
public void GetSingleKeywordArgument_ArgumentNameIsNull_ThrowsArgumentNullException() { // Arrange Element element = new PseudoList(); // Act var ex = Assert.Throws <ArgumentNullException>(() => element.GetSingleKeywordArgument(null)); // Assert Assert.That(ex.ParamName, Is.EqualTo("argumentName")); }
public void GetCarSymbolName_PseudoListIsEmpty_ThrowsArgumentException() { // Arrange var element = new PseudoList(); // Act var ex = Assert.Throws <ArgumentException>(() => element.GetCarSymbolName()); // Assert Assert.That(ex.Message, Does.StartWith("PseudoList is empty.")); Assert.That(ex.ParamName, Is.EqualTo("shouldBePseudoList")); }
public void GetCarSymbolName_CarIsNotSymbol_ThrowsArgumentException() { // Arrange var element = new PseudoList(new[] { new StringAtom("some string"), }); // Act var ex = Assert.Throws <ArgumentException>(() => element.GetCarSymbolName()); // Assert Assert.That(ex.Message, Does.StartWith("CAR of PseudoList is not a symbol.")); Assert.That(ex.ParamName, Is.EqualTo("shouldBePseudoList")); }
public void GetSingleKeywordArgument_ArgumentIsNotKeyword_ThrowsArgumentException(string badKeywordName) { // Arrange Element element = new PseudoList(); // Act var ex = Assert.Throws <ArgumentException>(() => element.GetSingleKeywordArgument(badKeywordName)); // Assert Assert.That(ex.Message, Does.StartWith($"'{badKeywordName}' is not a valid keyword.")); Assert.That(ex.ParamName, Is.EqualTo("argumentName")); }
private IEnumerable <ITextClass> ParseTextClasses(PseudoList arguments) { var textClasses = new List <ITextClass>(); foreach (var argument in arguments) { var symbolElement = (Symbol)argument; var textClass = this.ResolveTextClass(symbolElement.Name); textClasses.Add(textClass); } return(textClasses); }
private CliExecutorOptionDescriptor ExtractOptionDescriptor(PseudoList subForm) { var alias = subForm.GetSingleKeywordArgument <Symbol>(":alias").Name.ToLowerInvariant(); var options = subForm .GetAllKeywordArguments(":values") .Cast <StringAtom>() .Select(x => x.Value) .ToList(); var description = subForm.GetSingleKeywordArgument <StringAtom>(":description", true)?.Value; var optionDescriptor = new CliExecutorOptionDescriptor(alias, options, description); return(optionDescriptor); }
public void GetMultipleFreeArgumentSets_HappyPath_ReturnsExpectedResult(string form, string expectedRepresentation) { // Arrange var tokens = _lexer.Lexize(form); var reader = new TinyLispPseudoReader(); var pseudoList = reader.Read(tokens).Single().AsPseudoList(); // Act var list = pseudoList.GetMultipleFreeArgumentSets(); var listToPseudoList = new PseudoList(list); // Assert Assert.That(listToPseudoList.ToString(), Is.EqualTo(expectedRepresentation).IgnoreCase); }
public NextValueManager() { this.numbers = new PseudoList <int>(kNumberRandomness); this.numbers.Add(1); this.numbers.Add(2); this.numbers.Add(3); this.numbers.GenerateList(); this.numbers.Shuffle(); this.special = new PseudoList <int>(1); this.special.Add(1); for (int j = 0; j < kSpecialRareness; j++) { this.special.Add(0); } this.special.GenerateList(); this.special.Shuffle(); }
public void Constructor_ElementsArray_CreatesExpectedInstance() { // Arrange var elements = new Element[] { Nil.Instance, True.Instance, Symbol.Create("aha"), Symbol.Create(":key"), }; // Act var pseudoList = new PseudoList(elements); // Assert CollectionAssert.AreEqual(pseudoList, elements); }
public void AddElement_ArgumentIsNull_ThrowsArgumentNullException() { // Arrange var elements = new Element[] { Nil.Instance, True.Instance, Symbol.Create("aha"), Symbol.Create(":key"), }; var pseudoList = new PseudoList(elements); // Act var ex = Assert.Throws <ArgumentNullException>(() => pseudoList.AddElement(null)); // Assert Assert.That(ex.ParamName, Is.EqualTo("element")); }
private CliExecutorKeyDescriptor ExtractKeyDescriptor(PseudoList list, int index) { var keySubForm = list[index]; var alias = keySubForm.GetSingleKeywordArgument <Symbol>(":alias").Name.ToLowerInvariant(); var keys = keySubForm .GetAllKeywordArguments(":values") .Cast <StringAtom>() .Select(x => x.Value) .ToList(); var isMandatory = keySubForm.GetSingleArgumentAsBool(":is-mandatory") ?? false; var allowsMultiple = keySubForm.GetSingleArgumentAsBool(":allows-multiple") ?? false; var valueSubForm = list[index + 1]; IList <string> values = null; if (valueSubForm.GetCarSymbolName().ToLowerInvariant() == "multi-text") { values = valueSubForm .GetAllKeywordArguments(":values") .Cast <StringAtom>() .Select(x => x.Value) .ToList(); } var valueDescription = valueSubForm.GetSingleKeywordArgument <StringAtom>(":description", true)?.Value; var valueDocSubstitution = valueSubForm.GetSingleKeywordArgument <StringAtom>(":doc-subst", true)?.Value; var keyDescriptor = new CliExecutorKeyDescriptor( alias, keys, isMandatory, allowsMultiple, values, valueDescription, valueDocSubstitution); return(keyDescriptor); }
protected CliExecutorBase( string grammar, string version, bool supportsHelp) : base(ExtractName(grammar), version, supportsHelp) { var tinyLispLexer = new TinyLispLexer(); var tinyLispPseudoReader = new TinyLispPseudoReader(); var lispTokens = tinyLispLexer.Lexize(grammar); _form = tinyLispPseudoReader.Read(lispTokens); this.Descriptor = (new CliExecutorDescriptorBuilder(grammar)).Build(); if (this.Name == null) { if (this.Version != null) { throw new ArgumentException("Nameless executor cannot support version.", nameof(version)); } if (this.SupportsHelp) { throw new ArgumentException("Nameless executor cannot support help.", nameof(supportsHelp)); } } try { var helper = new CliExecutorDescriptorBuilder(grammar); this.Descriptor = helper.Build(); } catch (CliException) { // couldn't build descriptor } }
private NodeBunch BuildContent(PseudoList content) { NodeBox head = null; NodeBox tail = null; foreach (var item in content) { var result = this.BuildItem(item); if (head == null) { // first entry head = result.Head; tail = result.Tail; } else { tail.RequestLink(result.Head); tail = result.Tail; } } if (tail == null) { throw new BuildingException("Content is empty."); } if (tail.Links.Any()) { throw new BuildingException("Last item in a content must not have explicit links."); } var buildResult = new NodeBunch(head, tail); return(buildResult); }
private void CollectItems( PseudoList list, List <CliExecutorKeyDescriptor> keyList, List <CliExecutorArgumentDescriptor> argList, List <CliExecutorOptionDescriptor> optionList) { for (var i = 0; i < list.Count; i++) { var subForm = list[i].AsPseudoList(); var subFormCar = subForm.GetCarSymbolName().ToLowerInvariant(); string action; switch (subFormCar) { case "multi-text": action = subForm.GetSingleKeywordArgument <Symbol>(":action").Name.ToLowerInvariant(); switch (action) { case "key": var keyDescriptor = this.ExtractKeyDescriptor(list, i); keyList.Add(keyDescriptor); i++; break; case "option": var optionDescriptor = this.ExtractOptionDescriptor(subForm); optionList.Add(optionDescriptor); break; case "argument": var argumentDescriptor = this.ExtractArgumentDescriptor(subForm); argList.Add(argumentDescriptor); break; default: throw new CliException($"Unknown action: '{action}'."); } break; case "some-text": action = subForm.GetSingleKeywordArgument <Symbol>(":action").Name.ToLowerInvariant(); if (action == "argument") { var argumentDescriptor = this.ExtractArgumentDescriptor(subForm); argList.Add(argumentDescriptor); } else { throw new CliException($"Action '{action}' cannot be applied to node type '{subFormCar}'."); } break; case "executor": case "idle": case "fallback": case "end": continue; case "alt": case "seq": case "opt": CollectItems( subForm.GetFreeArguments(), keyList, argList, optionList); break; default: throw new CliException($"Unsupported node type: '{subFormCar}'."); } } }
public override INode CreateNode(PseudoList item) { var car = item.GetCarSymbolName().ToLowerInvariant(); if (car == "executor") { INode executorNode; var executorName = item.GetSingleKeywordArgument <Symbol>(":executor-name", true)?.Name; if (executorName == null) { executorNode = new IdleNode(this.NodeFamily, $"Root node for unnamed executor of type {this.GetType().FullName}"); } else { executorNode = new MultiTextNode( new string[] { item.GetSingleKeywordArgument <StringAtom>(":verb").Value }, new ITextClass[] { TermTextClass.Instance, }, true, ExecutorAction, this.NodeFamily, $"Executor Node. Name: [{executorName}]"); executorNode.Properties["executor-name"] = executorName; } return(executorNode); } var node = base.CreateNode(item); if (node == null) { throw new CliException($"Could not build node for item '{car}'."); } if (node is FallbackNode) { return(node); } if (!(node is ActionNode)) { throw new CliException($"'{typeof(ActionNode).Name}' instance was expected to be created."); } var baseResult = (ActionNode)node; var action = item.GetSingleKeywordArgument <Symbol>(":action", true)?.Name?.ToLowerInvariant(); string alias; switch (action) { case "key": baseResult.Action = KeyAction; alias = item.GetSingleKeywordArgument <Symbol>(":alias").Name; baseResult.Properties["alias"] = alias; break; case "value": baseResult.Action = ValueAction; break; case "option": baseResult.Action = OptionAction; alias = item.GetSingleKeywordArgument <Symbol>(":alias").Name; baseResult.Properties["alias"] = alias; break; case "argument": baseResult.Action = ArgumentAction; alias = item.GetSingleKeywordArgument <Symbol>(":alias").Name; baseResult.Properties["alias"] = alias; break; default: throw new CliException($"Keyword ':action' is missing or invalid for item '{car}'."); } return(baseResult); }
public override INode CreateNode(PseudoList item) { var car = item.GetCarSymbolName().ToLowerInvariant(); if (car == "worker") { INode workerNode; var workerName = item.GetSingleKeywordArgument <Symbol>(":worker-name", true)?.Name; if (workerName == null) { throw new CliException("Worker name is null."); } else { workerNode = new MultiTextNode( item .GetAllKeywordArguments(":verbs") .Cast <StringAtom>() .Select(x => x.Value) .ToList(), new ITextClass[] { TermTextClass.Instance, }, true, WorkerAction, this.NodeFamily, $"Worker Node. Name: [{workerName}]"); workerNode.Properties["worker-name"] = workerName; } return(workerNode); } var node = base.CreateNode(item); if (node is FallbackNode) { return(node); } if (!(node is ActionNode)) { throw new CliException("ActionNode was expected to be created."); } var actionNode = (ActionNode)base.CreateNode(item); if (actionNode == null) { throw new CliException($"Could not build node for item '{car}'."); } var action = item.GetSingleKeywordArgument <Symbol>(":action", true)?.Name?.ToLowerInvariant(); string alias; switch (action) { case "key": actionNode.Action = KeyAction; alias = item.GetSingleKeywordArgument <Symbol>(":alias").Name; actionNode.Properties["alias"] = alias; break; case "value": actionNode.Action = ValueAction; break; case "option": actionNode.Action = OptionAction; break; case "argument": actionNode.Action = ArgumentAction; alias = item.GetSingleKeywordArgument <Symbol>(":alias").Name; actionNode.Properties["alias"] = alias; break; default: throw new CliException($"Keyword ':action' is missing for item '{car}'."); } return(actionNode); }
public static IList <PseudoList> GetMultipleFreeArgumentSets(this Element shouldBePseudoList) { if (shouldBePseudoList == null) { throw new ArgumentNullException(nameof(shouldBePseudoList)); } var pseudoList = shouldBePseudoList as PseudoList; if (pseudoList == null) { throw new ArgumentException( $"Argument is not of type '{typeof(PseudoList).FullName}'.", nameof(shouldBePseudoList)); } if (pseudoList.Count == 0) { throw new ArgumentException( $"PseudoList is empty.", nameof(shouldBePseudoList)); } var index = 1; var result = new List <PseudoList>(); var startedWithKeyword = false; var startIndex = 1; while (true) { if (index == pseudoList.Count) { if (startIndex == -1) { // nothing to add. } else { // got free args. var firstArgIndex = startIndex; if (startedWithKeyword) { firstArgIndex++; } var lastArgIndex = index - 1; if (firstArgIndex <= lastArgIndex) { var freeArgsPseudoList = new PseudoList(); for (var i = firstArgIndex; i <= lastArgIndex; i++) { freeArgsPseudoList.AddElement(pseudoList[i]); } result.Add(freeArgsPseudoList); } } break; } var element = pseudoList[index]; if (element is Keyword) { // bumped into keyword if (startIndex == -1) { // reset again. startedWithKeyword = true; index++; } else { // was started, let's check, maybe we can deliver pseudo-list of free args. var delta = index - startIndex; if ( delta == 0 || (delta == 1 && startedWithKeyword) ) { // won't consider if free. // reset the entire procedure. startedWithKeyword = true; startIndex = -1; index++; } else { // got free args! var firstArgIndex = startIndex; if (startedWithKeyword) { firstArgIndex++; } var lastArgIndex = index - 1; var freeArgsPseudoList = new PseudoList(); for (var i = firstArgIndex; i <= lastArgIndex; i++) { freeArgsPseudoList.AddElement(pseudoList[i]); } result.Add(freeArgsPseudoList); startIndex = -1; index++; startedWithKeyword = true; } } } else { // bumped into non-keyword. if (startIndex == -1) { // let's start. startIndex = index; index++; } else { // was started, bumped into non-keyword again. good, let's continue. index++; } } } return(result); }
public static PseudoList GetAllKeywordArguments( this Element shouldBePseudoList, string argumentName, bool absenceIsAllowed = false) { if (shouldBePseudoList == null) { throw new ArgumentNullException(nameof(shouldBePseudoList)); } var list = shouldBePseudoList as PseudoList; if (list == null) { throw new ArgumentException( $"Argument is not of type '{typeof(PseudoList).FullName}'.", nameof(shouldBePseudoList)); } if (argumentName == null) { throw new ArgumentNullException(nameof(argumentName)); } if (!TinyLispHelper.IsValidSymbolName(argumentName, true)) { throw new ArgumentException($"'{argumentName}' is not a valid keyword.", nameof(argumentName)); } var wantedKeyword = Symbol.Create(argumentName); var index = list.FindFirstIndex(wantedKeyword); if (index == -1) { if (absenceIsAllowed) { return(new PseudoList()); // empty } else { throw new TinyLispException($"No argument for keyword '{argumentName}'."); } } index++; // move forward from keyword. var startIndex = index; while (true) { if (index == list.Count) { break; } var element = list[index]; if (element is Keyword) { break; } index++; } var lastIndex = index - 1; var result = new PseudoList(); for (var i = startIndex; i <= lastIndex; i++) { result.AddElement(list[i]); } return(result); }
private void ReadPseudoListContent(PseudoList list, IList <IToken> tokens, ref int index, int depth) { while (true) { if (index == tokens.Count) { if (depth > 0) { throw new TinyLispException("Unclosed form."); } else { return; } } var token = tokens[index]; if (token is LispPunctuationToken punctuationToken) { switch (punctuationToken.Value) { case Punctuation.RightParenthesis: if (depth == 0) { throw new TinyLispException("Unexpected ')'."); } else { index++; return; } case Punctuation.LeftParenthesis: index++; var innerList = new PseudoList(); this.ReadPseudoListContent(innerList, tokens, ref index, depth + 1); list.AddElement(innerList); break; default: throw new ArgumentOutOfRangeException(); } } else if (token is KeywordToken keywordToken) { var element = Symbol.Create(keywordToken.Keyword); list.AddElement(element); index++; } else if (token is LispSymbolToken symbolToken) { var element = Symbol.Create(symbolToken.SymbolName); list.AddElement(element); index++; } else if (token is TextToken textToken && textToken.Class is StringTextClass) { var element = new StringAtom(textToken.Text); list.AddElement(element); index++; }
public virtual INode CreateNode(PseudoList item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } try { var car = item.GetCarSymbolName(); INode node; switch (car) { case "EXACT-TEXT": node = new ExactTextNode( item.GetSingleKeywordArgument <StringAtom>(":value").Value, this.ParseTextClasses(item.GetAllKeywordArguments(":classes")), _isCaseSensitive, null, this.NodeFamily, item.GetItemName()); break; case "SOME-TEXT": node = new TextNode( this.ParseTextClasses(item.GetAllKeywordArguments(":classes")), null, this.NodeFamily, item.GetItemName()); break; case "MULTI-TEXT": node = new MultiTextNode( item.GetAllKeywordArguments(":values").Cast <StringAtom>().Select(x => x.Value), this.ParseTextClasses(item.GetAllKeywordArguments(":classes")), _isCaseSensitive, null, this.NodeFamily, item.GetItemName()); break; case "EXACT-PUNCTUATION": node = new ExactPunctuationNode( item.GetSingleKeywordArgument <StringAtom>(":value").Value.Single(), null, this.NodeFamily, item.GetItemName()); break; // todo: some-number? case "SOME-INTEGER": node = new IntegerNode( null, this.NodeFamily, item.GetItemName()); break; case "FALLBACK": var name = item.GetItemName(); if (name == null) { throw new BuildingException("Fallback node must have a name."); } node = new FallbackNode( this.CreateFallbackPredicate(name), this.NodeFamily, name); break; default: return(null); } return(node); } catch (Exception ex) { throw new BuildingException($"Could not build a node from item {item}.", ex); } }