77using Psw . FlowExpressions ;
88
99RunSamplesFex ( ) ;
10+ //TraceExpressionEval();
1011
1112//ExpressionEval();
1213//ExpressionEval("5");
@@ -196,6 +197,61 @@ void ExpressionEval(string calc = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )") {
196197 : scn . ErrorLog . AsConsoleError ( "Expression Error:" ) ) ;
197198}
198199
200+ // Expression Evaluation - using FexParser
201+ void TraceExpressionEval ( string calc = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )" ) {
202+
203+ /*
204+ * Expression Grammar:
205+ * expression => factor ( ( '-' | '+' ) factor )* ;
206+ * factor => unary ( ( '/' | '*' ) unary )* ;
207+ * unary => ( '-' unary ) | primary ;
208+ * primary => NUMBER | "(" expression ")" ;
209+ */
210+
211+ // Number Stack for calculations:
212+ Stack < double > numStack = new Stack < double > ( ) ;
213+
214+ Console . WriteLine ( $ "Calculate: { calc } ") ;
215+
216+ var fex = new FlowExpression < FexScanner > ( new ConsoleTracer ( ) ) ;
217+
218+ var expr = fex . Seq ( s => s . Trace ( c => "expr:" )
219+ . Ref ( "factor" )
220+ . RepOneOf ( 0 , - 1 , r => r . Trace ( c => "Expr +,-" )
221+ . Seq ( s => s . Ch ( '+' ) . TraceOp ( c => "Check for +" , 1 ) . Ref ( "factor" ) . Act ( c => numStack . Push ( numStack . Pop ( ) + numStack . Pop ( ) ) ) )
222+ . Seq ( s => s . Ch ( '-' ) . TraceOp ( c => "Check for -" , 1 ) . Ref ( "factor" ) . Act ( c => numStack . Push ( - numStack . Pop ( ) + numStack . Pop ( ) ) ) )
223+ ) ) ;
224+
225+ var factor = fex . Seq ( s => s . RefName ( "factor" ) . Trace ( c => "factor:" )
226+ . Ref ( "unary" )
227+ . RepOneOf ( 0 , - 1 , r => r . Trace ( c => "Factor *,/" )
228+ . Seq ( s => s . Ch ( '*' ) . TraceOp ( c => "Check for *" , 1 ) . Ref ( "unary" ) . Act ( c => numStack . Push ( numStack . Pop ( ) * numStack . Pop ( ) ) ) )
229+ . Seq ( s => s . Ch ( '/' ) . TraceOp ( c => "Check for /" , 1 ) . Ref ( "unary" )
230+ . Op ( c => numStack . Peek ( ) != 0 ) . OnFail ( "Division by 0" ) // Trap division by 0
231+ . Act ( c => numStack . Push ( 1 / numStack . Pop ( ) * numStack . Pop ( ) ) ) )
232+ ) ) ;
233+
234+ var unary = fex . Seq ( s => s . RefName ( "unary" ) . Trace ( c => "unary:" )
235+ . OneOf ( o => o
236+ . Seq ( s => s . Ch ( '-' ) . TraceOp ( c => " negate unary" ) . Ref ( "unary" ) . Act ( a => numStack . Push ( - numStack . Pop ( ) ) ) )
237+ . Ref ( "primary" )
238+ ) ) ;
239+
240+ var primary = fex . Seq ( s => s . RefName ( "primary" ) . Trace ( c => "primary:" )
241+ . OneOf ( o => o
242+ . Seq ( e => e . Ch ( '(' ) . TraceOp ( c => "Primary nesting (" , 1 ) . Fex ( expr ) . Ch ( ')' ) . OnFail ( ") expected" ) )
243+ . Seq ( s => s . NumDecimal ( n => numStack . Push ( n ) ) . TraceOp ( ( c , v ) => $ "Primary Number { ( double ) v } ", 1 ) )
244+ ) . OnFail ( "Primary expected" ) ) ;
245+
246+ var exprEval = fex . Seq ( s => s . GlobalPreOp ( c => c . SkipSp ( ) ) . Fex ( expr ) . IsEos ( ) . OnFail ( "invalid expression" ) ) ;
247+
248+ var scn = new FexScanner ( calc ) ;
249+
250+ Console . WriteLine ( exprEval . Run ( scn )
251+ ? $ "Answer = { numStack . Pop ( ) : F4} "
252+ : scn . ErrorLog . AsConsoleError ( "Expression Error:" ) ) ;
253+ }
254+
199255void RefExpressionEval ( string calc = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )" )
200256{
201257 /*
@@ -209,7 +265,7 @@ void RefExpressionEval(string calc = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )")
209265 // Number Stack for calculations:
210266 Stack < double > numStack = new Stack < double > ( ) ;
211267
212- // The FlowExpression object used to create FexElements with FexScanner as the context:
268+ // The FlowExpression factory used to create FexElements with FexScanner as the context:
213269 var fex = new FlowExpression < FexScanner > ( ) ;
214270
215271 // Define the main expression production, which returns a Sequence element:
@@ -242,7 +298,7 @@ void RefExpressionEval(string calc = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )")
242298 . Seq ( s => s . Ch ( '*' ) . Ref ( "unary" ) . Act ( c => numStack . Push ( numStack . Pop ( ) * numStack . Pop ( ) ) ) )
243299
244300 // If we have a '/' run unary, check for division by zero and then divide the top two values on the stack:
245- // Note again the stack is in reverse order.
301+ // o Note again the stack is in reverse order.
246302 . Seq ( s => s . Ch ( '/' ) . Ref ( "unary" )
247303 . Op ( c => numStack . Peek ( ) != 0 ) . OnFail ( "Division by 0" ) // Trap division by 0 and report as error.
248304 . Act ( c => numStack . Push ( 1 / numStack . Pop ( ) * numStack . Pop ( ) ) ) )
@@ -293,7 +349,7 @@ void RefExpressionEval(string calc = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )")
293349 var scn = new FexScanner ( calc ) ;
294350
295351 // Run the Axiom with the scanner which returns true/false:
296- // o If valid display the answer = top value on the stack.
352+ // o If valid display the answer ( = top value on the stack) .
297353 // o Else display the error logged in the scanner's shared ErrorLog.
298354 Console . WriteLine ( exprEval . Run ( scn )
299355 ? $ "Answer = { numStack . Pop ( ) : F4} "
@@ -373,3 +429,18 @@ public Sample(string name, Action action) {
373429 }
374430}
375431
432+ public class ConsoleTracer : IFexTracer
433+ {
434+ public void Trace ( string message , int level ) {
435+ Console . ForegroundColor = ConsoleColor . Cyan ;
436+ Console . WriteLine ( $ "{ new String ( ' ' , level * 2 ) } { message } ") ;
437+ Console . ForegroundColor = ConsoleColor . White ;
438+ }
439+
440+ public void Trace ( string message , bool pass , int level ) {
441+ Console . ForegroundColor = ConsoleColor . Cyan ;
442+ Console . WriteLine ( $ "{ new String ( ' ' , level * 2 ) } { message } [{ pass } ]") ;
443+ Console . ForegroundColor = ConsoleColor . White ;
444+ }
445+ }
446+
0 commit comments