├── .gitattributes ├── .gitignore ├── README.md ├── docs ├── Grammar.txt └── screenshot.png └── src ├── MiniC.Compiler.Demo ├── App.xaml ├── App.xaml.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── MainWindowViewModel.cs ├── MiniC.Compiler.Demo.csproj ├── Properties │ ├── Annotations.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── TreeItems │ ├── CompoundStatementItem.cs │ ├── DeclarationItem.cs │ ├── ExpressionItem.cs │ ├── SimpleItem.cs │ ├── StatementItem.cs │ ├── TreeItemBase.cs │ └── VariableDeclarationItem.cs └── packages.config ├── MiniC.Compiler.Tests ├── App.config ├── CompilerTests.fs ├── ILBuilderTests.fs ├── MiniC.Compiler.Tests.fsproj ├── ParserTests.fs ├── Sources │ ├── error1.minic │ ├── error10.minic │ ├── error11.minic │ ├── error12.minic │ ├── error13.minic │ ├── error14.minic │ ├── error15.minic │ ├── error16.minic │ ├── error17.minic │ ├── error18.minic │ ├── error19.minic │ ├── error2.minic │ ├── error20.minic │ ├── error21.minic │ ├── error22.minic │ ├── error23.minic │ ├── error24.minic │ ├── error25.minic │ ├── error3.minic │ ├── error4.minic │ ├── error5.minic │ ├── error6.minic │ ├── error7.minic │ ├── error8.minic │ ├── error9.minic │ ├── test1.minic │ ├── test2.minic │ ├── test3.minic │ ├── test4.minic │ ├── test5.minic │ ├── test6.minic │ └── test7.minic ├── SymbolTableTests.fs └── packages.config ├── MiniC.Compiler ├── Ast.fs ├── CodeGenerator.fs ├── Compiler.fs ├── CompilerException.fs ├── IL.fs ├── ILBuilder.fs ├── MiniC.Compiler.fsproj ├── Parser.fs ├── ParsingUtilities.fs ├── SemanticAnalysis.fs └── packages.config ├── MiniC.sln ├── MiniC.sln.DotSettings └── packages ├── AvalonEdit.4.2.0.8783 ├── AvalonEdit.4.2.0.8783.nupkg ├── AvalonEdit.4.2.0.8783.nuspec └── lib │ ├── Net35 │ ├── ICSharpCode.AvalonEdit.dll │ └── ICSharpCode.AvalonEdit.xml │ └── Net40 │ ├── ICSharpCode.AvalonEdit.dll │ └── ICSharpCode.AvalonEdit.xml ├── MahApps.Metro.0.10.0.0 ├── MahApps.Metro.0.10.0.0.nupkg ├── MahApps.Metro.0.10.0.0.nuspec ├── lib │ └── net40 │ │ ├── MahApps.Metro.dll │ │ └── System.Windows.Interactivity.dll └── tools │ └── install.ps1 ├── NUnit.2.6.2 ├── NUnit.2.6.2.nupkg ├── NUnit.2.6.2.nuspec ├── lib │ ├── nunit.framework.dll │ └── nunit.framework.xml └── license.txt ├── Piglet.1.4.0 ├── Piglet.1.4.0.nupkg ├── Piglet.1.4.0.nuspec └── lib │ └── net40 │ ├── Piglet.XML │ ├── Piglet.dll │ └── Piglet.pdb └── repositories.config /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Shamelessly stolen from http://gist.github.com/114476 2 | # .gitignore for .NET projects 3 | # Thanks to Derick Bailey 4 | # http://www.lostechies.com/blogs/derickbailey/archive/2009/05/18/a-net-c-developer-s-gitignore-file.aspx 5 | # Additional Thanks to 6 | # - Alexey Abramov 7 | 8 | # Standard VS.NET and ReSharper Foo 9 | obj 10 | bin 11 | Bin 12 | *.csproj.user 13 | *ReSharper* 14 | *resharper* 15 | *.dotCover 16 | *.suo 17 | *.cache 18 | Thumbs.db 19 | 20 | # Other useful stuff 21 | *.bak 22 | *.cache 23 | *.log 24 | *.swp 25 | *.user 26 | _compareTemp 27 | _notes 28 | aspnet_client 29 | httpd.parse.errors 30 | 31 | # Office Temp Files 32 | ~$* 33 | 34 | # If you have a deploy folder 35 | # deploy 36 | # deploy/* 37 | 38 | # Exclude ALL DLLs? ( 39 | # *.dll -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mini-C Compiler 2 | =============== 3 | 4 | This is a compiler for a subset of the C programming language. The compiler is written 5 | in F# and targets Microsoft Intermediate Language (MSIL). It was written so that I could 6 | teach myself F#. If you're interested in doing the same thing, you may find it useful. 7 | 8 | GUI 9 | --- 10 | 11 | I wrote a simple GUI to visualize the compilation process. The Abstract Syntax Tree (AST) 12 | and Microsoft Intermediate Language (MSIL) panels update in real-time when you change the source code. 13 | 14 | ![Screenshot](https://github.com/tgjones/mini-c/raw/master/docs/screenshot.png) 15 | 16 | Acknowledgements 17 | ---------------- 18 | 19 | * I got the grammar for this particular subset of C from 20 | [this paper](http://jamesvanboxtel.com/projects/minic-compiler/minic.pdf), 21 | which looks like a university course assignment. 22 | * Tim Robinson's [blog series on writing a Lisp compiler in F#](http://www.partario.com/blog/2009/05/lisp-compiler-in-f-introduction.html) 23 | was very useful. Although the source languages are different, I was still able 24 | to use many of his ideas for my implementation. 25 | 26 | License 27 | ------- 28 | 29 | Mini-C is released under the [MIT License](http://www.opensource.org/licenses/MIT). -------------------------------------------------------------------------------- /docs/Grammar.txt: -------------------------------------------------------------------------------- 1 | program → decl_list 2 | decl_list → decl_list decl | decl 3 | decl → var_decl | fun_decl 4 | var_decl → type_spec IDENT ; | type_spec IDENT [ ] ; 5 | type_spec → VOID | BOOL | INT | FLOAT 6 | fun_decl → type_spec IDENT ( params ) compound_stmt 7 | params → param_list | VOID 8 | param_list → param_list , param | param 9 | param → type_spec IDENT | type_spec IDENT [ ] 10 | stmt_list → stmt_list stmt | ε 11 | stmt → expr_stmt | compound_stmt | if_stmt | while_stmt | 12 | return_stmt | break_stmt 13 | expr_stmt → expr ; | ; 14 | while_stmt → WHILE ( expr ) stmt 15 | compound_stmt → { local_decls stmt_list } 16 | local_decls → local_decls local_decl | ε 17 | local_decl → type_spec IDENT ; | type_spec IDENT [ ] ; 18 | if_stmt → IF ( expr ) stmt | IF ( expr ) stmt ELSE stmt 19 | return_stmt → RETURN ; | RETURN expr ; 20 | 21 | The following expressions are listed in order of increasing precedence: 22 | expr → IDENT = expr | IDENT [ expr ] = expr 23 | → expr OR expr 24 | → expr EQ expr | expr NE expr 25 | → expr LE expr | expr < expr | expr GE expr | expr > expr 26 | → expr AND expr 27 | → expr + expr | expr - expr 28 | → expr * expr | expr / expr | expr % expr 29 | → ! expr | - expr | + expr 30 | → ( expr ) 31 | → IDENT | IDENT [ expr ] | IDENT ( args ) | IDENT . size 32 | → BOOL_LIT | INT_LIT | FLOAT_LIT | NEW type_spec [ expr ] 33 | 34 | arg_list → arg_list , expr | expr 35 | args → arg_list | ε -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/docs/screenshot.png -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Windows; 7 | 8 | namespace MiniC.Compiler.Demo 9 | { 10 | /// 11 | /// Interaction logic for App.xaml 12 | /// 13 | public partial class App : Application 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 33 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 62 | 63 | 64 | 66 | 67 | 69 | 70 | 71 | 72 | 73 | 74 | 76 | 77 | 79 | 80 | 81 | 82 | 83 | 84 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MahApps.Metro.Controls; 3 | 4 | namespace MiniC.Compiler.Demo 5 | { 6 | /// 7 | /// Interaction logic for MainWindow.xaml 8 | /// 9 | public partial class MainWindow : MetroWindow 10 | { 11 | public MainWindow() 12 | { 13 | InitializeComponent(); 14 | TextEditor.Text = ((MainWindowViewModel) DataContext).SourceCode; 15 | } 16 | 17 | private void TextEditor_OnTextChanged(object sender, EventArgs e) 18 | { 19 | ((MainWindowViewModel) DataContext).SourceCode = TextEditor.Text; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/MainWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using MiniC.Compiler.Demo.Annotations; 5 | using MiniC.Compiler.Demo.TreeItems; 6 | 7 | namespace MiniC.Compiler.Demo 8 | { 9 | public class MainWindowViewModel : INotifyPropertyChanged 10 | { 11 | private string _sourceCode; 12 | public string SourceCode 13 | { 14 | get { return _sourceCode; } 15 | set 16 | { 17 | _sourceCode = value; 18 | Recompile(); 19 | OnPropertyChanged("SourceCode"); 20 | } 21 | } 22 | 23 | private string _compilerErrors; 24 | public string CompilerErrors 25 | { 26 | get { return _compilerErrors; } 27 | set 28 | { 29 | _compilerErrors = value; 30 | OnPropertyChanged("CompilerErrors"); 31 | } 32 | } 33 | 34 | private IEnumerable _abstractSyntaxTree; 35 | public IEnumerable AbstractSyntaxTree 36 | { 37 | get { return _abstractSyntaxTree; } 38 | private set 39 | { 40 | _abstractSyntaxTree = value; 41 | OnPropertyChanged("AbstractSyntaxTree"); 42 | } 43 | } 44 | 45 | private IL.ILClass _intermediateLanguage; 46 | public IL.ILClass IntermediateLanguage 47 | { 48 | get { return _intermediateLanguage; } 49 | set 50 | { 51 | _intermediateLanguage = value; 52 | OnPropertyChanged("IntermediateLanguage"); 53 | } 54 | } 55 | 56 | public MainWindowViewModel() 57 | { 58 | _sourceCode = @"int fib(int n) { 59 | if (n == 0) 60 | return 0; 61 | if (n == 1) 62 | return 1; 63 | return fib(n - 1) + fib(n - 2); 64 | } 65 | 66 | int main(void) { 67 | return fib(10); 68 | }"; 69 | Recompile(); 70 | } 71 | 72 | private void Recompile() 73 | { 74 | AbstractSyntaxTree = null; 75 | IntermediateLanguage = null; 76 | CompilerErrors = string.Empty; 77 | try 78 | { 79 | var program = Parser.parse(_sourceCode); 80 | AbstractSyntaxTree = program.Select(x => new DeclarationItem(x)); 81 | var semanticAnalysisResult = SemanticAnalysis.analyze(program); 82 | IntermediateLanguage = new ILBuilder(semanticAnalysisResult).BuildClass(program); 83 | } 84 | catch (CompilerException ex) 85 | { 86 | CompilerErrors = ex.Message; 87 | } 88 | } 89 | 90 | #region INotifyPropertyChanged 91 | 92 | public event PropertyChangedEventHandler PropertyChanged; 93 | 94 | [NotifyPropertyChangedInvocator] 95 | protected virtual void OnPropertyChanged(string propertyName) 96 | { 97 | PropertyChangedEventHandler handler = PropertyChanged; 98 | if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 99 | } 100 | 101 | #endregion 102 | } 103 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/MiniC.Compiler.Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BAE211A9-744C-42C9-A70E-3A16505034CF} 8 | WinExe 9 | Properties 10 | MiniC.Compiler.Demo 11 | MiniC.Compiler.Demo 12 | v4.0 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | ..\packages\AvalonEdit.4.2.0.8783\lib\Net40\ICSharpCode.AvalonEdit.dll 40 | 41 | 42 | ..\packages\MahApps.Metro.0.10.0.0\lib\net40\MahApps.Metro.dll 43 | 44 | 45 | 46 | 47 | ..\packages\MahApps.Metro.0.10.0.0\lib\net40\System.Windows.Interactivity.dll 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 4.0 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | MSBuild:Compile 64 | Designer 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | MSBuild:Compile 75 | Designer 76 | 77 | 78 | App.xaml 79 | Code 80 | 81 | 82 | MainWindow.xaml 83 | Code 84 | 85 | 86 | 87 | 88 | 89 | 90 | Code 91 | 92 | 93 | True 94 | True 95 | Resources.resx 96 | 97 | 98 | True 99 | Settings.settings 100 | True 101 | 102 | 103 | ResXFileCodeGenerator 104 | Resources.Designer.cs 105 | 106 | 107 | 108 | SettingsSingleFileGenerator 109 | Settings.Designer.cs 110 | 111 | 112 | 113 | 114 | 115 | {2860d24f-85d9-4bc7-8463-ee1ec134290d} 116 | MiniC.Compiler 117 | 118 | 119 | 120 | 127 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/Properties/Annotations.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2012 JetBrains s.r.o. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using System; 18 | using System.ComponentModel; 19 | 20 | namespace MiniC.Compiler.Demo.Annotations 21 | { 22 | /// 23 | /// Indicates that marked element should be localized or not. 24 | /// 25 | /// 26 | /// 27 | /// [LocalizationRequiredAttribute(true)] 28 | /// public class Foo 29 | /// { 30 | /// private string str = "my string"; // Warning: Localizable string 31 | /// } 32 | /// 33 | /// 34 | [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] 35 | public sealed class LocalizationRequiredAttribute : Attribute 36 | { 37 | /// 38 | /// Initializes a new instance of the class with 39 | /// set to . 40 | /// 41 | public LocalizationRequiredAttribute() : this(true) 42 | { 43 | } 44 | 45 | /// 46 | /// Initializes a new instance of the class. 47 | /// 48 | /// true if a element should be localized; otherwise, false. 49 | public LocalizationRequiredAttribute(bool required) 50 | { 51 | Required = required; 52 | } 53 | 54 | /// 55 | /// Gets a value indicating whether a element should be localized. 56 | /// true if a element should be localized; otherwise, false. 57 | /// 58 | [UsedImplicitly] public bool Required { get; private set; } 59 | 60 | /// 61 | /// Returns whether the value of the given object is equal to the current . 62 | /// 63 | /// The object to test the value equality of. 64 | /// 65 | /// true if the value of the given object is equal to that of the current; otherwise, false. 66 | /// 67 | public override bool Equals(object obj) 68 | { 69 | var attribute = obj as LocalizationRequiredAttribute; 70 | return attribute != null && attribute.Required == Required; 71 | } 72 | 73 | /// 74 | /// Returns the hash code for this instance. 75 | /// 76 | /// A hash code for the current . 77 | public override int GetHashCode() 78 | { 79 | return base.GetHashCode(); 80 | } 81 | } 82 | 83 | /// 84 | /// Indicates that the marked method builds string by format pattern and (optional) arguments. 85 | /// Parameter, which contains format string, should be given in constructor. 86 | /// The format string should be in -like form 87 | /// 88 | /// 89 | /// 90 | /// [StringFormatMethod("message")] 91 | /// public void ShowError(string message, params object[] args) 92 | /// { 93 | /// //Do something 94 | /// } 95 | /// public void Foo() 96 | /// { 97 | /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string 98 | /// } 99 | /// 100 | /// 101 | [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 102 | public sealed class StringFormatMethodAttribute : Attribute 103 | { 104 | /// 105 | /// Initializes new instance of StringFormatMethodAttribute 106 | /// 107 | /// Specifies which parameter of an annotated method should be treated as format-string 108 | public StringFormatMethodAttribute(string formatParameterName) 109 | { 110 | FormatParameterName = formatParameterName; 111 | } 112 | 113 | /// 114 | /// Gets format parameter name 115 | /// 116 | [UsedImplicitly] public string FormatParameterName { get; private set; } 117 | } 118 | 119 | /// 120 | /// Indicates that the function argument should be string literal and match one of the parameters 121 | /// of the caller function. 122 | /// For example, ReSharper annotates the parameter of . 123 | /// 124 | /// 125 | /// 126 | /// public void Foo(string param) 127 | /// { 128 | /// if (param == null) 129 | /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol 130 | /// } 131 | /// 132 | /// 133 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] 134 | public sealed class InvokerParameterNameAttribute : Attribute { } 135 | 136 | /// 137 | /// Indicates that the method is contained in a type that implements 138 | /// interface 139 | /// and this method is used to notify that some property value changed. 140 | /// 141 | /// 142 | /// The method should be non-static and conform to one of the supported signatures: 143 | /// 144 | /// NotifyChanged(string) 145 | /// NotifyChanged(params string[]) 146 | /// NotifyChanged{T}(Expression{Func{T}}) 147 | /// NotifyChanged{T,U}(Expression{Func{T,U}}) 148 | /// SetProperty{T}(ref T, T, string) 149 | /// 150 | /// 151 | /// 152 | /// 153 | /// public class Foo : INotifyPropertyChanged 154 | /// { 155 | /// public event PropertyChangedEventHandler PropertyChanged; 156 | /// 157 | /// [NotifyPropertyChangedInvocator] 158 | /// protected virtual void NotifyChanged(string propertyName) 159 | /// {} 160 | /// 161 | /// private string _name; 162 | /// public string Name 163 | /// { 164 | /// get { return _name; } 165 | /// set 166 | /// { 167 | /// _name = value; 168 | /// NotifyChanged("LastName"); // Warning 169 | /// } 170 | /// } 171 | /// } 172 | /// 173 | /// Examples of generated notifications: 174 | /// 175 | /// NotifyChanged("Property") 176 | /// NotifyChanged(() => Property) 177 | /// NotifyChanged((VM x) => x.Property) 178 | /// SetProperty(ref myField, value, "Property") 179 | /// 180 | /// 181 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 182 | public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute 183 | { 184 | public NotifyPropertyChangedInvocatorAttribute() { } 185 | public NotifyPropertyChangedInvocatorAttribute(string parameterName) 186 | { 187 | ParameterName = parameterName; 188 | } 189 | 190 | [UsedImplicitly] public string ParameterName { get; private set; } 191 | } 192 | 193 | /// 194 | /// Indicates that the value of the marked element could be null sometimes, 195 | /// so the check for null is necessary before its usage. 196 | /// 197 | /// 198 | /// 199 | /// [CanBeNull] 200 | /// public object Test() 201 | /// { 202 | /// return null; 203 | /// } 204 | /// 205 | /// public void UseTest() 206 | /// { 207 | /// var p = Test(); 208 | /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' 209 | /// } 210 | /// 211 | /// 212 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field, AllowMultiple = false, Inherited = true)] 213 | public sealed class CanBeNullAttribute : Attribute { } 214 | 215 | /// 216 | /// Indicates that the value of the marked element could never be null 217 | /// 218 | /// 219 | /// 220 | /// [NotNull] 221 | /// public object Foo() 222 | /// { 223 | /// return null; // Warning: Possible 'null' assignment 224 | /// } 225 | /// 226 | /// 227 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field, AllowMultiple = false, Inherited = true)] 228 | public sealed class NotNullAttribute : Attribute { } 229 | 230 | /// 231 | /// Describes dependency between method input and output. 232 | /// 233 | /// 234 | ///

Function Definition Table syntax:

