Skip to content

Commit 1f69b08

Browse files
committed
Doc updates
1 parent fb04889 commit 1f69b08

File tree

12 files changed

+596
-175
lines changed

12 files changed

+596
-175
lines changed

Docs/FexElementsRef.md

Lines changed: 170 additions & 79 deletions
Large diffs are not rendered by default.

Docs/FexOverview.md

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@ Flow expressions are constructed from the various **FexElements** (*building blo
1212
1313
## FexElements perform several types of functions:
1414

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 as context.
15+
| Detailed References: |
16+
| :------------------- |
17+
| [Fex Element reference](Docs/FexElementsRef.md): describes each FexElement type with examples. |
18+
| [FexScanner context extensions](Docs/FexScannerExt.md): describes all the context extensions for the supplied FexScanner. |
19+
| [Custom Scanner](Docs/CustomScanner.md): a tutorial/example of a custom scanner and context extensions. |
1720

21+
### FexElement Types:
1822
- **Operators (Op):** Perform a operation on the context, returning a success status (true/false).
1923
- An operator is implemented via a `Func<context, bool>` delegate which can either operate on the context and/or the *closure* environment.
2024
- For example: if the context is a scanner then the Op would typically perform one of the scanning methods/functions.
@@ -24,7 +28,7 @@ Flow expressions are constructed from the various **FexElements** (*building blo
2428
- **Sequence(Seq):** A sequence is the primary construct used in flow expressions and defines a series of steps (1..n) to complete:
2529
- A step is any FexElement.
2630
- All steps is a sequence must complete else the sequence fails.
27-
- A step(s) may be optional and there are several rules governing this (see reference section).
31+
- Steps may be optional and there are several rules governing this (see reference section).
2832
- **Flow Control:** These elements control the flow of an expression:
2933
- Opt: Optional sequence.
3034
- OneOf: One of a set of sequences must pass.
@@ -48,18 +52,16 @@ using Psw.FlowExpressions;
4852

4953
void RefExpressionEval(string calc = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )")
5054
{
51-
/*
52-
* Expression Grammar:
53-
* expression => factor ( ( '-' | '+' ) factor )* ;
54-
* factor => unary ( ( '/' | '*' ) unary )* ;
55-
* unary => ( '-' unary ) | primary ;
56-
* primary => NUMBER | "(" expression ")" ;
57-
*/
55+
// Expression Grammar:
56+
// expression => factor ( ( '-' | '+' ) factor )* ;
57+
// factor => unary ( ( '/' | '*' ) unary )* ;
58+
// unary => ( '-' unary ) | primary ;
59+
// primary => NUMBER | "(" expression ")" ;
5860
5961
// Number Stack for calculations:
6062
Stack<double> numStack = new Stack<double>();
6163

62-
// The FlowExpression object used to create FexElements with FexScanner as the context:
64+
// The FlowExpression factory used to create FexElements with FexScanner as the context:
6365
var fex = new FlowExpression<FexScanner>();
6466

6567
// Define the main expression production, which returns a Sequence element:
@@ -92,7 +94,7 @@ void RefExpressionEval(string calc = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )")
9294
.Seq(s => s.Ch('*').Ref("unary").Act(c => numStack.Push(numStack.Pop() * numStack.Pop())))
9395

9496
// If we have a '/' run unary, check for division by zero and then divide the top two values on the stack:
95-
// Note again the stack is in reverse order.
97+
// o Note again the stack is in reverse order.
9698
.Seq(s => s.Ch('/').Ref("unary")
9799
.Op(c => numStack.Peek() != 0).OnFail("Division by 0") // Trap division by 0 and report as error.
98100
.Act(c => numStack.Push(1 / numStack.Pop() * numStack.Pop())))
@@ -143,10 +145,67 @@ void RefExpressionEval(string calc = "9 - (5.5 + 3) * 6 - 4 / ( 9 - 1 )")
143145
var scn = new FexScanner(calc);
144146

145147
// Run the Axiom with the scanner which returns true/false:
146-
// o If valid display the answer = top value on the stack.
148+
// o If valid display the answer (= top value on the stack).
147149
// o Else display the error logged in the scanner's shared ErrorLog.
148150
Console.WriteLine(exprEval.Run(scn)
149151
? $"Answer = {numStack.Pop():F4}"
150152
: scn.ErrorLog.AsConsoleError("Expression Error:"));
151153
}
154+
```
155+
156+
### Sequence chart:
157+
```mermaid
158+
graph LR
159+
subgraph "Seq (Sequence)"
160+
direction LR
161+
A[Step 1] --> B[Step 2] -.-> C[Step n];
162+
end
163+
```
164+
165+
### Opt(ional) chart:
166+
```mermaid
167+
graph LR
168+
subgraph "Opt (Optional)"
169+
A[Seq]
170+
end
171+
```
172+
173+
### One of chart:
174+
```mermaid
175+
graph LR
176+
subgraph OneOf
177+
direction LR
178+
A[Seq 1] -->|or| B[Seq 2] -.->|or| C[Seq 3];
179+
end
180+
```
181+
182+
### Opt One of chart:
183+
```mermaid
184+
graph LR
185+
subgraph "Opt (Optional)"
186+
subgraph OneOf
187+
direction LR
188+
A[Seq 1] -->|or| B[Seq 2] -.->|or| C[Seq 3];
189+
end
190+
end
191+
```
192+
193+
### Repeat:
194+
```mermaid
195+
graph LR
196+
subgraph "Rep (Repeat: min, max)"
197+
direction LR
198+
A[Seq];
199+
end
200+
```
201+
202+
### RepeatOneOf:
203+
```mermaid
204+
graph LR
205+
subgraph "Rep (Repeat: min, max)"
206+
subgraph OneOf
207+
direction LR
208+
A[Seq 1] -->|or| B[Seq 2] -.->|or| C[Seq 3];
209+
end
210+
end
152211
```

FexGraphs.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Flow Expression Graphs
2+
3+
### Class Diagram
4+
```mermaid
5+
classDiagram
6+
FexElement <|-- Seq
7+
FexElement <|-- Opt
8+
FexElement <|-- OneOf
9+
FexElement <|-- OptOneOf
10+
FexElement <|-- Rep
11+
FexElement <|-- RepOneOf
12+
```
13+
14+
### Sequence:
15+
```mermaid
16+
graph LR
17+
subgraph "Seq (Sequence)"
18+
direction LR
19+
A[FexElement 1] --> B[FexElement 2] -.-> C[FexElement n];
20+
end
21+
```
22+
23+
### Optional:
24+
```mermaid
25+
graph LR
26+
subgraph "Opt (Optional)"
27+
A[Seq]
28+
end
29+
```
30+
31+
### OneOf:
32+
```mermaid
33+
graph LR
34+
subgraph OneOf
35+
direction LR
36+
A[Seq 1] -->|or| B[Seq 2] -.->|or| C[Seq 3];
37+
end
38+
```
39+
40+
### OptOneOf:
41+
```mermaid
42+
graph LR
43+
subgraph OptOneOf
44+
direction LR
45+
A[Seq 1] -->|or| B[Seq 2] -.->|or| C[Seq 3];
46+
end
47+
```
48+
49+
### Repeat:
50+
```mermaid
51+
graph LR
52+
subgraph "Rep (Repeat: min, max)"
53+
direction LR
54+
A[Seq];
55+
end
56+
```
57+
58+
### RepOneOf:
59+
```mermaid
60+
graph LR
61+
subgraph "RepOneOf (min, max)"
62+
direction LR
63+
A[Seq 1] -->|or| B[Seq 2] -.->|or| C[Seq 3];
64+
end
65+
```

FexSampleSet/FexBuilderExt.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@ namespace FexExampleSet
1212
/// </summary>
1313
public static class FexBuilderExt
1414
{
15-
public static FexBuilder<T> CTrace<T>(this FexBuilder<T> b, string text)
16-
=> b.Trace((c, res) => Console.WriteLine($"{text} : {res}"));
15+
public static FexBuilder<T> CTrace<T>(this FexBuilder<T> b, string text)
16+
=> b.Trace((c) => Console.WriteLine($"{text}"));
1717

18-
public static FexBuilder<T> CTrace<T>(this FexBuilder<T> b, Func<T, string> trace)
19-
=> b.Trace((c, res) => Console.WriteLine($"{trace(c)} : {res}"));
18+
public static FexBuilder<T> CTraceOp<T>(this FexBuilder<T> b, string text)
19+
=> b.TraceOp((c, res) => Console.WriteLine($"{text} : {res}"));
2020

21-
public static FexBuilder<T> CTrace<T>(this FexBuilder<T> b, Func<T, object, string> trace)
22-
=> b.Trace((c, v, res) => Console.WriteLine($"{trace(c, v)} : {res}"));
21+
public static FexBuilder<T> CTraceOp<T>(this FexBuilder<T> b, Func<T, string> trace)
22+
=> b.TraceOp((c, res) => Console.WriteLine($"{trace(c)} : {res}"));
23+
24+
public static FexBuilder<T> CTraceOp<T>(this FexBuilder<T> b, Func<T, object, string> trace)
25+
=> b.TraceOp((c, v, res) => Console.WriteLine($"{trace(c, v)} : {res}"));
2326
}
2427

2528
}

FexSampleSet/FexSampleSet.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
<Nullable>enable</Nullable>
88
</PropertyGroup>
99

10+
<ItemGroup>
11+
<Compile Remove="FexBuilderExt.cs" />
12+
</ItemGroup>
13+
1014
<ItemGroup>
1115
<ProjectReference Include="..\Psw.FlowExpressions\Psw.FlowExpressions.csproj" />
1216
</ItemGroup>

FexSampleSet/Program.cs

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Psw.FlowExpressions;
88

99
RunSamplesFex();
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+
199255
void 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+

FlowExpressions.sln

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
77
ProjectSection(SolutionItems) = preProject
88
LICENSE.txt = LICENSE.txt
99
README.md = README.md
10+
Release Backlog.md = Release Backlog.md
1011
EndProjectSection
1112
EndProject
1213
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FexSampleSet", "FexSampleSet\FexSampleSet.csproj", "{8136AD86-0F27-4A2D-A7E4-9487EE62864D}"
@@ -15,6 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{16EDBBE2-1
1516
ProjectSection(SolutionItems) = preProject
1617
Docs\CustomScanner.md = Docs\CustomScanner.md
1718
Docs\FexElementsRef.md = Docs\FexElementsRef.md
19+
FexGraphs.md = FexGraphs.md
1820
Docs\FexOverview.md = Docs\FexOverview.md
1921
Docs\FexScannerExt.md = Docs\FexScannerExt.md
2022
EndProjectSection

Psw.FlowExpressions/FexBuildState.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ public class FexBuildState<T>
2020
public FexPreOp<T> PreOp = new FexPreOp<T>(null); // Will be added to every Op - use for e.g to skip spaces
2121
public Dictionary<string, FexRef<T>> RefSet = new Dictionary<string, FexRef<T>>();
2222

23+
public IFexTracer Tracer = null;
24+
public bool TraceOn => Tracer != null;
25+
2326
public FexElement<T> Production(FexBuilder<T> builder, FexElement<T> prod, Action<FexBuilder<T>> build) {
2427
HostFex = prod;
2528
build?.Invoke(builder);

0 commit comments

Comments
 (0)