git.haldean.org tau / 26ae72e
lots of polish changes, things are starting to feel pretty nice around here haldean 4 years ago
4 changed file(s) with 125 addition(s) and 74 deletion(s). Raw diff Collapse all Expand all
55 xmlns:local="clr-namespace:Tau"
66 mc:Ignorable="d"
77 FocusManager.FocusedElement="{Binding ElementName=Input}"
8 Title="tau" Height="338.621" Width="224.735" WindowStyle="ToolWindow">
8 Title="tau" Height="338.621" Width="224.735">
99 <Grid>
1010 <Grid.RowDefinitions>
1111 <RowDefinition Height="*"/>
1212 <RowDefinition Height="Auto"/>
1313 <RowDefinition Height="40"/>
1414 </Grid.RowDefinitions>
15 <ListBox Name="Output" Margin="10" Grid.Row="0" BorderBrush="{x:Null}">
15 <ListBox Name="Output" Margin="10" Grid.Row="0" BorderBrush="{x:Null}" Background="{x:Null}" SelectionChanged="SelectRegister" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
1616 <ListBox.ItemTemplate>
1717 <DataTemplate>
1818 <StackPanel Orientation="Horizontal">
19 <TextBlock Margin="0,0,10,0" FontFamily="PragmataPro" FontSize="20" Text="{Binding Name, Mode=OneWay}"/>
20 <TextBlock FontFamily="PragmataPro" FontSize="20" Text="{Binding Value, Mode=OneWay}"/>
19 <TextBlock Name="Name" Margin="0,0,10,0" FontFamily="PragmataPro" FontSize="20" Text="{Binding Name, Mode=OneWay}" Foreground="#FF7799CC" MinWidth="30"/>
20 <TextBlock Name="Value" FontFamily="PragmataPro" FontSize="20" Text="{Binding Value, Mode=OneWay, StringFormat=G8}" />
2121 </StackPanel>
2222 </DataTemplate>
2323 </ListBox.ItemTemplate>
2424 </ListBox>
25 <TextBlock Name="Help" FontFamily="PragmataPro" FontSize="9" VerticalAlignment="Bottom" Grid.Row="1" Margin="10,0" TextWrapping="Wrap"/>
26 <TextBox Name="Input" Margin="10,0,10,10" TextWrapping="Wrap" VerticalAlignment="Bottom" FontFamily="PragmataPro" FontSize="20" KeyUp="InputKey" Grid.Row="2" BorderBrush="{x:Null}"/>
25 <TextBlock Name="Help" FontFamily="PragmataPro" FontSize="10" VerticalAlignment="Bottom" Grid.Row="1" Margin="10,0" TextWrapping="Wrap" Foreground="#FF7799CC"/>
26 <TextBox Name="Input" Margin="10,0,10,10" TextWrapping="Wrap" VerticalAlignment="Bottom" FontFamily="PragmataPro" FontSize="20" KeyUp="InputKey" Grid.Row="2" BorderBrush="{x:Null}" Background="{x:Null}"/>
2727 </Grid>
2828 </Window>
2323 OpParser parser;
2424 ListBox output;
2525 TextBlock help;
26 TextBox input;
2627
2728 public MainWindow()
2829 {
3132 parser = OpParser.New();
3233 output = (ListBox)FindName("Output");
3334 help = (TextBlock)FindName("Help");
35 input = (TextBox)FindName("Input");
3436 }
3537
3638 private void InputKey(object sender, KeyEventArgs e)
4850 }
4951 if (parsed.Count() > 0)
5052 {
51 help.Text = parsed.Last().Doc;
53 var last = parsed.Last();
54 if (last.Doc.Length > 0)
55 help.Text = String.Format("{0} {1}", last.Name, last.Doc);
5256 }
57 }
58
59 private void SelectRegister(object sender, SelectionChangedEventArgs ev)
60 {
61 StackItem selected = (StackItem) output.SelectedValue;
62 if (selected == null)
63 return;
64 Clipboard.SetText(selected.Value.ToString());
65 help.Text = "copied " + selected.Name + " to clipboard";
66 output.SelectedIndex = -1;
67 input.Focus();
5368 }
5469 }
5570 }
55
66 namespace Tau
77 {
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
238 class OpParser
249 {
25 private Dictionary<string, Op> ops;
10 private Dictionary<string, StackMachine.Op> ops;
2611
2712 protected OpParser()
2813 {
29 ops = new Dictionary<string, Op>();
14 ops = new Dictionary<string, StackMachine.Op>();
3015 }
3116
3217 public static OpParser New()
3419 OpParser parser = new OpParser();
3520
3621 /* arithmetic */
37 parser.AddOp(new Op("+", "adds the top two values", BinOp((v1, v2) => { return v2 + v1; })));
38 parser.AddOp(new Op("*", "multiplies the top two values", BinOp((v1, v2) => { return v2 * v1; })));
39 parser.AddOp(new Op("-", "subtracts the top value from the value below it", BinOp((v1, v2) => { return v2 - v1; })));
40 parser.AddOp(new Op("/", "divides the top value into the value below it", BinOp((v1, v2) => { return v2 / v1; })));
22 parser.AddOp(new StackMachine.Op("+", "adds the top two values", BinOp((v1, v2) => { return v2 + v1; })));
23 parser.AddOp(new StackMachine.Op("*", "multiplies the top two values", BinOp((v1, v2) => { return v2 * v1; })));
24 parser.AddOp(new StackMachine.Op("-", "subtracts the top value from the value below it", BinOp((v1, v2) => { return v2 - v1; })));
25 parser.AddOp(new StackMachine.Op("/", "divides the top value into the value below it", BinOp((v1, v2) => { return v2 / v1; })));
26 parser.AddOp(new StackMachine.Op("^", "takes the second value to the power of the top value", BinOp((v1, v2) => { return Math.Pow(v2, v1); })));
4127
4228 /* stack */
43 parser.AddOp(new Op("dup", "duplicates the top value on the stack", (Stack<Double> values) =>
29 parser.AddOp(new StackMachine.Op("dup", "duplicates the top value on the stack", (Stack<Double> values) =>
4430 {
4531 if (values.Count == 0)
4632 return;
4834 values.Push(v);
4935 values.Push(v);
5036 }));
51 parser.AddOp(new Op("swap", "swaps the top two values on the stack", (Stack<Double> values) =>
37 parser.AddOp(new StackMachine.Op("swap", "swaps the top two values on the stack", (Stack<Double> values) =>
5238 {
5339 if (values.Count < 2)
5440 return;
5743 values.Push(v1);
5844 values.Push(v2);
5945 }));
46 foreach (var register in StackMachine.Registers)
47 {
48 parser.AddOp(new StackMachine.Op(register, "pushes the value of register " + register + " onto the stack",
49 (StackMachine m) => { m.Push(m.GetRegister(register)); }));
50 }
6051
6152 /* trig */
62 parser.AddOp(new Op("sin", "the sine of the top value, assumes value is in radians", UnOp((v) => { return Math.Sin(v); })));
63 parser.AddOp(new Op("cos", "the cosine of the top value, assumes value is in radians", UnOp((v) => { return Math.Cos(v); })));
64 parser.AddOp(new Op("tan", "the tangent of the top value, assumes value is in radians", UnOp((v) => { return Math.Tan(v); })));
65 parser.AddOp(new Op("pi", "pushes the value of π onto the stack", Const(Math.PI)));
66 parser.AddOp(new Op("tau", "pushes the value of τ onto the stack", Const(Math.PI * 2)));
67 parser.AddOp(new Op("rad", "converts the top value from degrees to radians", UnOp((v) => { return Math.PI * v / 180; })));
68 parser.AddOp(new Op("deg", "converts the top value from radians to degrees", UnOp((v) => { return 180 * v / Math.PI; })));
53 parser.AddOp(new StackMachine.Op("sin", "the sine of the top value, assumes value is in radians", UnOp((v) => { return Math.Sin(v); })));
54 parser.AddOp(new StackMachine.Op("cos", "the cosine of the top value, assumes value is in radians", UnOp((v) => { return Math.Cos(v); })));
55 parser.AddOp(new StackMachine.Op("tan", "the tangent of the top value, assumes value is in radians", UnOp((v) => { return Math.Tan(v); })));
56 parser.AddOp(new StackMachine.Op("pi", "pushes the value of π onto the stack", Const(Math.PI)));
57 parser.AddOp(new StackMachine.Op("tau", "pushes the value of τ onto the stack", Const(Math.PI * 2)));
58 parser.AddOp(new StackMachine.Op("rad", "converts the top value from degrees to radians", UnOp((v) => { return Math.PI * v / 180; })));
59 parser.AddOp(new StackMachine.Op("deg", "converts the top value from radians to degrees", UnOp((v) => { return 180 * v / Math.PI; })));
6960
7061 return parser;
7162 }
7263
73 void AddOp(Op o)
64 void AddOp(StackMachine.Op o)
7465 {
7566 ops.Add(o.Name, o);
7667 }
7768
7869 private delegate double Binary(double v1, double v2);
79 private static Op.Visitor BinOp(Binary func)
70 private static StackMachine.Op.StackVisitor BinOp(Binary func)
8071 {
8172 return (Stack<Double> values) =>
8273 {
8374 if (values.Count == 0)
8475 return;
8576 double v1 = values.Pop();
86 double v2 = values.Count == 0 ? v1 : values.Pop();
77 double v2 = values.Count == 0 ? 0 : values.Pop();
8778 values.Push(func(v1, v2));
8879 };
8980 }
9081 private delegate double Unary(double v);
91 private static Op.Visitor UnOp(Unary func)
82 private static StackMachine.Op.StackVisitor UnOp(Unary func)
9283 {
9384 return (Stack<Double> values) =>
9485 {
9889 values.Push(func(v));
9990 };
10091 }
101 private static Op.Visitor Const(double v)
92 private static StackMachine.Op.StackVisitor Const(double v)
10293 {
10394 return (Stack<Double> values) =>
10495 {
10697 };
10798 }
10899
109 private static Op PushOp(double v)
100 private static StackMachine.Op PushOp(double v)
110101 {
111 return new Op("constant", "", (Stack<Double> values) => { values.Push(v); });
102 return new StackMachine.Op("constant", "", (Stack<Double> values) => { values.Push(v); });
112103 }
113104
114 public IEnumerable<Op> Parse(string input)
105 public IEnumerable<StackMachine.Op> Parse(string input)
115106 {
116 List<Op> res = new List<Op>();
107 List<StackMachine.Op> res = new List<StackMachine.Op>();
117108 StringBuilder sb = new StringBuilder();
118109 bool inNumber = false;
119110 bool waitSub = false;
120111
121 for (int i = 0; i < input.Length; i++)
112 int i = 0;
113 while (i < input.Length)
122114 {
115 // Note: a for loop is not used here because i doesn't monotonically increase,
116 // and I find it confusing when index variables in for loops aren't immutable
117 // inside the loop. Sometimes, i is decremented for backtracking purposes.
118 char next = input[i++];
119
123120 // Whitespace exists only to be a boundary between other things
124 if (input[i] == ' ')
121 if (next == ' ')
125122 {
126123 if (waitSub)
127124 {
139136 }
140137 continue;
141138 }
142 sb.Append(input[i]);
139 sb.Append(next);
143140
144141 bool isNumber = Double.TryParse(sb.ToString(), out _);
145142 if (inNumber && !isNumber)
146143 {
147144 sb.Remove(sb.Length - 1, 1);
148145 res.Add(PushOp(Double.Parse(sb.ToString())));
149 sb = new StringBuilder(input.Substring(i, 1));
146 sb = new StringBuilder(input.Substring(i - 1, 1));
150147 inNumber = false;
148 // Fallthrough; this character starts a new token
151149 }
152 else if (isNumber)
150 if (isNumber)
153151 {
154152 inNumber = true;
153 continue;
155154 }
156 else if (sb.Length == 1 && input[i] == '-')
155 if (sb.Length == 1 && next == '-')
157156 {
158157 // Special-case the hyphen because it could either be a negation or a subtraction;
159158 // if it's not immediately followed by a number, we call it subtraction.
160159 waitSub = true;
160 continue;
161161 }
162 else if (waitSub)
162 if (waitSub)
163163 {
164164 // If we get here, the hyphen wasn't negation. Push the hypen and restart.
165165 waitSub = false;
166166 res.Add(ops["-"]);
167 sb = new StringBuilder(input.Substring(i, 1));
167 i--;
168 sb = new StringBuilder();
169 continue;
168170 }
169171 else if (ops.ContainsKey(sb.ToString()))
170172 {
55
66 namespace Tau
77 {
8
89 public class StackItem
910 {
1011 internal StackItem(string name, double v)
1415 }
1516 public string Name { get; }
1617 public double Value { get; }
17
18 internal static string NameIndex(int i)
19 {
20 switch (i)
21 {
22 case 0: return "X";
23 case 1: return "Y";
24 case 2: return "Z";
25 case 3: return "A";
26 case 4: return "B";
27 case 5: return "C";
28 }
29 var sb = new StringBuilder();
30 sb.Append("R");
31 sb.Append(i);
32 return sb.ToString();
33 }
3418
3519 public override string ToString()
3620 {
4428
4529 public class StackMachine
4630 {
31 public static readonly string[] Registers = new string[] {
32 "X", "Y", "Z", "A", "B", "C", "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10" };
33
34 public class Op
35 {
36 public delegate void Visitor(StackMachine m);
37 public delegate void StackVisitor(Stack<Double> s);
38 public string Name;
39 public Visitor Func;
40 public string Doc;
41
42 public Op(string n, string d, Visitor f)
43 {
44 Name = n;
45 Func = f;
46 Doc = d;
47 }
48 public Op(string n, string d, StackVisitor v)
49 : this(n, d, (StackMachine m) => { v(m.values); }) { }
50 }
51
4752 private Stack<Double> values;
4853
4954 public StackMachine()
5560 {
5661 foreach (Op o in stream)
5762 {
58 o.Func(values);
63 o.Func(this);
5964 }
6065 }
6166
6368 {
6469 return from v in values.Zip(Enumerable.Range(0, values.Count),
6570 (double v, int i) => { return new { Idx = i, V = v }; })
66 select new StackItem(StackItem.NameIndex(v.Idx), v.V);
71 select new StackItem(RegisterName(v.Idx), v.V);
6772 }
68
73
74 public double GetRegister(string name)
75 {
76 int idx = RegisterIndex(name);
77 if (idx >= values.Count)
78 return 0;
79 return values.ElementAt(idx);
80 }
81
82 public void Push(double v)
83 {
84 values.Push(v);
85 }
86
87 internal static int RegisterIndex(string name)
88 {
89 name = name.ToUpper();
90 int idx = Array.IndexOf(Registers, name);
91 if (idx < 0)
92 throw new ArgumentException("unknown register name " + name);
93 return idx;
94 }
95
96 internal static string RegisterName(int i)
97 {
98 if (i < Registers.Count())
99 return Registers[i];
100 // Registers after the first 16 exist, they're just unnamed.
101 return "";
102 }
69103 }
70104 }