├── .gitattributes
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .gitmodules
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── assets
├── Icon.png
└── IconShort.png
├── makefile
└── src
├── Hades.Common
├── Datatype.cs
├── Extensions
│ ├── AssemblyExtensions.cs
│ ├── IEnumerableExtensions.cs
│ └── StringExtensions.cs
├── Hades.Common.csproj
├── Source
│ ├── SourceCode.cs
│ ├── SourceLocation.cs
│ └── Span.cs
└── Util
│ └── ConsoleExtensions.cs
├── Hades.Core
├── Hades.Core.csproj
├── Program.cs
└── Tools
│ ├── Hermes.cs
│ └── ProjectInitializer.cs
├── Hades.Error
├── ErrorStrings.cs
└── Hades.Error.csproj
├── Hades.Language
├── Hades.Language.csproj
├── Lexer
│ ├── Keyword.cs
│ ├── Lexer.cs
│ └── LexerGrammar.ebnf
└── Parser
│ ├── Parser.cs
│ └── ParserGrammar.ebnf
├── Hades.Runtime
├── Hades.Runtime.csproj
├── HadesRuntime.cs
├── Objects
│ └── BuiltIns.cs
├── Scope.cs
├── ScopeValue.cs
└── Values
│ ├── BoolValue.cs
│ ├── DecValue.cs
│ ├── IntValue.cs
│ ├── ListValue.cs
│ ├── LiteralValue.cs
│ └── StringValue.cs
├── Hades.Syntax
├── Expression
│ ├── AccessModifier.cs
│ ├── BlockNode.cs
│ ├── Classifier.cs
│ ├── LiteralNode.cs
│ ├── Node.cs
│ ├── Nodes
│ │ ├── ArrayAccessNode.cs
│ │ ├── AssignmentNode.cs
│ │ ├── BlockNodes
│ │ │ ├── ClassNode.cs
│ │ │ ├── ForNode.cs
│ │ │ ├── FunctionNode.cs
│ │ │ ├── GenericBlockNode.cs
│ │ │ ├── IfNode.cs
│ │ │ ├── LambdaNode.cs
│ │ │ ├── MatchNode.cs
│ │ │ ├── StructNode.cs
│ │ │ ├── TryCatchElseNode.cs
│ │ │ ├── Util
│ │ │ │ └── ParameterWriter.cs
│ │ │ ├── VarBlockNode.cs
│ │ │ └── WhileNode.cs
│ │ ├── CallNode.cs
│ │ ├── InlineIf.cs
│ │ ├── LiteralNodes
│ │ │ ├── ArgsNode.cs
│ │ │ ├── BoolLiteralNode.cs
│ │ │ ├── CommandNode.cs
│ │ │ ├── DecLiteralNode.cs
│ │ │ ├── IdentifierNode.cs
│ │ │ ├── IntLiteralNode.cs
│ │ │ ├── ListNode.cs
│ │ │ ├── MultiDimensionalArrayNode.cs
│ │ │ ├── NullValueNode.cs
│ │ │ ├── OperationNodeNode.cs
│ │ │ ├── PlaceHolderNode.cs
│ │ │ └── StringLiteralNode.cs
│ │ ├── NoVariableNode.cs
│ │ ├── NullConditionNode.cs
│ │ ├── OperationNode.cs
│ │ ├── ParenthesesNode.cs
│ │ ├── PipelineNode.cs
│ │ ├── PutNode.cs
│ │ ├── RaiseNode.cs
│ │ ├── SideNode.cs
│ │ ├── ValueCallNode.cs
│ │ ├── VariableDeclarationNode.cs
│ │ └── WithNode.cs
│ └── RootNode.cs
├── Hades.Syntax.csproj
└── Lexeme
│ ├── Category.cs
│ ├── Classifier.cs
│ └── Token.cs
├── Hades.Testing
├── Hades.Testing.csproj
├── LexerTest.cs
└── ParserTest.cs
└── Hades.sln
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
262 | _site/api/
263 |
264 | _site/
265 |
266 | api/
267 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 |
2 | [submodule "docs"]
3 | path = docs
4 | url = https://github.com/Azer0s/HadesDoc.git
5 | branch = master
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: csharp
2 | mono: none
3 | dotnet: 2.1
4 | script:
5 | - make interpreter
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 5/16/19
2 |
3 | ### Introduced extension methods
4 |
5 | This allows for cool things like behavior based programming where one could have a bunch of group classes ("interfaces"), extend functions based on group and import these extensions based on usage.
6 |
7 | ```swift
8 | class IDomainObject
9 | end
10 |
11 | class Person < IDomainObject
12 | @public
13 | var string firstname
14 | var string lastname
15 | var object birthday
16 | end
17 | end
18 |
19 | [..]
20 |
21 | func extends object::IDomainObject persist(object::IConnection connection)
22 | [..]
23 | end
24 |
25 | [..]
26 |
27 | func extends object::IDomainObject toJson()
28 | put [..] //Default impl. of index on object returns all the members
29 | end
30 | ```
31 |
32 | So based on need, one could import `persist` or `toJson` as extensions. In the controller, for instance, `persist` is absolutely useless, so is `toJson` in a service.
33 |
34 | ## 5/5/19
35 |
36 | ### Fixed call on return #3dca537b60026e55dd33233da8ef11039da6980d
37 |
38 | ```js
39 | {x => x}(19)
40 | ({x => x}(19))(1)
41 | (({x => x}(19))(1))(19)
42 |
43 | test()()
44 | (test())()
45 | ((test())())()
46 | ```
47 |
48 | ## 5/4/19
49 |
50 | ### Added try-catch-else block #5d514e61ea4189601e8ca8bfbc99e8916394c9a9
51 |
52 | ```js
53 | try
54 | connection->open()
55 | console->out("Connection open!")
56 | connection->close()
57 | catch(object::SqlException e)
58 | console->out("SqlException was caught!")
59 | catch(e)
60 | console->out("An unknown exception was caught!")
61 | end
62 | ```
63 |
64 | ```js
65 | try
66 | connection->open()
67 | console->out("Connection open!")
68 | connection->close()
69 | catch(object::SqlException e)
70 | console->out("SqlException was caught!")
71 | catch(e)
72 | console->out("An unknown exception was caught!")
73 | else
74 | console->out("No exception thrown!")
75 | end
76 | ```
77 |
78 | ```js
79 | try
80 | connection->open()
81 | console->out("Connection open!")
82 | connection->close()
83 | else //Exception is not handled; if there was no exception, else block is called
84 | console->out("No exception thrown!")
85 | end
86 | ```
87 |
88 | ### Added if block, code beautification #8b3d4d6b35167b37a0efdb96e9ad2fc964719e1e
89 |
90 | ```js
91 | if(a < 10)
92 | console->out("a is smaller than 10")
93 | else if(a is 11)
94 | console->out("a is 11")
95 | else if(a > 11 and a < 21)
96 | console->out("a is greater than 11 and smaller than 21")
97 | else
98 | console->out("a is " + a)
99 | end
100 | ```
101 |
102 | ```javascript
103 | if(a < 10)
104 | console->out("a is smaller than 10")
105 | else
106 | console->out("a is " + a)
107 | end
108 | ```
109 |
110 | ```javascript
111 | if(a < 10)
112 | console->out("a is smaller than 10")
113 | end
114 | ```
115 |
116 | ```js
117 | if(a < 10)
118 | console->out("a is smaller than 10")
119 | else if(a is 11)
120 | console->out("a is 11")
121 | end
122 | ```
123 |
124 | ## 5/3/19
125 |
126 | ### Added fixed prefix #5aca4880462c36dc87643e614bd7e38602f7e334
127 |
128 | * Added the fixed prefix in front of func
129 |
130 | ### Added pipelines #f3129f23a2f7c7103a3dc8a89b19f846a84585a5
131 |
132 | This:
133 |
134 | ```js
135 | fruits
136 | |> map({x => x->toLower()}, ??)
137 | |> filter({x => x->startsWith("a")}, ??)
138 | |> forEach({x => console->out(x)}, ??)
139 | ```
140 |
141 | is now a thing.
142 |
143 | ## 4/26/19
144 |
145 | ### Removed multidimensional array access #b2a09b8f55e2024277e7fbd0ad9331f20d5f013a
146 |
147 | * We moved from `.` to `,` when accessing multidimensional array
148 |
149 | ```js
150 | var int[3,3] matrix = {{1,0,0},{0,1,0},{0,0,1}}
151 | var int?[2,2,2] 3dArray = {{{1,2},{3,null}},{{null,6},{7,8}}}
152 | ```
153 |
154 | ### Added specific object and proto validation to lambdas, function and variables #6623878e8ccec87a20c395cdd157931dea2b403b
155 |
156 | * Variable instantiations, function arguments and lambda arguments can now name specific class-/proto names
157 |
158 | ```swift
159 | func doStuff(args object::IClient a)
160 | a->stuff("Test")
161 | end
162 | ```
163 |
164 | ```swift
165 | var x = {args object::IClient a, int b =>
166 | a->moreStuff(b)
167 | }
168 | ```
169 |
170 | ```js
171 | var proto::console c
172 | ```
173 |
174 | ## 4/23/19
175 |
176 | ### Added lambda parameter types #c244d04d419cb163ce7e5581990df1dd39c44190
177 |
178 | * Lambdas can now have parameter types
179 |
180 | ```js
181 | var add = {x, y => x + y}
182 | ```
183 |
184 | ```js
185 | var mul = {int x, int y => x * y}
186 | ```
187 |
188 | ```js
189 | var sum = { args int vals =>
190 | var result = 0
191 |
192 | for(var i in a)
193 | result += i
194 | end
195 |
196 | put result
197 | }
198 | ```
199 |
200 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016-present Ariel Simulevski
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 |
2 |
3 | ## [WIP] Hades Programming Language
4 |
5 | [](https://travis-ci.org/Azer0s/HadesLang)
6 | [](https://github.com/Azer0s/HadesLang/blob/master/LICENSE)
7 |
8 | ## Welcome!
9 |
10 | This is the official repository for the reference implementation of the Hades Programming Language (standard library & interpreter).
11 |
12 | ### Hello world
13 | ```js
14 | with console from std:io
15 | console.out("Hello world")
16 | ```
17 |
18 | ### Pipelines
19 | ```js
20 | with list fixed from std:collections
21 | with console from std:io
22 |
23 | var fruits = list.of({"Apple", "Banana", "Mango", "Kiwi", "Avocado"})
24 |
25 | fruits
26 | |> map(??, {x => x.toLower()})
27 | |> filter({x => x.startsWith("a")})
28 | //if ?? is not in the parameters, the method is inserted as the first parameter
29 | |> forEach(??, {x => console.out(x)})
30 | ```
31 |
32 | ### Function guards
33 | ```swift
34 | with console from std:io
35 |
36 | func myFunction(a int) requires a < 10
37 | console.out("a is smaller than 10")
38 | end
39 |
40 | func myFunction(a int) requires a > 10
41 | console.out("a is greater than 10")
42 | end
43 |
44 | myFunction(5) // a is smaller than 10
45 | myFunction(17) // a is greater than 10
46 | ```
47 |
48 | ### Actors
49 | ```swift
50 | with msg from std:io
51 | with sleep from std:time
52 |
53 | func ping()
54 | receive(m)
55 | {:ping, data} => {_ =>
56 | sleep.seconds(1)
57 | send(data, :pong)
58 | }
59 | end
60 | ping()
61 | end
62 |
63 | func pong()
64 | receive(m)
65 | {:pong, data} => {_ =>
66 | sleep.seconds(1)
67 | send(data, :ping)
68 | }
69 | end
70 | pong()
71 | end
72 |
73 | var pingPid = spawn({_ => ping()}
74 | var pongPid = spawn({_ => pong()}
75 |
76 | send(pingPid, {:ping, pongPid})
77 | ```
78 |
79 | ### Fibonacci sequence
80 | ```js
81 | with console from std:io
82 |
83 | func fib(n)
84 | if((n is 0) or (n is 1))
85 | put n
86 | end
87 |
88 | put fib(n-1) + fib(n-2)
89 | end
90 |
91 | fib(10) |> console.out
92 | ```
93 |
94 | ### Optional static typing
95 | ```js
96 | with console from std:io
97 |
98 | func fib(n int64) -> int64
99 | if((n is 0) or (n is 1))
100 | put n
101 | end
102 |
103 | put fib(n-1) + fib(n-2)
104 | end
105 |
106 | fib(10) |> console.out
107 | ```
108 |
109 | ## Getting Started
110 |
111 | Learning Hades and writing your first programs.
112 |
113 | ### [Installing Hades](https://hadeslang.gitbook.io/doc/getting-started/installing-hades)
114 |
115 | Instructions for downloading HadesLang and/or embedding it into your programs.
116 |
117 | ### [Basic Syntax](https://hadeslang.gitbook.io/doc/getting-started/basic-syntax)
118 |
119 | Hades basics and quick introduction into the language.
120 |
121 | ### [Coding Conventions](https://hadeslang.gitbook.io/doc/getting-started/coding-conventions)
122 |
123 | Current coding style for HadesLang.
124 |
125 | ## References
126 |
127 | ### [Language Spec](https://hadeslang.gitbook.io/doc/language-spec)
128 |
129 | The official HadesLang specification.
130 |
131 | ### [Package Documentation](https://hadeslang.gitbook.io/doc/core-libraries/standard-library)
132 |
133 | Documentation and definition of the Hades standard library.
134 |
135 | ### [Tool Documentation](https://hadeslang.gitbook.io/doc/other/tools)
136 |
137 | Documentation for HadesLang tools.
138 |
139 | ### [Examples](https://hadeslang.gitbook.io/doc/other/examples)
140 |
141 | Examples of Hades in use.
142 |
--------------------------------------------------------------------------------
/assets/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azer0s/HadesLang/9bf2c11898b4369370b096d33bf9a22badcb7346/assets/Icon.png
--------------------------------------------------------------------------------
/assets/IconShort.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azer0s/HadesLang/9bf2c11898b4369370b096d33bf9a22badcb7346/assets/IconShort.png
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | all:
2 | @$(MAKE) interpreter
3 |
4 | interpreter:
5 | @echo
6 | @echo "\033[4m\033[1mBuilding HadesLang\033[0m"
7 | @echo
8 | @dotnet build src/Hades.Core/Hades.Core.csproj
9 |
10 | run:
11 | @echo
12 | @echo "\033[4m\033[1mRunning HadesLang\033[0m"
13 | @echo
14 | @dotnet run --project src/Hades.Core/Hades.Core.csproj
15 |
--------------------------------------------------------------------------------
/src/Hades.Common/Datatype.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Common
2 | {
3 | public enum Datatype
4 | {
5 | NONE = 0,
6 | BOOL,
7 | STRING,
8 | INT,
9 | DEC,
10 | OBJECT,
11 | PROTO,
12 | STRUCT,
13 | LAMBDA
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Hades.Common/Extensions/AssemblyExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Reflection;
4 |
5 | namespace Hades.Common.Extensions
6 | {
7 | public static class AssemblyExtensions
8 | {
9 | public static DateTime GetBuildDate(this Assembly assembly)
10 | {
11 | const string buildVersionMetadataPrefix = "+build";
12 |
13 | var attribute = assembly.GetCustomAttribute();
14 | if (attribute?.InformationalVersion == null) return default;
15 | var value = attribute.InformationalVersion;
16 | var index = value.IndexOf(buildVersionMetadataPrefix, StringComparison.Ordinal);
17 | if (index <= 0) return default;
18 | value = value.Substring(index + buildVersionMetadataPrefix.Length);
19 | return DateTime.TryParseExact(value, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var result) ? result : default;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Hades.Common/Extensions/IEnumerableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | // ReSharper disable LoopCanBeConvertedToQuery
5 | // ReSharper disable UnusedMethodReturnValue.Global
6 |
7 | namespace Hades.Common.Extensions
8 | {
9 | public static class IEnumerableExtensions
10 | {
11 | public static IEnumerable Map(this IEnumerable source, Func action)
12 | {
13 | foreach (var x in source)
14 | {
15 | yield return action(x);
16 | }
17 | }
18 |
19 | public static S Filter(this IEnumerable source, Func accumulator, S initialAccumulator)
20 | {
21 | var init = initialAccumulator;
22 | foreach (var x in source)
23 | {
24 | init = accumulator(init, x);
25 | }
26 |
27 | return init;
28 | }
29 |
30 | public static void ForEach(this IEnumerable source, Action action)
31 | {
32 | foreach (var x in source)
33 | {
34 | action(x);
35 | }
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/src/Hades.Common/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Common
2 | {
3 | public static class StringExtensions
4 | {
5 | ///
6 | /// Gets that at a given .
7 | ///
8 | /// The to search.
9 | /// The index of to return.
10 | ///
11 | /// The at the given . If is less than 0
12 | /// or greater than the length of the string, returns an ASCII NULL (\0).
13 | ///
14 | public static char CharAt(this string str, int index)
15 | {
16 | if (index > str.Length - 1 || index < 0)
17 | {
18 | return '\0';
19 | }
20 |
21 | return str[index];
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Hades.Common/Hades.Common.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 | latest
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/Hades.Common/Source/SourceCode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Hades.Common.Source
4 | {
5 | public sealed class SourceCode
6 | {
7 | private readonly Lazy _lines;
8 | private readonly string _sourceCode;
9 |
10 | public SourceCode(string sourceCode)
11 | {
12 | _sourceCode = sourceCode;
13 | _lines = new Lazy(() => _sourceCode.Split(new[] {Environment.NewLine}, StringSplitOptions.None));
14 | }
15 |
16 | public string[] Lines => _lines.Value;
17 |
18 | public char this[int index] => _sourceCode.CharAt(index);
19 | }
20 | }
--------------------------------------------------------------------------------
/src/Hades.Common/Source/SourceLocation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Hades.Common.Source
4 | {
5 | public struct SourceLocation : IEquatable
6 | {
7 | public int Column { get; }
8 |
9 | public int Index { get; }
10 |
11 | public int Line { get; }
12 |
13 | public SourceLocation(int index, int line, int column)
14 | {
15 | Index = index;
16 | Line = line;
17 | Column = column;
18 | }
19 |
20 | public static bool operator !=(SourceLocation left, SourceLocation right)
21 | {
22 | return !left.Equals(right);
23 | }
24 |
25 | public static bool operator ==(SourceLocation left, SourceLocation right)
26 | {
27 | return left.Equals(right);
28 | }
29 |
30 | public override bool Equals(object obj)
31 | {
32 | if (obj is SourceLocation location)
33 | {
34 | return Equals(location);
35 | }
36 |
37 | return base.Equals(obj);
38 | }
39 |
40 | public bool Equals(SourceLocation other)
41 | {
42 | return other.GetHashCode() == GetHashCode();
43 | }
44 |
45 | public override int GetHashCode()
46 | {
47 | return 0xB1679EE ^ Index ^ Line ^ Column;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/src/Hades.Common/Source/Span.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Hades.Common.Source
4 | {
5 | public struct Span : IEquatable
6 | {
7 | public SourceLocation End { get; }
8 |
9 | public int Length => End.Index - Start.Index;
10 |
11 | public SourceLocation Start { get; }
12 |
13 | public Span(SourceLocation start, SourceLocation end)
14 | {
15 | Start = start;
16 | End = end;
17 | }
18 |
19 | public static bool operator !=(Span left, Span right)
20 | {
21 | return !left.Equals(right);
22 | }
23 |
24 | public static bool operator ==(Span left, Span right)
25 | {
26 | return left.Equals(right);
27 | }
28 |
29 | public override bool Equals(object obj)
30 | {
31 | if (obj is Span span)
32 | {
33 | return Equals(span);
34 | }
35 |
36 | return base.Equals(obj);
37 | }
38 |
39 | public bool Equals(Span other)
40 | {
41 | return other.Start == Start && other.End == End;
42 | }
43 |
44 | public override int GetHashCode()
45 | {
46 | return 0x509CE ^ Start.GetHashCode() ^ End.GetHashCode();
47 | }
48 |
49 | public override string ToString()
50 | {
51 | return $"{Start.Line} {Start.Column} {Length}";
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/src/Hades.Common/Util/ConsoleExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Hades.Common.Util
4 | {
5 | public static class ConsoleFunctions
6 | {
7 | public static void ClearCurrentConsoleLine()
8 | {
9 | Console.SetCursorPosition(0, Console.CursorTop - 1);
10 | var currentLineCursor = Console.CursorTop;
11 | Console.SetCursorPosition(0, Console.CursorTop);
12 | Console.Write(new string(' ', Console.WindowWidth));
13 | Console.SetCursorPosition(0, currentLineCursor);
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Hades.Core/Hades.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 |
7 |
8 |
9 | build$([System.DateTime]::Now.ToString("yyyyMMddHHmmss"))
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/Hades.Core/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using Hades.Common;
6 | using Hades.Common.Extensions;
7 | using Hades.Common.Util;
8 | using Hades.Core.Tools;
9 | using Hades.Language.Lexer;
10 | using Hades.Language.Parser;
11 | using Hades.Runtime;
12 | using Hades.Syntax.Lexeme;
13 | using Newtonsoft.Json;
14 | using Classifier = Hades.Syntax.Lexeme.Classifier;
15 |
16 | namespace Hades.Core
17 | {
18 | public static class Program
19 | {
20 | private const string VERSION = "0.0.1";
21 | private static Scope scope = new Scope();
22 |
23 | private static void HighLight(IEnumerable tks)
24 | {
25 | Console.ForegroundColor = ConsoleColor.White;
26 | var tokens = tks.ToList();
27 | for (var i = 0; i < tokens.Count; i++)
28 | {
29 | var token = tokens[i];
30 |
31 | if (token.Kind == Classifier.Identifier && char.IsUpper(token.Value[0]))
32 | {
33 | Console.ForegroundColor = ConsoleColor.Green;
34 | }
35 |
36 | if (i + 1 < tokens.Count)
37 | {
38 | if (token.Kind == Classifier.Identifier && tokens[i + 1].Kind == Classifier.LeftParenthesis)
39 | {
40 | Console.ForegroundColor = ConsoleColor.DarkYellow;
41 | }
42 | }
43 |
44 | if (token.Kind == Classifier.Identifier && token.Value == "super")
45 | {
46 | Console.ForegroundColor = ConsoleColor.DarkCyan;
47 | }
48 |
49 | if (token.Kind == Classifier.Keyword)
50 | {
51 | Console.ForegroundColor = ConsoleColor.DarkCyan;
52 | }
53 |
54 | if (token.Kind == Classifier.StringLiteral)
55 | {
56 | Console.ForegroundColor = ConsoleColor.Red;
57 | token.Value = $"\"{token.Value}\"";
58 | }
59 |
60 | if (token.Kind == Classifier.IntLiteral || token.Kind == Classifier.DecLiteral)
61 | {
62 | Console.ForegroundColor = ConsoleColor.Green;
63 | }
64 |
65 |
66 | if (i - 1 >= 0)
67 | {
68 | if (token.Kind == Classifier.Identifier && tokens[i - 1].Kind == Classifier.Tag)
69 | {
70 | Console.ForegroundColor = ConsoleColor.Red;
71 | }
72 | }
73 |
74 | if (token.Kind == Classifier.BoolLiteral)
75 | {
76 | Console.ForegroundColor = ConsoleColor.DarkYellow;
77 | }
78 |
79 | if (token.Category == Category.Operator || token.Kind == Classifier.Question)
80 | {
81 | Console.ForegroundColor = ConsoleColor.Cyan;
82 | }
83 |
84 | if (Enum.TryParse(token.Value.ToUpper(), out _) && !token.Value.All(char.IsDigit))
85 | {
86 | Console.ForegroundColor = ConsoleColor.DarkYellow;
87 | }
88 |
89 | if (token.Kind == Classifier.LeftBracket || token.Kind == Classifier.RightBracket || token.Kind == Classifier.LeftBrace || token.Kind == Classifier.RightBrace)
90 | {
91 | Console.ForegroundColor = ConsoleColor.DarkGreen;
92 | }
93 |
94 | if (token.Category == Category.Comment)
95 | {
96 | Console.ForegroundColor = ConsoleColor.DarkGray;
97 | }
98 |
99 | if (token.Value == "std")
100 | {
101 | Console.ForegroundColor = ConsoleColor.White;
102 | }
103 |
104 | Console.Write(token.Value);
105 | Console.ForegroundColor = ConsoleColor.White;
106 | }
107 | }
108 |
109 | private static void PrintStart()
110 | {
111 | Console.ForegroundColor = ConsoleColor.White;
112 | Console.WriteLine($"Hades (Hades Interactive Console). Built {Assembly.GetExecutingAssembly().GetBuildDate():M/d/yy h:mm:ss tt}");
113 | Console.ResetColor();
114 |
115 | Console.ForegroundColor = ConsoleColor.DarkGray;
116 | Console.WriteLine($"Hades version {VERSION}");
117 | Console.WriteLine($"Running {Environment.OSVersion}");
118 | Console.WriteLine("Press space to enter/exit multiline mode");
119 | }
120 |
121 | private static IEnumerable GetTokens()
122 | {
123 | var lexer = new Lexer();
124 | var line = Console.ReadLine();
125 | IEnumerable tokens;
126 |
127 | if (line == " ")
128 | {
129 | // Multiple lines
130 | var lines = new List {line};
131 | do
132 | {
133 | Console.ForegroundColor = ConsoleColor.DarkGray;
134 | Console.Write("...");
135 | Console.ForegroundColor = ConsoleColor.White;
136 | line = Console.ReadLine();
137 |
138 | ConsoleFunctions.ClearCurrentConsoleLine();
139 | Console.ForegroundColor = ConsoleColor.DarkGray;
140 | Console.Write("...");
141 | tokens = lexer.LexFile(line);
142 | HighLight(tokens);
143 | Console.WriteLine();
144 |
145 | lines.Add(line);
146 | } while (line != " ");
147 |
148 | Console.WriteLine();
149 |
150 | lines.RemoveAt(line.Length - 1);
151 | tokens = lexer.LexFile(string.Join("\n", lines));
152 | }
153 | else
154 | {
155 | // Single line
156 | ConsoleFunctions.ClearCurrentConsoleLine();
157 | Console.ForegroundColor = ConsoleColor.DarkGray;
158 | Console.Write("hd>");
159 | tokens = lexer.LexFile(line);
160 | HighLight(tokens);
161 | Console.WriteLine();
162 |
163 | tokens = lexer.LexFile(line);
164 | }
165 |
166 | return tokens;
167 | }
168 |
169 | public static int Main(string[] args)
170 | {
171 | if (args.Length != 0)
172 | {
173 | switch (args.First())
174 | {
175 | case "new":
176 | return ProjectInitializer.Run(args.Skip(1).ToList());
177 | case "package":
178 | return 0;
179 | }
180 | }
181 |
182 | PrintStart();
183 |
184 | while (true)
185 | {
186 | Console.ForegroundColor = ConsoleColor.DarkGray;
187 | Console.Write("hd>");
188 | Console.ResetColor();
189 |
190 | try
191 | {
192 | var tokens = GetTokens();
193 | var parser = new Parser(tokens, true);
194 | var root = parser.Parse();
195 | var result = HadesRuntime.Run(root, ref scope);
196 |
197 | if (root.Children.Count == 1)
198 | {
199 | if (result.Datatype != Datatype.LAMBDA || result.Datatype != Datatype.PROTO || result.Datatype != Datatype.OBJECT || result.Datatype != Datatype.STRUCT)
200 | {
201 | Console.ForegroundColor = ConsoleColor.DarkGray;
202 | Console.WriteLine($"{result.Datatype.ToString().ToLower()} :: {result.Value}");
203 | Console.ResetColor();
204 | Console.WriteLine();
205 | }
206 | }
207 | //TODO: We should probably smooth this entire process out. And also use IEnumerable (?)
208 | //Are IEnumerable even faster here?
209 | //Anyway...I'm thinking of a callback solution. I know, I know...we all hate callbacks...but I think they would be a good fit for this use-case
210 | }
211 | catch (Exception e)
212 | {
213 | Console.WriteLine(e.Message);
214 | Console.WriteLine();
215 | }
216 | }
217 | }
218 | }
219 | }
--------------------------------------------------------------------------------
/src/Hades.Core/Tools/Hermes.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Core.Tools
2 | {
3 | public class Hermes
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/src/Hades.Core/Tools/ProjectInitializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text.RegularExpressions;
6 | using LibGit2Sharp;
7 |
8 | namespace Hades.Core.Tools
9 | {
10 | public static class ProjectInitializer
11 | {
12 | private static readonly Regex simpleName = new Regex("^[^\\.]*$");
13 | private static readonly Regex orgName = new Regex("^([^\\.]*)\\.([^\\.]*)\\.([^\\.]*)$");
14 |
15 | public static int Run(List args)
16 | {
17 | var name = args.First();
18 | var dir = args[1]; //working name
19 |
20 | Repository.Init(dir); //Init git repository
21 | dir += Path.DirectorySeparatorChar;
22 |
23 | var src = dir + "src" + Path.DirectorySeparatorChar;
24 | Directory.CreateDirectory(src);
25 |
26 | //SRC folder
27 | if (simpleName.IsMatch(name))
28 | {
29 | src += name;
30 | Directory.CreateDirectory(src);
31 | }
32 | else if (orgName.IsMatch(name))
33 | {
34 | foreach (var s in orgName.Match(name).Groups.Select(a => a.Value).Skip(1).Reverse())
35 | {
36 | src += s;
37 | Directory.CreateDirectory(src);
38 | src += Path.DirectorySeparatorChar;
39 | }
40 | }
41 | else
42 | {
43 | Console.Error.WriteLine("Could not create project: name has invalid format!");
44 | return 1;
45 | }
46 |
47 | var mainHd = src + Path.DirectorySeparatorChar + "main.hd";
48 | File.WriteAllText(mainHd, "with console from std:io\nconsole->out(\"Hello\")");
49 |
50 | var projectJson = dir + Path.DirectorySeparatorChar + "project.json";
51 | File.WriteAllText(projectJson, "");
52 | //TODO: Fill project.json
53 |
54 | Directory.CreateDirectory(dir + "libs");
55 |
56 | using (var repo = new Repository(dir))
57 | {
58 | repo.Index.Add(projectJson.Substring(dir.Length + 1, projectJson.Length - dir.Length - 1));
59 | repo.Index.Add(mainHd.Substring(dir.Length, mainHd.Length - dir.Length).Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", $"{Path.DirectorySeparatorChar}"));
60 |
61 | // Create the commiters signature and commit
62 | var author = new Signature("HadesProjectInitializer", "@hpi", DateTime.Now);
63 | var committer = author;
64 |
65 | // Commit to the repository
66 | repo.Commit("Initial commit", author, committer);
67 | }
68 |
69 | return 0;
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/src/Hades.Error/ErrorStrings.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable InconsistentNaming
2 |
3 | namespace Hades.Error
4 | {
5 | public static class ErrorStrings
6 | {
7 | public const string MESSAGE_UNKNOWN_RUNTIME_EXCEPTION = "Unknown runtime exception! Please check the AST! \n{0}";
8 | public const string MESSAGE_DUPLICATE_VARIABLE_DECLARATION = "Duplicate decleration of variable {0}!";
9 |
10 | public const string MESSAGE_EXPECTED_TYPE = "Expected a type!";
11 | public const string MESSAGE_EXPECTED_ACCESS_MODIFIER = "Expected an access modifier!";
12 | public const string MESSAGE_ILLEGAL_PROTECTED_IN_STRUCT = "A struct cannot contain a protected variable!";
13 | public const string MESSAGE_UNEXPECTED_CALL_OR_INLINE_IF = "Unexpected call or inline if!";
14 | public const string MESSAGE_CANT_USE_COMPLEX_LAMBDA_IN_MATCH_BLOCK = "Can't use a complex lambda in a match block!";
15 | public const string MESSAGE_UNEXPECTED_NODE = "Unexpected node: {0}!";
16 | public const string MESSAGE_UNEXPECTED_ACCESS_MODIFIER = "Unexpected access modifier!";
17 | public const string MESSAGE_UNEXPECTED_STATEMENT = "Unexpected statement!";
18 | public const string MESSAGE_CANT_HAVE_MULTIPLE_VARARGS = "Can't have multiple 'args' parameters in one function!";
19 | public const string MESSAGE_EXPECTED_IN = "Expected 'in' keyword!";
20 | public const string MESSAGE_UNEXPECTED_KEYWORD = "Unexpected keyword: {0}!";
21 | public const string MESSAGE_EXPECTED_LEFT_PARENTHESIS = "Expected left parenthesis!";
22 | public const string MESSAGE_OVERRIDE_WITHOUT_DECLARATION = "Function can't override another function or an operator without being marked as an override function!";
23 | public const string MESSAGE_EXPECTED_RIGHT_PARENTHESIS = "Expected right parenthesis!";
24 | public const string MESSAGE_EXPECTED_VALUE = "Expected {0}!";
25 | public const string MESSAGE_INVALID_ARRAY_EXPECTED_COMMA = "Invalid array literal! Expected a comma!";
26 | public const string MESSAGE_EXPECTED_COMMA = "Expected a comma!";
27 | public const string MESSAGE_EXPECTED_COLON = "Expected a colon!";
28 | public const string MESSAGE_EXPECTED_PARAMETERS = "Expected parameters!";
29 | public const string MESSAGE_IMMUTABLE_CANT_BE_NULLABLE = "An immutable variable can't be nullable!";
30 | public const string MESSAGE_TYPE_INFERRED_CANT_BE_NULLABLE = "A type infered variable can't be nullable!";
31 | public const string MESSAGE_IMMUTABLE_CANT_BE_DYNAMIC = "An immutable variable can't be dynamic!";
32 | public const string MESSAGE_DYNAMIC_NOT_POSSIBLE_WITH_STATIC_TYPES = "A variable with a static type can't also be a dynamic variable!";
33 | public const string MESSAGE_UNEXPECTED_EOF = "Unexpected end of file!";
34 | public const string MESSAGE_INVALID_LITERAL = "Invalid literal: {0}!";
35 | public const string MESSAGE_EXPECTED_TOKEN = "Expected token: {0}!";
36 | public const string MESSAGE_UNEXPECTED_TOKEN = "Unexpected token: {0}!";
37 | public const string MESSAGE_EXPECTED_IDENTIFIER = "Expected an identifier!";
38 | }
39 | }
--------------------------------------------------------------------------------
/src/Hades.Error/Hades.Error.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Hades.Language/Hades.Language.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Hades.Language/Lexer/Keyword.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Language.Lexer
2 | {
3 | public static class Keyword
4 | {
5 | public const string Class = "class";
6 | public const string Func = "func";
7 | public const string Args = "args";
8 | public const string Requires = "requires";
9 | public const string If = "if";
10 | public const string Else = "else";
11 | public const string While = "while";
12 | public const string For = "for";
13 | public const string In = "in";
14 | public const string Stop = "stop";
15 | public const string Skip = "skip";
16 | public const string Try = "try";
17 | public const string Catch = "catch";
18 | public const string End = "end";
19 | public const string Var = "var";
20 | public const string Let = "let";
21 | public const string Null = "null";
22 | public const string Undefined = "undefined";
23 | public const string Protected = "protected";
24 | public const string Public = "public";
25 | public const string Private = "private";
26 | public const string With = "with";
27 | public const string From = "from";
28 | public const string As = "as";
29 | public const string Sets = "sets";
30 | public const string Put = "put";
31 | public const string Raise = "raise";
32 | public const string Fixed = "fixed";
33 | public const string Match = "match";
34 | public const string Struct = "struct";
35 | public const string Extends = "extends";
36 | }
37 | }
--------------------------------------------------------------------------------
/src/Hades.Language/Lexer/Lexer.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable once CheckNamespace
2 |
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using Hades.Common.Source;
8 | using Hades.Syntax.Lexeme;
9 |
10 | namespace Hades.Language.Lexer
11 | {
12 | public class Lexer
13 | {
14 | private readonly StringBuilder _builder;
15 | private int _column;
16 | private int _index;
17 | private int _line;
18 | private SourceCode _sourceCode;
19 | private SourceLocation _tokenStart;
20 |
21 | public Lexer()
22 | {
23 | _builder = new StringBuilder();
24 | _sourceCode = null;
25 | }
26 |
27 | private char Ch => _sourceCode[_index];
28 |
29 | // ReSharper disable once UnusedMember.Local
30 | private char Last => Peek(-1);
31 | private char Next => Peek(1);
32 |
33 | #region Keywords
34 |
35 | private static readonly string[] BlockKeywords =
36 | {
37 | Keyword.Class,
38 | Keyword.Func,
39 | Keyword.Args,
40 | Keyword.Requires,
41 | Keyword.If,
42 | Keyword.Else,
43 | Keyword.While,
44 | Keyword.For,
45 | Keyword.In,
46 | Keyword.Stop,
47 | Keyword.Skip,
48 | Keyword.Raise,
49 | Keyword.Try,
50 | Keyword.Catch,
51 | Keyword.Struct,
52 | Keyword.Match,
53 | Keyword.Extends,
54 | Keyword.End
55 | };
56 |
57 | private static readonly string[] VarKeywords =
58 | {
59 | Keyword.Var,
60 | Keyword.Let,
61 | Keyword.Null,
62 | Keyword.Undefined
63 | };
64 |
65 | private static readonly string[] AccessModifierKeywords =
66 | {
67 | Keyword.Protected,
68 | Keyword.Public,
69 | Keyword.Private
70 | };
71 |
72 | private static readonly string[] ImportKeywords =
73 | {
74 | Keyword.With,
75 | Keyword.From,
76 | Keyword.As,
77 | Keyword.Sets,
78 | Keyword.Fixed
79 | };
80 |
81 | private static readonly string[] MiscKeywords =
82 | {
83 | Keyword.Put
84 | };
85 |
86 | private static List _keywordList = new List();
87 |
88 | private static List GetKeywordList()
89 | {
90 | if (_keywordList.Count != 0) return _keywordList;
91 |
92 | var list = BlockKeywords.ToList();
93 | list.AddRange(VarKeywords.ToList());
94 | list.AddRange(AccessModifierKeywords.ToList());
95 | list.AddRange(ImportKeywords.ToList());
96 | list.AddRange(MiscKeywords.ToList());
97 |
98 | _keywordList = list;
99 | return _keywordList;
100 | }
101 |
102 | public static List Keywords => GetKeywordList();
103 |
104 | #endregion
105 |
106 | #region Helper
107 |
108 | private void Advance()
109 | {
110 | _index++;
111 | _column++;
112 | }
113 |
114 | private void Consume()
115 | {
116 | _builder.Append(Ch);
117 | Advance();
118 | }
119 |
120 | private void Clear()
121 | {
122 | _builder.Clear();
123 | }
124 |
125 | private Token CreateToken(Classifier kind)
126 | {
127 | var contents = _builder.ToString();
128 | var end = new SourceLocation(_index, _line, _column);
129 | var start = _tokenStart;
130 |
131 | _tokenStart = end;
132 | _builder.Clear();
133 |
134 | return new Token(kind, contents, start, end);
135 | }
136 |
137 | private void DoNewLine()
138 | {
139 | _line++;
140 | _column = 0;
141 | }
142 |
143 | private char Peek(int ahead)
144 | {
145 | return _sourceCode[_index + ahead];
146 | }
147 |
148 | #endregion
149 |
150 | #region Checks
151 |
152 | private bool IsDigit()
153 | {
154 | return char.IsDigit(Ch);
155 | }
156 |
157 | // ReSharper disable once InconsistentNaming
158 | private bool IsEOF()
159 | {
160 | return Ch == '\0';
161 | }
162 |
163 | private bool IsIdentifier()
164 | {
165 | return IsLetterOrDigit() || Ch == '_';
166 | }
167 |
168 | private bool IsKeyword()
169 | {
170 | return Keywords.Contains(_builder.ToString());
171 | }
172 |
173 | private bool IsBoolLiteral()
174 | {
175 | var builder = _builder.ToString();
176 | return builder == "true" || builder == "false";
177 | }
178 |
179 | private bool IsLetter()
180 | {
181 | return char.IsLetter(Ch);
182 | }
183 |
184 | private bool IsLetterOrDigit()
185 | {
186 | return char.IsLetterOrDigit(Ch);
187 | }
188 |
189 | private bool IsNewLine()
190 | {
191 | return Ch == '\n';
192 | }
193 |
194 | private bool IsPunctuation()
195 | {
196 | return "<>{}()[]!%^&*+-=/,?:|~#@.".Contains(Ch);
197 | }
198 |
199 | private bool IsWhiteSpace()
200 | {
201 | return (char.IsWhiteSpace(Ch) || IsEOF() || Ch == 8203) && !IsNewLine();
202 | }
203 |
204 | #endregion
205 |
206 | #region Lexing
207 |
208 | public IEnumerable LexFile(string sourceCode)
209 | {
210 | return LexFile(new SourceCode(sourceCode));
211 | }
212 |
213 | public IEnumerable LexFile(SourceCode source)
214 | {
215 | _sourceCode = source;
216 | _builder.Clear();
217 | _line = 1;
218 | _index = 0;
219 | _column = 0;
220 | CreateToken(Classifier.EndOfFile);
221 |
222 | return LexContents();
223 | }
224 |
225 | private IEnumerable LexContents()
226 | {
227 | while (!IsEOF())
228 | {
229 | yield return LexToken();
230 | }
231 |
232 | yield return CreateToken(Classifier.EndOfFile);
233 | }
234 |
235 | private Token LexToken()
236 | {
237 | if (IsEOF())
238 | {
239 | return CreateToken(Classifier.EndOfFile);
240 | }
241 |
242 | if (IsNewLine())
243 | {
244 | return ScanNewLine();
245 | }
246 |
247 | if (IsWhiteSpace())
248 | {
249 | return ScanWhiteSpace();
250 | }
251 |
252 | if (IsDigit())
253 | {
254 | return ScanInteger();
255 | }
256 |
257 | if (Ch == '/' && (Next == '/' || Next == '*'))
258 | {
259 | return ScanComment();
260 | }
261 |
262 | if (IsLetter() || Ch == '_' && Ch != '@')
263 | {
264 | return ScanIdentifier();
265 | }
266 |
267 | if (Ch == '"')
268 | {
269 | return ScanStringLiteral();
270 | }
271 |
272 | return IsPunctuation() ? ScanPunctuation() : ScanWord();
273 | }
274 |
275 | private Token ScanBlockComment()
276 | {
277 | bool IsEndOfComment()
278 | {
279 | return Ch == '*' && Next == '/';
280 | }
281 |
282 | while (!IsEndOfComment())
283 | {
284 | if (IsEOF())
285 | {
286 | return CreateToken(Classifier.Error);
287 | }
288 |
289 | if (IsNewLine())
290 | {
291 | DoNewLine();
292 | }
293 |
294 | Consume();
295 | }
296 |
297 | Consume();
298 | Consume();
299 |
300 | return CreateToken(Classifier.BlockComment);
301 | }
302 |
303 | private Token ScanComment()
304 | {
305 | Consume();
306 | if (Ch == '*')
307 | {
308 | return ScanBlockComment();
309 | }
310 |
311 | Consume();
312 |
313 | while (!IsNewLine() && !IsEOF())
314 | {
315 | Consume();
316 | }
317 |
318 | return CreateToken(Classifier.LineComment);
319 | }
320 |
321 | private Token ScanDec()
322 | {
323 | if (Ch == '.')
324 | {
325 | Consume();
326 | }
327 |
328 | while (IsDigit())
329 | {
330 | Consume();
331 | }
332 |
333 | if (Ch == 'f')
334 | {
335 | Consume();
336 | }
337 |
338 | if (!IsWhiteSpace() && !IsPunctuation() && !IsEOF() && !IsNewLine())
339 | {
340 | if (IsLetter())
341 | {
342 | return ScanWord("'{0}' is an invalid float value");
343 | }
344 |
345 | return ScanWord();
346 | }
347 |
348 | return CreateToken(Classifier.DecLiteral);
349 | }
350 |
351 | private Token ScanIdentifier()
352 | {
353 | while (IsIdentifier())
354 | {
355 | Consume();
356 | }
357 |
358 | if (!IsWhiteSpace() && !IsPunctuation() && !IsEOF() && !IsNewLine())
359 | {
360 | return ScanWord();
361 | }
362 |
363 | if (IsBoolLiteral())
364 | {
365 | return CreateToken(Classifier.BoolLiteral);
366 | }
367 |
368 | switch (_builder.ToString())
369 | {
370 | case "and":
371 | return CreateToken(Classifier.BooleanAnd);
372 | case "or":
373 | return CreateToken(Classifier.BooleanOr);
374 | case "not":
375 | return CreateToken(Classifier.Not);
376 | case "is":
377 | return CreateToken(Classifier.Equal);
378 | }
379 |
380 | return CreateToken(IsKeyword() ? Classifier.Keyword : Classifier.Identifier);
381 | }
382 |
383 | private Token ScanInteger()
384 | {
385 | var i = 0;
386 | var idx = _index;
387 |
388 | //TODO: Support for hex and binary digits
389 |
390 | while (IsDigit())
391 | {
392 | Consume();
393 | i++;
394 | }
395 |
396 | if (Ch == '.')
397 | {
398 | return i > 0 ? ScanDec() : ScanWord("Literal can't start with .");
399 | }
400 |
401 | if (!IsWhiteSpace() && !IsPunctuation() && !IsEOF() && !IsNewLine())
402 | {
403 | _index = idx;
404 | Clear();
405 | return ScanIdentifier();
406 | }
407 |
408 | return CreateToken(Classifier.IntLiteral);
409 | }
410 |
411 | private Token ScanNewLine()
412 | {
413 | Consume();
414 |
415 | DoNewLine();
416 |
417 | return CreateToken(Classifier.NewLine);
418 | }
419 |
420 | private Token ScanPunctuation()
421 | {
422 | switch (Ch)
423 | {
424 | case ':':
425 | Consume();
426 | if (Ch != ':') return CreateToken(Classifier.Colon);
427 | Consume();
428 | return CreateToken(Classifier.NullCondition);
429 |
430 | case '{':
431 | Consume();
432 | return CreateToken(Classifier.LeftBracket);
433 |
434 | case '}':
435 | Consume();
436 | return CreateToken(Classifier.RightBracket);
437 |
438 | case '[':
439 | Consume();
440 | return CreateToken(Classifier.LeftBrace);
441 |
442 | case ']':
443 | Consume();
444 | return CreateToken(Classifier.RightBrace);
445 |
446 | case '(':
447 | Consume();
448 | return CreateToken(Classifier.LeftParenthesis);
449 |
450 | case ')':
451 | Consume();
452 | return CreateToken(Classifier.RightParenthesis);
453 |
454 | case '>':
455 | Consume();
456 | switch (Ch)
457 | {
458 | case '=':
459 | Consume();
460 | return CreateToken(Classifier.GreaterThanOrEqual);
461 | case '>':
462 | Consume();
463 | if (Ch == '=')
464 | {
465 | Consume();
466 | return CreateToken(Classifier.BitShiftRightEqual);
467 | }
468 |
469 | return CreateToken(Classifier.BitShiftRight);
470 | default:
471 | return CreateToken(Classifier.GreaterThan);
472 | }
473 |
474 | case '<':
475 | Consume();
476 | switch (Ch)
477 | {
478 | case '=':
479 | Consume();
480 | return CreateToken(Classifier.LessThanOrEqual);
481 | case '<':
482 | Consume();
483 | if (Ch != '=') return CreateToken(Classifier.BitShiftLeft);
484 | Consume();
485 | return CreateToken(Classifier.BitShiftLeftEqual);
486 | default:
487 | return CreateToken(Classifier.LessThan);
488 | }
489 |
490 | case '+':
491 | Consume();
492 | switch (Ch)
493 | {
494 | case '=':
495 | Consume();
496 | return CreateToken(Classifier.PlusEqual);
497 | case '+':
498 | Consume();
499 | return CreateToken(Classifier.PlusPlus);
500 | default:
501 | return CreateToken(Classifier.Plus);
502 | }
503 |
504 | case '-':
505 | Consume();
506 | switch (Ch)
507 | {
508 | case '=':
509 | Consume();
510 | return CreateToken(Classifier.MinusEqual);
511 | case '>':
512 | Consume();
513 | return CreateToken(Classifier.Arrow);
514 | case '-':
515 | Consume();
516 | return CreateToken(Classifier.MinusMinus);
517 | default:
518 | return CreateToken(Classifier.Minus);
519 | }
520 |
521 | case '=':
522 | Consume();
523 | switch (Ch)
524 | {
525 | case '=':
526 | Consume();
527 | return CreateToken(Classifier.Equal);
528 | case '>':
529 | Consume();
530 | return CreateToken(Classifier.FatArrow);
531 | default:
532 | return CreateToken(Classifier.Assignment);
533 | }
534 |
535 | case '!':
536 | Consume();
537 | if (Ch != '=') return CreateToken(Classifier.Not);
538 | Consume();
539 | return CreateToken(Classifier.NotEqual);
540 |
541 | case '*':
542 | Consume();
543 | if (Ch != '=') return CreateToken(Classifier.Mul);
544 | Consume();
545 | return CreateToken(Classifier.MulEqual);
546 |
547 | case '/':
548 | Consume();
549 | if (Ch != '=') return CreateToken(Classifier.Div);
550 | Consume();
551 | return CreateToken(Classifier.DivEqual);
552 |
553 | case ',':
554 | Consume();
555 | return CreateToken(Classifier.Comma);
556 |
557 | case '&':
558 | Consume();
559 | switch (Ch)
560 | {
561 | case '&':
562 | Consume();
563 | return CreateToken(Classifier.BooleanAnd);
564 | case '=':
565 | Consume();
566 | return CreateToken(Classifier.BitwiseAndEqual);
567 | default:
568 | return CreateToken(Classifier.BitwiseAnd);
569 | }
570 |
571 | case '|':
572 | Consume();
573 | switch (Ch)
574 | {
575 | case '|':
576 | Consume();
577 | return CreateToken(Classifier.BooleanOr);
578 | case '=':
579 | Consume();
580 | return CreateToken(Classifier.BitwiseOrEqual);
581 | case '>':
582 | Consume();
583 | return CreateToken(Classifier.Pipeline);
584 | default:
585 | return CreateToken(Classifier.BitwiseOr);
586 | }
587 |
588 | case '%':
589 | Consume();
590 | if (Ch != '=') return CreateToken(Classifier.Mod);
591 | Consume();
592 | return CreateToken(Classifier.ModEqual);
593 |
594 | case '^':
595 | Consume();
596 | if (Ch != '=') return CreateToken(Classifier.BitwiseXor);
597 | Consume();
598 | return CreateToken(Classifier.BitwiseXorEqual);
599 |
600 | case '~':
601 | Consume();
602 | if (Ch != '=') return CreateToken(Classifier.BitwiseNegate);
603 | Consume();
604 | return CreateToken(Classifier.BitwiseNegateEqual);
605 |
606 | case '@':
607 | Consume();
608 | return CreateToken(Classifier.At);
609 |
610 | case '#':
611 | Consume();
612 | return CreateToken(Classifier.Tag);
613 |
614 | case '?':
615 | Consume();
616 | if (Ch != '?') return CreateToken(Classifier.Question);
617 | Consume();
618 | return CreateToken(Classifier.DoubleQuestion);
619 |
620 | case '.':
621 | Consume();
622 | return CreateToken(Classifier.Dot);
623 |
624 | default: return ScanWord();
625 | }
626 | }
627 |
628 | private Token ScanStringLiteral()
629 | {
630 | Advance();
631 |
632 | var multiLine = Peek(0) == '"' && Peek(1) == '"';
633 | var hasError = false;
634 |
635 | if (multiLine)
636 | {
637 | _index++;
638 | _index++;
639 | }
640 |
641 | while (true)
642 | {
643 | if (IsEOF())
644 | {
645 | throw new Exception("Unexpected End Of File");
646 | }
647 |
648 | if (IsNewLine() && !multiLine)
649 | {
650 | throw new Exception("No newline in strings allowed!");
651 | }
652 |
653 | var consume = true;
654 |
655 | if (Ch == '\\' && Next == '"')
656 | {
657 | Consume();
658 | Consume();
659 | consume = false;
660 | }
661 |
662 | if (Ch == '"')
663 | {
664 | if (multiLine)
665 | {
666 | if (Peek(1) == '"' && Peek(2) == '"')
667 | {
668 | _index++;
669 | _index++;
670 |
671 | Advance();
672 | return CreateToken(Classifier.MultiLineStringLiteral);
673 | }
674 | }
675 | else
676 | {
677 | break;
678 | }
679 | }
680 |
681 | if (consume)
682 | {
683 | Consume();
684 | }
685 | }
686 |
687 | Advance();
688 |
689 | return hasError ? CreateToken(Classifier.Error) : CreateToken(Classifier.StringLiteral);
690 | }
691 |
692 | private Token ScanWhiteSpace()
693 | {
694 | while (IsWhiteSpace() && Ch != '\0')
695 | {
696 | Consume();
697 | }
698 |
699 | return CreateToken(Classifier.WhiteSpace);
700 | }
701 |
702 | private Token ScanWord(string message = "Unexpected Token '{0}'")
703 | {
704 | while (!IsWhiteSpace() && !IsEOF() && !IsPunctuation())
705 | {
706 | Consume();
707 | }
708 |
709 | throw new Exception(string.Format(message, _builder));
710 | }
711 |
712 | #endregion
713 | }
714 | }
--------------------------------------------------------------------------------
/src/Hades.Language/Lexer/LexerGrammar.ebnf:
--------------------------------------------------------------------------------
1 | letter = ? UNICODE Letter ? ;
2 | digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
3 |
4 | identifier = ( letter | "_" ), { letter | digit | "_" } ;
5 | string = "\"", { letter - new line }, "\"" ;
6 | multiline strings = "\"\"\"", { letter }, "\"\"\"" ;
7 |
8 | int = digit, { digit } ;
9 | bool = "true" | "false" ;
10 | dec = int, ".", int ;
11 |
12 | block keywords = "class" | "func" | "args" | "requires" | "if" | "else" | "while" | "for" | "in" | "stop" | "skip" | "try" | "catch" | "default" | "end" ;
13 | var keywords = "var" | "let" | "null" | "undefined" ;
14 | access modifier keywords = "global" | "public" | "private" ;
15 | comparison keywords = "is" | "not" | "and" | "or" ;
16 | import keywords = "with" | "from" | "as" | "sets";
17 | misc keywords = "put"
18 |
19 | keyword = block keywords | var keywords | access modifier keywords | comparison keywords | import keywords ;
20 |
21 | new line = \n ;
22 |
23 | lineComment = "//", { letter - new line }, + new line ;
24 | blockComment = "/*", { letter }, "*/" ;
25 |
26 | arithmetic punctuation = "+" | "-" | "*" | "/" | "%" | "<" | "<=" | ">" | ">=" ;
27 | logical punctuation = ""==" | "!=" | "&&" | "||" ;
28 | bitwise punctuation = "<<" | ">>" | "&" | "|" | "^" | "~" ;
29 | misc punctuation = "!" | "@" | "*" | "=" | "|>" | "#";
30 | punctuation = arithmetic punctuation | logical punctuation | bitwise punctuation | misc punctuation ;
--------------------------------------------------------------------------------
/src/Hades.Language/Parser/Parser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Hades.Common;
5 | using Hades.Error;
6 | using Hades.Language.Lexer;
7 | using Hades.Syntax.Expression;
8 | using Hades.Syntax.Expression.Nodes;
9 | using Hades.Syntax.Expression.Nodes.BlockNodes;
10 | using Hades.Syntax.Expression.Nodes.LiteralNodes;
11 | using Hades.Syntax.Lexeme;
12 | using Classifier = Hades.Syntax.Lexeme.Classifier;
13 | // ReSharper disable AccessToModifiedClosure
14 |
15 | namespace Hades.Language.Parser
16 | {
17 | public class Parser
18 | {
19 | private readonly IEnumerable _tokens;
20 | private int _index;
21 | private bool _repl;
22 |
23 | public Parser(IEnumerable tokens, bool repl)
24 | {
25 | _tokens = tokens.ToList().Where(a => a.Kind != Classifier.WhiteSpace && a.Category != Category.Comment && a.Kind != Classifier.NewLine);
26 | _index = 0;
27 | _repl = repl;
28 | }
29 |
30 | private Token Current => _tokens.ElementAtOrDefault(_index) ?? _tokens.Last();
31 | private Token Last => Peek(-1);
32 | private Token Next => Peek(1);
33 |
34 | #region Helper
35 |
36 | private Token Peek(int ahead)
37 | {
38 | return _tokens.ElementAtOrDefault(_index + ahead) ?? _tokens.Last();
39 | }
40 |
41 | private void Advance(int i = 0)
42 | {
43 | _index += i != 0 ? i : 1;
44 | }
45 |
46 | private void Error(string error)
47 | {
48 | throw new Exception($"{error} {Current.Span.Start.Line}:{Current.Span.Start.Index}");
49 | }
50 |
51 | private void Error(string error, params object[] format)
52 | {
53 | Error(string.Format(error, format));
54 | }
55 |
56 | private BlockNode ReadToEnd(BlockNode node, bool allowSkipStop = false, List keywords = null)
57 | {
58 | if (keywords == null)
59 | {
60 | keywords = new List {Keyword.End};
61 | }
62 |
63 | while (!Is(keywords))
64 | {
65 | if (IsEof())
66 | {
67 | Error(ErrorStrings.MESSAGE_UNEXPECTED_EOF);
68 | }
69 |
70 | node.Children.Add(ParseNext(allowSkipStop));
71 | }
72 |
73 | Advance();
74 | node.Children = node.Children.Where(a => a != null).ToList();
75 | return node;
76 | }
77 |
78 | private void ExpectIdentifier()
79 | {
80 | if (!Expect(Classifier.Identifier))
81 | {
82 | Error(ErrorStrings.MESSAGE_EXPECTED_IDENTIFIER);
83 | }
84 | }
85 |
86 | private void EnforceIdentifier()
87 | {
88 | Advance(-1);
89 | ExpectIdentifier();
90 | Advance();
91 | }
92 |
93 | ///
94 | /// Gets the specific type of an object or proto
95 | /// ```
96 | /// func doStuff(object::IClient a)
97 | /// a->stuff("Hello world")
98 | /// end
99 | /// ```
100 | ///
101 | /// Specific type or null
102 | private (string specificType, Datatype dt) GetSpecificType()
103 | {
104 | var dt = (Datatype) Enum.Parse(typeof(Datatype), Current.Value.ToUpper());
105 | string type = null;
106 |
107 | if (dt == Datatype.PROTO || dt == Datatype.OBJECT || dt == Datatype.STRUCT)
108 | {
109 | Advance();
110 | if (Is(Classifier.NullCondition))
111 | {
112 | ExpectIdentifier();
113 | Advance();
114 | type = Current.Value;
115 | Advance();
116 | }
117 | else
118 | {
119 | Advance(-1);
120 | }
121 | }
122 |
123 | return (type, dt);
124 | }
125 |
126 | private List<(Node Key, Datatype? Value, string SpecificType)> ParseArguments(Classifier expectedClassifier, string expect)
127 | {
128 | var args = new List<(Node Key, Datatype? Value, string SpecificType)>();
129 | do
130 | {
131 | Advance();
132 | if (IsType())
133 | {
134 | var (type, dt) = GetSpecificType();
135 |
136 | if (type == null)
137 | {
138 | Advance();
139 | }
140 |
141 | EnforceIdentifier();
142 | args.Add((new IdentifierNode(Current.Value), dt, type));
143 | Advance();
144 | }
145 | else if (Is(Keyword.Args))
146 | {
147 | Advance();
148 | if (IsType())
149 | {
150 | var (type, dt) = GetSpecificType();
151 |
152 | if (type == null)
153 | {
154 | Advance();
155 | }
156 |
157 | EnforceIdentifier();
158 | args.Add((new ArgsNode(Current.Value), dt, type));
159 | }
160 | else
161 | {
162 | EnforceIdentifier();
163 | args.Add((new ArgsNode(Current.Value), Datatype.NONE, null));
164 | }
165 |
166 | Advance();
167 | }
168 | else if (IsIdentifier())
169 | {
170 | args.Add((new IdentifierNode(Current.Value), null, null));
171 | Advance();
172 | }
173 | else
174 | {
175 | Error(ErrorStrings.MESSAGE_EXPECTED_IDENTIFIER);
176 | }
177 | } while (Is(Classifier.Comma));
178 |
179 | if (args.Any(a => a.Key is ArgsNode))
180 | {
181 | if (args.Count(a => a.Key is ArgsNode) > 1)
182 | {
183 | Error(ErrorStrings.MESSAGE_CANT_HAVE_MULTIPLE_VARARGS);
184 | }
185 | }
186 |
187 | if (!Is(expectedClassifier))
188 | {
189 | Error(ErrorStrings.MESSAGE_EXPECTED_VALUE, expect);
190 | }
191 |
192 | Advance();
193 |
194 | return args;
195 | }
196 |
197 | #endregion
198 |
199 | #region Checks
200 |
201 | private bool IsEof()
202 | {
203 | return Is(Classifier.EndOfFile);
204 | }
205 |
206 | private bool IsKeyword()
207 | {
208 | return Lexer.Lexer.Keywords.Contains(Current.Value);
209 | }
210 |
211 | private bool Type(Token token)
212 | {
213 | return Enum.GetValues(typeof(Datatype)).Cast().Select(a => a.ToString().ToLower()).Contains(token.Value);
214 | }
215 |
216 | private bool IsType()
217 | {
218 | return Type(Current);
219 | }
220 |
221 | private bool ExpectType()
222 | {
223 | return Type(Next);
224 | }
225 |
226 | private bool IsIdentifier()
227 | {
228 | return Is(Classifier.Identifier);
229 | }
230 |
231 | private bool Was(Classifier classifier)
232 | {
233 | return Last == classifier;
234 | }
235 |
236 | private bool Was(string token)
237 | {
238 | return Last == token;
239 | }
240 |
241 | private bool Is(string token)
242 | {
243 | return Current == token;
244 | }
245 |
246 | private bool Is(IEnumerable tokens)
247 | {
248 | return tokens.Any(token => token == Current);
249 | }
250 |
251 | private bool Is(Classifier classifier)
252 | {
253 | return Current == classifier;
254 | }
255 |
256 | private bool IsAccessModifier()
257 | {
258 | return Is(Keyword.Private) || Is(Keyword.Public) || Is(Keyword.Protected);
259 | }
260 |
261 | private bool Is(Category category)
262 | {
263 | return Current.Category == category;
264 | }
265 |
266 | private bool Expect(string token)
267 | {
268 | return Next == token;
269 | }
270 |
271 | private bool Expect(Classifier classifier)
272 | {
273 | return Next == classifier;
274 | }
275 |
276 | private bool Expect(Category category)
277 | {
278 | return Next.Category == category;
279 | }
280 |
281 | #endregion
282 |
283 | #region Parsing
284 |
285 | #region Blocks
286 |
287 | private Node ParseStruct(AccessModifier accessModifier)
288 | {
289 | Advance();
290 | EnforceIdentifier();
291 |
292 | var node = new StructNode{Name = Current.Value, AccessModifier = accessModifier};
293 | Advance();
294 |
295 | while (!Is(Keyword.End))
296 | {
297 | if (IsAccessModifier() && (Expect(Keyword.Var) || Expect(Keyword.Let)))
298 | {
299 | Advance();
300 | switch (Enum.Parse(Last.Value.First().ToString().ToUpper() + Last.Value.Substring(1)))
301 | {
302 | case AccessModifier.Protected:
303 | Error(ErrorStrings.MESSAGE_ILLEGAL_PROTECTED_IN_STRUCT);
304 | break;
305 | case AccessModifier.Public:
306 | node.PublicVariables.Add(ParseNext() as VariableDeclarationNode);
307 | break;
308 | default:
309 | node.PrivateVariables.Add(ParseNext() as VariableDeclarationNode);
310 | break;
311 | }
312 | }
313 | else
314 | {
315 | var childNode = ParseNext();
316 |
317 | if (childNode is VariableDeclarationNode vn)
318 | {
319 | node.PrivateVariables.Add(vn);
320 | }
321 | else if (childNode is VarBlockNode vbn)
322 | {
323 | switch (vbn.AccessModifier)
324 | {
325 | case AccessModifier.Protected:
326 | Error(ErrorStrings.MESSAGE_ILLEGAL_PROTECTED_IN_STRUCT);
327 | break;
328 | case AccessModifier.Public:
329 | node.PublicVariables.AddRange(vbn.VariableDeclarationNodes);
330 | break;
331 | default:
332 | node.PrivateVariables.AddRange(vbn.VariableDeclarationNodes);
333 | break;
334 | }
335 | }
336 | else
337 | {
338 | Error(ErrorStrings.MESSAGE_UNEXPECTED_NODE, childNode.GetType().Name.Replace("Node", ""));
339 | }
340 | }
341 | }
342 |
343 | Advance();
344 | return node;
345 | }
346 |
347 | private Node ParseClass(bool isFixed, AccessModifier accessModifier)
348 | {
349 | Advance();
350 | EnforceIdentifier();
351 |
352 | var node = new ClassNode {Name = Current.Value, Fixed = isFixed, AccessModifier = accessModifier};
353 |
354 | Advance();
355 |
356 | if (Is(Classifier.LessThan))
357 | {
358 | do
359 | {
360 | Advance();
361 | EnforceIdentifier();
362 | node.Parents.Add(Current.Value);
363 | Advance();
364 | } while (Is(Classifier.Comma));
365 |
366 | Advance(-1);
367 | }
368 |
369 | while (!Is(Keyword.End))
370 | {
371 | if (IsAccessModifier() && (Expect(Keyword.Var) || Expect(Keyword.Let)))
372 | {
373 | Advance();
374 | switch (Enum.Parse(Last.Value.First().ToString().ToUpper() + Last.Value.Substring(1)))
375 | {
376 | case AccessModifier.Protected:
377 | node.ProtectedVariables.Add(ParseNext() as VariableDeclarationNode);
378 | break;
379 | case AccessModifier.Public:
380 | node.PublicVariables.Add(ParseNext() as VariableDeclarationNode);
381 | break;
382 | default:
383 | node.PrivateVariables.Add(ParseNext() as VariableDeclarationNode);
384 | break;
385 | }
386 | }
387 | else
388 | {
389 | var childNode = ParseNext();
390 |
391 | if (childNode is VariableDeclarationNode vn)
392 | {
393 | node.PrivateVariables.Add(vn);
394 | }
395 | else if (childNode is FunctionNode fn)
396 | {
397 | if (fn.Name == node.Name)
398 | {
399 | node.Constructors.Add(fn);
400 | }
401 | else
402 | {
403 | node.Functions.Add(fn);
404 | }
405 | }
406 | else if (childNode is ClassNode cn)
407 | {
408 | node.Classes.Add(cn);
409 | }
410 | else if (childNode is StructNode sn)
411 | {
412 | node.Structs.Add(sn);
413 | }
414 | else if (childNode is VarBlockNode vbn)
415 | {
416 | switch (vbn.AccessModifier)
417 | {
418 | case AccessModifier.Protected:
419 | node.ProtectedVariables.AddRange(vbn.VariableDeclarationNodes);
420 | break;
421 | case AccessModifier.Public:
422 | node.PublicVariables.AddRange(vbn.VariableDeclarationNodes);
423 | break;
424 | default:
425 | node.PrivateVariables.AddRange(vbn.VariableDeclarationNodes);
426 | break;
427 | }
428 | }
429 | else
430 | {
431 | Error(ErrorStrings.MESSAGE_UNEXPECTED_NODE, childNode.GetType().Name.Replace("Node", ""));
432 | }
433 | }
434 | }
435 |
436 | Advance();
437 | return node;
438 | }
439 |
440 | private Node ParseFunc(bool isFixed, AccessModifier accessModifier)
441 | {
442 | Advance();
443 | var node = new FunctionNode {Fixed = isFixed, AccessModifier = accessModifier};
444 | if (Is(Classifier.Not))
445 | {
446 | node.Override = true;
447 | Advance();
448 | }
449 |
450 | if (Is(Keyword.Extends))
451 | {
452 | //TODO: Test this lol
453 | node.Extension = true;
454 | Advance();
455 |
456 | if (!IsType())
457 | {
458 | Error(ErrorStrings.MESSAGE_EXPECTED_TYPE);
459 | }
460 |
461 | var specificType = GetSpecificType();
462 |
463 | if (string.IsNullOrEmpty(specificType.specificType))
464 | {
465 | Advance();
466 | }
467 |
468 | node.ExtensionType = specificType;
469 | }
470 |
471 | EnforceIdentifier();
472 |
473 | if (Expect(Category.Operator))
474 | {
475 | if (!node.Override)
476 | {
477 | Error(ErrorStrings.MESSAGE_OVERRIDE_WITHOUT_DECLARATION);
478 | }
479 |
480 | Advance();
481 | node.Name = Current.Value;
482 | Advance();
483 | }
484 | else
485 | {
486 | EnforceIdentifier();
487 |
488 | node.Name = Current.Value;
489 | Advance();
490 | }
491 |
492 | if (!Is(Classifier.LeftParenthesis))
493 | {
494 | Error(ErrorStrings.MESSAGE_EXPECTED_LEFT_PARENTHESIS);
495 | }
496 |
497 | if (!Expect(Classifier.RightParenthesis))
498 | {
499 | node.Parameters = ParseArguments(Classifier.RightParenthesis, "right parenthesis");
500 | }
501 |
502 | if (Is(Keyword.Requires))
503 | {
504 | Advance();
505 | node.Guard = ParseStatement();
506 | }
507 |
508 | return ReadToEnd(node);
509 | }
510 |
511 | private Node GetCondition()
512 | {
513 | if (!Is(Classifier.LeftParenthesis))
514 | {
515 | Error(ErrorStrings.MESSAGE_EXPECTED_LEFT_PARENTHESIS);
516 | }
517 |
518 | Advance();
519 |
520 | var node = ParseStatement();
521 |
522 | if (!Is(Classifier.RightParenthesis))
523 | {
524 | Error(ErrorStrings.MESSAGE_EXPECTED_RIGHT_PARENTHESIS);
525 | }
526 |
527 | Advance();
528 | return node;
529 | }
530 |
531 | private Node ParseVariableGroup()
532 | {
533 | var node = new VarBlockNode();
534 | Advance();
535 | if (!IsAccessModifier())
536 | {
537 | Error(ErrorStrings.MESSAGE_EXPECTED_ACCESS_MODIFIER);
538 | }
539 |
540 | node.AccessModifier = Enum.Parse(Current.Value.First().ToString().ToUpper() + Current.Value.Substring(1));
541 | Advance();
542 |
543 | while (!Is(Keyword.End))
544 | {
545 | if (Is(Keyword.Var) || Is(Keyword.Let))
546 | {
547 | node.VariableDeclarationNodes.Add(ParseNext() as VariableDeclarationNode);
548 | }
549 | }
550 |
551 | Advance();
552 | return node;
553 | }
554 |
555 | private Node ParseMatch(bool allowSkipStop)
556 | {
557 | Advance();
558 | var node = new MatchNode {First = Is("first")};
559 |
560 | if (Is("first"))
561 | {
562 | Advance();
563 | }
564 |
565 | if (!Is(Classifier.LeftParenthesis))
566 | {
567 | Error(ErrorStrings.MESSAGE_EXPECTED_LEFT_PARENTHESIS);
568 | }
569 |
570 | Advance();
571 |
572 | node.Match = Is(Classifier.Underscore /*No statement*/) ? new NoVariableNode() : ParseStatement();
573 |
574 | if (!Is(Classifier.RightParenthesis))
575 | {
576 | Error(ErrorStrings.MESSAGE_EXPECTED_RIGHT_PARENTHESIS);
577 | }
578 |
579 | Advance();
580 |
581 | while (!Is(Keyword.End))
582 | {
583 | var cond = ParseStatement();
584 |
585 | if (!Is(Classifier.FatArrow))
586 | {
587 | Error(ErrorStrings.MESSAGE_EXPECTED_TOKEN, "=>");
588 | }
589 |
590 | Advance();
591 |
592 | var action = ParseStatement(allowSkipStop);
593 |
594 | if (action is LambdaNode ln)
595 | {
596 | if (ln.Complex)
597 | {
598 | Error(ErrorStrings.MESSAGE_CANT_USE_COMPLEX_LAMBDA_IN_MATCH_BLOCK);
599 | }
600 | }
601 |
602 | //Actually...mostly anything can be an action if you think about it.
603 | //We could have something like "Hello" => a->getAction(10)
604 | //So here, the action would be a call which would return a lambda
605 | //The only thing we *really* can't have is a complex lambda
606 |
607 | node.Statements.Add(cond, action);
608 | }
609 |
610 | Advance();
611 |
612 | return node;
613 | }
614 |
615 | private Node ParseWhile()
616 | {
617 | Advance();
618 | var node = new WhileNode {Condition = GetCondition()};
619 |
620 | return ReadToEnd(node, true);
621 | }
622 |
623 | private Node ParseIf(bool allowSkipStop)
624 | {
625 | Advance();
626 | var node = new IfNode {Condition = GetCondition(), If = ReadToEnd(new GenericBlockNode(), allowSkipStop, new List {Keyword.End, Keyword.Else})};
627 |
628 |
629 | if (Was(Keyword.End))
630 | {
631 | return node;
632 | }
633 |
634 | while (Was(Keyword.Else) && Is(Keyword.If))
635 | {
636 | Advance();
637 | node.ElseIfNodes.Add(new IfNode {Condition = GetCondition(), If = ReadToEnd(new GenericBlockNode(), allowSkipStop, new List {Keyword.End, Keyword.Else})});
638 | }
639 |
640 | if (Was(Keyword.Else))
641 | {
642 | node.Else = ReadToEnd(new GenericBlockNode(), allowSkipStop);
643 | }
644 |
645 | return node;
646 | }
647 |
648 | private Node ParseFor()
649 | {
650 | Advance();
651 | var node = new ForNode();
652 |
653 | if (!Is(Classifier.LeftParenthesis))
654 | {
655 | Error(ErrorStrings.MESSAGE_EXPECTED_LEFT_PARENTHESIS);
656 | }
657 |
658 | Advance();
659 |
660 | if (Is("_"))
661 | {
662 | node.Variable = new NoVariableNode();
663 | Advance();
664 | }
665 | else
666 | {
667 | var index = _index;
668 | try
669 | {
670 | node.Variable = ParseVariableDeclaration();
671 | }
672 | catch (Exception)
673 | {
674 | _index = index;
675 | EnforceIdentifier();
676 |
677 | node.Variable = new IdentifierNode(Current.Value);
678 | Advance();
679 | }
680 | }
681 |
682 | if (!Is(Keyword.In))
683 | {
684 | Error(ErrorStrings.MESSAGE_EXPECTED_IN);
685 | }
686 |
687 | Advance();
688 |
689 | node.Source = ParseStatement();
690 |
691 | if (!Is(Classifier.RightParenthesis))
692 | {
693 | Error(ErrorStrings.MESSAGE_EXPECTED_RIGHT_PARENTHESIS);
694 | }
695 |
696 | Advance();
697 |
698 | return ReadToEnd(node, true);
699 | }
700 |
701 | private Node ParseTryCatchElse(bool allowSkipStop)
702 | {
703 | Advance();
704 | var node = new TryCatchElseNode {Try = ReadToEnd(new GenericBlockNode(), allowSkipStop, new List {Keyword.Catch, Keyword.Else})};
705 |
706 |
707 | while (Was(Keyword.Catch))
708 | {
709 | var catchNode = new TryCatchElseNode.CatchBlock();
710 |
711 | if (!Is(Classifier.LeftParenthesis))
712 | {
713 | Error(ErrorStrings.MESSAGE_EXPECTED_LEFT_PARENTHESIS);
714 | }
715 |
716 | Advance();
717 |
718 | if (IsType())
719 | {
720 | (catchNode.SpecificType, catchNode.Datatype) = GetSpecificType();
721 |
722 | if (catchNode.SpecificType == null)
723 | {
724 | Advance();
725 | }
726 | }
727 |
728 | EnforceIdentifier();
729 | catchNode.Name = Current.Value;
730 | Advance();
731 |
732 | if (!Is(Classifier.RightParenthesis))
733 | {
734 | Error(ErrorStrings.MESSAGE_EXPECTED_RIGHT_PARENTHESIS);
735 | }
736 |
737 | Advance();
738 |
739 | catchNode.Block = ReadToEnd(new GenericBlockNode(), allowSkipStop, new List {Keyword.Catch, Keyword.End, Keyword.Else});
740 | node.Catch.Add(catchNode);
741 | }
742 |
743 | if (Was(Keyword.Else))
744 | {
745 | node.Else = ReadToEnd(new GenericBlockNode(), allowSkipStop);
746 | }
747 |
748 | return node;
749 | }
750 |
751 | #endregion
752 |
753 | #region Statements
754 |
755 | private Node ParsePackageImport()
756 | {
757 | var node = new WithNode();
758 | Advance();
759 |
760 | EnforceIdentifier();
761 |
762 | node.Target = Current.Value;
763 |
764 | if (Expect(Keyword.Fixed))
765 | {
766 | node.Fixed = true;
767 | Advance();
768 | }
769 |
770 | if (Expect(Keyword.As))
771 | {
772 | Advance(2);
773 |
774 |
775 | EnforceIdentifier();
776 |
777 | node.Name = Current.Value;
778 | }
779 |
780 | Advance();
781 |
782 | if (Is(Keyword.From)) //with x FROM ...
783 | {
784 | Advance();
785 | EnforceIdentifier();
786 |
787 | if (Expect(Classifier.Colon))
788 | {
789 | node.Native = true;
790 | node.NativePackage = Current.Value;
791 |
792 | Advance(2);
793 | EnforceIdentifier();
794 |
795 | node.Source = Current.Value;
796 | }
797 | else
798 | {
799 | node.Source = Current.Value;
800 | }
801 |
802 | Advance();
803 | }
804 |
805 | return node;
806 | }
807 |
808 | private Node ParseCall(Node baseNode)
809 | {
810 | EnforceIdentifier();
811 |
812 | var node = new CallNode {Source = baseNode, Target = new IdentifierNode(Current.Value)};
813 | Advance();
814 |
815 | return ParseCallSignature(node, true);
816 | }
817 |
818 | private Node ParseCallSignature(CallNode node, bool parseValueCall)
819 | {
820 | if (Is(Classifier.LeftParenthesis))
821 | {
822 | Advance();
823 | if (Is(Classifier.RightParenthesis))
824 | {
825 | Advance();
826 | }
827 | else
828 | {
829 | do
830 | {
831 | var name = "";
832 | if (Is(Classifier.Identifier) && Expect(Classifier.Assignment))
833 | {
834 | name = Current.Value;
835 | Advance(2);
836 | }
837 |
838 | node.Parameters.Add(ParseStatement(), name);
839 |
840 | if (!Is(Classifier.RightParenthesis))
841 | {
842 | if (!Is(Classifier.Comma))
843 | {
844 | Error(ErrorStrings.MESSAGE_EXPECTED_COMMA);
845 | }
846 |
847 | Advance();
848 | }
849 | } while (!Is(Classifier.RightParenthesis));
850 |
851 | Advance();
852 | }
853 | }
854 | else
855 | {
856 | if (parseValueCall)
857 | {
858 | return new ValueCallNode {Source = node.Source, Target = node.Target};
859 | }
860 |
861 | Error(ErrorStrings.MESSAGE_EXPECTED_PARAMETERS);
862 | }
863 |
864 | return node;
865 | }
866 |
867 | private Node ParseDeepCall(Node node)
868 | {
869 | Node deepCall = null;
870 |
871 | while (Is(Classifier.Arrow))
872 | {
873 | Advance();
874 | deepCall = ParseCall(deepCall ?? node);
875 | }
876 |
877 | return deepCall;
878 | }
879 |
880 | private Node ParseInlineIf(Node node)
881 | {
882 | Advance();
883 | var truthy = ParseStatement();
884 |
885 | if (!Is(Classifier.Colon))
886 | {
887 | Error(ErrorStrings.MESSAGE_EXPECTED_COLON);
888 | }
889 |
890 | Advance();
891 | var falsy = ParseStatement();
892 | return new InlineIf {Condition = node, Falsy = falsy, Truthy = truthy};
893 | }
894 |
895 | private Node ParsePipeline(Node node)
896 | {
897 | var root = node;
898 | do
899 | {
900 | Advance();
901 | var child = ParseStatement(true);
902 |
903 | if (child is ValueCallNode vcn)
904 | {
905 | var callNode = new CallNode {Source = vcn.Source, Target = vcn.Target};
906 | callNode.Parameters.Add(root, "");
907 | root = callNode;
908 | }
909 | else if (child is IdentifierNode id)
910 | {
911 | var callNode = new CallNode {Source = new IdentifierNode("this"), Target = id};
912 | callNode.Parameters.Add(root, "");
913 | root = callNode;
914 | }
915 | else if (child is ArrayAccessNode an)
916 | {
917 | var callNode = new CallNode {Source = new IdentifierNode("this"), Target = an};
918 | callNode.Parameters.Add(root, "");
919 | root = callNode;
920 | }
921 | else if (child is CallNode cn)
922 | {
923 | var placeHolders = cn.Parameters.Where(a => a.Key is PlaceHolderNode).Select(a => a).ToList();
924 |
925 | placeHolders.ForEach(a => { cn.Parameters.Remove(a.Key); });
926 |
927 | placeHolders.ForEach(a => { cn.Parameters.Add(root, a.Value); });
928 |
929 | root = cn;
930 | }
931 | else
932 | {
933 | Error(ErrorStrings.MESSAGE_UNEXPECTED_STATEMENT);
934 | }
935 | } while (Is(Classifier.Pipeline));
936 |
937 | return root;
938 | }
939 |
940 | #endregion
941 |
942 | #region Variables
943 |
944 | private Node ParseVariableDeclaration()
945 | {
946 | var variable = new VariableDeclarationNode {Mutable = Is(Keyword.Var)};
947 |
948 | if (Expect(Classifier.Mul))
949 | {
950 | if (Is(Keyword.Let))
951 | {
952 | Advance();
953 | Error(ErrorStrings.MESSAGE_IMMUTABLE_CANT_BE_DYNAMIC);
954 | }
955 |
956 | Advance();
957 |
958 | if (ExpectType())
959 | {
960 | Advance();
961 | Error(ErrorStrings.MESSAGE_DYNAMIC_NOT_POSSIBLE_WITH_STATIC_TYPES);
962 | }
963 |
964 | variable.Dynamic = true;
965 | }
966 |
967 | Advance();
968 |
969 | if (IsType())
970 | {
971 | var (type, dt) = GetSpecificType();
972 |
973 | if (type == null)
974 | {
975 | Advance();
976 | }
977 |
978 | variable.Datatype = dt;
979 | variable.SpecificType = type;
980 | }
981 |
982 | if (Is(Classifier.Question))
983 | {
984 | if (variable.Datatype == null && Peek(2) != Classifier.Assignment)
985 | {
986 | Error(ErrorStrings.MESSAGE_TYPE_INFERRED_CANT_BE_NULLABLE);
987 | }
988 |
989 | if (!variable.Mutable)
990 | {
991 | Advance(-2);
992 | Error(ErrorStrings.MESSAGE_IMMUTABLE_CANT_BE_NULLABLE);
993 | }
994 |
995 | Advance();
996 | variable.Nullable = true;
997 | }
998 |
999 | if (Is(Classifier.LeftBrace))
1000 | {
1001 | variable.Array = true;
1002 | Advance(); //[
1003 |
1004 | if (!Is(Classifier.RightBrace))
1005 | {
1006 | if (Is(Classifier.Mul))
1007 | {
1008 | variable.InfiniteArray = true;
1009 | Advance();
1010 | if (!Is(Classifier.RightBrace))
1011 | {
1012 | Error(ErrorStrings.MESSAGE_EXPECTED_TOKEN, Current.Kind.ToString());
1013 | }
1014 | }
1015 | else
1016 | {
1017 | variable.ArraySize = ParseStatement();
1018 |
1019 | if (Is(Classifier.Comma))
1020 | {
1021 | var multiDimensionalArray = new MultiDimensionalArrayNode();
1022 | multiDimensionalArray.Value.Add(variable.ArraySize);
1023 | while (Is(Classifier.Comma))
1024 | {
1025 | Advance();
1026 | multiDimensionalArray.Value.Add(ParseStatement());
1027 | }
1028 |
1029 | variable.ArraySize = multiDimensionalArray;
1030 | }
1031 | }
1032 | }
1033 |
1034 | Advance(); //]
1035 | }
1036 |
1037 | if (IsIdentifier())
1038 | {
1039 | variable.Name = Current.Value;
1040 | Advance();
1041 | }
1042 | else
1043 | {
1044 | Error(ErrorStrings.MESSAGE_EXPECTED_IDENTIFIER);
1045 | }
1046 |
1047 | return variable;
1048 | }
1049 |
1050 | private Node ParseVariableDeclarationAndAssignment()
1051 | {
1052 | var variable = ParseVariableDeclaration() as VariableDeclarationNode;
1053 |
1054 | if (Is(Classifier.Assignment))
1055 | {
1056 | Advance();
1057 | if (variable != null) variable.Assignment = ParseStatement(); //change to ParseStatement
1058 | }
1059 |
1060 | return variable;
1061 | }
1062 |
1063 | private Node ParseAssignment(Node node)
1064 | {
1065 | Classifier GetNoAssignType(Classifier classifier)
1066 | {
1067 | return (Classifier) Enum.Parse(typeof(Classifier), classifier.ToString().Replace("Equal", ""));
1068 | }
1069 |
1070 | Advance();
1071 | switch (Last.Kind)
1072 | {
1073 | case Classifier.Assignment:
1074 | return new AssignmentNode {Variable = node, Value = ParseStatement()};
1075 | default:
1076 | return new AssignmentNode {Variable = node, Value = new OperationNode {Operations = new List {node, new OperationNodeNode(GetNoAssignType(Last.Kind), Last.Value.Replace("=", "")), ParseStatement()}}};
1077 | }
1078 | }
1079 |
1080 | private Node ParseRightHand(Node node)
1081 | {
1082 | Advance();
1083 | return new SideNode(node, new OperationNodeNode(Last.Kind, Last.Value), Side.RIGHT);
1084 | }
1085 |
1086 | private Node ParseArrayAccess(Node node)
1087 | {
1088 | Advance();
1089 | var index = ParseStatement();
1090 |
1091 | if (!Is(Classifier.RightBrace))
1092 | {
1093 | Error(ErrorStrings.MESSAGE_EXPECTED_TOKEN, "]");
1094 | }
1095 |
1096 | Advance();
1097 |
1098 | return new ArrayAccessNode {BaseNode = node, Index = index};
1099 | }
1100 |
1101 | private Node ParseLeftHand(OperationNodeNode node)
1102 | {
1103 | Advance();
1104 | return new SideNode(ParseStatement(), node, Side.LEFT);
1105 | }
1106 |
1107 | private Node ParseArrayOrLambda()
1108 | {
1109 | Advance();
1110 |
1111 | var parameters = new List<(Node Key, Datatype? Value, string SpecificType)>();
1112 | var isLambda = true;
1113 | var index = _index;
1114 |
1115 | try
1116 | {
1117 | Advance(-1);
1118 | //Assume it's a lambda and parse arguments
1119 | parameters = ParseArguments(Classifier.FatArrow, "fat arrow");
1120 | }
1121 | catch (Exception)
1122 | {
1123 | //Not a lambda
1124 | isLambda = false;
1125 | }
1126 |
1127 | //Lambda
1128 | Node n;
1129 | if (isLambda && Was(Classifier.FatArrow))
1130 | {
1131 | var node = new LambdaNode();
1132 | node.Parameters.AddRange(parameters);
1133 |
1134 | while (!Is(Classifier.RightBracket))
1135 | {
1136 | node.Children.Add(ParseNext());
1137 | }
1138 |
1139 | if (node.Children.Count == 1)
1140 | {
1141 | node.Complex = false;
1142 | }
1143 |
1144 | Advance();
1145 | node.Children = node.Children.Where(a => a != null).ToList();
1146 | n = node;
1147 | }
1148 | else
1149 | {
1150 | _index = index;
1151 |
1152 | var vals = new List();
1153 |
1154 | //Collect array values
1155 | do
1156 | {
1157 | vals.Add(ParseStatement());
1158 | if (!Is(Classifier.RightBracket))
1159 | {
1160 | if (!Is(Classifier.Comma))
1161 | {
1162 | Error(ErrorStrings.MESSAGE_INVALID_ARRAY_EXPECTED_COMMA);
1163 | }
1164 |
1165 | Advance();
1166 | }
1167 | } while (!Is(Classifier.RightBracket));
1168 |
1169 | Advance();
1170 | var node = new ListNode {Value = vals};
1171 | n = node;
1172 | }
1173 |
1174 | return n;
1175 | }
1176 |
1177 | #endregion
1178 |
1179 | #region Entry
1180 |
1181 | public RootNode Parse()
1182 | {
1183 | var node = new RootNode();
1184 | while (!IsEof())
1185 | {
1186 | node.Children.Add(ParseNext());
1187 | }
1188 |
1189 | node.Children = node.Children.Where(a => a != null).ToList();
1190 | return node;
1191 | }
1192 |
1193 | ///
1194 | /// Parses blocks
1195 | ///
1196 | ///
1197 | private Node ParseNext(bool allowSkipStop = false)
1198 | {
1199 | while (Is(Classifier.NewLine))
1200 | {
1201 | Advance();
1202 | }
1203 |
1204 | if (Is(Classifier.EndOfFile) || Is(Keyword.End))
1205 | {
1206 | return null;
1207 | }
1208 |
1209 | if (Is(Classifier.At))
1210 | {
1211 | return ParseVariableGroup();
1212 | }
1213 |
1214 | if (Is(Classifier.Tag))
1215 | {
1216 | ExpectIdentifier();
1217 | Advance();
1218 | var annotation = Current.Value;
1219 |
1220 | Advance();
1221 |
1222 | Node annotationValue = new NoVariableNode();
1223 |
1224 | if (Is(Classifier.LeftParenthesis))
1225 | {
1226 | Advance();
1227 | var val = ParseStatement();
1228 |
1229 | if (val is BoolLiteralNode || val is DecLiteralNode || val is IntLiteralNode || val is StringLiteralNode || val is IdentifierNode)
1230 | {
1231 | annotationValue = val;
1232 | }
1233 |
1234 | if (!Is(Classifier.RightParenthesis))
1235 | {
1236 | Error(ErrorStrings.MESSAGE_EXPECTED_RIGHT_PARENTHESIS);
1237 | }
1238 |
1239 | Advance();
1240 | }
1241 |
1242 | var n = ParseNext();
1243 | n.Annotations.Add(annotation, annotationValue);
1244 | return n;
1245 | }
1246 |
1247 | if (IsKeyword())
1248 | {
1249 | if (allowSkipStop)
1250 | {
1251 | if (Is(Keyword.Skip))
1252 | {
1253 | Advance();
1254 | return new CommandNode(Keyword.Skip);
1255 | }
1256 |
1257 | if (Is(Keyword.Stop))
1258 | {
1259 | Advance();
1260 | return new CommandNode(Keyword.Stop);
1261 | }
1262 | }
1263 |
1264 | AccessModifier? accessModifier = null;
1265 | if (IsAccessModifier())
1266 | {
1267 | accessModifier = Enum.Parse(Current.Value.First().ToString().ToUpper() + Current.Value.Substring(1));
1268 | //TODO: Disallow protected?
1269 | Advance();
1270 | }
1271 |
1272 | var isFixed = false;
1273 | if (Is(Keyword.Fixed))
1274 | {
1275 | isFixed = true;
1276 | Advance();
1277 | //HACK: this is not beautiful
1278 | }
1279 |
1280 | void NoAccessModifierOrFixed()
1281 | {
1282 | if (isFixed) Error(ErrorStrings.MESSAGE_UNEXPECTED_KEYWORD, Keyword.Fixed);
1283 | if (accessModifier != null) Error(ErrorStrings.MESSAGE_UNEXPECTED_ACCESS_MODIFIER);
1284 | }
1285 |
1286 | switch (Current.Value)
1287 | {
1288 | case Keyword.Put:
1289 | NoAccessModifierOrFixed();
1290 | Advance();
1291 | return new PutNode {Statement = ParseStatement()};
1292 |
1293 | case Keyword.Match:
1294 | NoAccessModifierOrFixed();
1295 | return ParseMatch(allowSkipStop);
1296 |
1297 | case Keyword.Class:
1298 | return ParseClass(isFixed, accessModifier.GetValueOrDefault());
1299 |
1300 | case Keyword.Struct:
1301 | if (isFixed) Error(ErrorStrings.MESSAGE_UNEXPECTED_KEYWORD, Keyword.Fixed);
1302 | return ParseStruct(accessModifier.GetValueOrDefault());
1303 |
1304 | case Keyword.Func:
1305 | return ParseFunc(isFixed, accessModifier.GetValueOrDefault());
1306 |
1307 | case Keyword.While:
1308 | NoAccessModifierOrFixed();
1309 | return ParseWhile();
1310 |
1311 | case Keyword.If:
1312 | NoAccessModifierOrFixed();
1313 | return ParseIf(allowSkipStop);
1314 |
1315 | case Keyword.For:
1316 | NoAccessModifierOrFixed();
1317 | return ParseFor();
1318 |
1319 | case Keyword.Try:
1320 | NoAccessModifierOrFixed();
1321 | return ParseTryCatchElse(allowSkipStop);
1322 |
1323 | case Keyword.Skip:
1324 | case Keyword.Stop:
1325 | NoAccessModifierOrFixed();
1326 | Error(ErrorStrings.MESSAGE_UNEXPECTED_KEYWORD, Current.Value);
1327 | break;
1328 |
1329 | default:
1330 | NoAccessModifierOrFixed();
1331 | return ParseStatement();
1332 | }
1333 | }
1334 |
1335 | //TODO: Do some cleanup, many of these conditions are in because of testing
1336 | if (IsIdentifier() || Is(Category.Literal) && (Expect(Classifier.Arrow) || Expect(Classifier.Question)) || Is(Category.Literal) && Expect(Category.Operator) || Is(Classifier.LeftParenthesis) || Is(Classifier.LeftBracket) || Is(Classifier.Not) || Is(Classifier.Minus))
1337 | {
1338 | return ParseStatement();
1339 | }
1340 |
1341 | if (_repl)
1342 | {
1343 | if (Is(Category.Literal))
1344 | {
1345 | return ParseStatement();
1346 | }
1347 | }
1348 |
1349 | Error(ErrorStrings.MESSAGE_UNEXPECTED_TOKEN, Current.Value);
1350 |
1351 | return null;
1352 | }
1353 |
1354 | ///
1355 | /// For statements that can have more complex nodes within the statement
1356 | ///
1357 | ///
1358 | private Node ParseStatement(bool pipeline = false)
1359 | {
1360 | Node GetOperation(Node initial = null)
1361 | {
1362 | var ops = new OperationNode();
1363 | if (initial != null)
1364 | {
1365 | ops.Operations.Add(initial);
1366 | }
1367 |
1368 | while (Is(Category.Operator))
1369 | {
1370 | ops.Operations.Add(new OperationNodeNode(Current.Kind, Current.Value));
1371 | Advance();
1372 | ops.Operations.Add(ParseStatement());
1373 | }
1374 |
1375 | return ops;
1376 | }
1377 |
1378 | (Node, bool) GetAnonymousCall(Node init)
1379 | {
1380 | var ret = false;
1381 | if (init is LambdaNode || init is CallNode || init is ArrayAccessNode)
1382 | {
1383 | if (Is(Classifier.LeftParenthesis))
1384 | {
1385 | init = ParseCallSignature(new CallNode {Source = init, Target = new IdentifierNode("anonymous")}, false);
1386 | ret = true;
1387 | }
1388 | }
1389 |
1390 | return (init, ret);
1391 | }
1392 |
1393 | Node node;
1394 |
1395 | if (Is(Classifier.LeftParenthesis))
1396 | {
1397 | Advance();
1398 | var n = ParseStatement();
1399 |
1400 | if (!Is(Classifier.RightParenthesis))
1401 | {
1402 | Error(ErrorStrings.MESSAGE_EXPECTED_RIGHT_PARENTHESIS);
1403 | }
1404 |
1405 | Advance();
1406 |
1407 | while (Is(Category.Operator))
1408 | {
1409 | n = GetOperation(n);
1410 | }
1411 |
1412 | //I wanted to manually differentiate between a calculation and a calculation in ()
1413 | node = n is OperationNode ? new ParenthesesNode {Node = n} : n;
1414 | }
1415 | else
1416 | {
1417 | node = ParseStatementWithoutOperation();
1418 | }
1419 |
1420 | if (Is(Category.Operator) && node != null)
1421 | {
1422 | node = GetOperation(node);
1423 | }
1424 |
1425 | if (Is(Classifier.NullCondition))
1426 | {
1427 | Advance();
1428 | return new NullConditionNode {Condition = node, Operation = ParseStatement()};
1429 | }
1430 |
1431 | var isRet = false;
1432 |
1433 | do
1434 | {
1435 | if (Is(Classifier.Question))
1436 | {
1437 | node = ParseInlineIf(node);
1438 | }
1439 |
1440 | if (Is(Classifier.Pipeline) && !pipeline)
1441 | {
1442 | node = ParsePipeline(node);
1443 | }
1444 |
1445 | if (Is(Classifier.Arrow))
1446 | {
1447 | node = ParseDeepCall(node);
1448 | }
1449 |
1450 | if (Is(Classifier.LeftBrace))
1451 | {
1452 | node = ParseArrayAccess(node);
1453 | }
1454 | } while (new Func(() =>
1455 | {
1456 | (node, isRet) = GetAnonymousCall(node);
1457 | return isRet || Is(Classifier.Question) || (Is(Classifier.Pipeline) && !pipeline) || Is(Classifier.Arrow) || Is(Classifier.LeftBrace);
1458 | })());
1459 |
1460 | if (Is(Category.Assignment))
1461 | {
1462 | if (node is CallNode || node is InlineIf)
1463 | {
1464 | Error(ErrorStrings.MESSAGE_UNEXPECTED_CALL_OR_INLINE_IF);
1465 | }
1466 |
1467 | return ParseAssignment(node);
1468 | }
1469 |
1470 | if (Is(Category.RightHand))
1471 | {
1472 | if (node is CallNode || node is InlineIf)
1473 | {
1474 | Error(ErrorStrings.MESSAGE_UNEXPECTED_CALL_OR_INLINE_IF);
1475 | }
1476 |
1477 | return ParseRightHand(node);
1478 | }
1479 |
1480 | while (Is(Category.Operator))
1481 | {
1482 | node = GetOperation(node);
1483 | }
1484 |
1485 | return node;
1486 | }
1487 |
1488 | ///
1489 | /// For less complex statements (statements that do not require a preceding node
1490 | ///
1491 | /// Node
1492 | private Node ParseStatementWithoutOperation()
1493 | {
1494 | if (IsEof())
1495 | {
1496 | Error(ErrorStrings.MESSAGE_UNEXPECTED_EOF);
1497 | }
1498 |
1499 | if (IsKeyword())
1500 | {
1501 | switch (Current.Value)
1502 | {
1503 | case Keyword.Var:
1504 | case Keyword.Let:
1505 | return ParseVariableDeclarationAndAssignment();
1506 |
1507 | case Keyword.With:
1508 | return ParsePackageImport();
1509 |
1510 | case Keyword.Raise:
1511 | Advance();
1512 | return new RaiseNode {Exception = ParseStatement()};
1513 |
1514 | case Keyword.Null:
1515 | Advance();
1516 | return new NullValueNode();
1517 |
1518 | default:
1519 | Error(ErrorStrings.MESSAGE_UNEXPECTED_KEYWORD, Current.Value);
1520 | break;
1521 | }
1522 | }
1523 |
1524 | if (Is(Classifier.Identifier) && !IsKeyword())
1525 | {
1526 | //Call on object
1527 | if (Expect(Classifier.Arrow))
1528 | {
1529 | Advance(2);
1530 | return ParseCall(new IdentifierNode(Peek(-2).Value));
1531 | }
1532 |
1533 | //Call on this
1534 | if (Expect(Classifier.LeftParenthesis))
1535 | {
1536 | return ParseCall(new IdentifierNode("this"));
1537 | }
1538 |
1539 | Advance();
1540 | return new IdentifierNode(Last.Value);
1541 | }
1542 |
1543 | if (Is(Classifier.DoubleQuestion))
1544 | {
1545 | Advance();
1546 | return new PlaceHolderNode();
1547 | }
1548 |
1549 | if (Is(Category.Literal))
1550 | {
1551 | Node node = null;
1552 | switch (Current.Kind)
1553 | {
1554 | case Classifier.IntLiteral:
1555 | node = new IntLiteralNode(Current);
1556 | break;
1557 |
1558 | case Classifier.BoolLiteral:
1559 | node = new BoolLiteralNode(Current);
1560 | break;
1561 |
1562 | case Classifier.StringLiteral:
1563 | node = new StringLiteralNode(Current);
1564 | break;
1565 |
1566 | case Classifier.DecLiteral:
1567 | node = new DecLiteralNode(Current);
1568 | break;
1569 | }
1570 |
1571 | if (node == null)
1572 | {
1573 | Error(ErrorStrings.MESSAGE_INVALID_LITERAL, Current.Value);
1574 | }
1575 |
1576 | Advance();
1577 |
1578 | if (Is(Classifier.Arrow))
1579 | {
1580 | Advance();
1581 | return ParseCall(node);
1582 | }
1583 |
1584 | return node;
1585 | }
1586 |
1587 | if (Is(Classifier.LeftBracket))
1588 | {
1589 | return ParseArrayOrLambda();
1590 | }
1591 |
1592 | if (Is(Classifier.Not) || Is(Classifier.Minus))
1593 | {
1594 | return ParseLeftHand(new OperationNodeNode(Current.Kind, Current.Value));
1595 | }
1596 |
1597 | //TODO: Rework calculations
1598 | return null;
1599 | }
1600 |
1601 | #endregion
1602 |
1603 | #endregion
1604 | }
1605 | }
--------------------------------------------------------------------------------
/src/Hades.Language/Parser/ParserGrammar.ebnf:
--------------------------------------------------------------------------------
1 | main =
2 | statement
3 | | assignment
4 | | ifBlock
5 | | forBlock
6 | | whileBlock
7 | | matchBlock
8 | | tryBlock
9 | | funcBlock
10 | | classBlock
11 | | structBlock
12 | | main
13 | ;
--------------------------------------------------------------------------------
/src/Hades.Runtime/Hades.Runtime.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Hades.Runtime/HadesRuntime.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Hades.Common;
5 | using Hades.Error;
6 | using Hades.Runtime.Objects;
7 | using Hades.Runtime.Values;
8 | using Hades.Syntax.Expression;
9 | using Hades.Syntax.Expression.Nodes;
10 | using Hades.Syntax.Expression.Nodes.LiteralNodes;
11 | using Hades.Syntax.Lexeme;
12 |
13 | namespace Hades.Runtime
14 | {
15 | public static class HadesRuntime
16 | {
17 | #region Helpers
18 |
19 | private static void Error(string error, params object[] format)
20 | {
21 | Error(string.Format(error, format));
22 | }
23 |
24 | private static void Error(string error)
25 | {
26 | throw new Exception(error);
27 | }
28 |
29 | #endregion
30 |
31 | public static Scope Run(RootNode rootNode, ref Scope scope)
32 | {
33 | foreach (var node in rootNode.Children)
34 | {
35 | if (node is VariableDeclarationNode)
36 | {
37 | var child = RunStatement(node, scope);
38 |
39 | if (scope.Variables.Any(a => a.Key == child.Name))
40 | {
41 | Error(ErrorStrings.MESSAGE_DUPLICATE_VARIABLE_DECLARATION, child.Name);
42 | }
43 |
44 | scope.Variables.Add(child.Name, (child, AccessModifier.Private));
45 | return child;
46 | }
47 | }
48 |
49 | if (rootNode.Children.Count == 1)
50 | {
51 | return RunStatement(rootNode.Children.First(), scope);
52 | }
53 |
54 | return new Scope();
55 | }
56 |
57 | public static Scope RunStatement(Node node, Scope parent)
58 | {
59 | Scope scope = null;
60 |
61 | //Here are the literal values. These return a literal scope + all the built-ins the literal values have
62 | if (node is BoolLiteralNode boolLiteral)
63 | {
64 | // ReSharper disable once UseObjectOrCollectionInitializer
65 | scope = new Scope();
66 | scope.Value = new BoolValue{Value = boolLiteral.Value};
67 | scope.Functions = BuiltIns.BOOL_BUILT_INS;
68 | scope.Datatype = Datatype.BOOL;
69 | return scope;
70 | }
71 |
72 | //NOTE/TODO: Btw.: Annotations become important when dealing with var declarations, functions, classes and structs
73 |
74 | if (node is VariableDeclarationNode variableDeclaration)
75 | {
76 | scope = new Scope
77 | {
78 | Datatype = variableDeclaration.Datatype.GetValueOrDefault(Datatype.NONE),
79 | Name = variableDeclaration.Name,
80 | SpecificType = variableDeclaration.SpecificType,
81 | Mutable = variableDeclaration.Mutable,
82 | Dynamic = variableDeclaration.Dynamic,
83 | Nullable = variableDeclaration.Nullable
84 | };
85 |
86 | if (variableDeclaration.Array)
87 | {
88 | var size = -1;
89 | if (variableDeclaration.InfiniteArray)
90 | {
91 | //TODO: Multidimensional array
92 | var result = RunStatement(variableDeclaration.ArraySize, parent);
93 |
94 | if (result == null)
95 | {
96 | Error(ErrorStrings.MESSAGE_UNKNOWN_RUNTIME_EXCEPTION, variableDeclaration.ToString());
97 | }
98 |
99 | if (!(result.Value is IntValue))
100 | {
101 | throw new Exception();
102 | }
103 | }
104 | scope.Value = new ListValue {Size = size};
105 | }
106 |
107 | scope.Value = RunStatement(variableDeclaration.Assignment, parent);
108 | }
109 |
110 | return scope;
111 | }
112 | }
113 | }
--------------------------------------------------------------------------------
/src/Hades.Runtime/Objects/BuiltIns.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text;
3 | using Hades.Common;
4 | using Hades.Runtime.Values;
5 | using Hades.Syntax.Expression.Nodes;
6 | using Hades.Syntax.Expression.Nodes.LiteralNodes;
7 | // ReSharper disable InconsistentNaming
8 |
9 | namespace Hades.Runtime.Objects
10 | {
11 | public class BuiltIns
12 | {
13 | private static string CreateMD5(string input)
14 | {
15 | // Use input string to calculate MD5 hash
16 | using (var md5 = System.Security.Cryptography.MD5.Create())
17 | {
18 | var inputBytes = Encoding.ASCII.GetBytes(input);
19 | var hashBytes = md5.ComputeHash(inputBytes);
20 |
21 | // Convert the byte array to hexadecimal string
22 | var sb = new StringBuilder();
23 | foreach (var t in hashBytes)
24 | {
25 | sb.Append(t.ToString("X2"));
26 | }
27 | return sb.ToString();
28 | }
29 | }
30 |
31 | public static readonly Dictionary> OBJECT_BUILT_INS = new Dictionary>
32 | {
33 | {
34 | "toString",
35 | new List
36 | {
37 | new Scope
38 | {
39 | Name = "toString",
40 | IsNativeFunction = true,
41 | NativeFunctionSignature = new Dictionary{{"dst", Datatype.NONE}},
42 | NativeFunction = (scopes, scope) => HadesRuntime.RunStatement(new CallNode{Source = new IdentifierNode("this"), Target = new IdentifierNode("toString")}, scopes[0])
43 | }
44 | }
45 |
46 | //toString has to be implemented specifically for each value scope; this is why I call toString on the parameter
47 | //I am doing virtual abstract methods...pretty f-ing cool 😎
48 | },
49 | {
50 | "hash",
51 | new List
52 | {
53 | new Scope
54 | {
55 | Name = "hash",
56 | IsNativeFunction = true,
57 | NativeFunctionSignature = new Dictionary{{"dst", Datatype.NONE}},
58 | NativeFunction = (scopes, scope) => HadesRuntime.RunStatement(new CallNode{Source = new IdentifierNode("this"), Target = new IdentifierNode("hash")}, scopes[0])
59 | },
60 | new Scope
61 | {
62 | Name = "hash",
63 | IsNativeFunction = true,
64 | NativeFunctionSignature = new Dictionary(),
65 | NativeFunction = (scopes, scope) => HadesRuntime.RunStatement(new StringLiteralNode(CreateMD5((HadesRuntime.RunStatement(new CallNode{Source = new IdentifierNode("this"), Target = new IdentifierNode("toString")}, scope).Value as StringValue)?.Value)), scope)
66 | }
67 | }
68 | },
69 | {
70 | "nameof",
71 | new List
72 | {
73 | new Scope
74 | {
75 | Name = "nameof",
76 | IsNativeFunction = true,
77 | NativeFunctionSignature = new Dictionary{{"dst", Datatype.NONE}},
78 | NativeFunction = (scopes, scope) => HadesRuntime.RunStatement(new StringLiteralNode(scopes[0].Name), scope)
79 | },
80 | new Scope
81 | {
82 | Name = "nameof",
83 | IsNativeFunction = true,
84 | NativeFunctionSignature = new Dictionary(),
85 | NativeFunction = (scopes, scope) => HadesRuntime.RunStatement(new StringLiteralNode(scope.Name), scope)
86 | }
87 | }
88 | },
89 | {
90 | "type",
91 | new List
92 | {
93 | new Scope
94 | {
95 | Name = "type",
96 | IsNativeFunction = true,
97 | NativeFunctionSignature = new Dictionary{{"dst", Datatype.NONE}},
98 | NativeFunction = (scopes, scope) => HadesRuntime.RunStatement(new StringLiteralNode(scopes[0].Datatype.ToString().ToLower()), scope)
99 | },
100 | new Scope
101 | {
102 | Name = "type",
103 | IsNativeFunction = true,
104 | NativeFunctionSignature = new Dictionary(),
105 | NativeFunction = (scopes, scope) => HadesRuntime.RunStatement(new StringLiteralNode(scope.Datatype.ToString().ToLower()), scope)
106 | }
107 | }
108 | //TODO: equals
109 | }
110 | };
111 |
112 | private static Dictionary> _bool_built_ins;
113 |
114 | public static Dictionary> BOOL_BUILT_INS
115 | {
116 | get
117 | {
118 | if (_bool_built_ins != null) return _bool_built_ins;
119 |
120 | _bool_built_ins = OBJECT_BUILT_INS;
121 |
122 | //TODO: Equals
123 |
124 | _bool_built_ins["toString"].Add(new Scope
125 | {
126 | IsNativeFunction = true,
127 | NativeFunctionSignature = new Dictionary(),
128 | NativeFunction = (scopes, scope) =>
129 | {
130 | /*
131 | * Okay...let's unroll what happens here. toString returns a string.
132 | * As we all know, a string has built-ins.
133 | * So does the string that toString returns (obviously)
134 | * So to get all of these built-ins, we have to run a literal node through the runtime
135 | */
136 | // ReSharper disable once ConvertToLambdaExpression
137 | return HadesRuntime.RunStatement(
138 | new StringLiteralNode((scope.Value as BoolValue)?.Value.ToString().ToLower()),
139 | scope);
140 | }
141 | });
142 |
143 | return _bool_built_ins;
144 | }
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/src/Hades.Runtime/Scope.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Hades.Common;
4 | using Hades.Syntax.Expression;
5 |
6 | namespace Hades.Runtime
7 | {
8 | public class Scope : ScopeValue
9 | {
10 | //Except for classes and structs, every variable is private
11 | //I figured this is the best way to store variables
12 | //A variable needs to be identified, primarily by its name
13 | //A variable has an access modifier (in every scope - even in scopes where you can't set an access modifier -> then it's private)
14 | public Dictionary Variables { get; set; } = new Dictionary();
15 |
16 | //A scope needs to have a datatype, the exec scope has the datatype "NONE",
17 | public Datatype Datatype { get; set; }
18 |
19 | //Classes, structs, variables and protos have names
20 | //Lambdas and functions also have names
21 | //When a function calls another function and the runtime can't find the other function
22 | //(For whatever reason)
23 | //This is the method of last resort (the runtime checks if the function that needs to be invoked is maybe this very function itself)
24 | //Note: I'll probably look at the parent scope for overloads first, if no overloads fits the invocation, this has to be the function to call
25 | //If it's still not the function to call -> ¯\_(ツ)_/¯
26 | public string Name { get; set; }
27 |
28 | //A scope can be an object, struct or proto, these three can have a specific type
29 | public string SpecificType { get; set; }
30 |
31 | //A scope can have multiple functions, a function can have multiple overloads
32 | public Dictionary> Functions { get; set; } = new Dictionary>();
33 |
34 | //A scope can contain classes
35 | public List Classes { get; set; } = new List();
36 |
37 | //A scope can have structs
38 | public List Structs { get; set; } = new List();
39 |
40 | //A scope can have code (is executable)
41 | public Node Code { get; set; }
42 |
43 | //A scope can be a variable, a variable can have the value of a scope (instance of an object), a proto (code) or a literal (ValueScope)
44 | public ScopeValue Value { get; set; }
45 |
46 | //A scope can be a variable, a variable can be nullable
47 | public bool Nullable { get; set; }
48 |
49 | //A scope can be a variable, a variable can be mutable
50 | public bool Mutable { get; set; }
51 |
52 | //A scope can be a variable, a variable can be dynamic
53 | public bool Dynamic { get; set; }
54 |
55 | //I know...it's a bit weird that this is here...but tbh...I didn't have the nerve to deal with polymorphism in this
56 | //Like...for real...none whatsoever
57 | //Not doing it
58 | //Nuh uh
59 | //Also polymorphism sux and makes everything slower. Only reason I used it in the parser is because there are *too fucking many* attributes to keep track of
60 | public bool IsNativeFunction { get; set; }
61 |
62 | //This exposes the function signature so I can check if the user is not being a stupid asshat
63 | //The goal of this is preventing stupid asshatery
64 | public Dictionary NativeFunctionSignature { get; set; }
65 |
66 | //Honestly...this is actually not a bad solution
67 | //No CLR reflection, no weird casting shit
68 | //Just plain old func
69 | //We stan a simple queen
70 | public Func NativeFunction { get; set; }
71 | }
72 | }
--------------------------------------------------------------------------------
/src/Hades.Runtime/ScopeValue.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Runtime
2 | {
3 | public interface ScopeValue
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/src/Hades.Runtime/Values/BoolValue.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Runtime.Values
2 | {
3 | public class BoolValue : LiteralValue, ScopeValue
4 | {
5 | public override string ToString()
6 | {
7 | return Value.ToString().ToLower();
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Hades.Runtime/Values/DecValue.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Runtime.Values
2 | {
3 | public class DecValue : LiteralValue, ScopeValue
4 | {
5 |
6 | }
7 | }
--------------------------------------------------------------------------------
/src/Hades.Runtime/Values/IntValue.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Runtime.Values
2 | {
3 | public class IntValue : LiteralValue, ScopeValue
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/src/Hades.Runtime/Values/ListValue.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Hades.Runtime.Values
4 | {
5 | //A scope can have be an array variable
6 | //An array variable can be n-dimensional
7 | public class ListValue : LiteralValue>, ScopeValue
8 | {
9 | public int Size { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Hades.Runtime/Values/LiteralValue.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Runtime.Values
2 | {
3 | public abstract class LiteralValue
4 | {
5 | public T Value { get; set; }
6 |
7 | public override string ToString()
8 | {
9 | return Value.ToString();
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/Hades.Runtime/Values/StringValue.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Runtime.Values
2 | {
3 | public class StringValue : LiteralValue, ScopeValue
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/AccessModifier.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression
2 | {
3 | public enum AccessModifier
4 | {
5 | Private = 0,
6 | Protected,
7 | Public
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/BlockNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Hades.Syntax.Expression
5 | {
6 | public abstract class BlockNode : Node
7 | {
8 | protected BlockNode(Classifier classifier) : base(classifier)
9 | {
10 | }
11 |
12 | public List Children { get; set; } = new List();
13 |
14 | protected override string ToStr()
15 | {
16 | var str = "";
17 | foreach (var child in Children)
18 | {
19 | str += string.Join('\n', child.ToString().Split('\n').Select(a => $" {a}")) + "\n";
20 | }
21 |
22 | if (!string.IsNullOrEmpty(str))
23 | {
24 | str = str.Substring(0, str.Length - 1);
25 | }
26 |
27 | return str;
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Classifier.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression
2 | {
3 | public enum Classifier
4 | {
5 | Root,
6 | With,
7 | VariableDeclaration,
8 | IntLiteral,
9 | BoolLiteral,
10 | StringLiteral,
11 | DecLiteral,
12 | Call,
13 | Identifier,
14 | Operation,
15 | NullCondition,
16 | Exception,
17 | Lambda,
18 | Put,
19 | Function,
20 | While,
21 | Command,
22 | ValueCall,
23 | Assignment,
24 | For,
25 | LeftHand,
26 | RightHand,
27 | Misc,
28 | ListLiteral,
29 | NullLiteral,
30 | MultiDimensionalArrayAccess,
31 | InlineIf,
32 | Pipeline,
33 | Placeholder,
34 | If,
35 | TryCatch,
36 | Class,
37 | Match,
38 | ArrayAccess,
39 | Struct
40 | }
41 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/LiteralNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression
2 | {
3 | public abstract class LiteralNode : Node
4 | {
5 | protected LiteralNode(Classifier classifier) : base(classifier)
6 | {
7 | }
8 |
9 | public T Value { get; set; }
10 |
11 | protected override string ToStr()
12 | {
13 | return string.Empty;
14 | }
15 |
16 | public override string ToString()
17 | {
18 | return $"Value: {Value}";
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Node.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Hades.Syntax.Expression
4 | {
5 | public abstract class Node
6 | {
7 | protected Node(Classifier classifier)
8 | {
9 | Classifier = classifier;
10 | }
11 |
12 | public Classifier Classifier { get; }
13 | public Dictionary Annotations { get; } = new Dictionary();
14 |
15 | protected abstract string ToStr();
16 |
17 | public override string ToString()
18 | {
19 | return $"{GetType().Name.Replace("Node", "")} => {ToStr()}";
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/ArrayAccessNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes
2 | {
3 | public class ArrayAccessNode : Node
4 | {
5 | public ArrayAccessNode() : base(Classifier.ArrayAccess)
6 | {
7 | }
8 |
9 | public Node Index { get; set; }
10 | public Node BaseNode { get; set; }
11 |
12 | protected override string ToStr()
13 | {
14 | return $"Index [{Index}] from ({BaseNode})";
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/AssignmentNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes
2 | {
3 | public class AssignmentNode : Node
4 | {
5 | public AssignmentNode() : base(Classifier.Assignment)
6 | {
7 | }
8 |
9 | public Node Variable { get; set; }
10 | public Node Value { get; set; }
11 |
12 | protected override string ToStr()
13 | {
14 | return $"({Value}) to ({Variable})";
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/ClassNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Hades.Common.Extensions;
4 |
5 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
6 | {
7 | public class ClassNode : Node
8 | {
9 | public ClassNode() : base(Classifier.Class)
10 | {
11 | }
12 |
13 | public AccessModifier AccessModifier { get; set; }
14 | public bool Fixed { get; set; }
15 | public List Parents { get; } = new List();
16 | public string Name { get; set; }
17 | public List PublicVariables { get; } = new List();
18 | public List PrivateVariables { get; } = new List();
19 | public List ProtectedVariables { get; } = new List();
20 | public List Functions { get; } = new List();
21 | public List Classes { get; } = new List();
22 | public List Constructors { get; } = new List();
23 | public List Structs { get; } = new List();
24 |
25 | protected override string ToStr()
26 | {
27 | var privateVars = string.Join("\n ", PrivateVariables);
28 |
29 | if (privateVars != string.Empty)
30 | {
31 | privateVars = $"\n Private variables:\n {privateVars}";
32 | }
33 |
34 | var protectedVars = string.Join("\n ", ProtectedVariables);
35 |
36 | if (protectedVars != string.Empty)
37 | {
38 | protectedVars = $"\n Protected variables:\n {protectedVars}";
39 | }
40 |
41 | var publicVars = string.Join("\n ", PublicVariables);
42 |
43 | if (publicVars != string.Empty)
44 | {
45 | publicVars = $"\n Public variables:\n {publicVars}";
46 | }
47 |
48 | var functions = Functions.Map(a => a.ToString().Replace("\n", "\n ")).ToList();
49 | var fn = string.Empty;
50 |
51 | if (functions.Count != 0)
52 | {
53 | fn = $"\n Functions:\n {string.Join("\n ", functions)}";
54 | }
55 |
56 | var constructors = Constructors.Map(a => a.ToString().Replace("\n", "\n ")).ToList();
57 | var ctor = string.Empty;
58 |
59 | if (constructors.Count != 0)
60 | {
61 | ctor = $"\n Constructors:\n {string.Join("\n ", constructors)}";
62 | }
63 |
64 | var structs = Structs.Map(a => a.ToString().Replace("\n", "\n ")).ToList();
65 | var stcts = string.Empty;
66 |
67 | if (constructors.Count != 0)
68 | {
69 | stcts = $"\n Structs:\n {string.Join("\n ", structs)}";
70 | }
71 |
72 | var inherits = string.Empty;
73 | if (Parents.Count != 0)
74 | {
75 | inherits = $" inherits from {string.Join(", ", Parents)}";
76 | }
77 |
78 | var fix = Fixed ? " fixed" : "";
79 |
80 | return $"{Name}{fix}{inherits}{privateVars}{protectedVars}{publicVars}{ctor}{fn}{stcts}";
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/ForNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
2 | {
3 | public class ForNode : BlockNode
4 | {
5 | public ForNode() : base(Classifier.For)
6 | {
7 | }
8 |
9 | public Node Variable { get; set; }
10 | public Node Source { get; set; }
11 |
12 | protected override string ToStr()
13 | {
14 | return $"Source: ({Source}) into Variable: ({Variable})\n{base.ToStr()}";
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/FunctionNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Hades.Common;
3 | using Hades.Syntax.Expression.Nodes.BlockNodes.Util;
4 |
5 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
6 | {
7 | public class FunctionNode : BlockNode
8 | {
9 | public FunctionNode() : base(Classifier.Function)
10 | {
11 | }
12 |
13 | public bool Override { get; set; }
14 | public bool Fixed { get; set; }
15 | public string Name { get; set; }
16 | public List<(Node Key, Datatype? Value, string SpecificType)> Parameters { get; set; } = new List<(Node Key, Datatype? Value, string SpecificType)>();
17 | public Node Guard { get; set; }
18 | public AccessModifier AccessModifier { get; set; }
19 | public bool Extension { get; set; }
20 | public (string specificType, Datatype dt) ExtensionType { get; set; }
21 |
22 | protected override string ToStr()
23 | {
24 | var args = ParameterWriter.PrintParameters(Parameters);
25 |
26 | if (!string.IsNullOrEmpty(args))
27 | {
28 | args = args.Substring(0, args.Length - 1);
29 | args = $" with parameters {args}";
30 | }
31 |
32 | var accessModifier = AccessModifier.ToString().ToLower();
33 | var guard = Guard != null ? " with guard (" + Guard + ")" : "";
34 | var over = Override ? "override " : "";
35 | var fix = Fixed ? " fixed " : " ";
36 |
37 | var extends = Extension ? (string.IsNullOrEmpty(ExtensionType.specificType) ? $" extends {ExtensionType.dt.ToString().ToLower()}" : $" extends {ExtensionType.dt.ToString().ToLower()}::{ExtensionType.specificType}") : " <";
38 |
39 | var str = $"{accessModifier}{fix}{over}{Name}{extends}{args}{guard}\n{base.ToStr()}";
40 | return str;
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/GenericBlockNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
2 | {
3 | public class GenericBlockNode : BlockNode
4 | {
5 | public GenericBlockNode() : base(Classifier.Misc)
6 | {
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/IfNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
5 | {
6 | public class IfNode : Node
7 | {
8 | public IfNode() : base(Classifier.If)
9 | {
10 | }
11 |
12 | public Node Condition { get; set; }
13 | public BlockNode If { get; set; }
14 | public List ElseIfNodes { get; } = new List();
15 | public BlockNode Else { get; set; }
16 |
17 | protected override string ToStr()
18 | {
19 | var str = "";
20 | foreach (var child in If.Children)
21 | {
22 | str += string.Join('\n', child.ToString().Split('\n').Select(a => $" {a}")) + "\n";
23 | }
24 |
25 | if (!string.IsNullOrEmpty(str))
26 | {
27 | str = str.Substring(0, str.Length - 1);
28 | }
29 |
30 | var elseIfStr = string.Empty;
31 |
32 | foreach (var elseIfNode in ElseIfNodes)
33 | {
34 | elseIfStr += $"\nElse {elseIfNode}";
35 | }
36 |
37 | var elseStr = string.Empty;
38 |
39 | if (Else != null)
40 | {
41 | foreach (var elseChild in Else.Children)
42 | {
43 | elseStr += string.Join('\n', elseChild.ToString().Split('\n').Select(a => $" {a}")) + "\n";
44 | }
45 | }
46 |
47 | if (!string.IsNullOrEmpty(elseStr))
48 | {
49 | elseStr = $"\nElse => \n{elseStr.Substring(0, elseStr.Length - 1)}";
50 | }
51 |
52 | return $"Condition: ({Condition})\n{str}{elseIfStr}{elseStr}";
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/LambdaNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Hades.Common;
3 | using Hades.Syntax.Expression.Nodes.BlockNodes.Util;
4 |
5 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
6 | {
7 | public class LambdaNode : BlockNode
8 | {
9 | public LambdaNode() : base(Classifier.Lambda)
10 | {
11 | }
12 |
13 | public List<(Node Key, Datatype? Value, string SpecificType)> Parameters { get; } = new List<(Node Key, Datatype? Value, string SpecificType)>();
14 | public bool Complex { get; set; }
15 |
16 | protected override string ToStr()
17 | {
18 | var args = ParameterWriter.PrintParameters(Parameters);
19 |
20 | if (!string.IsNullOrEmpty(args))
21 | {
22 | args = args.Substring(0, args.Length - 1);
23 | args = $" with parameters {args}";
24 | }
25 |
26 | var complex = Complex ? "Complex" : "Simple";
27 | var str = $"{complex} lambda{args}\n{base.ToStr()}";
28 | return str;
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/MatchNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
5 | {
6 | public class MatchNode : Node
7 | {
8 | public MatchNode() : base(Classifier.Match)
9 | {
10 | }
11 |
12 | public Node Match { get; set; }
13 | public bool First { get; set; }
14 | public Dictionary Statements { get; set; } = new Dictionary();
15 |
16 | protected override string ToStr()
17 | {
18 | var str = Match == null ? "any" : $"({Match})";
19 |
20 | var functions = Statements.Select(a => $"* ({a.Key})" + " => " + a.Value.ToString().Replace("\n", "\n ")).ToList();
21 | var fn = string.Empty;
22 |
23 | if (functions.Count != 0)
24 | {
25 | fn = $"\n {string.Join("\n ", functions)}";
26 | }
27 |
28 | var first = First ? " first" : "";
29 | return str + first + " to " + fn;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/StructNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
4 | {
5 | public class StructNode : Node
6 | {
7 | public StructNode() : base(Classifier.Struct)
8 | {
9 | }
10 |
11 | public string Name { get; set; }
12 | public AccessModifier AccessModifier { get; set; }
13 | public List PublicVariables { get; } = new List();
14 | public List PrivateVariables { get; } = new List();
15 |
16 | protected override string ToStr()
17 | {
18 | var privateVars = string.Join("\n ", PrivateVariables);
19 |
20 | if (privateVars != string.Empty)
21 | {
22 | privateVars = $"\n Private variables:\n {privateVars}";
23 | }
24 |
25 | var publicVars = string.Join("\n ", PublicVariables);
26 |
27 | if (publicVars != string.Empty)
28 | {
29 | publicVars = $"\n Public variables:\n {publicVars}";
30 | }
31 |
32 | return $"{Name}{privateVars}{publicVars}";
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/TryCatchElseNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Hades.Common;
4 |
5 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
6 | {
7 | public class TryCatchElseNode : Node
8 | {
9 | public TryCatchElseNode() : base(Classifier.TryCatch)
10 | {
11 | }
12 |
13 | public BlockNode Try { get; set; }
14 | public BlockNode Else { get; set; }
15 | public List Catch { get; } = new List();
16 |
17 | protected override string ToStr()
18 | {
19 | var tryStr = "";
20 | foreach (var child in Try.Children)
21 | {
22 | tryStr += string.Join('\n', child.ToString().Split('\n').Select(a => $" {a}")) + "\n";
23 | }
24 |
25 | if (!string.IsNullOrEmpty(tryStr))
26 | {
27 | tryStr = $"\n Try:\n{tryStr.Substring(0, tryStr.Length - 1)}";
28 | }
29 |
30 | var catchString = string.Empty;
31 |
32 | foreach (var catchNode in Catch)
33 | {
34 | var specificType = catchNode.Name;
35 |
36 | if (catchNode.Datatype != Datatype.NONE)
37 | {
38 | specificType = $"{catchNode.Datatype.ToString().ToLower()} {catchNode.Name}";
39 | }
40 |
41 | if (!string.IsNullOrEmpty(catchNode.SpecificType))
42 | {
43 | specificType = $"{catchNode.Datatype.ToString().ToLower()}({catchNode.SpecificType}) {catchNode.Name}";
44 | }
45 |
46 | catchString += $"\n Catch {specificType}\n";
47 |
48 | foreach (var blockChild in catchNode.Block.Children)
49 | {
50 | catchString += string.Join('\n', blockChild.ToString().Split('\n').Select(a => $" {a}")) + "\n";
51 | }
52 |
53 | catchString = catchString.TrimEnd('\n');
54 | }
55 |
56 | var elseStr = "";
57 | foreach (var child in Else.Children)
58 | {
59 | elseStr += string.Join('\n', child.ToString().Split('\n').Select(a => $" {a}")) + "\n";
60 | }
61 |
62 | if (!string.IsNullOrEmpty(elseStr))
63 | {
64 | elseStr = $"\n Else:\n{elseStr.Substring(0, elseStr.Length - 1)}";
65 | }
66 |
67 | return $"{tryStr}{catchString}{elseStr}";
68 | }
69 |
70 | public struct CatchBlock
71 | {
72 | public BlockNode Block;
73 | public string SpecificType;
74 | public Datatype Datatype;
75 | public string Name;
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/Util/ParameterWriter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Hades.Common;
3 |
4 | namespace Hades.Syntax.Expression.Nodes.BlockNodes.Util
5 | {
6 | public static class ParameterWriter
7 | {
8 | public static string PrintParameters(IEnumerable<(Node Key, Datatype? Value, string SpecificType)> parameters)
9 | {
10 | var args = "";
11 |
12 | foreach (var parameter in parameters)
13 | {
14 | if (parameter.Value != null)
15 | {
16 | if (parameter.SpecificType != null)
17 | {
18 | args += $"(({parameter.Key}):{parameter.Value.ToString().ToLower()}[{parameter.SpecificType}]),";
19 | }
20 | else
21 | {
22 | args += $"(({parameter.Key}):{parameter.Value.ToString().ToLower()}),";
23 | }
24 | }
25 | else
26 | {
27 | args += $"({parameter.Key}),";
28 | }
29 | }
30 |
31 | return args;
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/VarBlockNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
4 | {
5 | public class VarBlockNode : Node
6 | {
7 | public AccessModifier AccessModifier { get; set; }
8 | public List VariableDeclarationNodes { get; } = new List();
9 |
10 | public VarBlockNode() : base(Classifier.Misc)
11 | {
12 | }
13 |
14 | protected override string ToStr()
15 | {
16 | return "";
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/BlockNodes/WhileNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes.BlockNodes
2 | {
3 | public class WhileNode : BlockNode
4 | {
5 | public WhileNode() : base(Classifier.While)
6 | {
7 | }
8 |
9 | public Node Condition { get; set; }
10 |
11 | protected override string ToStr()
12 | {
13 | return $"Condition: ({Condition})\n{base.ToStr()}";
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/CallNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Hades.Syntax.Expression.Nodes.LiteralNodes;
3 |
4 | namespace Hades.Syntax.Expression.Nodes
5 | {
6 | public class CallNode : Node
7 | {
8 | public CallNode() : base(Classifier.Call)
9 | {
10 | }
11 |
12 | public Node Source { get; set; }
13 | public Node Target { get; set; }
14 | public Dictionary Parameters { get; } = new Dictionary();
15 |
16 | protected override string ToStr()
17 | {
18 | var str = "";
19 |
20 | foreach (var keyValuePair in Parameters)
21 | {
22 | if (!string.IsNullOrEmpty(keyValuePair.Value))
23 | {
24 | str += $"({keyValuePair.Value}=";
25 | }
26 | else
27 | {
28 | str += "(";
29 | }
30 |
31 | str += $"{keyValuePair.Key}),";
32 | }
33 |
34 | if (!string.IsNullOrEmpty(str))
35 | {
36 | str = str.Substring(0, str.Length - 1);
37 | str = $" with parameters {str}";
38 | }
39 |
40 | return $"({Target}) on ({Source}){str}";
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/InlineIf.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes
2 | {
3 | public class InlineIf : Node
4 | {
5 | public Node Condition;
6 | public Node Falsy;
7 | public Node Truthy;
8 |
9 | public InlineIf() : base(Classifier.InlineIf)
10 | {
11 | }
12 |
13 | protected override string ToStr()
14 | {
15 | return $"(if ({Condition}), ({Truthy}), otherwise ({Falsy}))";
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/ArgsNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
2 | {
3 | public class ArgsNode : LiteralNode
4 | {
5 | public ArgsNode(string identifier) : base(Classifier.Identifier)
6 | {
7 | Value = identifier;
8 | }
9 |
10 | public override string ToString()
11 | {
12 | return $"Varargs Value: {Value}";
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/BoolLiteralNode.cs:
--------------------------------------------------------------------------------
1 | using Hades.Syntax.Lexeme;
2 |
3 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
4 | {
5 | public class BoolLiteralNode : LiteralNode
6 | {
7 | public BoolLiteralNode(Token token) : base(Classifier.BoolLiteral)
8 | {
9 | Value = bool.Parse(token.Value);
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/CommandNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
2 | {
3 | public class CommandNode : LiteralNode
4 | {
5 | public CommandNode(string command) : base(Classifier.Command)
6 | {
7 | Value = command;
8 | }
9 |
10 | public override string ToString()
11 | {
12 | return $"Command: {Value}";
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/DecLiteralNode.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using Hades.Syntax.Lexeme;
3 |
4 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
5 | {
6 | public class DecLiteralNode : LiteralNode
7 | {
8 | public DecLiteralNode(Token token) : base(Classifier.DecLiteral)
9 | {
10 | Value = decimal.Parse(token.Value, CultureInfo.InvariantCulture);
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/IdentifierNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
2 | {
3 | public class IdentifierNode : LiteralNode
4 | {
5 | public IdentifierNode(string identifier) : base(Classifier.Identifier)
6 | {
7 | Value = identifier;
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/IntLiteralNode.cs:
--------------------------------------------------------------------------------
1 | using Hades.Syntax.Lexeme;
2 |
3 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
4 | {
5 | public class IntLiteralNode : LiteralNode
6 | {
7 | public IntLiteralNode(Token token) : base(Classifier.IntLiteral)
8 | {
9 | Value = int.Parse(token.Value);
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/ListNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
5 | {
6 | public class ListNode : LiteralNode>
7 | {
8 | public ListNode() : base(Classifier.ListLiteral)
9 | {
10 | }
11 |
12 | public override string ToString()
13 | {
14 | return $"Value: {{{string.Join(",", Value.Select(a => $"({a})").ToList())}}}";
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/MultiDimensionalArrayNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
4 | {
5 | public class MultiDimensionalArrayNode : LiteralNode>
6 | {
7 | public MultiDimensionalArrayNode() : base(Classifier.MultiDimensionalArrayAccess)
8 | {
9 | Value = new List();
10 | }
11 |
12 | public override string ToString()
13 | {
14 | return string.Join(",", Value);
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/NullValueNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
2 | {
3 | public class NullValueNode : LiteralNode
4 | {
5 | public NullValueNode() : base(Classifier.NullLiteral)
6 | {
7 | Value = "NULL";
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/OperationNodeNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
2 | {
3 | public class OperationNodeNode : LiteralNode
4 | {
5 | public readonly string Representation;
6 |
7 | public OperationNodeNode(Lexeme.Classifier classifier, string val) : base(Classifier.Operation)
8 | {
9 | Value = classifier;
10 | Representation = val;
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/PlaceHolderNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
2 | {
3 | public class PlaceHolderNode : LiteralNode
4 | {
5 | public PlaceHolderNode() : base(Classifier.Placeholder)
6 | {
7 | Value = "Placeholder";
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/LiteralNodes/StringLiteralNode.cs:
--------------------------------------------------------------------------------
1 | using Hades.Syntax.Lexeme;
2 |
3 | namespace Hades.Syntax.Expression.Nodes.LiteralNodes
4 | {
5 | public class StringLiteralNode : LiteralNode
6 | {
7 | public StringLiteralNode(string value) : base(Classifier.StringLiteral)
8 | {
9 | Value = value;
10 | }
11 |
12 | public StringLiteralNode(Token token) : base(Classifier.StringLiteral)
13 | {
14 | Value = token.Value;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/NoVariableNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes
2 | {
3 | public class NoVariableNode : LiteralNode
4 |
5 | {
6 | public NoVariableNode() : base(Classifier.Misc)
7 | {
8 | Value = "NONE";
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/NullConditionNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes
2 | {
3 | public class NullConditionNode : Node
4 | {
5 | public NullConditionNode() : base(Classifier.NullCondition)
6 | {
7 | }
8 |
9 | public Node Condition { get; set; }
10 | public Node Operation { get; set; }
11 |
12 | protected override string ToStr()
13 | {
14 | return $"Return ({Operation}) if ({Condition}) evaluates to null";
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/OperationNode.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Hades.Syntax.Expression.Nodes.LiteralNodes;
3 |
4 | namespace Hades.Syntax.Expression.Nodes
5 | {
6 | public class OperationNode : Node
7 | {
8 | public OperationNode() : base(Classifier.Operation)
9 | {
10 | }
11 |
12 | public List Operations { get; set; } = new List();
13 |
14 | protected override string ToStr()
15 | {
16 | var str = "";
17 |
18 | foreach (var operation in Operations)
19 | {
20 | switch (operation)
21 | {
22 | case OperationNode _:
23 | str += $"[{operation}]";
24 | break;
25 | case OperationNodeNode operationNode:
26 | str += $" {operationNode.Representation} ";
27 | break;
28 | default:
29 | str += $"({operation})";
30 | break;
31 | }
32 | }
33 |
34 | return str;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/ParenthesesNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes
2 | {
3 | public class ParenthesesNode : Node
4 | {
5 | public ParenthesesNode() : base(Classifier.Misc)
6 | {
7 | }
8 |
9 | public Node Node { get; set; }
10 |
11 | protected override string ToStr()
12 | {
13 | return "";
14 | }
15 |
16 | public override string ToString()
17 | {
18 | return $"[{Node}]";
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/PipelineNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes
2 | {
3 | public class PipelineNode : Node
4 | {
5 | public Node Destination;
6 | public Node Source;
7 |
8 | public PipelineNode() : base(Classifier.Pipeline)
9 | {
10 | }
11 |
12 | protected override string ToStr()
13 | {
14 | return "";
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/PutNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes
2 | {
3 | public class PutNode : Node
4 | {
5 | public PutNode() : base(Classifier.Put)
6 | {
7 | }
8 |
9 | public Node Statement { get; set; }
10 |
11 | protected override string ToStr()
12 | {
13 | return $"Statement: ({Statement})";
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/RaiseNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes
2 | {
3 | public class RaiseNode : Node
4 | {
5 | public RaiseNode() : base(Classifier.Exception)
6 | {
7 | }
8 |
9 | public Node Exception { get; set; }
10 |
11 | protected override string ToStr()
12 | {
13 | return $"Raise exception ({Exception})";
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/SideNode.cs:
--------------------------------------------------------------------------------
1 | using Hades.Syntax.Expression.Nodes.LiteralNodes;
2 |
3 | namespace Hades.Syntax.Expression.Nodes
4 | {
5 | public enum Side
6 | {
7 | LEFT,
8 | RIGHT
9 | }
10 |
11 | public class SideNode : Node
12 | {
13 | private readonly Node BaseNode;
14 | private readonly OperationNodeNode Operation;
15 | private readonly Side Side;
16 |
17 | public SideNode(Node baseNode, OperationNodeNode operation, Side side) : base(side == Side.LEFT ? Classifier.LeftHand : Classifier.RightHand)
18 | {
19 | Side = side;
20 | BaseNode = baseNode;
21 | Operation = operation;
22 | }
23 |
24 | protected override string ToStr()
25 | {
26 | var side = Side == Side.LEFT ? "LeftHand" : "RightHand";
27 | return $"{side} {Operation.Representation} on ({BaseNode})";
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/ValueCallNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression.Nodes
2 | {
3 | public class ValueCallNode : Node
4 | {
5 | public ValueCallNode() : base(Classifier.ValueCall)
6 | {
7 | }
8 |
9 | public Node Source { get; set; }
10 | public Node Target { get; set; }
11 |
12 | protected override string ToStr()
13 | {
14 | return $"Variable ({Target}) from ({Source})";
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/VariableDeclarationNode.cs:
--------------------------------------------------------------------------------
1 | using Hades.Common;
2 |
3 | namespace Hades.Syntax.Expression.Nodes
4 | {
5 | public class VariableDeclarationNode : Node
6 | {
7 | public VariableDeclarationNode() : base(Classifier.VariableDeclaration)
8 | {
9 | }
10 |
11 | public bool Mutable { get; set; }
12 | public string Name { get; set; }
13 |
14 | // ReSharper disable once MemberCanBePrivate.Global
15 | public Datatype? Datatype { get; set; }
16 | public bool Array { get; set; }
17 | public Node ArraySize { get; set; }
18 | public bool InfiniteArray { get; set; }
19 | public Node Assignment { get; set; }
20 | public bool Dynamic { get; set; }
21 | public bool Nullable { get; set; }
22 | public string SpecificType { get; set; }
23 |
24 | protected override string ToStr()
25 | {
26 | var nullable = Nullable ? " nullable" : "";
27 | var dynamic = Dynamic ? " dynamic" : "";
28 | var mutable = Mutable ? " mutable" : " imutable";
29 | var array = Array ? " array" : "";
30 | var arraySize = ArraySize != null ? " (" + ArraySize + ")" : "";
31 | var datatype = Datatype != null ? " " + Datatype.ToString().ToLower() : "";
32 |
33 | if (SpecificType != null)
34 | {
35 | datatype += $"[{SpecificType}]";
36 | }
37 |
38 | var assignment = Assignment != null ? "(" + Assignment + ")" : "";
39 | var withAssignment = assignment != "" ? " with assignment " : "";
40 | return $"Create{nullable}{dynamic}{mutable}{datatype}{array}{arraySize} variable {Name}{withAssignment}{assignment}";
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/Nodes/WithNode.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable MemberCanBePrivate.Global
2 |
3 | namespace Hades.Syntax.Expression.Nodes
4 | {
5 | public class WithNode : Node
6 | {
7 | public WithNode() : base(Classifier.With)
8 | {
9 | }
10 |
11 | public string Target { get; set; } //package to import
12 |
13 | private string _source { get; set; }
14 |
15 | public string Source
16 | {
17 | set => _source = value;
18 | get => string.IsNullOrEmpty(_source) ? Target : _source;
19 | } //source
20 |
21 | public string NativePackage { get; set; } //source of the native package
22 |
23 | private string _name { get; set; }
24 |
25 | public string Name
26 | {
27 | set => _name = value;
28 | get => string.IsNullOrEmpty(_name) ? Target : _name;
29 | } //the name to be set
30 |
31 | public bool Native { get; set; } //for native imports like std libs
32 | public bool Fixed { get; set; }
33 |
34 | protected override string ToStr()
35 | {
36 | var @fixed = Fixed ? " fixed " : " ";
37 | return Native ? $"Import{@fixed}{Target} from native package {NativePackage}:{Source} as {Name}" : $"Import{@fixed}{Target} from {Source} as {Name}";
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Expression/RootNode.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Expression
2 | {
3 | public class RootNode : BlockNode
4 | {
5 | public RootNode() : base(Classifier.Root)
6 | {
7 | }
8 |
9 | public override string ToString()
10 | {
11 | return $"ROOT\n{base.ToStr()}";
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Hades.Syntax.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/Hades.Syntax/Lexeme/Category.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Lexeme
2 | {
3 | public enum Category
4 | {
5 | Unknown,
6 | WhiteSpace,
7 | Comment,
8 |
9 | Literal,
10 | Identifier,
11 | Grouping,
12 | Punctuation,
13 | Operator,
14 |
15 | Invalid,
16 | Other,
17 | Assignment,
18 | LeftHand,
19 | RightHand
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Lexeme/Classifier.cs:
--------------------------------------------------------------------------------
1 | namespace Hades.Syntax.Lexeme
2 | {
3 | public enum Classifier
4 | {
5 | EndOfFile,
6 | Error,
7 |
8 | #region WhiteSpace
9 |
10 | WhiteSpace,
11 | NewLine,
12 |
13 | #endregion
14 |
15 | #region Comments
16 |
17 | LineComment,
18 | BlockComment,
19 |
20 | #endregion
21 |
22 | #region Literal
23 |
24 | IntLiteral,
25 | MultiLineStringLiteral,
26 | StringLiteral,
27 | DecLiteral,
28 | BoolLiteral,
29 |
30 | #endregion
31 |
32 | #region Identifiers
33 |
34 | Identifier,
35 | Keyword,
36 |
37 | #endregion
38 |
39 | #region Groupings
40 |
41 | LeftBracket, // {
42 | RightBracket, // }
43 | RightBrace, // ]
44 | LeftBrace, // [
45 | LeftParenthesis, // (
46 | RightParenthesis, // )
47 |
48 | #endregion Groupings
49 |
50 | #region Operators
51 |
52 | GreaterThanOrEqual, //>=
53 | GreaterThan, //>
54 |
55 | LessThanOrEqual, //<=
56 | LessThan, //<
57 |
58 | PlusEqual, //+=
59 | PlusPlus, //++
60 | Plus, //+
61 |
62 | MinusEqual, // -=
63 | MinusMinus, // --
64 | Minus, // -
65 |
66 | Assignment, // =
67 |
68 | Not, // !
69 | NotEqual, // !=
70 |
71 | Mul, // *
72 | MulEqual, // *=
73 |
74 | Div, // /
75 | DivEqual, // /=
76 |
77 | BooleanAnd, // &&
78 | BooleanOr, // ||
79 |
80 | BitwiseAnd, // &
81 | BitwiseOr, // |
82 |
83 | BitwiseAndEqual, // &=
84 | BitwiseOrEqual, // |=
85 |
86 | ModEqual, // %=
87 | Mod, // %
88 |
89 | BitwiseXorEqual, // ^=
90 | BitwiseXor, // ^
91 |
92 | BitwiseNegate, //~
93 | BitwiseNegateEqual, //~=
94 |
95 | Equal, //==
96 |
97 | BitShiftLeft, // <<
98 | BitShiftRight, // >>
99 | BitShiftLeftEqual, // <<=
100 | BitShiftRightEqual, // >>=
101 |
102 | Question, //?
103 | DoubleQuestion, //??
104 | NullCondition, //::
105 |
106 | Pipeline, //|>
107 | Tag, //#
108 |
109 | #endregion
110 |
111 | #region Punctuation
112 |
113 | Comma, //,
114 | Colon, //:
115 | Arrow, // ->
116 | FatArrow, // =>
117 | Underscore, //_
118 | At, //@
119 | Dot, //.
120 |
121 | #endregion Punctuation
122 | }
123 | }
--------------------------------------------------------------------------------
/src/Hades.Syntax/Lexeme/Token.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Hades.Common.Source;
3 |
4 | namespace Hades.Syntax.Lexeme
5 | {
6 | public sealed class Token : IEquatable
7 | {
8 | private readonly Lazy _category;
9 |
10 | public Token(string value)
11 | {
12 | Value = value;
13 | }
14 |
15 | public Token(Classifier kind, string contents, SourceLocation start, SourceLocation end)
16 | {
17 | Kind = kind;
18 | Value = contents;
19 | Span = new Span(start, end);
20 |
21 | _category = new Lazy(GetTokenCategory);
22 | }
23 |
24 | public Category Category => _category.Value;
25 |
26 | public Classifier Kind { get; }
27 |
28 | public Span Span { get; }
29 |
30 | public string Value { get; set; }
31 |
32 | public bool Equals(Token other)
33 | {
34 | if (other == null)
35 | {
36 | return false;
37 | }
38 |
39 | return other.Value == Value &&
40 | other.Span == Span &&
41 | other.Kind == Kind;
42 | }
43 |
44 | public static bool operator !=(Token left, string right)
45 | {
46 | return left?.Value != right;
47 | }
48 |
49 | public static bool operator !=(string left, Token right)
50 | {
51 | return right?.Value != left;
52 | }
53 |
54 | public static bool operator !=(Token left, Classifier right)
55 | {
56 | return left?.Kind != right;
57 | }
58 |
59 | public static bool operator !=(Classifier left, Token right)
60 | {
61 | return right?.Kind != left;
62 | }
63 |
64 | public static bool operator ==(Token left, string right)
65 | {
66 | return left?.Value == right;
67 | }
68 |
69 | public static bool operator ==(string left, Token right)
70 | {
71 | return right?.Value == left;
72 | }
73 |
74 | public static bool operator ==(Token left, Classifier right)
75 | {
76 | return left?.Kind == right;
77 | }
78 |
79 | public static bool operator ==(Classifier left, Token right)
80 | {
81 | return right?.Kind == left;
82 | }
83 |
84 | public override bool Equals(object obj)
85 | {
86 | if (obj is Token token)
87 | {
88 | return Equals(token);
89 | }
90 |
91 | return base.Equals(obj);
92 | }
93 |
94 | public override int GetHashCode()
95 | {
96 | return Value.GetHashCode() ^ Span.GetHashCode() ^ Kind.GetHashCode();
97 | }
98 |
99 | public bool IsTrivia()
100 | {
101 | return Category == Category.WhiteSpace || Category == Category.Comment;
102 | }
103 |
104 | private Category GetTokenCategory()
105 | {
106 | switch (Kind)
107 | {
108 | case Classifier.Arrow:
109 | case Classifier.FatArrow:
110 | case Classifier.Colon:
111 | case Classifier.Comma:
112 | case Classifier.Underscore:
113 | case Classifier.At:
114 | case Classifier.Dot:
115 | return Category.Punctuation;
116 |
117 | case Classifier.Equal:
118 | case Classifier.NotEqual:
119 | case Classifier.LessThan:
120 | case Classifier.LessThanOrEqual:
121 | case Classifier.GreaterThan:
122 | case Classifier.GreaterThanOrEqual:
123 | case Classifier.Mod:
124 | case Classifier.Mul:
125 | case Classifier.Plus:
126 | case Classifier.Div:
127 | case Classifier.BooleanOr:
128 | case Classifier.BooleanAnd:
129 | case Classifier.BitwiseXor:
130 | case Classifier.BitwiseOr:
131 | case Classifier.BitwiseAnd:
132 | case Classifier.BitShiftLeft:
133 | case Classifier.BitShiftRight:
134 | case Classifier.BitwiseNegate:
135 | case Classifier.Minus:
136 | case Classifier.Not:
137 | return Category.Operator;
138 |
139 | case Classifier.MinusMinus:
140 | case Classifier.PlusPlus:
141 | return Category.RightHand;
142 |
143 | case Classifier.Assignment:
144 | case Classifier.MulEqual:
145 | case Classifier.MinusEqual:
146 | case Classifier.ModEqual:
147 | case Classifier.PlusEqual:
148 | case Classifier.BitwiseXorEqual:
149 | case Classifier.BitwiseOrEqual:
150 | case Classifier.BitwiseAndEqual:
151 | case Classifier.BitShiftLeftEqual:
152 | case Classifier.BitShiftRightEqual:
153 | case Classifier.BitwiseNegateEqual:
154 | case Classifier.DivEqual:
155 | return Category.Assignment;
156 |
157 | case Classifier.BlockComment:
158 | case Classifier.LineComment:
159 | return Category.Comment;
160 |
161 | case Classifier.NewLine:
162 | case Classifier.WhiteSpace:
163 | return Category.WhiteSpace;
164 |
165 | case Classifier.LeftBrace:
166 | case Classifier.LeftBracket:
167 | case Classifier.LeftParenthesis:
168 | case Classifier.RightBrace:
169 | case Classifier.RightBracket:
170 | case Classifier.RightParenthesis:
171 | return Category.Grouping;
172 |
173 | case Classifier.Identifier:
174 | case Classifier.Keyword:
175 | return Category.Identifier;
176 |
177 | case Classifier.StringLiteral:
178 | case Classifier.DecLiteral:
179 | case Classifier.IntLiteral:
180 | case Classifier.BoolLiteral:
181 | case Classifier.MultiLineStringLiteral:
182 | return Category.Literal;
183 |
184 | case Classifier.Error:
185 | return Category.Invalid;
186 |
187 | case Classifier.Pipeline:
188 | case Classifier.Tag:
189 | case Classifier.Question:
190 | return Category.Other;
191 |
192 |
193 | default: return Category.Unknown;
194 | }
195 | }
196 |
197 | public override string ToString()
198 | {
199 | return $"{Kind} : {Value}";
200 | }
201 | }
202 | }
--------------------------------------------------------------------------------
/src/Hades.Testing/Hades.Testing.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Hades.Testing/LexerTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Hades.Language.Lexer;
4 | using Hades.Syntax.Lexeme;
5 | using NUnit.Framework;
6 |
7 | namespace Hades.Testing
8 | {
9 | [TestFixture]
10 | public class LexerTest
11 | {
12 | private readonly Lexer _lexer = new Lexer();
13 |
14 | [Test]
15 | public void EnsureParseDec()
16 | {
17 | var results = _lexer.LexFile("1.5").ToList();
18 | Assert.That(() => results[0].Kind == Classifier.DecLiteral && results[1].Kind == Classifier.EndOfFile);
19 | }
20 |
21 | [Test]
22 | public void EnsureParseInt()
23 | {
24 | var results = _lexer.LexFile("40").ToList();
25 | Assert.That(() => results[0].Kind == Classifier.IntLiteral && results[1].Kind == Classifier.EndOfFile);
26 | }
27 |
28 | [Test]
29 | public void EnsureParseKeyword()
30 | {
31 | foreach (var keyword in Lexer.Keywords)
32 | {
33 | var results = _lexer.LexFile(keyword).ToList();
34 | Console.WriteLine(keyword);
35 | Assert.That(() => results[0].Kind == Classifier.Keyword && results[1].Kind == Classifier.EndOfFile);
36 | }
37 | }
38 |
39 | [Test]
40 | public void EnsureParseMultiLineStringLiteral()
41 | {
42 | var results = _lexer.LexFile("\"\"\"Hello \\\"World\\n\\t\\\"\"\"\"").ToList();
43 | Assert.That(() => results[0].Kind == Classifier.MultiLineStringLiteral && results[1].Kind == Classifier.EndOfFile);
44 | }
45 |
46 | [Test]
47 | public void EnsureParseStringLiteral()
48 | {
49 | var results = _lexer.LexFile("\"Hello \\\"World\\\"\"").ToList();
50 | Assert.That(() => results[0].Kind == Classifier.StringLiteral && results[1].Kind == Classifier.EndOfFile);
51 | }
52 |
53 | [Test]
54 | public void EnsurePipelines()
55 | {
56 | var results = _lexer.LexFile("list->of({1,2,3,4,5})\n\t|> print").ToList();
57 | Assert.That(() => results.Count == 22);
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/src/Hades.Testing/ParserTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Hades.Language.Lexer;
4 | using Hades.Language.Parser;
5 | using Hades.Syntax.Expression.Nodes;
6 | using NUnit.Framework;
7 |
8 | namespace Hades.Testing
9 | {
10 | [TestFixture]
11 | public class ParserTest
12 | {
13 | private void FailTest(string code, bool fail)
14 | {
15 | var lexer = new Lexer();
16 | var parser = new Parser(lexer.LexFile(code), false);
17 |
18 | if (fail)
19 | {
20 | Assert.Throws(() => Console.WriteLine(parser.Parse()));
21 | }
22 | else
23 | {
24 | Assert.DoesNotThrow(() => Console.WriteLine(parser.Parse()));
25 | }
26 | }
27 |
28 | [TestCase("(1 + 2) + 3", false)]
29 | [TestCase("(1 + 2) * (3 + (5 - 6))", false)]
30 | [TestCase("(1) * (3 + (5))", false)]
31 | [TestCase("1 + 2 * 3 - 4 / 5", false)]
32 | [TestCase("1 + 2 * 3 - 4 / !5", false)]
33 | [Test]
34 | public void CalculationTest(string code, bool fail)
35 | {
36 | FailTest(code, fail);
37 | }
38 |
39 | [TestCase("exceptions->ArgumentNullException(\"{} is null\"->format(nameof(b)))", false)]
40 | [TestCase("console->print(\"Variable is of type string\")", false)]
41 | [TestCase("console->out(\"Connection open!\")", false)]
42 | [TestCase("square(a)", false)]
43 | [TestCase("root(a)", false)]
44 | [TestCase("square(a,b", true)]
45 | [TestCase("root(a", true)]
46 | [Test]
47 | public void EnsureCall(string code, bool fail)
48 | {
49 | FailTest(code, fail);
50 | }
51 |
52 | [TestCase("var string a = \"Hello\"", false)]
53 | [TestCase("var int a = 12", false)]
54 | [TestCase("var dec a = 1.2", false)]
55 | [TestCase("var bool a = false", false)]
56 | [TestCase("let string a = \"Hello\"", false)]
57 | [TestCase("let int a = 12", false)]
58 | [TestCase("let dec a = 1.2", false)]
59 | [TestCase("let bool a = false", false)]
60 | [TestCase("var a = \"Hello\"", false)]
61 | [TestCase("var a = 12", false)]
62 | [TestCase("var a = 1.2", false)]
63 | [TestCase("var a = false", false)]
64 | [TestCase("let a = \"Hello\"", false)]
65 | [TestCase("let a = 12", false)]
66 | [TestCase("let a = 1.2", false)]
67 | [TestCase("let a = false", false)]
68 | [TestCase("var string a", false)]
69 | [TestCase("var int a", false)]
70 | [TestCase("var dec a", false)]
71 | [TestCase("var bool a", false)]
72 | [TestCase("let string a", false)]
73 | [TestCase("let int a", false)]
74 | [TestCase("let dec a", false)]
75 | [TestCase("let bool a", false)]
76 | [TestCase("var a", false)]
77 | [TestCase("let a", false)]
78 | [TestCase("var string? a = \"Hello\"", false)]
79 | [TestCase("var int? a = 12", false)]
80 | [TestCase("var dec? a = 1.2", false)]
81 | [TestCase("var bool? a = false", false)]
82 | [TestCase("var* a = \"Hello\"", false)]
83 | [TestCase("var* a = 12", false)]
84 | [TestCase("var* a = 1.2", false)]
85 | [TestCase("var* a = false", false)]
86 | [TestCase("var? a = \"Hello\"", false)]
87 | [TestCase("var? a = 12", false)]
88 | [TestCase("var? a = 1.2", false)]
89 | [TestCase("var? a = false", false)]
90 | [TestCase("var string?[*] a", false)]
91 | [TestCase("var int?[*] a", false)]
92 | [TestCase("var dec?[*] a", false)]
93 | [TestCase("var bool?[*] a", false)]
94 | [TestCase("var*[] a", false)]
95 | [TestCase("var int[3,3] matrix = {{1,0,0},{0,1,0},{0,0,1}}", false)]
96 | [TestCase("var int?[2,2,2] 3dArray = {{{1,2},{3,null}},{{null,6},{7,8}}}", false)]
97 | [TestCase("var object::IClient a", false)]
98 | [TestCase("var object::IClient? a", false)]
99 | [TestCase("var object::IClient?[] a", false)]
100 | [TestCase("var object::IClient?[*] a", false)]
101 | [TestCase("let object::IClient? a", true)]
102 | [TestCase("var* object::IClient a", true)]
103 | [TestCase("var* string a = \"Hello\"", true)]
104 | [TestCase("var* int a = 12", true)]
105 | [TestCase("var* dec a = 1.2", true)]
106 | [TestCase("var* bool a = false", true)]
107 | [TestCase("let* string a = \"Hello\"", true)]
108 | [TestCase("let* int a = 12", true)]
109 | [TestCase("let* dec a = 1.2", true)]
110 | [TestCase("let* bool a = false", true)]
111 | [TestCase("var? a", true)]
112 | [TestCase("let? a", true)]
113 | [Test]
114 | public void EnsureVariableDeclaration(string code, bool fail)
115 | {
116 | FailTest(code, fail);
117 | }
118 |
119 | [TestCase("with server", "server", "server", "server", false, null)]
120 | [TestCase("with server as foo", "server", "server", "foo", false, null)]
121 | [TestCase("with console from std:io", "io", "console", "console", true, "std")]
122 | [TestCase("with math as m from std:math", "math", "math", "m", true, "std")]
123 | [TestCase("with console fixed from std:io", "io", "console", "console", true, "std")]
124 | [TestCase("with math fixed as m from std:math", "math", "math", "m", true, "std")]
125 | [Test]
126 | public void EnsureWith(string code, string source, string target, string name, bool native, string nativePackage)
127 | {
128 | var lexer = new Lexer();
129 | var parser = new Parser(lexer.LexFile(code), false);
130 | var root = parser.Parse();
131 | var node = root.Children.First();
132 | Assert.IsTrue(node is WithNode);
133 | var withNode = node as WithNode;
134 |
135 | Assert.AreEqual(withNode.Source, source);
136 | Assert.AreEqual(withNode.Target, target);
137 | Assert.AreEqual(withNode.Name, name);
138 | Assert.AreEqual(withNode.Native, native);
139 | Assert.AreEqual(withNode.NativePackage, nativePackage);
140 | }
141 |
142 | [TestCase("fib(10) |> doStuff |> console->out", false)]
143 | [TestCase("fib(10) |> console->out", false)]
144 | [TestCase("fruits |> map({x => x->toLower()}, ??) |> filter({x => x->startsWith(\"a\")}, ??) |> forEach({x => console->out(x)}, ??)", false)]
145 | [Test]
146 | public void PipelineTes(string code, bool fail)
147 | {
148 | FailTest(code, fail);
149 | }
150 |
151 | [TestCase(
152 | "with console from std:io\n" +
153 | "func myFunction(int a) requires a is 11\n" +
154 | "console->out(\"a is 11\")\n" +
155 | "console->out(\"a is 11\")\n" +
156 | "end", false)]
157 |
158 | [TestCase(
159 | "while(c not 10)\n" +
160 | "console->out(\"c is {}\"->format(c))\n" +
161 | "end", false)]
162 |
163 | [TestCase(
164 | "with console from std:io\n" +
165 | "func myFunction(int a) requires a is 11\n" +
166 | "console->out(\"a is 11\")\n" +
167 | "skip\n" +
168 | "end", true)]
169 |
170 | [TestCase(
171 | "with console from std:io\n" +
172 | "func myFunction(int a) requires a is 11\n" +
173 | "console->out(\"a is 11\")\n" +
174 | "a->b = {x,y=>x+y}(1,2)->toString({x=>x})\n" +
175 | "end", false)]
176 |
177 | [TestCase(
178 | "for(var arg in a)\n" +
179 | "console->out(arg)\n" +
180 | "skip\n" +
181 | "end\n" +
182 | "for(var arg in a)\n" +
183 | "console->out(arg)\n" +
184 | "skip\n" +
185 | "end", false)]
186 |
187 | [TestCase(
188 | "func doStuff(object::IClient a)\n" +
189 | "a->stuff(\"Hello\")\n" +
190 | "end", false)]
191 |
192 | [TestCase(
193 | "func doStuff(object::IClient a, int b)\n" +
194 | "a->stuff(\"Hello\")\n" +
195 | "end", false)]
196 |
197 | [TestCase(
198 | "func doStuff(object::IClient a, c)\n" +
199 | "a->stuff(\"Hello\")\n" +
200 | "end", false)]
201 |
202 | [TestCase(
203 | "func doStuff(object::IClient a, c, int d)\n" +
204 | "a->stuff(\"Hello\")\n" +
205 | "end", false)]
206 |
207 | [TestCase(
208 | "func doStuff(args object::IClient a, c)\n" +
209 | "a->stuff(\"Hello\")\n" +
210 | "end", false)]
211 |
212 | [TestCase(
213 | "with console from std:io\n" +
214 | "if(a < 10)\n" +
215 | "console->out(\"a is smaller than 10\")\n" +
216 | "else if(a is 11)\n" +
217 | "console->out(\"a is 11\")\n" +
218 | "else if(a > 11 and a < 21)\n" +
219 | "console->out(\"a is greater than 11 and smaller than 21\")\n" +
220 | "else\n" +
221 | "console->out(\"a is \" + a)\n" +
222 | "end", false)]
223 |
224 | [TestCase(
225 | "with math as m from std:math\n" +
226 | "func doMath(int a)\n" +
227 | "func root(int b)\n" +
228 | "put m->sqrt(b)\n" +
229 | "end\n" +
230 | "func square(b)\n" +
231 | "put b * b\n" +
232 | "end\n" +
233 | "put square(a) + root(a)\n" +
234 | "end", false)]
235 |
236 | [TestCase(
237 | "srv->get(\"/:path\", {req, res => \n" +
238 | "let path = req->param\n" +
239 | "try\n" +
240 | "if (file->exists(path))\n" +
241 | "let f = file->open(path)\n" +
242 | "res->send(f->read())\n" +
243 | "else\n" +
244 | "raise 404\n" +
245 | "end\n" +
246 | "catch(int status)\n" +
247 | "res->status(status)\n" +
248 | "else\n" +
249 | "res->status(200)\n" +
250 | "end\n" +
251 | "})", false)]
252 |
253 | [TestCase(
254 | "var fruits = list->of({\"Apple\", \"Banana\", \"Mango\", \"Kiwi\", \"Avocado\"})\n" +
255 | "fruits\n" +
256 | "|> map({x => x->toLower()}, ??)\n" +
257 | "|> filter({x => x->startsWith(\"a\")}, ??)\n" +
258 | "|> forEach({x => console->out(x)}, ??)", false)]
259 |
260 | [TestCase(
261 | "let string[] fruits = {\"Apple\", \"Banana\", \"Mango\", \"Kiwi\"}\n" +
262 | "for(var fruit in fruits)\n" +
263 | "console->out(\"{} is very healthy\"->format(fruit))\n" +
264 | "end\n" +
265 | "var a = params->get(0)\n" +
266 | "var b = params->get(1)\n" +
267 | "a :: raise exceptions->ArgumentNullException(\"{} is null\"->format(nameof(a)))\n" +
268 | "b :: raise exceptions->ArgumentNullException(\"{} is null\"->format(nameof(b)))", false)]
269 |
270 | [TestCase(
271 | "try\n" +
272 | "connection->open()\n" +
273 | "console->out(\"Connection open!\")\n" +
274 | "connection->close()\n" +
275 | "catch(object::SqlException e)\n" +
276 | "console->out(\"SqlException was caught!\")\n" +
277 | "catch(e)\n" +
278 | "console->out(\"An unknown exception was caught!\")\n" +
279 | "else\n" +
280 | "console->out(\"No exception thrown!\")\n" +
281 | "end", false)]
282 |
283 | [TestCase(
284 | "struct employee\n"+
285 | "public let string firstname = \"John\"\n"+
286 | "public let lastname = \"Doe\"\n"+
287 | "private var int age = 18\n"+
288 | "var string[] attributes //When no access modifier is given, the variable will have private access\n"+
289 | "end\n"+
290 | "class Person\n"+
291 | "var int id\n"+
292 | "@public\n"+
293 | "var string firstname\n"+
294 | "var string lastname\n"+
295 | "end\n"+
296 | "end\n"+
297 | "struct Person\n"+
298 | "@public\n"+
299 | "var string firstname\n"+
300 | "var string lastname\n"+
301 | "end\n"+
302 | "@private\n"+
303 | "var int id\n"+
304 | "end\n"+
305 | "end", false)]
306 |
307 | [TestCase(
308 | "with console from std:io\n" +
309 | "func myFunction(int a) requires a is 11\n" +
310 | "console->out(\"a is 11\")\n" +
311 | "a->b = {x,y=>x+y}(1,2)->toString({x=>x})\n" +
312 | "end\n" +
313 | "a[0]()->t() |> console\n" +
314 | "struct Person\n" +
315 | "let string firstname\n" +
316 | "var string lastname\n" +
317 | "let birthday\n" +
318 | "var nationality\n" +
319 | "end", false)]
320 |
321 | [TestCase(
322 | "#route(\"/\")\n" +
323 | "func handleMain()\n" +
324 | "put 200\n" +
325 | "end\n" +
326 | "#service\n" +
327 | "#constructor(true)\n" +
328 | "class UserService\n" +
329 | "#injected(UserRepository)\n" +
330 | "let userRepository\n" +
331 | "func getById(id)\n" +
332 | "put userRepository->findById(id)->orElse(null)\n" +
333 | "end\n" +
334 | "end", false)]
335 |
336 | [TestCase(
337 | "let dec[*] points = \n" +
338 | "{\n" +
339 | "{245, 1400},\n" +
340 | "{312, 1600},\n" +
341 | "{279, 1700},\n" +
342 | "{308, 1875},\n" +
343 | "{199, 1100},\n" +
344 | "{219, 1550},\n" +
345 | "{405, 2350},\n" +
346 | "{324, 2450},\n" +
347 | "{319, 1425},\n" +
348 | "{255, 1700}\n" +
349 | "}\n" +
350 | "var weight = 10.0\n" +
351 | "var bias = 100.0\n" +
352 | "var lr = 0.000001\n" +
353 | "for(_ in range(1000000))\n" +
354 | "for(var point in points)\n" +
355 | "var prediction = point[0] * weight + bias\n" +
356 | "var error = point[1] - prediction\n" +
357 | "var gradient = point[0] * error * lr\n" +
358 | "bias += gradient\n" +
359 | "weight += weight * gradient\n" +
360 | "end\n" +
361 | "end", false)]
362 | [Test]
363 | public void ProgramTest(string code, bool fail)
364 | {
365 | FailTest(code, fail);
366 | }
367 | }
368 | }
--------------------------------------------------------------------------------
/src/Hades.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hades.Core", "Hades.Core\Hades.Core.csproj", "{54BC2912-B7A1-44B3-B2E9-41A0C48FBADE}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hades.Syntax", "Hades.Syntax\Hades.Syntax.csproj", "{9CBB69AF-EAC6-4556-9495-4F06D295726C}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hades.Common", "Hades.Common\Hades.Common.csproj", "{1F693DED-1D90-47C8-9EC0-EEE047E24C59}"
8 | EndProject
9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hades.Language", "Hades.Language\Hades.Language.csproj", "{0E884807-1A5C-4AB1-BE54-A00B9B91202E}"
10 | EndProject
11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hades.Testing", "Hades.Testing\Hades.Testing.csproj", "{BA1980E8-AC47-4934-8160-80410AA99EC0}"
12 | EndProject
13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hades.Error", "Hades.Error\Hades.Error.csproj", "{AA64808F-6020-4F86-B2C1-F13402FA2F68}"
14 | EndProject
15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hades.Runtime", "Hades.Runtime\Hades.Runtime.csproj", "{F0148F87-2E59-453A-8618-83A249D13F39}"
16 | EndProject
17 | Global
18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 | Debug|Any CPU = Debug|Any CPU
20 | Release|Any CPU = Release|Any CPU
21 | EndGlobalSection
22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
23 | {54BC2912-B7A1-44B3-B2E9-41A0C48FBADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {54BC2912-B7A1-44B3-B2E9-41A0C48FBADE}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {54BC2912-B7A1-44B3-B2E9-41A0C48FBADE}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {54BC2912-B7A1-44B3-B2E9-41A0C48FBADE}.Release|Any CPU.Build.0 = Release|Any CPU
27 | {9CBB69AF-EAC6-4556-9495-4F06D295726C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28 | {9CBB69AF-EAC6-4556-9495-4F06D295726C}.Debug|Any CPU.Build.0 = Debug|Any CPU
29 | {9CBB69AF-EAC6-4556-9495-4F06D295726C}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {9CBB69AF-EAC6-4556-9495-4F06D295726C}.Release|Any CPU.Build.0 = Release|Any CPU
31 | {1F693DED-1D90-47C8-9EC0-EEE047E24C59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {1F693DED-1D90-47C8-9EC0-EEE047E24C59}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {1F693DED-1D90-47C8-9EC0-EEE047E24C59}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {1F693DED-1D90-47C8-9EC0-EEE047E24C59}.Release|Any CPU.Build.0 = Release|Any CPU
35 | {0E884807-1A5C-4AB1-BE54-A00B9B91202E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36 | {0E884807-1A5C-4AB1-BE54-A00B9B91202E}.Debug|Any CPU.Build.0 = Debug|Any CPU
37 | {0E884807-1A5C-4AB1-BE54-A00B9B91202E}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 | {0E884807-1A5C-4AB1-BE54-A00B9B91202E}.Release|Any CPU.Build.0 = Release|Any CPU
39 | {BA1980E8-AC47-4934-8160-80410AA99EC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
40 | {BA1980E8-AC47-4934-8160-80410AA99EC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
41 | {BA1980E8-AC47-4934-8160-80410AA99EC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
42 | {BA1980E8-AC47-4934-8160-80410AA99EC0}.Release|Any CPU.Build.0 = Release|Any CPU
43 | {AA64808F-6020-4F86-B2C1-F13402FA2F68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
44 | {AA64808F-6020-4F86-B2C1-F13402FA2F68}.Debug|Any CPU.Build.0 = Debug|Any CPU
45 | {AA64808F-6020-4F86-B2C1-F13402FA2F68}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 | {AA64808F-6020-4F86-B2C1-F13402FA2F68}.Release|Any CPU.Build.0 = Release|Any CPU
47 | {F0148F87-2E59-453A-8618-83A249D13F39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
48 | {F0148F87-2E59-453A-8618-83A249D13F39}.Debug|Any CPU.Build.0 = Debug|Any CPU
49 | {F0148F87-2E59-453A-8618-83A249D13F39}.Release|Any CPU.ActiveCfg = Release|Any CPU
50 | {F0148F87-2E59-453A-8618-83A249D13F39}.Release|Any CPU.Build.0 = Release|Any CPU
51 | EndGlobalSection
52 | EndGlobal
53 |
--------------------------------------------------------------------------------