Skip to content

Commit e5ff980

Browse files
committed
Block scanner extension
1 parent ca6c3a8 commit e5ff980

File tree

6 files changed

+291
-249
lines changed

6 files changed

+291
-249
lines changed

FexOverview.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Flow Expressions Overview
2+
3+
A flow expression can implement just about any type of *flow logic*. Mainly for, but not limited to, **Parser** and **DSL** (domain specific language) construction.
4+
5+
Flow expressions are constructed from the various FexElements (*building blocks*) via a fluent API. These FexElements define the logical flow and operation of the expression in a very readable and maintainable format.
6+
7+
> A running Flow Expression operates on a user supplied **Context**, which is any environment that manages and provides input/content/state.
8+
>
9+
> For a Parser, the context would be a **Scanner** that manages text scanning and provides <i>Tokens</i> to operate on.<br/>
10+
>
11+
> A comprehensive [FexScanner](Docs/FexScannerExt.md) is provided (derived from *ScriptScanner* in the [Scanners](https://github.com/PromicSW/scanners) repo/library) but you can roll your own if required.
12+
13+
## FexElements perform several types of functions:
14+
15+
> Please see the [Fex Element reference](Docs/FexElementsRef.md) section for full details.<br>
16+
> Also [FexScanner context extensions](Docs/FexScannerExt.md) for extensions specific to the FexScanner context.
17+
18+
- **Operators (Op):** Perform a operation on the context, returning a success status (true/false).
19+
- Certain Op's produce and record a value for later use via one of the Action elements.
20+
- An Op can operate on the context directly, but typically *Operator Extension methods* are defined to create re-usable (and more readable) operators specific to the context used.
21+
- There is also a facility to attach Pre-Operations to operators (e.g skip spaces when scanning etc.)
22+
- **Sequence(Seq):** A sequence is the primary construct used in flow expressions and defines a series of steps (1..n) to complete:
23+
- A step is any FexElement.
24+
- All steps is a sequence must complete else the sequence fails.
25+
- A step(s) may be optional and there are several rules governing this.
26+
- **Flow Control:** These elements control the flow of an expression:
27+
- Opt: Optional sequence.
28+
- OneOf: One of a set of sequences must pass.
29+
- NotOneOf: Inverse of OneOf.
30+
- Rep...: Repeat sequences.
31+
32+
- **Actions:** Perform actions based on the current state of the production to check values or perform any other actions required on the context:
33+
- Actions don't affect the validity of a sequence.
34+
- There are general actions and those that operate on a value recorded by and operator.
35+
- An Action can operate on the context directly, but typically *Action Extension methods* are defined to create re-usable (and more readable) actions specific to the context used.
36+
- **Error reporting:** Elements for error handling and reporting.
37+
- **Tracing:** Tracing elements primarily for debugging purposes.

FexSampleSet/Program.cs

Lines changed: 34 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
RunSamplesFex();
1010

11+
//ExpressionEval();
12+
//ExpressionEval("5");
13+
1114

1215
void RunSamples() {
1316

@@ -58,43 +61,24 @@ void RunSamplesFex() {
5861

5962
string val = "";
6063

64+
// FexNoContext is just an empty class since we don't do any scanning here.
6165
new FlowExpression<FexNoContext>()
6266
.Rep0N(r => r
63-
.Act(c => Console.WriteLine("Run Sample:"))
64-
.RepAct(samples.Count, (c, i) => Console.WriteLine($" {i + 1} - {samples[i].Name}"))
65-
.Act(c => Console.Write(" Blank to Exit\r\n> "))
66-
.Op(o => !string.IsNullOrEmpty(val = Console.ReadLine()))
67-
.Act(c => {
68-
Console.Clear();
69-
if (int.TryParse(val, out int m)) {
70-
if (m > 0 && m <= samples.Count) {
71-
Console.WriteLine($"Run: {samples[m - 1].Name}");
72-
samples[m - 1].Run();
73-
Console.WriteLine();
67+
.Act(c => Console.WriteLine("Run Sample:"))
68+
.RepAct(samples.Count, (c, i) => Console.WriteLine($" {i + 1} - {samples[i].Name}"))
69+
.Act(c => Console.Write(" Blank to Exit\r\n> "))
70+
.Op(o => !string.IsNullOrEmpty(val = Console.ReadLine()))
71+
.Act(c => {
72+
Console.Clear();
73+
if (int.TryParse(val, out int m)) {
74+
if (m > 0 && m <= samples.Count) {
75+
Console.WriteLine($"Run: {samples[m - 1].Name}");
76+
samples[m - 1].Run();
77+
Console.WriteLine();
78+
}
7479
}
75-
}
76-
})
77-
).Run(new FexNoContext());
78-
79-
//var fex = new FlowExpression<FexNoContext>();
80-
81-
//fex.Rep0N(r => r
82-
// .Act(c => Console.WriteLine("Run Sample:"))
83-
// .RepAct(samples.Count, (c, i) => Console.WriteLine($" {i + 1} - {samples[i].Name}"))
84-
// .Act(c => Console.Write(" Blank to Exit\r\n> "))
85-
// .Op(o => !string.IsNullOrEmpty(val = Console.ReadLine()))
86-
// .Act(c => {
87-
// Console.Clear();
88-
// if (int.TryParse(val, out int m)) {
89-
// if (m > 0 && m <= samples.Count) {
90-
// Console.WriteLine($"Run: {samples[m - 1].Name}");
91-
// samples[m - 1].Run();
92-
// Console.WriteLine();
93-
// }
94-
// }
95-
// })
96-
//).Run(new FexNoContext());
97-
80+
})
81+
).Run(new FexNoContext());
9882
}
9983

10084
// QuickStart sample
@@ -110,13 +94,13 @@ void QuickStart() {
11094
* - number: 4 digits
11195
*/
11296

113-
var fex = new FlowExpression<FexScanner>(); // Flow Expression using FexScanner
114-
string dcode = "", acode = "", number = ""; // Will record the values in here
97+
var fex = new FlowExpression<FexScanner>(); // Flow Expression using FexScanner.
98+
string dcode = "", acode = "", number = ""; // Will record values in here.
11599

116100
// Build the flow expression with 'Axiom' tnumber:
117101
var tnumber = fex.Seq(s => s
118102
.Ch('(').OnFail("( expected")
119-
.Rep(3, -1, r => r.Digit(v => dcode += v)).OnFail("at least 3 digit dialing code expected")
103+
.Rep(3, -1, r => r.Digit(v => dcode += v)).OnFail("3+ digit dialing code expected")
120104
.Ch(')').OnFail(") expected")
121105
.Sp()
122106
.Rep(3, r => r.Digit(v => acode += v)).OnFail("3 digit area code expected")
@@ -158,22 +142,20 @@ void DemoSimpleScanner() {
158142
}
159143

160144
// Expression Evaluation - using FexParser
161-
void ExpressionEval() {
145+
void ExpressionEval(string calc = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )") {
162146

163147
/*
164148
* Expression Grammar:
165-
* expression => factor ( ( '-' | '+' ) factor )* ;
166-
* factor => unary ( ( '/' | '*' ) unary )* ;
167-
* unary => ( '-' ) unary | primary ;
168-
* primary => NUMBER | "(" expression ")" ;
149+
* expression => factor ( ( '-' | '+' ) factor )* ;
150+
* factor => unary ( ( '/' | '*' ) unary )* ;
151+
* unary => ( '-' unary ) | primary ;
152+
* primary => NUMBER | "(" expression ")" ;
169153
*/
170154

171-
// Number Stack for calculations
155+
// Number Stack for calculations:
172156
Stack<double> numStack = new Stack<double>();
173157

174-
var expr1 = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )";
175-
176-
Console.WriteLine($"Evaluating expression: {expr1}");
158+
Console.WriteLine($"Calculate: {calc}");
177159

178160
var fex = new FlowExpression<FexScanner>();
179161

@@ -197,20 +179,20 @@ void ExpressionEval() {
197179
.OneOf(o => o
198180
.Seq(s => s.Ch('-').Ref("unary").Act(a => numStack.Push(-numStack.Pop())))
199181
.Ref("primary")
200-
).OnFail("Primary expected"));
182+
));
201183

202184
var primary = fex.Seq(s => s.RefName("primary")
203185
.OneOf(o => o
204186
.Seq(e => e.Ch('(').Fex(expr).Ch(')').OnFail(") expected"))
205187
.Seq(s => s.NumDecimal(n => numStack.Push(n)))
206-
));
188+
).OnFail("Primary expected"));
207189

208190
var exprEval = fex.Seq(s => s.GlobalPreOp(c => c.SkipSp()).Fex(expr).IsEos().OnFail("invalid expression"));
209191

210-
var scn = new FexScanner(expr1);
192+
var scn = new FexScanner(calc);
211193

212194
Console.WriteLine(exprEval.Run(scn)
213-
? $"Passed = {numStack.Pop():F4}"
195+
? $"Answer = {numStack.Pop():F4}"
214196
: scn.ErrorLog.AsConsoleError("Expression Error:"));
215197
}
216198

