├── .gitignore ├── README.md ├── Restructor.sln ├── RestructorRuntime ├── Game.cs ├── Properties │ └── AssemblyInfo.cs ├── RestructorRuntime.csproj └── Runtime.cs └── restructor ├── CodeRender.cs ├── Docs └── TODO.txt ├── Game ├── spaceship.gif └── spaceship.xaml ├── Images ├── Copy.png ├── New.png ├── Open.png ├── Paste.png ├── Restruct.png ├── Run.png ├── Save As.png ├── Save Exe.png ├── Save.png └── Undo.png ├── Main.cs ├── Node.cs ├── NodeTypes ├── Builtin.cs ├── BuiltinOp.cs ├── Function.cs ├── FunctionValue.cs ├── Literals.cs ├── NodeType.cs ├── PlaceHolder.cs └── Unparsed.cs ├── Parser.cs ├── Program.cs ├── Properties └── app.manifest ├── Restructor.csproj ├── Selection.cs ├── TwoNode.cs └── Uniques.cs /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.suo 2 | **/*.user 3 | **/bin/** 4 | **/obj/** 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Prototype of the Restructor project 2 | 3 | For details: http://strlen.com/restructor/ 4 | 5 | This code is very incomplete, you have been warned. 6 | 7 | License: Apache v2. 8 | -------------------------------------------------------------------------------- /Restructor.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Restructor", "Restructor\Restructor.csproj", "{9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestructorRuntime", "RestructorRuntime\RestructorRuntime.csproj", "{4A4D852B-3887-49A7-A83B-B020C7082C57}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|Mixed Platforms = Debug|Mixed Platforms 12 | Debug|x86 = Debug|x86 13 | Release|Any CPU = Release|Any CPU 14 | Release|Mixed Platforms = Release|Mixed Platforms 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {4A4D852B-3887-49A7-A83B-B020C7082C57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {4A4D852B-3887-49A7-A83B-B020C7082C57}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {4A4D852B-3887-49A7-A83B-B020C7082C57}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 21 | {4A4D852B-3887-49A7-A83B-B020C7082C57}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 22 | {4A4D852B-3887-49A7-A83B-B020C7082C57}.Debug|x86.ActiveCfg = Debug|Any CPU 23 | {4A4D852B-3887-49A7-A83B-B020C7082C57}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {4A4D852B-3887-49A7-A83B-B020C7082C57}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {4A4D852B-3887-49A7-A83B-B020C7082C57}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 26 | {4A4D852B-3887-49A7-A83B-B020C7082C57}.Release|Mixed Platforms.Build.0 = Release|Any CPU 27 | {4A4D852B-3887-49A7-A83B-B020C7082C57}.Release|x86.ActiveCfg = Release|Any CPU 28 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 31 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Debug|Mixed Platforms.Build.0 = Debug|x86 32 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Debug|x86.ActiveCfg = Debug|x86 33 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Debug|x86.Build.0 = Debug|x86 34 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Release|Any CPU.ActiveCfg = Release|x86 35 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Release|Mixed Platforms.ActiveCfg = Release|x86 36 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Release|Mixed Platforms.Build.0 = Release|x86 37 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Release|x86.ActiveCfg = Release|x86 38 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79}.Release|x86.Build.0 = Release|x86 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /RestructorRuntime/Game.cs: -------------------------------------------------------------------------------- 1 | // Note: this file uses XNA for minimal game rendering. 2 | // It is disabled by default because most people won't have it installed, and MS doesn't support 3 | // it anymore. 4 | // Enable it by uncommenting the line below, and adding the XNA assembly references to the project. 5 | 6 | //#define USE_XNA 7 | 8 | using System; 9 | using real = System.Double; 10 | 11 | #if USE_XNA 12 | 13 | using System.Collections.Generic; 14 | using Microsoft.Xna.Framework; 15 | using Microsoft.Xna.Framework.Graphics; 16 | using Microsoft.Xna.Framework.Input; 17 | 18 | public class Game : Microsoft.Xna.Framework.Game 19 | { 20 | GraphicsDeviceManager graphics; 21 | public SpriteBatch sb; 22 | public Dictionary textures = new Dictionary(); 23 | Action update; 24 | Action draw; 25 | 26 | public Game(string title, Action _update, Action _draw) 27 | { 28 | update = _update; 29 | draw = _draw; 30 | graphics = new GraphicsDeviceManager(this); 31 | Content.RootDirectory = "Content"; 32 | IsMouseVisible = true; 33 | Window.Title = title; 34 | Window.AllowUserResizing = true; 35 | } 36 | 37 | protected override void Initialize() 38 | { 39 | base.Initialize(); 40 | } 41 | 42 | protected override void LoadContent() 43 | { 44 | sb = new SpriteBatch(GraphicsDevice); 45 | } 46 | 47 | protected override void UnloadContent() 48 | { 49 | foreach (var t in textures) t.Value.Dispose(); 50 | } 51 | 52 | protected override void Update(GameTime gameTime) 53 | { 54 | KeyboardState keyState = Keyboard.GetState(); 55 | // TODO: closes restructor as well, for testing only 56 | if (keyState.IsKeyDown(Keys.Escape)) Exit(); 57 | 58 | MouseState ms = Mouse.GetState(); 59 | //if (ms.LeftButton) 60 | 61 | base.Update(gameTime); 62 | } 63 | 64 | protected override void Draw(GameTime gameTime) 65 | { 66 | GraphicsDevice.Clear(Color.CornflowerBlue); 67 | 68 | sb.Begin(); 69 | draw((real)gameTime.TotalGameTime.TotalSeconds); 70 | sb.End(); 71 | 72 | base.Draw(gameTime); 73 | } 74 | 75 | public void RenderSprite(string name, real x, real y, real scale, real rot) 76 | { 77 | Texture2D sprite; 78 | if (!textures.TryGetValue(name, out sprite)) 79 | { 80 | sprite = Texture2D.FromStream(GraphicsDevice, 81 | new System.IO.StreamReader("..\\..\\Game\\" + name).BaseStream); 82 | if (sprite == null) return; 83 | textures[name] = sprite; 84 | } 85 | sb.Draw(sprite, new Vector2((float)x, (float)y), null, Color.White, (float)rot, 86 | new Vector2(sprite.Width / 2, sprite.Height / 2), (float)scale, SpriteEffects.None, 0); 87 | } 88 | } 89 | 90 | #else 91 | 92 | using System.Windows; 93 | 94 | public class Game 95 | { 96 | public Game(string title, Action _update, Action _draw) { } 97 | 98 | public void Run() 99 | { 100 | MessageBox.Show( 101 | "This would show a rotating spaceship if you had XNA installed, see top of Game.cs"); 102 | } 103 | 104 | public void RenderSprite(string name, real x, real y, real scale, real rot) { } 105 | } 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /RestructorRuntime/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RestructorRuntime")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RestructorRuntime")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e3b01bda-bc3e-44da-b4ed-776f8d6315fd")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /RestructorRuntime/RestructorRuntime.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {4A4D852B-3887-49A7-A83B-B020C7082C57} 9 | Library 10 | Properties 11 | RestructorRuntime 12 | RestructorRuntime 13 | v4.0 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 51 | -------------------------------------------------------------------------------- /RestructorRuntime/Runtime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using real = System.Double; 4 | 5 | public interface IRestructorCode 6 | { 7 | string __main(); 8 | } 9 | 10 | public class Runtime 11 | { 12 | public static string[] __commandlineargs = {}; 13 | 14 | //public static void __init() { System.Diagnostics.Debugger.Launch(); } 15 | 16 | public static void __dbg(int i) { System.Diagnostics.Debug.WriteLine("dbg: " + i); } 17 | public static void __dbg(real r) { System.Diagnostics.Debug.WriteLine("dbg: " + r); } 18 | public static void __dbg(IntPtr i) { System.Diagnostics.Debug.WriteLine("dbg: "); } 19 | public static void __dbg(Object o) { System.Diagnostics.Debug.WriteLine("dbg: " + o); } 20 | 21 | public static string __itoa(int i) { return i.ToString(); } 22 | public static string __ftoa(real r) { return r.ToString(); } 23 | public static string __concat(string a, string b) { return a + b; } 24 | 25 | public static int ftoi(real r) { return (int)r; } 26 | public static real sqrt(real r) { return Math.Sqrt(r); } 27 | 28 | public static int atoi(string a) { int r; int.TryParse(a, out r); return r; } 29 | public static real atof(string a) { real r; real.TryParse(a, out r); return r; } 30 | 31 | public static string getargs(int i) 32 | { 33 | return i<__commandlineargs.Length ? __commandlineargs[i] : ""; 34 | } 35 | 36 | public static Func, List> map(Func f) 37 | { 38 | return (List l) => 39 | { 40 | var o = new List(); 41 | foreach (var e in l) o.Add(f(e)); 42 | return o; 43 | }; 44 | } 45 | 46 | public static Func, A> reduce(A s, Func f) 47 | { 48 | return (List l) => 49 | { 50 | foreach (var e in l) s = f(s, e); 51 | return s; 52 | }; 53 | } 54 | 55 | public static T generictest(T x) { return x; } 56 | 57 | //public static R apply(V v, Func f) { return f(v); } 58 | 59 | public static Game g = null; 60 | 61 | public static void game(string title, Action update, Action draw) 62 | { 63 | g = new Game(title, update, draw); 64 | g.Run(); 65 | } 66 | 67 | public static void rendersprite(string name, real x, real y, real scale, real rot) 68 | { 69 | g.RenderSprite(name, x, y, scale, rot); 70 | } 71 | } -------------------------------------------------------------------------------- /restructor/CodeRender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Media; 6 | using System.Windows.Input; 7 | using System.Diagnostics; 8 | 9 | abstract class CodeRender 10 | { 11 | internal virtual void Push(Object n) { } 12 | internal virtual void Pop(Object n) { } 13 | 14 | internal virtual void Text(string text, int leftm, int rightm) { } 15 | 16 | internal virtual void Scale(double sc, Action a) { } 17 | 18 | internal virtual void AltNext(string s) { } 19 | internal virtual void ColNext(Brush b) { } 20 | internal virtual void WeightNext(int w) { } 21 | 22 | internal virtual void EditBox(string text, Object n) { } 23 | 24 | internal virtual void StartTopLevel() { } 25 | internal virtual void SaveFunctionLHS() { } 26 | internal virtual void Function(int i, int depth) { } 27 | } 28 | 29 | class CodeRenderGUI : CodeRender 30 | { 31 | MainWin w; 32 | 33 | List stack = new List(); 34 | List nodestack = new List(); 35 | internal Grid topgrid = null; 36 | 37 | internal StackPanel lastsp = null; 38 | internal StackPanel lastspf = null; 39 | internal TextBox lasttextbox = null; 40 | 41 | int errnesting = 0; 42 | 43 | // these are set for one item, then cleared 44 | double scale = 0; 45 | string altop = null; 46 | Brush col = null; 47 | int fontweight = 0; 48 | 49 | internal CodeRenderGUI(MainWin _w) { w = _w; } 50 | 51 | StackPanel Top() { return stack.Count == 0 ? null : stack[stack.Count - 1]; } 52 | 53 | void SetNode(FrameworkElement fe) 54 | { 55 | if (nodestack.Count > 0) fe.Tag = nodestack[nodestack.Count - 1]; 56 | } 57 | 58 | internal override void Push(Object n) 59 | { 60 | var sp = GUI.StackPanel(true); 61 | sp.Tag = n; 62 | stack.Add(sp); 63 | nodestack.Add(n); 64 | if (n != null && n is Node && (n as Node).err != null) errnesting++; 65 | } 66 | 67 | internal override void Pop(Object n) 68 | { 69 | var top = Top(); 70 | stack.RemoveAt(stack.Count - 1); 71 | nodestack.RemoveAt(nodestack.Count - 1); 72 | 73 | FrameworkElement fe = top; 74 | if (n != null) 75 | { 76 | var node = n as Node; 77 | if (node != null) 78 | { 79 | if (node.err != null) errnesting--; 80 | 81 | fe.AllowDrop = true; 82 | 83 | string tt = null; 84 | 85 | if (node.err != null) 86 | { 87 | tt = node.err; 88 | fe = new Border 89 | { 90 | BorderBrush = Brushes.Red, 91 | BorderThickness = new Thickness(2), 92 | CornerRadius = new CornerRadius(2), 93 | Child = top 94 | }; 95 | } 96 | else if (node.clrt != null && errnesting == 0) 97 | { 98 | tt = NodeType.NiceType(node.clrt); 99 | } 100 | 101 | if (tt != null) fe.ToolTip = new ToolTip { Content = tt }; 102 | } 103 | } 104 | 105 | if (Top() != null) Top().Children.Add(fe); 106 | 107 | lastsp = top; 108 | } 109 | 110 | internal override void Text(string text, int leftm, int rightm) 111 | { 112 | var tb = GUI.TextBlock(Top(), text, leftm, rightm); 113 | SetNode(tb); 114 | if (scale != 0) { tb.LayoutTransform = new ScaleTransform(scale, scale); } 115 | if (altop != null) { tb.Text = altop; altop = null; } 116 | if (col != null) { tb.Foreground = col; col = Brushes.Black; } 117 | if (fontweight > 0) 118 | { 119 | tb.FontWeight = FontWeight.FromOpenTypeWeight(fontweight); 120 | fontweight = 0; 121 | } 122 | } 123 | 124 | internal override void Scale(double sc, Action a) { scale = sc; a(); scale = 0; } 125 | 126 | internal override void AltNext(string s) { altop = s; } 127 | internal override void ColNext(Brush b) { col = b; } 128 | internal override void WeightNext(int w) { fontweight = w; } 129 | 130 | internal override void EditBox(string text, Object n) 131 | { 132 | var tb = GUI.TextBox(Top(), text); 133 | SetNode(tb); 134 | tb.PreviewKeyDown += (s, e) => 135 | { 136 | if (e.Key == Key.Enter) 137 | { 138 | if (n is Node) w.Replace(n as Node, tb.Text); 139 | else if (n is NodeType) { (n as NodeType).name = tb.Text; w.TreeChanged(); } 140 | e.Handled = true; 141 | } 142 | }; 143 | lasttextbox = tb; 144 | } 145 | 146 | internal override void StartTopLevel() 147 | { 148 | topgrid = new Grid(); 149 | topgrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); 150 | topgrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); 151 | topgrid.Margin = new Thickness(5); 152 | } 153 | 154 | internal override void SaveFunctionLHS() { lastspf = lastsp; } 155 | 156 | internal override void Function(int i, int depth) 157 | { 158 | lastspf.Margin = new Thickness { Left = depth * 20 }; 159 | topgrid.Children.Add(lastspf); 160 | topgrid.Children.Add(lastsp); 161 | topgrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); 162 | Grid.SetRow(lastspf, i); 163 | Grid.SetRow(lastsp, i); 164 | Grid.SetColumn(lastsp, 1); 165 | } 166 | } 167 | 168 | class CodeRenderText : CodeRender 169 | { 170 | string lhs = ""; 171 | internal string s = ""; 172 | internal string program = ""; 173 | 174 | internal override void Text(string text, int leftm, int rightm) { s += text; } 175 | internal override void EditBox(string text, Object n) { s += text; } 176 | internal override void SaveFunctionLHS() { lhs = s; s = ""; } 177 | internal override void Function(int i, int depth) 178 | { 179 | program += "".PadRight(depth * 4) + lhs + s + "\n"; 180 | lhs = s = ""; 181 | } 182 | 183 | internal override void Scale(double sc, Action a) { a(); } 184 | } 185 | -------------------------------------------------------------------------------- /restructor/Docs/TODO.txt: -------------------------------------------------------------------------------- 1 | - a; b => c; d 2 | requires different prec on left & right for ; 3 | make more normal assignment/scope instead 4 | 5 | can use GenericParameterPosition instead of hashtable 6 | 7 | 8 | - make renames safe 9 | - gotta stop main from being renamed? give it a permanent name / unredef 10 | 11 | - revert => to inline syntax 12 | - deal with functions being added/removed upon editing 13 | 14 | - improve / check side effect analysis 15 | - collect side effects must do every function only once 16 | 17 | - closure implementation: using globals for now, as its simplest? restict function value usage. 18 | - clear sflds, for GC 19 | - actually analyze minimum vars needed to be freevar 20 | 21 | - (1-4)*(1-4) 22 | evaluates two sides of * each, should be shared, even if side effecting (with {}). 23 | this is essentially rule 2b which is not done yet! 24 | 25 | - allow optional type decl on lambdas that are used without a function type context 26 | 27 | - order of eval for apply is function first, make sure that is taken into account for side effects. 28 | 29 | - type tooltips show last variant typechecked 30 | - somehow keep a list of types where needed? or depend on context 31 | 32 | - double click on 33 | - = to edit whole body 34 | - hover over args shows types too 35 | 36 | - can we simplify freevar handling? what if in future they need to be objects? escape? 37 | - once we do side effects, free vars passed along must be by reference 38 | 39 | - divide by 0 checking globally? need global exception handler 40 | - make sure every access to n.clrt checks for null 41 | 42 | - aggressive restruct that removes unused variables and their caller args 43 | 44 | - other operators? 45 | 46 | - fixme's 47 | 48 | - remove/redo Sanity() (very slow) 49 | - more asserts 50 | 51 | - make node use array for memory efficiency? default capacity = 4 52 | first profile what uses memory on a large program 53 | 54 | 55 | "Dispatcher processing has been suspended, but messages are still being processed" in DoDragDrop() 56 | http://social.msdn.microsoft.com/forums/en-US/wpf/thread/d5695ebb-9e68-4ac0-8a09-c68a119e140a 57 | 58 | if we ever want to switch from double to float: 59 | - comments // real 60 | - using real 61 | 62 | 63 | parse/paste/edit / restruct -> validate -> redogui 64 | (validate ->) check err -> restruct / run / save exe 65 | 66 | 67 | 68 | 69 | 70 | 71 | x = v => body // static function call: could be: v |> x => body 72 | x = v // assign: can't be made into: v |> x 73 | x => body // fv 74 | v |> x => body // fv + apply 75 | `body ! // fv + apply: could be: () |> () => body 76 | 77 | x1 = v1 => x2 = v2 => body // multiple static function calls: could be: v1 |> x1 => v2 |> x2 => body 78 | // or also: (x1, x2) = (v1, v2) => body or x1, x2 = v1, v2 => body 79 | (v1, v2) |> (x1, x2) => body or v1, v2 |> x1, x2 => body // fv + apply 80 | 81 | 82 | 83 | 84 | - general case fv must have typed args 85 | - specific case: 86 | v |> fv 87 | v |> hof(fv) 88 | so: an apply passes its arg types to the function.. in the case of a hof, it can pass it on. 89 | if type checking comes across fv arg with no type, and no arg type passed in, that is an error. 90 | -------------------------------------------------------------------------------- /restructor/Game/spaceship.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Game/spaceship.gif -------------------------------------------------------------------------------- /restructor/Game/spaceship.xaml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /restructor/Images/Copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Images/Copy.png -------------------------------------------------------------------------------- /restructor/Images/New.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Images/New.png -------------------------------------------------------------------------------- /restructor/Images/Open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Images/Open.png -------------------------------------------------------------------------------- /restructor/Images/Paste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Images/Paste.png -------------------------------------------------------------------------------- /restructor/Images/Restruct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Images/Restruct.png -------------------------------------------------------------------------------- /restructor/Images/Run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Images/Run.png -------------------------------------------------------------------------------- /restructor/Images/Save As.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Images/Save As.png -------------------------------------------------------------------------------- /restructor/Images/Save Exe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Images/Save Exe.png -------------------------------------------------------------------------------- /restructor/Images/Save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Images/Save.png -------------------------------------------------------------------------------- /restructor/Images/Undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aardappel/restructor/9066622a9631a3130df39d99958c720928340c07/restructor/Images/Undo.png -------------------------------------------------------------------------------- /restructor/Main.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Media; 5 | using System.Windows.Media.Imaging; 6 | using System.Windows.Input; 7 | using System.IO; 8 | using System.Reflection; 9 | 10 | internal class GUI 11 | { 12 | internal static StackPanel StackPanel(bool horiz) 13 | { 14 | return new StackPanel 15 | { 16 | VerticalAlignment = VerticalAlignment.Center, 17 | Orientation = horiz ? Orientation.Horizontal : Orientation.Vertical 18 | }; 19 | } 20 | 21 | internal static TextBlock TextBlock(Panel parent, string s, int leftm = 0, int rightm = 0) 22 | { 23 | var tb = new TextBlock 24 | { 25 | Text = s, 26 | VerticalAlignment = VerticalAlignment.Center, 27 | Padding = new Thickness { Left = leftm, Right = rightm } 28 | }; 29 | if (parent != null) parent.Children.Add(tb); 30 | return tb; 31 | } 32 | 33 | internal static TextBox TextBox(Panel parent, string name, Action act = null) 34 | { 35 | var tb = new TextBox { Text = name, BorderThickness = new Thickness(1.5) }; 36 | if (act != null) tb.TextChanged += (s, e) => act(tb.Text); 37 | if (parent != null) parent.Children.Add(tb); 38 | return tb; 39 | } 40 | 41 | internal static ToolBar ToolBar(ToolBarTray parent) 42 | { 43 | var tb = new ToolBar(); 44 | tb.Background = new SolidColorBrush(Color.FromRgb(0xBC, 0xC7, 0xD8)); 45 | parent.ToolBars.Add(tb); 46 | return tb; 47 | } 48 | 49 | internal static Button Button(MainWin w, ToolBar parent, string name, ICommand cmd, 50 | bool requiressel = false, Action act = null) 51 | { 52 | var tt = new ToolTip(); 53 | tt.Content = name; 54 | var bt = new Button { Content = name, ToolTip = tt }; 55 | var bm = LoadImage(name); 56 | if (bm != null) 57 | { 58 | var im = new Image(); 59 | im.Source = bm; 60 | im.Height = im.Width = 16; 61 | bt.Content = im; 62 | } 63 | bt.Command = cmd; 64 | if (act != null) 65 | { 66 | w.CommandBindings.Add(new CommandBinding(cmd, (s, e) => act(), 67 | (s, e) => e.CanExecute = !requiressel || 68 | w.sel.selected != null)); 69 | } 70 | //bt.Click += (s, e) => act(); 71 | if(!bt.IsEnabled) bt.Opacity = 0.5f; 72 | bt.IsEnabledChanged += (s, e) => bt.Opacity = (bool)e.NewValue ? 1.0f : 0.5f; 73 | if (parent != null) parent.Items.Add(bt); 74 | return bt; 75 | 76 | //KeyBinding OpenCmdKeyBinding = 77 | // new KeyBinding(ApplicationCommands.Copy, Key.C, ModifierKeys.Control); 78 | //this.InputBindings.Add(OpenCmdKeyBinding); 79 | } 80 | 81 | internal static Separator TBSep() 82 | { 83 | var sep = new Separator(); 84 | sep.SnapsToDevicePixels = true; 85 | sep.Background = Brushes.DarkGray; 86 | return sep; 87 | } 88 | 89 | internal static BitmapImage LoadImage(string name) 90 | { 91 | try 92 | { 93 | return new BitmapImage(new Uri("pack://application:,,,/Images/" + name + ".png")); 94 | } 95 | catch 96 | { 97 | return null; 98 | } 99 | } 100 | } 101 | 102 | internal class RsCommand : ICommand 103 | { 104 | Action act; 105 | public RsCommand(Action _a) { act = _a; } 106 | public bool CanExecute(object p) { return true; } 107 | public void Execute(object p) 108 | { 109 | Mouse.OverrideCursor = Cursors.Wait; 110 | act(); 111 | Mouse.OverrideCursor = null; 112 | } 113 | public event EventHandler CanExecuteChanged { add { } remove { } } 114 | } 115 | 116 | /* 117 | internal class RWin : Window 118 | { 119 | void RenderSprite(Canvas view, Canvas sp, double scale, double x, double y) 120 | { 121 | sp.LayoutTransform = new ScaleTransform(scale, scale); 122 | view.Children.Add(sp); 123 | Canvas.SetTop(sp, x-sp.Width*scale/2); 124 | Canvas.SetLeft(sp, y-sp.Height*scale/2); 125 | } 126 | 127 | internal RWin() 128 | { 129 | Title = "test"; 130 | var ship = (Canvas)System.Windows.Markup.XamlReader.Load( 131 | new System.IO.StreamReader("..\\..\\Game\\spaceship.xaml").BaseStream); 132 | 133 | var view = new Canvas(); 134 | RenderSprite(view, ship, 0.1, 100, 100); 135 | //RenderSprite(view, ship, 0.1, 100, 200); 136 | Content = view; 137 | } 138 | } 139 | */ 140 | 141 | internal class MainWin : Window 142 | { 143 | internal Program prog = new Program(); 144 | internal Selection sel; 145 | 146 | double scale = 1.2; 147 | 148 | DockPanel dp = null; 149 | FrameworkElement lastsp = null; 150 | 151 | Point dragstartpoint; 152 | internal Node dragnode = null; // for reference only, actual data is text 153 | 154 | void SetScale() { dp.LayoutTransform = new ScaleTransform(scale, scale); } 155 | 156 | string LoadFile(string fn) 157 | { 158 | try 159 | { 160 | var streamReader = new StreamReader(fn, System.Text.Encoding.ASCII); 161 | var text = streamReader.ReadToEnd(); 162 | streamReader.Close(); 163 | return text; 164 | } 165 | catch 166 | { 167 | return ""; 168 | } 169 | } 170 | 171 | void TestCase() 172 | { 173 | /* 174 | var src = 175 | "2+4*3-6+1*9-2+4*3-6+1*9-5+7*6*8-2+4*3-6+1*9-5+2+4*3-6+1*9-5+7*6*8-7*6*8-7+7*2+4*" + 176 | "3-2+4*3-6+2+4*3-6+1*9-5+2+4*3-6+1*9-2+4*3-6+1*9-2+4*3-6+1*9-5+7*6*8-2+4*3-6+1*9-" + 177 | "5+2+4*3-6+1*9-5+7*6*8-7*6*8-7+7*2+4*3-2+4*3-6+2+4*2+4*3-6+1*9-2+4*3-6+1*9-5+7*6*" + 178 | "8-2+4*3-6+1*9-5+2+4*3-6+1*9-5+7*6*8-7*6*8-7+7*2+4*3-2+4*3-6+2+4*3-6+1*9-5+2+4*3-" + 179 | "6+1*9-5+7*6*8-7*2+4*3-6+1*9-5+7*6*8-7*8-2+4*3-6+1*9-2+4*3-6+1*9-5+7*6*8-2+4*3-6+" + 180 | "1*9-5+2+4*3-6+1*9-5+7*6*8-7*6*8-7+7*2+4*3-2+4*3-6+2+4*3-6+1*9-5+2+4*3-6+1*9-5+7*" + 181 | "6*8-7*2+4*3-6+1*9-5+7*2+4*3-6+1*9-2+4*3-6+1*9-5+7*6*8-2+4*3-6+1*9-5+2+4*3-6+1*9-" + 182 | "5+7*6*8-7*6*8-7+7*2+4*3-2+4*3-6+2+4*3-6+1*9-5+2+4*3-2+4*3-6+1*9-2+4*3-6+1*9-5+7*" + 183 | "6*8-2+4*3-6+1*9-5+2+4*3-6+1*9-5+7*6*8-7*6*8-7+7*2+4*3-2+4*3-6+2+4*3-6+1*9-5+2+4*" + 184 | "3-6+1*9-5+7*6*8-7*2+4*3-6+1*9-5+7*6*8-7*8-7*9-5+2+4*3-6+1*9-5+7*6*8-7*6*8-7+1*9-" + 185 | "5+7*6*8-7*8-7+1*9-5+7*6*8-7*2+4*3-6+1*9-5+7*6*8-7*8-7*9-5+2+4*3-6+1*9-5+7*6*8-7*" + 186 | "6*8-7+1*9-5+7*6*8-7*8-7*8-7*8-7*9-5+2+4*3-6+1*9-5+7*6*8-7*6*8-7+1*9-5+7*6*8-7*8-" + 187 | "7*9-5+2+4*3-6+1*9-5+7*6*8-7*6*8-7+1*9-5+7*6*8-7*8-7-6+1*9-5+2+4*3-6+1*9-5+7*6*8-" + 188 | "7*2+4*3-6+1*9-5+7*6*8-7*8-7*9-5+2+4*3-6+1*9-5+7*6*8-7*6*8-7+1*9-5+7*6*8-7*8-7+7*" + 189 | "6*8-7*2+4*3-6+1*9-5+7*6*8-7*8-7*9-5+2+4*3-6+1*9-5+7*6*8-7*6*8-7+1*9-5+7*6*8-7*8-7"; 190 | */ 191 | 192 | //var src = "((1-3)+(1-4))*((2-7)+(2-6))"; // simple free vars 193 | 194 | var src = "game(\"test\", time => 0, time => rendersprite(\"spaceship.gif\", 50, 50, 0.5," + 195 | " time)); (v = 111 => v*v; v = 6; v = 7; \"list: \" + ([1, 2, 3] |> map(x =>" + 196 | " x*x) |> reduce(0, (a, x) => a+x)) + \" then \" + sqrt(10) + \" and \" +" + 197 | " (((1!=0)-3)+(1-atoi(\"4\")))*((2-atoi(getargs(0)))+(2+-v)))"; 198 | 199 | //var src = "v = 111 => v*v; v = 6; v = 7; sqrt(10) + \" and \" + (((1!=0)-3)+" + 200 | // "(1-atoi(\"4\")))*((2-atoi(getargs(0)))+(2+-v))"; 201 | 202 | //var src = "x = 2 => ((x = 3) + x) * ((x = 5) + 4)"; 203 | 204 | //var src = "sqrt(1+2+3)+\"a\"+\"b\""; 205 | 206 | //var src = "generictest(16)"; 207 | 208 | // FIXME: code executed twice... should be into arg if no side effects 209 | //var src = "(1-4)*(1-4)"; 210 | 211 | // FIXME: inner x not recognized. 212 | //var src = "x = 0 => (y = 0 => x = x + y; y) + (y = 0 => x = x - y; y)"; 213 | 214 | //var src = LoadFile("test.txt"); 215 | 216 | prog.mf.root = new Parser(src, prog).Parse(prog.mf); 217 | 218 | prog.Validate(); 219 | prog.Restructor(); 220 | } 221 | 222 | internal void TreeChanged() 223 | { 224 | sel.DeSelect(); 225 | 226 | prog.Validate(); 227 | 228 | if(lastsp!=null) dp.Children.Remove(lastsp); 229 | var cr = new CodeRenderGUI(this); 230 | prog.RenderCode(cr); 231 | dp.Children.Add(lastsp = cr.topgrid); 232 | DockPanel.SetDock(lastsp, Dock.Bottom); 233 | 234 | SetScale(); 235 | 236 | GC.Collect(); 237 | } 238 | 239 | internal void Replace(Node n, string s) 240 | { 241 | n.CopyOver(new Parser(s, prog).Parse(prog.FindNodeParent(n))); 242 | TreeChanged(); 243 | } 244 | 245 | internal MainWin() 246 | { 247 | sel = new Selection(this); 248 | 249 | TestCase(); 250 | 251 | Title = "Restructor"; 252 | FontFamily = new FontFamily("Consolas"); 253 | 254 | dp = new DockPanel(); 255 | 256 | var tbs = new ToolBarTray(); 257 | tbs.Background = new SolidColorBrush(Color.FromRgb(0xA4, 0xB3, 0xC8)); 258 | dp.Children.Add(tbs); 259 | DockPanel.SetDock(tbs, Dock.Top); 260 | 261 | var tb = GUI.ToolBar(tbs); 262 | var res = GUI.TextBox(null, "Ready."); 263 | GUI.Button(this, tb, "New", ApplicationCommands.New, false, 264 | () => { prog.New(); TreeChanged(); }); 265 | GUI.Button(this, tb, "Open", ApplicationCommands.Open, false, 266 | () => MessageBox.Show("Not Implimented: Open")); 267 | GUI.Button(this, tb, "Save", ApplicationCommands.Save, false, () => 268 | { 269 | var cr = new CodeRenderText(); 270 | prog.RenderCode(cr); 271 | var sw = new StreamWriter("text.restruct"); 272 | sw.Write(cr.program); 273 | sw.Close(); 274 | res.Text = "Saved Restructor Code."; 275 | }); 276 | GUI.Button(this, tb, "Save As", ApplicationCommands.SaveAs, false, 277 | () => MessageBox.Show("Not Implimented: SaveAs")); 278 | tb.Items.Add(GUI.TBSep()); 279 | GUI.Button(this, tb, "Copy", ApplicationCommands.Copy, true, 280 | () => Clipboard.SetText(sel.selected.ToString())); 281 | GUI.Button(this, tb, "Paste", ApplicationCommands.Paste, true, 282 | () => { if (sel.selected is Node) Replace(sel.selected as Node, Clipboard.GetText()); }); 283 | GUI.Button(this, tb, "Undo", ApplicationCommands.Undo, false, 284 | () => MessageBox.Show("Not Implimented: Undo")); 285 | tb.Items.Add(GUI.TBSep()); 286 | GUI.Button(this, tb, "Restruct", new RsCommand(() => 287 | { 288 | res.Text = prog.Restructor(); 289 | TreeChanged(); 290 | })); 291 | GUI.Button(this, tb, "Run", new RsCommand( 292 | () => { res.Text = prog.GenCode(true, false); GC.Collect(); })); 293 | GUI.Button(this, tb, "Save Exe", new RsCommand( 294 | () => { res.Text = prog.GenCode(false, false); GC.Collect(); })); 295 | GUI.Button(this, tb, "DBG", new RsCommand( 296 | () => { res.Text = prog.GenCode(true, true); GC.Collect(); })); 297 | GUI.Button(this, tb, "DBGS", new RsCommand( 298 | () => { res.Text = prog.GenCode(false, true); GC.Collect(); })); 299 | 300 | var tb2 = GUI.ToolBar(tbs); 301 | FocusManager.SetIsFocusScope(tb2, false); 302 | var tbsp = GUI.StackPanel(true); 303 | GUI.TextBox(tbsp, "", s => Runtime.__commandlineargs = s.Split(' ')); 304 | tbsp.Children.Add(res); 305 | tb2.Items.Add(tbsp); 306 | 307 | foreach (DependencyObject a in tb.Items) ToolBar.SetOverflowMode(a, OverflowMode.Never); 308 | foreach (DependencyObject a in tb2.Items) ToolBar.SetOverflowMode(a, OverflowMode.Never); 309 | 310 | TreeChanged(); 311 | Content = dp; 312 | 313 | MouseWheel += (s, e) => 314 | { 315 | scale *= 1.0 + e.Delta / 600.0; 316 | if(scale>2) scale = 2; 317 | if(scale<1) scale = 1; 318 | SetScale(); 319 | }; 320 | 321 | MouseLeftButtonDown += (s, e) => 322 | { 323 | if (e.ClickCount > 1) sel.SetEditMode(); 324 | else sel.Selected(); 325 | }; 326 | 327 | PreviewMouseLeftButtonDown += (s, e) => 328 | { 329 | sel.ReSelectEditBox(); 330 | dragstartpoint = Mouse.GetPosition(this); 331 | }; 332 | 333 | MouseMove += (s, e) => 334 | { 335 | sel.HoverMove(e.GetPosition(this)); 336 | 337 | if (e.LeftButton == MouseButtonState.Pressed && dragnode==null) 338 | { 339 | Vector diff = dragstartpoint - Mouse.GetPosition(this); 340 | 341 | if ((Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance || 342 | Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance) && 343 | sel.selected != null) 344 | { 345 | var seln = sel.selected as Node; 346 | if (seln != null && !(seln.t is Unparsed)) 347 | { 348 | dragnode = seln; 349 | DataObject data = 350 | new DataObject(DataFormats.UnicodeText, sel.selected.ToString()); 351 | DragDrop.DoDragDrop(sel.selectedui, data, DragDropEffects.Copy); 352 | dragnode = null; 353 | } 354 | } 355 | } 356 | }; 357 | 358 | DragOver += (s, e) => 359 | { 360 | sel.HoverMove(e.GetPosition(this)); 361 | if(sel.hover == dragnode) e.Effects = DragDropEffects.None; 362 | }; 363 | 364 | Drop += (s, e) => 365 | { 366 | if (e.Data.GetDataPresent(DataFormats.UnicodeText)) 367 | { 368 | if(sel.hover!=null && sel.hover is Node) 369 | Replace(sel.hover as Node, e.Data.GetData(DataFormats.UnicodeText) as string); 370 | } 371 | }; 372 | 373 | KeyDown += (s, e) => 374 | { 375 | switch (e.Key) 376 | { 377 | default: 378 | sel.HandleKey(e.Key); 379 | break; 380 | } 381 | }; 382 | 383 | TextInput += (s, e) => 384 | sel.SetEditMode(e.Text); 385 | } 386 | } 387 | 388 | internal class ReApp : Application 389 | { 390 | [STAThread] 391 | internal static void Main() 392 | { 393 | (new ReApp()).Run(new MainWin()); 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /restructor/Node.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Reflection.Emit; 5 | 6 | class Node 7 | { 8 | List l = null; 9 | internal NodeType t = null; 10 | internal string err = null; 11 | 12 | internal Type clrt = null; 13 | 14 | internal bool refactorused = false; 15 | 16 | internal Node(NodeType _t) { t = _t; } 17 | internal Node(NodeType _t, Node a) : this(_t) { Add(a); } 18 | internal Node(NodeType _t, Node a, Node b) : this(_t, a) { Add(b); } 19 | 20 | internal int Arity() { return l == null ? 0 : l.Count; } 21 | internal Node At(int i) { Sanity(); return l[i]; } 22 | internal void Insert(int i, Node n) { l.Insert(i, n); } 23 | internal void Remove(int pos) { l.RemoveAt(pos); } 24 | 25 | internal bool Exists(Predicate fun) 26 | { 27 | if (l != null) foreach (var n in l) if(fun(n)) return true; 28 | return false; 29 | } 30 | 31 | internal bool Exists(Func fun) 32 | { 33 | var i = 0; 34 | if (l != null) foreach (var n in l) if (fun(n, i++)) return true; 35 | return false; 36 | } 37 | 38 | internal void ForEach(Action fun) 39 | { 40 | if (l != null) foreach (var n in l) fun(n); 41 | } 42 | 43 | internal void ForEach(Action fun) 44 | { 45 | var i = 0; 46 | if (l != null) foreach (var n in l) fun(n, i++); 47 | } 48 | 49 | internal bool ExistsNodeFun(PredicateNodeFun fun, Function f) 50 | { 51 | return fun(this, f) || (l != null && l.Exists(n => n.ExistsNodeFun(fun, f))); 52 | } 53 | 54 | internal bool ExistsNode(Predicate fun) 55 | { 56 | return fun(this) || (l != null && l.Exists(n => n.ExistsNode(fun))); 57 | } 58 | 59 | internal void ForAllNodes(Action fun) 60 | { 61 | fun(this); 62 | ForEach(n => n.ForAllNodes(fun)); 63 | } 64 | 65 | internal void IfFunction(Action f) 66 | { 67 | var ft = t as Function; 68 | if (ft!=null) f(ft); 69 | } 70 | 71 | internal void IfBuiltin(Action f) 72 | { 73 | var bi = t as Builtin; 74 | if (bi!=null) f(bi); 75 | } 76 | 77 | internal void IfPlaceHolder(Action f) 78 | { 79 | var ph = t as PlaceHolder; 80 | if (ph!=null) f(ph); 81 | } 82 | 83 | internal void Sanity() 84 | { 85 | /* FIXME: remove*/ 86 | Debug.Assert(!(t is Function) || 87 | ((Function)t).undeclared || 88 | (l != null && l.Count == ((Function)t).args.Count) || 89 | (l == null && 0 == ((Function)t).args.Count)); 90 | } 91 | 92 | internal bool Eq(Node o) 93 | { 94 | var i = 0; 95 | return t == o.t && Arity()==o.Arity() && (Arity()==0 || l.TrueForAll(n => n.Eq(o.At(i++)))); 96 | } 97 | 98 | internal void Add(Node a) 99 | { 100 | (l ?? (l = new List())).Add(a); 101 | } 102 | 103 | internal void CopyOver(Node o) 104 | { 105 | l = o.l; 106 | t = o.t; 107 | err = o.err; 108 | clrt = o.clrt; 109 | Sanity(); 110 | } 111 | 112 | internal void Register(Dictionary rd, Function f) 113 | { 114 | Sanity(); 115 | refactorused = false; 116 | ForEach((n, i) => 117 | { 118 | var tn = new TwoNode { a = this, b = n, domf = f, pos = i }; 119 | SameNodeSet sns; 120 | if (!rd.TryGetValue(tn, out sns)) rd[tn] = sns = new SameNodeSet(); 121 | sns.l.Add(tn); 122 | 123 | n.Register(rd, f); 124 | }); 125 | } 126 | 127 | public override string ToString() 128 | { 129 | var cr = new CodeRenderText(); 130 | RenderCode(cr, null); 131 | return cr.s; 132 | } 133 | 134 | internal void ConvertToUnparsed() 135 | { 136 | var u = new Unparsed(); 137 | u.name = ToString(); 138 | l = null; 139 | t = u; 140 | } 141 | 142 | internal void RenderCodeChildren(CodeRender cr, string lb = "(", string rb = ")", int start = 0) 143 | { 144 | cr.Text(lb, 0, Program.bracketspacing); 145 | if (l != null) for (var i = start; i n.clrt != variants.caller.At(i).clrt)) 183 | return FindVariant(variants.next); 184 | return variants; 185 | } 186 | 187 | internal void CollectSideEffects(Dictionary sedb) 188 | { 189 | ForEach(n => n.CollectSideEffects(sedb)); 190 | t.CollectSideEffects(sedb, this); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /restructor/NodeTypes/Builtin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Reflection.Emit; 5 | 6 | class Builtin : NodeType 7 | { 8 | internal MethodInfo clrf = null; 9 | 10 | internal override void RenderCode(CodeRender cr, Node n, Node nparent) 11 | { 12 | base.RenderCode(cr, n, nparent); 13 | n.RenderCodeChildren(cr); 14 | } 15 | 16 | internal override Type GenCode(ILGenerator g, Function f, Node n, Type requested) 17 | { 18 | Dictionary bindings = null; 19 | n.ForEach((cn, i) => 20 | { 21 | var gent = clrf.GetParameters()[i].ParameterType; 22 | BindGPs(gent, cn.clrt, f, ref bindings, false); 23 | cn.GenCode(g, f, gent); 24 | }); 25 | if (bindings != null) 26 | { 27 | var gas = clrf.GetGenericArguments(); 28 | var i = 0; 29 | foreach (var ga in gas) gas[i++] = bindings[ga.Name]; 30 | var bound = clrf.MakeGenericMethod(gas); 31 | g.Emit(OpCodes.Call, bound); 32 | } 33 | else 34 | { 35 | g.Emit(OpCodes.Call, clrf); 36 | } 37 | return n.clrt; 38 | } 39 | 40 | internal static void BindGPs(Type at, Type ct, Function f, 41 | ref Dictionary bindings, bool funcisequaltoaction) 42 | { 43 | if (at.IsGenericParameter && !ct.IsGenericParameter) 44 | { 45 | if (!(bindings ?? (bindings = new Dictionary())).ContainsKey(at.Name)) 46 | bindings[at.Name] = ct; 47 | } 48 | else if (at.IsGenericType && ct.IsGenericType) 49 | { 50 | var atas = at.GetGenericArguments(); 51 | var ctas = ct.GetGenericArguments(); 52 | var atn = atas.Length; 53 | var ctn = ctas.Length; 54 | 55 | if (funcisequaltoaction) 56 | { 57 | atn = NumFuncArgs(at, atn); 58 | ctn = NumFuncArgs(ct, ctn); 59 | if (atn != ctn || (atn < 0 && at.Name != ct.Name)) return; 60 | if (atn < 0) atn = atas.Length; 61 | } 62 | else 63 | { 64 | if (atn != ctn || at.Name != ct.Name) return; 65 | } 66 | 67 | for (var j = 0; j bindings) 75 | { 76 | if (bindings != null) 77 | { 78 | if (t.IsGenericParameter) 79 | { 80 | Type bt; 81 | if (bindings.TryGetValue(t.Name, out bt)) return bt; 82 | } 83 | else if (t.IsGenericType) 84 | { 85 | var gas = t.GetGenericArguments(); 86 | var j = 0; 87 | foreach (var ga in gas) gas[j++] = ReplaceGPs(ga, bindings); 88 | return t.GetGenericTypeDefinition().MakeGenericType(gas); 89 | } 90 | } 91 | return t; 92 | } 93 | 94 | internal override Type TypeCheck(Node n, Function f, Type expected) 95 | { 96 | Dictionary bindings = null; 97 | 98 | if (expected != null) 99 | { 100 | BindGPs(clrf.ReturnType, expected, f, ref bindings, true); 101 | } 102 | 103 | if (ArgCheck(n, clrf.GetParameters().Length)) 104 | { 105 | var i = 0; 106 | foreach (var pi in clrf.GetParameters()) 107 | { 108 | var cn = n.At(i++); 109 | var pt = ReplaceGPs(pi.ParameterType, bindings); 110 | cn.TypeCheck(f, pt); 111 | if (cn.clrt != null) 112 | { 113 | BindGPs(pt, cn.clrt, f, ref bindings, false); 114 | pt = ReplaceGPs(pt, bindings); // TODO: bit unoptimal 115 | SubType(cn, pt); 116 | } 117 | } 118 | } 119 | 120 | return ReplaceGPs(clrf.ReturnType, bindings); 121 | } 122 | 123 | internal override void CollectSideEffects(Dictionary sedb, Node n) 124 | { 125 | // TODO: put side effect attributes to builtins, and check them here 126 | } 127 | } 128 | 129 | -------------------------------------------------------------------------------- /restructor/NodeTypes/BuiltinOp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Reflection.Emit; 5 | using System.Runtime.InteropServices; 6 | using System.Diagnostics; 7 | using real = System.Double; 8 | 9 | class BuiltinOp : NodeType 10 | { 11 | internal int precedence = 0; 12 | internal int flags = 0; 13 | 14 | internal const int no_precedence = 1; 15 | internal const int right_assoc = 2; 16 | internal const int unary_only = 4; 17 | internal const int unary_and_binary = 8; 18 | internal const int int_result = 16; 19 | internal const int int_to_int = 32; 20 | internal const int is_add = 64; 21 | 22 | bool NeedsParens(Node p, Node n) 23 | { 24 | if (n.Arity() != 2 || p == null) return false; 25 | var pop = p.t as BuiltinOp; 26 | return (pop != null && 27 | (pop.flags & no_precedence) == 0 && 28 | ( 29 | pop.precedence > precedence || // parent op has higher prec: (A+B)*C 30 | p.Arity() == 1 || // parent is unary: -(A+B) 31 | ( 32 | // we are opposite-factored on same prec: C-(A+B) 33 | pop.precedence == precedence && 34 | ((pop.flags & right_assoc) != 0 ? p.At(0) : p.At(1)) == n 35 | ) 36 | )); 37 | } 38 | 39 | internal override void RenderCode(CodeRender cr, Node n, Node nparent) 40 | { 41 | const double operatorscale = 1.3; 42 | string altop = null; 43 | 44 | switch (name) 45 | { 46 | case "!=": altop = "\u2260"; break; 47 | case "<=": altop = "\u2264"; break; 48 | case ">=": altop = "\u2265"; break; 49 | case "==": altop = "\u2A75"; break; 50 | case "*": altop = "\u2A2F"; break; 51 | case "/": altop = "\u00F7"; break; 52 | case "!": altop = "\u00AC"; break; 53 | case "|>": altop = "\u22B3"; break; 54 | } 55 | 56 | if (NeedsParens(nparent, n)) cr.Text("(", 0, Program.bracketspacing); 57 | 58 | if (name == "__apply") 59 | { 60 | n.At(0).RenderCode(cr, n); 61 | cr.Scale(operatorscale, () => cr.Text("!", 0, 0)); 62 | if (n.Arity() > 1) n.RenderCodeChildren(cr, "(", ")", 1); 63 | } 64 | else if (name == "|>") 65 | { 66 | n.At(1).RenderCode(cr, n); 67 | cr.AltNext(altop); 68 | cr.Scale(operatorscale, 69 | () => cr.Text("|>", Program.operatorspacing, Program.operatorspacing)); 70 | n.At(0).RenderCode(cr, n); 71 | } 72 | else if (name == "__list") 73 | { 74 | n.RenderCodeChildren(cr, "[", "]"); 75 | } 76 | else if (n.Arity() == 1) 77 | { 78 | cr.AltNext(altop); 79 | cr.Scale(operatorscale, 80 | () => cr.Text(name, Program.operatorspacing, Program.operatorspacing)); 81 | n.At(0).RenderCode(cr, n); 82 | } 83 | else if (n.Arity() == 2) 84 | { 85 | n.At(name == "=" ? 1 : 0).RenderCode(cr, n); 86 | cr.AltNext(altop); 87 | cr.Scale(operatorscale, 88 | () => cr.Text(name, Program.operatorspacing, Program.operatorspacing)); 89 | n.At(name == "=" ? 0 : 1).RenderCode(cr, n); 90 | } 91 | else Debug.Assert(false); 92 | 93 | if (NeedsParens(nparent, n)) cr.Text(")", Program.bracketspacing, 0); 94 | } 95 | 96 | internal override Type GenCode(ILGenerator g, Function f, Node n, Type requested) 97 | { 98 | switch (name) 99 | { 100 | case ";": 101 | n.At(0).GenCode(g, f, typeof(void)); 102 | n.At(1).GenCode(g, f, null); 103 | break; 104 | 105 | case "=": 106 | var ph = n.At(1).t as PlaceHolder; 107 | n.At(0).GenCode(g, f, ph.clrt); 108 | if (requested != typeof(void)) g.Emit(OpCodes.Dup); 109 | if (ph.isfreevar) g.Emit(OpCodes.Stsfld, ph.curfb); 110 | else g.Emit(OpCodes.Starg, (short)ph.GetCLRArg(f)); 111 | if (requested == typeof(void)) return requested; 112 | break; 113 | 114 | case "|>": 115 | case "__apply": 116 | n.At(0).GenCode(g, f, null); 117 | var type = n.At(0).clrt; 118 | var targs = type.GetGenericArguments(); 119 | for(var i = 1; i 126 | { 127 | g.Emit(OpCodes.Dup); 128 | cn.GenCode(g, f, n.clrt.GetGenericArguments()[0]); 129 | g.Emit(OpCodes.Call, n.clrt.GetMethod("Add")); 130 | }); 131 | break; 132 | 133 | default: 134 | n.ForEach(cn => cn.GenCode(g, f, (flags & int_result) != 0 ? typeof(int) : n.clrt)); 135 | switch (name) 136 | { 137 | case "+": if (n.clrt != typeof(string)) g.Emit(OpCodes.Add); 138 | else g.Emit(OpCodes.Call, typeof(Runtime).GetMethod("__concat")); 139 | break; 140 | case "-": g.Emit(n.Arity() == 2 ? OpCodes.Sub : OpCodes.Neg); 141 | break; 142 | case "*": g.Emit(OpCodes.Mul); 143 | break; 144 | case "/": g.Emit(OpCodes.Div); 145 | break; 146 | case "==": g.Emit(OpCodes.Ceq); 147 | break; 148 | case "!=": g.Emit(OpCodes.Ceq); 149 | g.Emit(OpCodes.Ldc_I4_0); 150 | g.Emit(OpCodes.Ceq); 151 | // TODO: reorg args instead, and/or generate branch 152 | break; 153 | case "<": g.Emit(OpCodes.Clt); 154 | break; 155 | case "<=": g.Emit(OpCodes.Cgt); 156 | g.Emit(OpCodes.Ldc_I4_0); 157 | g.Emit(OpCodes.Ceq); 158 | break; 159 | case ">": g.Emit(OpCodes.Cgt); 160 | break; 161 | case ">=": g.Emit(OpCodes.Clt); 162 | g.Emit(OpCodes.Ldc_I4_0); 163 | g.Emit(OpCodes.Ceq); 164 | break; 165 | case "!": g.Emit(OpCodes.Ldc_I4_0); 166 | g.Emit(OpCodes.Ceq); 167 | break; 168 | default: Debug.Assert(false); 169 | break; 170 | } 171 | break; 172 | } 173 | return n.clrt; 174 | } 175 | 176 | internal override Type TypeCheck(Node n, Function f, Type expected) 177 | { 178 | switch (name) 179 | { 180 | case "|>": 181 | case "__apply": 182 | var ats = new Type[n.Arity() - 1]; 183 | for (var i = 1; i < n.Arity(); i++) 184 | { 185 | n.At(i).TypeCheck(f); 186 | if ((ats[i - 1] = n.At(i).clrt) == null) return null; 187 | } 188 | 189 | Type gt = null; 190 | switch(n.Arity() - 1) 191 | { 192 | case 0: gt = typeof(Action); break; 193 | case 1: gt = typeof(Action<>).MakeGenericType(ats); break; 194 | case 2: gt = typeof(Action<,>).MakeGenericType(ats); break; 195 | case 3: gt = typeof(Action<,,>).MakeGenericType(ats); break; 196 | default: 197 | // TODO: can we do this more elegantly? is bounded by generic return types 198 | // of builtins, so not a big deal 199 | Debug.Assert(false); break; 200 | } 201 | n.At(0).TypeCheck(f, gt); 202 | 203 | var type = n.At(0).clrt; 204 | if (type == null) return null; 205 | 206 | var targs = type.GetGenericArguments(); 207 | var nargs = NumFuncArgs(type, targs.Length); 208 | if (nargs < 0) 209 | { 210 | TypeError(n.At(0), "function value"); 211 | return null; 212 | } 213 | 214 | ArgCheck(n, nargs + 1); 215 | 216 | return nargs==targs.Length ? typeof(void) : targs[targs.Length - 1]; 217 | } 218 | 219 | n.ForEach(cn => cn.TypeCheck(f)); 220 | 221 | switch (name) 222 | { 223 | case ";": 224 | return n.At(1).clrt; 225 | 226 | case "=": 227 | return SubType(n.At(0), (n.At(1).t as PlaceHolder).clrt); 228 | 229 | case "__list": 230 | { 231 | Type ut = null; 232 | n.ForEach(cn => ut = TypeUnion(ut, cn.clrt)); 233 | var lut = typeof(List<>); 234 | return lut.MakeGenericType(new Type[] { ut }); 235 | } 236 | 237 | default: 238 | if ((flags & int_to_int) != 0) 239 | { 240 | return SubType(n.At(0), typeof(int)); 241 | } 242 | else 243 | { 244 | Type ut = null; 245 | if (n.Exists(cn => 246 | { 247 | ut = TypeUnion(ut, cn.clrt); 248 | 249 | // TODO: comparison operators must also work on pointers, by value? etc 250 | if (!IsSubType(cn.clrt, typeof(real))) 251 | { 252 | if ((flags & is_add) != 0) 253 | { 254 | if (!IsSubType(cn.clrt, typeof(string))) 255 | { 256 | TypeError(cn, "numeric or string"); 257 | return true; 258 | } 259 | } 260 | else 261 | { 262 | TypeError(cn, "numeric"); 263 | return true; 264 | } 265 | } 266 | return false; 267 | })) return null; 268 | return ut; 269 | } 270 | } 271 | } 272 | 273 | internal override void CollectSideEffects(Dictionary sedb, Node n) 274 | { 275 | switch (name) 276 | { 277 | case "=": 278 | var se = n.At(1).t.GetSideEffect(sedb); 279 | se.write = true; 280 | break; 281 | } 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /restructor/NodeTypes/Function.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | 7 | delegate bool PredicateNodeFun(Node n, Function f); 8 | 9 | class Variant 10 | { 11 | internal Variant next; 12 | internal Node caller; 13 | 14 | internal MethodBuilder mb = null; 15 | } 16 | 17 | class Function : NodeType 18 | { 19 | internal Node root = null; 20 | internal List args = new List(); 21 | internal List freevars = new List(); 22 | 23 | internal Function parent = null; 24 | List funcs = new List(); 25 | 26 | internal Variant variants = null; 27 | 28 | internal int numcallers = 0; 29 | internal Node lastcaller = null; 30 | internal bool usedasfunctionvalue = false; 31 | 32 | internal bool typechecked = false; 33 | 34 | internal void Add(Function f) 35 | { 36 | funcs.Add(f); 37 | f.parent = this; 38 | } 39 | 40 | internal void ForAllRoots(Action fun) 41 | { 42 | fun(root, this); 43 | foreach (var f in funcs) f.ForAllRoots(fun); 44 | } 45 | 46 | internal void ForAllFuncs(Action fun) 47 | { 48 | fun(this); 49 | foreach (var f in funcs) f.ForAllFuncs(fun); 50 | } 51 | 52 | internal void ForAllNodes(Action fun) 53 | { 54 | ForAllRoots((r, _) => r.ForAllNodes(fun)); 55 | } 56 | 57 | internal Function Find(Predicate fun) { 58 | return fun(this) ? this : funcs.Find(f => f.Find(fun) != null); 59 | } 60 | 61 | internal bool ExistsFun(Predicate fun) { 62 | return fun(this) || funcs.Exists(f => f.ExistsFun(fun)); 63 | } 64 | 65 | internal bool ExistsNode(Predicate fun) 66 | { 67 | return root.ExistsNode(fun) || funcs.Exists(f => f.ExistsNode(fun)); 68 | } 69 | 70 | internal bool ExistsNodeFun(PredicateNodeFun fun) { 71 | return root.ExistsNodeFun(fun, this) || funcs.Exists(f => f.ExistsNodeFun(fun)); 72 | } 73 | 74 | internal void ForEachArg(Action fun) 75 | { 76 | var i = 0; 77 | foreach (var arg in args) fun(arg, i++); 78 | } 79 | 80 | internal bool ExistsArg(Func fun) 81 | { 82 | var i = 0; 83 | foreach (var arg in args) if (fun(arg, i++)) return true; 84 | return false; 85 | } 86 | 87 | internal void RemoveAll(Predicate fun) 88 | { 89 | foreach (var f in funcs) f.RemoveAll(fun); 90 | funcs.RemoveAll(fun); 91 | } 92 | 93 | internal override void RenderCode(CodeRender cr, Node n, Node nparent) 94 | { 95 | base.RenderCode(cr, n, nparent); 96 | if (n == null) return; 97 | n.RenderCodeChildren(cr); 98 | } 99 | 100 | internal bool Inline() 101 | { 102 | if (numcallers > 1 || usedasfunctionvalue || parent == null) return false; 103 | 104 | if (numcallers == 0) 105 | { 106 | Debug.WriteLine("Orphan: " + name + " : " + root); 107 | } 108 | else 109 | { 110 | Debug.Assert(args.Count == lastcaller.Arity()); 111 | foreach (var arg in args) 112 | { 113 | int num = 0; 114 | if (ExistsNode(n => n.t == arg && num++ > 0)) return false; 115 | } 116 | 117 | Debug.WriteLine("Inline " + name + " : " + root); 118 | 119 | foreach (var arg in args) 120 | root.ForAllNodes(n => { 121 | if (n.t == arg) n.CopyOver(lastcaller.At(args.IndexOf(arg))); 122 | }); 123 | ForAllFuncs(f => { foreach (var arg in args) f.freevars.Remove(arg); }); 124 | 125 | lastcaller.CopyOver(root); 126 | } 127 | 128 | CleanUpAfterInline(); 129 | return true; 130 | } 131 | 132 | void CleanUpAfterInline() 133 | { 134 | parent.funcs.Remove(this); 135 | 136 | foreach (var f in funcs) 137 | { 138 | parent.funcs.Add(f); 139 | f.parent = parent; 140 | } 141 | } 142 | 143 | internal override void InlineCount(Node n) 144 | { 145 | Debug.Assert(args != null); 146 | numcallers++; 147 | if (lastcaller != null) Debug.Assert(lastcaller.Arity() == n.Arity()); 148 | lastcaller = n; 149 | } 150 | 151 | internal bool OneOnOneInline(Program prog) 152 | { 153 | if (args.Count != root.Arity() || usedasfunctionvalue) return false; 154 | if (ExistsArg((arg, i) => arg != root.At(i++).t)) return false; 155 | 156 | Debug.WriteLine("OneOnOne " + name + " : " + root); 157 | 158 | prog.mf.ForAllNodes(n => { if (n.t == this) n.t = root.t; }); 159 | 160 | CleanUpAfterInline(); 161 | return true; 162 | } 163 | 164 | internal int Depth() { return parent == null ? 0 : parent.Depth() + 1; } 165 | 166 | void Path(List l) 167 | { 168 | l.Insert(0, this); 169 | if (parent != null) parent.Path(l); 170 | } 171 | 172 | internal Function LCA(Function o) 173 | { 174 | if (o == null) return this; 175 | var la = new List(); Path(la); 176 | var lb = new List(); o.Path(lb); 177 | var n = Math.Min(la.Count, lb.Count); 178 | for (var i = 0; i < n; i++) if (la[i] != lb[i]) return la[i - 1]; 179 | return la[n - 1]; 180 | } 181 | 182 | internal void MergeEqualArgs(List l) 183 | { 184 | for (var i = args.Count - 1; i > 0; i--) for (var j = 0; j < i; j++) 185 | { 186 | if (l.TrueForAll(tn => tn.a.At(i).Eq(tn.a.At(j)))) 187 | { 188 | // does not handle free vars, but the vars we look at are all local. 189 | root.ForAllNodes(n => { if (n.t == args[i]) n.t = args[j]; }); 190 | args.RemoveAt(i); 191 | foreach (var tn in l) tn.a.Remove(i); 192 | break; 193 | } 194 | } 195 | } 196 | 197 | static int numphs = 0; 198 | 199 | internal MethodBuilder GenCodeFun(Type coercebodyto = null) 200 | { 201 | var al = new List(); 202 | foreach (var arg in args) al.Add(arg.clrt); 203 | foreach (var fv in freevars) if (!fv.isfreevar) al.Add(fv.clrt); // TODO: ever the case? 204 | 205 | var mb = Program._cls.DefineMethod( 206 | name, 207 | MethodAttributes.Public | 208 | (parent != null ? MethodAttributes.Static : MethodAttributes.Virtual), 209 | coercebodyto != null ? coercebodyto : root.clrt, 210 | al.ToArray()); 211 | var g = mb.GetILGenerator(); 212 | 213 | ForEachArg((arg, i) => 214 | { 215 | if (arg.isfreevar) 216 | { 217 | arg.curfb = 218 | Program._cls.DefineField(arg.name + "_" + numphs++, 219 | arg.clrt, 220 | FieldAttributes.Static | FieldAttributes.Assembly); 221 | g.Emit(OpCodes.Ldarg, (short)i); 222 | g.Emit(OpCodes.Stsfld, arg.curfb); 223 | } 224 | }); 225 | 226 | root.GenCode(g, this, coercebodyto); 227 | 228 | foreach (var arg in args) arg.curfb = null; 229 | 230 | g.Emit(OpCodes.Ret); 231 | 232 | return mb; 233 | } 234 | 235 | internal void GenCodeVal(ILGenerator g, Type clrt) 236 | { 237 | Debug.Assert(variants.next == null); 238 | g.Emit(OpCodes.Ldnull); 239 | g.Emit(OpCodes.Ldftn, variants.mb ?? (variants.mb = GenCodeFun())); 240 | g.Emit(OpCodes.Newobj, clrt.GetConstructor(new Type[] { typeof(object), typeof(IntPtr) })); 241 | } 242 | 243 | internal override Type GenCode(ILGenerator g, Function f, Node n, Type requested) 244 | { 245 | ForEachArg((arg, i) => 246 | { 247 | Node cn = n.At(i); 248 | // no coerce, as the function is specialized to the type of these exps, and arg.clrt is 249 | // not set to right variant yet 250 | cn.GenCode(g, f, null); 251 | }); 252 | // TODO: will this ever not be the case? 253 | foreach (var fv in freevars) if (!fv.isfreevar) fv.GenCode(g, f, null, null); 254 | 255 | var v = n.FindVariant(variants); 256 | Debug.Assert(v != null); 257 | 258 | if (v.mb == null) 259 | { 260 | // have to redecorate with types for any variant function 261 | // TODO: ideally this recursion would cut off at non-variant functions... though 262 | // variants are likely to be leafs anyway. 263 | // profile if codegen on a large program ever spends much time in typecheck 264 | if (variants.next != null) TypeCheckFun(n); 265 | v.mb = GenCodeFun(); 266 | } 267 | 268 | g.Emit(OpCodes.Call, v.mb); 269 | return n.clrt; 270 | } 271 | 272 | internal void TypeCheckFunMinimal() 273 | { 274 | typechecked = true; 275 | foreach (var f in funcs) f.typechecked = false; 276 | root.TypeCheck(this); 277 | // make sure orphaned functions are decorated 278 | foreach (var f in funcs) if (!f.typechecked) f.TypeCheckFunMinimal(); 279 | } 280 | 281 | internal void TypeCheckFun(Node n) // called from both typecheck and codegen 282 | { 283 | ForEachArg((arg, i) => { arg.clrt = n.At(i).clrt; arg.pherr = null; }); 284 | TypeCheckFunMinimal(); 285 | ForEachArg((arg, i) => { 286 | if (arg.pherr != null) n.At(i).err = "in " + name + "(): " + arg.pherr; 287 | }); 288 | } 289 | 290 | internal override Type TypeCheck(Node n, Function f, Type expected) 291 | { 292 | if (n != null) n.ForEach(cn => cn.TypeCheck(f)); 293 | 294 | if (undeclared) 295 | { 296 | Function uf; 297 | if (f == null || (uf = f.LookupFun(name)) == null) 298 | { 299 | n.err = "unknown function: " + name; 300 | return null; 301 | } 302 | 303 | n.t = uf; 304 | undeclared = false; 305 | return uf.TypeCheck(n, f, expected); 306 | } 307 | 308 | if (n != null && ArgCheck(n, args.Count)) 309 | { 310 | var v = n.FindVariant(variants); 311 | if(v!=null) return v.caller.clrt; 312 | 313 | TypeCheckFun(n); 314 | } 315 | else 316 | { 317 | if (expected != null) // function value 318 | { 319 | // FIXME: also need to check for variant, store list of types instead 320 | var targs = expected.GetGenericArguments(); 321 | var nargs = NumFuncArgs(expected, targs.Length); 322 | // if this fails, it means we're passing a function value to builtin requiring a 323 | // List<> or so, which will give an error higher up 324 | if (nargs < 0) 325 | { 326 | return null; 327 | } 328 | if (nargs != args.Count) 329 | { 330 | // FIXME: error associated with the wrong node 331 | root.err = "function value requires " + nargs + " parameters"; 332 | return null; 333 | } 334 | ForEachArg((arg, i) => 335 | { 336 | arg.clrt = targs[i]; 337 | arg.pherr = null; 338 | }); 339 | TypeCheckFunMinimal(); 340 | foreach (var arg in args) 341 | { 342 | // FIXME!! 343 | if (arg.pherr != null) 344 | { 345 | root.err = "error in body: " + arg.pherr; 346 | return null; 347 | } 348 | } 349 | } 350 | else 351 | { 352 | // either main function, an orphaned function, or wrong #of args, try and typecheck 353 | // as much as we can 354 | TypeCheckFunMinimal(); 355 | } 356 | } 357 | 358 | variants = new Variant { next = variants, caller = n }; 359 | 360 | return root.clrt; 361 | } 362 | 363 | internal Function LookupFun(string name) 364 | { 365 | foreach (var f in funcs) if (f.name == name) return f; 366 | return parent == null ? null : parent.LookupFun(name); 367 | } 368 | 369 | internal PlaceHolder LookupVar(string name) 370 | { 371 | foreach (var arg in args) if (arg.name == name) return arg; 372 | foreach (var fv in freevars) if (fv.name == name) return fv; 373 | return null; 374 | } 375 | 376 | internal void AddFreeVarsFrom(Function of) 377 | { 378 | foreach(var fv in of.freevars) if(!freevars.Contains(fv)) freevars.Add(fv); 379 | } 380 | 381 | internal override void CollectSideEffects(Dictionary sedb, Node n) 382 | { 383 | // FIXME: this is hugely expensive, only do this once per function! 384 | root.CollectSideEffects(sedb); 385 | } 386 | } 387 | 388 | -------------------------------------------------------------------------------- /restructor/NodeTypes/FunctionValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Reflection.Emit; 4 | 5 | class FunctionValue : NodeType 6 | { 7 | public Function reff = null; 8 | 9 | internal override void InlineCount(Node n) { reff.usedasfunctionvalue = true; } 10 | 11 | internal override void RenderCode(CodeRender cr, Node n, Node nparent) 12 | { 13 | cr.Text("`" + (reff != null ? reff.name : name), Program.namespacing, Program.namespacing); 14 | } 15 | 16 | internal override Type TypeCheck(Node n, Function f, Type expected) 17 | { 18 | if (undeclared) 19 | { 20 | if (f == null || (reff = f.LookupFun(name)) == null) 21 | { 22 | n.err = "unknown function: " + name; 23 | return null; 24 | } 25 | 26 | undeclared = false; 27 | } 28 | 29 | reff.TypeCheck(null, f, expected); 30 | if (reff.root.clrt == null) return null; 31 | 32 | if (expected != null) 33 | { 34 | var fas = expected.GetGenericArguments(); 35 | var nargs = NumFuncArgs(expected, fas.Length); 36 | if (nargs < 0) return null; 37 | if (nargs == fas.Length) return expected; 38 | Debug.Assert(nargs == fas.Length - 1); 39 | 40 | fas[fas.Length - 1] = reff.root.clrt; 41 | return expected.GetGenericTypeDefinition().MakeGenericType(fas); 42 | } 43 | else 44 | { 45 | return reff.root.clrt == typeof(void) // FIXME: do these need arg types? 46 | ? typeof(Action) 47 | : typeof(Func<>).MakeGenericType(new Type[] { reff.root.clrt }); 48 | } 49 | 50 | } 51 | 52 | internal override Type GenCode(ILGenerator g, Function f, Node n, Type requested) 53 | { 54 | reff.GenCodeVal(g, n.clrt); 55 | return n.clrt; 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /restructor/NodeTypes/Literals.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Media; 3 | using System.Reflection.Emit; 4 | using real = System.Double; 5 | 6 | class Int : NodeType 7 | { 8 | int i = 0; 9 | 10 | internal Int(int _i) { i = _i; name = i.ToString(); } 11 | 12 | internal override void RenderCode(CodeRender cr, Node n, Node nparent) 13 | { 14 | cr.ColNext(Brushes.Red); cr.Text(name, Program.namespacing, Program.namespacing); 15 | } 16 | 17 | internal override Type GenCode(ILGenerator g, Function f, Node n, Type requested) 18 | { 19 | g.Emit(OpCodes.Ldc_I4, i); 20 | return typeof(int); 21 | } 22 | 23 | internal override Type TypeCheck(Node n, Function f, Type expected) 24 | { 25 | return typeof(int); 26 | } 27 | } 28 | 29 | class Real : NodeType 30 | { 31 | real d = 0; 32 | 33 | internal Real(real _d) { d = _d; name = d.ToString("0.0#################"); } 34 | 35 | internal override void RenderCode(CodeRender cr, Node n, Node nparent) 36 | { 37 | cr.ColNext(Brushes.Red); 38 | cr.Text(name, Program.namespacing, Program.namespacing); 39 | } 40 | 41 | internal override Type GenCode(ILGenerator g, Function f, Node n, Type requested) 42 | { 43 | g.Emit(OpCodes.Ldc_R8, d); 44 | return typeof(real); // real 45 | } 46 | 47 | internal override Type TypeCheck(Node n, Function f, Type expected) { return typeof(real); } 48 | } 49 | 50 | class String : NodeType 51 | { 52 | internal String(string _s) { name = _s; } 53 | 54 | internal override void RenderCode(CodeRender cr, Node n, Node nparent) 55 | { 56 | cr.ColNext(Brushes.Green); 57 | cr.Text("\"" + name + "\"", Program.namespacing, Program.namespacing); 58 | } 59 | 60 | internal override Type GenCode(ILGenerator g, Function f, Node n, Type requested) 61 | { 62 | g.Emit(OpCodes.Ldstr, name); 63 | return typeof(string); 64 | } 65 | 66 | internal override Type TypeCheck(Node n, Function f, Type expected) { return typeof(string); } 67 | } 68 | -------------------------------------------------------------------------------- /restructor/NodeTypes/NodeType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Reflection.Emit; 4 | using System.Collections.Generic; 5 | using real = System.Double; 6 | 7 | abstract class NodeType 8 | { 9 | internal string name = null; 10 | internal bool undeclared = false; 11 | 12 | public override string ToString() { return name; } 13 | 14 | internal virtual void InlineCount(Node n) {} 15 | 16 | internal virtual void RenderCode(CodeRender cr, Node n, Node nparent) 17 | { 18 | cr.Text(name, Program.namespacing, Program.namespacing); 19 | } 20 | 21 | internal virtual Type GenCode(ILGenerator g, Function f, Node n, Type requested) 22 | { 23 | Debug.Assert(false); 24 | return null; 25 | } 26 | 27 | internal abstract Type TypeCheck(Node n, Function f, Type expected); 28 | 29 | internal virtual void CollectSideEffects(Dictionary sedb, Node n) { } 30 | 31 | internal SideEffect GetSideEffect(Dictionary sedb) 32 | { 33 | SideEffect se; 34 | if(sedb.TryGetValue(this, out se)) return se; 35 | se = new SideEffect { id = this, write = false }; 36 | sedb[this] = se; 37 | return se; 38 | } 39 | 40 | static internal string NiceGenericType(string s, Type[] gas, string pre, string post) 41 | { 42 | s += pre; 43 | var j = 0; 44 | foreach (var ga in gas) 45 | { 46 | s += NiceType(ga); 47 | j++; 48 | if (j != gas.Length) s += ","; 49 | } 50 | return s + post; 51 | } 52 | 53 | static internal string NiceType(Type t) 54 | { 55 | if (t == typeof(int)) return "int"; 56 | if (t == typeof(real)) return "real"; 57 | if (t == typeof(bool)) return "bool"; 58 | if (t == typeof(string)) return "string"; 59 | if (t == typeof(object)) return "object"; 60 | if (t == typeof(void)) return "void"; 61 | 62 | if (t.IsGenericType) 63 | { 64 | var gas = t.GetGenericArguments(); 65 | var s = t.Name; 66 | var cut = s.IndexOf('`'); 67 | if (cut >= 0) s = s.Substring(0, cut); 68 | switch (s) 69 | { 70 | case "Func": 71 | var post = " => " + NiceType(gas[gas.Length - 1]); 72 | Array.Resize(ref gas, gas.Length - 1); 73 | if (gas.Length == 1) return NiceGenericType("", gas, "", post); 74 | else return NiceGenericType("", gas, "(", ")" + post); 75 | 76 | case "List": return NiceGenericType("", gas, "[", "]"); 77 | default: return NiceGenericType(s, gas, "<", ">"); 78 | } 79 | } 80 | 81 | return t.ToString(); 82 | } 83 | 84 | internal static int NumFuncArgs(Type t, int nargs) // TODO: can this be done more efficiently? 85 | { 86 | if (nargs >= 1) 87 | { 88 | if (t.Name == "Func`" + nargs) return nargs - 1; 89 | if (t.Name == "Action`" + nargs) return nargs; 90 | } 91 | else if (t.Name == "Action") return 0; 92 | return -1; 93 | } 94 | 95 | static internal bool IsSubType(Type sub, Type super) 96 | { 97 | return super == null // null means error elsewhere... don't cascade 98 | || sub == null 99 | || sub == super 100 | || super == typeof(object) 101 | || super == typeof(void) 102 | || (sub == typeof(int) && super == typeof(real)) 103 | || (sub == typeof(int) && super == typeof(string)) 104 | || (sub == typeof(real) && super == typeof(string)) 105 | ; 106 | } 107 | 108 | internal void TypeError(Node n, string needed) 109 | { 110 | var err = name + " expects a " + needed + " type (" + NiceType(n.clrt) + " given)"; 111 | var ph = n.t as PlaceHolder; 112 | if (ph != null) ph.pherr = err; 113 | else n.err = err; 114 | } 115 | 116 | internal Type SubType(Node n, Type super) 117 | { 118 | if (!IsSubType(n.clrt, super)) TypeError(n, NiceType(super)); 119 | return super; 120 | } 121 | 122 | static internal Type TypeUnion(Type a, Type b) 123 | { 124 | if (a == b) return a; 125 | 126 | if (a == null) return b; 127 | if (b == null) return a; 128 | 129 | if (a == typeof(void)) return a; 130 | if (b == typeof(void)) return b; 131 | 132 | if (a == typeof(real) && b == typeof(int)) return a; 133 | if (b == typeof(real) && a == typeof(int)) return b; 134 | 135 | if (a == typeof(string) && b == typeof(int)) return a; 136 | if (a == typeof(string) && b == typeof(real)) return a; 137 | if (b == typeof(string) && a == typeof(int)) return b; 138 | if (b == typeof(string) && a == typeof(real)) return b; 139 | 140 | return typeof(object); 141 | } 142 | /* 143 | static internal Type TypeIntersect(Type a, Type b) 144 | { 145 | if (a == typeof(void) || b == typeof(void)) return null; 146 | 147 | if (a == b) return a; 148 | 149 | if (a == typeof(object)) return b; 150 | if (b == typeof(object)) return a; 151 | 152 | if (a == typeof(real) && b == typeof(int)) return b; 153 | if (b == typeof(real) && a == typeof(int)) return a; 154 | 155 | if (a == typeof(string) && b == typeof(int)) return b; 156 | if (a == typeof(string) && b == typeof(real)) return b; 157 | 158 | return null; 159 | } 160 | */ 161 | static internal void Coerce(Type from, Type to, ILGenerator g) 162 | { 163 | if (to == null || from == to || to.IsGenericParameter) 164 | { 165 | return; 166 | } 167 | else if (from == typeof(int) && to == typeof(real)) 168 | { 169 | g.Emit(OpCodes.Conv_R8); // real 170 | } 171 | else if (from == typeof(int) && to == typeof(string)) 172 | { 173 | g.Emit(OpCodes.Call, typeof(Runtime).GetMethod("__itoa")); 174 | } 175 | else if (from == typeof(real) && to == typeof(string)) 176 | { 177 | g.Emit(OpCodes.Call, typeof(Runtime).GetMethod("__ftoa")); 178 | } 179 | else if (to == typeof(void)) 180 | { 181 | if (from != typeof(void)) g.Emit(OpCodes.Pop); 182 | } 183 | else if (to == typeof(object)) 184 | { 185 | if (from == typeof(int) || from == typeof(real)) 186 | { 187 | g.Emit(OpCodes.Box); 188 | } 189 | else if (!from.IsSubclassOf(typeof(object))) 190 | { 191 | Debug.Assert(false); // must be a value type we don't box yet? 192 | } 193 | } 194 | else if (to.IsGenericType && from.IsGenericType && to.Name == from.Name) 195 | { 196 | return; 197 | } 198 | else 199 | { 200 | Debug.Assert(false); // must catch this combination in the type checker 201 | } 202 | } 203 | 204 | static internal bool ArgCheck(Node n, int required) 205 | { 206 | if (n.Arity() != required) 207 | { 208 | n.err = "arguments required: " + required + " (" + n.Arity() + " given)"; 209 | return false; 210 | } 211 | 212 | return true; 213 | } 214 | } 215 | 216 | -------------------------------------------------------------------------------- /restructor/NodeTypes/PlaceHolder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | 7 | class PlaceHolder : NodeType 8 | { 9 | internal Type clrt = null; 10 | 11 | internal bool isfreevar = true; // FIXME: detect properly 12 | internal FieldBuilder curfb = null; 13 | 14 | internal string pherr = null; 15 | 16 | internal int GetCLRArg(Function f) 17 | { 18 | var i = 0; 19 | for (Function fp = f; fp != null; fp = null /*fp.parent*/) // TODO: cleanup 20 | { 21 | i = 0; 22 | foreach (var arg in fp.args) 23 | { 24 | if (arg == this) return i; else i++; 25 | } 26 | foreach (var fv in fp.freevars) 27 | { 28 | if (!fv.isfreevar) { if (fv == this) return i; else i++; } // TODO: needed? 29 | } 30 | /* 31 | if (fp.args.Exists(arg => ++i > 0 && arg == this) || 32 | fp.freevars.Exists(arg => ++i > 0 && arg == this)) break; 33 | */ 34 | } 35 | Debug.Assert(/*i > 0*/false); 36 | return i;//i - 1; 37 | } 38 | 39 | internal override void RenderCode(CodeRender cr, Node n, Node nparent) 40 | { 41 | cr.WeightNext(700); 42 | cr.Text(name, Program.namespacing, Program.namespacing); 43 | } 44 | 45 | internal override Type GenCode(ILGenerator g, Function f, Node n, Type requested) 46 | { 47 | if(isfreevar) g.Emit(OpCodes.Ldsfld, curfb); 48 | else g.Emit(OpCodes.Ldarg, (short)GetCLRArg(f)); 49 | return clrt; 50 | } 51 | 52 | internal override Type TypeCheck(Node n, Function f, Type expected) 53 | { 54 | if (undeclared) 55 | { 56 | var ph = f.LookupVar(name); 57 | if (ph == null) 58 | { 59 | n.err = "unknown variable: " + name; 60 | } 61 | else 62 | { 63 | n.t = ph; 64 | undeclared = false; 65 | return ph.TypeCheck(n, f, expected); 66 | } 67 | } 68 | return clrt; 69 | } 70 | 71 | internal override void CollectSideEffects(Dictionary sedb, Node n) 72 | { 73 | GetSideEffect(sedb); 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /restructor/NodeTypes/Unparsed.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | class Unparsed : NodeType 4 | { 5 | internal string parseerr = null; 6 | internal int errorpos = 0; 7 | 8 | override internal void RenderCode(CodeRender cr, Node n, Node nparent) 9 | { 10 | cr.EditBox(name, n); 11 | } 12 | 13 | internal override Type TypeCheck(Node n, Function f, Type expected) 14 | { 15 | n.err = parseerr; 16 | return null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /restructor/Parser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using real = System.Double; 4 | 5 | class Parser 6 | { 7 | string buf; 8 | string origbuf; 9 | char token; 10 | int pos; 11 | int line; 12 | string attr; 13 | Program prog; 14 | BuiltinOp op; 15 | 16 | //Function topfun = null; 17 | Function curfun = null; 18 | 19 | List funnames = new List(); 20 | 21 | internal Parser(string _b, Program _p) 22 | { 23 | buf = (origbuf = _b) + "\0"; 24 | pos = 0; 25 | prog = _p; 26 | line = 1; 27 | } 28 | 29 | class ParseError : Exception { internal Node n; } 30 | 31 | Node Error(string s) 32 | { 33 | throw new ParseError 34 | { 35 | n = new Node(new Unparsed 36 | { 37 | name = origbuf, 38 | errorpos = pos, 39 | parseerr = s + " (line: " + line + ")" 40 | }) 41 | }; 42 | } 43 | 44 | void Op(string name, int p, int flags = 0) 45 | { 46 | token = 'O'; 47 | op = prog.uniques.GetOp(name, p, flags); 48 | } 49 | 50 | void Next() 51 | { 52 | op = null; // token is not an operator 53 | 54 | for(;;) switch(token = buf[pos++]) 55 | { 56 | case '\0': pos--; token = 'E'; return; 57 | case '\n': line++; continue; 58 | case ' ': case '\t': case '\r': continue; 59 | 60 | case '(': case ')': case ',': case '`': case '[': case ']': return; 61 | 62 | case '+': Op("+", 50, BuiltinOp.is_add); return; 63 | case '-': Op("-", 50, BuiltinOp.unary_and_binary); return; 64 | case '*': Op("*", 60); return; 65 | case '/': Op("/", 60); return; 66 | 67 | case '=': 68 | if (buf[pos] == '=') { pos++; Op("==", 40, BuiltinOp.int_result); } 69 | else if (buf[pos] == '>') { pos++; Op("=>", 5, BuiltinOp.right_assoc); } 70 | else Op("=", 20, BuiltinOp.right_assoc); 71 | 72 | return; 73 | 74 | case '>': 75 | if (buf[pos] == '=') { pos++; Op(">=", 41, BuiltinOp.int_result); } 76 | else Op(">", 41, BuiltinOp.int_result); 77 | return; 78 | 79 | case '<': 80 | if (buf[pos] == '=') { pos++; Op("<=", 41, BuiltinOp.int_result); } 81 | else Op("<", 41, BuiltinOp.int_result); 82 | return; 83 | 84 | case '!': 85 | if (buf[pos] == '=') { pos++; Op("!=", 40, BuiltinOp.int_result); } 86 | else Op("!", 40, BuiltinOp.unary_only | BuiltinOp.int_to_int); 87 | return; 88 | 89 | case '|': 90 | if (buf[pos] == '>') { pos++; Op("|>", 30); } 91 | else Error("unimplemented operator: |"); 92 | return; 93 | 94 | case ';': Op(";", 10, BuiltinOp.right_assoc); return; 95 | 96 | case '\"': 97 | attr = ""; 98 | token = 'S'; 99 | for (; ; ) 100 | { 101 | char c = buf[pos]; 102 | if (c < ' ') Error("illegal character in string constant: " + (int)c); 103 | pos++; 104 | if (c == '\"') break; 105 | attr += c; 106 | } 107 | return; 108 | 109 | default: 110 | { 111 | attr = token.ToString(); 112 | if(Char.IsLetter(token) || token=='_') 113 | { 114 | while(Char.IsLetter(buf[pos]) || buf[pos]=='_') attr += buf[pos++]; 115 | token = 'I'; 116 | } 117 | else if(Char.IsDigit(token)) 118 | { 119 | while(Char.IsDigit(buf[pos])) attr += buf[pos++]; 120 | token = 'N'; 121 | if (buf[pos] == '.') 122 | { 123 | pos++; 124 | attr += '.'; 125 | while (Char.IsDigit(buf[pos])) attr += buf[pos++]; 126 | token = 'F'; 127 | } 128 | } 129 | else 130 | { 131 | Error("unknown character: " + (token<' ' ? ((int)token).ToString() : attr)); 132 | } 133 | return; 134 | } 135 | } 136 | } 137 | 138 | string Token2Str(char t) 139 | { 140 | switch(t) 141 | { 142 | case 'E': return "end of code"; 143 | case 'I': return "identifier"; 144 | case 'N': return "integer"; 145 | case 'F': return "float"; 146 | case 'S': return "string"; 147 | case 'O': return "operator"; 148 | default: return t.ToString(); 149 | } 150 | } 151 | 152 | void Expect(char t) { if(token!=t) Error(Token2Str(t)+" expected"); Next(); } 153 | 154 | internal Node Parse(Function parent) 155 | { 156 | try 157 | { 158 | Next(); 159 | curfun = parent; 160 | var exp = ParseExp(); 161 | Expect('E'); 162 | curfun = null; 163 | //if (topfun != null) parent.Add(topfun); 164 | return exp; 165 | } 166 | catch(ParseError pe) 167 | { 168 | // shouldn't be needed, but incase typecheck doesn't reach all nodes 169 | pe.n.err = (pe.n.t as Unparsed).parseerr; 170 | //topfun = null; 171 | return pe.n; 172 | } 173 | } 174 | 175 | Node ParseExp(int minprec = 0) 176 | { 177 | var n = ParseFactor(); 178 | // deal with trailing lower prec of any level, guaranteed to consume whole exp 179 | while (op != null && op.precedence > minprec) n = ParseExp(n, minprec); 180 | return n; 181 | } 182 | 183 | Node ParseExp(Node n, int minprec) 184 | { 185 | // Right Assoc -> Recursive 186 | if ((op.flags & BuiltinOp.right_assoc) != 0) 187 | return ParseRHS(n, op.precedence-1); 188 | 189 | int thisprec = op.precedence; 190 | // Left Assoc -> Loop 191 | while (op!=null && op.precedence == thisprec) n = ParseRHS(n, thisprec); 192 | 193 | // deal with trailing lower prec than this 194 | if (op != null && op.precedence > minprec && op.precedence < thisprec) 195 | return ParseExp(n, minprec); 196 | 197 | return n; 198 | } 199 | 200 | Node ParseRHS(Node lhs, int minprec) 201 | { 202 | if ((op.flags & BuiltinOp.unary_only) != 0) 203 | Error(op.name + " cannot be used as a binary operator"); 204 | var sop = op; 205 | Next(); 206 | 207 | if (sop.name == "=") 208 | { 209 | var ph = lhs.t as PlaceHolder; 210 | if (ph == null) Error("left of = must be a variable"); 211 | return new Node(sop, ParseExp(minprec), lhs); // swap order to keep consistent L->R eval 212 | } 213 | else if (sop.name == "=>") 214 | { 215 | var f = prog.GenFun(funnames); 216 | funnames.Add(f.name); 217 | 218 | //if(curfun!=null) 219 | // FIXME: what if an error occurs below? we'd have a half finished function 220 | curfun.Add(f); 221 | //else topfun = f; 222 | 223 | var bf = curfun; 224 | curfun = f; 225 | f.root = ParseExp(minprec); 226 | curfun = bf; 227 | 228 | if(lhs.t is BuiltinOp) 229 | { 230 | switch(lhs.t.name) 231 | { 232 | case "=": 233 | var ph = lhs.At(1).t as PlaceHolder; 234 | f.args.Add(ph); 235 | return new Node(f, lhs.At(0)); 236 | 237 | case "__tuple": 238 | lhs.ForEach(n => 239 | { 240 | if (!(n.t is PlaceHolder)) 241 | Error("lambda parameters must be identifiers"); 242 | f.args.Add(n.t as PlaceHolder); 243 | }); 244 | return new Node(new FunctionValue { reff = f, name = f.name });// fixme: dup 245 | } 246 | } 247 | else if (lhs.t is PlaceHolder) 248 | { 249 | f.args.Add(lhs.t as PlaceHolder); 250 | return new Node(new FunctionValue { reff = f, name = f.name }); 251 | } 252 | Error("left of => must be a variable/tuple or assignment"); 253 | 254 | } 255 | else if (sop.name == "|>") 256 | { 257 | return new Node(sop, ParseExp(minprec), lhs); // same order as __apply 258 | } 259 | 260 | return new Node(sop, lhs, ParseExp(minprec)); 261 | } 262 | 263 | Node ParseFactor() 264 | { 265 | Node r; 266 | switch(token) 267 | { 268 | case 'N': 269 | r = new Node(prog.uniques.GetInt(int.Parse(attr))); 270 | Next(); 271 | return r; 272 | 273 | case 'F': 274 | r = new Node(prog.uniques.GetReal(real.Parse(attr))); 275 | Next(); 276 | return r; 277 | 278 | case 'S': 279 | r = new Node(prog.uniques.GetStr(attr)); 280 | Next(); 281 | return r; 282 | 283 | case '(': 284 | Next(); 285 | r = ParseExp(); 286 | if (token == ',') 287 | { 288 | r = new Node(prog.uniques.tupleop, r); 289 | do 290 | { 291 | Next(); 292 | r.Add(ParseExp()); 293 | } while (token == ','); 294 | } 295 | Expect(')'); 296 | return r; 297 | 298 | case '[': 299 | Next(); 300 | r = new Node(prog.uniques.listop); 301 | if (token != ']') for (; ; ) 302 | { 303 | r.Add(ParseExp()); 304 | if (token == ']') break; 305 | Expect(','); 306 | } 307 | Next(); 308 | return r; 309 | 310 | case '`': 311 | Next(); 312 | r = new Node(new FunctionValue { name = attr, undeclared = true }); 313 | Expect('I'); 314 | return r; 315 | 316 | case 'I': 317 | string name = attr; 318 | r = new Node(new PlaceHolder { name = name, undeclared = true }); 319 | Next(); 320 | 321 | bool isapply; 322 | if (isapply = token == '!') 323 | { 324 | Next(); 325 | r = new Node(prog.uniques.applyop, r); 326 | } 327 | 328 | if (token == '(') 329 | { 330 | Next(); 331 | 332 | if (!isapply) 333 | { 334 | Builtin bf; 335 | prog.uniques.builtins.TryGetValue(name, out bf); 336 | 337 | r = new Node(bf ?? (NodeType)new Function { name = name, undeclared = true }); 338 | } 339 | 340 | if(token!=')') for(;;) 341 | { 342 | r.Add(ParseExp()); 343 | if(token==')') break; 344 | Expect(','); 345 | } 346 | Next(); 347 | } 348 | 349 | return r; 350 | 351 | default: 352 | if (op!=null) 353 | { 354 | if ((op.flags & (BuiltinOp.unary_only | BuiltinOp.unary_and_binary)) == 0) 355 | Error(op.name + " cannot be used as a unary operator"); 356 | 357 | BuiltinOp sop = op; 358 | Next(); 359 | // FIXME: set this to whatever minprec the op requires 360 | return new Node(sop, ParseExp(100)); 361 | } 362 | 363 | return Error("expression expected"); 364 | } 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /restructor/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | using System.Threading; 7 | 8 | class SideEffect 9 | { 10 | internal NodeType id; 11 | internal bool write; 12 | } 13 | 14 | class Program 15 | { 16 | internal Function mf = null; 17 | internal Uniques uniques = new Uniques(); 18 | 19 | internal const int bracketspacing = 1; 20 | internal const int commaspacing = 3; 21 | internal const int equalsspacing = 10; 22 | internal const int namespacing = 1; 23 | internal const int operatorspacing = 2; 24 | 25 | internal Program() 26 | { 27 | New(); 28 | } 29 | 30 | internal void New() 31 | { 32 | mf = new Function { name = "__main" }; 33 | mf.root = new Parser("\"Hello, World!\"", this).Parse(mf); 34 | 35 | Validate(); // normally called by TreeChanged() instead. 36 | } 37 | 38 | Node GenVar(Function f) 39 | { 40 | string s; 41 | for (var i = 0; ; i++) 42 | { 43 | s = GenName(false, i); 44 | for (Function fp = f; fp != null; fp = fp.parent) 45 | if (fp.args.Exists(arg => arg.name == s)) 46 | goto keepgoing; 47 | break; 48 | keepgoing: ; 49 | } 50 | 51 | var ph = new PlaceHolder { name = s }; 52 | f.args.Add(ph); 53 | return new Node(ph); 54 | } 55 | 56 | internal Function GenFun(List parsednames = null) 57 | { 58 | for (var i = 0; ; i++) 59 | { 60 | string id = GenName(true, i); 61 | if (parsednames != null && parsednames.Contains(id)) continue; 62 | if (mf.ExistsFun(of => of.name == id)) continue; 63 | return new Function { name = id }; 64 | } 65 | } 66 | 67 | string GenName(bool funname, int gennames) 68 | { 69 | var basechar = funname ? 'a' : 'A'; 70 | if (gennames < 26) return ((char)(basechar + gennames)).ToString(); 71 | Debug.Assert(gennames < 26 * 26); 72 | return ((char)(basechar + gennames / 26 - 1)).ToString() + 73 | ((char)(basechar + gennames % 26)).ToString(); 74 | } 75 | 76 | internal Function FindNodeParent(Node n) 77 | { 78 | Function rf = null; 79 | mf.ExistsNodeFun((on, f) => on == n && (rf = f) != null); 80 | return rf; 81 | } 82 | 83 | internal string Restructor() 84 | { 85 | if (CodeProblems()) 86 | { 87 | return "Cannot restruct code while there are errors / edits."; 88 | } 89 | 90 | bool cull_unused_args = false; 91 | if (cull_unused_args) 92 | { 93 | // If by means of editing a function argument (or free variable) has become unused, it 94 | // can be removed. This also removes all caller argument values, which is contentious, 95 | // since it may be code the programmer cares about, so ideally should be a separate pass 96 | // from Restructor where it shows you graphically in the editor what is dead code. 97 | // Until we have that, we just delete it, to ensure the Restructor algoritm is maximally 98 | // efficient. 99 | // FIXME: also, this must not remove parameters needed for the function-value arguments 100 | // to e.g. game(). 101 | for (;;) 102 | { 103 | bool modified = false; 104 | mf.ForAllFuncs(f => 105 | { 106 | f.freevars.RemoveAll(ph => !f.ExistsNode(n => n.t == ph)); 107 | // Have to use a regular loop to ensure element is removed within iteration. 108 | for (int i = 0; i < f.args.Count; i++) 109 | { 110 | var ph = f.args[i]; 111 | if (!f.ExistsNode(n => n.t == ph)) 112 | { 113 | // FIXME: this also removes code with side effects, which is clearly NOT 114 | // ok. A side effect argument should be pulled out of the argument list 115 | // and made into a seperate statement. 116 | f.args.RemoveAt(i); 117 | mf.ForAllNodes(n => 118 | { 119 | if (n.t == f) 120 | { 121 | n.Remove(i); 122 | modified = true; 123 | } 124 | }); 125 | i--; 126 | } 127 | } 128 | }); 129 | if (!modified) break; 130 | } 131 | } 132 | 133 | for (; ; ) 134 | { 135 | Debug.WriteLine("Starting Pass"); 136 | 137 | // Iterate thru all code, and add every possible TwoNode you find into a map from each 138 | // possible kind of TwoNode to a list of occurrences. (collecting structurally 139 | // equivalent copies in the code). 140 | // For example, if the code is `(1+2)*(1+3)`, then parent `+` with child `1` (at index 141 | // 0) will be the highest occurring TwoNode. 142 | 143 | var rd = new Dictionary(new TwoNodeEqualityComparer()); 144 | 145 | mf.ForAllRoots((n, f) => n.Register(rd, f)); 146 | 147 | // Add all map items where the list has at least 2 items to an overal list, and sort 148 | // that by occurrences. 149 | 150 | var lsns = new List(); 151 | foreach (KeyValuePair kvp in rd) 152 | if (kvp.Value.l.Count > 1) 153 | lsns.Add(kvp.Value); 154 | 155 | lsns.Sort(new SameNodeSetOrderComparer()); 156 | 157 | lsns.RemoveAll(sns => 158 | { 159 | // Any node may only participate in one TwoNode, since a refactor is destructive. 160 | // Combinations that didn't get to participate can be picked up by next passes. 161 | sns.l.RemoveAll(tn => 162 | { 163 | bool used = tn.a.refactorused || tn.b.refactorused; 164 | // have to do this while checking, because of add(add(add(...))) situations 165 | tn.a.refactorused = tn.b.refactorused = true; 166 | return used; 167 | }); 168 | return sns.l.Count <= 1; 169 | }); 170 | 171 | // Nothing to refactor, we're done! 172 | 173 | if (lsns.Count == 0) break; 174 | 175 | lsns.Sort(new SameNodeSetOrderComparer()); 176 | 177 | // Go through the overal list in order of number of occurrences: 178 | 179 | foreach (var sns in lsns) 180 | { 181 | var a = sns.l[0].a; 182 | var b = sns.l[0].b; 183 | var pos = sns.l[0].pos; 184 | 185 | // Create a new function that will have the TwoNode as body. Generate placeholders 186 | // for all children of the parent that are not the given child, and all children of 187 | // the child. 188 | // So here the function would be: `f(x) = 1 + x` 189 | var f = GenFun(); 190 | f.root = new Node(a.t); 191 | 192 | // Find the lowest common ancestor function that dominates all occurrences, so 193 | // function is defined as local as possible.Make that the parent function. (this 194 | // could be skipped if all functions were global). 195 | Function parent = null; 196 | foreach (var tn in sns.l) parent = tn.domf.LCA(parent); 197 | 198 | Debug.WriteLine("Make Function " + f.name + " : " + sns.l.Count + " => " + 199 | parent.name); 200 | 201 | parent.Add(f); 202 | 203 | for (var i = 0; i < a.Arity(); i++) 204 | { 205 | if (i == pos) 206 | { 207 | var nb = new Node(b.t); 208 | b.ForEach(n => nb.Add(GenVar(f))); 209 | f.root.Add(nb); 210 | } 211 | else 212 | { 213 | f.root.Add(GenVar(f)); 214 | } 215 | } 216 | 217 | // if the child was already a placeholder, it becomes a free variable of the new 218 | // function, similarly if either node is a function, then its free vars are added 219 | // to the new function. 220 | b.IfPlaceHolder(ph => f.freevars.Add(ph)); 221 | a.IfFunction(of => f.AddFreeVarsFrom(of)); 222 | b.IfFunction(of => f.AddFreeVarsFrom(of)); 223 | 224 | // if the child wasn't the last child: (because if it is the last child, the order 225 | // of evaluation vs the other children doesn't change, so no special action is 226 | // needed!) 227 | if (pos + 1 < a.Arity()) 228 | { 229 | // Find all side-effects in all children of the child. This takes the entire 230 | // call graph into account from this node. 231 | var bdb = new Dictionary(); 232 | b.t.CollectSideEffects(bdb, b); 233 | 234 | // If there are any side - effecs, for each following child, for each of its 235 | // occurrences, check if the two children are order dependent. 236 | // E.g. `f(a = 1, a)` or `f(a, a = 1)` are order dependent, and we have to 237 | // ensure to retain the evaluation order. `f(a = 1, b)` is not order dependent. 238 | if (bdb.Count > 0) for (int p = pos + 1; p < a.Arity(); p++) 239 | { 240 | foreach (var tn in sns.l) 241 | { 242 | // TODO: could instead compare against b's dict rather than create 243 | var adb = new Dictionary(); 244 | tn.a.At(p).CollectSideEffects(adb); 245 | if (adb.Count > 0) foreach (var bkvp in bdb) 246 | { 247 | SideEffect ase; 248 | if (adb.TryGetValue(bkvp.Key, out ase)) 249 | { 250 | var bse = bkvp.Value; 251 | if (ase.write || bse.write) 252 | { 253 | // Found an order-dependent occurrence. 254 | // TODO: should really allow for non-se callers to call a 255 | // pure version of function 256 | goto have_se; 257 | } 258 | } 259 | } 260 | } 261 | continue; 262 | have_se: 263 | foreach (var tn in sns.l) 264 | { 265 | Function nof = GenFun(); 266 | parent.Add(nof); 267 | nof.root = tn.a.At(p); 268 | tn.a.Remove(p); 269 | tn.a.Insert(p, new Node( 270 | new FunctionValue { reff = nof, name = nof.name })); 271 | // FIXME: free vars? 272 | } 273 | var apply = new Node(uniques.applyop, f.root.At(p)); 274 | f.root.Remove(p); 275 | f.root.Insert(p, apply); 276 | } 277 | } 278 | 279 | f.root.Sanity(); 280 | 281 | // Now replace all occurrences with our new function, so our expression becomes 282 | // `f(2) * f(3)`. 283 | 284 | foreach (var tn in sns.l) 285 | { 286 | Debug.Assert(tn.a.Arity() + tn.b.Arity() - 1 == f.args.Count); 287 | Debug.Assert(tn.a.At(pos) == tn.b); 288 | 289 | tn.a.t = f; 290 | tn.a.Remove(pos); 291 | 292 | tn.b.ForEach((n, j) => tn.a.Insert(pos + j, n)); 293 | tn.a.Sanity(); 294 | } 295 | 296 | // If any arguments are equal accross all occurrences, merge those arguments. 297 | f.MergeEqualArgs(sns.l); 298 | } 299 | 300 | // Inline all functions that have just 1 caller. This is essential, because the above 301 | // algorithm generates a ton of mini-functions whose body contains just one node, so 302 | // this inlining is responsible for making those back into the largest possible 303 | // functions, and shifting the boundaries of abstractions. 304 | // Going down to one caller is a natural consequence of the above algorithm whenever a 305 | // function call with multiple occurrences becomes the given child of a new function. 306 | // Together, these parts of the algorithm create the "emergent" behavior of finding 307 | // optimal function boundaries regardless of the code structure. 308 | InlineAll(); 309 | } 310 | 311 | InlineAll(); 312 | Validate(); // should not be needed, just incase restructor trampled on some types 313 | 314 | return "Restructed."; 315 | } 316 | 317 | internal void InlineAll() 318 | { 319 | do 320 | { 321 | mf.ForAllFuncs(f => { 322 | f.numcallers = 0; 323 | f.lastcaller = null; 324 | f.usedasfunctionvalue = false; 325 | }); 326 | // can't do this out of loop since lastcaller objects may move during inline 327 | mf.ForAllNodes(n => n.t.InlineCount(n)); 328 | } while (mf.ExistsFun(f => f.Inline())); 329 | 330 | while (mf.ExistsFun(f => f.OneOnOneInline(this))) { }; 331 | } 332 | 333 | internal void Validate() 334 | { 335 | mf.ForAllFuncs(f => { f.variants = null; f.root.ForAllNodes(n => n.err = null); }); 336 | mf.TypeCheck(null, null, null); 337 | mf.SubType(mf.root, typeof(string)); 338 | } 339 | 340 | bool CodeProblems() { 341 | return mf.ExistsNode(n => { 342 | /*Debug.Assert(n.clrt!=null); */ 343 | return n.err != null || n.t is Unparsed || n.clrt == null; 344 | }); 345 | } 346 | 347 | internal static TypeBuilder _cls = null; 348 | internal static bool _debug = false; 349 | 350 | internal string GenCode(bool run, bool debug) 351 | { 352 | // should not be needed, but to be sure there's no missing types etc, to avoid nasty 353 | // codegen errors 354 | Validate(); 355 | 356 | if (CodeProblems()) 357 | { 358 | return "Cannot compile code while there are errors / edits."; 359 | } 360 | 361 | var asm = Thread.GetDomain().DefineDynamicAssembly( 362 | new AssemblyName("RestructorAssembly"), AssemblyBuilderAccess.RunAndSave); 363 | var mod = asm.DefineDynamicModule("RestructorModule", "RestructorOutput.exe", false); 364 | var cls = mod.DefineType("RestructorClass", TypeAttributes.Public); 365 | cls.AddInterfaceImplementation(typeof(IRestructorCode)); 366 | 367 | _cls = cls; 368 | _debug = debug; 369 | var mb = mf.GenCodeFun(typeof(string)); 370 | _cls = null; 371 | _debug = false; 372 | 373 | cls.DefineMethodOverride(mb, typeof(IRestructorCode).GetMethod(mf.name)); 374 | 375 | cls.CreateType(); 376 | 377 | string ret = ""; 378 | if (run) 379 | { 380 | var code = (IRestructorCode)asm.CreateInstance("RestructorClass"); 381 | try 382 | { 383 | ret = code.__main(); 384 | Debug.WriteLine("RUN: " + ret); 385 | } 386 | catch (Exception e) 387 | { 388 | ret = "exception: " + e.Message + "\n" + e.StackTrace; 389 | } 390 | } 391 | else 392 | { 393 | var clse = mod.DefineType("RestructorEntry", TypeAttributes.Public); 394 | 395 | var mmb = clse.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, 396 | typeof(int), new Type[] { typeof(string[]) }); 397 | var g = mmb.GetILGenerator(); 398 | g.Emit(OpCodes.Newobj, typeof(Runtime).GetConstructor(new Type[0])); 399 | g.Emit(OpCodes.Ldarg_0); 400 | g.Emit(OpCodes.Stfld, typeof(Runtime).GetField("__commandlineargs")); 401 | //g.Emit(OpCodes.Call, typeof(Runtime).GetMethod("__init", new Type[0])); 402 | g.Emit(OpCodes.Newobj, cls.GetConstructor(new Type[0])); 403 | g.Emit(OpCodes.Call, mb); 404 | g.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", 405 | new Type[] { typeof(string) })); 406 | g.Emit(OpCodes.Ldc_I4_0); 407 | g.Emit(OpCodes.Ret); 408 | 409 | clse.CreateType(); 410 | 411 | asm.SetEntryPoint(mmb, PEFileKinds.WindowApplication); 412 | 413 | try 414 | { 415 | asm.Save("RestructorOutput.exe", 416 | PortableExecutableKinds.Required32Bit | PortableExecutableKinds.ILOnly, 417 | ImageFileMachine.I386); 418 | ret = "Saved Exe."; 419 | } 420 | catch 421 | { 422 | ret = "Couldn't write exe."; 423 | } 424 | } 425 | 426 | // TODO: maybe should only clear the mb's if the gui wants to be able to look at the 427 | // variants, or just call Validate() again 428 | mf.ForAllFuncs(f => f.variants = null); 429 | 430 | return ret; 431 | } 432 | 433 | void CreateArgListGui(List l, CodeRender cr, string left, string right) 434 | { 435 | cr.Text(left, 0, bracketspacing); 436 | foreach (var arg in l) 437 | { 438 | cr.Push(arg); 439 | arg.RenderCode(cr, null, null); 440 | cr.Pop(arg); 441 | if (arg != l[l.Count - 1]) cr.Text(",", 0, commaspacing); 442 | } 443 | cr.Text(right, bracketspacing, 0); 444 | } 445 | 446 | internal void RenderCode(CodeRender cr) 447 | { 448 | cr.StartTopLevel(); 449 | var i = 0; 450 | mf.ForAllFuncs(f => 451 | { 452 | cr.Push(null); 453 | cr.Push(f); 454 | f.RenderCode(cr, null, null); 455 | cr.Pop(f); 456 | CreateArgListGui(f.args, cr, "(", ")"); 457 | if(f.freevars.Count>0) cr.Scale(0.7, () => CreateArgListGui(f.freevars, cr, "[", "]")); 458 | cr.Pop(null); 459 | cr.SaveFunctionLHS(); 460 | 461 | cr.Push(null); 462 | cr.Text("=", equalsspacing, equalsspacing); 463 | f.root.RenderCode(cr, null); 464 | cr.Pop(null); 465 | 466 | cr.Function(i++, f.Depth()); 467 | }); 468 | } 469 | } 470 | -------------------------------------------------------------------------------- /restructor/Properties/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 47 | -------------------------------------------------------------------------------- /restructor/Restructor.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {9FDCC979-FA97-47E6-A7BA-E38F8F1B3D79} 9 | WinExe 10 | Properties 11 | Restructor 12 | Restructor 13 | v4.0 14 | Client 15 | 512 16 | publish\ 17 | true 18 | Disk 19 | false 20 | Foreground 21 | 7 22 | Days 23 | false 24 | false 25 | true 26 | 0 27 | 1.0.0.%2a 28 | false 29 | false 30 | true 31 | 32 | 33 | x86 34 | true 35 | full 36 | false 37 | bin\Debug\ 38 | DEBUG;TRACE 39 | prompt 40 | 4 41 | false 42 | MinimumRecommendedRules.ruleset 43 | Off 44 | 45 | 46 | x86 47 | pdbonly 48 | true 49 | bin\Release\ 50 | TRACE 51 | prompt 52 | 4 53 | Off 54 | 55 | 56 | 57 | 58 | 59 | Internet 60 | 61 | 62 | false 63 | 64 | 65 | Properties\app.manifest 66 | 67 | 68 | true 69 | bin\Debug\ 70 | DEBUG;TRACE 71 | full 72 | AnyCPU 73 | bin\Debug\Restructor.exe.CodeAnalysisLog.xml 74 | true 75 | GlobalSuppressions.cs 76 | Off 77 | prompt 78 | MinimumRecommendedRules.ruleset 79 | ;c:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 80 | ;c:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 81 | 82 | 83 | bin\Release\ 84 | TRACE 85 | true 86 | pdbonly 87 | AnyCPU 88 | bin\Release\Restructor.exe.CodeAnalysisLog.xml 89 | true 90 | GlobalSuppressions.cs 91 | Off 92 | prompt 93 | MinimumRecommendedRules.ruleset 94 | ;c:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 95 | true 96 | ;c:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 97 | true 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | False 140 | Microsoft .NET Framework 4 Client Profile %28x86 and x64%29 141 | true 142 | 143 | 144 | False 145 | .NET Framework 3.5 SP1 Client Profile 146 | false 147 | 148 | 149 | False 150 | .NET Framework 3.5 SP1 151 | false 152 | 153 | 154 | False 155 | Windows Installer 3.1 156 | true 157 | 158 | 159 | 160 | 161 | {4A4D852B-3887-49A7-A83B-B020C7082C57} 162 | RestructorRuntime 163 | 164 | 165 | 166 | 167 | 168 | 169 | 176 | -------------------------------------------------------------------------------- /restructor/Selection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Media; 5 | using System.Windows.Input; 6 | 7 | class Selection 8 | { 9 | internal Object selected = null; 10 | internal Object hover = null; 11 | internal StackPanel selectedui = null; 12 | internal StackPanel hoverui = null; 13 | 14 | MainWin w; 15 | 16 | internal Selection(MainWin _w) { w = _w; } 17 | 18 | internal void HoverMove(Point p) 19 | { 20 | if (hoverui != null && hoverui != selectedui) hoverui.Background = null; 21 | hoverui = null; 22 | hover = null; 23 | 24 | var htr = VisualTreeHelper.HitTest(w, p); 25 | if (htr == null) return; 26 | 27 | var fe = htr.VisualHit as Visual; 28 | if (fe == null) return; 29 | 30 | while (fe != null) 31 | { 32 | if (fe is StackPanel) 33 | { 34 | var sp = fe as StackPanel; 35 | var n = sp.Tag; 36 | if (n == null) return; 37 | hover = n; 38 | hoverui = sp; 39 | if (hoverui != selectedui) hoverui.Background = Brushes.LightCyan; 40 | return; 41 | } 42 | fe = VisualTreeHelper.GetParent(fe) as Visual; 43 | } 44 | } 45 | 46 | bool FindHoverUI(DependencyObject o, Object n) 47 | { 48 | if (o is StackPanel) 49 | { 50 | var sp = o as StackPanel; 51 | if (sp.Tag == n) 52 | { 53 | hoverui = sp; 54 | hover = n; 55 | return true; 56 | } 57 | } 58 | for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++) 59 | if (FindHoverUI(VisualTreeHelper.GetChild(o, i), n)) 60 | return true; 61 | return false; 62 | } 63 | 64 | internal void Selected() 65 | { 66 | if (selectedui != null) selectedui.Background = null; 67 | 68 | if (hoverui != null) 69 | { 70 | hoverui.Background = Brushes.SkyBlue; 71 | hoverui.Focusable = true; 72 | Keyboard.Focus(hoverui); 73 | } 74 | else 75 | { 76 | Keyboard.ClearFocus(); 77 | } 78 | 79 | if (selected != null && selected != hover && 80 | selected is Node && (selected as Node).t is Unparsed) 81 | { 82 | var tb = selectedui.Children[0] as TextBox; 83 | var hn = hover; 84 | w.Replace(selected as Node, tb.Text); 85 | if (hn != null) 86 | { 87 | FindHoverUI(w, hn); 88 | Selected(); 89 | } 90 | return; 91 | } 92 | 93 | selected = hover; 94 | selectedui = hoverui; 95 | } 96 | 97 | internal void ReSelectEditBox() 98 | { 99 | if (selected == null && hover != null && hover is Node && (hover as Node).t is Unparsed) 100 | { 101 | selected = hover; 102 | selectedui = hoverui; 103 | } 104 | } 105 | 106 | internal void DeSelect() 107 | { 108 | selected = null; 109 | selectedui = null; 110 | hover = null; 111 | hoverui = null; 112 | } 113 | 114 | internal void SetEditMode(string start = null) 115 | { 116 | if (selected == null) return; 117 | var sel = selected as Node; 118 | 119 | string txt; 120 | if (sel != null) 121 | { 122 | if (sel.t is Unparsed) return; 123 | sel.ConvertToUnparsed(); 124 | txt = sel.t.name; 125 | } 126 | else if (selected is NodeType) 127 | { 128 | txt = (selected as NodeType).name; 129 | } 130 | else 131 | { 132 | return; 133 | } 134 | 135 | selectedui.Children.Clear(); 136 | 137 | var cr = new CodeRenderGUI(w); 138 | cr.EditBox(txt, selected); 139 | 140 | var tb = cr.lasttextbox; 141 | if (start != null) { tb.Text = start; tb.CaretIndex = 1; } 142 | else tb.SelectAll(); 143 | selectedui.Children.Add(tb); 144 | tb.Loaded += (s, e) => { Keyboard.Focus(tb); }; 145 | } 146 | 147 | internal void HandleKey(Key k) 148 | { 149 | switch (k) 150 | { 151 | case Key.Enter: 152 | SetEditMode(); 153 | break; 154 | } 155 | } 156 | } -------------------------------------------------------------------------------- /restructor/TwoNode.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | struct TwoNode 4 | { 5 | internal Node a, b; 6 | internal Function domf; 7 | internal int pos; 8 | } 9 | 10 | class SameNodeSet 11 | { 12 | internal List l = new List(); 13 | } 14 | 15 | class TwoNodeEqualityComparer : IEqualityComparer 16 | { 17 | public bool Equals(TwoNode tn1, TwoNode tn2) 18 | { 19 | return tn1.a.t == tn2.a.t && 20 | tn1.b.t == tn2.b.t && 21 | tn1.pos == tn2.pos && 22 | tn1.a.Arity() == tn2.a.Arity() && 23 | tn1.b.Arity() == tn2.b.Arity(); 24 | } 25 | 26 | public int GetHashCode(TwoNode tn) 27 | { 28 | return tn.a.t.name.GetHashCode() ^ 29 | tn.b.t.name.GetHashCode(); 30 | } 31 | } 32 | 33 | class SameNodeSetOrderComparer : IComparer 34 | { 35 | public int Compare(SameNodeSet x, SameNodeSet y) 36 | { 37 | return x.l.Count < y.l.Count ? 1 : (x.l.Count > y.l.Count ? -1 : 0); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /restructor/Uniques.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Diagnostics; 5 | using real = System.Double; 6 | 7 | class Uniques 8 | { 9 | internal Dictionary builtins = new Dictionary(); 10 | internal Dictionary builtinops = new Dictionary(); 11 | 12 | Dictionary inthash = new Dictionary(); 13 | Dictionary strhash = new Dictionary(); 14 | Dictionary flthash = new Dictionary(); 15 | 16 | internal BuiltinOp applyop, listop, tupleop; 17 | 18 | internal Uniques() 19 | { 20 | var mis = typeof(Runtime).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | 21 | BindingFlags.Instance | BindingFlags.Static); 22 | foreach (var mi in mis) 23 | { 24 | var pis = mi.GetParameters(); 25 | Debug.WriteLine("method: " + mi.Name + ":" + pis.Length); 26 | builtins[mi.Name] = new Builtin { name = mi.Name, clrf = mi }; 27 | } 28 | 29 | applyop = GetOp("__apply", 1000, 0); 30 | tupleop = GetOp("__tuple", 0, BuiltinOp.no_precedence); 31 | listop = GetOp("__list", 0, BuiltinOp.no_precedence); 32 | } 33 | 34 | internal Int GetInt(int i) 35 | { 36 | Int it; 37 | if (inthash.TryGetValue(i, out it)) return it; 38 | return inthash[i] = new Int(i); 39 | } 40 | 41 | internal Real GetReal(real d) 42 | { 43 | Real dt; 44 | if (flthash.TryGetValue(d, out dt)) return dt; 45 | return flthash[d] = new Real(d); 46 | } 47 | 48 | internal String GetStr(string s) 49 | { 50 | String st; 51 | if (strhash.TryGetValue(s, out st)) return st; 52 | return strhash[s] = new String(s); 53 | } 54 | 55 | internal BuiltinOp GetOp(string name, int p, int flags) 56 | { 57 | BuiltinOp op; 58 | if (builtinops.TryGetValue(name, out op)) return op; 59 | return builtinops[name] = new BuiltinOp { name = name, precedence = p, flags = flags }; 60 | } 61 | } --------------------------------------------------------------------------------