├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .travis.yml ├── Dust.CLI ├── Dust.CLI.csproj └── Program.cs ├── Dust.UnitTests ├── Dust.UnitTests.csproj ├── ExpectTests.cs └── Parser │ ├── BinaryExpressionParserTests.cs │ ├── FunctionDeclarationParserTests.cs │ ├── LiteralParserTests.cs │ ├── ParserTests.cs │ └── VariableDeclarationParserTests.cs ├── Dust.sln ├── Dust ├── Compiler │ ├── Binding │ │ ├── BinaryOperatorKind.cs │ │ ├── Binder.cs │ │ ├── BoundBinaryOperator.cs │ │ └── Tree │ │ │ ├── BoundExpression.cs │ │ │ ├── BoundNode.cs │ │ │ ├── BoundStatement.cs │ │ │ ├── BoundTree.cs │ │ │ ├── Expressions │ │ │ ├── BoundBinaryExpression.cs │ │ │ └── BoundLiteralExpression.cs │ │ │ └── Statements │ │ │ └── BoundExpressionStatement.cs │ ├── Diagnostics │ │ ├── Diagnostic.cs │ │ ├── DiagnosticSeverity.cs │ │ ├── Error.cs │ │ └── Errors.cs │ ├── Interpreter │ │ └── Interpreter.cs │ ├── Lexer │ │ ├── StringReader.cs │ │ ├── SyntaxLexer.cs │ │ ├── SyntaxToken.cs │ │ └── SyntaxTokenKind.cs │ ├── Parser │ │ ├── AccessModifier.cs │ │ ├── AccessModifierKind.cs │ │ ├── FunctionParameter.cs │ │ ├── Parsers │ │ │ ├── BinaryExpressionParser.cs │ │ │ ├── FunctionDeclarationParser.cs │ │ │ ├── LiteralParser.cs │ │ │ ├── SyntaxParserExtension.cs │ │ │ └── VariableDeclarationParser.cs │ │ ├── SyntaxParseResult.cs │ │ ├── SyntaxParser.cs │ │ └── SyntaxTree │ │ │ ├── CodeBlockNode.cs │ │ │ ├── Expression.cs │ │ │ ├── Expressions │ │ │ ├── BinaryExpression.cs │ │ │ └── LiteralExpression.cs │ │ │ ├── Statement.cs │ │ │ ├── Statements │ │ │ ├── ExpressionStatement.cs │ │ │ ├── FunctionDeclaration.cs │ │ │ └── VariableDeclaration.cs │ │ │ └── SyntaxNode.cs │ ├── SourcePosition.cs │ ├── SourceRange.cs │ ├── SyntaxFacts.cs │ └── Types │ │ ├── Definitions │ │ ├── DustBool.cs │ │ ├── DustDouble.cs │ │ ├── DustFloat.cs │ │ ├── DustInt.cs │ │ ├── DustNumber.cs │ │ ├── DustObject.cs │ │ ├── DustString.cs │ │ └── DustVoid.cs │ │ ├── DustType.cs │ │ └── DustTypes.cs ├── Dust.csproj └── Extensions │ ├── IEnumerableExtensions.cs │ ├── StringExtensions.cs │ └── TypeExtension.cs ├── LICENSE └── README.md /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build-linux: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - uses: actions/checkout@v1 10 | 11 | - name: Setup .NET Core 12 | uses: actions/setup-dotnet@v1 13 | with: 14 | dotnet-version: 3.0.101 15 | - name: Get NuGet packages 16 | run: dotnet restore 17 | - name: Build with dotnet 18 | run: dotnet build --configuration Release 19 | - name: Run Unit Tests 20 | run: dotnet test 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/csharp,jetbrains+all 3 | 4 | ### Csharp ### 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | ## 8 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | *.sln.DotSettings 16 | 17 | # User-specific files (MonoDevelop/Xamarin Studio) 18 | *.userprefs 19 | 20 | # Build results 21 | [Dd]ebug/ 22 | [Dd]ebugPublic/ 23 | [Rr]elease/ 24 | [Rr]eleases/ 25 | x64/ 26 | x86/ 27 | bld/ 28 | [Bb]in/ 29 | [Oo]bj/ 30 | [Ll]og/ 31 | 32 | # Visual Studio 2015 cache/options directory 33 | .vs/ 34 | # Uncomment if you have tasks that create the project's static files in wwwroot 35 | #wwwroot/ 36 | 37 | # MSTest test Results 38 | [Tt]est[Rr]esult*/ 39 | [Bb]uild[Ll]og.* 40 | 41 | # NUNIT 42 | *.VisualState.xml 43 | TestResult.xml 44 | 45 | # Build Results of an ATL Project 46 | [Dd]ebugPS/ 47 | [Rr]eleasePS/ 48 | dlldata.c 49 | 50 | # .NET Core 51 | project.lock.json 52 | project.fragment.lock.json 53 | artifacts/ 54 | **/Properties/launchSettings.json 55 | 56 | *_i.c 57 | *_p.c 58 | *_i.h 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.svclog 79 | *.scc 80 | 81 | # Chutzpah Test files 82 | _Chutzpah* 83 | 84 | # Visual C++ cache files 85 | ipch/ 86 | *.aps 87 | *.ncb 88 | *.opendb 89 | *.opensdf 90 | *.sdf 91 | *.cachefile 92 | *.VC.db 93 | *.VC.VC.opendb 94 | 95 | # Visual Studio profiler 96 | *.psess 97 | *.vsp 98 | *.vspx 99 | *.sap 100 | 101 | # TFS 2012 Local Workspace 102 | $tf/ 103 | 104 | # Guidance Automation Toolkit 105 | *.gpState 106 | 107 | # ReSharper is a .NET coding add-in 108 | _ReSharper*/ 109 | *.[Rr]e[Ss]harper 110 | *.DotSettings.user 111 | 112 | # JustCode is a .NET coding add-in 113 | .JustCode 114 | 115 | # TeamCity is a build add-in 116 | _TeamCity* 117 | 118 | # DotCover is a Code Coverage Tool 119 | *.dotCover 120 | 121 | # Visual Studio code coverage results 122 | *.coverage 123 | *.coveragexml 124 | 125 | # NCrunch 126 | _NCrunch_* 127 | .*crunch*.local.xml 128 | nCrunchTemp_* 129 | 130 | # MightyMoose 131 | *.mm.* 132 | AutoTest.Net/ 133 | 134 | # Web workbench (sass) 135 | .sass-cache/ 136 | 137 | # Installshield output folder 138 | [Ee]xpress/ 139 | 140 | # DocProject is a documentation generator add-in 141 | DocProject/buildhelp/ 142 | DocProject/Help/*.HxT 143 | DocProject/Help/*.HxC 144 | DocProject/Help/*.hhc 145 | DocProject/Help/*.hhk 146 | DocProject/Help/*.hhp 147 | DocProject/Help/Html2 148 | DocProject/Help/html 149 | 150 | # Click-Once directory 151 | publish/ 152 | 153 | # Publish Web Output 154 | *.[Pp]ublish.xml 155 | *.azurePubxml 156 | # TODO: Uncomment the next line to ignore your web deploy settings. 157 | # By default, sensitive information, such as encrypted password 158 | # should be stored in the .pubxml.user file. 159 | #*.pubxml 160 | *.pubxml.user 161 | *.publishproj 162 | 163 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 164 | # checkin your Azure Web App publish settings, but sensitive information contained 165 | # in these scripts will be unencrypted 166 | PublishScripts/ 167 | 168 | # NuGet Packages 169 | *.nupkg 170 | # The packages folder can be ignored because of Package Restore 171 | **/packages/* 172 | # except build/, which is used as an MSBuild target. 173 | !**/packages/build/ 174 | # Uncomment if necessary however generally it will be regenerated when needed 175 | #!**/packages/repositories.config 176 | # NuGet v3's project.json files produces more ignorable files 177 | *.nuget.props 178 | *.nuget.targets 179 | 180 | # Microsoft Azure Build Output 181 | csx/ 182 | *.build.csdef 183 | 184 | # Microsoft Azure Emulator 185 | ecf/ 186 | rcf/ 187 | 188 | # Windows Store app package directories and files 189 | AppPackages/ 190 | BundleArtifacts/ 191 | Package.StoreAssociation.xml 192 | _pkginfo.txt 193 | 194 | # Visual Studio cache files 195 | # files ending in .cache can be ignored 196 | *.[Cc]ache 197 | # but keep track of directories ending in .cache 198 | !*.[Cc]ache/ 199 | 200 | # Others 201 | ClientBin/ 202 | ~$* 203 | *~ 204 | *.dbmdl 205 | *.dbproj.schemaview 206 | *.jfm 207 | *.pfx 208 | *.publishsettings 209 | orleans.codegen.cs 210 | 211 | # Since there are multiple workflows, uncomment next line to ignore bower_components 212 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 213 | #bower_components/ 214 | 215 | # RIA/Silverlight projects 216 | Generated_Code/ 217 | 218 | # Backup & report files from converting an old project file 219 | # to a newer Visual Studio version. Backup files are not needed, 220 | # because we have git ;-) 221 | _UpgradeReport_Files/ 222 | Backup*/ 223 | UpgradeLog*.XML 224 | UpgradeLog*.htm 225 | 226 | # SQL Server files 227 | *.mdf 228 | *.ldf 229 | *.ndf 230 | 231 | # Business Intelligence projects 232 | *.rdl.data 233 | *.bim.layout 234 | *.bim_*.settings 235 | 236 | # Microsoft Fakes 237 | FakesAssemblies/ 238 | 239 | # GhostDoc plugin setting file 240 | *.GhostDoc.xml 241 | 242 | # Node.js Tools for Visual Studio 243 | .ntvs_analysis.dat 244 | node_modules/ 245 | 246 | # Typescript v1 declaration files 247 | typings/ 248 | 249 | # Visual Studio 6 build log 250 | *.plg 251 | 252 | # Visual Studio 6 workspace options file 253 | *.opt 254 | 255 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 256 | *.vbw 257 | 258 | # Visual Studio LightSwitch build output 259 | **/*.HTMLClient/GeneratedArtifacts 260 | **/*.DesktopClient/GeneratedArtifacts 261 | **/*.DesktopClient/ModelManifest.xml 262 | **/*.Server/GeneratedArtifacts 263 | **/*.Server/ModelManifest.xml 264 | _Pvt_Extensions 265 | 266 | # Paket dependency manager 267 | .paket/paket.exe 268 | paket-files/ 269 | 270 | # FAKE - F# Make 271 | .fake/ 272 | 273 | # JetBrains Rider 274 | .idea/ 275 | *.sln.iml 276 | 277 | # CodeRush 278 | .cr/ 279 | 280 | # Python Tools for Visual Studio (PTVS) 281 | __pycache__/ 282 | *.pyc 283 | 284 | # Cake - Uncomment if you are using it 285 | # tools/** 286 | # !tools/packages.config 287 | 288 | # Telerik's JustMock configuration file 289 | *.jmconfig 290 | 291 | # BizTalk build output 292 | *.btp.cs 293 | *.btm.cs 294 | *.odx.cs 295 | *.xsd.cs 296 | 297 | ### JetBrains+all ### 298 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 299 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 300 | 301 | # User-specific stuff: 302 | .idea/**/workspace.xml 303 | .idea/**/tasks.xml 304 | .idea/dictionaries 305 | 306 | # Sensitive or high-churn files: 307 | .idea/**/dataSources/ 308 | .idea/**/dataSources.ids 309 | .idea/**/dataSources.xml 310 | .idea/**/dataSources.local.xml 311 | .idea/**/sqlDataSources.xml 312 | .idea/**/dynamic.xml 313 | .idea/**/uiDesigner.xml 314 | 315 | # Gradle: 316 | .idea/**/gradle.xml 317 | .idea/**/libraries 318 | 319 | # CMake 320 | cmake-build-debug/ 321 | 322 | # Mongo Explorer plugin: 323 | .idea/**/mongoSettings.xml 324 | 325 | ## File-based project format: 326 | *.iws 327 | 328 | ## Plugin-specific files: 329 | 330 | # IntelliJ 331 | /out/ 332 | 333 | # mpeltonen/sbt-idea plugin 334 | .idea_modules/ 335 | 336 | # JIRA plugin 337 | atlassian-ide-plugin.xml 338 | 339 | # Cursive Clojure plugin 340 | .idea/replstate.xml 341 | 342 | # Ruby plugin and RubyMine 343 | /.rakeTasks 344 | 345 | # Crashlytics plugin (for Android Studio and IntelliJ) 346 | com_crashlytics_export_strings.xml 347 | crashlytics.properties 348 | crashlytics-build.properties 349 | fabric.properties 350 | 351 | ### JetBrains+all Patch ### 352 | # Ignores the whole idea folder 353 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 354 | 355 | 356 | 357 | # End of https://www.gitignore.io/api/csharp,jetbrains+all 358 | \.vscode/ 359 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | mono: none 3 | dotnet: 3.0.101 4 | 5 | solution: Dust.sln 6 | 7 | install: 8 | - dotnet restore 9 | 10 | script: 11 | - dotnet build --configuration Release 12 | - dotnet test 13 | -------------------------------------------------------------------------------- /Dust.CLI/Dust.CLI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp3.0 5 | default 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Dust.CLI/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Dust.Compiler.Diagnostics; 4 | using Dust.Compiler.Interpreter; 5 | using Dust.Compiler.Lexer; 6 | using Dust.Compiler.Parser; 7 | using Dust.Compiler.Types; 8 | 9 | namespace Dust.CLI 10 | { 11 | public static class Program 12 | { 13 | public static void Main() 14 | { 15 | while (true) 16 | { 17 | Console.Write("> "); 18 | 19 | string input = Console.ReadLine(); 20 | 21 | if (input == "") 22 | { 23 | continue; 24 | } 25 | 26 | if (input == "exit") 27 | { 28 | return; 29 | } 30 | 31 | SyntaxLexer lexer = new SyntaxLexer(); 32 | SyntaxParser parser = new SyntaxParser(); 33 | Interpreter interpreter = new Interpreter(); 34 | 35 | List tokens = lexer.Lex(input); 36 | 37 | SyntaxParseResult result = parser.Parse(tokens); 38 | 39 | foreach (Diagnostic diagnostic in result.Diagnostics) 40 | { 41 | Console.WriteLine($"{diagnostic.Severity}: {diagnostic.Message} at {diagnostic.Range.Start}"); 42 | } 43 | 44 | DustObject value = interpreter.Interpret(result.Node); 45 | 46 | Console.WriteLine($"{value.ToString().Value} ({value.TypeName})"); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Dust.UnitTests/Dust.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Dust.UnitTests/ExpectTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dust.UnitTests 4 | { 5 | public class ExpectTests 6 | { 7 | public ExpectFunctions Expect(T result) 8 | { 9 | return new ExpectFunctions(result); 10 | } 11 | 12 | public class ExpectFunctions 13 | { 14 | private readonly T result; 15 | 16 | public ExpectFunctions(T result) 17 | { 18 | this.result = result; 19 | } 20 | 21 | public TExpectFunctions To => (TExpectFunctions) Activator.CreateInstance(typeof(TExpectFunctions), result); 22 | public TExpectFunctions And => (TExpectFunctions) Activator.CreateInstance(typeof(TExpectFunctions), result); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Dust.UnitTests/Parser/BinaryExpressionParserTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Dust.Compiler.Lexer; 4 | using Dust.Compiler.Parser.SyntaxTree; 5 | using Dust.Compiler.Parser.SyntaxTree.Expressions; 6 | using Xunit; 7 | 8 | namespace Dust.UnitTests.Parser 9 | { 10 | public class BinaryExpressionParserTests : ParserTests 11 | { 12 | [Theory] 13 | [InlineData("11+11")] 14 | [InlineData("11 + 11")] 15 | [InlineData("11 - 11")] 16 | [InlineData("11 * 11")] 17 | [InlineData("11 / 11")] 18 | [InlineData("11 % 11")] 19 | public void SimpleArithmeticBinaryExpressions(string code) 20 | { 21 | Setup(code); 22 | 23 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateLiteralBinaryExpression(tokens.First(), 11, tokens[1], tokens[2], 11)); 24 | } 25 | 26 | [Theory] 27 | [InlineData("11+=11")] 28 | [InlineData("11 += 11")] 29 | [InlineData("11 -= 11")] 30 | [InlineData("11 *= 11")] 31 | [InlineData("11 /= 11")] 32 | [InlineData("11 %= 11")] 33 | [InlineData("11 ** 11")] 34 | public void TwoCharArithmeticBinaryExpressions(string code) 35 | { 36 | Setup(code); 37 | 38 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateLiteralBinaryExpression(tokens.First(), 11, tokens[1], tokens[2], 11)); 39 | } 40 | 41 | [Theory] 42 | [InlineData("11 == 11")] 43 | [InlineData("11 != 11")] 44 | [InlineData("11 && 11")] 45 | [InlineData("11 || 11")] 46 | [InlineData("11 > 11")] 47 | [InlineData("11 >= 11")] 48 | [InlineData("11 < 11")] 49 | [InlineData("11 <= 11")] 50 | public void BooleanBinaryExpressions(string code) 51 | { 52 | Setup(code); 53 | 54 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateLiteralBinaryExpression(tokens.First(), 11, tokens[1], tokens[2], 11)); 55 | } 56 | 57 | [Theory] 58 | [InlineData("11**=11")] 59 | [InlineData("11 **= 11")] 60 | public void ThreeCharBinaryExpressions(string code) 61 | { 62 | Setup(code); 63 | 64 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateLiteralBinaryExpression(tokens.First(), 11, tokens[1], tokens[2], 11)); 65 | } 66 | 67 | private CodeBlockNode CreateLiteralBinaryExpression(SyntaxToken leftToken, int leftValue, SyntaxToken operatorToken, SyntaxToken rightToken, int rightValue) 68 | { 69 | BinaryExpression expression = new BinaryExpression(CreateLiteralExpression(leftToken, leftValue), operatorToken, CreateLiteralExpression(rightToken, rightValue)); 70 | 71 | root.Children.Add(new ExpressionStatement(expression)); 72 | 73 | return root; 74 | } 75 | 76 | private static Expression CreateLiteralExpression(SyntaxToken token, object value) 77 | { 78 | Expression expression = (Expression) Activator.CreateInstance(typeof(LiteralExpression), token, value); 79 | 80 | return expression; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /Dust.UnitTests/Parser/FunctionDeclarationParserTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Dust.Compiler.Lexer; 4 | using Dust.Compiler.Parser; 5 | using Dust.Compiler.Parser.SyntaxTree; 6 | using Xunit; 7 | 8 | namespace Dust.UnitTests.Parser 9 | { 10 | public class FunctionDeclarationParserTests : ParserTests 11 | { 12 | private List identifiers; 13 | 14 | protected override void Setup(string code) 15 | { 16 | base.Setup(code); 17 | 18 | identifiers = tokens.FindAll((token) => token.Is(SyntaxTokenKind.Identifier)); 19 | } 20 | 21 | [Theory] 22 | [InlineData("fn function() {}", true)] 23 | [InlineData("fn function()")] 24 | [InlineData("fn function {}", true)] 25 | [InlineData("fn function")] 26 | public void SimpleFunction(string code, bool hasBody = false) 27 | { 28 | Setup(code); 29 | 30 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateFunctionDeclarationNode(identifiers.First(), hasBody: hasBody)); 31 | } 32 | 33 | [Theory] 34 | [InlineData("fn function(): int {}", true)] 35 | [InlineData("fn function(): int")] 36 | [InlineData("fn function: int {}", true)] 37 | [InlineData("fn function: int")] 38 | public void SimpleFunctionWithReturnType(string code, bool hasBody = false) 39 | { 40 | Setup(code); 41 | 42 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateFunctionDeclarationNode(identifiers.First(), null, identifiers.Last(), hasBody)); 43 | } 44 | 45 | [Theory] 46 | [InlineData("fn function(param1: int) {}", true)] 47 | [InlineData("fn function(param1: int)")] 48 | public void FunctionWithOneParameter(string code, bool hasBody = false) 49 | { 50 | Setup(code); 51 | 52 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateFunctionDeclarationNode(identifiers.First(), new List 53 | { 54 | new FunctionParameter(identifiers[1], identifiers[2], false) 55 | }, hasBody: hasBody)); 56 | } 57 | 58 | 59 | [Theory] 60 | [InlineData("fn function(mut param1: int, param2: string, param3: float, mut param4) {}", true)] 61 | [InlineData("fn function(mut param1: int, param2: string, param3: float, mut param4)")] 62 | public void FunctionWithMultipleParameters(string code, bool hasBody = false) 63 | { 64 | Setup(code); 65 | 66 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateFunctionDeclarationNode(identifiers.First(), new List 67 | { 68 | new FunctionParameter(identifiers[1], identifiers[2], true), 69 | new FunctionParameter(identifiers[3], identifiers[4], false), 70 | new FunctionParameter(identifiers[5], identifiers[6], false), 71 | new FunctionParameter(identifiers[7], null, true) 72 | }, hasBody: hasBody)); 73 | } 74 | 75 | [Theory] 76 | [InlineData("fn function(mut param1: int, param2: string, param3: float, mut param4): int {}", true)] 77 | [InlineData("fn function(mut param1: int, param2: string, param3: float, mut param4): int")] 78 | public void FunctionWithMultipleParametersAndReturnType(string code, bool hasBody = false) 79 | { 80 | Setup(code); 81 | 82 | // TODO: Do this automagically 83 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateFunctionDeclarationNode(identifiers.First(), new List 84 | { 85 | new FunctionParameter(identifiers[1], identifiers[2], true), 86 | new FunctionParameter(identifiers[3], identifiers[4], false), 87 | new FunctionParameter(identifiers[5], identifiers[6], false), 88 | new FunctionParameter(identifiers[7], null, true) 89 | }, identifiers.Last(), hasBody)); 90 | } 91 | 92 | private CodeBlockNode CreateFunctionDeclarationNode(SyntaxToken functionNameToken, List parameters = null, SyntaxToken returnTypeToken = null, bool hasBody = true) 93 | { 94 | if (parameters == null) 95 | { 96 | parameters = new List(); 97 | } 98 | 99 | root.Children.Add(new FunctionDeclaration(tokens.Find((token) => token.Is(SyntaxTokenKind.FnKeyword)), functionNameToken, parameters, tokens.Find((token) => token.Is(SyntaxTokenKind.CloseParenthesis)), hasBody 100 | ? new CodeBlockNode(tokens.Find((token) => token.Is(SyntaxTokenKind.OpenBrace))) {ClosingBrace = tokens.Find((token) => token.Is(SyntaxTokenKind.CloseBrace))} 101 | : null, returnTypeToken)); 102 | 103 | return root; 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /Dust.UnitTests/Parser/LiteralParserTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Dust.Compiler.Lexer; 4 | using Dust.Compiler.Parser.SyntaxTree; 5 | using Dust.Compiler.Parser.SyntaxTree.Expressions; 6 | using Xunit; 7 | 8 | namespace Dust.UnitTests.Parser 9 | { 10 | public class LiteralParserTests : ParserTests 11 | { 12 | [Theory] 13 | [InlineData("\"\"", "")] 14 | [InlineData("\"a string\"", "a string")] 15 | [InlineData("'single quote string'", "single quote string")] 16 | public void StringLiteral(string code, string expectedValue) 17 | { 18 | Setup(code); 19 | 20 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateLiteralExpression(tokens.First(), expectedValue)); 21 | } 22 | 23 | [Theory] 24 | [InlineData("12345678", 12345678)] 25 | [InlineData("12.33", 12.33f)] 26 | [InlineData("420.69f", 420.69f)] 27 | [InlineData("420.69d", 420.69)] 28 | public void NumericLiteral(string code, object expectedValue) 29 | { 30 | Setup(code); 31 | 32 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateLiteralExpression(tokens.First(), expectedValue)); 33 | } 34 | 35 | private CodeBlockNode CreateLiteralExpression(SyntaxToken token, object value) 36 | { 37 | Expression expression = (Expression) Activator.CreateInstance(typeof(LiteralExpression), token, value); 38 | 39 | root.Children.Add(new ExpressionStatement(expression)); 40 | 41 | return root; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Dust.UnitTests/Parser/ParserTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Dust.Compiler; 4 | using Dust.Compiler.Diagnostics; 5 | using Dust.Compiler.Lexer; 6 | using Dust.Compiler.Parser; 7 | using Dust.Compiler.Parser.SyntaxTree; 8 | using JetBrains.Annotations; 9 | using KellermanSoftware.CompareNetObjects; 10 | using Xunit; 11 | 12 | namespace Dust.UnitTests.Parser 13 | { 14 | public class ParserTests : ExpectTests 15 | { 16 | protected CodeBlockNode root; 17 | protected SourceRange range; 18 | protected List tokens; 19 | 20 | private readonly SyntaxLexer lexer = new SyntaxLexer(); 21 | private readonly SyntaxParser parser = new SyntaxParser(); 22 | private static readonly CompareLogic comparer = new CompareLogic(); 23 | 24 | protected virtual void Setup(string code) 25 | { 26 | range = SourceRange.FromText(code); 27 | root = new CodeBlockNode(range: range); 28 | tokens = lexer.Lex(code); 29 | } 30 | 31 | protected SyntaxParseResult Parse() 32 | { 33 | return parser.Parse(tokens); 34 | } 35 | 36 | [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] 37 | public class ExceptToFunctions 38 | { 39 | private readonly SyntaxParseResult result; 40 | 41 | public ExceptToFunctions(SyntaxParseResult result) 42 | { 43 | this.result = result; 44 | } 45 | 46 | public ExpectFunctions ParseSuccessfully() 47 | { 48 | Assert.True(result.Diagnostics.Any((diagnostic) => diagnostic.Severity == DiagnosticSeverity.Error) == false, $"Failed to parse\n{string.Join('\n', result.Diagnostics)}"); 49 | 50 | return new ExpectFunctions(result); 51 | } 52 | 53 | public ExpectFunctions SyntaxTreeOf(SyntaxNode node) 54 | { 55 | ComparisonResult comparisonResult = comparer.Compare(result.Node, node); 56 | 57 | string[] differences = comparisonResult.Differences.Select((difference) => $"{difference.ExpectedName}.{difference.PropertyName} ({difference.Object2Value}) != {difference.ActualName}.{difference.PropertyName} ({(difference.Object1)})").ToArray(); 58 | 59 | Assert.True(comparisonResult.AreEqual, $"Syntax tree mismatch\n{string.Join("\n", differences.ToArray())}"); 60 | 61 | return new ExpectFunctions(result); 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Dust.UnitTests/Parser/VariableDeclarationParserTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Dust.Compiler.Lexer; 5 | using Dust.Compiler.Parser.SyntaxTree; 6 | using Dust.Compiler.Parser.SyntaxTree.Expressions; 7 | using Xunit; 8 | 9 | namespace Dust.UnitTests.Parser 10 | { 11 | public class VariableDeclarationParserTests : ParserTests 12 | { 13 | private bool isMutable; 14 | private List identifiers; 15 | 16 | protected override void Setup(string code) 17 | { 18 | base.Setup(code); 19 | 20 | isMutable = tokens.Any((token) => token.Is(SyntaxTokenKind.MutKeyword)); 21 | identifiers = tokens.FindAll((token) => token.Is(SyntaxTokenKind.Identifier)); 22 | } 23 | 24 | [Theory] 25 | [InlineData("let var: int")] 26 | [InlineData("mut var: int")] 27 | public void SimpleVariable(string code) 28 | { 29 | Setup(code); 30 | 31 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateVariableDeclaration(identifiers.First(), identifiers[1])); 32 | } 33 | 34 | [Theory] 35 | [InlineData("let var: int = 10", 10)] 36 | [InlineData("let var: string = 'string'", "string")] 37 | [InlineData("mut var: string = 'string'", "string")] 38 | [InlineData("mut var = 'string'", "string", false)] 39 | [InlineData("let var = 'string'", "string", false)] 40 | public void VariableWithInitializer(string code, object value, bool hasType = true) 41 | { 42 | Setup(code); 43 | 44 | Expect(Parse()).To.ParseSuccessfully().And.SyntaxTreeOf(CreateVariableDeclaration(identifiers.First(), hasType ? identifiers[1] : null, CreateLiteralExpression(tokens[tokens.Count - 2], value))); 45 | } 46 | 47 | private CodeBlockNode CreateVariableDeclaration(SyntaxToken nameToken, SyntaxToken typeToken, Expression initializer = null) 48 | { 49 | if (root == null) 50 | { 51 | throw new Exception("You forgot to call Setup(code)"); 52 | } 53 | 54 | root.Children.Add(new VariableDeclaration(tokens.First(), nameToken, isMutable, typeToken, initializer)); 55 | 56 | return root; 57 | } 58 | 59 | private static Expression CreateLiteralExpression(SyntaxToken token, object value) 60 | { 61 | Expression expression = (Expression) Activator.CreateInstance(typeof(LiteralExpression), token, value); 62 | 63 | return expression; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Dust.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dust", "Dust\Dust.csproj", "{D04A33B9-0AC9-4570-B9E2-4E84B22FADBB}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dust.CLI", "Dust.CLI\Dust.CLI.csproj", "{8E63F424-FE78-46B0-80BB-0102F89502B9}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dust.UnitTests", "Dust.UnitTests\Dust.UnitTests.csproj", "{7FEC2C2A-6BC4-4297-9D24-93854ECF56A3}" 8 | EndProject 9 | Global 10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 11 | Debug|Any CPU = Debug|Any CPU 12 | Release|Any CPU = Release|Any CPU 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {D04A33B9-0AC9-4570-B9E2-4E84B22FADBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 16 | {D04A33B9-0AC9-4570-B9E2-4E84B22FADBB}.Debug|Any CPU.Build.0 = Debug|Any CPU 17 | {D04A33B9-0AC9-4570-B9E2-4E84B22FADBB}.Release|Any CPU.ActiveCfg = Release|Any CPU 18 | {D04A33B9-0AC9-4570-B9E2-4E84B22FADBB}.Release|Any CPU.Build.0 = Release|Any CPU 19 | {8E63F424-FE78-46B0-80BB-0102F89502B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {8E63F424-FE78-46B0-80BB-0102F89502B9}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {8E63F424-FE78-46B0-80BB-0102F89502B9}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {8E63F424-FE78-46B0-80BB-0102F89502B9}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {7FEC2C2A-6BC4-4297-9D24-93854ECF56A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {7FEC2C2A-6BC4-4297-9D24-93854ECF56A3}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {7FEC2C2A-6BC4-4297-9D24-93854ECF56A3}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {7FEC2C2A-6BC4-4297-9D24-93854ECF56A3}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Dust/Compiler/Binding/BinaryOperatorKind.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | 3 | namespace Dust.Compiler.Parser 4 | { 5 | public enum BinaryOperatorKind 6 | { 7 | Add = SyntaxTokenKind.Plus, 8 | Subtract = SyntaxTokenKind.Minus, 9 | Multiply = SyntaxTokenKind.Asterisk, 10 | Divide = SyntaxTokenKind.Slash, 11 | Modulo = SyntaxTokenKind.Percent, 12 | Exponentiate = SyntaxTokenKind.AsteriskAsterisk, 13 | Equal = SyntaxTokenKind.EqualsEquals, 14 | NotEqual = SyntaxTokenKind.NotEqual, 15 | And = SyntaxTokenKind.AmpersandAmpersand, 16 | Or = SyntaxTokenKind.PipePipe, 17 | GreaterThan = SyntaxTokenKind.GreaterThan, 18 | GreaterThanEqual = SyntaxTokenKind.GreaterThanEqual, 19 | LessThan = SyntaxTokenKind.LessThan, 20 | LessThanEqual = SyntaxTokenKind.LessThanEqual, 21 | Assign = SyntaxTokenKind.Equal, 22 | AddAssign = SyntaxTokenKind.PlusEquals, 23 | SubtractAssign = SyntaxTokenKind.MinusEquals, 24 | MultiplyAssign = SyntaxTokenKind.AsteriskEquals, 25 | DivideAssign = SyntaxTokenKind.SlashEquals, 26 | ModuloAssign = SyntaxTokenKind.PercentEquals, 27 | ExponentiateAssign = SyntaxTokenKind.AsteriskAsteriskEquals, 28 | } 29 | } -------------------------------------------------------------------------------- /Dust/Compiler/Binding/Binder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Dust.Compiler.Binding.Tree; 3 | using Dust.Compiler.Binding.Tree.Expressions; 4 | using Dust.Compiler.Parser; 5 | using Dust.Compiler.Parser.SyntaxTree; 6 | using Dust.Compiler.Parser.SyntaxTree.Expressions; 7 | 8 | namespace Dust.Compiler.Binding 9 | { 10 | public class Binder 11 | { 12 | public BoundTree Bind(CodeBlockNode node) 13 | { 14 | BoundTree tree = new BoundTree(); 15 | 16 | foreach (Statement statement in node.Children) 17 | { 18 | tree.Statements.Add(BindStatement(statement)); 19 | } 20 | 21 | return tree; 22 | } 23 | 24 | private BoundStatement BindStatement(Statement statement) 25 | { 26 | if (statement is ExpressionStatement expressionStatement) 27 | { 28 | return BindExpressionStatement(expressionStatement); 29 | } 30 | 31 | return null; 32 | } 33 | 34 | private BoundExpressionStatement BindExpressionStatement(ExpressionStatement expressionStatement) 35 | { 36 | return new BoundExpressionStatement(BindExpression(expressionStatement.Expression)); 37 | } 38 | 39 | private BoundExpression BindExpression(Expression expression) 40 | { 41 | if (expression is BinaryExpression binaryExpression) 42 | { 43 | return BindBinaryExpression(binaryExpression); 44 | } 45 | 46 | if (expression is LiteralExpression literalExpression) 47 | { 48 | return BindLiteralExpression(literalExpression); 49 | } 50 | 51 | return null; 52 | } 53 | 54 | private BoundBinaryExpression BindBinaryExpression(BinaryExpression binaryExpression) 55 | { 56 | BoundExpression left = BindExpression(binaryExpression.Left); 57 | BoundExpression right = BindExpression(binaryExpression.Right); 58 | 59 | BinaryOperatorKind kind = (BinaryOperatorKind) binaryExpression.OperatorToken.Kind; 60 | BoundBinaryOperator @operator = BoundBinaryOperator.Bind(left.Type, kind, right.Type); 61 | 62 | if (@operator == null) 63 | { 64 | // TODO: BoundErrorExpressions. 65 | // return new BoundErrorExpression(binaryExpression); 66 | 67 | throw new Exception($"{kind} operation not implemented for {left.Type} and {right.Type}."); 68 | } 69 | 70 | return new BoundBinaryExpression(left, @operator, right); 71 | } 72 | 73 | private BoundLiteralExpression BindLiteralExpression(LiteralExpression literalExpression) 74 | { 75 | return new BoundLiteralExpression(literalExpression.Value); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /Dust/Compiler/Binding/BoundBinaryOperator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Dust.Compiler.Parser; 3 | using Dust.Compiler.Types; 4 | 5 | namespace Dust.Compiler.Binding 6 | { 7 | public class BoundBinaryOperator 8 | { 9 | public BinaryOperatorKind Kind { get; } 10 | public DustType LeftType { get; } 11 | public DustType RightType { get; } 12 | public DustType ReturnType { get; } 13 | public bool IsInterchangeable { get; } 14 | 15 | public BoundBinaryOperator(BinaryOperatorKind kind, DustType type) 16 | : this(kind, type, type, type) 17 | { 18 | } 19 | 20 | public BoundBinaryOperator(BinaryOperatorKind kind, DustType leftType, DustType rightType, DustType returnType, bool isInterchangeable = true) 21 | { 22 | Kind = kind; 23 | LeftType = leftType; 24 | RightType = rightType; 25 | ReturnType = returnType; 26 | IsInterchangeable = isInterchangeable; 27 | } 28 | 29 | private static readonly List binaryOperators = new List 30 | { 31 | new BoundBinaryOperator(BinaryOperatorKind.Add, DustTypes.Number), 32 | new BoundBinaryOperator(BinaryOperatorKind.Add, DustTypes.String), 33 | new BoundBinaryOperator(BinaryOperatorKind.Add, DustTypes.String, DustTypes.Number, DustTypes.String), 34 | new BoundBinaryOperator(BinaryOperatorKind.Subtract, DustTypes.Number), 35 | new BoundBinaryOperator(BinaryOperatorKind.Subtract, DustTypes.String), 36 | new BoundBinaryOperator(BinaryOperatorKind.Divide, DustTypes.Number), 37 | new BoundBinaryOperator(BinaryOperatorKind.Divide, DustTypes.String), 38 | new BoundBinaryOperator(BinaryOperatorKind.Divide, DustTypes.String, DustTypes.Int, DustTypes.String), 39 | new BoundBinaryOperator(BinaryOperatorKind.Multiply, DustTypes.Number), 40 | new BoundBinaryOperator(BinaryOperatorKind.Multiply, DustTypes.String, DustTypes.Int, DustTypes.String), 41 | new BoundBinaryOperator(BinaryOperatorKind.Modulo, DustTypes.Number), 42 | new BoundBinaryOperator(BinaryOperatorKind.Exponentiate, DustTypes.Number), 43 | new BoundBinaryOperator(BinaryOperatorKind.Equal, DustTypes.Bool), 44 | new BoundBinaryOperator(BinaryOperatorKind.Equal, DustTypes.Number), 45 | new BoundBinaryOperator(BinaryOperatorKind.NotEqual, DustTypes.Bool), 46 | new BoundBinaryOperator(BinaryOperatorKind.NotEqual, DustTypes.Number), 47 | new BoundBinaryOperator(BinaryOperatorKind.And, DustTypes.Bool), 48 | new BoundBinaryOperator(BinaryOperatorKind.Or, DustTypes.Bool), 49 | new BoundBinaryOperator(BinaryOperatorKind.GreaterThan, DustTypes.Number), 50 | new BoundBinaryOperator(BinaryOperatorKind.GreaterThanEqual, DustTypes.Number), 51 | new BoundBinaryOperator(BinaryOperatorKind.LessThan, DustTypes.Number), 52 | new BoundBinaryOperator(BinaryOperatorKind.LessThanEqual, DustTypes.Number), 53 | }; 54 | 55 | public static BoundBinaryOperator Bind(DustType leftType, BinaryOperatorKind kind, DustType rightType) 56 | { 57 | foreach (BoundBinaryOperator @operator in binaryOperators) 58 | { 59 | if (@operator.Kind == kind) 60 | { 61 | if (@operator.LeftType.IsAssignableFrom(leftType) && @operator.RightType.IsAssignableFrom(rightType)) 62 | { 63 | return @operator; 64 | } 65 | 66 | if (@operator.IsInterchangeable && @operator.LeftType.IsAssignableFrom(rightType) && @operator.RightType.IsAssignableFrom(leftType)) 67 | { 68 | return @operator; 69 | } 70 | } 71 | } 72 | 73 | return null; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Dust/Compiler/Binding/Tree/BoundExpression.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Types; 2 | 3 | namespace Dust.Compiler.Binding.Tree 4 | { 5 | public abstract class BoundExpression : BoundNode 6 | { 7 | public DustObject Value { get; protected set; } 8 | public virtual DustType Type => Value; 9 | } 10 | } -------------------------------------------------------------------------------- /Dust/Compiler/Binding/Tree/BoundNode.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Binding.Tree 2 | { 3 | public class BoundNode 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /Dust/Compiler/Binding/Tree/BoundStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Binding.Tree 2 | { 3 | public class BoundStatement : BoundNode 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /Dust/Compiler/Binding/Tree/BoundTree.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Dust.Compiler.Binding.Tree 4 | { 5 | public class BoundTree 6 | { 7 | public List Statements { get; } 8 | 9 | public BoundTree() 10 | { 11 | Statements = new List(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Dust/Compiler/Binding/Tree/Expressions/BoundBinaryExpression.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Types; 2 | 3 | namespace Dust.Compiler.Binding.Tree.Expressions 4 | { 5 | public class BoundBinaryExpression : BoundExpression 6 | { 7 | public BoundExpression Left { get; } 8 | public BoundBinaryOperator Operator { get; } 9 | public BoundExpression Right { get; } 10 | public override DustType Type => Operator.ReturnType; 11 | 12 | public BoundBinaryExpression(BoundExpression left, BoundBinaryOperator @operator, BoundExpression right) 13 | { 14 | Left = left; 15 | Operator = @operator; 16 | Right = right; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Dust/Compiler/Binding/Tree/Expressions/BoundLiteralExpression.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Types; 2 | 3 | namespace Dust.Compiler.Binding.Tree.Expressions 4 | { 5 | public class BoundLiteralExpression : BoundExpression 6 | { 7 | public BoundLiteralExpression(object value) 8 | { 9 | Value = DustTypes.FromNative(value); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Dust/Compiler/Binding/Tree/Statements/BoundExpressionStatement.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Parser.SyntaxTree; 2 | 3 | namespace Dust.Compiler.Binding.Tree 4 | { 5 | public class BoundExpressionStatement : BoundStatement 6 | { 7 | public BoundExpression Expression { get; } 8 | 9 | public BoundExpressionStatement(BoundExpression expression) 10 | { 11 | Expression = expression; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Dust/Compiler/Diagnostics/Diagnostic.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Diagnostics 2 | { 3 | public class Diagnostic 4 | { 5 | public string Message { get; } 6 | public DiagnosticSeverity Severity { get; } 7 | public SourceRange Range { get; } 8 | 9 | protected Diagnostic(string message, SourceRange range, DiagnosticSeverity severity) 10 | { 11 | Message = message; 12 | Range = range; 13 | Severity = severity; 14 | } 15 | 16 | public override string ToString() 17 | { 18 | return Message; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Dust/Compiler/Diagnostics/DiagnosticSeverity.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Diagnostics 2 | { 3 | public enum DiagnosticSeverity 4 | { 5 | Info, 6 | Warning, 7 | Error 8 | } 9 | } -------------------------------------------------------------------------------- /Dust/Compiler/Diagnostics/Error.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Diagnostics 2 | { 3 | public class Error : Diagnostic 4 | { 5 | public Error(string message, SourceRange range) 6 | : base(message, range, DiagnosticSeverity.Error) 7 | { 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /Dust/Compiler/Diagnostics/Errors.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | using Dust.Compiler.Parser; 3 | 4 | namespace Dust.Compiler.Diagnostics 5 | { 6 | public static class Errors 7 | { 8 | public static Error ModifierAlreadySeen(SyntaxToken modifierToken) => CreateError($"modifier '{AccessModifier.ParseKind(modifierToken.Kind)}' already seen", modifierToken); 9 | public static Error IncombinableModifier(SyntaxToken modiferToken, AccessModifierKind otherKind) => CreateError($"modifier '{AccessModifier.ParseKind(modiferToken.Kind)}' can't be combined with modifier '{otherKind}'", modiferToken); 10 | public static Error UnexpectedToken(SyntaxToken actualToken, SyntaxTokenKind expectedKind) => CreateError($"unexpected token <{actualToken.Kind}>, expected <{expectedKind}>", actualToken); 11 | public static Error ExpectedToken(SyntaxToken token, SyntaxTokenKind expectedKind) => CreateError($"expected token <{expectedKind}> but couldn't find it", token); 12 | public static Error UnexpectedTokenGlobal(SyntaxToken token) => CreateError($"unexpected token '{token.Kind}'", token); 13 | public static Error UnknownType(SyntaxToken typeToken) => CreateError($"unknown type '{typeToken.Text}'", typeToken); 14 | 15 | private static Error CreateError(string message, SyntaxToken token) 16 | { 17 | return new Error(message, token.Range); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Dust/Compiler/Interpreter/Interpreter.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Binding; 2 | using Dust.Compiler.Binding.Tree; 3 | using Dust.Compiler.Binding.Tree.Expressions; 4 | using Dust.Compiler.Parser; 5 | using Dust.Compiler.Parser.SyntaxTree; 6 | using Dust.Compiler.Types; 7 | 8 | namespace Dust.Compiler.Interpreter 9 | { 10 | public class Interpreter 11 | { 12 | public DustObject Interpret(CodeBlockNode root) 13 | { 14 | BoundTree tree = new Binder().Bind(root); 15 | 16 | /*foreach (Statement statement in root.Children) 17 | {*/ 18 | return EvaluateStatement(tree.Statements[0]); 19 | /*}*/ 20 | } 21 | 22 | private DustObject EvaluateStatement(BoundStatement statement) 23 | { 24 | if (statement is BoundExpressionStatement expressionStatement) 25 | { 26 | return EvaluateExpressionStatement(expressionStatement); 27 | } 28 | 29 | return null; 30 | } 31 | 32 | private DustObject EvaluateExpression(BoundExpression expression) 33 | { 34 | if (expression is BoundBinaryExpression binaryExpression) 35 | { 36 | return EvaluateBinaryExpression(binaryExpression); 37 | } 38 | 39 | if (expression is BoundLiteralExpression literalExpression) 40 | { 41 | return EvaluateLiteralExpression(literalExpression); 42 | } 43 | 44 | return null; 45 | } 46 | 47 | private DustObject EvaluateExpressionStatement(BoundExpressionStatement expressionStatement) 48 | { 49 | return EvaluateExpression(expressionStatement.Expression); 50 | } 51 | 52 | private DustObject EvaluateBinaryExpression(BoundBinaryExpression binaryExpression) 53 | { 54 | DustObject left = EvaluateExpression(binaryExpression.Left); 55 | DustObject right = EvaluateExpression(binaryExpression.Right); 56 | 57 | BoundBinaryOperator @operator = binaryExpression.Operator; 58 | 59 | if (@operator.IsInterchangeable && !left.IsAssignableFrom(@operator.LeftType)) 60 | { 61 | (left, right) = (right, left); 62 | } 63 | 64 | switch (binaryExpression.Operator.Kind) 65 | { 66 | case BinaryOperatorKind.Add: 67 | return left.Add(right); 68 | case BinaryOperatorKind.Subtract: 69 | return left.Subtract(right); 70 | case BinaryOperatorKind.Multiply: 71 | return left.Multiply(right); 72 | case BinaryOperatorKind.Divide: 73 | return left.Divide(right); 74 | /*case BinaryOperatorKind.Modulo: 75 | if (left is double || right is double) 76 | { 77 | return (double) left % (double) right; 78 | } 79 | 80 | if (left is float || right is float) 81 | { 82 | return (float) left % (float) right; 83 | } 84 | 85 | return (int) left % (int) right; 86 | case BinaryOperatorKind.Exponentiate: 87 | return Convert.ChangeType(Math.Pow(Convert.ToDouble(left), Convert.ToDouble(right)), binaryExpression.Type.ToNativeType()); 88 | case BinaryOperatorKind.Equal: 89 | { 90 | if (left is bool leftBoolValue && right is bool rightBoolValue) 91 | { 92 | return leftBoolValue == rightBoolValue; 93 | } 94 | 95 | if (left is double || right is double) 96 | { 97 | return (double) left == (double) right; 98 | } 99 | 100 | if (left is float || right is float) 101 | { 102 | return (float) left == (float) right; 103 | } 104 | 105 | if (left is int || right is int) 106 | { 107 | return (int) left == (int) right; 108 | } 109 | 110 | throw new Exception("Equal only implemented for booleans."); 111 | } 112 | case BinaryOperatorKind.NotEqual: 113 | { 114 | if (left is bool leftBoolValue && right is bool rightBoolValue) 115 | { 116 | return leftBoolValue != rightBoolValue; 117 | } 118 | 119 | if (left is double || right is double) 120 | { 121 | return (double) left != (double) right; 122 | } 123 | 124 | if (left is float || right is float) 125 | { 126 | return (float) left != (float) right; 127 | } 128 | 129 | if (left is int || right is int) 130 | { 131 | return (int) left != (int) right; 132 | } 133 | 134 | throw new Exception("NotEqual only implemented for booleans."); 135 | } 136 | case BinaryOperatorKind.And: 137 | return (bool) left && (bool) right; 138 | case BinaryOperatorKind.Or: 139 | return (bool) left || (bool) right; 140 | case BinaryOperatorKind.GreaterThan: 141 | return Convert.ToDouble(left) > Convert.ToDouble(right); 142 | case BinaryOperatorKind.GreaterThanEqual: 143 | return Convert.ToDouble(left) >= Convert.ToDouble(right); 144 | case BinaryOperatorKind.LessThan: 145 | return Convert.ToDouble(left) < Convert.ToDouble(right); 146 | case BinaryOperatorKind.LessThanEqual: 147 | return Convert.ToDouble(left) <= Convert.ToDouble(right);*/ 148 | default: 149 | return null; 150 | } 151 | } 152 | 153 | private DustObject EvaluateLiteralExpression(BoundLiteralExpression literalExpression) 154 | { 155 | return literalExpression.Value; 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /Dust/Compiler/Lexer/StringReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using Dust.Extensions; 5 | 6 | namespace Dust.Compiler.Lexer 7 | { 8 | public class StringReader 9 | { 10 | public int Position { get; private set; } 11 | 12 | public SourcePosition SourcePosition => GetSourcePosition(Position); 13 | 14 | public char Current => Text[Position]; 15 | public string Text { get; } 16 | 17 | public StringReader(string text) 18 | { 19 | Position = -1; 20 | Text = text; 21 | } 22 | 23 | public char Peek() 24 | { 25 | if (Position + 1 >= Text.Length) 26 | { 27 | return default; 28 | } 29 | 30 | return Text[Position + 1]; 31 | } 32 | 33 | public char PeekBack() 34 | { 35 | return Text[Position - 1]; 36 | } 37 | 38 | public char Advance() 39 | { 40 | Position++; 41 | 42 | if (IsAtEnd()) 43 | { 44 | return default; 45 | } 46 | 47 | return Text[Position]; 48 | } 49 | 50 | public string Range(int start, int end) 51 | { 52 | return Text.SubstringRange(start, end); 53 | } 54 | 55 | public string Range(SourcePosition start, int end) 56 | { 57 | return Range(start.Position, end); 58 | } 59 | 60 | public string Range(SourceRange range) 61 | { 62 | return Range(range.Start.Position, range.End.Position); 63 | } 64 | 65 | public void Revert() 66 | { 67 | Position--; 68 | } 69 | 70 | public SourcePosition GetSourcePosition(int position) 71 | { 72 | Debug.Assert(position <= Text.Length); 73 | 74 | string text = Text.SubstringRange(0, position); 75 | 76 | int line = text.Count(character => character == '\n'); 77 | int column = Math.Max(position - (text.LastIndexOf('\n') == -1 ? 0 : text.LastIndexOf('\n') + 1), 0); 78 | 79 | return new SourcePosition(line, column); 80 | } 81 | 82 | public bool IsAtEnd() 83 | { 84 | return Position >= Text.Length; 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /Dust/Compiler/Lexer/SyntaxLexer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace Dust.Compiler.Lexer 6 | { 7 | public class SyntaxLexer 8 | { 9 | private StringReader source; 10 | 11 | public List Lex(string text) 12 | { 13 | Regex.Replace(text, "(\r\n|\r|\n)", "\n"); 14 | 15 | source = new StringReader(text); 16 | 17 | List tokens = new List(); 18 | 19 | if (string.IsNullOrWhiteSpace(source.Text)) 20 | { 21 | return tokens; 22 | } 23 | 24 | char character = source.Advance(); 25 | 26 | while (!source.IsAtEnd()) 27 | { 28 | SyntaxToken syntaxToken = LexCharacter(character); 29 | 30 | if (syntaxToken != null) 31 | { 32 | tokens.Add(syntaxToken); 33 | } 34 | 35 | character = source.Advance(); 36 | } 37 | 38 | tokens.Add(new SyntaxToken 39 | { 40 | Kind = SyntaxTokenKind.EndOfFile, 41 | Position = source.GetSourcePosition(source.Text.Length - 1) 42 | }); 43 | 44 | return tokens; 45 | } 46 | 47 | private SyntaxToken LexCharacter(char character) 48 | { 49 | int start = source.Position; 50 | 51 | SyntaxToken token = new SyntaxToken 52 | { 53 | Position = source.GetSourcePosition(start) 54 | }; 55 | 56 | switch (character) 57 | { 58 | case ' ': 59 | return null; 60 | case '(': 61 | token.Kind = SyntaxTokenKind.OpenParenthesis; 62 | 63 | break; 64 | case ')': 65 | token.Kind = SyntaxTokenKind.CloseParenthesis; 66 | 67 | break; 68 | case '{': 69 | token.Kind = SyntaxTokenKind.OpenBrace; 70 | 71 | break; 72 | case '}': 73 | token.Kind = SyntaxTokenKind.CloseBrace; 74 | 75 | break; 76 | case '[': 77 | token.Kind = SyntaxTokenKind.OpenBracket; 78 | 79 | break; 80 | case ']': 81 | token.Kind = SyntaxTokenKind.CloseBracket; 82 | 83 | break; 84 | case '.': 85 | token.Kind = SyntaxTokenKind.Dot; 86 | 87 | break; 88 | case ':': 89 | token.Kind = SyntaxTokenKind.Colon; 90 | 91 | break; 92 | case ',': 93 | token.Kind = SyntaxTokenKind.Comma; 94 | 95 | break; 96 | case '=': 97 | if (source.Peek() == '=') 98 | { 99 | source.Advance(); 100 | 101 | token.Kind = SyntaxTokenKind.EqualsEquals; 102 | 103 | break; 104 | } 105 | 106 | token.Kind = SyntaxTokenKind.Equal; 107 | 108 | break; 109 | case '+': 110 | if (source.Peek() == '=') 111 | { 112 | source.Advance(); 113 | 114 | token.Kind = SyntaxTokenKind.PlusEquals; 115 | 116 | break; 117 | } 118 | 119 | if (source.Peek() == '+') 120 | { 121 | source.Advance(); 122 | 123 | token.Kind = SyntaxTokenKind.PlusPlus; 124 | 125 | break; 126 | } 127 | 128 | token.Kind = SyntaxTokenKind.Plus; 129 | 130 | break; 131 | case '-': 132 | if (source.Peek() == '=') 133 | { 134 | source.Advance(); 135 | 136 | token.Kind = SyntaxTokenKind.MinusEquals; 137 | 138 | break; 139 | } 140 | 141 | if (source.Peek() == '-') 142 | { 143 | source.Advance(); 144 | 145 | token.Kind = SyntaxTokenKind.MinusMinus; 146 | 147 | break; 148 | } 149 | 150 | token.Kind = SyntaxTokenKind.Minus; 151 | 152 | break; 153 | case '*': 154 | if (source.Peek() == '=') 155 | { 156 | source.Advance(); 157 | 158 | token.Kind = SyntaxTokenKind.AsteriskEquals; 159 | 160 | break; 161 | } 162 | 163 | if (source.Peek() == '*') 164 | { 165 | source.Advance(); 166 | 167 | if (source.Peek() == '=') 168 | { 169 | source.Advance(); 170 | 171 | token.Kind = SyntaxTokenKind.AsteriskAsteriskEquals; 172 | 173 | break; 174 | } 175 | 176 | token.Kind = SyntaxTokenKind.AsteriskAsterisk; 177 | 178 | break; 179 | } 180 | 181 | token.Kind = SyntaxTokenKind.Asterisk; 182 | 183 | break; 184 | case '%': 185 | if (source.Peek() == '=') 186 | { 187 | source.Advance(); 188 | 189 | token.Kind = SyntaxTokenKind.PercentEquals; 190 | 191 | break; 192 | } 193 | 194 | token.Kind = SyntaxTokenKind.Percent; 195 | 196 | break; 197 | case '/': 198 | if (source.Peek() == '=') 199 | { 200 | source.Advance(); 201 | 202 | token.Kind = SyntaxTokenKind.SlashEquals; 203 | 204 | break; 205 | } 206 | 207 | if (source.Peek() == '/') 208 | { 209 | source.Advance(); 210 | 211 | token.Kind = SyntaxTokenKind.SlashSlash; 212 | 213 | break; 214 | } 215 | 216 | token.Kind = SyntaxTokenKind.Slash; 217 | 218 | break; 219 | case '!': 220 | if (source.Peek() == '=') 221 | { 222 | source.Advance(); 223 | 224 | token.Kind = SyntaxTokenKind.NotEqual; 225 | 226 | break; 227 | } 228 | 229 | token.Kind = SyntaxTokenKind.Bang; 230 | 231 | break; 232 | case '&': 233 | if (source.Peek() == '&') 234 | { 235 | source.Advance(); 236 | 237 | token.Kind = SyntaxTokenKind.AmpersandAmpersand; 238 | 239 | break; 240 | } 241 | 242 | token.Kind = SyntaxTokenKind.Ampersand; 243 | 244 | break; 245 | case '|': 246 | if (source.Peek() == '|') 247 | { 248 | source.Advance(); 249 | 250 | token.Kind = SyntaxTokenKind.PipePipe; 251 | 252 | break; 253 | } 254 | 255 | token.Kind = SyntaxTokenKind.Pipe; 256 | 257 | break; 258 | case '>': 259 | if (source.Peek() == '=') 260 | { 261 | source.Advance(); 262 | 263 | token.Kind = SyntaxTokenKind.GreaterThanEqual; 264 | 265 | break; 266 | } 267 | 268 | token.Kind = SyntaxTokenKind.GreaterThan; 269 | 270 | break; 271 | case '<': 272 | if (source.Peek() == '=') 273 | { 274 | source.Advance(); 275 | 276 | token.Kind = SyntaxTokenKind.LessThanEqual; 277 | 278 | break; 279 | } 280 | 281 | token.Kind = SyntaxTokenKind.LessThan; 282 | 283 | break; 284 | case '"': 285 | token = LexStringLiteral(false); 286 | 287 | break; 288 | case '\'': 289 | token = LexStringLiteral(true); 290 | 291 | break; 292 | case '\n': 293 | token.Kind = SyntaxTokenKind.EndOfLine; 294 | 295 | break; 296 | default: 297 | if (char.IsDigit(character)) 298 | { 299 | token = LexNumericLiteral(); 300 | 301 | break; 302 | } 303 | 304 | if (char.IsLetter(character) || character == '_') 305 | { 306 | token = LexIdentifierOrKeyword(); 307 | 308 | break; 309 | } 310 | 311 | token = null; 312 | 313 | break; 314 | } 315 | 316 | if (token == null) 317 | { 318 | return new SyntaxToken(new SourceRange(source.GetSourcePosition(start), source.SourcePosition)) 319 | { 320 | Kind = SyntaxTokenKind.Invalid 321 | }; 322 | } 323 | 324 | if (token.Position == null) 325 | { 326 | token.Position = source.GetSourcePosition(start); 327 | } 328 | 329 | if (token.Lexeme == null) 330 | { 331 | int offset = start == source.Position ? 1 : 0; 332 | string text = source.Range(start, source.Position + offset); 333 | 334 | token.Lexeme = text; 335 | 336 | if (token.Text == null) 337 | { 338 | token.Text = text; 339 | } 340 | } 341 | 342 | return token; 343 | } 344 | 345 | private SyntaxToken LexNumericLiteral() 346 | { 347 | char character = source.Current; 348 | 349 | SourcePosition startPosition = source.SourcePosition; 350 | 351 | bool dotFound = false; 352 | 353 | SyntaxTokenKind? kind = null; 354 | 355 | while (!source.IsAtEnd()) 356 | { 357 | if (character == '.') 358 | { 359 | if (dotFound) 360 | { 361 | Console.WriteLine("invalid dot"); 362 | 363 | return null; 364 | } 365 | 366 | dotFound = true; 367 | 368 | kind = SyntaxTokenKind.FloatLiteral; 369 | } 370 | 371 | if (SyntaxFacts.IsNumeric(source.Peek())) 372 | { 373 | character = source.Advance(); 374 | } 375 | else 376 | { 377 | break; 378 | } 379 | } 380 | 381 | char next = source.Peek(); 382 | 383 | bool suffix = false; 384 | 385 | if (next != default) 386 | { 387 | char nextLower = char.ToLower(next); 388 | 389 | if (nextLower == 'd') 390 | { 391 | kind = SyntaxTokenKind.DoubleLiteral; 392 | 393 | suffix = true; 394 | } 395 | else if (nextLower == 'f') 396 | { 397 | kind = SyntaxTokenKind.FloatLiteral; 398 | 399 | suffix = true; 400 | } 401 | } 402 | 403 | string text = source.Range(startPosition, source.Position + 1); 404 | 405 | if (suffix) 406 | { 407 | source.Advance(); 408 | } 409 | 410 | return new SyntaxToken 411 | { 412 | Kind = kind ?? SyntaxTokenKind.IntLiteral, 413 | Position = startPosition, 414 | Text = text, 415 | Lexeme = source.Range(startPosition, source.Position + 1) 416 | }; 417 | } 418 | 419 | private SyntaxToken LexIdentifierOrKeyword() 420 | { 421 | SourcePosition start = source.SourcePosition; 422 | 423 | while (!source.IsAtEnd()) 424 | { 425 | if (SyntaxFacts.IsValidIdentiferOrKeywordCharacter(source.Peek())) 426 | { 427 | source.Advance(); 428 | } 429 | else 430 | { 431 | break; 432 | } 433 | } 434 | 435 | string text = source.Range(start.Position, source.Position + 1); 436 | 437 | SyntaxTokenKind? keywordKind = LexKeyword(text); 438 | 439 | return new SyntaxToken 440 | { 441 | Kind = keywordKind ?? SyntaxTokenKind.Identifier, 442 | Position = start, 443 | Text = text, 444 | Lexeme = text 445 | }; 446 | } 447 | 448 | private SyntaxToken LexStringLiteral(bool singleQuote) 449 | { 450 | SourcePosition startPosition = source.SourcePosition; 451 | 452 | source.Advance(); 453 | 454 | char character = source.Current; 455 | 456 | char terminator = singleQuote ? '\'' : '"'; 457 | 458 | while (!source.IsAtEnd() && character != terminator) 459 | { 460 | character = source.Advance(); 461 | } 462 | 463 | if (source.IsAtEnd()) 464 | { 465 | // syntax error 466 | Console.WriteLine("unterminated string literal"); 467 | 468 | return null; 469 | } 470 | 471 | source.Advance(); 472 | 473 | string lexeme = source.Range(startPosition, source.Position); 474 | 475 | return new SyntaxToken 476 | { 477 | Kind = SyntaxTokenKind.StringLiteral, 478 | Position = startPosition, 479 | Text = lexeme.Substring(1, lexeme.Length - 2), 480 | Lexeme = lexeme 481 | }; 482 | } 483 | 484 | private static SyntaxTokenKind? LexKeyword(string text) 485 | { 486 | switch (text) 487 | { 488 | case "let": 489 | return SyntaxTokenKind.LetKeyword; 490 | case "fn": 491 | return SyntaxTokenKind.FnKeyword; 492 | case "mut": 493 | return SyntaxTokenKind.MutKeyword; 494 | case "return": 495 | return SyntaxTokenKind.ReturnKeyword; 496 | case "typeof": 497 | return SyntaxTokenKind.TypeOfKeyword; 498 | case "true": 499 | return SyntaxTokenKind.TrueKeyword; 500 | case "false": 501 | return SyntaxTokenKind.FalseKeyword; 502 | case "if": 503 | return SyntaxTokenKind.IfKeyword; 504 | case "else": 505 | return SyntaxTokenKind.ElseKeyword; 506 | case "elif": 507 | return SyntaxTokenKind.ElifKeyword; 508 | case "null": 509 | return SyntaxTokenKind.NullKeyword; 510 | case "public": 511 | return SyntaxTokenKind.PublicKeyword; 512 | case "internal": 513 | return SyntaxTokenKind.InternalKeyword; 514 | case "protected": 515 | return SyntaxTokenKind.ProtectedKeyword; 516 | case "private": 517 | return SyntaxTokenKind.PrivateKeyword; 518 | case "static": 519 | return SyntaxTokenKind.StaticKeyword; 520 | default: 521 | return null; 522 | } 523 | } 524 | } 525 | } -------------------------------------------------------------------------------- /Dust/Compiler/Lexer/SyntaxToken.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Dust.Compiler.Parser.SyntaxTree; 3 | 4 | namespace Dust.Compiler.Lexer 5 | { 6 | public class SyntaxToken : SyntaxNode 7 | { 8 | public static SyntaxToken Invalid { get; } = new SyntaxToken 9 | { 10 | Kind = SyntaxTokenKind.Invalid 11 | }; 12 | 13 | public SyntaxTokenKind Kind { get; set; } 14 | public string Text { get; set; } 15 | public string Lexeme { get; set; } 16 | public SourcePosition Position { get; set; } 17 | public sealed override SourceRange Range => new SourceRange(Position, Position + Lexeme.Length - 1); 18 | 19 | public bool Is(SyntaxTokenKind kind) => kind == Kind; 20 | public bool IsOr(params SyntaxTokenKind[] kinds) => kinds.Contains(Kind); 21 | 22 | public bool Isnt(SyntaxTokenKind kind) => !Is(kind); 23 | public bool IsntOr(params SyntaxTokenKind[] kinds) => !IsOr(kinds); 24 | 25 | public SyntaxToken(SourceRange range = null) 26 | { 27 | Range = range; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Dust/Compiler/Lexer/SyntaxTokenKind.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Lexer 2 | { 3 | public enum SyntaxTokenKind 4 | { 5 | // Simple tokens 6 | OpenParenthesis, 7 | CloseParenthesis, 8 | OpenBrace, 9 | CloseBrace, 10 | OpenBracket, 11 | CloseBracket, 12 | Dot, 13 | Comma, 14 | Colon, 15 | Bang, 16 | Equal, 17 | NotEqual, 18 | EqualsEquals, 19 | Plus, 20 | PlusEquals, 21 | PlusPlus, 22 | Minus, 23 | MinusEquals, 24 | MinusMinus, 25 | Asterisk, 26 | AsteriskEquals, 27 | AsteriskAsterisk, 28 | AsteriskAsteriskEquals, 29 | Slash, 30 | SlashEquals, 31 | SlashSlash, 32 | Percent, 33 | PercentEquals, 34 | Ampersand, 35 | AmpersandAmpersand, 36 | Pipe, 37 | PipePipe, 38 | GreaterThan, 39 | GreaterThanEqual, 40 | LessThan, 41 | LessThanEqual, 42 | 43 | // Literals 44 | IntLiteral, 45 | FloatLiteral, 46 | DoubleLiteral, 47 | StringLiteral, 48 | Identifier, 49 | 50 | // Keywords 51 | LetKeyword, 52 | FnKeyword, 53 | MutKeyword, 54 | ReturnKeyword, 55 | TypeOfKeyword, 56 | TrueKeyword, 57 | FalseKeyword, 58 | IfKeyword, 59 | ElseKeyword, 60 | ElifKeyword, 61 | NullKeyword, 62 | PublicKeyword, 63 | InternalKeyword, 64 | ProtectedKeyword, 65 | PrivateKeyword, 66 | StaticKeyword, 67 | 68 | // Invisible tokens 69 | EndOfLine, 70 | EndOfFile, 71 | 72 | Invalid, 73 | } 74 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/AccessModifier.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | 3 | namespace Dust.Compiler.Parser 4 | { 5 | public class AccessModifier 6 | { 7 | public SyntaxToken Token { get; } 8 | public AccessModifierKind Kind { get; } 9 | 10 | public AccessModifier(SyntaxToken token, AccessModifierKind kind) 11 | { 12 | Token = token; 13 | Kind = kind; 14 | } 15 | 16 | public static AccessModifierKind? ParseKind(SyntaxTokenKind kind) 17 | { 18 | switch (kind) 19 | { 20 | case SyntaxTokenKind.PublicKeyword: 21 | return AccessModifierKind.Public; 22 | case SyntaxTokenKind.InternalKeyword: 23 | return AccessModifierKind.Internal; 24 | case SyntaxTokenKind.ProtectedKeyword: 25 | return AccessModifierKind.Protected; 26 | case SyntaxTokenKind.PrivateKeyword: 27 | return AccessModifierKind.Private; 28 | case SyntaxTokenKind.StaticKeyword: 29 | return AccessModifierKind.Static; 30 | } 31 | 32 | return null; 33 | } 34 | 35 | public override string ToString() 36 | { 37 | return Kind.ToString(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/AccessModifierKind.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Parser 2 | { 3 | public enum AccessModifierKind 4 | { 5 | Public, 6 | Internal, 7 | Protected, 8 | Private, 9 | Static 10 | } 11 | 12 | public static class AccessModifierKindExtensions 13 | { 14 | public static string ToString(AccessModifierKind kind) 15 | { 16 | return kind.ToString().ToLower(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/FunctionParameter.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | 3 | namespace Dust.Compiler.Parser 4 | { 5 | public class FunctionParameter 6 | { 7 | public SyntaxToken NameToken { get; } 8 | public SyntaxToken TypeToken { get; } 9 | public bool IsMutable { get; } 10 | 11 | public FunctionParameter(SyntaxToken nameToken, SyntaxToken typeToken, bool isMutable) 12 | { 13 | NameToken = nameToken; 14 | TypeToken = typeToken; 15 | IsMutable = isMutable; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/Parsers/BinaryExpressionParser.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | using Dust.Compiler.Parser.SyntaxTree; 3 | using Dust.Compiler.Parser.SyntaxTree.Expressions; 4 | 5 | namespace Dust.Compiler.Parser.Parsers 6 | { 7 | public class BinaryExpressionParser : SyntaxParserExtension 8 | { 9 | public BinaryExpressionParser(SyntaxParser parser) 10 | : base(parser) 11 | { 12 | } 13 | 14 | public BinaryExpression Parse(Expression left) 15 | { 16 | SyntaxToken operatorToken = Parser.CurrentToken; 17 | 18 | Parser.Advance(); 19 | 20 | Expression right = Parser.ParseExpression(); 21 | 22 | return new BinaryExpression(left, operatorToken, right); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/Parsers/FunctionDeclarationParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Dust.Compiler.Diagnostics; 3 | using Dust.Compiler.Lexer; 4 | using Dust.Compiler.Parser.SyntaxTree; 5 | 6 | namespace Dust.Compiler.Parser.Parsers 7 | { 8 | public class FunctionDeclarationParser : SyntaxParserExtension 9 | { 10 | public FunctionDeclarationParser(SyntaxParser parser) 11 | : base(parser) 12 | { 13 | } 14 | 15 | public FunctionDeclaration Parse() 16 | { 17 | SyntaxToken fnToken = Parser.ExpectToken(SyntaxTokenKind.FnKeyword); 18 | SyntaxToken nameToken = Parser.ExpectToken(SyntaxTokenKind.Identifier); 19 | SyntaxToken closeParenthesisToken = null; 20 | 21 | List parameters = new List(); 22 | 23 | if (Parser.MatchToken(SyntaxTokenKind.OpenParenthesis)) 24 | { 25 | if (Parser.MatchToken(SyntaxTokenKind.CloseParenthesis) == false) 26 | { 27 | // TODO: This probably shouldn't be an infinite loop 28 | while (true) 29 | { 30 | bool isMutable = Parser.MatchToken(SyntaxTokenKind.MutKeyword); 31 | 32 | SyntaxToken parameterNameToken = Parser.ExpectToken(SyntaxTokenKind.Identifier); 33 | SyntaxToken parameterTypeToken = Parser.ParseOptionalType(); 34 | 35 | parameters.Add(new FunctionParameter(parameterNameToken, parameterTypeToken, isMutable)); 36 | 37 | if (Parser.MatchToken(SyntaxTokenKind.Comma) == false) 38 | { 39 | closeParenthesisToken = Parser.ExpectToken(SyntaxTokenKind.CloseParenthesis); 40 | 41 | break; 42 | } 43 | } 44 | } 45 | else 46 | { 47 | closeParenthesisToken = Parser.PeekBack(); 48 | } 49 | } 50 | 51 | SyntaxToken returnTypeToken = Parser.ParseOptionalType(); 52 | 53 | CodeBlockNode bodyNode = null; 54 | 55 | if (Parser.MatchToken(SyntaxTokenKind.OpenBrace)) 56 | { 57 | bodyNode = new CodeBlockNode(Parser.PeekBack()); 58 | 59 | while (Parser.MatchToken(SyntaxTokenKind.CloseBrace) == false) 60 | { 61 | if (Parser.IsAtEnd()) 62 | { 63 | Parser.Error(Errors.ExpectedToken(Parser.CurrentToken, SyntaxTokenKind.CloseBrace)); 64 | } 65 | 66 | Statement statement = Parser.ParseStatement(); 67 | 68 | if (statement == null) 69 | { 70 | break; 71 | } 72 | 73 | bodyNode.Children.Add(statement); 74 | } 75 | 76 | bodyNode.ClosingBrace = Parser.PeekBack(); 77 | } 78 | 79 | return new FunctionDeclaration(fnToken, nameToken, parameters, closeParenthesisToken, bodyNode, returnTypeToken); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/Parsers/LiteralParser.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | using Dust.Compiler.Parser.SyntaxTree; 3 | using Dust.Compiler.Parser.SyntaxTree.Expressions; 4 | 5 | namespace Dust.Compiler.Parser.Parsers 6 | { 7 | public class LiteralParser : SyntaxParserExtension 8 | { 9 | public LiteralParser(SyntaxParser parser) 10 | : base(parser) 11 | { 12 | } 13 | 14 | public Expression TryParse() 15 | { 16 | SyntaxToken token = Parser.CurrentToken; 17 | 18 | Parser.Advance(); 19 | 20 | switch (token.Kind) 21 | { 22 | case SyntaxTokenKind.StringLiteral: 23 | return new LiteralExpression(token, token.Text); 24 | case SyntaxTokenKind.IntLiteral: 25 | return new LiteralExpression(token, int.Parse(token.Text)); 26 | case SyntaxTokenKind.FloatLiteral: 27 | return new LiteralExpression(token, float.Parse(token.Text)); 28 | case SyntaxTokenKind.DoubleLiteral: 29 | return new LiteralExpression(token, double.Parse(token.Text)); 30 | default: 31 | Parser.Revert(); 32 | 33 | return null; 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/Parsers/SyntaxParserExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Parser.Parsers 2 | { 3 | public class SyntaxParserExtension 4 | { 5 | protected SyntaxParser Parser { get; } 6 | 7 | public SyntaxParserExtension(SyntaxParser parser) 8 | { 9 | Parser = parser; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/Parsers/VariableDeclarationParser.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | using Dust.Compiler.Parser.SyntaxTree; 3 | 4 | namespace Dust.Compiler.Parser.Parsers 5 | { 6 | public class VariableDeclarationParser : SyntaxParserExtension 7 | { 8 | public VariableDeclarationParser(SyntaxParser parser) 9 | : base(parser) 10 | { 11 | } 12 | 13 | public VariableDeclaration Parse(bool isMutable) 14 | { 15 | SyntaxToken letOrMutToken = Parser.ExpectToken(isMutable ? SyntaxTokenKind.MutKeyword : SyntaxTokenKind.LetKeyword); 16 | SyntaxToken nameToken = Parser.ExpectToken(SyntaxTokenKind.Identifier); 17 | SyntaxToken typeToken = Parser.ParseOptionalType(); 18 | Expression initializer = null; 19 | 20 | if (Parser.MatchToken(SyntaxTokenKind.Equal)) 21 | { 22 | initializer = Parser.ParseExpression(); 23 | } 24 | 25 | return new VariableDeclaration(letOrMutToken, nameToken, isMutable, typeToken, initializer); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxParseResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Dust.Compiler.Diagnostics; 3 | using Dust.Compiler.Parser.SyntaxTree; 4 | 5 | namespace Dust.Compiler.Parser 6 | { 7 | public class SyntaxParseResult 8 | { 9 | public CodeBlockNode Node { get; } 10 | public List Diagnostics { get; } 11 | 12 | public SyntaxParseResult(CodeBlockNode node, List diagnostics) 13 | { 14 | Node = node; 15 | Diagnostics = diagnostics; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Dust.Compiler.Diagnostics; 3 | using Dust.Compiler.Lexer; 4 | using Dust.Compiler.Parser.Parsers; 5 | using Dust.Compiler.Parser.SyntaxTree; 6 | 7 | namespace Dust.Compiler.Parser 8 | { 9 | public class SyntaxParser 10 | { 11 | private readonly List diagnostics = new List(); 12 | 13 | public List tokens; 14 | private int position; 15 | 16 | public SyntaxToken CurrentToken => position >= tokens.Count ? SyntaxToken.Invalid : tokens[position]; 17 | 18 | private FunctionDeclarationParser functionDeclarationParser; 19 | private VariableDeclarationParser variableDeclarationParser; 20 | private LiteralParser literalParser; 21 | private BinaryExpressionParser binaryExpressionParser; 22 | 23 | public SyntaxParseResult Parse(List tokens) 24 | { 25 | this.tokens = tokens; 26 | position = 0; 27 | 28 | diagnostics.Clear(); 29 | 30 | if (tokens.Count == 0) 31 | { 32 | return null; 33 | } 34 | 35 | functionDeclarationParser = new FunctionDeclarationParser(this); 36 | variableDeclarationParser = new VariableDeclarationParser(this); 37 | literalParser = new LiteralParser(this); 38 | binaryExpressionParser = new BinaryExpressionParser(this); 39 | 40 | CodeBlockNode module = new CodeBlockNode(); 41 | 42 | while (CurrentToken.Isnt(SyntaxTokenKind.EndOfFile)) 43 | { 44 | if (CurrentToken.Is(SyntaxTokenKind.Invalid)) 45 | { 46 | Advance(); 47 | 48 | continue; 49 | } 50 | 51 | Statement declaration = ParseDeclaration(); 52 | 53 | if (declaration != null) 54 | { 55 | module.Children.Add(declaration); 56 | } 57 | else 58 | { 59 | Advance(); 60 | } 61 | } 62 | 63 | return new SyntaxParseResult(module, diagnostics); 64 | } 65 | 66 | private Statement ParseDeclaration() 67 | { 68 | if (CurrentToken.Is(SyntaxTokenKind.FnKeyword)) 69 | { 70 | return functionDeclarationParser.Parse(); 71 | } 72 | 73 | if (CurrentToken.IsOr(SyntaxTokenKind.LetKeyword)) 74 | { 75 | return variableDeclarationParser.Parse(false); 76 | } 77 | 78 | if (CurrentToken.IsOr(SyntaxTokenKind.MutKeyword)) 79 | { 80 | return variableDeclarationParser.Parse(true); 81 | } 82 | 83 | Statement statement = ParseStatement(); 84 | 85 | if (statement == null) 86 | { 87 | Error(Errors.UnexpectedTokenGlobal(CurrentToken)); 88 | 89 | return null; 90 | } 91 | 92 | return statement; 93 | } 94 | 95 | public Statement ParseStatement() 96 | { 97 | Expression expression = ParseExpression(); 98 | 99 | if (expression != null) 100 | { 101 | return new ExpressionStatement(expression); 102 | } 103 | 104 | return null; 105 | } 106 | 107 | public Expression ParseExpression() 108 | { 109 | Expression expression = literalParser.TryParse(); 110 | 111 | /*if (SyntaxFacts.IsUnaryOperator(CurrentToken)) 112 | { 113 | expression = unaryExpressionParser.Parse(); 114 | }*/ 115 | 116 | if (SyntaxFacts.IsBinaryOperator(CurrentToken)) 117 | { 118 | return binaryExpressionParser.Parse(expression); 119 | } 120 | 121 | return expression; 122 | } 123 | 124 | public void Error(Error error) 125 | { 126 | diagnostics.Add(error); 127 | } 128 | 129 | public bool MatchToken(SyntaxTokenKind kind, int offset = 0) 130 | { 131 | if (IsAtEnd() || CurrentToken.Is(SyntaxTokenKind.EndOfFile)) 132 | { 133 | return false; 134 | } 135 | 136 | if (position + offset >= tokens.Count || position + offset < 0) 137 | { 138 | return false; 139 | } 140 | 141 | if (CurrentToken.Is(kind)) 142 | { 143 | Advance(); 144 | 145 | return true; 146 | } 147 | 148 | return false; 149 | } 150 | 151 | public SyntaxToken ExpectToken(SyntaxTokenKind kind, int offset = 0) 152 | { 153 | bool match = MatchToken(kind, offset); 154 | 155 | if (match == false) 156 | { 157 | Error(Errors.UnexpectedToken(CurrentToken, kind)); 158 | 159 | return SyntaxToken.Invalid; 160 | } 161 | 162 | return PeekBack(); 163 | } 164 | 165 | public SyntaxToken Advance() 166 | { 167 | position++; 168 | 169 | return tokens[position - 1]; 170 | } 171 | 172 | public SyntaxToken Peek() 173 | { 174 | return IsAtEnd() ? null : tokens[position + 1]; 175 | } 176 | 177 | public SyntaxToken PeekBack(int offset = 1) 178 | { 179 | if (position == 0) 180 | { 181 | return SyntaxToken.Invalid; 182 | } 183 | 184 | return tokens[position - offset]; 185 | } 186 | 187 | public void Revert() 188 | { 189 | position--; 190 | } 191 | 192 | public bool IsAtEnd() 193 | { 194 | return CurrentToken.Is(SyntaxTokenKind.EndOfFile); 195 | } 196 | 197 | public SyntaxToken ParseOptionalType() 198 | { 199 | if (MatchToken(SyntaxTokenKind.Colon)) 200 | { 201 | return Advance(); 202 | } 203 | 204 | return null; 205 | } 206 | } 207 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxTree/CodeBlockNode.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Dust.Compiler.Lexer; 3 | 4 | namespace Dust.Compiler.Parser.SyntaxTree 5 | { 6 | public sealed class CodeBlockNode : SyntaxNode 7 | { 8 | public SyntaxToken OpeningBrace { get; } 9 | public SyntaxToken ClosingBrace { get; set; } 10 | 11 | public List Children { get; } 12 | 13 | public CodeBlockNode(SyntaxToken openingBrace = null, SourceRange range = null) 14 | { 15 | OpeningBrace = openingBrace; 16 | Children = new List(); 17 | Range = range; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxTree/Expression.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Parser.SyntaxTree 2 | { 3 | public abstract class Expression : SyntaxNode 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxTree/Expressions/BinaryExpression.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | 3 | namespace Dust.Compiler.Parser.SyntaxTree.Expressions 4 | { 5 | public class BinaryExpression : Expression 6 | { 7 | public Expression Left { get; } 8 | public SyntaxToken OperatorToken { get; } 9 | public Expression Right { get; } 10 | 11 | public BinaryExpression(Expression left, SyntaxToken operatorToken, Expression right) 12 | { 13 | Left = left; 14 | OperatorToken = operatorToken; 15 | Right = right; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxTree/Expressions/LiteralExpression.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | 3 | namespace Dust.Compiler.Parser.SyntaxTree.Expressions 4 | { 5 | public class LiteralExpression : Expression 6 | { 7 | public SyntaxToken Token { get; } 8 | public object Value { get; } 9 | 10 | public LiteralExpression(SyntaxToken token, object value) 11 | { 12 | Token = token; 13 | Value = value; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxTree/Statement.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Parser.SyntaxTree 2 | { 3 | public abstract class Statement : SyntaxNode 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxTree/Statements/ExpressionStatement.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Parser.SyntaxTree 2 | { 3 | public class ExpressionStatement : Statement 4 | { 5 | public Expression Expression { get; } 6 | 7 | public ExpressionStatement(Expression expression) 8 | { 9 | Expression = expression; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxTree/Statements/FunctionDeclaration.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Dust.Compiler.Lexer; 3 | 4 | namespace Dust.Compiler.Parser.SyntaxTree 5 | { 6 | public class FunctionDeclaration : Statement 7 | { 8 | public SyntaxToken FnToken { get; } 9 | public SyntaxToken NameToken { get; } 10 | public List Parameters { get; } 11 | public SyntaxToken ClosingParenthesis { get; } 12 | public SyntaxToken ReturnTypeToken { get; } 13 | public CodeBlockNode Body { get; } 14 | 15 | public FunctionDeclaration(SyntaxToken fnToken, SyntaxToken nameToken, List parameters, SyntaxToken closingParenthesis, CodeBlockNode body, SyntaxToken returnTypeToken) 16 | { 17 | FnToken = fnToken; 18 | NameToken = nameToken; 19 | Parameters = parameters; 20 | ClosingParenthesis = closingParenthesis; 21 | Body = body; 22 | ReturnTypeToken = returnTypeToken; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxTree/Statements/VariableDeclaration.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | 3 | namespace Dust.Compiler.Parser.SyntaxTree 4 | { 5 | public class VariableDeclaration : Statement 6 | { 7 | public SyntaxToken LetOrMutToken { get; } 8 | public SyntaxToken NameToken { get; } 9 | public bool IsMutable { get; } 10 | public SyntaxToken TypeToken { get; } 11 | public Expression Initializer { get; } 12 | 13 | public VariableDeclaration(SyntaxToken letOrMutToken, SyntaxToken nameToken, bool isMutable, SyntaxToken typeToken, Expression initializer) 14 | { 15 | LetOrMutToken = letOrMutToken; 16 | NameToken = nameToken; 17 | IsMutable = isMutable; 18 | TypeToken = typeToken; 19 | Initializer = initializer; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Dust/Compiler/Parser/SyntaxTree/SyntaxNode.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace Dust.Compiler.Parser.SyntaxTree 6 | { 7 | public abstract class SyntaxNode 8 | { 9 | public virtual SourceRange Range 10 | { 11 | get 12 | { 13 | if (range != null) 14 | { 15 | return range; 16 | } 17 | 18 | SyntaxNode[] children = GetChildren().ToArray(); 19 | 20 | range = new SourceRange(children.First().Range.Start, children.Last().Range.End); 21 | 22 | return range; 23 | } 24 | protected set => range = value; 25 | } 26 | 27 | private SourceRange range; 28 | 29 | public virtual IEnumerable GetChildren() 30 | { 31 | PropertyInfo[] properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); 32 | 33 | foreach (PropertyInfo property in properties) 34 | { 35 | if (typeof(SyntaxNode).IsAssignableFrom(property.PropertyType)) 36 | { 37 | SyntaxNode child = (SyntaxNode) property.GetValue(this); 38 | 39 | if (child != null) 40 | { 41 | yield return child; 42 | } 43 | } 44 | else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType)) 45 | { 46 | foreach (SyntaxNode child in (IEnumerable) property.GetValue(this)) 47 | { 48 | if (child != null) 49 | { 50 | yield return child; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Dust/Compiler/SourcePosition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dust.Compiler 4 | { 5 | public class SourcePosition : IEquatable 6 | { 7 | public string File { get; } 8 | public int Line { get; } 9 | public int Position { get; } 10 | 11 | public SourcePosition(int line, int position) 12 | { 13 | File = ""; 14 | Line = line; 15 | Position = position; 16 | } 17 | 18 | public static SourcePosition operator +(SourcePosition left, SourcePosition right) 19 | { 20 | return new SourcePosition(right.Line, left.Position + right.Position); 21 | } 22 | 23 | public static SourcePosition operator +(SourcePosition left, int right) 24 | { 25 | return new SourcePosition(left.Line, left.Position + right); 26 | } 27 | 28 | public static SourcePosition operator -(SourcePosition left, SourcePosition right) 29 | { 30 | return new SourcePosition(right.Line, left.Position - right.Position); 31 | } 32 | 33 | public static SourcePosition operator -(SourcePosition left, int right) 34 | { 35 | return new SourcePosition(left.Line, left.Position - right); 36 | } 37 | 38 | public static SourcePosition operator ++(SourcePosition position) 39 | { 40 | return new SourcePosition(position.Line, position.Position + 1); 41 | } 42 | 43 | public static SourcePosition operator --(SourcePosition position) 44 | { 45 | return new SourcePosition(position.Line, position.Position - 1); 46 | } 47 | 48 | public static bool operator ==(SourcePosition left, SourcePosition right) 49 | { 50 | if ((object) left == null && (object) right == null) 51 | { 52 | return true; 53 | } 54 | 55 | return left?.Line == right?.Line && left.Position == right.Position; 56 | } 57 | 58 | public static bool operator !=(SourcePosition left, SourcePosition right) 59 | { 60 | return left == right == false; 61 | } 62 | 63 | public override string ToString() 64 | { 65 | return $"line {Line}, column {Position}"; 66 | } 67 | 68 | public bool Equals(SourcePosition other) 69 | { 70 | if ((object) other == null) 71 | { 72 | return false; 73 | } 74 | 75 | return string.Equals(File, other.File) && Line == other.Line && Position == other.Position; 76 | } 77 | 78 | public override bool Equals(object obj) 79 | { 80 | if (ReferenceEquals(null, obj)) return false; 81 | if (ReferenceEquals(this, obj)) return true; 82 | return obj.GetType() == GetType() && Equals((SourcePosition) obj); 83 | } 84 | 85 | public override int GetHashCode() 86 | { 87 | unchecked 88 | { 89 | int hashCode = File != null ? File.GetHashCode() : 0; 90 | hashCode = (hashCode * 397) ^ Line; 91 | hashCode = (hashCode * 397) ^ Position; 92 | return hashCode; 93 | } 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /Dust/Compiler/SourceRange.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Dust.Compiler 5 | { 6 | public class SourceRange : IEquatable 7 | { 8 | public SourcePosition Start { get; } 9 | public SourcePosition End { get; } 10 | 11 | public SourceRange(SourcePosition start, SourcePosition end) 12 | { 13 | Start = start; 14 | End = end; 15 | } 16 | 17 | public static SourceRange FromText(string text, int startLine = 0, int offset = 0) 18 | { 19 | return new SourceRange(new SourcePosition(startLine, offset), new SourcePosition(text.Count((c) => c == '\n') + startLine, offset + text.Length - 1)); 20 | } 21 | 22 | public bool Equals(SourceRange other) 23 | { 24 | if (ReferenceEquals(null, other)) return false; 25 | if (ReferenceEquals(this, other)) return true; 26 | 27 | return Equals(Start, other.Start) && Equals(End, other.End); 28 | } 29 | 30 | public override string ToString() 31 | { 32 | return $"({Start} -> {End})"; 33 | } 34 | 35 | public override bool Equals(object obj) 36 | { 37 | if (ReferenceEquals(null, obj)) return false; 38 | if (ReferenceEquals(this, obj)) return true; 39 | 40 | return obj.GetType() == GetType() && Equals((SourceRange) obj); 41 | } 42 | 43 | public override int GetHashCode() 44 | { 45 | unchecked 46 | { 47 | return ((Start != null ? Start.GetHashCode() : 0) * 397) ^ (End != null ? End.GetHashCode() : 0); 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Dust/Compiler/SyntaxFacts.cs: -------------------------------------------------------------------------------- 1 | using Dust.Compiler.Lexer; 2 | using Dust.Compiler.Parser; 3 | 4 | namespace Dust.Compiler 5 | { 6 | public static class SyntaxFacts 7 | { 8 | public static bool IsValidIdentiferOrKeywordCharacter(char character) 9 | { 10 | return char.IsLetterOrDigit(character) || character == '_'; 11 | } 12 | 13 | public static bool IsNumeric(char character) 14 | { 15 | return char.IsDigit(character) || character == '.'; 16 | } 17 | 18 | public static bool IsUnaryOperator(SyntaxToken token) 19 | { 20 | return token.IsOr(SyntaxTokenKind.Plus, SyntaxTokenKind.Minus, SyntaxTokenKind.Bang, SyntaxTokenKind.PlusPlus, SyntaxTokenKind.MinusMinus, 21 | SyntaxTokenKind.AsteriskAsterisk, SyntaxTokenKind.SlashSlash); 22 | } 23 | 24 | public static bool IsBinaryOperator(SyntaxToken token) 25 | { 26 | return IsBinaryArithmeticOperator(token) || IsBinaryBooleanOperator(token) || IsAssignmentOperator(token); 27 | } 28 | 29 | public static bool IsBinaryArithmeticOperator(SyntaxToken token) 30 | { 31 | return token.IsOr(SyntaxTokenKind.Plus, SyntaxTokenKind.Minus, SyntaxTokenKind.Asterisk, SyntaxTokenKind.Slash, SyntaxTokenKind.AsteriskAsterisk, 32 | SyntaxTokenKind.Percent); 33 | } 34 | 35 | public static bool IsBinaryBooleanOperator(SyntaxToken token) 36 | { 37 | return token.IsOr(SyntaxTokenKind.EqualsEquals, SyntaxTokenKind.NotEqual, SyntaxTokenKind.AmpersandAmpersand, SyntaxTokenKind.PipePipe, 38 | SyntaxTokenKind.GreaterThan, SyntaxTokenKind.GreaterThanEqual, SyntaxTokenKind.LessThan, SyntaxTokenKind.LessThanEqual); 39 | } 40 | 41 | public static bool IsAssignmentOperator(SyntaxToken token) 42 | { 43 | return token.IsOr(SyntaxTokenKind.Equal, SyntaxTokenKind.PlusEquals, SyntaxTokenKind.MinusEquals, SyntaxTokenKind.AsteriskEquals, SyntaxTokenKind.SlashEquals, 44 | SyntaxTokenKind.AsteriskAsteriskEquals, SyntaxTokenKind.PercentEquals); 45 | } 46 | 47 | public static BinaryOperatorKind GetBinaryOperatorKind(SyntaxToken operatorToken) 48 | { 49 | return (BinaryOperatorKind) operatorToken.Kind; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Dust/Compiler/Types/Definitions/DustBool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dust.Compiler.Types 4 | { 5 | public class DustBool : DustObject 6 | { 7 | public bool Value { get; } 8 | 9 | public DustBool(bool value) 10 | : this() 11 | { 12 | Value = value; 13 | } 14 | 15 | public DustBool() 16 | : base("bool") 17 | { 18 | } 19 | 20 | public override Type ToNativeType() 21 | { 22 | return typeof(bool); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Dust/Compiler/Types/Definitions/DustDouble.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace Dust.Compiler.Types 5 | { 6 | public class DustDouble : DustNumber 7 | { 8 | public double Value { get; } 9 | public override int Rank => 3; 10 | 11 | public DustDouble(double value) 12 | : this() 13 | { 14 | Value = value; 15 | } 16 | 17 | public DustDouble() 18 | : base("double") 19 | { 20 | } 21 | 22 | public override DustObject Add(DustObject other) 23 | { 24 | return new DustDouble(Value + other.ToDouble()); 25 | } 26 | 27 | public override DustObject Subtract(DustObject other) 28 | { 29 | return new DustDouble(Value - other.ToDouble()); 30 | } 31 | 32 | public override DustObject Multiply(DustObject other) 33 | { 34 | return new DustDouble(Value * other.ToDouble()); 35 | } 36 | 37 | public override DustObject Divide(DustObject other) 38 | { 39 | return new DustDouble(Value / other.ToDouble()); 40 | } 41 | 42 | public override DustInt ToInt() 43 | { 44 | return new DustInt(Convert.ToInt32(Value)); 45 | } 46 | 47 | public override DustFloat ToFloat() 48 | { 49 | return new DustFloat(Convert.ToSingle(Value)); 50 | } 51 | 52 | public override DustDouble ToDouble() 53 | { 54 | return Value; 55 | } 56 | 57 | public override Type ToNativeType() 58 | { 59 | return typeof(double); 60 | } 61 | 62 | public override DustString ToString() 63 | { 64 | return Value.ToString(CultureInfo.InvariantCulture); 65 | } 66 | 67 | public static implicit operator DustDouble(double value) 68 | { 69 | return new DustDouble(value); 70 | } 71 | 72 | public static implicit operator double(DustDouble dustDouble) 73 | { 74 | return dustDouble.Value; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Dust/Compiler/Types/Definitions/DustFloat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace Dust.Compiler.Types 5 | { 6 | public class DustFloat : DustNumber 7 | { 8 | public float Value { get; } 9 | public override int Rank => 2; 10 | 11 | public DustFloat(float value) 12 | : base("float") 13 | { 14 | Value = value; 15 | } 16 | 17 | public DustFloat() 18 | : base("float") 19 | { 20 | } 21 | 22 | public override DustObject Add(DustObject other) 23 | { 24 | return new DustFloat(Value + other.ToFloat()); 25 | } 26 | 27 | public override DustObject Subtract(DustObject other) 28 | { 29 | return new DustFloat(Value - other.ToFloat()); 30 | } 31 | 32 | public override DustObject Multiply(DustObject other) 33 | { 34 | return new DustFloat(Value * other.ToFloat()); 35 | } 36 | 37 | public override DustObject Divide(DustObject other) 38 | { 39 | return new DustFloat(Value / other.ToFloat()); 40 | } 41 | 42 | public override DustInt ToInt() 43 | { 44 | return (int) Value; 45 | } 46 | 47 | public override DustFloat ToFloat() 48 | { 49 | return this; 50 | } 51 | 52 | public override DustDouble ToDouble() 53 | { 54 | return (double) Value; 55 | } 56 | 57 | public override Type ToNativeType() 58 | { 59 | return typeof(float); 60 | } 61 | 62 | public override DustString ToString() 63 | { 64 | return Value.ToString(CultureInfo.InvariantCulture); 65 | } 66 | 67 | public static implicit operator DustFloat(float value) 68 | { 69 | return new DustFloat(value); 70 | } 71 | 72 | public static implicit operator float(DustFloat dustFloat) 73 | { 74 | return dustFloat.Value; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Dust/Compiler/Types/Definitions/DustInt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dust.Compiler.Types 4 | { 5 | public class DustInt : DustNumber 6 | { 7 | public int Value { get; } 8 | public override int Rank => 1; 9 | 10 | public DustInt(int value) 11 | : this() 12 | { 13 | Value = value; 14 | } 15 | 16 | public DustInt() 17 | : base("int") 18 | { 19 | } 20 | 21 | public override DustObject Add(DustObject other) 22 | { 23 | return new DustInt(Value + other.ToInt()); 24 | } 25 | 26 | public override DustObject Subtract(DustObject other) 27 | { 28 | return new DustInt(Value - other.ToInt()); 29 | } 30 | 31 | public override DustObject Multiply(DustObject other) 32 | { 33 | return new DustInt(Value * other.ToInt()); 34 | } 35 | 36 | public override DustObject Divide(DustObject other) 37 | { 38 | return new DustFloat(Value / other.ToFloat()); 39 | } 40 | 41 | public override DustInt ToInt() 42 | { 43 | return this; 44 | } 45 | 46 | public override DustFloat ToFloat() 47 | { 48 | return (float) Value; 49 | } 50 | 51 | public override DustDouble ToDouble() 52 | { 53 | return (double) Value; 54 | } 55 | 56 | public override Type ToNativeType() 57 | { 58 | return typeof(int); 59 | } 60 | 61 | public override DustString ToString() 62 | { 63 | return Value.ToString(); 64 | } 65 | 66 | public static implicit operator DustInt(int value) 67 | { 68 | return new DustInt(value); 69 | } 70 | 71 | public static implicit operator int(DustInt dustInt) 72 | { 73 | return dustInt.Value; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Dust/Compiler/Types/Definitions/DustNumber.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Types 2 | { 3 | public class DustNumber : DustObject 4 | { 5 | public DustNumber() 6 | : base("number") 7 | { 8 | } 9 | 10 | protected DustNumber(string typeName) 11 | : base(typeName, DustTypes.Number) 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Dust/Compiler/Types/Definitions/DustObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dust.Compiler.Types 4 | { 5 | public class DustObject : DustType 6 | { 7 | public DustObject() 8 | : base("object") 9 | { 10 | } 11 | 12 | protected DustObject(string typeName, DustType superType = null) 13 | : base(typeName, superType ?? DustTypes.Object) 14 | { 15 | } 16 | 17 | public virtual DustObject Add(DustObject other) 18 | { 19 | return null; 20 | } 21 | 22 | public virtual DustObject Subtract(DustObject other) 23 | { 24 | return null; 25 | } 26 | 27 | public virtual DustObject Multiply(DustObject other) 28 | { 29 | return null; 30 | } 31 | 32 | public virtual DustObject Divide(DustObject other) 33 | { 34 | return null; 35 | } 36 | 37 | public virtual DustInt ToInt() 38 | { 39 | throw new Exception($"Cannot convert type {TypeName} to type int."); 40 | // return null; 41 | } 42 | 43 | public virtual DustFloat ToFloat() 44 | { 45 | throw new Exception($"Cannot convert type {TypeName} to type float."); 46 | // return null; 47 | } 48 | 49 | public virtual DustDouble ToDouble() 50 | { 51 | throw new Exception($"Cannot convert type {TypeName} to type double."); 52 | // return null; 53 | } 54 | 55 | public virtual DustString ToString() 56 | { 57 | throw new Exception($"Cannot convert type {TypeName} to type string."); 58 | // return null; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Dust/Compiler/Types/Definitions/DustString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Dust.Compiler.Types 5 | { 6 | public class DustString : DustObject 7 | { 8 | public string Value { get; } 9 | 10 | public DustString(string value) 11 | : this() 12 | { 13 | Value = value; 14 | } 15 | 16 | public DustString() 17 | : base("string") 18 | { 19 | } 20 | 21 | public override DustObject Add(DustObject other) 22 | { 23 | return new DustString(Value + other.ToString()); 24 | } 25 | 26 | public override DustObject Subtract(DustObject other) 27 | { 28 | if (other is DustString) 29 | { 30 | return new DustString(Value.Replace(other.ToString(), "")); 31 | } 32 | 33 | return base.Subtract(other); 34 | } 35 | 36 | public override DustObject Multiply(DustObject other) 37 | { 38 | if (other is DustInt) 39 | { 40 | return new DustString(string.Concat(Enumerable.Repeat(Value, other.ToInt()))); 41 | } 42 | 43 | return base.Multiply(other); 44 | } 45 | 46 | public override DustObject Divide(DustObject other) 47 | { 48 | if (other is DustInt) 49 | { 50 | throw new NotImplementedException("Arrays are not implemented"); 51 | } 52 | 53 | if (other is DustString) 54 | { 55 | throw new NotImplementedException("Arrays are not implemented"); 56 | } 57 | 58 | return base.Divide(other); 59 | } 60 | 61 | public override DustString ToString() 62 | { 63 | return Value; 64 | } 65 | 66 | 67 | public static implicit operator DustString(string value) 68 | { 69 | return new DustString(value); 70 | } 71 | 72 | public static implicit operator string(DustString dustString) 73 | { 74 | return dustString.Value; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Dust/Compiler/Types/Definitions/DustVoid.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Compiler.Types 2 | { 3 | public class DustVoid : DustType 4 | { 5 | public DustVoid() 6 | : base("void") 7 | { 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /Dust/Compiler/Types/DustType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dust.Compiler.Types 4 | { 5 | public class DustType 6 | { 7 | public string TypeName { get; } 8 | public DustType SuperType { get; } 9 | public virtual int Rank => 0; 10 | 11 | protected DustType(string typeName, DustType superType = null) 12 | { 13 | TypeName = typeName; 14 | SuperType = superType; 15 | } 16 | 17 | public virtual Type ToNativeType() 18 | { 19 | return null; 20 | } 21 | 22 | public bool IsAssignableFrom(DustType type) 23 | { 24 | // TODO: Support multiple levels of inheritance 25 | return this == type || this == type.SuperType; 26 | } 27 | 28 | public static bool operator ==(DustType left, DustType right) 29 | { 30 | if ((object) left == null && (object) right == null) 31 | { 32 | return true; 33 | } 34 | 35 | return left?.Equals(right) ?? false; 36 | } 37 | 38 | public static bool operator !=(DustType left, DustType right) 39 | { 40 | return !(left == right); 41 | } 42 | 43 | public bool Equals(DustType other) 44 | { 45 | return TypeName == other.TypeName && SuperType == other.SuperType; 46 | } 47 | 48 | public override bool Equals(object obj) 49 | { 50 | if (ReferenceEquals(null, obj)) return false; 51 | if (ReferenceEquals(this, obj)) return true; 52 | if (obj.GetType() != GetType()) return false; 53 | 54 | return Equals((DustType) obj); 55 | } 56 | 57 | public override int GetHashCode() 58 | { 59 | unchecked 60 | { 61 | int hashCode = (TypeName != null ? TypeName.GetHashCode() : 0); 62 | hashCode = (hashCode * 397) ^ (SuperType != null ? SuperType.GetHashCode() : 0); 63 | hashCode = (hashCode * 397) ^ Rank; 64 | 65 | return hashCode; 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /Dust/Compiler/Types/DustTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dust.Compiler.Types 4 | { 5 | public static class DustTypes 6 | { 7 | public static DustType Void => new DustVoid(); 8 | public static DustType Object => new DustObject(); 9 | public static DustType Int => new DustInt(); 10 | public static DustType Float => new DustFloat(); 11 | public static DustType Double => new DustDouble(); 12 | public static DustType Number => new DustNumber(); 13 | public static DustType String => new DustString(); 14 | public static DustType Bool => new DustBool(); 15 | 16 | public static DustType TypeOf(object value) 17 | { 18 | if (value is int) 19 | { 20 | return Int; 21 | } 22 | 23 | if (value is float) 24 | { 25 | return Float; 26 | } 27 | 28 | if (value is double) 29 | { 30 | return Double; 31 | } 32 | 33 | if (value is bool) 34 | { 35 | return Bool; 36 | } 37 | 38 | throw new Exception($"{nameof(TypeOf)} not implemented for `{value.GetType()}`"); 39 | } 40 | 41 | public static DustType BestTypeFor(DustType type1, DustType type2) 42 | { 43 | return type1.Rank > type2.Rank ? type1 : type2; 44 | } 45 | 46 | public static DustObject FromNative(object value) 47 | { 48 | if (value is int intValue) 49 | { 50 | return new DustInt(intValue); 51 | } 52 | 53 | if (value is float floatValue) 54 | { 55 | return new DustFloat(floatValue); 56 | } 57 | 58 | if (value is double doubleValue) 59 | { 60 | return new DustDouble(doubleValue); 61 | } 62 | 63 | if (value is bool boolValue) 64 | { 65 | return new DustBool(boolValue); 66 | } 67 | 68 | if (value is string stringValue) 69 | { 70 | return new DustString(stringValue); 71 | } 72 | 73 | throw new Exception($"{nameof(FromNative)} not implemented for `{value.GetType()}`"); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /Dust/Dust.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netcoreapp3.0 4 | default 5 | 6 | -------------------------------------------------------------------------------- /Dust/Extensions/IEnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Dust.Extensions 5 | { 6 | // ReSharper disable once InconsistentNaming - cannot add this to abbreviations list from some reason 7 | public static class IEnumerableExtensions 8 | { 9 | public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) 10 | where TKey : IComparable 11 | { 12 | HashSet seenKeys = new HashSet(); 13 | List elements = new List(); 14 | 15 | foreach (TSource element in source) 16 | { 17 | if (seenKeys.Add(keySelector(element))) 18 | { 19 | elements.Add(element); 20 | } 21 | else 22 | { 23 | elements.RemoveAll((e) => keySelector(e).Equals(keySelector(element))); 24 | } 25 | } 26 | 27 | return elements; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Dust/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Dust.Extensions 2 | { 3 | public static class StringExtension 4 | { 5 | public static string SubstringRange(this string value, int start, int end) 6 | { 7 | return value.Substring(start, end - start); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /Dust/Extensions/TypeExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dust.Extensions 4 | { 5 | public static class TypeExtension 6 | { 7 | public static bool Extends(this Type type, Type other) 8 | { 9 | return other.IsAssignableFrom(type) && type != other; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 DustLanguage 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dust [![Build Status](https://travis-ci.org/DustLanguage/Dust.svg?branch=master)](https://travis-ci.org/DustLanguage/Dust) [![Gitter room](https://badges.gitter.im/DustLanguage.png)](https://gitter.im/DustLanguage) 2 | 3 | Dust is an open source programming language written in C# and .NET Core. 4 | 5 | ## Introduction 6 | 7 | Dust is a simple and easy-to-understand programming language inspired by 8 | many other programming languages such as C#, Go, Rust, Swift, Elixir, F#, Dart and so on. 9 | 10 | If you want to take a more in-depth look into the development process, check out our [Trello board](https://trello.com/b/viwlhPnj). 11 | 12 | Contributions are more than welcome even if you don't have any experience 13 | in programming language design, we're all here to learn. To learn more, 14 | see [Contributing](#contributing). 15 | 16 | If you have any suggestions, questions regarding contributing, syntax or anything 17 | else, please ask away in our [Gitter room](https://gitter.im/DustLanguage). 18 | 19 | ## Syntax 20 | 21 | Planned syntax for a console hello world: 22 | ```c 23 | let fn main() { 24 | println("Hello World") // Print text and add a new line. 25 | println("from Dust!") 26 | } 27 | ``` 28 | Properties (variables): 29 | ```c 30 | let immutable = 0 + 1 + 2 + 3 // Immutable property with initializer 31 | let mut mutable = 3 + 2 + 1 + 0 // Mutable property with initializer 32 | ``` 33 | Functions: 34 | ```c 35 | let fn aFunction(mut mutableParam, immutableParam) { // A function with a mutable and immutable parameter. Returns their sum 36 | mutableParam = 12 // Mutate the mutable parameter 37 | 38 | let result = mutableParam + immutableParam // Add them and store the sum into 'result' property 39 | 40 | return result // Return the result (sum) 41 | } 42 | ``` 43 | 44 | More examples will be added soon but for now you can take a look at [this]( 45 | https://pastebin.com/hhiV7wc7) obsolete syntax mock just to get an overview 46 | of other planned features and concepts. 47 | 48 | ## Contributing 49 | 50 | As I said above contributions are more than welcome even if you don't have 51 | any experience in programming language design. If you're willing to contribute, take a look at [these](https://github.com/DustLanguage/Dust/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) issues. 52 | 53 | 54 | ## Copyright 55 | This project is licensed under MIT license. 56 | --------------------------------------------------------------------------------