235 | /// 236 | /// FDT ::= FDTRow [;FDTRow]* 237 | /// FDTRow ::= Input => Output | Output <= Input 238 | /// Input ::= ParameterName: Value [, Input]* 239 | /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} 240 | /// Value ::= true | false | null | notnull | canbenull 241 | /// 242 | /// If method has single input parameter, it's name could be omitted.
243 | /// Using halt (or void/nothing, which is the same) for method output means that the methos doesn't return normally.
244 | /// canbenull annotation is only applicable for output parameters.
245 | /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute with rows separated by semicolon.
246 | ///
247 | /// 248 | /// 249 | /// 250 | /// [ContractAnnotation("=> halt")] 251 | /// public void TerminationMethod() 252 | /// 253 | /// 254 | /// [ContractAnnotation("halt <= condition: false")] 255 | /// public void Assert(bool condition, string text) // Regular Assertion method 256 | /// 257 | /// 258 | /// [ContractAnnotation("s:null => true")] 259 | /// public bool IsNullOrEmpty(string s) // String.IsNullOrEmpty 260 | /// 261 | /// 262 | /// // A method that returns null if the parameter is null, and not null if the parameter is not null 263 | /// [ContractAnnotation("null => null; notnull => notnull")] 264 | /// public object Transform(object data) 265 | /// 266 | /// 267 | /// [ContractAnnotation("s:null=>false; =>true,result:notnull; =>false, result:null")] 268 | /// public bool TryParse(string s, out Person result) 269 | /// 270 | /// 271 | /// 272 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] 273 | public sealed class ContractAnnotationAttribute : Attribute 274 | { 275 | public ContractAnnotationAttribute([NotNull] string fdt) : this (fdt, false) 276 | { 277 | } 278 | 279 | public ContractAnnotationAttribute([NotNull] string fdt, bool forceFullStates) 280 | { 281 | FDT = fdt; 282 | ForceFullStates = forceFullStates; 283 | } 284 | 285 | public string FDT { get; private set; } 286 | public bool ForceFullStates { get; private set; } 287 | } 288 | 289 | /// 290 | /// Indicates that the value of the marked type (or its derivatives) 291 | /// cannot be compared using '==' or '!=' operators and Equals() should be used instead. 292 | /// However, using '==' or '!=' for comparison with null is always permitted. 293 | /// 294 | /// 295 | /// 296 | /// [CannotApplyEqualityOperator] 297 | /// class NoEquality 298 | /// { 299 | /// } 300 | /// 301 | /// class UsesNoEquality 302 | /// { 303 | /// public void Test() 304 | /// { 305 | /// var ca1 = new NoEquality(); 306 | /// var ca2 = new NoEquality(); 307 | /// 308 | /// if (ca1 != null) // OK 309 | /// { 310 | /// bool condition = ca1 == ca2; // Warning 311 | /// } 312 | /// } 313 | /// } 314 | /// 315 | /// 316 | [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] 317 | public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } 318 | 319 | /// 320 | /// When applied to a target attribute, specifies a requirement for any type marked with 321 | /// the target attribute to implement or inherit specific type or types. 322 | /// 323 | /// 324 | /// 325 | /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement 326 | /// public class ComponentAttribute : Attribute 327 | /// {} 328 | /// 329 | /// [Component] // ComponentAttribute requires implementing IComponent interface 330 | /// public class MyComponent : IComponent 331 | /// {} 332 | /// 333 | /// 334 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] 335 | [BaseTypeRequired(typeof(Attribute))] 336 | public sealed class BaseTypeRequiredAttribute : Attribute 337 | { 338 | /// 339 | /// Initializes new instance of BaseTypeRequiredAttribute 340 | /// 341 | /// Specifies which types are required 342 | public BaseTypeRequiredAttribute(Type baseType) 343 | { 344 | BaseTypes = new[] { baseType }; 345 | } 346 | 347 | /// 348 | /// Gets enumerations of specified base types 349 | /// 350 | public Type[] BaseTypes { get; private set; } 351 | } 352 | 353 | /// 354 | /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), 355 | /// so this symbol will not be marked as unused (as well as by other usage inspections) 356 | /// 357 | [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] 358 | public sealed class UsedImplicitlyAttribute : Attribute 359 | { 360 | [UsedImplicitly] public UsedImplicitlyAttribute() 361 | : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } 362 | 363 | [UsedImplicitly] 364 | public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) 365 | { 366 | UseKindFlags = useKindFlags; 367 | TargetFlags = targetFlags; 368 | } 369 | 370 | [UsedImplicitly] public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) 371 | : this(useKindFlags, ImplicitUseTargetFlags.Default) { } 372 | 373 | [UsedImplicitly] public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) 374 | : this(ImplicitUseKindFlags.Default, targetFlags) { } 375 | 376 | [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } 377 | 378 | /// 379 | /// Gets value indicating what is meant to be used 380 | /// 381 | [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } 382 | } 383 | 384 | /// 385 | /// Should be used on attributes and causes ReSharper 386 | /// to not mark symbols marked with such attributes as unused (as well as by other usage inspections) 387 | /// 388 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] 389 | public sealed class MeansImplicitUseAttribute : Attribute 390 | { 391 | [UsedImplicitly] public MeansImplicitUseAttribute() 392 | : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } 393 | 394 | [UsedImplicitly] 395 | public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) 396 | { 397 | UseKindFlags = useKindFlags; 398 | TargetFlags = targetFlags; 399 | } 400 | 401 | [UsedImplicitly] public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) 402 | : this(useKindFlags, ImplicitUseTargetFlags.Default) 403 | { 404 | } 405 | 406 | [UsedImplicitly] public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) 407 | : this(ImplicitUseKindFlags.Default, targetFlags) { } 408 | 409 | [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } 410 | 411 | /// 412 | /// Gets value indicating what is meant to be used 413 | /// 414 | [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } 415 | } 416 | 417 | [Flags] 418 | public enum ImplicitUseKindFlags 419 | { 420 | Default = Access | Assign | InstantiatedWithFixedConstructorSignature, 421 | 422 | /// 423 | /// Only entity marked with attribute considered used 424 | /// 425 | Access = 1, 426 | 427 | /// 428 | /// Indicates implicit assignment to a member 429 | /// 430 | Assign = 2, 431 | 432 | /// 433 | /// Indicates implicit instantiation of a type with fixed constructor signature. 434 | /// That means any unused constructor parameters won't be reported as such. 435 | /// 436 | InstantiatedWithFixedConstructorSignature = 4, 437 | 438 | /// 439 | /// Indicates implicit instantiation of a type 440 | /// 441 | InstantiatedNoFixedConstructorSignature = 8, 442 | } 443 | 444 | /// 445 | /// Specify what is considered used implicitly when marked with or 446 | /// 447 | [Flags] 448 | public enum ImplicitUseTargetFlags 449 | { 450 | Default = Itself, 451 | 452 | Itself = 1, 453 | 454 | /// 455 | /// Members of entity marked with attribute are considered used 456 | /// 457 | Members = 2, 458 | 459 | /// 460 | /// Entity marked with attribute and all its members considered used 461 | /// 462 | WithMembers = Itself | Members 463 | } 464 | 465 | /// 466 | /// This attribute is intended to mark publicly available API which should not be removed and so is treated as used. 467 | /// 468 | [MeansImplicitUse] 469 | public sealed class PublicAPIAttribute : Attribute 470 | { 471 | public PublicAPIAttribute() { } 472 | public PublicAPIAttribute(string comment) { } 473 | } 474 | 475 | /// 476 | /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. 477 | /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. 478 | /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. 479 | /// 480 | [AttributeUsage(AttributeTargets.Parameter, Inherited = true)] 481 | public sealed class InstantHandleAttribute : Attribute { } 482 | 483 | 484 | /// 485 | /// Indicates that a method does not make any observable state changes. 486 | /// The same as 487 | /// 488 | /// 489 | /// 490 | /// [Pure] 491 | /// private int Multiply(int x, int y) 492 | /// { 493 | /// return x*y; 494 | /// } 495 | /// 496 | /// public void Foo() 497 | /// { 498 | /// const int a=2, b=2; 499 | /// Multiply(a, b); // Waring: Return value of pure method is not used 500 | /// } 501 | /// 502 | /// 503 | [AttributeUsage(AttributeTargets.Method, Inherited = true)] 504 | public sealed class PureAttribute : Attribute { } 505 | 506 | /// 507 | /// Indicates that a parameter is a path to a file or a folder within a web project. 508 | /// Path can be relative or absolute, starting from web root (~). 509 | /// 510 | [AttributeUsage(AttributeTargets.Parameter)] 511 | public class PathReferenceAttribute : Attribute 512 | { 513 | public PathReferenceAttribute() { } 514 | 515 | [UsedImplicitly] 516 | public PathReferenceAttribute([PathReference] string basePath) 517 | { 518 | BasePath = basePath; 519 | } 520 | 521 | [UsedImplicitly] public string BasePath { get; private set; } 522 | } 523 | 524 | // ASP.NET MVC attributes 525 | 526 | /// 527 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC action. 528 | /// If applied to a method, the MVC action name is calculated implicitly from the context. 529 | /// Use this attribute for custom wrappers similar to 530 | /// 531 | /// 532 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 533 | public sealed class AspMvcActionAttribute : Attribute 534 | { 535 | [UsedImplicitly] public string AnonymousProperty { get; private set; } 536 | 537 | public AspMvcActionAttribute() { } 538 | 539 | public AspMvcActionAttribute(string anonymousProperty) 540 | { 541 | AnonymousProperty = anonymousProperty; 542 | } 543 | } 544 | 545 | /// 546 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC araa. 547 | /// Use this attribute for custom wrappers similar to 548 | /// 549 | /// 550 | [AttributeUsage(AttributeTargets.Parameter)] 551 | public sealed class AspMvcAreaAttribute : PathReferenceAttribute 552 | { 553 | [UsedImplicitly] public string AnonymousProperty { get; private set; } 554 | 555 | [UsedImplicitly] public AspMvcAreaAttribute() { } 556 | 557 | public AspMvcAreaAttribute(string anonymousProperty) 558 | { 559 | AnonymousProperty = anonymousProperty; 560 | } 561 | } 562 | 563 | /// 564 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC controller. 565 | /// If applied to a method, the MVC controller name is calculated implicitly from the context. 566 | /// Use this attribute for custom wrappers similar to 567 | /// 568 | /// 569 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 570 | public sealed class AspMvcControllerAttribute : Attribute 571 | { 572 | [UsedImplicitly] public string AnonymousProperty { get; private set; } 573 | 574 | public AspMvcControllerAttribute() { } 575 | 576 | public AspMvcControllerAttribute(string anonymousProperty) 577 | { 578 | AnonymousProperty = anonymousProperty; 579 | } 580 | } 581 | 582 | /// 583 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. 584 | /// Use this attribute for custom wrappers similar to 585 | /// 586 | /// 587 | [AttributeUsage(AttributeTargets.Parameter)] 588 | public sealed class AspMvcMasterAttribute : Attribute { } 589 | 590 | /// 591 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. 592 | /// Use this attribute for custom wrappers similar to 593 | /// 594 | /// 595 | [AttributeUsage(AttributeTargets.Parameter)] 596 | public sealed class AspMvcModelTypeAttribute : Attribute { } 597 | 598 | /// 599 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC partial view. 600 | /// If applied to a method, the MVC partial view name is calculated implicitly from the context. 601 | /// Use this attribute for custom wrappers similar to 602 | /// 603 | /// 604 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 605 | public sealed class AspMvcPartialViewAttribute : PathReferenceAttribute { } 606 | 607 | /// 608 | /// ASP.NET MVC attribute. Allows disabling all inspections for MVC views within a class or a method. 609 | /// 610 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 611 | public sealed class AspMvcSupressViewErrorAttribute : Attribute { } 612 | 613 | /// 614 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. 615 | /// Use this attribute for custom wrappers similar to 616 | /// 617 | /// 618 | [AttributeUsage(AttributeTargets.Parameter)] 619 | public sealed class AspMvcDisplayTemplateAttribute : Attribute { } 620 | 621 | /// 622 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. 623 | /// Use this attribute for custom wrappers similar to 624 | /// 625 | /// 626 | [AttributeUsage(AttributeTargets.Parameter)] 627 | public sealed class AspMvcEditorTemplateAttribute : Attribute { } 628 | 629 | /// 630 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC view. 631 | /// If applied to a method, the MVC view name is calculated implicitly from the context. 632 | /// Use this attribute for custom wrappers similar to 633 | /// 634 | /// 635 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 636 | public sealed class AspMvcViewAttribute : PathReferenceAttribute { } 637 | 638 | /// 639 | /// ASP.NET MVC attribute. When applied to a parameter of an attribute, 640 | /// indicates that this parameter is an MVC action name. 641 | /// 642 | /// 643 | /// 644 | /// [ActionName("Foo")] 645 | /// public ActionResult Login(string returnUrl) 646 | /// { 647 | /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK 648 | /// return RedirectToAction("Bar"); // Error: Cannot resolve action 649 | /// } 650 | /// 651 | /// 652 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] 653 | public sealed class AspMvcActionSelectorAttribute : Attribute { } 654 | 655 | // Razor attributes 656 | 657 | /// 658 | /// Razor attribute. Indicates that a parameter or a method is a Razor section. 659 | /// Use this attribute for custom wrappers similar to 660 | /// 661 | /// 662 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, Inherited = true)] 663 | public sealed class RazorSectionAttribute : Attribute { } 664 | 665 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("MiniC.Compiler.Demo")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("MiniC.Compiler.Demo")] 15 | [assembly: AssemblyCopyright("Copyright © 2013")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.17929 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MiniC.Compiler.Demo.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MiniC.Compiler.Demo.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.17929 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MiniC.Compiler.Demo.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings) (global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/TreeItems/CompoundStatementItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.FSharp.Collections; 4 | 5 | namespace MiniC.Compiler.Demo.TreeItems 6 | { 7 | public class CompoundStatementItem : TreeItemBase 8 | { 9 | public CompoundStatementItem(Tuple, FSharpList> compoundStatement, 10 | string name = "Compound Statement") 11 | { 12 | Text = name; 13 | Children = new TreeItemBase[] 14 | { 15 | new SimpleItem("Local Declarations", compoundStatement.Item1.Select(x => new VariableDeclarationItem(x))), 16 | new SimpleItem("Statements", compoundStatement.Item2.Select(x => new StatementItem(x))) 17 | }; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/TreeItems/DeclarationItem.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace MiniC.Compiler.Demo.TreeItems 4 | { 5 | public class DeclarationItem : TreeItemBase 6 | { 7 | public DeclarationItem(Ast.Declaration declaration) 8 | { 9 | if (declaration.IsFunctionDeclaration) 10 | { 11 | var typedDeclaration = (Ast.Declaration.FunctionDeclaration) declaration; 12 | Text = "Function Declaration (Name = " + typedDeclaration.Item.Item2 + ")"; 13 | Children = new TreeItemBase[] 14 | { 15 | new SimpleItem("Return Type: " + typedDeclaration.Item.Item1), 16 | new SimpleItem("Name: " + typedDeclaration.Item.Item2), 17 | new SimpleItem("Parameters", typedDeclaration.Item.Item3.Select(x => new VariableDeclarationItem(x))), 18 | new CompoundStatementItem(typedDeclaration.Item.Item4, "Body") 19 | }; 20 | } 21 | else if (declaration.IsStaticVariableDeclaration) 22 | { 23 | Text = "Static Variable Declaration"; 24 | var typedDeclaration = (Ast.Declaration.StaticVariableDeclaration) declaration; 25 | if (typedDeclaration.Item.IsScalarVariableDeclaration) 26 | Children = new[] { new VariableDeclarationItem(typedDeclaration.Item) }; 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/TreeItems/ExpressionItem.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace MiniC.Compiler.Demo.TreeItems 4 | { 5 | public class ExpressionItem : TreeItemBase 6 | { 7 | public ExpressionItem(Ast.Expression expression) 8 | { 9 | if (expression.IsArrayAllocationExpression) 10 | { 11 | var typedExpression = (Ast.Expression.ArrayAllocationExpression) expression; 12 | Text = "Array Allocation Expression"; 13 | Children = new TreeItemBase[] 14 | { 15 | new SimpleItem("Type: " + typedExpression.Item1), 16 | new SimpleItem("Size", new[] { new ExpressionItem(typedExpression.Item2) }) 17 | }; 18 | } 19 | else if (expression.IsArrayIdentifierExpression) 20 | { 21 | var typedExpression = (Ast.Expression.ArrayIdentifierExpression) expression; 22 | Text = "Array Identifier Expression: " + typedExpression.Item1.Identifier; 23 | Children = new[] 24 | { 25 | new SimpleItem("Index", new[] { new ExpressionItem(typedExpression.Item2) }) 26 | }; 27 | } 28 | else if (expression.IsArraySizeExpression) 29 | { 30 | var typedExpression = (Ast.Expression.ArraySizeExpression) expression; 31 | Text = "Array Size Expression: " + typedExpression.Item.Identifier; 32 | } 33 | else if (expression.IsScalarAssignmentExpression) 34 | { 35 | var typedExpression = (Ast.Expression.ScalarAssignmentExpression) expression; 36 | Text = "Scalar Assignment Expression"; 37 | Children = new TreeItemBase[] 38 | { 39 | new SimpleItem("Variable: " + typedExpression.Item1.Identifier), 40 | new SimpleItem("Expression", new[] { new ExpressionItem(typedExpression.Item2) }) 41 | }; 42 | } 43 | else if (expression.IsArrayAssignmentExpression) 44 | { 45 | var typedExpression = (Ast.Expression.ArrayAssignmentExpression) expression; 46 | Text = "Array Assignment Expression"; 47 | Children = new TreeItemBase[] 48 | { 49 | new SimpleItem("Variable: " + typedExpression.Item1.Identifier), 50 | new SimpleItem("Index", new[] { new ExpressionItem(typedExpression.Item2) }), 51 | new SimpleItem("Expression", new[] { new ExpressionItem(typedExpression.Item3) }) 52 | }; 53 | } 54 | else if (expression.IsBinaryExpression) 55 | { 56 | var typedExpression = (Ast.Expression.BinaryExpression) expression; 57 | Text = "Binary Expression"; 58 | Children = new TreeItemBase[] 59 | { 60 | new SimpleItem("Left", new[] { new ExpressionItem(typedExpression.Item1) }), 61 | new SimpleItem("Operator: " + typedExpression.Item2), 62 | new SimpleItem("Right", new[] { new ExpressionItem(typedExpression.Item3) }) 63 | }; 64 | } 65 | else if (expression.IsFunctionCallExpression) 66 | { 67 | var typedExpression = (Ast.Expression.FunctionCallExpression) expression; 68 | Text = "Function Call Expression"; 69 | Children = new TreeItemBase[] 70 | { 71 | new SimpleItem("Name: " + typedExpression.Item1), 72 | new SimpleItem("Arguments", typedExpression.Item2.Select(x => new ExpressionItem(x))) 73 | }; 74 | } 75 | else if (expression.IsIdentifierExpression) 76 | { 77 | Text = "Identifier Expression: " + ((Ast.Expression.IdentifierExpression) expression).Item.Identifier; 78 | } 79 | else if (expression.IsLiteralExpression) 80 | { 81 | Text = "Literal Expression: " + ((Ast.Expression.LiteralExpression) expression).Item; 82 | } 83 | else if (expression.IsUnaryExpression) 84 | { 85 | var typedExpression = (Ast.Expression.UnaryExpression) expression; 86 | Text = "Unary Expression"; 87 | Children = new TreeItemBase[] 88 | { 89 | new SimpleItem("Operator: " + typedExpression.Item1), 90 | new SimpleItem("Expression", new[] { new ExpressionItem(typedExpression.Item2) }) 91 | }; 92 | } 93 | else 94 | throw new System.NotImplementedException(); 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/TreeItems/SimpleItem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiniC.Compiler.Demo.TreeItems 4 | { 5 | public class SimpleItem : TreeItemBase 6 | { 7 | public SimpleItem(string value, IEnumerable children = null) 8 | { 9 | Text = value; 10 | Children = children; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/TreeItems/StatementItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.FSharp.Core; 4 | 5 | namespace MiniC.Compiler.Demo.TreeItems 6 | { 7 | public class StatementItem : TreeItemBase 8 | { 9 | public StatementItem(Ast.Statement statement) 10 | { 11 | if (statement.IsBreakStatement) 12 | { 13 | Text = "Break Statement"; 14 | } 15 | else if (statement.IsCompoundStatement) 16 | { 17 | var typedStatement = (Ast.Statement.CompoundStatement) statement; 18 | Text = "Compound Statement"; 19 | Children = new TreeItemBase[] 20 | { 21 | new SimpleItem("Local Declarations", typedStatement.Item.Item1.Select(x => new VariableDeclarationItem(x))), 22 | new SimpleItem("Statements", typedStatement.Item.Item2.Select(x => new StatementItem(x))) 23 | }; 24 | } 25 | else if (statement.IsExpressionStatement) 26 | { 27 | var typedStatement = (Ast.Statement.ExpressionStatement) statement; 28 | Text = "Expression Statement"; 29 | if (typedStatement.Item.IsNop) 30 | { 31 | Children = new[] { new SimpleItem("Nop") }; 32 | } 33 | else if (typedStatement.Item.IsExpression) 34 | { 35 | var typedStatement2 = (Ast.ExpressionStatement.Expression) typedStatement.Item; 36 | Children = new[] { new ExpressionItem(typedStatement2.Item) }; 37 | } 38 | } 39 | else if (statement.IsIfStatement) 40 | { 41 | var typedStatement = (Ast.Statement.IfStatement) statement; 42 | Text = "If Statement"; 43 | Children = new TreeItemBase[] 44 | { 45 | new SimpleItem("Condition", new[] { new ExpressionItem(typedStatement.Item.Item1) }), 46 | new SimpleItem("Then", new[] { new StatementItem(typedStatement.Item.Item2) }) 47 | }; 48 | if (FSharpOption.get_IsSome(typedStatement.Item.Item3)) 49 | Children = Children.Union(new[] 50 | { 51 | new SimpleItem("Else", new[] 52 | { 53 | new StatementItem(typedStatement.Item.Item3.Value) 54 | }) 55 | }); 56 | } 57 | else if (statement.IsReturnStatement) 58 | { 59 | var typedStatement = (Ast.Statement.ReturnStatement) statement; 60 | Text = "Return Statement"; 61 | if (FSharpOption.get_IsSome(typedStatement.Item)) 62 | Children = new[] { new ExpressionItem(typedStatement.Item.Value) }; 63 | } 64 | else if (statement.IsWhileStatement) 65 | { 66 | var typedStatement = (Ast.Statement.WhileStatement) statement; 67 | Text = "While Statement"; 68 | Children = new TreeItemBase[] 69 | { 70 | new SimpleItem("Condition", new[] { new ExpressionItem(typedStatement.Item.Item1) }), 71 | new SimpleItem("Body", new[] { new StatementItem(typedStatement.Item.Item2) }) 72 | }; 73 | } 74 | else 75 | throw new NotImplementedException(); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/TreeItems/TreeItemBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiniC.Compiler.Demo.TreeItems 4 | { 5 | public abstract class TreeItemBase 6 | { 7 | public string Text { get; protected set; } 8 | public IEnumerable Children { get; protected set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/TreeItems/VariableDeclarationItem.cs: -------------------------------------------------------------------------------- 1 | namespace MiniC.Compiler.Demo.TreeItems 2 | { 3 | public class VariableDeclarationItem : TreeItemBase 4 | { 5 | public VariableDeclarationItem(Ast.VariableDeclaration variableDeclaration) 6 | { 7 | if (variableDeclaration.IsScalarVariableDeclaration) 8 | { 9 | var scalarDeclaration = (Ast.VariableDeclaration.ScalarVariableDeclaration) variableDeclaration; 10 | Text = scalarDeclaration.Item1 + " " + scalarDeclaration.Item2; 11 | } 12 | else 13 | { 14 | var arrayDeclaration = (Ast.VariableDeclaration.ArrayVariableDeclaration) variableDeclaration; 15 | Text = arrayDeclaration.Item1 + " " + arrayDeclaration.Item2; 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Demo/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/CompilerTests.fs: -------------------------------------------------------------------------------- 1 | module MiniC.Compiler.Tests.CompilerTests 2 | 3 | open System 4 | open System.Diagnostics 5 | open System.IO 6 | open System.Reflection 7 | open NUnit.Framework 8 | open MiniC.Compiler 9 | 10 | [] 11 | let cleanup() = 12 | if Directory.Exists "Binaries" then 13 | Directory.Delete("Binaries", true) 14 | 15 | [] 16 | let ``can compile and run assembly in memory``() = 17 | let code = "int main(void) { return 123; }" 18 | let (compiledType, entryPoint) = Compiler.compileToMemory (new AssemblyName "Foo") code 19 | 20 | Assert.That(compiledType, Is.Not.Null) 21 | Assert.That(entryPoint, Is.Not.Null) 22 | 23 | // Use reflection to execute. 24 | let result = entryPoint.Invoke(None, Array.empty) 25 | Assert.That(result, Is.EqualTo(123)) 26 | 27 | [] 28 | [] 29 | [] 30 | [] 31 | [] 32 | [] 33 | let ``can compile, save and execute console application with correct return value`` sourceFile = 34 | let code = File.ReadAllText(Path.Combine("Sources", sourceFile)) 35 | let targetFileName = Path.Combine("Sources", Path.GetFileNameWithoutExtension(sourceFile) + ".exe") 36 | Compiler.compileToFile targetFileName code 37 | 38 | let testProcess = Process.Start(targetFileName) 39 | testProcess.WaitForExit() 40 | 41 | testProcess.ExitCode 42 | 43 | [] 44 | let ``can compile, save and execute application with I/O``() = 45 | let sourceFile = "test7.minic" 46 | let code = File.ReadAllText(Path.Combine("Sources", sourceFile)) 47 | let targetFileName = Path.Combine("Sources", Path.GetFileNameWithoutExtension(sourceFile) + ".exe") 48 | Compiler.compileToFile targetFileName code 49 | 50 | let processStartInfo = ProcessStartInfo(targetFileName) 51 | processStartInfo.RedirectStandardInput <- true 52 | processStartInfo.RedirectStandardOutput <- true 53 | processStartInfo.UseShellExecute <- false 54 | let testProcess = Process.Start(processStartInfo) 55 | 56 | let output = ref "" 57 | testProcess.OutputDataReceived.Add (fun args -> output := !output + args.Data + "\n") 58 | 59 | testProcess.BeginOutputReadLine() 60 | 61 | let inputStream = testProcess.StandardInput 62 | inputStream.WriteLine "9" 63 | inputStream.WriteLine "200" 64 | 65 | inputStream.Close() 66 | 67 | testProcess.WaitForExit() 68 | 69 | Assert.That(testProcess.ExitCode, Is.EqualTo 0) 70 | Assert.That(!output, Is.EqualTo ("3\n400\n\n")) 71 | 72 | [] 73 | [] 74 | [] 75 | [] 76 | [=,>,&&,\.")>] 77 | [] 78 | [] 79 | [] 80 | [] 81 | [] 82 | [] 83 | [] 84 | [] 85 | [] 86 | [] 87 | [] 88 | [] 89 | [] 90 | [] 91 | [] 92 | [] 93 | [] 94 | [] 95 | [] 96 | [] 97 | let ``can detect semantic errors`` sourceFile (compilerError : string) = 98 | let code = File.ReadAllText(Path.Combine("Sources", sourceFile)) 99 | Assert.That( 100 | (fun () -> Compiler.compileToMemory (AssemblyName(Path.GetFileNameWithoutExtension sourceFile)) code |> ignore), 101 | Throws.TypeOf().With.Message.EqualTo compilerError) -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/ILBuilderTests.fs: -------------------------------------------------------------------------------- 1 | module MiniC.Compiler.Tests.ILBuilderTests 2 | 3 | open NUnit.Framework 4 | open MiniC.Compiler 5 | open MiniC.Compiler.IL 6 | 7 | [] 8 | let ``can build int return value``() = 9 | let program = 10 | [ 11 | Ast.FunctionDeclaration( 12 | Ast.Int, "main", [], 13 | ([], [ Ast.ReturnStatement( 14 | Some (Ast.LiteralExpression (Ast.IntLiteral 123)) 15 | ) 16 | ]) 17 | ) 18 | ] 19 | let semanticAnalysisResult = SemanticAnalysis.analyze program 20 | let ilBuilder = new ILBuilder(semanticAnalysisResult) 21 | let result = ilBuilder.BuildClass program 22 | let expected = 23 | { 24 | Name = "main"; 25 | ReturnType = typeof; 26 | Parameters = []; 27 | Locals = []; 28 | Body = [ IL.Ldc_I4(123); IL.Ret ]; 29 | } 30 | Assert.That(result.Fields, Is.Empty) 31 | Assert.That(result.Methods, Has.Length.EqualTo 5) 32 | Assert.That(result.Methods.[4], Is.EqualTo expected) 33 | 34 | [] 35 | let ``can build binary expression``() = 36 | let program = 37 | [ 38 | Ast.FunctionDeclaration( 39 | Ast.Int, "main", [], 40 | ( 41 | [], 42 | [ 43 | Ast.ReturnStatement( 44 | Some ( 45 | Ast.BinaryExpression( 46 | Ast.LiteralExpression (Ast.IntLiteral 123), 47 | Ast.Add, 48 | Ast.LiteralExpression (Ast.IntLiteral 456) 49 | ) 50 | ) 51 | ) 52 | ] 53 | ) 54 | ) 55 | ] 56 | let semanticAnalysisResult = SemanticAnalysis.analyze program 57 | let ilBuilder = new ILBuilder(semanticAnalysisResult) 58 | let result = ilBuilder.BuildClass program 59 | let expected = 60 | { 61 | Name = "main"; 62 | ReturnType = typeof; 63 | Parameters = []; 64 | Locals = []; 65 | Body = [ IL.Ldc_I4(123); IL.Ldc_I4(456); IL.Add; IL.Ret ]; 66 | } 67 | Assert.That(result.Fields, Is.Empty) 68 | Assert.That(result.Methods, Has.Length.EqualTo 5) 69 | Assert.That(result.Methods.[4], Is.EqualTo expected) -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/MiniC.Compiler.Tests.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | ada721d1-b2fb-4c8c-a435-8d6649a050f2 9 | Library 10 | MiniC.Compiler.Tests 11 | MiniC.Compiler.Tests 12 | v4.0 13 | MiniC.Compiler.Tests 14 | 15 | 4.3.0.0 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 3 25 | bin\Debug\MiniC.Compiler.Tests.XML 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | bin\Release\MiniC.Compiler.Tests.XML 35 | 36 | 37 | 11 38 | 39 | 40 | 41 | 42 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 43 | 44 | 45 | 46 | 47 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 48 | 49 | 50 | 51 | 52 | 53 | 54 | PreserveNewest 55 | 56 | 57 | PreserveNewest 58 | 59 | 60 | PreserveNewest 61 | 62 | 63 | PreserveNewest 64 | 65 | 66 | PreserveNewest 67 | 68 | 69 | PreserveNewest 70 | 71 | 72 | PreserveNewest 73 | 74 | 75 | PreserveNewest 76 | 77 | 78 | PreserveNewest 79 | 80 | 81 | PreserveNewest 82 | 83 | 84 | PreserveNewest 85 | 86 | 87 | PreserveNewest 88 | 89 | 90 | PreserveNewest 91 | 92 | 93 | PreserveNewest 94 | 95 | 96 | PreserveNewest 97 | 98 | 99 | PreserveNewest 100 | 101 | 102 | PreserveNewest 103 | 104 | 105 | PreserveNewest 106 | 107 | 108 | PreserveNewest 109 | 110 | 111 | PreserveNewest 112 | 113 | 114 | PreserveNewest 115 | 116 | 117 | PreserveNewest 118 | 119 | 120 | PreserveNewest 121 | 122 | 123 | PreserveNewest 124 | 125 | 126 | PreserveNewest 127 | 128 | 129 | PreserveNewest 130 | 131 | 132 | PreserveNewest 133 | 134 | 135 | PreserveNewest 136 | 137 | 138 | PreserveNewest 139 | 140 | 141 | PreserveNewest 142 | 143 | 144 | PreserveNewest 145 | 146 | 147 | PreserveNewest 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | True 159 | 160 | 161 | 162 | ..\packages\NUnit.2.6.2\lib\nunit.framework.dll 163 | True 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | MiniC.Compiler 172 | {2860d24f-85d9-4bc7-8463-ee1ec134290d} 173 | True 174 | 175 | 176 | 183 | -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/ParserTests.fs: -------------------------------------------------------------------------------- 1 | module MiniC.Compiler.Tests.ParserTests 2 | 3 | open NUnit.Framework 4 | open MiniC.Compiler 5 | 6 | [] 7 | let ``can parse empty main method``() = 8 | let result = Parser.parse "void main(void) { }" 9 | let expected = 10 | [ Ast.FunctionDeclaration(Ast.Void, "main", [], ([], [])) ] 11 | Assert.That(result, Is.EqualTo(expected)) 12 | 13 | [] 14 | let ``can parse empty main method with random whitespace``() = 15 | let result = Parser.parse " void main ( void ) { } " 16 | let expected = 17 | [ Ast.FunctionDeclaration(Ast.Void, "main", [], ([], [])) ] 18 | Assert.That(result, Is.EqualTo(expected)) 19 | 20 | [] 21 | let ``can parse nearly-empty main method``() = 22 | let result = Parser.parse " 23 | void main(void) { 24 | ; 25 | }" 26 | let expected = 27 | [ Ast.FunctionDeclaration(Ast.Void, "main", [], ([], [Ast.ExpressionStatement(Ast.Nop)])) ] 28 | Assert.That(result, Is.EqualTo(expected)) 29 | 30 | [] 31 | let ``can parse binary arithmetic expressions``() = 32 | let doTest operator binaryOperator = 33 | let code = (sprintf " 34 | void main(void) { 35 | 123%s456; 36 | }" operator) 37 | let result = Parser.parse code 38 | let expected = 39 | [Ast.FunctionDeclaration( 40 | Ast.Void, "main", [], 41 | ( 42 | [], 43 | [ 44 | Ast.ExpressionStatement( 45 | Ast.Expression( 46 | Ast.BinaryExpression( 47 | Ast.LiteralExpression(Ast.IntLiteral(123)), 48 | binaryOperator, 49 | Ast.LiteralExpression(Ast.IntLiteral(456)) 50 | ) 51 | ) 52 | ) 53 | ] 54 | ) 55 | )] 56 | Assert.That(result, Is.EqualTo(expected)) 57 | 58 | doTest "+" Ast.Add 59 | doTest "-" Ast.Subtract 60 | doTest "*" Ast.Multiply 61 | doTest "/" Ast.Divide 62 | doTest "%" Ast.Modulus 63 | 64 | [] 65 | let ``can parse unary arithmetic expressions``() = 66 | let doTest operator unaryOperator = 67 | let code = (sprintf " 68 | void main(void) { 69 | %s123; 70 | }" operator) 71 | let result = Parser.parse code 72 | let expected = 73 | [Ast.FunctionDeclaration( 74 | Ast.Void, "main", [], 75 | ( 76 | [], 77 | [ 78 | Ast.ExpressionStatement( 79 | Ast.Expression( 80 | Ast.UnaryExpression( 81 | unaryOperator, 82 | Ast.LiteralExpression(Ast.IntLiteral(123)) 83 | ) 84 | ) 85 | ) 86 | ] 87 | ) 88 | )] 89 | Assert.That(result, Is.EqualTo(expected)) 90 | 91 | doTest "!" Ast.LogicalNegate 92 | doTest "-" Ast.Negate 93 | doTest "+" Ast.Identity 94 | 95 | [] 96 | let ``can parse return void statement``() = 97 | let result = Parser.parse " 98 | void main(void) { 99 | return; 100 | }" 101 | let expected = 102 | [Ast.FunctionDeclaration(Ast.Void, "main", [], ([], [Ast.ReturnStatement(None)])) ] 103 | Assert.That(result, Is.EqualTo(expected)) 104 | 105 | [] 106 | let ``can parse return expression statement``() = 107 | let result = Parser.parse " 108 | int foo(void) { 109 | return 123; 110 | }" 111 | let expected = 112 | [Ast.FunctionDeclaration( 113 | Ast.Int, "foo", [], 114 | ( 115 | [], 116 | [ 117 | Ast.ReturnStatement( 118 | Some(Ast.LiteralExpression(Ast.IntLiteral(123))) 119 | ) 120 | ] 121 | ) 122 | )] 123 | Assert.That(result, Is.EqualTo(expected)) 124 | 125 | [] 126 | let ``can parse parameters and expression``() = 127 | let result = Parser.parse " 128 | int foo(int a, float b[]) { 129 | return a*123 + 456; 130 | }" 131 | let expected = 132 | [Ast.FunctionDeclaration( 133 | Ast.Int, "foo", 134 | [ 135 | Ast.ScalarVariableDeclaration (Ast.Int, "a") 136 | Ast.ArrayVariableDeclaration (Ast.Float, "b") 137 | ], 138 | ( 139 | [], 140 | [ 141 | Ast.ReturnStatement( 142 | Some( 143 | Ast.BinaryExpression( 144 | Ast.BinaryExpression( 145 | Ast.IdentifierExpression { Identifier = "a" }, 146 | Ast.Multiply, 147 | Ast.LiteralExpression(Ast.IntLiteral 123) 148 | ), 149 | Ast.Add, 150 | Ast.LiteralExpression(Ast.IntLiteral 456) 151 | ) 152 | ) 153 | ) 154 | ] 155 | ) 156 | )] 157 | Assert.That(result, Is.EqualTo(expected)) 158 | 159 | [] 160 | let ``can parse if statement``() = 161 | let result = Parser.parse " 162 | int bar(int a, bool b) { 163 | if (b) 164 | return a; 165 | return 1234*a; 166 | }" 167 | let expected = 168 | [Ast.FunctionDeclaration( 169 | Ast.Int, 170 | "bar", 171 | [ 172 | Ast.ScalarVariableDeclaration (Ast.Int, "a") 173 | Ast.ScalarVariableDeclaration (Ast.Bool, "b") 174 | ], 175 | ( 176 | [], 177 | [ 178 | Ast.IfStatement( 179 | Ast.IdentifierExpression { Identifier = "b" }, 180 | Ast.ReturnStatement(Some(Ast.IdentifierExpression { Identifier = "a" })), 181 | None 182 | ) 183 | Ast.ReturnStatement( 184 | Some( 185 | Ast.BinaryExpression( 186 | Ast.LiteralExpression(Ast.IntLiteral(1234)), 187 | Ast.Multiply, 188 | Ast.IdentifierExpression { Identifier = "a" } 189 | ) 190 | ) 191 | ) 192 | ] 193 | ) 194 | )] 195 | Assert.That(result, Is.EqualTo(expected)) 196 | 197 | [] 198 | let ``can parse complex arithmetic expression``() = 199 | let result = Parser.parse " 200 | int z; 201 | float y[]; 202 | 203 | int baz(int a, int b) { 204 | return 1234*z % 456/b + 789; 205 | }" 206 | let expected = 207 | [ 208 | Ast.StaticVariableDeclaration(Ast.ScalarVariableDeclaration(Ast.Int, "z")) 209 | Ast.StaticVariableDeclaration(Ast.ArrayVariableDeclaration(Ast.Float, "y")) 210 | Ast.FunctionDeclaration( 211 | Ast.Int, "baz", 212 | [ 213 | Ast.ScalarVariableDeclaration (Ast.Int, "a") 214 | Ast.ScalarVariableDeclaration (Ast.Int, "b") 215 | ], 216 | ( 217 | [], 218 | [ 219 | Ast.ReturnStatement( 220 | Some( 221 | Ast.BinaryExpression( 222 | Ast.BinaryExpression( 223 | Ast.BinaryExpression( 224 | Ast.BinaryExpression( 225 | Ast.LiteralExpression(Ast.IntLiteral(1234)), 226 | Ast.Multiply, 227 | Ast.IdentifierExpression { Identifier = "z" } 228 | ), 229 | Ast.Modulus, 230 | Ast.LiteralExpression(Ast.IntLiteral(456)) 231 | ), 232 | Ast.Divide, 233 | Ast.IdentifierExpression { Identifier = "b" } 234 | ), 235 | Ast.Add, 236 | Ast.LiteralExpression(Ast.IntLiteral(789)) 237 | ) 238 | ) 239 | ) 240 | ] 241 | ) 242 | ) 243 | ] 244 | Assert.That(result, Is.EqualTo(expected)) 245 | 246 | [] 247 | let ``can parse logical negation and unary subtraction expression``() = 248 | let result = Parser.parse " 249 | int foo(bool b) { 250 | if (!b) 251 | return -1234*a; 252 | }" 253 | let expected = 254 | [Ast.FunctionDeclaration( 255 | Ast.Int, 256 | "foo", 257 | [ Ast.ScalarVariableDeclaration (Ast.Bool, "b") ], 258 | ( 259 | [], 260 | [ 261 | Ast.IfStatement( 262 | Ast.UnaryExpression (Ast.LogicalNegate, Ast.IdentifierExpression { Identifier = "b" }), 263 | Ast.ReturnStatement( 264 | Some( 265 | Ast.BinaryExpression( 266 | Ast.UnaryExpression( 267 | Ast.Negate, 268 | Ast.LiteralExpression(Ast.IntLiteral(1234)) 269 | ), 270 | Ast.Multiply, 271 | Ast.IdentifierExpression { Identifier = "a" } 272 | ) 273 | ) 274 | ), 275 | None 276 | ) 277 | ] 278 | ) 279 | )] 280 | Assert.That(result, Is.EqualTo(expected)) 281 | 282 | [] 283 | let ``can parse if / else statement``() = 284 | let result = Parser.parse " 285 | int main(int a, bool b) { 286 | if (b) 287 | return a; 288 | else 289 | return 1; 290 | }" 291 | let expected = 292 | [Ast.FunctionDeclaration( 293 | Ast.Int, 294 | "main", 295 | [ 296 | Ast.ScalarVariableDeclaration (Ast.Int, "a") 297 | Ast.ScalarVariableDeclaration (Ast.Bool, "b") 298 | ], 299 | ( 300 | [], 301 | [ 302 | Ast.IfStatement( 303 | Ast.IdentifierExpression { Identifier = "b" }, 304 | Ast.ReturnStatement(Some(Ast.IdentifierExpression { Identifier = "a" })), 305 | Some(Ast.ReturnStatement(Some(Ast.LiteralExpression (Ast.IntLiteral 1)))) 306 | ) 307 | ] 308 | ) 309 | )] 310 | Assert.That(result, Is.EqualTo(expected)) 311 | 312 | [] 313 | let ``can parse while and break statements``() = 314 | let result = Parser.parse " 315 | int main(bool b) { 316 | while (b) 317 | break; 318 | return 1; 319 | }" 320 | let expected = 321 | [Ast.FunctionDeclaration( 322 | Ast.Int, "main", 323 | [ Ast.ScalarVariableDeclaration (Ast.Bool, "b") ], 324 | ( 325 | [], 326 | [ 327 | Ast.WhileStatement( 328 | Ast.IdentifierExpression { Identifier = "b" }, 329 | Ast.BreakStatement 330 | ) 331 | Ast.ReturnStatement(Some(Ast.LiteralExpression (Ast.IntLiteral 1))) 332 | ] 333 | ) 334 | )] 335 | Assert.That(result, Is.EqualTo(expected)) 336 | 337 | [] 338 | let ``can parse local declarations in compound statements``() = 339 | let result = Parser.parse " 340 | int main(void) { 341 | int a; 342 | bool b; 343 | float c; 344 | int d[]; 345 | { 346 | int e; 347 | } 348 | }" 349 | let expected = 350 | [Ast.FunctionDeclaration( 351 | Ast.Int, "main", [], 352 | ( 353 | [ 354 | Ast.ScalarVariableDeclaration(Ast.Int, "a") 355 | Ast.ScalarVariableDeclaration(Ast.Bool, "b") 356 | Ast.ScalarVariableDeclaration(Ast.Float, "c") 357 | Ast.ArrayVariableDeclaration(Ast.Int, "d") 358 | ], 359 | [ 360 | Ast.CompoundStatement( 361 | [ Ast.ScalarVariableDeclaration(Ast.Int, "e") ], 362 | [] 363 | ) 364 | ] 365 | ) 366 | )] 367 | Assert.That(result, Is.EqualTo(expected)) 368 | 369 | [] 370 | let ``can parse function call``() = 371 | let result = Parser.parse " 372 | int func(int i, int j) { 373 | return i + j; 374 | } 375 | 376 | int main(int i) { 377 | return func(i + 1, i - 1); 378 | }" 379 | let expected = 380 | [ 381 | Ast.FunctionDeclaration( 382 | Ast.Int, "func", 383 | [ 384 | Ast.ScalarVariableDeclaration(Ast.Int, "i") 385 | Ast.ScalarVariableDeclaration(Ast.Int, "j") 386 | ], 387 | ( 388 | [], 389 | [ 390 | Ast.ReturnStatement( 391 | Some( 392 | Ast.BinaryExpression( 393 | Ast.IdentifierExpression { Identifier = "i" }, 394 | Ast.Add, 395 | Ast.IdentifierExpression { Identifier = "j" } 396 | ) 397 | ) 398 | ) 399 | ] 400 | ) 401 | ) 402 | Ast.FunctionDeclaration( 403 | Ast.Int, "main", 404 | [ Ast.ScalarVariableDeclaration (Ast.Int, "i") ], 405 | ( 406 | [], 407 | [ 408 | Ast.ReturnStatement( 409 | Some( 410 | Ast.FunctionCallExpression( 411 | "func", 412 | [ 413 | Ast.BinaryExpression( 414 | Ast.IdentifierExpression { Identifier = "i" }, 415 | Ast.Add, 416 | Ast.LiteralExpression(Ast.IntLiteral 1) 417 | ) 418 | Ast.BinaryExpression( 419 | Ast.IdentifierExpression { Identifier = "i" }, 420 | Ast.Subtract, 421 | Ast.LiteralExpression(Ast.IntLiteral 1) 422 | ) 423 | ] 424 | ) 425 | ) 426 | ) 427 | ] 428 | ) 429 | ) 430 | ] 431 | Assert.That(result, Is.EqualTo(expected)) 432 | 433 | [] 434 | let ``can parse assignment expressions``() = 435 | let result = Parser.parse " 436 | int main(int i, int j[], float k) { 437 | i = i + 1; 438 | j[i % 2] = (i); 439 | k = 1.25; 440 | return i; 441 | }" 442 | let expected = 443 | [Ast.FunctionDeclaration( 444 | Ast.Int, "main", 445 | [ 446 | Ast.ScalarVariableDeclaration (Ast.Int, "i") 447 | Ast.ArrayVariableDeclaration (Ast.Int, "j") 448 | Ast.ScalarVariableDeclaration (Ast.Float, "k") 449 | ], 450 | ( 451 | [], 452 | [ 453 | Ast.ExpressionStatement( 454 | Ast.Expression( 455 | Ast.ScalarAssignmentExpression( 456 | { Identifier = "i" }, 457 | Ast.BinaryExpression( 458 | Ast.IdentifierExpression { Identifier = "i" }, 459 | Ast.Add, 460 | Ast.LiteralExpression(Ast.IntLiteral(1)) 461 | ) 462 | ) 463 | ) 464 | ) 465 | Ast.ExpressionStatement( 466 | Ast.Expression( 467 | Ast.ArrayAssignmentExpression( 468 | { Identifier = "j" }, 469 | Ast.BinaryExpression( 470 | Ast.IdentifierExpression { Identifier = "i" }, 471 | Ast.Modulus, 472 | Ast.LiteralExpression(Ast.IntLiteral 2) 473 | ), 474 | Ast.IdentifierExpression { Identifier = "i" } 475 | ) 476 | ) 477 | ) 478 | Ast.ExpressionStatement( 479 | Ast.Expression( 480 | Ast.ScalarAssignmentExpression( 481 | { Identifier = "k" }, 482 | Ast.LiteralExpression(Ast.FloatLiteral(1.25)) 483 | ) 484 | ) 485 | ) 486 | Ast.ReturnStatement(Some(Ast.IdentifierExpression { Identifier = "i" })) 487 | ] 488 | ) 489 | )] 490 | Assert.That(result, Is.EqualTo(expected)) 491 | 492 | [] 493 | let ``can parse array expressions``() = 494 | let result = Parser.parse " 495 | void main(int i, int j[]) { 496 | i = j[i * (1 + 2)]; 497 | i = j.size; 498 | j = new int[j.size * 2]; 499 | }" 500 | let expected = 501 | [Ast.FunctionDeclaration( 502 | Ast.Void, "main", 503 | [ 504 | Ast.ScalarVariableDeclaration (Ast.Int, "i") 505 | Ast.ArrayVariableDeclaration (Ast.Int, "j") 506 | ], 507 | ( 508 | [], 509 | [ 510 | Ast.ExpressionStatement( 511 | Ast.Expression( 512 | Ast.ScalarAssignmentExpression( 513 | { Identifier = "i" }, 514 | Ast.ArrayIdentifierExpression( 515 | { Identifier = "j" }, 516 | Ast.BinaryExpression( 517 | Ast.IdentifierExpression { Identifier = "i" }, 518 | Ast.Multiply, 519 | Ast.BinaryExpression( 520 | Ast.LiteralExpression(Ast.IntLiteral 1), 521 | Ast.Add, 522 | Ast.LiteralExpression(Ast.IntLiteral 2) 523 | ) 524 | ) 525 | ) 526 | ) 527 | ) 528 | ) 529 | Ast.ExpressionStatement( 530 | Ast.Expression( 531 | Ast.ScalarAssignmentExpression( 532 | { Identifier = "i" }, 533 | Ast.ArraySizeExpression { Identifier = "j" } 534 | ) 535 | ) 536 | ) 537 | Ast.ExpressionStatement( 538 | Ast.Expression( 539 | Ast.ScalarAssignmentExpression( 540 | { Identifier = "j" }, 541 | Ast.ArrayAllocationExpression( 542 | Ast.Int, 543 | Ast.BinaryExpression( 544 | Ast.ArraySizeExpression { Identifier = "j" }, 545 | Ast.Multiply, 546 | Ast.LiteralExpression(Ast.IntLiteral 2) 547 | ) 548 | ) 549 | ) 550 | ) 551 | ) 552 | ] 553 | ) 554 | )] 555 | Assert.That(result, Is.EqualTo(expected)) 556 | 557 | [] 558 | let ``can parse logical comparison expressions``() = 559 | let doTest operator binaryOperator = 560 | let code = (sprintf " 561 | bool main(void) { 562 | return true %s false; 563 | }" operator) 564 | let result = Parser.parse code 565 | let expected = 566 | [Ast.FunctionDeclaration( 567 | Ast.Bool, "main", [], 568 | ( 569 | [], 570 | [ 571 | Ast.ReturnStatement( 572 | Some( 573 | Ast.BinaryExpression( 574 | Ast.LiteralExpression(Ast.BoolLiteral(true)), 575 | binaryOperator, 576 | Ast.LiteralExpression(Ast.BoolLiteral(false)) 577 | ) 578 | ) 579 | ) 580 | ] 581 | ) 582 | )] 583 | Assert.That(result, Is.EqualTo(expected)) 584 | 585 | doTest "||" Ast.ConditionalOr 586 | doTest "==" Ast.Equal 587 | doTest "!=" Ast.NotEqual 588 | doTest "<=" Ast.LessEqual 589 | doTest "<" Ast.Less 590 | doTest ">=" Ast.GreaterEqual 591 | doTest ">" Ast.Greater 592 | doTest "&&" Ast.ConditionalAnd 593 | 594 | [] 595 | let ``can parse and ignore comments``() = 596 | let result = Parser.parse " 597 | void main(void) { 598 | return /* this is a mid-line comment */; 599 | // This is another type of comment 600 | }" 601 | let expected = 602 | [Ast.FunctionDeclaration( 603 | Ast.Void, "main", [], 604 | ( 605 | [], 606 | [ Ast.ReturnStatement(None) ] 607 | ) 608 | )] 609 | Assert.That(result, Is.EqualTo(expected)) 610 | 611 | [] 612 | let ``can parse operator precedence``() = 613 | let result = Parser.parse " 614 | int isqrt(int a, int guess) { 615 | int x; 616 | if (guess == (x = (guess + a/guess)/2)) 617 | return guess; 618 | return isqrt(a, x); 619 | }" 620 | let expected = 621 | [Ast.FunctionDeclaration( 622 | Ast.Int, "isqrt", 623 | [ 624 | Ast.ScalarVariableDeclaration (Ast.Int, "a") 625 | Ast.ScalarVariableDeclaration (Ast.Int, "guess") 626 | ], 627 | ( 628 | [ Ast.ScalarVariableDeclaration (Ast.Int, "x") ], 629 | [ 630 | Ast.IfStatement( 631 | Ast.BinaryExpression( 632 | Ast.IdentifierExpression { Identifier = "guess" }, 633 | Ast.Equal, 634 | Ast.ScalarAssignmentExpression( 635 | { Identifier = "x" }, 636 | Ast.BinaryExpression( 637 | Ast.BinaryExpression( 638 | Ast.IdentifierExpression { Identifier = "guess" }, 639 | Ast.Add, 640 | Ast.BinaryExpression( 641 | Ast.IdentifierExpression { Identifier = "a" }, 642 | Ast.Divide, 643 | Ast.IdentifierExpression { Identifier = "guess" } 644 | ) 645 | ), 646 | Ast.Divide, 647 | Ast.LiteralExpression(Ast.IntLiteral 2) 648 | ) 649 | ) 650 | ), 651 | Ast.ReturnStatement(Some(Ast.IdentifierExpression { Identifier = "guess" })), 652 | None 653 | ) 654 | Ast.ReturnStatement( 655 | Some( 656 | Ast.FunctionCallExpression( 657 | "isqrt", 658 | [ 659 | Ast.IdentifierExpression { Identifier = "a" } 660 | Ast.IdentifierExpression { Identifier = "x" } 661 | ] 662 | ) 663 | ) 664 | ) 665 | ] 666 | ) 667 | )] 668 | Assert.That(result, Is.EqualTo(expected)) -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error1.minic: -------------------------------------------------------------------------------- 1 | int x; 2 | bool x; 3 | 4 | void main(void) { 5 | return; 6 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error10.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | return true; 3 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error11.minic: -------------------------------------------------------------------------------- 1 | bool test(bool i) { 2 | return !i; 3 | } 4 | 5 | void main(void) { 6 | test(3); 7 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error12.minic: -------------------------------------------------------------------------------- 1 | bool test(bool i) { 2 | return !i; 3 | } 4 | 5 | void main(void) { 6 | test(3, 2); 7 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error13.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | foo = 1; 3 | return; 4 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error14.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | break; 3 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error15.minic: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | return; 3 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error16.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | int i[]; 3 | 4 | i = new int[3]; 5 | i[true] = 0; 6 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error17.minic: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | int i[]; 3 | 4 | i = new int[3]; 5 | return i[1.0]; 6 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error18.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | int i[]; 3 | 4 | i = new int[true]; 5 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error19.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | int i[]; 3 | 4 | i = true; 5 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error2.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | int y; 3 | int y; 4 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error20.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | int i[]; 3 | 4 | i = new int[3]; 5 | i[0] = true; 6 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error21.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | int i[]; 3 | i = 0; 4 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error22.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | int i; 3 | i[0] = 1; 4 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error23.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | int i[]; 3 | int j[]; 4 | i[0] = j; 5 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error24.minic: -------------------------------------------------------------------------------- 1 | void func(void) { 2 | 3 | } 4 | 5 | void func(void) { 6 | 7 | } 8 | 9 | void main(void) { 10 | 11 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error25.minic: -------------------------------------------------------------------------------- 1 | void func(void) { 2 | 3 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error3.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | ^; 3 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error4.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | int x; 3 | x = true; 4 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error5.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | does_not_exist x; 3 | x = 2; 4 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error6.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | bool x; 3 | int y; 4 | 5 | x = true; 6 | y = 3; 7 | 8 | if (x < y) 9 | return 42; 10 | else 11 | return -42; 12 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error7.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | bool x; 3 | int y; 4 | 5 | x = true; 6 | y = 3; 7 | 8 | if (x != y) 9 | return 42; 10 | else 11 | return -42; 12 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error8.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | if (true || 42) 3 | return; 4 | else 5 | return; 6 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/error9.minic: -------------------------------------------------------------------------------- 1 | void main(void) { 2 | return foo(42); 3 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/test1.minic: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | int i; 3 | int j; 4 | i = 1; 5 | j = 2 * 3; 6 | return i - j; 7 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/test2.minic: -------------------------------------------------------------------------------- 1 | int fib(int n) { 2 | if (n == 0) 3 | return 0; 4 | if (n == 1) 5 | return 1; 6 | return fib(n - 1) + fib(n - 2); 7 | } 8 | 9 | int main(void) { 10 | return fib(10); 11 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/test3.minic: -------------------------------------------------------------------------------- 1 | int a; 2 | 3 | void myFunc(float a, int b, bool c) { 4 | a = 2.0; 5 | { 6 | bool c; 7 | c = false; 8 | if (c) { 9 | return; 10 | } 11 | } 12 | while (b > 0) { 13 | b = b - 1; 14 | if (b == 5) { 15 | break; 16 | } 17 | } 18 | } 19 | 20 | int main(void) { 21 | a = (3); 22 | myFunc(1.0, 2, true); 23 | return a; 24 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/test4.minic: -------------------------------------------------------------------------------- 1 | int main(void) { 2 | bool b; 3 | int i1; 4 | int i2; 5 | int i3; 6 | int i4; 7 | int i5; 8 | 9 | b = 1 > 2; 10 | b = 1 >= 2; 11 | b = 1 == 2; 12 | b = 1 < 2; 13 | b = 1 <= 2; 14 | 15 | b = 1 > 2; 16 | b = 1 >= 2; 17 | b = 1 == 2; 18 | b = 1 < 2; 19 | b = 1 <= 2; 20 | 21 | b = true || false; 22 | b = true && false; 23 | 24 | i1 = 1 + 1; 25 | i2 = 1 - 1; 26 | i3 = 1 * 3; 27 | i4 = 4 / 2; 28 | i5 = 5 % 3; 29 | 30 | b = !b; 31 | 32 | return i1 + i2 + i3 + i4 + i5; 33 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/test5.minic: -------------------------------------------------------------------------------- 1 | int func1(void) { 2 | int a; 3 | a = 1; 4 | return a; 5 | } 6 | 7 | int func2(void) { 8 | int a; 9 | a = 2; 10 | return a; 11 | } 12 | 13 | int func3(void) { 14 | int a; 15 | a = 4; 16 | return a; 17 | } 18 | 19 | int func4(void) { 20 | int a; 21 | { 22 | int b; 23 | b = 8; 24 | a = b; 25 | } 26 | { 27 | int b; 28 | b = 16; 29 | a = a + b; 30 | } 31 | return a; 32 | } 33 | 34 | int main(void) { 35 | int a; 36 | int b; 37 | int c; 38 | a = b = c = 32; 39 | 40 | return func1() + func2() + func3() + func4() 41 | + a + b + c; 42 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/test6.minic: -------------------------------------------------------------------------------- 1 | int field[]; 2 | 3 | int func(int array[ ]) { 4 | return array[3 - field[0]]; 5 | } 6 | 7 | int main(void) { 8 | float i[]; 9 | int j[]; 10 | int k; 11 | 12 | field = new int[2]; 13 | field[0] = 1; 14 | field[1] = 2; 15 | 16 | i = new float[0]; 17 | 18 | j = new int[3]; 19 | j[0 + 0] = -1; 20 | k = j[1] = j[0] - 2; 21 | j[2] = 16; 22 | 23 | return func(j) + field.size + k; 24 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/Sources/test7.minic: -------------------------------------------------------------------------------- 1 | // This example is from "Mini-C to JVM" 2 | // http://jamesvanboxtel.com/projects/minic-compiler/minic.pdf 3 | 4 | int isqrt(int a, int guess) { // tail recursive integer square root 5 | int x; 6 | if (guess == (x = (guess + a/guess)/2)) // guess via Newton’s method 7 | return guess; 8 | return isqrt(a, x); // tail recurse 9 | } 10 | 11 | int num; 12 | float f; 13 | 14 | void main(void) { 15 | num = iread(); 16 | iprint(isqrt(num, num/2)); 17 | 18 | f = fread(); 19 | fprint(f * 2.0); 20 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/SymbolTableTests.fs: -------------------------------------------------------------------------------- 1 | module MiniC.Compiler.Tests.SymbolTableTests 2 | 3 | open NUnit.Framework 4 | open MiniC.Compiler 5 | open MiniC.Compiler.SemanticAnalysis 6 | 7 | [] 8 | let ``can find declaration in symbol table``() = 9 | // Arrange. 10 | let variableDeclarationA = Ast.StaticVariableDeclaration (Ast.ScalarVariableDeclaration(Ast.Float, "a")) 11 | let localDeclarationA1 = Ast.ScalarVariableDeclaration(Ast.Int, "a") 12 | let localDeclarationB1 = Ast.ScalarVariableDeclaration(Ast.Bool, "b") 13 | let localDeclarationA2 = Ast.ScalarVariableDeclaration(Ast.Int, "a") 14 | let localDeclarationC1 = Ast.ArrayVariableDeclaration(Ast.Int, "c") 15 | let identifier1 = { Ast.Identifier = "a" } 16 | let identifier2 = { Ast.Identifier = "a" } 17 | let identifierExpression1 = Ast.IdentifierExpression identifier1 18 | let identifierExpression2 = Ast.IdentifierExpression identifier2 19 | let program = 20 | [ 21 | variableDeclarationA; 22 | Ast.FunctionDeclaration( 23 | Ast.Int, "main", 24 | [ Ast.ScalarVariableDeclaration(Ast.Int, "a")], // Shadows previous variable 25 | ( 26 | [ 27 | localDeclarationA1; // Shadows previous parameter 28 | localDeclarationB1 29 | ], 30 | [ 31 | Ast.CompoundStatement( 32 | [ 33 | localDeclarationA2; // Shadows previous local 34 | localDeclarationC1 35 | ], 36 | [ Ast.ExpressionStatement (Ast.Expression identifierExpression1) ] 37 | ); 38 | Ast.ExpressionStatement (Ast.Expression identifierExpression2) 39 | ] 40 | ) 41 | ) 42 | ] 43 | let symbolTable = new SymbolTable(program) 44 | 45 | // Act. 46 | let result1 = symbolTable.[identifier1] 47 | let result2 = symbolTable.[identifier2] 48 | 49 | // Assert. 50 | Assert.That(result1, Is.SameAs(localDeclarationA2)) 51 | Assert.That(result2, Is.SameAs(localDeclarationA1)) -------------------------------------------------------------------------------- /src/MiniC.Compiler.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/MiniC.Compiler/Ast.fs: -------------------------------------------------------------------------------- 1 | module MiniC.Compiler.Ast 2 | 3 | type Program = Declaration list 4 | 5 | and Declaration = 6 | | StaticVariableDeclaration of VariableDeclaration 7 | | FunctionDeclaration of FunctionDeclaration 8 | 9 | and TypeSpec = 10 | | Void 11 | | Bool 12 | | Int 13 | | Float 14 | override x.ToString() = 15 | match x with 16 | | Void -> "void" 17 | | Bool -> "bool" 18 | | Int -> "int" 19 | | Float -> "float" 20 | 21 | and VariableDeclaration = 22 | | ScalarVariableDeclaration of TypeSpec * Identifier 23 | | ArrayVariableDeclaration of TypeSpec * Identifier 24 | 25 | and FunctionDeclaration = TypeSpec * Identifier * Parameters * CompoundStatement 26 | 27 | and Identifier = string 28 | 29 | and Parameters = VariableDeclaration list 30 | 31 | and IdentifierRef = { Identifier : string; } 32 | 33 | and Statement = 34 | | ExpressionStatement of ExpressionStatement 35 | | CompoundStatement of CompoundStatement 36 | | IfStatement of IfStatement 37 | | WhileStatement of WhileStatement 38 | | ReturnStatement of Expression option 39 | | BreakStatement 40 | 41 | and ExpressionStatement = 42 | | Expression of Expression 43 | | Nop 44 | 45 | and CompoundStatement = LocalDeclarations * Statement list 46 | 47 | and LocalDeclarations = VariableDeclaration list 48 | 49 | and IfStatement = Expression * Statement * Statement option 50 | 51 | and WhileStatement = Expression * Statement 52 | 53 | and Expression = 54 | | ScalarAssignmentExpression of IdentifierRef * Expression 55 | | ArrayAssignmentExpression of IdentifierRef * Expression * Expression 56 | | BinaryExpression of Expression * BinaryOperator * Expression 57 | | UnaryExpression of UnaryOperator * Expression 58 | | IdentifierExpression of IdentifierRef 59 | | ArrayIdentifierExpression of IdentifierRef * Expression 60 | | FunctionCallExpression of Identifier * Arguments 61 | | ArraySizeExpression of IdentifierRef 62 | | LiteralExpression of Literal 63 | | ArrayAllocationExpression of TypeSpec * Expression 64 | 65 | and BinaryOperator = 66 | | ConditionalOr 67 | | Equal 68 | | NotEqual 69 | | LessEqual 70 | | Less 71 | | GreaterEqual 72 | | Greater 73 | | ConditionalAnd 74 | | Add 75 | | Subtract 76 | | Multiply 77 | | Divide 78 | | Modulus 79 | override x.ToString() = 80 | match x with 81 | | ConditionalOr -> "||" 82 | | Equal -> "==" 83 | | NotEqual -> "!=" 84 | | LessEqual -> "<=" 85 | | Less -> "<" 86 | | GreaterEqual -> ">=" 87 | | Greater -> ">" 88 | | ConditionalAnd -> "&&" 89 | | Add -> "+" 90 | | Subtract -> "-" 91 | | Multiply -> "*" 92 | | Divide -> "/" 93 | | Modulus -> "%" 94 | 95 | and UnaryOperator = 96 | | LogicalNegate 97 | | Negate 98 | | Identity 99 | 100 | and Arguments = Expression list 101 | 102 | and Literal = 103 | | BoolLiteral of bool 104 | | IntLiteral of int 105 | | FloatLiteral of float 106 | override x.ToString() = 107 | match x with 108 | | BoolLiteral(b) -> b.ToString() 109 | | IntLiteral(i) -> i.ToString() 110 | | FloatLiteral(f) -> f.ToString() -------------------------------------------------------------------------------- /src/MiniC.Compiler/CodeGenerator.fs: -------------------------------------------------------------------------------- 1 | namespace MiniC.Compiler 2 | 3 | open System.Collections.Generic 4 | open System.Reflection 5 | open System.Reflection.Emit 6 | open IL 7 | 8 | type MethodMappingDictionary = Dictionary 9 | type FieldMappingDictionary = Dictionary 10 | 11 | type MethodGenerator(typeBuilder : TypeBuilder, ilMethod : ILMethod, 12 | methodMappings : MethodMappingDictionary, 13 | fieldMappings : FieldMappingDictionary) = 14 | let methodAttributes = MethodAttributes.Public ||| MethodAttributes.Static 15 | let methodBuilder = typeBuilder.DefineMethod(ilMethod.Name, methodAttributes) 16 | do methodMappings.Add(ilMethod.Name, methodBuilder) 17 | 18 | let ilGenerator = methodBuilder.GetILGenerator() 19 | 20 | let labelMappings = new Dictionary() 21 | 22 | let getLabel ilLabel = 23 | if labelMappings.ContainsKey ilLabel then 24 | labelMappings.[ilLabel] 25 | else 26 | let label = ilGenerator.DefineLabel() 27 | labelMappings.Add(ilLabel, label) 28 | label 29 | 30 | let emitOpCode (ilGenerator : ILGenerator) = function 31 | | Add -> ilGenerator.Emit(OpCodes.Add) 32 | | Br(l) -> ilGenerator.Emit(OpCodes.Br, getLabel l) 33 | | Brfalse(l) -> ilGenerator.Emit(OpCodes.Brfalse, getLabel l) 34 | | Brtrue(l) -> ilGenerator.Emit(OpCodes.Brtrue, getLabel l) 35 | | Call(n) -> ilGenerator.Emit(OpCodes.Call, methodMappings.[n]) 36 | | CallClr(m) -> ilGenerator.Emit(OpCodes.Call, m) 37 | | Ceq -> ilGenerator.Emit(OpCodes.Ceq) 38 | | Cge -> ilGenerator.Emit(OpCodes.Clt) 39 | ilGenerator.Emit(OpCodes.Ldc_I4_0) 40 | ilGenerator.Emit(OpCodes.Ceq) 41 | | Cgt -> ilGenerator.Emit(OpCodes.Cgt) 42 | | Cle -> ilGenerator.Emit(OpCodes.Cgt) 43 | ilGenerator.Emit(OpCodes.Ldc_I4_0) 44 | ilGenerator.Emit(OpCodes.Ceq) 45 | | Clt -> ilGenerator.Emit(OpCodes.Clt) 46 | | Dup -> ilGenerator.Emit(OpCodes.Dup) 47 | | Div -> ilGenerator.Emit(OpCodes.Div) 48 | | Label(l) -> ilGenerator.MarkLabel(getLabel l) 49 | | Ldarg(i) -> ilGenerator.Emit(OpCodes.Ldarg, i) 50 | | Ldc_I4(i) -> ilGenerator.Emit(OpCodes.Ldc_I4, i) 51 | | Ldc_R8(r) -> ilGenerator.Emit(OpCodes.Ldc_R8, r) 52 | | Ldelem(t) -> ilGenerator.Emit(OpCodes.Ldelem, t) 53 | | Ldlen -> ilGenerator.Emit(OpCodes.Ldlen) 54 | | Ldloc(i) -> ilGenerator.Emit(OpCodes.Ldloc, i) 55 | | Ldsfld(v) -> ilGenerator.Emit(OpCodes.Ldsfld, fieldMappings.[v]) 56 | | Mul -> ilGenerator.Emit(OpCodes.Mul) 57 | | Neg -> ilGenerator.Emit(OpCodes.Neg) 58 | | Newarr(t) -> ilGenerator.Emit(OpCodes.Newarr, t) 59 | | Pop -> ilGenerator.Emit(OpCodes.Pop) 60 | | Rem -> ilGenerator.Emit(OpCodes.Rem) 61 | | Ret -> ilGenerator.Emit(OpCodes.Ret) 62 | | Starg(i) -> ilGenerator.Emit(OpCodes.Starg, i) 63 | | Stelem(t) -> ilGenerator.Emit(OpCodes.Stelem, t) 64 | | Stloc(i) -> ilGenerator.Emit(OpCodes.Stloc, i) 65 | | Stsfld(v) -> ilGenerator.Emit(OpCodes.Stsfld, fieldMappings.[v]) 66 | | Sub -> ilGenerator.Emit(OpCodes.Sub) 67 | 68 | let emitLocal (ilGenerator : ILGenerator) variable = 69 | ilGenerator.DeclareLocal(variable.Type).SetLocalSymInfo(variable.Name) 70 | 71 | member x.Generate() = 72 | methodBuilder.SetReturnType ilMethod.ReturnType 73 | methodBuilder.SetParameters (List.toArray (ilMethod.Parameters |> List.map (fun p -> p.Type))) 74 | 75 | let defineParameter index name = 76 | methodBuilder.DefineParameter(index, ParameterAttributes.In, name) |> ignore 77 | ilMethod.Parameters |> List.iteri (fun i p -> defineParameter (i + 1) p.Name) 78 | 79 | ilMethod.Locals |> List.iter (emitLocal ilGenerator) 80 | ilMethod.Body |> List.iter (emitOpCode ilGenerator) 81 | 82 | let rec last = 83 | function 84 | | head :: [] -> head 85 | | head :: tail -> last tail 86 | | _ -> failwith "Empty list." 87 | if (last ilMethod.Body) <> Ret then // TODO: Maybe don't need to do this? 88 | ilGenerator.Emit(OpCodes.Ret) 89 | 90 | type CodeGenerator(moduleBuilder : ModuleBuilder, ilClass : ILClass, moduleName : string) = 91 | let fieldMappings = new FieldMappingDictionary() 92 | 93 | let generateField (typeBuilder : TypeBuilder) (ilField : ILVariable) = 94 | let fieldAttributes = FieldAttributes.Public ||| FieldAttributes.Static 95 | let fieldBuilder = typeBuilder.DefineField(ilField.Name, ilField.Type, fieldAttributes) 96 | fieldMappings.Add(ilField, fieldBuilder) 97 | 98 | member x.GenerateType() = 99 | let typeAttributes = TypeAttributes.Abstract ||| TypeAttributes.Sealed ||| TypeAttributes.Public 100 | let typeBuilder = moduleBuilder.DefineType(moduleName + ".Program", typeAttributes) 101 | 102 | ilClass.Fields |> List.iter (generateField typeBuilder) 103 | 104 | let methodMappings = new MethodMappingDictionary() 105 | let generateMethod ilMethod = 106 | let methodGenerator = new MethodGenerator(typeBuilder, ilMethod, methodMappings, fieldMappings) 107 | methodGenerator.Generate() 108 | ilClass.Methods |> List.iter generateMethod 109 | 110 | (typeBuilder.CreateType(), typeBuilder.GetMethod("main")) // TODO -------------------------------------------------------------------------------- /src/MiniC.Compiler/Compiler.fs: -------------------------------------------------------------------------------- 1 | module MiniC.Compiler.Compiler 2 | 3 | open System 4 | open System.IO 5 | open System.Reflection 6 | open System.Reflection.Emit 7 | 8 | let compile (assemblyBuilder : AssemblyBuilder) code = 9 | let assemblyName = assemblyBuilder.GetName() 10 | let moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".exe", true) 11 | 12 | let program = Parser.parse code 13 | let semanticAnalysisResult = SemanticAnalysis.analyze program 14 | 15 | let ilBuilder = new ILBuilder(semanticAnalysisResult) 16 | let ilClass = ilBuilder.BuildClass program 17 | 18 | let codeGenerator = new CodeGenerator(moduleBuilder, ilClass, assemblyName.Name) 19 | let (compiledType, entryPoint) = codeGenerator.GenerateType() 20 | assemblyBuilder.SetEntryPoint entryPoint 21 | (compiledType, entryPoint) 22 | 23 | let compileToMemory assemblyName code = 24 | let assemblyBuilder = 25 | AppDomain.CurrentDomain.DefineDynamicAssembly( 26 | assemblyName, AssemblyBuilderAccess.RunAndSave) 27 | compile assemblyBuilder code 28 | 29 | let compileToFile fileName code = 30 | let assemblyName = new AssemblyName (Path.GetFileNameWithoutExtension fileName) 31 | let assemblyBuilder = 32 | AppDomain.CurrentDomain.DefineDynamicAssembly( 33 | assemblyName, AssemblyBuilderAccess.RunAndSave, 34 | Path.GetDirectoryName(fileName)) 35 | let (_, _) = compile assemblyBuilder code 36 | assemblyBuilder.Save (Path.GetFileName fileName) -------------------------------------------------------------------------------- /src/MiniC.Compiler/CompilerException.fs: -------------------------------------------------------------------------------- 1 | namespace MiniC.Compiler 2 | 3 | type CompilerException(message : string) = 4 | inherit System.Exception(message) 5 | 6 | module CompilerErrors = 7 | let create m = CompilerException m 8 | 9 | let lexerError a = create (sprintf "MC001 Lexer error: %s" a) 10 | let parserError a = create (sprintf "MC002 Parser error: %s" a) 11 | let variableAlreadyDefined a = create (sprintf "MC003 A variable named '%s' is already defined in this scope" a) 12 | let cannotConvertType a b = create (sprintf "MC004 Cannot convert type '%s' to '%s'" a b) 13 | let operatorCannotBeApplied a b c = create (sprintf "MC005 Operator '%s' cannot be applied to operands of type '%s' and '%s'" a b c) 14 | let nameDoesNotExist a = create (sprintf "MC006 The name '%s' does not exist in the current context" a) 15 | let invalidArguments a b c d = create (sprintf "MC007 Call to function '%s' has some invalid arguments. Argument %i: Cannot convert from '%s' to '%s'" a b c d) 16 | let wrongNumberOfArguments a b c = create (sprintf "MC008 Function '%s' takes %i arguments, but here was given %i" a b c) 17 | let noEnclosingLoop() = create "MC009 No enclosing loop out of which to break" 18 | let cannotApplyIndexing a = create (sprintf "MC010 Cannot apply indexing with [] to an expression of type '%s'" a) 19 | let functionAlreadyDefined a = create (sprintf "MC011 A function named '%s' is already defined" a) 20 | let missingEntryPoint() = create "MC012 Program does not contain a 'main' method suitable for an entry point" -------------------------------------------------------------------------------- /src/MiniC.Compiler/IL.fs: -------------------------------------------------------------------------------- 1 | module MiniC.Compiler.IL 2 | 3 | open System 4 | 5 | type ILClass = 6 | { 7 | Fields : ILVariable list; 8 | Methods : ILMethod list; 9 | } 10 | 11 | and ILMethod = 12 | { 13 | Name : string; 14 | ReturnType : Type; 15 | Parameters : ILVariable list; 16 | Locals : ILVariable list; 17 | Body : ILOpCode list; 18 | } 19 | 20 | and ILVariable = 21 | { 22 | Type : Type; 23 | Name : string; 24 | } 25 | 26 | and ILLabel = int 27 | 28 | and ILOpCode = 29 | | Add 30 | | Br of ILLabel 31 | | Brfalse of ILLabel 32 | | Brtrue of ILLabel 33 | | Call of string 34 | | CallClr of System.Reflection.MethodInfo 35 | | Ceq 36 | | Cge 37 | | Cgt 38 | | Cle 39 | | Clt 40 | | Dup 41 | | Div 42 | | Label of ILLabel 43 | | Ldarg of int16 44 | | Ldc_I4 of int 45 | | Ldc_R8 of float 46 | | Ldelem of Type 47 | | Ldlen 48 | | Ldloc of int16 49 | | Ldsfld of ILVariable 50 | | Mul 51 | | Neg 52 | | Newarr of Type 53 | | Pop 54 | | Rem 55 | | Ret 56 | | Starg of int16 57 | | Stelem of Type 58 | | Stloc of int16 59 | | Stsfld of ILVariable 60 | | Sub 61 | 62 | override x.ToString() = 63 | match x with 64 | | Add -> "add" 65 | | Br(l) -> sprintf "br %i" l 66 | | Brfalse(l) -> sprintf "brfalse %i" l 67 | | Brtrue(l) -> sprintf "brtrue %i" l 68 | | Call(s) -> sprintf "call %s" s 69 | | CallClr(mi) -> sprintf "call %s.%s" mi.DeclaringType.FullName mi.Name 70 | | Ceq -> "ceq" 71 | | Cge -> "cge" 72 | | Cgt -> "cgt" 73 | | Cle -> "cle" 74 | | Clt -> "clt" 75 | | Dup -> "dup" 76 | | Div -> "div" 77 | | Label(l) -> sprintf "label %i" l 78 | | Ldarg(s) -> sprintf "ldarg %i" s 79 | | Ldc_I4(i) -> sprintf "ldc_i4 %i" i 80 | | Ldc_R8(f) -> sprintf "ldc_r8 %f" f 81 | | Ldelem(t) -> sprintf "ldelem %s" t.Name 82 | | Ldlen -> "ldlen" 83 | | Ldloc(s) -> sprintf "ldloc %i" s 84 | | Ldsfld(v) -> sprintf "ldsfld %s" v.Name 85 | | Mul -> "mul" 86 | | Neg -> "neg" 87 | | Newarr(t) -> sprintf "newarr %s" t.Name 88 | | Pop -> "pop" 89 | | Rem -> "rem" 90 | | Ret -> "ret" 91 | | Starg(s) -> sprintf "starg %i" s 92 | | Stelem(t) -> sprintf "stelem %s" t.Name 93 | | Stloc(s) -> sprintf "stloc %i" s 94 | | Stsfld(v) -> sprintf "stsfld %s" v.Name 95 | | Sub -> "sub" -------------------------------------------------------------------------------- /src/MiniC.Compiler/ILBuilder.fs: -------------------------------------------------------------------------------- 1 | namespace MiniC.Compiler 2 | 3 | open System.Collections.Generic 4 | open SemanticAnalysis 5 | open IL 6 | 7 | type private ILVariableScope = 8 | | FieldScope of ILVariable 9 | | ArgumentScope of int16 10 | | LocalScope of int16 11 | 12 | type private VariableMappingDictionary = Dictionary 13 | 14 | module private ILBuilderUtilities = 15 | let typeOf = 16 | function 17 | | Ast.Void -> typeof 18 | | Ast.Bool -> typeof 19 | | Ast.Int -> typeof 20 | | Ast.Float -> typeof 21 | 22 | let createILVariable = 23 | function 24 | | Ast.ScalarVariableDeclaration(t, i) as d -> 25 | { 26 | ILVariable.Type = typeOf t; 27 | Name = i; 28 | } 29 | | Ast.ArrayVariableDeclaration(t, i) as d -> 30 | { 31 | ILVariable.Type = (typeOf t).MakeArrayType(); 32 | Name = i; 33 | } 34 | 35 | open ILBuilderUtilities 36 | 37 | type ILMethodBuilder(semanticAnalysisResult : SemanticAnalysisResult, 38 | variableMappings : VariableMappingDictionary) = 39 | let mutable argumentIndex = 0s 40 | let mutable localIndex = 0s 41 | let arrayAssignmentLocals = Dictionary() 42 | let mutable labelIndex = 0 43 | let currentWhileStatementEndLabel = Stack() 44 | 45 | let lookupILVariableScope identifierRef = 46 | let declaration = semanticAnalysisResult.SymbolTable.[identifierRef] 47 | variableMappings.[declaration] 48 | 49 | let makeLabel() = 50 | let result = labelIndex 51 | labelIndex <- labelIndex + 1 52 | result 53 | 54 | let rec processBinaryExpression = 55 | function 56 | | (l, Ast.ConditionalOr, r) -> 57 | let leftIsFalseLabel = makeLabel() 58 | let endLabel = makeLabel() 59 | List.concat [ processExpression l 60 | [ Brfalse leftIsFalseLabel ] 61 | [ Ldc_I4 1 ] 62 | [ Br endLabel ] 63 | [ Label leftIsFalseLabel ] 64 | processExpression r 65 | [ Label endLabel ] ] 66 | | (l, Ast.ConditionalAnd, r) -> 67 | let leftIsTrueLabel = makeLabel() 68 | let endLabel = makeLabel() 69 | List.concat [ processExpression l 70 | [ Brtrue leftIsTrueLabel ] 71 | [ Ldc_I4 0 ] 72 | [ Br endLabel ] 73 | [ Label leftIsTrueLabel ] 74 | processExpression r 75 | [ Label endLabel ] ] 76 | | (l, op, r) -> List.concat [ (processExpression l); 77 | (processExpression r); 78 | [ processBinaryOperator op ] ] 79 | 80 | and processBinaryOperator = 81 | function 82 | | Ast.Add -> Add 83 | | Ast.Divide -> Div 84 | | Ast.Multiply -> Mul 85 | | Ast.Modulus -> Rem 86 | | Ast.Subtract -> Sub 87 | | Ast.Equal -> Ceq 88 | | Ast.Greater -> Cgt 89 | | Ast.GreaterEqual -> Cge 90 | | Ast.Less -> Clt 91 | | Ast.LessEqual -> Cle 92 | | _ -> failwith "Shouldn't be here" 93 | 94 | and processIdentifierLoad identifierRef = 95 | match lookupILVariableScope identifierRef with 96 | | FieldScope(v) -> [ Ldsfld v ] 97 | | ArgumentScope(i) -> [ Ldarg i ] 98 | | LocalScope(i) -> [ Ldloc i ] 99 | 100 | and processIdentifierStore identifierRef = 101 | match lookupILVariableScope identifierRef with 102 | | FieldScope(v) -> [ Stsfld v ] 103 | | ArgumentScope(i) -> [ Starg i ] 104 | | LocalScope(i) -> [ Stloc i ] 105 | 106 | and processExpression expression = 107 | match expression with 108 | | Ast.ScalarAssignmentExpression(i, e) -> 109 | List.concat [ processExpression e 110 | [ Dup ] 111 | processIdentifierStore i ] 112 | | Ast.ArrayAssignmentExpression(i, e1, e2) as ae -> 113 | List.concat [ processIdentifierLoad i 114 | processExpression e1 115 | processExpression e2 116 | [ Dup ] 117 | [ Stloc arrayAssignmentLocals.[ae] ] 118 | [ Stelem (typeOf (semanticAnalysisResult.SymbolTable.GetIdentifierTypeSpec i).Type) ] 119 | [ Ldloc arrayAssignmentLocals.[ae] ] ] 120 | | Ast.BinaryExpression(a, b, c) -> processBinaryExpression (a, b, c) 121 | | Ast.UnaryExpression(op, e) -> 122 | List.concat [ processExpression e 123 | processUnaryOperator op] 124 | | Ast.IdentifierExpression(i) -> processIdentifierLoad i 125 | | Ast.ArrayIdentifierExpression(i, e) -> 126 | List.concat [ processIdentifierLoad i 127 | processExpression e 128 | [ Ldelem (typeOf (semanticAnalysisResult.SymbolTable.GetIdentifierTypeSpec i).Type) ] ] 129 | | Ast.FunctionCallExpression(i, a) -> 130 | List.concat [ a |> List.collect processExpression 131 | [ Call i ] ] 132 | | Ast.ArraySizeExpression(i) -> 133 | List.concat [ processIdentifierLoad i 134 | [ Ldlen ] ] 135 | | Ast.LiteralExpression(l) -> 136 | match l with 137 | | Ast.IntLiteral(x) -> [ Ldc_I4(x) ] 138 | | Ast.FloatLiteral(x) -> [ Ldc_R8(x) ] 139 | | Ast.BoolLiteral(x) -> [ (if x then Ldc_I4(1) else Ldc_I4(0)) ] 140 | | Ast.ArrayAllocationExpression(t, e) -> 141 | List.concat [ processExpression e 142 | [ Newarr (typeOf t) ] ] 143 | 144 | and processUnaryOperator = 145 | function 146 | | Ast.LogicalNegate -> [ Ldc_I4 0; Ceq ] 147 | | Ast.Negate -> [ Neg ] 148 | | Ast.Identity -> [ ] 149 | 150 | and processStatement = 151 | function 152 | | Ast.ExpressionStatement(x) -> 153 | match x with 154 | | Ast.Expression(x) -> 155 | let isNotVoid = semanticAnalysisResult.ExpressionTypes.[x].Type <> Ast.Void 156 | List.concat [ processExpression x 157 | (if isNotVoid then [ Pop ] else []) ] 158 | 159 | | Ast.Nop -> [] 160 | | Ast.CompoundStatement(_, s) -> s |> List.collect processStatement 161 | | Ast.IfStatement(e, s1, Some(s2)) -> 162 | let thenLabel = makeLabel() 163 | let endLabel = makeLabel() 164 | List.concat [ processExpression e 165 | [ Brtrue thenLabel ] 166 | processStatement s2 167 | [ Br endLabel ] 168 | [ Label thenLabel ] 169 | processStatement s1 170 | [ Label endLabel ] ] 171 | | Ast.IfStatement(e, s1, None) -> 172 | let thenLabel = makeLabel() 173 | let endLabel = makeLabel() 174 | List.concat [ processExpression e 175 | [ Brtrue thenLabel ] 176 | [ Br endLabel ] 177 | [ Label thenLabel ] 178 | processStatement s1 179 | [ Label endLabel ] ] 180 | | Ast.WhileStatement(e, s) -> 181 | let startLabel = makeLabel() 182 | let conditionLabel = makeLabel() 183 | let endLabel = makeLabel() 184 | currentWhileStatementEndLabel.Push endLabel 185 | let result = List.concat [ [ Br conditionLabel ] 186 | [ Label startLabel ] 187 | processStatement s 188 | [ Label conditionLabel ] 189 | processExpression e 190 | [ Brtrue startLabel ] 191 | [ Label endLabel ] ] 192 | currentWhileStatementEndLabel.Pop() |> ignore 193 | result 194 | | Ast.ReturnStatement(x) -> 195 | match x with 196 | | Some(x) -> (processExpression x) @ [ Ret ] 197 | | None -> [ Ret ] 198 | | Ast.BreakStatement -> 199 | [ Br (currentWhileStatementEndLabel.Peek()) ] 200 | 201 | let processVariableDeclaration (mutableIndex : byref<_>) f d = 202 | let v = createILVariable d 203 | variableMappings.Add(d, f mutableIndex) 204 | mutableIndex <- mutableIndex + 1s 205 | v 206 | 207 | let processLocalDeclaration declaration = 208 | processVariableDeclaration &localIndex (fun i -> LocalScope i) declaration 209 | let processParameter declaration = 210 | processVariableDeclaration &argumentIndex (fun i -> ArgumentScope i) declaration 211 | 212 | let rec collectLocalDeclarations statement = 213 | let rec fromStatement = 214 | function 215 | | Ast.ExpressionStatement(es) -> 216 | match es with 217 | | Ast.Expression(e) -> fromExpression e 218 | | Ast.Nop -> [] 219 | | Ast.CompoundStatement(localDeclarations, statements) -> 220 | List.concat [ localDeclarations |> List.map processLocalDeclaration; 221 | statements |> List.collect collectLocalDeclarations ] 222 | | Ast.IfStatement(e, s1, Some(s2)) -> 223 | List.concat [ fromExpression e 224 | collectLocalDeclarations s1 225 | collectLocalDeclarations s2 ] 226 | | Ast.IfStatement(e, s1, None) -> 227 | List.concat [ fromExpression e 228 | collectLocalDeclarations s1 ] 229 | | Ast.WhileStatement(e, s) -> 230 | List.concat [ fromExpression e 231 | collectLocalDeclarations s ] 232 | | Ast.ReturnStatement(Some(e)) -> 233 | List.concat [ fromExpression e ] 234 | | _ -> [] 235 | 236 | and fromExpression = 237 | function 238 | | Ast.ScalarAssignmentExpression(i, e) -> fromExpression e 239 | | Ast.ArrayAssignmentExpression(i, e1, e2) as ae -> 240 | let v = { 241 | ILVariable.Type = typeOf ((semanticAnalysisResult.SymbolTable.GetIdentifierTypeSpec i).Type); 242 | Name = "ArrayAssignmentTemp" + string localIndex; 243 | } 244 | arrayAssignmentLocals.Add(ae, localIndex); 245 | localIndex <- localIndex + 1s 246 | List.concat [ [ v ]; fromExpression e2 ] 247 | | Ast.BinaryExpression(l, op, r) -> List.concat [ fromExpression l; fromExpression r; ] 248 | | Ast.UnaryExpression(op, e) -> fromExpression e 249 | | Ast.ArrayIdentifierExpression(i, e) -> fromExpression e 250 | | Ast.FunctionCallExpression(i, a) -> a |> List.collect fromExpression 251 | | Ast.ArrayAllocationExpression(t, e) -> fromExpression e 252 | | _ -> [] 253 | 254 | fromStatement statement 255 | 256 | member x.BuildMethod(returnType, name, parameters, (localDeclarations, statements)) = 257 | { 258 | Name = name; 259 | ReturnType = typeOf returnType; 260 | Parameters = parameters |> List.map processParameter; 261 | Locals = List.concat [ localDeclarations |> List.map processLocalDeclaration; 262 | statements |> List.collect collectLocalDeclarations ] 263 | Body = statements |> List.collect processStatement; 264 | } 265 | 266 | type ILBuilder(semanticAnalysisResult) = 267 | let variableMappings = new VariableMappingDictionary(HashIdentity.Reference) 268 | 269 | let processStaticVariableDeclaration d = 270 | let v = createILVariable d 271 | variableMappings.Add(d, FieldScope(v)) 272 | v 273 | 274 | member x.BuildClass (program : Ast.Program) = 275 | let variableDeclarations = 276 | program 277 | |> List.choose (fun x -> 278 | match x with 279 | | Ast.StaticVariableDeclaration(x) -> Some(x) 280 | | _ -> None) 281 | 282 | let functionDeclarations = 283 | program 284 | |> List.choose (fun x -> 285 | match x with 286 | | Ast.FunctionDeclaration(_, _, _, _ as a) -> Some a 287 | | _ -> None) 288 | 289 | let processFunctionDeclaration functionDeclaration = 290 | let ilMethodBuilder = new ILMethodBuilder(semanticAnalysisResult, variableMappings) 291 | ilMethodBuilder.BuildMethod functionDeclaration 292 | 293 | let builtInMethods = [ 294 | { 295 | Name = "iread"; 296 | ReturnType = typeof; 297 | Parameters = []; 298 | Locals = []; 299 | Body = [ CallClr(typeof.GetMethod("ReadLine")) 300 | CallClr(typeof.GetMethod("ToInt32", [| typeof |])) 301 | Ret ]; 302 | }; 303 | { 304 | Name = "fread"; 305 | ReturnType = typeof; 306 | Parameters = []; 307 | Locals = []; 308 | Body = [ CallClr(typeof.GetMethod("ReadLine")) 309 | CallClr(typeof.GetMethod("ToDouble", [| typeof |])) 310 | Ret ]; 311 | }; 312 | { 313 | Name = "iprint"; 314 | ReturnType = typeof; 315 | Parameters = [ { Type = typeof; Name = "value"; }]; 316 | Locals = []; 317 | Body = [ Ldarg(0s) 318 | CallClr(typeof.GetMethod("WriteLine", [| typeof |])) 319 | Ret ]; 320 | }; 321 | { 322 | Name = "fprint"; 323 | ReturnType = typeof; 324 | Parameters = [ { Type = typeof; Name = "value"; }]; 325 | Locals = []; 326 | Body = [ Ldarg(0s) 327 | CallClr(typeof.GetMethod("WriteLine", [| typeof |])) 328 | Ret ]; 329 | } ] 330 | 331 | { 332 | Fields = variableDeclarations |> List.map processStaticVariableDeclaration; 333 | Methods = List.concat [ builtInMethods 334 | functionDeclarations |> List.map processFunctionDeclaration ]; 335 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler/MiniC.Compiler.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 2860d24f-85d9-4bc7-8463-ee1ec134290d 9 | Library 10 | MiniC.Compiler 11 | MiniC.Compiler 12 | v4.0 13 | MiniC.Compiler 14 | 15 | 4.3.0.0 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 3 25 | bin\Debug\MiniC.Compiler.XML 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | bin\Release\MiniC.Compiler.XML 35 | 36 | 37 | 11 38 | 39 | 40 | 41 | 42 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 43 | 44 | 45 | 46 | 47 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | True 67 | 68 | 69 | 70 | ..\packages\Piglet.1.4.0\lib\net40\Piglet.dll 71 | True 72 | 73 | 74 | 75 | 76 | 77 | 84 | -------------------------------------------------------------------------------- /src/MiniC.Compiler/Parser.fs: -------------------------------------------------------------------------------- 1 | module MiniC.Compiler.Parser 2 | 3 | open Piglet.Parser 4 | open CompilerErrors 5 | open Ast 6 | open ParsingUtilities 7 | 8 | let configurator = ParserFactory.Configure() 9 | 10 | // Non-terminals 11 | 12 | let nonTerminal<'T> () = new NonTerminalWrapper<'T>(configurator.CreateNonTerminal()) 13 | 14 | let program = nonTerminal() 15 | let declarationList = nonTerminal() 16 | let declaration = nonTerminal() 17 | let staticVariableDeclaration = nonTerminal() 18 | let functionDeclaration = nonTerminal() 19 | let typeSpec = nonTerminal() 20 | let parameters = nonTerminal() 21 | let parameterList = nonTerminal() 22 | let parameter = nonTerminal() 23 | let optionalStatementList = nonTerminal() 24 | let statementList = nonTerminal() 25 | let statement = nonTerminal() 26 | let expressionStatement = nonTerminal() 27 | let whileStatement = nonTerminal() 28 | let compoundStatement = nonTerminal() 29 | let optionalLocalDeclarations = nonTerminal() 30 | let localDeclarations = nonTerminal() 31 | let localDeclaration = nonTerminal() 32 | let ifStatement = nonTerminal() 33 | let optionalElseStatement = nonTerminal() 34 | let returnStatement = nonTerminal() 35 | let breakStatement = nonTerminal() 36 | let expression = nonTerminal() 37 | let unaryOperator = nonTerminal() 38 | let optionalArguments = nonTerminal() 39 | let arguments = nonTerminal() 40 | 41 | // Terminals 42 | 43 | let terminalParse<'T> regex (onParse : (string -> 'T)) = 44 | new TerminalWrapper<'T>(configurator.CreateTerminal(regex, (fun s -> box (onParse s)))) 45 | 46 | let terminal regex = 47 | new TerminalWrapper(configurator.CreateTerminal(regex)) 48 | 49 | let ifKeyword = terminal "if" 50 | let elseKeyword = terminal "else" 51 | let whileKeyword = terminal "while" 52 | let returnKeyword = terminal "return" 53 | let breakKeyword = terminal "break" 54 | let newKeyword = terminal "new" 55 | let sizeKeyword = terminal "size" 56 | let voidKeyword = terminal "void" 57 | let plus = terminal @"\+" 58 | let minus = terminal "-" 59 | let exclamation = terminal "!" 60 | let asterisk = terminal @"\*" 61 | let intLiteral = terminalParse @"\d+" (fun s -> Ast.IntLiteral(int32 s)) 62 | let floatLiteral = terminalParse @"\d+\.\d+" (fun s -> Ast.FloatLiteral(float s)) 63 | let trueLiteral = terminalParse "true" (fun s -> Ast.BoolLiteral(true)) 64 | let falseLiteral = terminalParse "false" (fun s -> Ast.BoolLiteral(false)) 65 | let boolKeyword = terminalParse "bool" (fun s -> Ast.Bool) 66 | let intKeyword = terminalParse "int" (fun s -> Ast.Int) 67 | let floatKeyword = terminalParse "float" (fun s -> Ast.Float) 68 | let identifier = terminalParse "[a-zA-Z_][a-zA-Z_0-9]*" (fun s -> s) 69 | let openParen = terminal @"\(" 70 | let closeParen = terminal @"\)" 71 | let openCurly = terminal @"\{" 72 | let closeCurly = terminal @"\}" 73 | let openSquare = terminal @"\[" 74 | let closeSquare = terminal @"\]" 75 | let semicolon = terminal ";" 76 | let comma = terminal "," 77 | let percent = terminal "%" 78 | let forwardSlash = terminal "/" 79 | let singleEquals = terminal "=" 80 | let doublePipes = terminal @"\|\|" 81 | let doubleEquals = terminal "==" 82 | let bangEquals = terminal "!=" 83 | let openAngleEquals = terminal "<=" 84 | let openAngle = terminal "<" 85 | let closeAngleEquals = terminal ">=" 86 | let closeAngle = terminal ">" 87 | let doubleAmpersands = terminal "&&" 88 | let period = terminal @"\." 89 | 90 | // Precedence 91 | 92 | let optionalElsePrecedenceGroup = configurator.LeftAssociative() 93 | 94 | configurator.LeftAssociative(downcast elseKeyword.Symbol) |> ignore 95 | 96 | configurator.LeftAssociative(downcast singleEquals.Symbol) 97 | |> ignore 98 | configurator.LeftAssociative(downcast doublePipes.Symbol) 99 | |> ignore 100 | configurator.LeftAssociative(downcast doubleEquals.Symbol, 101 | downcast bangEquals.Symbol) 102 | |> ignore 103 | configurator.LeftAssociative(downcast openAngleEquals.Symbol, 104 | downcast openAngle.Symbol, 105 | downcast closeAngleEquals.Symbol, 106 | downcast closeAngle.Symbol) 107 | |> ignore 108 | configurator.LeftAssociative(downcast doubleAmpersands.Symbol) 109 | |> ignore 110 | configurator.LeftAssociative(downcast exclamation.Symbol, 111 | downcast plus.Symbol, 112 | downcast minus.Symbol) 113 | |> ignore 114 | configurator.LeftAssociative(downcast asterisk.Symbol, 115 | downcast forwardSlash.Symbol, 116 | downcast percent.Symbol) 117 | |> ignore 118 | 119 | let binaryExpressionPrecedenceGroup = configurator.LeftAssociative() 120 | let unaryExpressionPrecedenceGroup = configurator.RightAssociative() 121 | 122 | // Productions 123 | 124 | program.AddProduction(declarationList).SetReduceToFirst() 125 | 126 | declarationList.AddProduction(declarationList, declaration).SetReduceFunction (fun a b -> a @ [b]) 127 | declarationList.AddProduction(declaration).SetReduceFunction (fun a -> [a]) 128 | 129 | declaration.AddProduction(staticVariableDeclaration).SetReduceFunction (fun a -> Ast.StaticVariableDeclaration a) 130 | declaration.AddProduction(functionDeclaration) .SetReduceFunction (fun a -> Ast.FunctionDeclaration a) 131 | 132 | typeSpec.AddProduction(voidKeyword).SetReduceFunction (fun _ -> Ast.Void) 133 | typeSpec.AddProduction(boolKeyword).SetReduceToFirst() 134 | typeSpec.AddProduction(intKeyword).SetReduceToFirst() 135 | typeSpec.AddProduction(floatKeyword).SetReduceToFirst() 136 | 137 | staticVariableDeclaration.AddProduction(typeSpec, identifier, semicolon) 138 | .SetReduceFunction (fun a b _ -> Ast.ScalarVariableDeclaration(a, b)) 139 | staticVariableDeclaration.AddProduction(typeSpec, identifier, openSquare, closeSquare, semicolon) 140 | .SetReduceFunction (fun a b _ _ _ -> Ast.ArrayVariableDeclaration(a, b)) 141 | 142 | functionDeclaration.AddProduction(typeSpec, identifier, openParen, parameters, closeParen, compoundStatement) 143 | .SetReduceFunction (fun a b _ d _ f -> (a, b, d, f)) 144 | 145 | parameters.AddProduction(parameterList).SetReduceToFirst() 146 | parameters.AddProduction(voidKeyword).SetReduceFunction (fun _ -> []) 147 | 148 | parameterList.AddProduction(parameterList, comma, parameter).SetReduceFunction (fun a _ c -> a @ [c]) 149 | parameterList.AddProduction(parameter) .SetReduceFunction (fun a -> [a]) 150 | 151 | parameter.AddProduction(typeSpec, identifier) .SetReduceFunction (fun a b -> Ast.ScalarVariableDeclaration(a, b)) 152 | parameter.AddProduction(typeSpec, identifier, openSquare, closeSquare).SetReduceFunction (fun a b _ _ -> Ast.ArrayVariableDeclaration(a, b)) 153 | 154 | optionalStatementList.AddProduction(statementList).SetReduceToFirst() 155 | optionalStatementList.AddProduction() .SetReduceFunction (fun () -> []) 156 | 157 | statementList.AddProduction(statementList, statement).SetReduceFunction (fun a b -> a @ [b]) 158 | statementList.AddProduction(statement) .SetReduceFunction (fun a -> [a]) 159 | 160 | statement.AddProduction(expressionStatement).SetReduceFunction (fun a -> Ast.ExpressionStatement a) 161 | statement.AddProduction(compoundStatement) .SetReduceFunction (fun a -> Ast.CompoundStatement a) 162 | statement.AddProduction(ifStatement) .SetReduceFunction (fun a -> Ast.IfStatement a) 163 | statement.AddProduction(whileStatement) .SetReduceFunction (fun a -> Ast.WhileStatement a) 164 | statement.AddProduction(returnStatement) .SetReduceFunction (fun a -> Ast.ReturnStatement a) 165 | statement.AddProduction(breakStatement) .SetReduceFunction (fun a -> Ast.BreakStatement) 166 | 167 | expressionStatement.AddProduction(expression, semicolon).SetReduceFunction (fun a _ -> Ast.Expression a) 168 | expressionStatement.AddProduction(semicolon) .SetReduceFunction (fun _ -> Ast.Nop) 169 | 170 | whileStatement.AddProduction(whileKeyword, openParen, expression, closeParen, statement) 171 | .SetReduceFunction (fun a b c d e -> (c, e)) 172 | 173 | compoundStatement.AddProduction(openCurly, optionalLocalDeclarations, optionalStatementList, closeCurly) 174 | .SetReduceFunction (fun _ b c _ -> (b, c)) 175 | 176 | optionalLocalDeclarations.AddProduction(localDeclarations).SetReduceToFirst() 177 | optionalLocalDeclarations.AddProduction() .SetReduceFunction (fun () -> []) 178 | 179 | localDeclarations.AddProduction(localDeclarations, localDeclaration).SetReduceFunction (fun a b -> a @ [b]) 180 | localDeclarations.AddProduction(localDeclaration) .SetReduceFunction (fun a -> [a]) 181 | 182 | localDeclaration.AddProduction(typeSpec, identifier, semicolon) .SetReduceFunction (fun a b _ -> Ast.ScalarVariableDeclaration(a, b)) 183 | localDeclaration.AddProduction(typeSpec, identifier, openSquare, closeSquare, semicolon).SetReduceFunction (fun a b _ _ _ -> Ast.ArrayVariableDeclaration(a, b)) 184 | 185 | ifStatement.AddProduction(ifKeyword, openParen, expression, closeParen, statement, optionalElseStatement) 186 | .SetReduceFunction (fun _ _ c _ e f -> (c, e, f)) 187 | 188 | let elseStatementProduction = optionalElseStatement.AddProduction(elseKeyword, statement) 189 | elseStatementProduction.SetReduceFunction (fun _ b -> Some b) 190 | elseStatementProduction.SetPrecedence optionalElsePrecedenceGroup 191 | 192 | let elseEpsilonProduction = optionalElseStatement.AddProduction() 193 | elseEpsilonProduction.SetReduceFunction (fun () -> None) 194 | elseEpsilonProduction.SetPrecedence optionalElsePrecedenceGroup 195 | 196 | returnStatement.AddProduction(returnKeyword, expression, semicolon).SetReduceFunction (fun _ b _ -> Some b) 197 | returnStatement.AddProduction(returnKeyword, semicolon) .SetReduceFunction (fun _ _ -> None) 198 | 199 | breakStatement.AddProduction(breakKeyword, semicolon).SetReduceFunction (fun _ _ -> ()) 200 | 201 | expression.AddProduction(identifier, singleEquals, expression) 202 | .SetReduceFunction (fun a _ c -> Ast.ScalarAssignmentExpression({ Identifier = a }, c)) 203 | expression.AddProduction(identifier, openSquare, expression, closeSquare, singleEquals, expression) 204 | .SetReduceFunction (fun a _ c _ _ f -> Ast.ArrayAssignmentExpression({ Identifier = a }, c, f)) 205 | 206 | expression.AddProduction(expression, doublePipes, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.ConditionalOr, c)) 207 | expression.AddProduction(expression, doubleEquals, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.Equal, c)) 208 | expression.AddProduction(expression, bangEquals, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.NotEqual, c)) 209 | expression.AddProduction(expression, openAngleEquals, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.LessEqual, c)) 210 | expression.AddProduction(expression, openAngle, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.Less, c)) 211 | expression.AddProduction(expression, closeAngleEquals, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.GreaterEqual, c)) 212 | expression.AddProduction(expression, closeAngle, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.Greater, c)) 213 | expression.AddProduction(expression, doubleAmpersands, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.ConditionalAnd, c)) 214 | expression.AddProduction(expression, plus, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.Add, c)) 215 | expression.AddProduction(expression, minus, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.Subtract, c)) 216 | expression.AddProduction(expression, asterisk, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.Multiply, c)) 217 | expression.AddProduction(expression, forwardSlash, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.Divide, c)) 218 | expression.AddProduction(expression, percent, expression).SetReduceFunction (fun a _ c -> Ast.BinaryExpression(a, Ast.Modulus, c)) 219 | 220 | let unaryExpressionProduction = expression.AddProduction(unaryOperator, expression) 221 | unaryExpressionProduction.SetReduceFunction (fun a b -> Ast.UnaryExpression(a, b)) 222 | unaryExpressionProduction.SetPrecedence unaryExpressionPrecedenceGroup 223 | 224 | expression.AddProduction(openParen, expression, closeParen).SetReduceFunction (fun _ b _ -> b) 225 | 226 | expression.AddProduction(identifier).SetReduceFunction (fun a -> Ast.IdentifierExpression ({ Identifier = a })) 227 | expression.AddProduction(identifier, openSquare, expression, closeSquare) 228 | .SetReduceFunction (fun a _ c _ -> Ast.ArrayIdentifierExpression({ Identifier = a }, c)) 229 | expression.AddProduction(identifier, openParen, optionalArguments, closeParen) 230 | .SetReduceFunction (fun a _ c _ -> Ast.FunctionCallExpression(a, c)) 231 | expression.AddProduction(identifier, period, sizeKeyword) 232 | .SetReduceFunction (fun a _ _ -> Ast.ArraySizeExpression { Identifier = a }) 233 | 234 | expression.AddProduction(trueLiteral) .SetReduceFunction (fun a -> Ast.LiteralExpression a) 235 | expression.AddProduction(falseLiteral).SetReduceFunction (fun a -> Ast.LiteralExpression a) 236 | expression.AddProduction(intLiteral) .SetReduceFunction (fun a -> Ast.LiteralExpression a) 237 | expression.AddProduction(floatLiteral).SetReduceFunction (fun a -> Ast.LiteralExpression a) 238 | 239 | expression.AddProduction(newKeyword, typeSpec, openSquare, expression, closeSquare) 240 | .SetReduceFunction (fun _ b _ d _ -> Ast.ArrayAllocationExpression(b, d)) 241 | 242 | unaryOperator.AddProduction(exclamation).SetReduceFunction (fun a -> Ast.LogicalNegate) 243 | unaryOperator.AddProduction(minus) .SetReduceFunction (fun a -> Ast.Negate) 244 | unaryOperator.AddProduction(plus) .SetReduceFunction (fun a -> Ast.Identity) 245 | 246 | optionalArguments.AddProduction(arguments).SetReduceToFirst() 247 | optionalArguments.AddProduction() .SetReduceFunction (fun () -> []) 248 | 249 | arguments.AddProduction(arguments, comma, expression).SetReduceFunction (fun a _ c -> a @ [c]) 250 | arguments.AddProduction(expression) .SetReduceFunction (fun a -> [a]) 251 | 252 | // Ignore whitespace and comments 253 | configurator.LexerSettings.Ignore <- [| @"\s+"; @"/\*[^(\*/)]*\*/"; @"//[^\n]*\n" |] 254 | 255 | 256 | 257 | let parser = configurator.CreateParser() 258 | 259 | let parse (s : string) = 260 | try 261 | parser.Parse(s) :?> Program 262 | with 263 | | :? Piglet.Lexer.LexerException as ex -> 264 | raise (lexerError ex.Message) 265 | | :? Piglet.Parser.ParseException as ex -> 266 | raise (parserError ex.Message) -------------------------------------------------------------------------------- /src/MiniC.Compiler/ParsingUtilities.fs: -------------------------------------------------------------------------------- 1 | module MiniC.Compiler.ParsingUtilities 2 | 3 | open Piglet.Parser 4 | open Piglet.Parser.Configuration 5 | 6 | type ProductionWrapperBase (production : IProduction) = 7 | member x.Production = production 8 | member x.SetReduceToFirst () = production.SetReduceToFirst() 9 | member x.SetPrecedence(precedenceGroup) = production.SetPrecedence(precedenceGroup) 10 | 11 | type ProductionWrapper<'T> (production : IProduction) = 12 | inherit ProductionWrapperBase(production) 13 | member x.SetReduceFunction (f : (unit -> 'T)) = 14 | production.SetReduceFunction (fun o -> box (f ())) 15 | 16 | type ProductionWrapper<'a,'T> (production : IProduction) = 17 | inherit ProductionWrapperBase(production) 18 | member x.SetReduceFunction (f : ('a -> 'T)) = 19 | production.SetReduceFunction (fun o -> box (f (unbox o.[0]))) 20 | 21 | type ProductionWrapper<'a,'b,'T> (production : IProduction) = 22 | inherit ProductionWrapperBase(production) 23 | member x.SetReduceFunction (f : ('a -> 'b -> 'T)) = 24 | production.SetReduceFunction (fun o -> box (f (unbox o.[0]) (unbox o.[1]))) 25 | 26 | type ProductionWrapper<'a,'b,'c,'T> (production : IProduction) = 27 | inherit ProductionWrapperBase(production) 28 | member x.SetReduceFunction (f : ('a -> 'b -> 'c -> 'T)) = 29 | production.SetReduceFunction (fun o -> box (f (unbox o.[0]) 30 | (unbox o.[1]) 31 | (unbox o.[2]))) 32 | 33 | type ProductionWrapper<'a,'b,'c,'d,'T> (production : IProduction) = 34 | inherit ProductionWrapperBase(production) 35 | member x.SetReduceFunction (f : ('a -> 'b -> 'c -> 'd -> 'T)) = 36 | production.SetReduceFunction (fun o -> box (f (unbox o.[0]) 37 | (unbox o.[1]) 38 | (unbox o.[2]) 39 | (unbox o.[3]))) 40 | 41 | type ProductionWrapper<'a,'b,'c,'d,'e,'T> (production : IProduction) = 42 | inherit ProductionWrapperBase(production) 43 | member x.SetReduceFunction (f : ('a -> 'b -> 'c -> 'd -> 'e -> 'T)) = 44 | production.SetReduceFunction (fun o -> box (f (unbox o.[0]) 45 | (unbox o.[1]) 46 | (unbox o.[2]) 47 | (unbox o.[3]) 48 | (unbox o.[4]))) 49 | 50 | type ProductionWrapper<'a,'b,'c,'d,'e,'f,'T> (production : IProduction) = 51 | inherit ProductionWrapperBase(production) 52 | member x.SetReduceFunction (f : ('a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'T)) = 53 | production.SetReduceFunction (fun o -> box (f (unbox o.[0]) 54 | (unbox o.[1]) 55 | (unbox o.[2]) 56 | (unbox o.[3]) 57 | (unbox o.[4]) 58 | (unbox o.[5]))) 59 | 60 | type ProductionWrapper<'a,'b,'c,'d,'e,'f,'g,'T> (production : IProduction) = 61 | inherit ProductionWrapperBase(production) 62 | member x.SetReduceFunction (f : ('a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g -> 'T)) = 63 | production.SetReduceFunction (fun o -> box (f (unbox o.[0]) 64 | (unbox o.[1]) 65 | (unbox o.[2]) 66 | (unbox o.[3]) 67 | (unbox o.[4]) 68 | (unbox o.[5]) 69 | (unbox o.[6]))) 70 | 71 | type SymbolWrapper<'T> (symbol : ISymbol) = 72 | member x.Symbol = symbol 73 | 74 | type TerminalWrapper<'T> (terminal : ITerminal) = 75 | inherit SymbolWrapper<'T>(terminal) 76 | 77 | type NonTerminalWrapper<'T> (nonTerminal : INonTerminal) = 78 | inherit SymbolWrapper<'T>(nonTerminal) 79 | 80 | member x.AddProduction () = 81 | let production = nonTerminal.AddProduction() 82 | new ProductionWrapper<'T>(production) 83 | 84 | member x.AddProduction (part : SymbolWrapper<'a>) = 85 | let production = nonTerminal.AddProduction(part.Symbol) 86 | new ProductionWrapper<'a,'T>(production) 87 | 88 | member x.AddProduction((part1 : SymbolWrapper<'a>), (part2 : SymbolWrapper<'b>)) = 89 | let production = nonTerminal.AddProduction(part1.Symbol, part2.Symbol) 90 | new ProductionWrapper<'a,'b,'T>(production) 91 | 92 | member x.AddProduction((part1 : SymbolWrapper<'a>), 93 | (part2 : SymbolWrapper<'b>), 94 | (part3 : SymbolWrapper<'c>)) = 95 | let production = nonTerminal.AddProduction(part1.Symbol, 96 | part2.Symbol, 97 | part3.Symbol) 98 | new ProductionWrapper<'a,'b,'c,'T>(production) 99 | 100 | member x.AddProduction((part1 : SymbolWrapper<'a>), 101 | (part2 : SymbolWrapper<'b>), 102 | (part3 : SymbolWrapper<'c>), 103 | (part4 : SymbolWrapper<'d>)) = 104 | let production = nonTerminal.AddProduction(part1.Symbol, 105 | part2.Symbol, 106 | part3.Symbol, 107 | part4.Symbol) 108 | new ProductionWrapper<'a,'b,'c,'d,'T>(production) 109 | 110 | member x.AddProduction((part1 : SymbolWrapper<'a>), 111 | (part2 : SymbolWrapper<'b>), 112 | (part3 : SymbolWrapper<'c>), 113 | (part4 : SymbolWrapper<'d>), 114 | (part5 : SymbolWrapper<'e>)) = 115 | let production = nonTerminal.AddProduction(part1.Symbol, 116 | part2.Symbol, 117 | part3.Symbol, 118 | part4.Symbol, 119 | part5.Symbol) 120 | new ProductionWrapper<'a,'b,'c,'d,'e,'T>(production) 121 | 122 | member x.AddProduction((part1 : SymbolWrapper<'a>), 123 | (part2 : SymbolWrapper<'b>), 124 | (part3 : SymbolWrapper<'c>), 125 | (part4 : SymbolWrapper<'d>), 126 | (part5 : SymbolWrapper<'e>), 127 | (part6 : SymbolWrapper<'f>)) = 128 | let production = nonTerminal.AddProduction(part1.Symbol, 129 | part2.Symbol, 130 | part3.Symbol, 131 | part4.Symbol, 132 | part5.Symbol, 133 | part6.Symbol) 134 | new ProductionWrapper<'a,'b,'c,'d,'e,'f,'T>(production) 135 | 136 | member x.AddProduction((part1 : SymbolWrapper<'a>), 137 | (part2 : SymbolWrapper<'b>), 138 | (part3 : SymbolWrapper<'c>), 139 | (part4 : SymbolWrapper<'d>), 140 | (part5 : SymbolWrapper<'e>), 141 | (part6 : SymbolWrapper<'f>), 142 | (part7 : SymbolWrapper<'g>)) = 143 | let production = nonTerminal.AddProduction(part1.Symbol, 144 | part2.Symbol, 145 | part3.Symbol, 146 | part4.Symbol, 147 | part5.Symbol, 148 | part6.Symbol, 149 | part7.Symbol) 150 | new ProductionWrapper<'a,'b,'c,'d,'e,'f,'g,'T>(production) -------------------------------------------------------------------------------- /src/MiniC.Compiler/SemanticAnalysis.fs: -------------------------------------------------------------------------------- 1 | module MiniC.Compiler.SemanticAnalysis 2 | 3 | open System.Collections.Generic 4 | open CompilerErrors 5 | open Ast 6 | 7 | type private SymbolScope(parent : SymbolScope option) = 8 | let mutable list = List.empty 9 | let identifierFromDeclaration = 10 | function 11 | | ScalarVariableDeclaration(_, i) 12 | | ArrayVariableDeclaration(_, i) -> i 13 | 14 | let declaresIdentifier (identifierRef : IdentifierRef) declaration = 15 | (identifierFromDeclaration declaration) = identifierRef.Identifier 16 | 17 | member x.AddDeclaration declaration = 18 | if List.exists (fun x -> identifierFromDeclaration x = identifierFromDeclaration declaration) list then 19 | raise (variableAlreadyDefined (identifierFromDeclaration declaration)) 20 | list <- declaration :: list 21 | 22 | member x.FindDeclaration identifierRef = 23 | let found = List.tryFind (fun x -> declaresIdentifier identifierRef x) list 24 | match found with 25 | | Some(d) -> d 26 | | None -> 27 | match parent with 28 | | Some(ss) -> ss.FindDeclaration identifierRef 29 | | None -> raise (nameDoesNotExist (identifierRef.Identifier)) 30 | 31 | type private SymbolScopeStack() = 32 | let stack = new Stack() 33 | do stack.Push(new SymbolScope(None)) 34 | 35 | member x.CurrentScope = stack.Peek() 36 | 37 | member x.Push() = stack.Push(new SymbolScope(Some(stack.Peek()))) 38 | member x.Pop() = stack.Pop() |> ignore 39 | member x.AddDeclaration declaration = stack.Peek().AddDeclaration declaration 40 | 41 | type VariableType = 42 | { 43 | Type : TypeSpec; 44 | IsArray : bool; 45 | } 46 | override x.ToString() = 47 | x.Type.ToString() + (if x.IsArray then "[]" else "") 48 | 49 | let scalarType t = { Type = t; IsArray = false; } 50 | 51 | type FunctionTableEntry = 52 | { 53 | ReturnType : TypeSpec; 54 | ParameterTypes : VariableType list; 55 | } 56 | 57 | let typeOfDeclaration = 58 | function 59 | | Ast.ScalarVariableDeclaration(t, _) -> { Type = t; IsArray = false } 60 | | Ast.ArrayVariableDeclaration(t, _) -> { Type = t; IsArray = true } 61 | 62 | type FunctionTable(program) as self = 63 | inherit Dictionary() 64 | 65 | let rec scanDeclaration = 66 | function 67 | | StaticVariableDeclaration(x) -> () 68 | | FunctionDeclaration(t, i, p, _) -> 69 | if self.ContainsKey i then 70 | raise (functionAlreadyDefined i) 71 | self.Add(i, { ReturnType = t; ParameterTypes = List.map typeOfDeclaration p; }) 72 | 73 | do 74 | // First add built-in methods 75 | self.Add("iread", { ReturnType = Int; ParameterTypes = []; }) 76 | self.Add("iprint", { ReturnType = Void; ParameterTypes = [ { Type = Int; IsArray = false } ]; }) 77 | self.Add("fread", { ReturnType = Float; ParameterTypes = []; }) 78 | self.Add("fprint", { ReturnType = Void; ParameterTypes = [ { Type = Float; IsArray = false } ]; }) 79 | program |> List.iter scanDeclaration 80 | 81 | type SymbolTable(program) as self = 82 | inherit Dictionary(HashIdentity.Reference) 83 | 84 | let whileStatementStack = Stack() 85 | let symbolScopeStack = new SymbolScopeStack() 86 | 87 | let rec scanDeclaration = 88 | function 89 | | StaticVariableDeclaration(x) -> symbolScopeStack.AddDeclaration x 90 | | FunctionDeclaration(x) -> scanFunctionDeclaration x 91 | 92 | and scanFunctionDeclaration (functionReturnType, _, parameters, compoundStatement) = 93 | let rec scanCompoundStatement (localDeclarations, statements) = 94 | symbolScopeStack.Push() 95 | localDeclarations |> List.iter (fun d -> symbolScopeStack.AddDeclaration d) 96 | statements |> List.iter scanStatement 97 | symbolScopeStack.Pop() |> ignore 98 | 99 | and scanStatement = 100 | function 101 | | ExpressionStatement(es) -> 102 | match es with 103 | | Expression(e) -> scanExpression e 104 | | Nop -> () 105 | | CompoundStatement(x) -> scanCompoundStatement x 106 | | IfStatement(e, s1, Some(s2)) -> 107 | scanExpression e 108 | scanStatement s1 109 | scanStatement s2 110 | | IfStatement(e, s1, None) -> 111 | scanExpression e 112 | scanStatement s1 113 | | WhileStatement(e, s) -> 114 | whileStatementStack.Push (e, s) 115 | scanExpression e 116 | scanStatement s 117 | whileStatementStack.Pop() |> ignore 118 | | ReturnStatement(Some(e)) -> 119 | scanExpression e 120 | | ReturnStatement(None) -> 121 | if functionReturnType <> Void then 122 | raise (cannotConvertType (Void.ToString()) (functionReturnType.ToString())) 123 | | BreakStatement -> 124 | if whileStatementStack.Count = 0 then 125 | raise (noEnclosingLoop()) 126 | 127 | and addIdentifierMapping identifierRef = 128 | let declaration = symbolScopeStack.CurrentScope.FindDeclaration identifierRef 129 | self.Add(identifierRef, declaration) 130 | 131 | and scanExpression = 132 | function 133 | | ScalarAssignmentExpression(i, e) -> 134 | addIdentifierMapping i 135 | scanExpression e 136 | | ArrayAssignmentExpression(i, e1, e2) -> 137 | addIdentifierMapping i 138 | scanExpression e1 139 | scanExpression e2 140 | | BinaryExpression(e1, _, e2) -> 141 | scanExpression e1 142 | scanExpression e2 143 | | UnaryExpression(_, e) -> 144 | scanExpression e 145 | | IdentifierExpression(i) -> 146 | addIdentifierMapping i 147 | | ArrayIdentifierExpression(i, e) -> 148 | addIdentifierMapping i 149 | scanExpression e 150 | | FunctionCallExpression(_, args) -> 151 | args |> List.iter scanExpression 152 | | ArraySizeExpression(i) -> 153 | addIdentifierMapping i 154 | | LiteralExpression(l) -> () 155 | | ArrayAllocationExpression(_, e) -> 156 | scanExpression e 157 | 158 | symbolScopeStack.Push() 159 | parameters |> List.iter symbolScopeStack.AddDeclaration 160 | scanCompoundStatement compoundStatement 161 | symbolScopeStack.Pop() |> ignore 162 | 163 | do program |> List.iter scanDeclaration 164 | 165 | member x.GetIdentifierTypeSpec identifierRef = 166 | typeOfDeclaration self.[identifierRef] 167 | 168 | type ExpressionTypeDictionary(program, functionTable : FunctionTable, symbolTable : SymbolTable) as self = 169 | inherit Dictionary(HashIdentity.Reference) 170 | 171 | let rec scanDeclaration = 172 | function 173 | | FunctionDeclaration(x) -> scanFunctionDeclaration x 174 | | _ -> () 175 | 176 | and scanFunctionDeclaration (functionReturnType, _, _, compoundStatement) = 177 | let rec scanCompoundStatement (_, statements) = 178 | statements |> List.iter scanStatement 179 | 180 | and scanStatement = 181 | function 182 | | ExpressionStatement(es) -> 183 | match es with 184 | | Expression(e) -> scanExpression e |> ignore 185 | | Nop -> () 186 | | CompoundStatement(x) -> scanCompoundStatement x 187 | | IfStatement(e, s1, Some(s2)) -> 188 | scanExpression e |> ignore 189 | scanStatement s1 190 | scanStatement s2 191 | | IfStatement(e, s1, None) -> 192 | scanExpression e |> ignore 193 | scanStatement s1 194 | | WhileStatement(e, s) -> 195 | scanExpression e |> ignore 196 | scanStatement s 197 | | ReturnStatement(Some(e)) -> 198 | let typeOfE = scanExpression e 199 | if typeOfE <> scalarType functionReturnType then raise (cannotConvertType (typeOfE.ToString()) (functionReturnType.ToString())) 200 | | _ -> () 201 | 202 | and scanExpression expression = 203 | let checkArrayIndexType e = 204 | let arrayIndexType = scanExpression e 205 | if arrayIndexType <> scalarType Int then 206 | raise (cannotConvertType (arrayIndexType.ToString()) (Int.ToString())) 207 | 208 | let expressionType = 209 | match expression with 210 | | ScalarAssignmentExpression(i, e) -> 211 | let typeOfE = scanExpression e 212 | let typeOfI = symbolTable.GetIdentifierTypeSpec i 213 | if typeOfE <> typeOfI then raise (cannotConvertType (typeOfE.ToString()) (typeOfI.ToString())) 214 | typeOfI 215 | | ArrayAssignmentExpression(i, e1, e2) -> 216 | checkArrayIndexType e1 217 | 218 | let typeOfE2 = scanExpression e2 219 | let typeOfI = symbolTable.GetIdentifierTypeSpec i 220 | 221 | if not typeOfI.IsArray then 222 | raise (cannotApplyIndexing (typeOfI.ToString())) 223 | 224 | if typeOfE2.IsArray then 225 | raise (cannotConvertType (typeOfE2.ToString()) (typeOfI.Type.ToString())) 226 | 227 | if typeOfE2.Type <> typeOfI.Type then raise (cannotConvertType (typeOfE2.ToString()) (typeOfI.Type.ToString())) 228 | 229 | scalarType typeOfI.Type 230 | | BinaryExpression(e1, op, e2) -> 231 | let typeOfE1 = scanExpression e1 232 | let typeOfE2 = scanExpression e2 233 | match op with 234 | | ConditionalOr | ConditionalAnd -> 235 | match typeOfE1, typeOfE2 with 236 | | { Type = Bool; IsArray = false; }, { Type = Bool; IsArray = false; } -> () 237 | | _ -> raise (operatorCannotBeApplied (op.ToString()) (typeOfE1.ToString()) (typeOfE2.ToString())) 238 | scalarType Bool 239 | | Equal | NotEqual -> 240 | match typeOfE1, typeOfE2 with 241 | | { Type = a; IsArray = false; }, { Type = b; IsArray = false; } when a = b && a <> Void -> () 242 | | _ -> raise (operatorCannotBeApplied (op.ToString()) (typeOfE1.ToString()) (typeOfE2.ToString())) 243 | scalarType Bool 244 | | LessEqual | Less | GreaterEqual | Greater -> 245 | match typeOfE1, typeOfE2 with 246 | | { Type = Int; IsArray = false; }, { Type = Int; IsArray = false; } 247 | | { Type = Float; IsArray = false; }, { Type = Float; IsArray = false; } -> 248 | () 249 | | _ -> raise (operatorCannotBeApplied (op.ToString()) (typeOfE1.ToString()) (typeOfE2.ToString())) 250 | scalarType Bool 251 | | Add | Subtract | Multiply | Divide | Modulus -> 252 | typeOfE1 253 | | UnaryExpression(_, e) -> 254 | scanExpression e 255 | | IdentifierExpression(i) -> 256 | symbolTable.GetIdentifierTypeSpec i 257 | | ArrayIdentifierExpression(i, e) -> 258 | checkArrayIndexType e 259 | scalarType (symbolTable.GetIdentifierTypeSpec i).Type 260 | | FunctionCallExpression(i, a) -> 261 | if not (functionTable.ContainsKey i) then 262 | raise (nameDoesNotExist i) 263 | let calledFunction = functionTable.[i] 264 | let parameterTypes = calledFunction.ParameterTypes 265 | if List.length a <> List.length parameterTypes then 266 | raise (wrongNumberOfArguments i (List.length parameterTypes) (List.length a)) 267 | let argumentTypes = a |> List.map scanExpression 268 | let checkTypesMatch index l r = 269 | if l <> r then raise (invalidArguments i (index + 1) (l.ToString()) (r.ToString())) 270 | List.iteri2 checkTypesMatch argumentTypes parameterTypes 271 | scalarType calledFunction.ReturnType 272 | | ArraySizeExpression(i) -> 273 | scalarType Int 274 | | LiteralExpression(l) -> 275 | match l with 276 | | BoolLiteral(b) -> scalarType Bool 277 | | IntLiteral(i) -> scalarType Int 278 | | FloatLiteral(f) -> scalarType Float 279 | | ArrayAllocationExpression(t, e) -> 280 | checkArrayIndexType e 281 | { Type = t; IsArray = true } 282 | 283 | self.Add(expression, expressionType) 284 | 285 | expressionType 286 | 287 | scanCompoundStatement compoundStatement 288 | 289 | do program |> List.iter scanDeclaration 290 | 291 | type SemanticAnalysisResult = 292 | { 293 | SymbolTable : SymbolTable; 294 | ExpressionTypes : ExpressionTypeDictionary; 295 | } 296 | 297 | let analyze program = 298 | let symbolTable = new SymbolTable(program) 299 | let functionTable = new FunctionTable(program) 300 | 301 | if not (functionTable.ContainsKey "main") then 302 | raise (missingEntryPoint()) 303 | 304 | let expressionTypes = new ExpressionTypeDictionary(program, functionTable, symbolTable) 305 | 306 | { 307 | SymbolTable = symbolTable; 308 | ExpressionTypes = expressionTypes; 309 | } -------------------------------------------------------------------------------- /src/MiniC.Compiler/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/MiniC.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BD1E5409-ACF7-4843-8EA1-CC2D4C7F3587}" 7 | ProjectSection(SolutionItems) = preProject 8 | ..\docs\Grammar.txt = ..\docs\Grammar.txt 9 | EndProjectSection 10 | EndProject 11 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MiniC.Compiler", "MiniC.Compiler\MiniC.Compiler.fsproj", "{2860D24F-85D9-4BC7-8463-EE1EC134290D}" 12 | EndProject 13 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MiniC.Compiler.Tests", "MiniC.Compiler.Tests\MiniC.Compiler.Tests.fsproj", "{ADA721D1-B2FB-4C8C-A435-8D6649A050F2}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniC.Compiler.Demo", "MiniC.Compiler.Demo\MiniC.Compiler.Demo.csproj", "{BAE211A9-744C-42C9-A70E-3A16505034CF}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {2860D24F-85D9-4BC7-8463-EE1EC134290D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {2860D24F-85D9-4BC7-8463-EE1EC134290D}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {2860D24F-85D9-4BC7-8463-EE1EC134290D}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {2860D24F-85D9-4BC7-8463-EE1EC134290D}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {ADA721D1-B2FB-4C8C-A435-8D6649A050F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {ADA721D1-B2FB-4C8C-A435-8D6649A050F2}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {ADA721D1-B2FB-4C8C-A435-8D6649A050F2}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {ADA721D1-B2FB-4C8C-A435-8D6649A050F2}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {BAE211A9-744C-42C9-A70E-3A16505034CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {BAE211A9-744C-42C9-A70E-3A16505034CF}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {BAE211A9-744C-42C9-A70E-3A16505034CF}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {BAE211A9-744C-42C9-A70E-3A16505034CF}.Release|Any CPU.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(SolutionProperties) = preSolution 37 | HideSolutionNode = FALSE 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /src/MiniC.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | <data><IncludeFilters /><ExcludeFilters /></data> 4 | <data /> -------------------------------------------------------------------------------- /src/packages/AvalonEdit.4.2.0.8783/AvalonEdit.4.2.0.8783.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/AvalonEdit.4.2.0.8783/AvalonEdit.4.2.0.8783.nupkg -------------------------------------------------------------------------------- /src/packages/AvalonEdit.4.2.0.8783/AvalonEdit.4.2.0.8783.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AvalonEdit 5 | 4.2.0.8783 6 | Daniel Grunwald 7 | Daniel Grunwald 8 | http://www.opensource.org/licenses/lgpl-2.1.php 9 | http://www.avalonedit.net/ 10 | http://community.sharpdevelop.net/blogs/mattward/SharpDevelop.png 11 | true 12 | AvalonEdit is the WPF-based text editor used in SharpDevelop. There are two builds of AvalonEdit included in this package. One that targets .NET 4.0 and one that targets .NET 3.5. 13 | AvalonEdit is the WPF-based text editor used in SharpDevelop 14 | Improved WPF text rendering performance. 15 | Region tooltips added. 16 | C# syntax highlighting: Do not colorize punctuation. 17 | WPF Text Editor SharpDevelop AvalonEdit 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/packages/AvalonEdit.4.2.0.8783/lib/Net35/ICSharpCode.AvalonEdit.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/AvalonEdit.4.2.0.8783/lib/Net35/ICSharpCode.AvalonEdit.dll -------------------------------------------------------------------------------- /src/packages/AvalonEdit.4.2.0.8783/lib/Net40/ICSharpCode.AvalonEdit.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/AvalonEdit.4.2.0.8783/lib/Net40/ICSharpCode.AvalonEdit.dll -------------------------------------------------------------------------------- /src/packages/MahApps.Metro.0.10.0.0/MahApps.Metro.0.10.0.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/MahApps.Metro.0.10.0.0/MahApps.Metro.0.10.0.0.nupkg -------------------------------------------------------------------------------- /src/packages/MahApps.Metro.0.10.0.0/MahApps.Metro.0.10.0.0.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MahApps.Metro 5 | 0.10.0.0 6 | MahApps.Metro 7 | Paul Jenkins; Jake Ginnivan 8 | Paul Jenkins; Jake Ginnivan 9 | http://www.opensource.org/licenses/MS-PL 10 | https://github.com/MahApps/MahApps.Metro 11 | http://c713056.r56.cf2.rackcdn.com/mahapps.metro.logo2.png 12 | false 13 | The goal of MahApps.Metro is to allow devs to quickly and easily cobble togther a "Metro" UI for their WPF4+ apps, with minimal effort. 14 | The goal of MahApps.Metro is to allow devs to quickly and easily cobble togther a "Metro" UI for their WPF4+ apps, with minimal effort. 15 | 16 | WPF UI Metro 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/packages/MahApps.Metro.0.10.0.0/lib/net40/MahApps.Metro.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/MahApps.Metro.0.10.0.0/lib/net40/MahApps.Metro.dll -------------------------------------------------------------------------------- /src/packages/MahApps.Metro.0.10.0.0/lib/net40/System.Windows.Interactivity.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/MahApps.Metro.0.10.0.0/lib/net40/System.Windows.Interactivity.dll -------------------------------------------------------------------------------- /src/packages/MahApps.Metro.0.10.0.0/tools/install.ps1: -------------------------------------------------------------------------------- 1 | param($rootPath, $toolsPath, $package, $project) 2 | 3 | $project.DTE.ItemOperations.Navigate('http://mahapps.com/MahApps.Metro/') -------------------------------------------------------------------------------- /src/packages/NUnit.2.6.2/NUnit.2.6.2.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/NUnit.2.6.2/NUnit.2.6.2.nupkg -------------------------------------------------------------------------------- /src/packages/NUnit.2.6.2/NUnit.2.6.2.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NUnit 5 | 2.6.2 6 | NUnit 7 | Charlie Poole 8 | Charlie Poole 9 | http://nunit.org/nuget/license.html 10 | http://nunit.org/ 11 | http://nunit.org/nuget/nunit_32x32.png 12 | false 13 | NUnit features a fluent assert syntax, parameterized, generic and theory tests and is user-extensible. A number of runners, both from the NUnit project and by third parties, are able to execute NUnit tests. 14 | 15 | Version 2.6 is the seventh major release of this well-known and well-tested programming tool. 16 | 17 | This package includes only the framework assembly. You will need to install the NUnit.Runners package unless you are using a third-party runner. 18 | NUnit is a unit-testing framework for all .Net languages with a strong TDD focus. 19 | Version 2.6 is the seventh major release of NUnit. 20 | 21 | Unlike earlier versions, this package includes only the framework assembly. You will need to install the NUnit.Runners package unless you are using a third-party runner. 22 | 23 | The nunit.mocks assembly is now provided by the NUnit.Mocks package. The pnunit.framework assembly is provided by the pNUnit package. 24 | en-US 25 | test testing tdd framework fluent assert theory plugin addin 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/packages/NUnit.2.6.2/lib/nunit.framework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/NUnit.2.6.2/lib/nunit.framework.dll -------------------------------------------------------------------------------- /src/packages/NUnit.2.6.2/license.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/NUnit.2.6.2/license.txt -------------------------------------------------------------------------------- /src/packages/Piglet.1.4.0/Piglet.1.4.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/Piglet.1.4.0/Piglet.1.4.0.nupkg -------------------------------------------------------------------------------- /src/packages/Piglet.1.4.0/Piglet.1.4.0.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Piglet 5 | 1.4.0 6 | Per Dervall 7 | Per Dervall 8 | https://github.com/Dervall/Piglet/blob/master/LICENSE.txt 9 | https://github.com/Dervall/Piglet 10 | https://raw.github.com/Dervall/Piglet/master/logo32.png 11 | false 12 | Piglet is a lightweight library for lexing and parsing text, in the spirit of those big parser and lexer genererators such as bison, antlr and flex focusing on ease of use and integration. 13 | Parser and lexer generator that does not require a pre-build step and configurable using fluent configuration 14 | parser generator lexer fluent 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/packages/Piglet.1.4.0/lib/net40/Piglet.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/Piglet.1.4.0/lib/net40/Piglet.dll -------------------------------------------------------------------------------- /src/packages/Piglet.1.4.0/lib/net40/Piglet.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgjones/mini-c/d7f10323417277a6f3fe027e2b6dd74f745ac380/src/packages/Piglet.1.4.0/lib/net40/Piglet.pdb -------------------------------------------------------------------------------- /src/packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | --------------------------------------------------------------------------------