interactive help text
haldean
5 years ago
5 | 5 | xmlns:local="clr-namespace:Tau" |
6 | 6 | mc:Ignorable="d" |
7 | 7 | FocusManager.FocusedElement="{Binding ElementName=Input}" |
8 | Title="tau" Height="257.492" Width="160.859" WindowStyle="ToolWindow"> | |
8 | Title="tau" Height="249.621" Width="159.735" WindowStyle="ToolWindow"> | |
9 | 9 | <Grid> |
10 | <TextBlock TextWrapping="Wrap" Text="0" Margin="10,10,10,30" FontFamily="PragmataPro" Name="Result" FontSize="20"/> | |
11 | <TextBox Margin="10,0,10,9" TextWrapping="Wrap" VerticalAlignment="Bottom" FontFamily="PragmataPro" Name="Input" FontSize="20" KeyUp="InputKey"/> | |
10 | <Grid.RowDefinitions> | |
11 | <RowDefinition Height="*"/> | |
12 | <RowDefinition Height="Auto"/> | |
13 | <RowDefinition Height="40"/> | |
14 | </Grid.RowDefinitions> | |
15 | <TextBlock Name="Result" TextWrapping="Wrap" Text="0" Margin="10,10,10,72" FontFamily="PragmataPro" FontSize="20"/> | |
16 | <TextBlock Name="Help" FontFamily="PragmataPro" FontSize="9" VerticalAlignment="Bottom" Grid.Row="1" Margin="10,0" TextWrapping="Wrap"/> | |
17 | <TextBox Name="Input" Margin="10,0,10,10" TextWrapping="Wrap" VerticalAlignment="Bottom" FontFamily="PragmataPro" FontSize="20" KeyUp="InputKey" Grid.Row="2"/> | |
12 | 18 | </Grid> |
13 | 19 | </Window> |
20 | 20 | public partial class MainWindow : Window |
21 | 21 | { |
22 | 22 | StackMachine sm; |
23 | OpParser parser; | |
23 | 24 | TextBlock result; |
25 | TextBlock help; | |
24 | 26 | |
25 | 27 | public MainWindow() |
26 | 28 | { |
27 | 29 | InitializeComponent(); |
28 | sm = StackMachine.New(); | |
29 | result = (TextBlock) FindName("Result"); | |
30 | sm = new StackMachine(); | |
31 | parser = OpParser.New(); | |
32 | result = (TextBlock)FindName("Result"); | |
33 | help = (TextBlock)FindName("Help"); | |
30 | 34 | } |
31 | 35 | |
32 | 36 | private void InputKey(object sender, KeyEventArgs e) |
33 | 37 | { |
34 | switch (e.Key) | |
38 | TextBox t = (TextBox)sender; | |
39 | var parsed = parser.Parse(t.Text); | |
40 | help.Text = ""; | |
41 | ||
42 | if (e.Key == Key.Enter) | |
35 | 43 | { |
36 | case Key.Enter: | |
37 | TextBox t = (TextBox)sender; | |
38 | sm.Run(t.Text); | |
39 | result.Text = sm.State(); | |
40 | t.Text = ""; | |
41 | break; | |
42 | default: | |
43 | break; | |
44 | sm.Run(parsed); | |
45 | result.Text = sm.State(); | |
46 | t.Text = ""; | |
47 | return; | |
48 | } | |
49 | if (parsed.Count() > 0) | |
50 | { | |
51 | help.Text = parsed.Last().Doc; | |
44 | 52 | } |
45 | 53 | } |
46 | 54 | } |
0 | using System; | |
1 | using System.Collections.Generic; | |
2 | using System.Linq; | |
3 | using System.Text; | |
4 | using System.Threading.Tasks; | |
5 | ||
6 | namespace Tau | |
7 | { | |
8 | public class Op | |
9 | { | |
10 | public delegate void Visitor(Stack<Double> s); | |
11 | public string Name; | |
12 | public Visitor Func; | |
13 | public string Doc; | |
14 | ||
15 | public Op(string n, string d, Visitor f) | |
16 | { | |
17 | Name = n; | |
18 | Func = f; | |
19 | Doc = d; | |
20 | } | |
21 | } | |
22 | ||
23 | class OpParser | |
24 | { | |
25 | private Dictionary<string, Op> ops; | |
26 | ||
27 | protected OpParser() | |
28 | { | |
29 | ops = new Dictionary<string, Op>(); | |
30 | } | |
31 | ||
32 | public static OpParser New() | |
33 | { | |
34 | OpParser parser = new OpParser(); | |
35 | parser.AddOp(new Op("+", "adds the top two values", BinOp((v1, v2) => { return v2 + v1; }))); | |
36 | parser.AddOp(new Op("*", "multiplies the top two values", BinOp((v1, v2) => { return v2 * v1; }))); | |
37 | parser.AddOp(new Op("-", "subtracts the top value from the value below it", BinOp((v1, v2) => { return v2 - v1; }))); | |
38 | parser.AddOp(new Op("/", "divides the top value into the value below it", BinOp((v1, v2) => { return v2 / v1; }))); | |
39 | parser.AddOp(new Op("dup", "duplicates the top value on the stack", (Stack<Double> values) => { | |
40 | if (values.Count == 0) | |
41 | return; | |
42 | double v = values.Pop(); | |
43 | values.Push(v); | |
44 | values.Push(v); | |
45 | })); | |
46 | parser.AddOp(new Op("swap", "swaps the top two values on the stack", (Stack<Double> values) => { | |
47 | if (values.Count < 2) | |
48 | return; | |
49 | double v1 = values.Pop(); | |
50 | double v2 = values.Pop(); | |
51 | values.Push(v1); | |
52 | values.Push(v2); | |
53 | })); | |
54 | return parser; | |
55 | } | |
56 | ||
57 | void AddOp(Op o) | |
58 | { | |
59 | ops.Add(o.Name, o); | |
60 | } | |
61 | ||
62 | private delegate double Binary(double v1, double v2); | |
63 | private static Op.Visitor BinOp(Binary func) | |
64 | { | |
65 | return (Stack<Double> values) => | |
66 | { | |
67 | if (values.Count == 0) | |
68 | return; | |
69 | double v1 = values.Pop(); | |
70 | double v2 = values.Count == 0 ? v1 : values.Pop(); | |
71 | values.Push(func(v1, v2)); | |
72 | }; | |
73 | } | |
74 | ||
75 | private static Op PushOp(double v) | |
76 | { | |
77 | return new Op("constant", "", (Stack<Double> values) => { values.Push(v); }); | |
78 | } | |
79 | ||
80 | public IEnumerable<Op> Parse(string input) | |
81 | { | |
82 | List<Op> res = new List<Op>(); | |
83 | StringBuilder sb = new StringBuilder(); | |
84 | bool inNumber = false; | |
85 | bool waitSub = false; | |
86 | ||
87 | for (int i = 0; i < input.Length; i++) | |
88 | { | |
89 | // Whitespace exists only to be a boundary between other things | |
90 | if (input[i] == ' ') | |
91 | { | |
92 | if (waitSub) | |
93 | { | |
94 | // If we get here, the hyphen wasn't negation. Push the hypen and restart. | |
95 | waitSub = false; | |
96 | res.Add(ops["-"]); | |
97 | // We drop the whitespace implicitly by not adding anything to this. | |
98 | sb = new StringBuilder(); | |
99 | } | |
100 | if (inNumber) | |
101 | { | |
102 | res.Add(PushOp(Double.Parse(sb.ToString()))); | |
103 | sb = new StringBuilder(); | |
104 | inNumber = false; | |
105 | } | |
106 | continue; | |
107 | } | |
108 | sb.Append(input[i]); | |
109 | ||
110 | bool isNumber = Double.TryParse(sb.ToString(), out _); | |
111 | if (inNumber && !isNumber) | |
112 | { | |
113 | sb.Remove(sb.Length - 1, 1); | |
114 | res.Add(PushOp(Double.Parse(sb.ToString()))); | |
115 | sb = new StringBuilder(input.Substring(i, 1)); | |
116 | inNumber = false; | |
117 | } | |
118 | else if (isNumber) | |
119 | { | |
120 | inNumber = true; | |
121 | } | |
122 | else if (sb.Length == 1 && input[i] == '-') | |
123 | { | |
124 | // Special-case the hyphen because it could either be a negation or a subtraction; | |
125 | // if it's not immediately followed by a number, we call it subtraction. | |
126 | waitSub = true; | |
127 | } | |
128 | else if (waitSub) | |
129 | { | |
130 | // If we get here, the hyphen wasn't negation. Push the hypen and restart. | |
131 | waitSub = false; | |
132 | res.Add(ops["-"]); | |
133 | sb = new StringBuilder(input.Substring(i, 1)); | |
134 | } | |
135 | else if (ops.ContainsKey(sb.ToString())) | |
136 | { | |
137 | res.Add(ops[sb.ToString()]); | |
138 | sb = new StringBuilder(); | |
139 | } | |
140 | } | |
141 | ||
142 | if (inNumber) | |
143 | res.Add(PushOp(Double.Parse(sb.ToString()))); | |
144 | else if (ops.ContainsKey(sb.ToString())) | |
145 | { | |
146 | res.Add(ops[sb.ToString()]); | |
147 | sb = new StringBuilder(); | |
148 | } | |
149 | return res; | |
150 | } | |
151 | } | |
152 | } |
5 | 5 | |
6 | 6 | namespace Tau |
7 | 7 | { |
8 | class Op | |
9 | { | |
10 | public delegate void Visitor(Stack<Double> s); | |
11 | public string Name; | |
12 | public Visitor Func; | |
13 | public string Doc; | |
14 | ||
15 | public Op(string n, string d, Visitor f) | |
16 | { | |
17 | Name = n; | |
18 | Func = f; | |
19 | Doc = d; | |
20 | } | |
21 | } | |
22 | ||
23 | 8 | public class StackMachine |
24 | 9 | { |
25 | 10 | private Stack<Double> values; |
26 | private Dictionary<string, Op> ops; | |
27 | 11 | |
28 | protected StackMachine() | |
12 | public StackMachine() | |
29 | 13 | { |
30 | 14 | values = new Stack<double>(); |
31 | ops = new Dictionary<string, Op>(); | |
32 | } | |
33 | ||
34 | void AddOp(Op o) | |
35 | { | |
36 | ops.Add(o.Name, o); | |
37 | 15 | } |
38 | 16 | |
39 | private delegate double Binary(double v1, double v2); | |
40 | private static Op.Visitor BinOp(Binary func) | |
17 | public void Run(IEnumerable<Op> stream) | |
41 | 18 | { |
42 | return (Stack<Double> values) => | |
19 | foreach (Op o in stream) | |
43 | 20 | { |
44 | if (values.Count == 0) | |
45 | return; | |
46 | double v1 = values.Pop(); | |
47 | double v2 = values.Count == 0 ? v1 : values.Pop(); | |
48 | values.Push(func(v1, v2)); | |
49 | }; | |
50 | } | |
51 | ||
52 | public static StackMachine New() | |
53 | { | |
54 | StackMachine sm = new StackMachine(); | |
55 | sm.AddOp(new Op("+", "adds the top two values", BinOp((v1, v2) => { return v2 + v1; }))); | |
56 | sm.AddOp(new Op("*", "multiplies the top two values", BinOp((v1, v2) => { return v2 * v1; }))); | |
57 | sm.AddOp(new Op("-", "subtracts the top value from the value below it", BinOp((v1, v2) => { return v2 - v1; }))); | |
58 | sm.AddOp(new Op("/", "divides the top value into the value below it", BinOp((v1, v2) => { return v2 - v1; }))); | |
59 | sm.AddOp(new Op("dup", "duplicates the top value on the stack", (Stack<Double> values) => { | |
60 | if (values.Count == 0) | |
61 | return; | |
62 | double v = values.Pop(); | |
63 | values.Push(v); | |
64 | values.Push(v); | |
65 | })); | |
66 | sm.AddOp(new Op("swap", "swaps the top two values on the stack", (Stack<Double> values) => { | |
67 | if (values.Count < 2) | |
68 | return; | |
69 | double v1 = values.Pop(); | |
70 | double v2 = values.Pop(); | |
71 | values.Push(v1); | |
72 | values.Push(v2); | |
73 | })); | |
74 | return sm; | |
75 | } | |
76 | ||
77 | public void Run(string data) | |
78 | { | |
79 | string[] bits = data.Split(); | |
80 | foreach (string b in bits) | |
81 | { | |
82 | double res; | |
83 | if (Double.TryParse(b, out res)) | |
84 | { | |
85 | Push(res); | |
86 | continue; | |
87 | } | |
88 | Push(b); | |
21 | o.Func(values); | |
89 | 22 | } |
90 | } | |
91 | ||
92 | ||
93 | public void Push(string opname) | |
94 | { | |
95 | Op op; | |
96 | if (ops.TryGetValue(opname, out op)) | |
97 | { | |
98 | op.Func(values); | |
99 | } | |
100 | } | |
101 | ||
102 | public void Push(Double v) | |
103 | { | |
104 | values.Push(v); | |
105 | 23 | } |
106 | 24 | |
107 | 25 | public string State() |