I want to convert a string of playing cards given as in A into a form B such that I can produce a result C.
A: "(AK,AQ,(A2:*h*h)):*s*s!AsQs,(JJ:*s*h)"
B: (((AsKs union AsQs union (A2 intersect hand with two hearts)) intersect hand with two spades) less AsQs) union (JJ intersect hand with one spade one heart)
C: AsKs,JsJh
The precedence of operations is
- 1) parentheses, ‘()’
- 2) intersect and less (left associative), ‘:!’
- 3) union, ‘,’
I have the functionality to perform the operations in B, for example Intersect(“AA”, “*s*s”) == “AsAs”, but I need a way to get from the form A into the form B of inputs and associated operations that I can perform in sequence to arrive at C.
I have looked at Irony and some other lexer/parser solutions, but they seem a bit overkill for this problem. Is there a simpler solution?
- Perhaps recursively splitting the string into a tree with nodes representing operations?
- Breaking down the string in reverse order of precedence and pushing it onto a stack? If so how (roughly) could this be implemented?
A working example of what I am trying to emulate is given here. You can find a more detailed description of the syntax for A here.
You could parse
Ato construct a tree of instructions and then execute them starting from the leaves (as I assume you’d want to execute the contents of the innermost brackets first). For this parsing task, you could use Regexes, or whatever else you want. Off the top of my head, I think you can first look for the parenthesis and operator symbols to figure out the tree structure, then fill in each leaf with the actual elementary instruction and each junction with the operator.The data structure storing
Acan be composed of objects (call themExpression) implementing an interfaceIOperand; eachExpressionshould have three fields:Operatorwhich is an instance ofenum Operations {Union, Intersection, SetDiff}, or just a string, depending on which you prefer.Operand1andOperand2, which can be either something like “Ah” (defining a set of cards), or anotherExpression. Therefore, they should be declared as instances ofIOperand.Your class for holding a single set of cards, like “Ah”, should also implement this
IOperand. The interfaceIOperanditself need not actually do anything.For matching each given instruction to an operation, you could use a simple switch, or alternatively a
Dictionaryofstring(orenum) todelegate IOperand SetOp(IOperand, IOperand);, and then populate yourDictionarywith (anonymous) functions (that would contain the instructions forB).In the
Dictionarycase, you would simply be able to doinstructionList[thisExpression.Operation](thisExpression);(this can probably be done a bit more elegantly to avoid referencingthisExpressiontwice) and the appropriate C# translation of the string input would be executed.Proof of Concept
I have made a basic implementation and a console app to demonstrate usage here: https://bitbucket.org/Superbest/lexer
If you succeed at progressing through the whole exercise with your sanity intact (the computer will use you as a surrogate parsing and operation library, so good luck with that) then the final step should ask you to evaluate an expression equal to
Cand then print what you just entered back at you. (When running for the first time, you might want to comply with the program’s directives. I suspect it is very easy to break the code and get lots of exceptions, and confuse yourself very much if you try to think about what happened to your invalid input.)If you are running the code as is, try the following sequence of answers (, = Enter):
Your output will be:
You should be able to simply update the bodies of methods marked obsolete and have a working program.
If you wish to add more binary operations, see the TODO in the body of
Simplification.Simplification(). The appropriate syntax will be obvious from looking at the three operations I already made; as it is, the code supports only binary operations. If your methods consume strings, you can use theSimplifiedOperand.Symbolfield.