@@ -244,14 +226,14 @@ void ExpressionREPL() {
244226
.OneOf(o => o
245227
.Seq(s => s.Ch('-').Ref("unary").Act(a => numStack.Push(-numStack.Pop())))
246228
.Ref("primary")
247-
).OnFail("Primary expected"));
229+
));
248230

249231
var primary = fex.Seq(s => s.RefName("primary")
250232
.OneOf(o => o
251233
.Seq(e => e.Ch('(').Fex(expr).Ch(')').OnFail(") expected"))
252234
.Seq(s => s.NumDecimal(n => numStack.Push(n)))
253235
.Seq(s => s.Ch('a').Act(c => numStack.Push(ans))) // a is previous answer
254-
));
236+
).OnFail("Primary expected"));
255237

256238
var exprEval = fex.Seq(s => s.GlobalPreOp(c => c.SkipSp()).Fex(expr).IsEos().OnFail("invalid expression"));
257239

FlowExpressions.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{16EDBBE2-1
1515
ProjectSection(SolutionItems) = preProject
1616
Docs\CustomScanner.md = Docs\CustomScanner.md
1717
Docs\FexElementsRef.md = Docs\FexElementsRef.md
18+
FexOverview.md = FexOverview.md
1819
Docs\FexScannerExt.md = Docs\FexScannerExt.md
1920
EndProjectSection
2021
EndProject

0 commit comments

Comments
 (0)