├── .github └── workflows │ └── release.yml ├── .gitignore ├── .goreleaser.yaml ├── LICENSE ├── README.md ├── ast └── ast.go ├── cache └── cache.go ├── cli └── cli.go ├── config └── config.go ├── devtools └── cli.go ├── diagnostics └── diagnostics.go ├── environment ├── environment.go ├── object.go └── types.go ├── go.mod ├── go.sum ├── lexer ├── lex.go ├── lex_test.go └── print.go ├── lsp ├── lsp.go └── read.go ├── main.go ├── makefile ├── parser ├── parser.go └── parser_test.go ├── r ├── args.go ├── base.go ├── call.go └── libpath.go ├── read.go ├── repl.go ├── run.go ├── token ├── print.go └── token.go ├── transpile.go ├── transpiler ├── transpile.go └── transpile_test.go ├── vapour.go └── walker ├── diagnostics.go ├── types.go ├── walk.go └── walk_test.go /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | pull_request: 5 | # run only against tags 6 | tags: 7 | - "*" 8 | push: 9 | # run only against tags 10 | tags: 11 | - "*" 12 | 13 | permissions: 14 | contents: write 15 | # packages: write 16 | # issues: write 17 | 18 | jobs: 19 | goreleaser: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 26 | - name: Set up Go 27 | uses: actions/setup-go@v5 28 | with: 29 | go-version: stable 30 | # More assembly might be required: Docker logins, GPG, etc. 31 | # It all depends on your needs. 32 | - name: Run GoReleaser 33 | uses: goreleaser/goreleaser-action@v6 34 | with: 35 | # either 'goreleaser' (default) or 'goreleaser-pro' 36 | distribution: goreleaser 37 | # 'latest', 'nightly', or a semver 38 | version: "~> v2" 39 | args: release --clean 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | dist/ 3 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | before: 4 | hooks: 5 | - go mod tidy 6 | - go generate ./... 7 | 8 | builds: 9 | - env: 10 | - CGO_ENABLED=0 11 | goos: 12 | - linux 13 | - darwin 14 | - windows 15 | 16 | archives: 17 | - format: tar.gz 18 | name_template: >- 19 | {{ .ProjectName }}_ 20 | {{- title .Os }}_ 21 | {{- if eq .Arch "amd64" }}x86_64 22 | {{- else if eq .Arch "386" }}i386 23 | {{- else }}{{ .Arch }}{{ end }} 24 | {{- if .Arm }}v{{ .Arm }}{{ end }} 25 | # use zip for windows archives 26 | format_overrides: 27 | - goos: windows 28 | format: zip 29 | 30 | changelog: 31 | sort: asc 32 | filters: 33 | exclude: 34 | - "^docs:" 35 | - "^test:" 36 | 37 | # .goreleaser.yaml 38 | nfpms: 39 | # note that this is an array of nfpm configs 40 | - # 41 | id: vapour 42 | 43 | # Name of the package. 44 | # 45 | # Default: ProjectName. 46 | # Templates: allowed. 47 | package_name: vapour 48 | 49 | # Your app's vendor. 50 | vendor: Vapour 51 | 52 | # Your app's homepage. 53 | # 54 | # Default: inferred from global metadata. 55 | homepage: https://vapour.run 56 | 57 | # Your app's maintainer (probably you). 58 | # 59 | # Default: inferred from global metadata. 60 | maintainer: John Coene 61 | 62 | # Your app's description. 63 | # 64 | # Default: inferred from global metadata. 65 | description: |- 66 | Vapour is a typed superset of R. 67 | 68 | # Your app's license. 69 | # 70 | # Default: inferred from global metadata. 71 | license: Apache 2.0 72 | 73 | # Formats to be generated. 74 | formats: 75 | - apk 76 | - deb 77 | - rpm 78 | - termux.deb 79 | - archlinux 80 | 81 | # Umask to be used on files without explicit mode set. (overridable) 82 | # 83 | # Default: 0o002 (will remove world-writable permissions). 84 | umask: 0o002 85 | 86 | # Path that the binaries should be installed. 87 | # 88 | # Default: '/usr/bin'. 89 | bindir: /usr/bin 90 | 91 | # Version Release. 92 | release: 1 93 | 94 | # Section. 95 | section: default 96 | 97 | # Makes a meta package - an empty package that contains only supporting 98 | # files and dependencies. 99 | # When set to `true`, the `builds` option is ignored. 100 | meta: true 101 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 - present Louis Pilfold 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://vapour.run/img/vapour.png) 2 | 3 |
4 |

5 | A typed superset of the R programming language 6 |

7 | Docs | Get Started | Install 8 |
9 | 10 | > [!WARNING] 11 | > Vapour is in (very) early alpha! 12 | 13 | ```r 14 | type person: object { 15 | age: int, 16 | name: char 17 | } 18 | 19 | func create(name: char): person { 20 | return person(name = name) 21 | } 22 | 23 | @generic 24 | func (p: any) set_age(age: int, ...: any): any 25 | 26 | @default 27 | func(p: any) set_age(age: int): null { 28 | stop("not implemented") 29 | } 30 | 31 | func(p: person) set_age(age: int): person { 32 | p$age = age 33 | return p 34 | } 35 | 36 | let john: person = create("John") |> 37 | set_age(36) 38 | ``` 39 | -------------------------------------------------------------------------------- /ast/ast.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | 7 | "github.com/vapourlang/vapour/token" 8 | ) 9 | 10 | type Node interface { 11 | TokenLiteral() string 12 | String() string 13 | Item() token.Item 14 | } 15 | 16 | type Statement interface { 17 | Node 18 | statementNode() 19 | } 20 | 21 | type Expression interface { 22 | Node 23 | expressionNode() 24 | } 25 | 26 | type Program struct { 27 | Statements []Statement 28 | } 29 | 30 | func (p *Program) Item() token.Item { return token.Item{} } 31 | 32 | func (p *Program) TokenLiteral() string { 33 | if len(p.Statements) > 0 { 34 | return p.Statements[0].TokenLiteral() 35 | } 36 | 37 | return "" 38 | } 39 | 40 | func (p *Program) String() string { 41 | var out bytes.Buffer 42 | 43 | for _, s := range p.Statements { 44 | out.WriteString(s.String()) 45 | } 46 | 47 | return out.String() 48 | } 49 | 50 | // Statements 51 | type LetStatement struct { 52 | Token token.Item 53 | Name string 54 | Type Types 55 | Value Expression 56 | } 57 | 58 | func (ls *LetStatement) Item() token.Item { return ls.Token } 59 | func (ls *LetStatement) statementNode() {} 60 | func (ls *LetStatement) TokenLiteral() string { return ls.Token.Value } 61 | func (ls *LetStatement) String() string { 62 | if ls.Value == nil { 63 | return "" 64 | } 65 | 66 | var out bytes.Buffer 67 | 68 | out.WriteString("# " + ls.Type.String() + "\n") 69 | 70 | out.WriteString(ls.Name) 71 | out.WriteString(" = ") 72 | 73 | if ls.Value != nil { 74 | out.WriteString(ls.Value.String()) 75 | } 76 | 77 | return out.String() 78 | } 79 | 80 | type ConstStatement struct { 81 | Token token.Item 82 | Name string 83 | Type Types 84 | Value Expression 85 | } 86 | 87 | func (cs *ConstStatement) Item() token.Item { return cs.Token } 88 | func (cs *ConstStatement) statementNode() {} 89 | func (cs *ConstStatement) TokenLiteral() string { return cs.Token.Value } 90 | func (cs *ConstStatement) String() string { 91 | if cs.Value == nil { 92 | return "" 93 | } 94 | 95 | var out bytes.Buffer 96 | 97 | out.WriteString(cs.Name) 98 | out.WriteString(" = ") 99 | 100 | if cs.Value != nil { 101 | out.WriteString(cs.Value.String()) 102 | } 103 | 104 | return out.String() 105 | } 106 | 107 | type Type struct { 108 | Name string 109 | Package string 110 | List bool 111 | } 112 | 113 | func (t *Type) String() string { 114 | return t.Name 115 | } 116 | 117 | type Types []*Type 118 | 119 | func (types Types) String() string { 120 | var strs []string 121 | for _, t := range types { 122 | name := t.Name 123 | 124 | if t.Package != "" { 125 | name = t.Package + "::" + name 126 | } 127 | 128 | if t.List { 129 | name = "[]" + name 130 | } 131 | 132 | strs = append(strs, name) 133 | } 134 | return strings.Join(strs, ", ") 135 | } 136 | 137 | type TypeFunction struct { 138 | Token token.Item // type token 139 | Name string 140 | Arguments []Types 141 | Return Types 142 | } 143 | 144 | func (tf *TypeFunction) Item() token.Item { return tf.Token } 145 | func (tf *TypeFunction) statementNode() {} 146 | func (tf *TypeFunction) TokenLiteral() string { return tf.Token.Value } 147 | func (tf *TypeFunction) String() string { 148 | var out bytes.Buffer 149 | 150 | out.WriteString("# function type ") 151 | out.WriteString(tf.Name) 152 | 153 | for _, a := range tf.Arguments { 154 | out.WriteString("# " + a.String()) 155 | } 156 | 157 | return out.String() 158 | } 159 | 160 | type TypeStatement struct { 161 | Token token.Item // type token 162 | Name string 163 | Object string 164 | Type Types 165 | Attributes []*TypeAttributesStatement 166 | } 167 | 168 | func (ts *TypeStatement) Item() token.Item { return ts.Token } 169 | func (ts *TypeStatement) statementNode() {} 170 | func (ts *TypeStatement) TokenLiteral() string { return ts.Token.Value } 171 | func (ts *TypeStatement) String() string { 172 | var out bytes.Buffer 173 | 174 | out.WriteString("# type ") 175 | out.WriteString(ts.Name + ": " + ts.Object + " - ") 176 | for _, v := range ts.Type { 177 | out.WriteString(v.Name + " ") 178 | } 179 | out.WriteString("\n") 180 | for _, v := range ts.Attributes { 181 | out.WriteString(v.String()) 182 | } 183 | out.WriteString("\n") 184 | 185 | return out.String() 186 | } 187 | 188 | type TypeAttributesStatement struct { 189 | Token token.Item // type token 190 | Name string 191 | Type Types 192 | } 193 | 194 | func (ta *TypeAttributesStatement) Item() token.Item { return ta.Token } 195 | func (ta *TypeAttributesStatement) statementNode() {} 196 | func (ta *TypeAttributesStatement) TokenLiteral() string { return ta.Token.Value } 197 | func (ta *TypeAttributesStatement) String() string { 198 | var out bytes.Buffer 199 | 200 | out.WriteString("# attribute ") 201 | out.WriteString(ta.Name + ": ") 202 | for _, v := range ta.Type { 203 | out.WriteString(v.Name + " ") 204 | } 205 | out.WriteString("\n") 206 | 207 | return out.String() 208 | } 209 | 210 | type CommentStatement struct { 211 | Token token.Item 212 | Value string 213 | } 214 | 215 | func (c *CommentStatement) Item() token.Item { return c.Token } 216 | func (c *CommentStatement) statementNode() {} 217 | func (c *CommentStatement) TokenLiteral() string { return c.Token.Value } 218 | func (c *CommentStatement) String() string { 219 | var out bytes.Buffer 220 | 221 | out.WriteString(c.TokenLiteral() + "\n") 222 | 223 | return out.String() 224 | } 225 | 226 | type NewLine struct { 227 | Token token.Item 228 | } 229 | 230 | func (nl *NewLine) Item() token.Item { return nl.Token } 231 | func (nl *NewLine) statementNode() {} 232 | func (nl *NewLine) TokenLiteral() string { return nl.Token.Value } 233 | func (nl *NewLine) String() string { 234 | return "\n" 235 | } 236 | 237 | type DeferStatement struct { 238 | Token token.Item 239 | Func Expression 240 | } 241 | 242 | func (ds *DeferStatement) Item() token.Item { return ds.Token } 243 | func (ds *DeferStatement) statementNode() {} 244 | func (ds *DeferStatement) TokenLiteral() string { return ds.Token.Value } 245 | func (ds *DeferStatement) String() string { 246 | var out bytes.Buffer 247 | 248 | out.WriteString("\non.exit((") 249 | 250 | if ds.Func != nil { 251 | out.WriteString(ds.Func.String()) 252 | } 253 | 254 | out.WriteString(")())") 255 | 256 | return out.String() 257 | } 258 | 259 | type ReturnStatement struct { 260 | Token token.Item 261 | ReturnValue Expression 262 | } 263 | 264 | func (rs *ReturnStatement) Item() token.Item { return rs.Token } 265 | func (rs *ReturnStatement) statementNode() {} 266 | func (rs *ReturnStatement) TokenLiteral() string { return rs.Token.Value } 267 | func (rs *ReturnStatement) String() string { 268 | var out bytes.Buffer 269 | 270 | out.WriteString("\n" + rs.TokenLiteral() + "(") 271 | 272 | if rs.ReturnValue != nil { 273 | out.WriteString(rs.ReturnValue.String()) 274 | } 275 | 276 | out.WriteString(")") 277 | 278 | return out.String() 279 | } 280 | 281 | type ExpressionStatement struct { 282 | Token token.Item // the first token of the expression 283 | Expression Expression 284 | } 285 | 286 | func (es *ExpressionStatement) Item() token.Item { return es.Token } 287 | func (es *ExpressionStatement) statementNode() {} 288 | func (es *ExpressionStatement) TokenLiteral() string { return es.Token.Value } 289 | func (es *ExpressionStatement) String() string { 290 | if es.Expression != nil { 291 | return es.Expression.String() 292 | } 293 | return "" 294 | } 295 | 296 | type BlockStatement struct { 297 | Token token.Item // the { token 298 | Statements []Statement 299 | } 300 | 301 | func (bs *BlockStatement) Item() token.Item { return bs.Token } 302 | func (bs *BlockStatement) statementNode() {} 303 | func (bs *BlockStatement) TokenLiteral() string { return bs.Token.Value } 304 | func (bs *BlockStatement) String() string { 305 | var out bytes.Buffer 306 | 307 | for _, s := range bs.Statements { 308 | out.WriteString(s.String()) 309 | } 310 | 311 | return out.String() 312 | } 313 | 314 | // Expressions 315 | type Comma struct { 316 | Token token.Item 317 | } 318 | 319 | func (c *Comma) Item() token.Item { return c.Token } 320 | func (c *Comma) expressionNode() {} 321 | func (c *Comma) TokenLiteral() string { return c.Token.Value } 322 | func (c *Comma) String() string { 323 | return "," 324 | } 325 | 326 | type Identifier struct { 327 | Token token.Item // the token.IDENT token 328 | Type Types 329 | Value string 330 | } 331 | 332 | func (i *Identifier) Item() token.Item { return i.Token } 333 | func (i *Identifier) expressionNode() {} 334 | func (i *Identifier) TokenLiteral() string { return i.Token.Value } 335 | func (i *Identifier) String() string { 336 | return i.Value 337 | } 338 | 339 | type Attribute struct { 340 | Token token.Item 341 | Value string 342 | } 343 | 344 | func (a *Attribute) Item() token.Item { return a.Token } 345 | func (a *Attribute) expressionNode() {} 346 | func (a *Attribute) TokenLiteral() string { return a.Token.Value } 347 | func (a *Attribute) String() string { 348 | return a.Value 349 | } 350 | 351 | type Square struct { 352 | Token token.Item 353 | } 354 | 355 | func (s *Square) Item() token.Item { return s.Token } 356 | func (s *Square) expressionNode() {} 357 | func (s *Square) TokenLiteral() string { return s.Token.Value } 358 | func (s *Square) String() string { 359 | return s.Token.Value 360 | } 361 | 362 | type Boolean struct { 363 | Token token.Item 364 | Value bool 365 | Type *Type 366 | } 367 | 368 | func (b *Boolean) Item() token.Item { return b.Token } 369 | func (b *Boolean) expressionNode() {} 370 | func (b *Boolean) TokenLiteral() string { return b.Token.Value } 371 | func (b *Boolean) String() string { 372 | if b.Value { 373 | return "TRUE" 374 | } 375 | 376 | return "FALSE" 377 | } 378 | 379 | type IntegerLiteral struct { 380 | Token token.Item 381 | Value string 382 | Type *Type 383 | } 384 | 385 | func (il *IntegerLiteral) Item() token.Item { return il.Token } 386 | func (il *IntegerLiteral) expressionNode() {} 387 | func (il *IntegerLiteral) TokenLiteral() string { return il.Token.Value } 388 | func (il *IntegerLiteral) String() string { return il.Token.Value } 389 | 390 | type FloatLiteral struct { 391 | Token token.Item 392 | Value string 393 | Type *Type 394 | } 395 | 396 | func (fl *FloatLiteral) Item() token.Item { return fl.Token } 397 | func (fl *FloatLiteral) expressionNode() {} 398 | func (fl *FloatLiteral) TokenLiteral() string { return fl.Token.Value } 399 | func (fl *FloatLiteral) String() string { return fl.Token.Value } 400 | 401 | type VectorLiteral struct { 402 | Token token.Item 403 | Value []Expression 404 | } 405 | 406 | func (v *VectorLiteral) Item() token.Item { return v.Token } 407 | func (v *VectorLiteral) expressionNode() {} 408 | func (v *VectorLiteral) TokenLiteral() string { return v.Token.Value } 409 | func (v *VectorLiteral) String() string { 410 | var out bytes.Buffer 411 | 412 | out.WriteString("c(") 413 | for i, e := range v.Value { 414 | if e == nil { 415 | continue 416 | } 417 | out.WriteString(e.String()) 418 | if i < len(v.Value)-1 { 419 | out.WriteString(", ") 420 | } 421 | } 422 | out.WriteString(")\n") 423 | 424 | return out.String() 425 | } 426 | 427 | type SquareRightLiteral struct { 428 | Token token.Item 429 | Value string 430 | } 431 | 432 | func (s *SquareRightLiteral) Item() token.Item { return s.Token } 433 | func (s *SquareRightLiteral) expressionNode() {} 434 | func (s *SquareRightLiteral) TokenLiteral() string { return s.Token.Value } 435 | func (s *SquareRightLiteral) String() string { 436 | var out bytes.Buffer 437 | 438 | out.WriteString(s.Value) 439 | out.WriteString("\n") 440 | 441 | return out.String() 442 | } 443 | 444 | type For struct { 445 | Token token.Item 446 | Name *LetStatement 447 | Vector Expression 448 | Value *BlockStatement 449 | } 450 | 451 | func (f *For) Item() token.Item { return f.Token } 452 | func (f *For) expressionNode() {} 453 | func (f *For) TokenLiteral() string { return f.Token.Value } 454 | func (f *For) String() string { 455 | var out bytes.Buffer 456 | 457 | out.WriteString("for(") 458 | out.WriteString(f.Name.String()) 459 | out.WriteString(" in ") 460 | out.WriteString(f.Vector.String()) 461 | out.WriteString(")\n {") 462 | out.WriteString(f.Value.String()) 463 | out.WriteString("}\n") 464 | 465 | return out.String() 466 | } 467 | 468 | type While struct { 469 | Token token.Item 470 | Statement Statement 471 | Value *BlockStatement 472 | } 473 | 474 | func (w *While) Item() token.Item { return w.Token } 475 | func (w *While) expressionNode() {} 476 | func (w *While) TokenLiteral() string { return w.Token.Value } 477 | func (w *While) String() string { 478 | var out bytes.Buffer 479 | 480 | out.WriteString("while(") 481 | out.WriteString(w.Statement.String()) 482 | out.WriteString(")\n {") 483 | out.WriteString(w.Value.String()) 484 | out.WriteString("}\n") 485 | 486 | return out.String() 487 | } 488 | 489 | type Null struct { 490 | Token token.Item 491 | Value string 492 | Type *Type 493 | } 494 | 495 | func (n *Null) Item() token.Item { return n.Token } 496 | func (n *Null) expressionNode() {} 497 | func (n *Null) TokenLiteral() string { return n.Token.Value } 498 | func (n *Null) String() string { 499 | var out bytes.Buffer 500 | 501 | out.WriteString(n.Value) 502 | 503 | return out.String() 504 | } 505 | 506 | type Keyword struct { 507 | Token token.Item 508 | Value string 509 | Type *Type 510 | } 511 | 512 | func (kw *Keyword) Item() token.Item { return kw.Token } 513 | func (kw *Keyword) expressionNode() {} 514 | func (kw *Keyword) TokenLiteral() string { return kw.Token.Value } 515 | func (kw *Keyword) String() string { 516 | var out bytes.Buffer 517 | 518 | out.WriteString(kw.Value) 519 | 520 | return out.String() 521 | } 522 | 523 | type StringLiteral struct { 524 | Token token.Item 525 | Str string 526 | Type *Type 527 | } 528 | 529 | func (sl *StringLiteral) Item() token.Item { return sl.Token } 530 | func (sl *StringLiteral) expressionNode() {} 531 | func (sl *StringLiteral) TokenLiteral() string { return sl.Token.Value } 532 | func (sl *StringLiteral) String() string { 533 | var out bytes.Buffer 534 | 535 | out.WriteString(sl.Token.Value + sl.Str + sl.Token.Value) 536 | 537 | return out.String() 538 | } 539 | 540 | type PrefixExpression struct { 541 | Token token.Item // The prefix token, e.g. ! 542 | Operator string 543 | Right Expression 544 | } 545 | 546 | func (pe *PrefixExpression) Item() token.Item { return pe.Token } 547 | func (pe *PrefixExpression) expressionNode() {} 548 | func (pe *PrefixExpression) TokenLiteral() string { return pe.Token.Value } 549 | func (pe *PrefixExpression) String() string { 550 | var out bytes.Buffer 551 | 552 | out.WriteString("(") 553 | out.WriteString(pe.Operator) 554 | out.WriteString(pe.Right.String()) 555 | out.WriteString(")") 556 | 557 | return out.String() 558 | } 559 | 560 | type InfixExpression struct { 561 | Token token.Item // The operator token, e.g. + 562 | Left Expression 563 | Operator string 564 | Right Expression 565 | } 566 | 567 | func (ie *InfixExpression) Item() token.Item { return ie.Token } 568 | func (ie *InfixExpression) expressionNode() {} 569 | func (ie *InfixExpression) TokenLiteral() string { return ie.Token.Value } 570 | func (ie *InfixExpression) String() string { 571 | var out bytes.Buffer 572 | 573 | out.WriteString(ie.Left.String()) 574 | if ie.Operator != "::" && ie.Operator != "$" && ie.Operator != ".." { 575 | out.WriteString(" ") 576 | } 577 | 578 | if ie.Operator != ".." { 579 | out.WriteString(ie.Operator) 580 | } 581 | 582 | if ie.Operator == ".." { 583 | out.WriteString(":") 584 | } 585 | 586 | if ie.Operator != "::" && ie.Operator != "$" && ie.Operator != ".." { 587 | out.WriteString(" ") 588 | } 589 | 590 | if ie.Right != nil { 591 | out.WriteString(ie.Right.String()) 592 | } 593 | 594 | return out.String() 595 | } 596 | 597 | type IfExpression struct { 598 | Token token.Item // The 'if' token 599 | Condition Expression 600 | Consequence *BlockStatement 601 | Alternative *BlockStatement 602 | } 603 | 604 | func (ie *IfExpression) Item() token.Item { return ie.Token } 605 | func (ie *IfExpression) expressionNode() {} 606 | func (ie *IfExpression) TokenLiteral() string { return ie.Token.Value } 607 | func (ie *IfExpression) String() string { 608 | var out bytes.Buffer 609 | 610 | out.WriteString("if(") 611 | out.WriteString(ie.Condition.String()) 612 | out.WriteString("){\n") 613 | out.WriteString(ie.Consequence.String()) 614 | out.WriteString("}") 615 | 616 | if ie.Alternative != nil { 617 | out.WriteString(" else {\n") 618 | out.WriteString(ie.Alternative.String()) 619 | out.WriteString("\n}\n") 620 | } 621 | 622 | return out.String() 623 | } 624 | 625 | type FunctionLiteral struct { 626 | Token token.Item // The 'func' token 627 | Name string 628 | NameToken token.Item 629 | Operator string 630 | MethodVariable string 631 | Method *Type 632 | ReturnType Types 633 | Parameters []*Parameter 634 | Body *BlockStatement 635 | } 636 | 637 | func (fl *FunctionLiteral) Item() token.Item { return fl.Token } 638 | func (fl *FunctionLiteral) expressionNode() {} 639 | func (fl *FunctionLiteral) TokenLiteral() string { return fl.Token.Value } 640 | func (fl *FunctionLiteral) String() string { 641 | var out bytes.Buffer 642 | 643 | params := []string{} 644 | for _, p := range fl.Parameters { 645 | params = append(params, p.String()) 646 | } 647 | 648 | if fl.Name != "" { 649 | out.WriteString("#' @yield ") 650 | for i, v := range fl.ReturnType { 651 | out.WriteString(v.Name) 652 | if i < len(fl.ReturnType)-1 { 653 | out.WriteString(" | ") 654 | } 655 | out.WriteString("\n") 656 | } 657 | out.WriteString(fl.Name) 658 | } 659 | 660 | if fl.Method != nil { 661 | out.WriteString("." + fl.Method.Name) 662 | } 663 | 664 | if fl.Operator != "" { 665 | out.WriteString(" " + fl.Operator + " ") 666 | } 667 | 668 | out.WriteString("function") 669 | out.WriteString("(") 670 | out.WriteString(strings.Join(params, ", ")) 671 | out.WriteString(") {\n") 672 | if fl.Body != nil { 673 | out.WriteString(fl.Body.String()) 674 | } 675 | out.WriteString("}\n") 676 | 677 | return out.String() 678 | } 679 | 680 | type Parameter struct { 681 | Token token.Item // The 'func' token 682 | Name string 683 | Operator string 684 | Type Types 685 | Default *ExpressionStatement 686 | Method bool 687 | } 688 | 689 | func (p *Parameter) Item() token.Item { return p.Token } 690 | func (p *Parameter) expressionNode() {} 691 | func (p *Parameter) TokenLiteral() string { return p.Token.Value } 692 | func (p *Parameter) String() string { 693 | var out bytes.Buffer 694 | 695 | out.WriteString(p.Name) 696 | if p.Operator != "" { 697 | out.WriteString(" " + p.Operator + " " + p.Default.String()) 698 | } 699 | return out.String() 700 | } 701 | 702 | type Argument struct { 703 | Token token.Item 704 | Name string 705 | Value Expression 706 | } 707 | 708 | type CallExpression struct { 709 | Token token.Item // The '(' token 710 | Function string // Identifier or FunctionLiteral 711 | Name string 712 | Arguments []Argument 713 | } 714 | 715 | func (ce *CallExpression) Item() token.Item { return ce.Token } 716 | func (ce *CallExpression) expressionNode() {} 717 | func (ce *CallExpression) TokenLiteral() string { return ce.Token.Value } 718 | func (ce *CallExpression) String() string { 719 | var out bytes.Buffer 720 | 721 | args := []string{} 722 | for _, a := range ce.Arguments { 723 | args = append(args, a.Value.String()) 724 | } 725 | 726 | out.WriteString(ce.Function) 727 | out.WriteString("(") 728 | out.WriteString(strings.Join(args, ", ")) 729 | out.WriteString(")") 730 | 731 | return out.String() 732 | } 733 | 734 | type DecoratorEnvironment struct { 735 | Token token.Item 736 | Arguments []Argument 737 | Type *TypeStatement 738 | } 739 | 740 | func (d *DecoratorEnvironment) Item() token.Item { return d.Token } 741 | func (d *DecoratorEnvironment) expressionNode() {} 742 | func (d *DecoratorEnvironment) TokenLiteral() string { return d.Token.Value } 743 | func (d *DecoratorEnvironment) String() string { 744 | var out bytes.Buffer 745 | 746 | out.WriteString("# environment: ") 747 | for _, arg := range d.Arguments { 748 | out.WriteString(arg.Name) 749 | } 750 | out.WriteString(d.Type.String()) 751 | 752 | return out.String() 753 | } 754 | 755 | type DecoratorMatrix struct { 756 | Token token.Item 757 | Arguments []Argument 758 | Type *TypeStatement 759 | } 760 | 761 | func (d *DecoratorMatrix) Item() token.Item { return d.Token } 762 | func (d *DecoratorMatrix) expressionNode() {} 763 | func (d *DecoratorMatrix) TokenLiteral() string { return d.Token.Value } 764 | func (d *DecoratorMatrix) String() string { 765 | var out bytes.Buffer 766 | 767 | out.WriteString("# matrix: ") 768 | for _, arg := range d.Arguments { 769 | out.WriteString(arg.Name) 770 | } 771 | out.WriteString(d.Type.String()) 772 | 773 | return out.String() 774 | } 775 | 776 | type DecoratorFactor struct { 777 | Token token.Item 778 | Arguments []Argument 779 | Type *TypeStatement 780 | } 781 | 782 | func (d *DecoratorFactor) Item() token.Item { return d.Token } 783 | func (d *DecoratorFactor) expressionNode() {} 784 | func (d *DecoratorFactor) TokenLiteral() string { return d.Token.Value } 785 | func (d *DecoratorFactor) String() string { 786 | var out bytes.Buffer 787 | 788 | out.WriteString("# matrix: ") 789 | for _, arg := range d.Arguments { 790 | out.WriteString(arg.Name) 791 | } 792 | out.WriteString(d.Type.String()) 793 | 794 | return out.String() 795 | } 796 | 797 | type DecoratorClass struct { 798 | Token token.Item // The 'class' token 799 | Classes []string 800 | Type *TypeStatement 801 | } 802 | 803 | func (d *DecoratorClass) Item() token.Item { return d.Token } 804 | func (d *DecoratorClass) expressionNode() {} 805 | func (d *DecoratorClass) TokenLiteral() string { return d.Token.Value } 806 | func (d *DecoratorClass) String() string { 807 | var out bytes.Buffer 808 | 809 | out.WriteString("# classes: ") 810 | out.WriteString(strings.Join(d.Classes, ",")) 811 | out.WriteString("\n") 812 | out.WriteString(d.Type.String()) 813 | 814 | return out.String() 815 | } 816 | 817 | type DecoratorGeneric struct { 818 | Token token.Item // The 'generic' token 819 | Func Expression 820 | } 821 | 822 | func (d *DecoratorGeneric) Item() token.Item { return d.Token } 823 | func (d *DecoratorGeneric) expressionNode() {} 824 | func (d *DecoratorGeneric) TokenLiteral() string { return d.Token.Value } 825 | func (d *DecoratorGeneric) String() string { 826 | var out bytes.Buffer 827 | 828 | out.WriteString("# classes") 829 | out.WriteString(d.Func.String()) 830 | out.WriteString("\n") 831 | 832 | return out.String() 833 | } 834 | 835 | type DecoratorDefault struct { 836 | Token token.Item // The 'generic' token 837 | Func Expression 838 | } 839 | 840 | func (d *DecoratorDefault) Item() token.Item { return d.Token } 841 | func (d *DecoratorDefault) expressionNode() {} 842 | func (d *DecoratorDefault) TokenLiteral() string { return d.Token.Value } 843 | func (d *DecoratorDefault) String() string { 844 | var out bytes.Buffer 845 | 846 | out.WriteString("# default") 847 | out.WriteString(d.Func.String()) 848 | out.WriteString("\n") 849 | 850 | return out.String() 851 | } 852 | -------------------------------------------------------------------------------- /cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | var cache = make(map[string]interface{}) 4 | 5 | func Set(key string, value interface{}) { 6 | cache[key] = value 7 | } 8 | 9 | func Get(key string) (interface{}, bool) { 10 | v, ok := cache[key] 11 | return v, ok 12 | } 13 | 14 | func Has(key string) bool { 15 | _, ok := cache[key] 16 | return ok 17 | } 18 | 19 | func Clear() { 20 | cache = make(map[string]interface{}) 21 | } 22 | -------------------------------------------------------------------------------- /cli/cli.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "runtime" 6 | ) 7 | 8 | var Reset = "\033[0m" 9 | var Red = "\033[31m" 10 | var Green = "\033[32m" 11 | var Yellow = "\033[33m" 12 | var Blue = "\033[34m" 13 | var Purple = "\033[35m" 14 | var Cyan = "\033[36m" 15 | var Gray = "\033[37m" 16 | var White = "\033[97m" 17 | var Bold = "\033[1m" 18 | 19 | func init() { 20 | if runtime.GOOS == "windows" { 21 | Reset = "" 22 | Red = "" 23 | Green = "" 24 | Yellow = "" 25 | Blue = "" 26 | Purple = "" 27 | Cyan = "" 28 | Gray = "" 29 | White = "" 30 | } 31 | } 32 | 33 | type CLI struct { 34 | Indir *string 35 | Outdir *string 36 | LSP *bool 37 | TCP *bool 38 | Port *string 39 | Repl *bool 40 | Help *bool 41 | Version *bool 42 | Check *bool 43 | Run *bool 44 | Types *string 45 | Infile *string 46 | Outfile *string 47 | Devtools *string 48 | } 49 | 50 | func Cli() CLI { 51 | // inputs 52 | indir := flag.String("indir", "", "Directory of vapour files to process") 53 | outdir := flag.String("outdir", "R", "Directory where to place transpiled files from `dir` (defaults to R)") 54 | infile := flag.String("infile", "", "Vapour file to process") 55 | outfile := flag.String("outfile", "vapour.R", "Name of R file to where to palce transpiled `infile`. (defaults to vapour.R)") 56 | 57 | // types 58 | types := flag.String("types", "inst/types.vp", "Path where to generate the type files, only applies if passing a directory with -indir") 59 | 60 | // run type checker 61 | check := flag.Bool("check-only", false, "Run type checker") 62 | 63 | // lsp 64 | lsp := flag.Bool("lsp", false, "Run the language server protocol") 65 | tcp := flag.Bool("tcp", false, "Run the language server protocol on TCP") 66 | port := flag.String("port", "3000", "Port on which to run the language server protocol, only used if -tcp flag is passed (defaults to 3000)") 67 | 68 | // run 69 | run := flag.Bool("run-only", false, "Run the transpiled vapour files") 70 | 71 | // repl 72 | repl := flag.Bool("repl", false, "Run the REPL") 73 | 74 | // version 75 | version := flag.Bool("version", false, "Retrieve vapour version") 76 | 77 | // devtools 78 | devtools := flag.String("devtools", "", "Run {devtools} functions after transpilation, accepts `document`, `check`, `install`, separate by comma (e.g.: `document,check`)") 79 | 80 | flag.Parse() 81 | 82 | return CLI{ 83 | Indir: indir, 84 | Outdir: outdir, 85 | LSP: lsp, 86 | TCP: tcp, 87 | Port: port, 88 | Infile: infile, 89 | Outfile: outfile, 90 | Repl: repl, 91 | Check: check, 92 | Run: run, 93 | Version: version, 94 | Types: types, 95 | Devtools: devtools, 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "path" 7 | ) 8 | 9 | var file string = ".vapour" 10 | 11 | type lspConfig struct { 12 | When []string `json:"when"` 13 | Severity []string `json:"severity"` 14 | } 15 | 16 | type Config struct { 17 | Lsp *lspConfig `json:"lsp"` 18 | } 19 | 20 | func makeConfigPath(conf string) string { 21 | dirname, err := os.UserHomeDir() 22 | 23 | if err != nil { 24 | return "" 25 | } 26 | 27 | return path.Join(dirname, conf) 28 | } 29 | 30 | func hasConfig(conf string) bool { 31 | p := makeConfigPath(conf) 32 | 33 | if p == "" { 34 | return false 35 | } 36 | 37 | _, err := os.Stat(p) 38 | 39 | return !os.IsNotExist(err) 40 | } 41 | 42 | func ReadConfig() *Config { 43 | configuration := &Config{ 44 | Lsp: &lspConfig{ 45 | When: []string{"open", "save", "close", "text"}, 46 | Severity: []string{"fatal", "warn", "info", "hint"}, 47 | }, 48 | } 49 | 50 | if !hasConfig(file) { 51 | return configuration 52 | } 53 | 54 | p := makeConfigPath(file) 55 | data, err := os.ReadFile(p) 56 | 57 | if err != nil { 58 | return configuration 59 | } 60 | 61 | err = json.Unmarshal(data, configuration) 62 | 63 | if err != nil { 64 | return configuration 65 | } 66 | 67 | return configuration 68 | } 69 | -------------------------------------------------------------------------------- /devtools/cli.go: -------------------------------------------------------------------------------- 1 | package devtools 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/vapourlang/vapour/cli" 8 | "github.com/vapourlang/vapour/r" 9 | ) 10 | 11 | func Run(valid bool, args cli.CLI) { 12 | if !valid { 13 | return 14 | } 15 | 16 | if *args.Devtools == "" { 17 | return 18 | } 19 | 20 | commands := strings.Split(*args.Devtools, ",") 21 | 22 | for _, c := range commands { 23 | cmd := "devtools::" + c + "()" 24 | out, err := r.Callr(cmd) 25 | 26 | if err != nil { 27 | fmt.Println(err.Error()) 28 | continue 29 | } 30 | 31 | fmt.Println(string(out[:])) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /diagnostics/diagnostics.go: -------------------------------------------------------------------------------- 1 | package diagnostics 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/vapourlang/vapour/cli" 8 | "github.com/vapourlang/vapour/token" 9 | ) 10 | 11 | // To match LSP specs 12 | type Severity int 13 | 14 | const ( 15 | Fatal Severity = iota 16 | Warn 17 | Hint 18 | Info 19 | ) 20 | 21 | type Diagnostic struct { 22 | Token token.Item 23 | Message string 24 | Severity Severity 25 | } 26 | 27 | type Diagnostics []Diagnostic 28 | 29 | func New(token token.Item, message string, severity Severity) Diagnostic { 30 | return Diagnostic{ 31 | Token: token, 32 | Message: message, 33 | Severity: severity, 34 | } 35 | } 36 | 37 | func NewError(token token.Item, message string) Diagnostic { 38 | return Diagnostic{ 39 | Token: token, 40 | Message: message, 41 | Severity: Fatal, 42 | } 43 | } 44 | 45 | func NewWarning(token token.Item, message string) Diagnostic { 46 | return Diagnostic{ 47 | Token: token, 48 | Message: message, 49 | Severity: Warn, 50 | } 51 | } 52 | 53 | func NewInfo(token token.Item, message string) Diagnostic { 54 | return Diagnostic{ 55 | Token: token, 56 | Message: message, 57 | Severity: Info, 58 | } 59 | } 60 | 61 | func NewHint(token token.Item, message string) Diagnostic { 62 | return Diagnostic{ 63 | Token: token, 64 | Message: message, 65 | Severity: Hint, 66 | } 67 | } 68 | 69 | func (d Diagnostics) String() string { 70 | var out bytes.Buffer 71 | 72 | for _, v := range d { 73 | out.WriteString(v.String()) 74 | } 75 | 76 | return out.String() 77 | } 78 | 79 | func (v Diagnostic) String() string { 80 | var out bytes.Buffer 81 | out.WriteString("[" + prefix(v.Severity) + "]\t") 82 | out.WriteString(v.Token.File) 83 | out.WriteString(":") 84 | out.WriteString(fmt.Sprintf("%v", v.Token.Line)) 85 | out.WriteString(":") 86 | out.WriteString(fmt.Sprintf("%v", v.Token.Char)) 87 | out.WriteString(" " + v.Message + "\n") 88 | return out.String() 89 | } 90 | 91 | func (d Diagnostics) Print() { 92 | fmt.Printf("%v", d.String()) 93 | } 94 | 95 | func prefix(s Severity) string { 96 | if s == Fatal { 97 | return cli.Red + "ERROR" + cli.Reset 98 | } 99 | 100 | if s == Warn { 101 | return cli.Yellow + "WARN" + cli.Reset 102 | } 103 | 104 | if s == Info { 105 | return cli.Blue + "INFO" + cli.Reset 106 | } 107 | 108 | return cli.Green + "HINT" + cli.Reset 109 | } 110 | 111 | func (ds Diagnostics) UniqueLine() Diagnostics { 112 | uniques := Diagnostics{} 113 | 114 | set := make(map[int]bool) 115 | for _, d := range ds { 116 | _, ok := set[d.Token.Line] 117 | 118 | if ok { 119 | continue 120 | } 121 | 122 | set[d.Token.Line] = true 123 | 124 | uniques = append(uniques, d) 125 | } 126 | 127 | return uniques 128 | } 129 | 130 | func (ds Diagnostics) Unique() Diagnostics { 131 | uniques := Diagnostics{} 132 | 133 | set := make(map[string]bool) 134 | for _, d := range ds { 135 | key := fmt.Sprintf("%d%d", d.Token.Line, d.Token.Char) 136 | _, ok := set[key] 137 | 138 | if ok { 139 | continue 140 | } 141 | 142 | set[key] = true 143 | 144 | uniques = append(uniques, d) 145 | } 146 | 147 | return uniques 148 | } 149 | -------------------------------------------------------------------------------- /environment/environment.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/vapourlang/vapour/ast" 7 | "github.com/vapourlang/vapour/r" 8 | ) 9 | 10 | type Environment struct { 11 | variables map[string]Variable 12 | types map[string]Type 13 | functions map[string]Function 14 | class map[string]Class 15 | matrix map[string]Matrix 16 | factor map[string]Factor 17 | signature map[string]Signature 18 | method map[string]Methods 19 | env map[string]Env 20 | returnType ast.Types 21 | outer *Environment 22 | } 23 | 24 | var library []string 25 | 26 | func SetLibrary(paths []string) { 27 | library = paths 28 | } 29 | 30 | func Enclose(outer *Environment, t ast.Types) *Environment { 31 | env := New() 32 | env.returnType = t 33 | env.outer = outer 34 | return env 35 | } 36 | 37 | func (env *Environment) ReturnType() ast.Types { 38 | ret := env.returnType 39 | if ret == nil && env.outer != nil { 40 | return env.outer.ReturnType() 41 | } 42 | return ret 43 | } 44 | 45 | func Open(env *Environment) *Environment { 46 | return env.outer 47 | } 48 | 49 | // types 50 | var baseTypes = []string{ 51 | "int", 52 | "any", 53 | "num", 54 | "raw", 55 | "char", 56 | "bool", 57 | "null", 58 | "date", 59 | "complex", 60 | "posixlt", 61 | "posixct", 62 | } 63 | 64 | // objects 65 | var baseObjects = []string{ 66 | "list", 67 | "object", 68 | "factor", 69 | "matrix", 70 | "vector", 71 | "struct", 72 | "dataframe", 73 | "environment", 74 | "impliedList", 75 | } 76 | 77 | func NewGlobalEnvironment() *Environment { 78 | v := make(map[string]Variable) 79 | t := make(map[string]Type) 80 | f := make(map[string]Function) 81 | c := make(map[string]Class) 82 | m := make(map[string]Matrix) 83 | s := make(map[string]Signature) 84 | fct := make(map[string]Factor) 85 | meth := make(map[string]Methods) 86 | e := make(map[string]Env) 87 | 88 | env := &Environment{ 89 | functions: f, 90 | variables: v, 91 | types: t, 92 | class: c, 93 | matrix: m, 94 | signature: s, 95 | env: e, 96 | factor: fct, 97 | method: meth, 98 | outer: nil, 99 | } 100 | 101 | for _, t := range baseTypes { 102 | env.SetType(Type{Used: true, Name: t, Type: []*ast.Type{{Name: t, List: false}}}) 103 | } 104 | 105 | fns, err := r.ListBaseFunctions() 106 | 107 | if err != nil { 108 | fmt.Printf("failed to fetch base R functions: %v", err.Error()) 109 | return env 110 | } 111 | 112 | for _, pkg := range fns { 113 | for _, fn := range pkg.Functions { 114 | env.SetFunction(fn, Function{Value: &ast.FunctionLiteral{}, Package: pkg.Name}) 115 | } 116 | } 117 | 118 | return env 119 | } 120 | 121 | func New() *Environment { 122 | v := make(map[string]Variable) 123 | t := make(map[string]Type) 124 | f := make(map[string]Function) 125 | c := make(map[string]Class) 126 | m := make(map[string]Matrix) 127 | s := make(map[string]Signature) 128 | fct := make(map[string]Factor) 129 | meth := make(map[string]Methods) 130 | 131 | return &Environment{ 132 | functions: f, 133 | variables: v, 134 | types: t, 135 | class: c, 136 | matrix: m, 137 | signature: s, 138 | factor: fct, 139 | method: meth, 140 | outer: nil, 141 | } 142 | } 143 | 144 | func (e *Environment) SetSignatureUsed(name string) (Signature, bool) { 145 | obj, ok := e.signature[name] 146 | 147 | if !ok && e.outer != nil { 148 | return e.outer.SetSignatureUsed(name) 149 | } 150 | 151 | obj.Used = true 152 | e.signature[name] = obj 153 | 154 | return obj, ok 155 | } 156 | 157 | func (e *Environment) SetTypeUsed(pkg, name string) (Type, bool) { 158 | obj, ok := e.types[makeTypeKey(pkg, name)] 159 | 160 | if !ok && e.outer != nil { 161 | return e.outer.SetTypeUsed(pkg, name) 162 | } 163 | 164 | obj.Used = true 165 | e.types[name] = obj 166 | 167 | return obj, ok 168 | } 169 | 170 | func (e *Environment) GetVariable(name string, outer bool) (Variable, bool) { 171 | obj, ok := e.variables[name] 172 | if !ok && e.outer != nil && outer { 173 | obj, ok = e.outer.GetVariable(name, outer) 174 | } 175 | return obj, ok 176 | } 177 | 178 | func (e *Environment) GetVariableParent(name string) (Variable, bool) { 179 | if e.outer == nil { 180 | return Variable{}, false 181 | } 182 | 183 | obj, ok := e.outer.GetVariable(name, true) 184 | 185 | return obj, ok 186 | } 187 | 188 | func (e *Environment) SetVariable(name string, val Variable) Variable { 189 | e.variables[name] = val 190 | return val 191 | } 192 | 193 | func (e *Environment) SetVariableUsed(name string) (Variable, bool) { 194 | obj, ok := e.variables[name] 195 | 196 | if !ok && e.outer != nil { 197 | return e.outer.SetVariableUsed(name) 198 | } 199 | 200 | obj.Used = true 201 | e.variables[name] = obj 202 | 203 | return obj, ok 204 | } 205 | 206 | func (e *Environment) SetVariableNotMissing(name string) { 207 | v, exists := e.GetVariable(name, false) 208 | 209 | if !exists { 210 | return 211 | } 212 | 213 | v.CanMiss = false 214 | e.SetVariable(name, v) 215 | } 216 | 217 | func makeTypeKey(pkg, name string) string { 218 | if pkg == "" { 219 | return name 220 | } 221 | 222 | return pkg + "::" + name 223 | } 224 | 225 | func (e *Environment) GetType(pkg, name string) (Type, bool) { 226 | e.LoadPackageTypes(pkg) 227 | obj, ok := e.types[makeTypeKey(pkg, name)] 228 | if !ok && e.outer != nil { 229 | obj, ok = e.outer.GetType(pkg, name) 230 | } 231 | 232 | if ok { 233 | e.SetTypeUsed(pkg, name) 234 | } 235 | 236 | return obj, ok 237 | } 238 | 239 | func (e *Environment) SetType(val Type) Type { 240 | e.types[makeTypeKey(val.Package, val.Name)] = val 241 | return val 242 | } 243 | 244 | func (e *Environment) GetFunction(name string, outer bool) (Function, bool) { 245 | obj, ok := e.functions[name] 246 | if !ok && e.outer != nil && outer { 247 | obj, ok = e.outer.GetFunction(name, outer) 248 | } 249 | return obj, ok 250 | } 251 | 252 | func (e *Environment) SetFunction(name string, val Function) Function { 253 | e.functions[name] = val 254 | return val 255 | } 256 | 257 | func (e *Environment) GetClass(name string) (Class, bool) { 258 | obj, ok := e.class[name] 259 | if !ok && e.outer != nil { 260 | obj, ok = e.outer.GetClass(name) 261 | } 262 | return obj, ok 263 | } 264 | 265 | func (e *Environment) SetClass(name string, val Class) Class { 266 | e.class[name] = val 267 | return val 268 | } 269 | 270 | func (e *Environment) GetEnv(name string) (Env, bool) { 271 | obj, ok := e.env[name] 272 | if !ok && e.outer != nil { 273 | obj, ok = e.outer.GetEnv(name) 274 | } 275 | return obj, ok 276 | } 277 | 278 | func (e *Environment) SetEnv(name string, val Env) Env { 279 | e.env[name] = val 280 | return val 281 | } 282 | 283 | func (e *Environment) GetFactor(name string) (Factor, bool) { 284 | obj, ok := e.factor[name] 285 | if !ok && e.outer != nil { 286 | obj, ok = e.outer.GetFactor(name) 287 | } 288 | return obj, ok 289 | } 290 | 291 | func (e *Environment) SetFactor(name string, val Factor) Factor { 292 | e.factor[name] = val 293 | return val 294 | } 295 | 296 | func (e *Environment) GetSignature(name string) (Signature, bool) { 297 | obj, ok := e.signature[name] 298 | if !ok && e.outer != nil { 299 | obj, ok = e.outer.GetSignature(name) 300 | } 301 | return obj, ok 302 | } 303 | 304 | func (e *Environment) SetSignature(name string, val Signature) Signature { 305 | e.signature[name] = val 306 | return val 307 | } 308 | 309 | func (e *Environment) GetMatrix(name string) (Matrix, bool) { 310 | obj, ok := e.matrix[name] 311 | if !ok && e.outer != nil { 312 | obj, ok = e.outer.GetMatrix(name) 313 | } 314 | return obj, ok 315 | } 316 | 317 | func (e *Environment) SetMatrix(name string, val Matrix) Matrix { 318 | e.matrix[name] = val 319 | return val 320 | } 321 | 322 | func (e *Environment) AddMethod(name string, val Method) Method { 323 | e.method[name] = append(e.method[name], val) 324 | return val 325 | } 326 | 327 | func (e *Environment) GetMethods(name string) (Methods, bool) { 328 | obj, ok := e.method[name] 329 | if !ok && e.outer != nil { 330 | obj, ok = e.outer.GetMethods(name) 331 | } 332 | return obj, ok 333 | } 334 | 335 | func (e *Environment) GetMethod(name string, t *ast.Type) (Method, bool) { 336 | obj, ok := e.method[name] 337 | if !ok && e.outer != nil { 338 | obj, ok = e.outer.GetMethods(name) 339 | } 340 | 341 | if !ok { 342 | return Method{}, false 343 | } 344 | 345 | for _, o := range obj { 346 | if o.Value.Method == t { 347 | return o, true 348 | } 349 | } 350 | 351 | return Method{}, false 352 | } 353 | 354 | func (e *Environment) HasMethods(name string, t *ast.Type) bool { 355 | obj, ok := e.GetMethods(name) 356 | 357 | if !ok { 358 | return false 359 | } 360 | 361 | for _, o := range obj { 362 | if o.Value.Method == t { 363 | return true 364 | } 365 | } 366 | 367 | return false 368 | } 369 | 370 | func (e *Environment) Types() map[string]Type { 371 | return e.types 372 | } 373 | 374 | func (e *Environment) Variables() map[string]Variable { 375 | return e.variables 376 | } 377 | -------------------------------------------------------------------------------- /environment/object.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "github.com/vapourlang/vapour/ast" 5 | "github.com/vapourlang/vapour/token" 6 | ) 7 | 8 | // this should be an interface but I haven't got the time right now 9 | type Function struct { 10 | Token token.Item 11 | Package string 12 | Value *ast.FunctionLiteral 13 | Name string 14 | } 15 | 16 | type Methods []Method 17 | 18 | type Method struct { 19 | Token token.Item 20 | Package string 21 | Value *ast.FunctionLiteral 22 | Name string 23 | } 24 | 25 | type Variable struct { 26 | Token token.Item 27 | Value ast.Types 28 | HasValue bool 29 | CanMiss bool 30 | IsConst bool 31 | Used bool 32 | Name string 33 | } 34 | 35 | type Type struct { 36 | Token token.Item 37 | Type ast.Types 38 | Package string 39 | Used bool 40 | Object string 41 | Name string 42 | Attributes []*ast.TypeAttributesStatement 43 | } 44 | 45 | type Class struct { 46 | Token token.Item 47 | Value *ast.DecoratorClass 48 | } 49 | 50 | type Matrix struct { 51 | Token token.Item 52 | Value *ast.DecoratorMatrix 53 | } 54 | 55 | type Factor struct { 56 | Token token.Item 57 | Value *ast.DecoratorFactor 58 | } 59 | 60 | type Env struct { 61 | Token token.Item 62 | Value *ast.DecoratorEnvironment 63 | } 64 | 65 | type Signature struct { 66 | Token token.Item 67 | Value *ast.TypeFunction 68 | Used bool 69 | } 70 | -------------------------------------------------------------------------------- /environment/types.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path" 7 | "strings" 8 | 9 | "github.com/vapourlang/vapour/ast" 10 | "github.com/vapourlang/vapour/lexer" 11 | "github.com/vapourlang/vapour/parser" 12 | ) 13 | 14 | type Code struct { 15 | lines []string 16 | } 17 | 18 | func (c *Code) add(line string) { 19 | c.lines = append(c.lines, line) 20 | } 21 | 22 | func (c *Code) String() string { 23 | return strings.Join(c.lines, "\n") 24 | } 25 | 26 | func (e *Environment) GenerateTypes() *Code { 27 | code := &Code{} 28 | 29 | for typeName, typeObject := range e.types { 30 | if IsNativeType(typeName) || IsNativeObject(typeName) { 31 | continue 32 | } 33 | 34 | class, classExists := e.GetClass(typeName) 35 | 36 | if classExists { 37 | code.add("@class(" + strings.Join(class.Value.Classes, ", ") + ")") 38 | } 39 | 40 | curlyLeft := "{" 41 | if len(typeObject.Attributes) == 0 { 42 | curlyLeft = "" 43 | } 44 | 45 | if typeObject.Object != "impliedList" { 46 | code.add("type " + typeName + ": " + typeObject.Object + " " + curlyLeft) 47 | } 48 | 49 | if typeObject.Object == "impliedList" { 50 | code.add("type " + typeName + ": " + collaseTypes(typeObject.Type) + " " + curlyLeft) 51 | } 52 | 53 | if len(typeObject.Attributes) == 0 { 54 | continue 55 | } 56 | 57 | for i, a := range typeObject.Attributes { 58 | sep := "" 59 | if i < len(typeObject.Attributes)-1 { 60 | sep = "," 61 | } 62 | code.add("\t" + a.Name + ": " + collaseTypes(a.Type) + sep) 63 | } 64 | code.add("}") 65 | } 66 | 67 | return code 68 | } 69 | 70 | func collaseTypes(types []*ast.Type) string { 71 | var str []string 72 | 73 | for _, t := range types { 74 | typeString := "" 75 | if t.List { 76 | typeString += "[]" 77 | } 78 | 79 | typeString += t.Name 80 | 81 | str = append(str, typeString) 82 | } 83 | 84 | return strings.Join(str, " | ") 85 | } 86 | 87 | func IsNativeType(name string) bool { 88 | for _, t := range baseTypes { 89 | if name == t { 90 | return true 91 | } 92 | } 93 | return false 94 | } 95 | 96 | func IsNativeObject(name string) bool { 97 | for _, t := range baseObjects { 98 | if name == t { 99 | return true 100 | } 101 | } 102 | return false 103 | } 104 | 105 | var packagesLoaded []string 106 | 107 | func isLoaded(library string) bool { 108 | for _, p := range packagesLoaded { 109 | if p == library { 110 | return true 111 | } 112 | } 113 | 114 | return false 115 | } 116 | 117 | func (env *Environment) LoadPackageTypes(pkg string) { 118 | if len(library) == 0 { 119 | return 120 | } 121 | 122 | if isLoaded(pkg) { 123 | return 124 | } 125 | 126 | packagesLoaded = append(packagesLoaded, pkg) 127 | 128 | for _, lib := range library { 129 | typeFile := path.Join(lib, pkg, "types.vp") 130 | 131 | if _, err := os.Stat(typeFile); errors.Is(err, os.ErrNotExist) { 132 | continue 133 | } 134 | 135 | content, err := os.ReadFile(typeFile) 136 | 137 | if err != nil { 138 | continue 139 | } 140 | 141 | // lex 142 | l := lexer.NewCode(typeFile, string(content)) 143 | l.Run() 144 | 145 | if l.HasError() { 146 | continue 147 | } 148 | 149 | // parse 150 | p := parser.New(l) 151 | prog := p.Run() 152 | 153 | if p.HasError() { 154 | continue 155 | } 156 | 157 | // range over the Statements 158 | // these should all be type declarations 159 | for _, p := range prog.Statements { 160 | switch node := p.(type) { 161 | case *ast.TypeStatement: 162 | env.SetType( 163 | Type{ 164 | Token: node.Token, 165 | Type: node.Type, 166 | Attributes: node.Attributes, 167 | Object: node.Object, 168 | Name: node.Name, 169 | Package: pkg, 170 | Used: true, 171 | }, 172 | ) 173 | } 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/vapourlang/vapour 2 | 3 | go 1.22.0 4 | 5 | require github.com/tliron/glsp v0.2.2 6 | 7 | require ( 8 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 9 | github.com/gorilla/websocket v1.5.3 // indirect 10 | github.com/iancoleman/strcase v0.3.0 // indirect 11 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 12 | github.com/mattn/go-isatty v0.0.20 // indirect 13 | github.com/mattn/go-runewidth v0.0.16 // indirect 14 | github.com/muesli/termenv v0.15.2 // indirect 15 | github.com/petermattis/goid v0.0.0-20240716203034-badd1c0974d6 // indirect 16 | github.com/pkg/errors v0.9.1 // indirect 17 | github.com/rivo/uniseg v0.4.7 // indirect 18 | github.com/sasha-s/go-deadlock v0.3.1 // indirect 19 | github.com/segmentio/ksuid v1.0.4 // indirect 20 | github.com/sourcegraph/jsonrpc2 v0.2.0 // indirect 21 | github.com/tliron/commonlog v0.2.18 // indirect 22 | github.com/tliron/kutil v0.3.25 // indirect 23 | golang.org/x/crypto v0.26.0 // indirect 24 | golang.org/x/sys v0.24.0 // indirect 25 | golang.org/x/term v0.23.0 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 2 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 3 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 4 | github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= 5 | github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 6 | github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= 7 | github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 8 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 9 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 10 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 11 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 12 | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 13 | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 14 | github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= 15 | github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= 16 | github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= 17 | github.com/petermattis/goid v0.0.0-20240716203034-badd1c0974d6 h1:DUDJI8T/9NcGbbL+AWk6vIYlmQ8ZBS8LZqVre6zbkPQ= 18 | github.com/petermattis/goid v0.0.0-20240716203034-badd1c0974d6/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= 19 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 20 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 21 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 22 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 23 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 24 | github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= 25 | github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= 26 | github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= 27 | github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= 28 | github.com/sourcegraph/jsonrpc2 v0.2.0 h1:KjN/dC4fP6aN9030MZCJs9WQbTOjWHhrtKVpzzSrr/U= 29 | github.com/sourcegraph/jsonrpc2 v0.2.0/go.mod h1:ZafdZgk/axhT1cvZAPOhw+95nz2I/Ra5qMlU4gTRwIo= 30 | github.com/tliron/commonlog v0.2.18 h1:F0zY09VDGTasPCpP9KvE8xqqVNMUfwMJQ0Xvo5Y6BRs= 31 | github.com/tliron/commonlog v0.2.18/go.mod h1:7f3OMSgVyGAFbRKwlvfUErnB6U75LgW8wa6NlWuswGg= 32 | github.com/tliron/glsp v0.2.2 h1:IKPfwpE8Lu8yB6Dayta+IyRMAbTVunudeauEgjXBt+c= 33 | github.com/tliron/glsp v0.2.2/go.mod h1:GMVWDNeODxHzmDPvYbYTCs7yHVaEATfYtXiYJ9w1nBg= 34 | github.com/tliron/kutil v0.3.25 h1:oaPN6K0zsH3KcVnsocA3kAlfR0XYDzADob6xdjqe56k= 35 | github.com/tliron/kutil v0.3.25/go.mod h1:ZvOJuF6PTGvjfHmn2dFcgz+EDEzRQqQUztK+7djlXIw= 36 | golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= 37 | golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= 38 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 39 | golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= 40 | golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 41 | golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= 42 | golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= 43 | -------------------------------------------------------------------------------- /lexer/lex.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "unicode/utf8" 7 | 8 | "github.com/vapourlang/vapour/diagnostics" 9 | "github.com/vapourlang/vapour/token" 10 | ) 11 | 12 | type File struct { 13 | Path string 14 | Content []byte 15 | } 16 | 17 | type Files []File 18 | 19 | type Lexer struct { 20 | Files Files 21 | filePos int 22 | input string 23 | start int 24 | pos int 25 | width int 26 | line int // line number 27 | char int // character number in line 28 | Items token.Items 29 | errors diagnostics.Diagnostics 30 | } 31 | 32 | const stringNumber = "0123456789" 33 | const stringAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 34 | const stringAlphaNum = stringAlpha + stringNumber 35 | const stringMathOp = "+-*/^" 36 | 37 | func New(fl Files) *Lexer { 38 | return &Lexer{ 39 | Files: fl, 40 | } 41 | } 42 | 43 | func NewCode(fl, code string) *Lexer { 44 | return New( 45 | Files{ 46 | {Path: fl, Content: []byte(code)}, 47 | }, 48 | ) 49 | } 50 | 51 | func NewTest(code string) *Lexer { 52 | return New( 53 | Files{ 54 | {Path: "test.vp", Content: []byte(code)}, 55 | }, 56 | ) 57 | } 58 | 59 | func (l *Lexer) HasError() bool { 60 | return len(l.errors) > 0 61 | } 62 | 63 | func (l *Lexer) Errors() diagnostics.Diagnostics { 64 | return l.errors.UniqueLine() 65 | } 66 | 67 | func (l *Lexer) errorf(format string, args ...interface{}) stateFn { 68 | err := token.Item{ 69 | Char: l.char, 70 | Pos: l.pos, 71 | Line: l.line, 72 | Class: token.ItemError, 73 | Value: fmt.Sprintf(format, args...), 74 | File: l.Files[l.filePos].Path, 75 | } 76 | l.errors = append(l.errors, diagnostics.NewError(err, err.Value)) 77 | return nil 78 | } 79 | 80 | func (l *Lexer) emit(t token.ItemType) { 81 | // skip empty tokens 82 | if l.start == l.pos { 83 | return 84 | } 85 | 86 | l.Items = append(l.Items, token.Item{ 87 | Char: l.char, 88 | Line: l.line, 89 | Pos: l.pos, 90 | Class: t, 91 | Value: l.input[l.start:l.pos], 92 | File: l.Files[l.filePos].Path, 93 | }) 94 | l.start = l.pos 95 | } 96 | 97 | func (l *Lexer) emitEOF() { 98 | l.Items = append(l.Items, token.Item{Class: token.ItemEOF, Value: "EOF"}) 99 | } 100 | 101 | // returns currently accepted token 102 | func (l *Lexer) token() string { 103 | return l.input[l.start:l.pos] 104 | } 105 | 106 | // next returns the next rune in the input. 107 | func (l *Lexer) next() rune { 108 | if l.pos >= len(l.input) { 109 | l.width = 0 110 | return token.EOF 111 | } 112 | 113 | r, w := utf8.DecodeRuneInString(l.input[l.pos:]) 114 | l.width = w 115 | l.pos += l.width 116 | l.char += l.width 117 | return r 118 | } 119 | 120 | func (l *Lexer) ignore() { 121 | l.start = l.pos 122 | } 123 | 124 | func (l *Lexer) backup() { 125 | l.pos -= l.width 126 | l.char -= l.width 127 | } 128 | 129 | func (l *Lexer) peek(n int) rune { 130 | var r rune 131 | for i := 0; i < n; i++ { 132 | r = l.next() 133 | } 134 | 135 | for i := 0; i < n; i++ { 136 | l.backup() 137 | } 138 | 139 | return r 140 | } 141 | 142 | type stateFn func(*Lexer) stateFn 143 | 144 | func (l *Lexer) Run() { 145 | for i, f := range l.Files { 146 | l.filePos = i 147 | l.input = string(f.Content) + "\n" 148 | l.width = 0 149 | l.pos = 0 150 | l.start = 0 151 | l.line = 0 152 | l.char = 0 153 | l.Lex() 154 | 155 | // remove the EOF 156 | if i < len(l.Files)-1 { 157 | l.Items = l.Items[:len(l.Items)-1] 158 | } 159 | } 160 | } 161 | 162 | func (l *Lexer) Lex() { 163 | for state := lexDefault; state != nil; { 164 | state = state(l) 165 | } 166 | } 167 | 168 | func lexDefault(l *Lexer) stateFn { 169 | r1 := l.peek(1) 170 | 171 | if r1 == token.EOF { 172 | l.emitEOF() 173 | return nil 174 | } 175 | 176 | if r1 == '"' { 177 | l.next() 178 | l.emit(token.ItemDoubleQuote) 179 | return l.lexString('"') 180 | } 181 | 182 | if r1 == '\'' { 183 | l.next() 184 | l.emit(token.ItemSingleQuote) 185 | return l.lexString('\'') 186 | } 187 | 188 | if r1 == '#' { 189 | return lexComment 190 | } 191 | 192 | if r1 == '@' { 193 | l.next() 194 | l.ignore() 195 | return lexDecorator 196 | } 197 | 198 | // we parsed strings: we skip spaces and tabs 199 | if r1 == ' ' || r1 == '\t' { 200 | l.next() 201 | l.ignore() 202 | return lexDefault 203 | } 204 | 205 | if r1 == '\n' || r1 == '\r' { 206 | l.next() 207 | l.emit(token.ItemNewLine) 208 | l.line++ 209 | l.char = 0 210 | return lexDefault 211 | } 212 | 213 | // peek one more rune 214 | r2 := l.peek(2) 215 | 216 | if r1 == '[' && r2 == '[' { 217 | l.next() 218 | l.next() 219 | l.emit(token.ItemDoubleLeftSquare) 220 | return lexDefault 221 | } 222 | 223 | if r1 == ']' && r2 == ']' { 224 | l.next() 225 | l.next() 226 | l.emit(token.ItemDoubleRightSquare) 227 | return lexDefault 228 | } 229 | 230 | if r1 == '[' { 231 | l.next() 232 | l.emit(token.ItemLeftSquare) 233 | return lexDefault 234 | } 235 | 236 | if r1 == ']' { 237 | l.next() 238 | l.emit(token.ItemRightSquare) 239 | return lexDefault 240 | } 241 | 242 | if r1 == '.' && r2 == '.' && l.peek(3) == '.' { 243 | l.next() 244 | l.next() 245 | l.next() 246 | l.emit(token.ItemThreeDot) 247 | return lexDefault 248 | } 249 | 250 | if r1 == '.' && r2 == '.' { 251 | l.next() 252 | l.next() 253 | l.emit(token.ItemRange) 254 | return lexDefault 255 | } 256 | 257 | // if it's not %% it's an infix 258 | if r1 == '%' && r2 != '%' { 259 | return lexInfix 260 | } 261 | 262 | // it's a modulus 263 | if r1 == '%' && r2 == '%' { 264 | l.next() 265 | l.next() 266 | l.emit(token.ItemModulus) 267 | return lexDefault 268 | } 269 | 270 | if r1 == '=' && r2 == '=' { 271 | l.next() 272 | l.next() 273 | l.emit(token.ItemDoubleEqual) 274 | return lexDefault 275 | } 276 | 277 | if r1 == '!' && r2 == '=' { 278 | l.next() 279 | l.next() 280 | l.emit(token.ItemNotEqual) 281 | return lexDefault 282 | } 283 | 284 | if r1 == '!' { 285 | l.next() 286 | l.emit(token.ItemBang) 287 | return lexDefault 288 | } 289 | 290 | if r1 == '>' && r2 == '=' { 291 | l.next() 292 | l.next() 293 | l.emit(token.ItemGreaterOrEqual) 294 | return lexDefault 295 | } 296 | 297 | if r1 == '<' && r2 == '=' { 298 | l.next() 299 | l.next() 300 | l.emit(token.ItemLessOrEqual) 301 | return lexDefault 302 | } 303 | 304 | if r1 == '<' && r2 == ' ' { 305 | l.next() 306 | l.emit(token.ItemLessThan) 307 | return lexDefault 308 | } 309 | 310 | if r1 == '>' && r2 == ' ' { 311 | l.next() 312 | l.emit(token.ItemGreaterThan) 313 | return lexDefault 314 | } 315 | 316 | if r1 == '<' && r2 == '-' { 317 | l.next() 318 | l.next() 319 | l.emit(token.ItemAssignParent) 320 | return lexDefault 321 | } 322 | 323 | if r1 == ':' && r2 == ':' && l.peek(3) == ':' { 324 | l.next() 325 | l.next() 326 | l.next() 327 | l.emit(token.ItemNamespaceInternal) 328 | return lexIdentifier 329 | } 330 | 331 | if r1 == ':' && r2 == ':' { 332 | l.next() 333 | l.next() 334 | l.emit(token.ItemNamespace) 335 | return lexIdentifier 336 | } 337 | 338 | if r1 == '=' && r2 == '>' { 339 | l.next() 340 | l.next() 341 | l.emit(token.ItemArrow) 342 | return lexDefault 343 | } 344 | 345 | // we also emit namespace:: (above) 346 | // so we can assume this is not 347 | if r1 == ':' { 348 | l.next() 349 | l.emit(token.ItemColon) 350 | return lexType 351 | } 352 | 353 | if r1 == '&' { 354 | l.next() 355 | l.emit(token.ItemAnd) 356 | return lexDefault 357 | } 358 | 359 | if r1 == '|' && r2 == '>' { 360 | l.next() 361 | l.next() 362 | l.emit(token.ItemPipe) 363 | return lexDefault 364 | } 365 | 366 | if r1 == '|' { 367 | l.next() 368 | l.emit(token.ItemOr) 369 | return lexDefault 370 | } 371 | 372 | if r1 == '$' { 373 | l.next() 374 | l.emit(token.ItemDollar) 375 | return lexAttribute 376 | } 377 | 378 | if r1 == ',' { 379 | l.next() 380 | l.emit(token.ItemComma) 381 | return lexDefault 382 | } 383 | 384 | if r1 == '=' { 385 | l.next() 386 | l.emit(token.ItemAssign) 387 | return lexDefault 388 | } 389 | 390 | if r1 == '(' { 391 | l.next() 392 | l.emit(token.ItemLeftParen) 393 | return lexDefault 394 | } 395 | 396 | if r1 == ')' { 397 | l.next() 398 | l.emit(token.ItemRightParen) 399 | return lexIdentifier 400 | } 401 | 402 | if r1 == '{' { 403 | l.next() 404 | l.emit(token.ItemLeftCurly) 405 | return lexDefault 406 | } 407 | 408 | if r1 == '}' { 409 | l.next() 410 | l.emit(token.ItemRightCurly) 411 | return lexDefault 412 | } 413 | 414 | if r1 == '[' && r2 == '[' { 415 | l.next() 416 | l.emit(token.ItemDoubleLeftSquare) 417 | return lexDefault 418 | } 419 | 420 | if r1 == '[' { 421 | l.next() 422 | l.emit(token.ItemLeftSquare) 423 | return lexDefault 424 | } 425 | 426 | if r1 == ']' && r2 == ']' { 427 | l.next() 428 | l.emit(token.ItemDoubleRightSquare) 429 | return lexDefault 430 | } 431 | 432 | if r1 == ']' { 433 | l.next() 434 | l.emit(token.ItemRightSquare) 435 | return lexDefault 436 | } 437 | 438 | if r1 == '?' { 439 | l.next() 440 | l.emit(token.ItemQuestion) 441 | return lexDefault 442 | } 443 | 444 | if r1 == '`' { 445 | l.next() 446 | l.emit(token.ItemBacktick) 447 | return lexDefault 448 | } 449 | 450 | if r1 == '+' && r2 == '=' { 451 | l.next() 452 | l.next() 453 | l.emit(token.ItemAssignInc) 454 | return lexDefault 455 | } 456 | 457 | if r1 == '-' && r2 == '=' { 458 | l.next() 459 | l.next() 460 | l.emit(token.ItemAssignDec) 461 | return lexDefault 462 | } 463 | 464 | if l.acceptNumber() { 465 | return lexNumber 466 | } 467 | 468 | if l.acceptMathOp() { 469 | return lexMathOp 470 | } 471 | 472 | if l.acceptAlphaNumeric() { 473 | return lexIdentifier 474 | } 475 | 476 | l.next() 477 | return lexDefault 478 | } 479 | 480 | func lexDecorator(l *Lexer) stateFn { 481 | l.acceptRun(stringAlpha + "_") 482 | 483 | tok := l.token() 484 | if tok == "generic" { 485 | l.emit(token.ItemDecoratorGeneric) 486 | return lexDefault 487 | } 488 | 489 | if tok == "default" { 490 | l.emit(token.ItemDecoratorDefault) 491 | return lexDefault 492 | } 493 | 494 | if tok == "class" { 495 | l.emit(token.ItemDecoratorClass) 496 | } 497 | 498 | if tok == "matrix" { 499 | l.emit(token.ItemDecoratorMatrix) 500 | } 501 | 502 | if tok == "factor" { 503 | l.emit(token.ItemDecoratorFactor) 504 | } 505 | 506 | if tok == "environment" { 507 | l.emit(token.ItemDecoratorEnvironment) 508 | } 509 | 510 | r := l.peek(1) 511 | 512 | if r != '(' && tok == "class" { 513 | l.errorf("expecting (, gor `%c`", r) 514 | return lexDefault 515 | } 516 | 517 | l.next() 518 | 519 | l.emit(token.ItemLeftParen) 520 | 521 | return lexIdentifier 522 | } 523 | 524 | func lexMathOp(l *Lexer) stateFn { 525 | l.acceptRun(stringMathOp) 526 | 527 | tk := l.token() 528 | 529 | if tk == "+" { 530 | l.emit(token.ItemPlus) 531 | } 532 | 533 | if tk == "-" { 534 | l.emit(token.ItemMinus) 535 | } 536 | 537 | if tk == "*" { 538 | l.emit(token.ItemMultiply) 539 | } 540 | 541 | if tk == "/" { 542 | l.emit(token.ItemDivide) 543 | } 544 | 545 | if tk == "^" { 546 | l.emit(token.ItemPower) 547 | } 548 | 549 | return lexDefault 550 | } 551 | 552 | func lexNumber(l *Lexer) stateFn { 553 | l.acceptRun(stringNumber) 554 | 555 | r1 := l.peek(1) 556 | r2 := l.peek(2) 557 | 558 | if r1 == 'e' { 559 | l.next() 560 | l.acceptRun(stringNumber) 561 | } 562 | 563 | if r1 == '.' && r2 == '.' { 564 | l.emit(token.ItemInteger) 565 | l.next() 566 | l.next() 567 | l.emit(token.ItemRange) 568 | return lexNumber 569 | } 570 | 571 | if l.accept(".") { 572 | l.acceptRun(stringNumber) 573 | l.emit(token.ItemFloat) 574 | return lexDefault 575 | } 576 | 577 | l.emit(token.ItemInteger) 578 | return lexDefault 579 | } 580 | 581 | func lexComment(l *Lexer) stateFn { 582 | r := l.peek(1) 583 | for r != '\n' && r != token.EOF { 584 | l.next() 585 | r = l.peek(1) 586 | } 587 | 588 | l.emit(token.ItemComment) 589 | 590 | return lexDefault 591 | } 592 | 593 | func (l *Lexer) lexString(closing rune) func(l *Lexer) stateFn { 594 | return func(l *Lexer) stateFn { 595 | var c rune 596 | r := l.peek(1) 597 | for r != closing && r != token.EOF { 598 | c = l.next() 599 | r = l.peek(1) 600 | } 601 | 602 | // this means the closing is escaped so 603 | // it's not in fact closing: 604 | // we move the cursor and keep parsing string 605 | // e.g.: "hello \"world\"" 606 | if c == '\\' && r == closing { 607 | l.next() 608 | return l.lexString(closing) 609 | } 610 | 611 | if r == token.EOF { 612 | l.next() 613 | return l.errorf("expecting closing quote, got %v", l.token()) 614 | } 615 | 616 | l.emit(token.ItemString) 617 | 618 | r = l.next() 619 | 620 | if r == '"' { 621 | l.emit(token.ItemDoubleQuote) 622 | } 623 | 624 | if r == '\'' { 625 | l.emit(token.ItemSingleQuote) 626 | } 627 | 628 | return lexDefault 629 | } 630 | } 631 | 632 | func lexInfix(l *Lexer) stateFn { 633 | l.next() 634 | r := l.peek(1) 635 | for r != '%' && r != token.EOF { 636 | l.next() 637 | r = l.peek(1) 638 | } 639 | 640 | if r == token.EOF { 641 | l.next() 642 | return l.errorf("expecting closing %%, got %v", l.token()) 643 | } 644 | 645 | l.next() 646 | 647 | l.emit(token.ItemInfix) 648 | 649 | return lexDefault 650 | } 651 | 652 | func lexIdentifier(l *Lexer) stateFn { 653 | l.acceptRun(stringAlphaNum + "_.") 654 | 655 | if l.peek(1) == '.' && l.peek(2) != '.' { 656 | l.acceptRun(stringAlphaNum + "_") 657 | } 658 | 659 | tk := l.token() 660 | 661 | if tk == "TRUE" || tk == "FALSE" || tk == "true" || tk == "false" { 662 | l.emit(token.ItemBool) 663 | return lexDefault 664 | } 665 | 666 | if tk == "if" { 667 | l.emit(token.ItemIf) 668 | return lexDefault 669 | } 670 | 671 | if tk == "else" { 672 | l.emit(token.ItemElse) 673 | return lexDefault 674 | } 675 | 676 | if tk == "return" { 677 | l.emit(token.ItemReturn) 678 | return lexDefault 679 | } 680 | 681 | if tk == "NULL" { 682 | l.emit(token.ItemNULL) 683 | return lexDefault 684 | } 685 | 686 | if tk == "NA" { 687 | l.emit(token.ItemNA) 688 | return lexDefault 689 | } 690 | 691 | if tk == "inf" { 692 | l.emit(token.ItemInf) 693 | return lexDefault 694 | } 695 | 696 | if tk == "while" { 697 | l.emit(token.ItemWhile) 698 | return lexDefault 699 | } 700 | 701 | if tk == "for" { 702 | l.emit(token.ItemFor) 703 | return lexFor 704 | } 705 | 706 | if tk == "repeat" { 707 | l.emit(token.ItemRepeat) 708 | return lexDefault 709 | } 710 | 711 | if tk == "next" { 712 | l.emit(token.ItemNext) 713 | return lexDefault 714 | } 715 | 716 | if tk == "break" { 717 | l.emit(token.ItemBreak) 718 | return lexDefault 719 | } 720 | 721 | if tk == "func" { 722 | l.emit(token.ItemFunction) 723 | return lexFunc 724 | } 725 | 726 | if tk == "nan" { 727 | l.emit(token.ItemNan) 728 | return lexDefault 729 | } 730 | 731 | if tk == "in" { 732 | l.emit(token.ItemIn) 733 | return lexDefault 734 | } 735 | 736 | if tk == "let" { 737 | l.emit(token.ItemLet) 738 | return lexLet 739 | } 740 | 741 | if tk == "const" { 742 | l.emit(token.ItemConst) 743 | return lexLet 744 | } 745 | 746 | if tk == "type" { 747 | l.emit(token.ItemTypesDecl) 748 | return lexTypeDeclaration 749 | } 750 | 751 | if tk == "defer" { 752 | l.emit(token.ItemDefer) 753 | return lexDefault 754 | } 755 | 756 | l.emit(token.ItemIdent) 757 | return lexDefault 758 | } 759 | 760 | func lexFor(l *Lexer) stateFn { 761 | r := l.peek(1) 762 | if r == ' ' { 763 | l.next() 764 | l.ignore() 765 | } 766 | 767 | if r == '\t' { 768 | l.next() 769 | l.ignore() 770 | } 771 | 772 | r = l.peek(1) 773 | 774 | if r != '(' { 775 | l.errorf("expecting `(`, got `%c`", r) 776 | return lexDefault 777 | } 778 | 779 | l.next() 780 | l.emit(token.ItemLeftParen) 781 | 782 | return lexIdentifier 783 | } 784 | 785 | func lexFunc(l *Lexer) stateFn { 786 | r := l.peek(1) 787 | 788 | if r == ' ' { 789 | l.next() 790 | l.ignore() 791 | } 792 | 793 | if r == '\t' { 794 | l.next() 795 | l.ignore() 796 | } 797 | 798 | r = l.peek(1) 799 | 800 | // method 801 | if r == '(' { 802 | l.next() 803 | l.emit(token.ItemLeftParen) 804 | return lexMethod 805 | } 806 | 807 | // function 808 | return lexIdentifier 809 | } 810 | 811 | func lexMethod(l *Lexer) stateFn { 812 | // first param in R 813 | l.acceptRun(stringAlpha + "_") 814 | l.emit(token.ItemIdent) 815 | 816 | r := l.peek(1) 817 | 818 | if r == ' ' { 819 | l.next() 820 | l.ignore() 821 | } 822 | 823 | if r == '\t' { 824 | l.next() 825 | l.ignore() 826 | } 827 | 828 | // type 829 | l.acceptRun(stringAlpha + "_") 830 | l.emit(token.ItemTypes) 831 | 832 | r = l.peek(1) 833 | 834 | if r == ')' { 835 | l.next() 836 | l.emit(token.ItemRightParen) 837 | } 838 | 839 | r = l.peek(1) 840 | 841 | if r == ' ' { 842 | l.next() 843 | l.ignore() 844 | } 845 | 846 | if r == '\t' { 847 | l.next() 848 | l.ignore() 849 | } 850 | 851 | // method name 852 | l.acceptRun(stringAlpha + "_") 853 | l.emit(token.ItemIdent) 854 | 855 | return lexIdentifier 856 | } 857 | 858 | func lexTypeDeclaration(l *Lexer) stateFn { 859 | r := l.peek(1) 860 | 861 | if r != ' ' { 862 | l.errorf("expecting a space, got `%c`", r) 863 | return lexDefault 864 | } 865 | 866 | // ignore space 867 | l.next() 868 | l.ignore() 869 | 870 | // emit custom type 871 | l.acceptRun(stringAlphaNum + "_") 872 | l.emit(token.ItemTypes) 873 | 874 | // emit colon 875 | r = l.peek(1) 876 | 877 | if r != ':' { 878 | l.errorf("expecting `:`, got `%c`", r) 879 | return lexDefault 880 | } 881 | 882 | l.next() 883 | l.emit(token.ItemColon) 884 | 885 | // ignore space 886 | l.next() 887 | l.ignore() 888 | 889 | // emit custom type 890 | l.acceptRun(stringAlphaNum + "_") 891 | 892 | tok := l.token() 893 | if tok == "struct" { 894 | l.emit(token.ItemObjStruct) 895 | return lexStruct 896 | } 897 | 898 | if tok == "list" { 899 | l.emit(token.ItemObjList) 900 | return lexStruct 901 | } 902 | 903 | if tok == "object" { 904 | l.emit(token.ItemObjObject) 905 | return lexDefault 906 | } 907 | 908 | if tok == "environment" { 909 | l.emit(token.ItemObjEnvironment) 910 | return lexDefault 911 | } 912 | 913 | if tok == "dataframe" { 914 | l.emit(token.ItemObjDataframe) 915 | return lexDefault 916 | } 917 | 918 | if tok == "matrix" { 919 | l.emit(token.ItemObjMatrix) 920 | return lexStruct 921 | } 922 | 923 | if tok == "factor" { 924 | l.emit(token.ItemObjFactor) 925 | return lexStruct 926 | } 927 | 928 | if tok == "func" { 929 | l.emit(token.ItemObjFunc) 930 | return lexFuncSignature 931 | } 932 | 933 | l.emit(token.ItemTypes) 934 | 935 | return lexType 936 | } 937 | 938 | func lexFuncSignature(l *Lexer) stateFn { 939 | if l.peek(1) != '(' { 940 | l.errorf("expecting `(`, got `%c`", l.peek(1)) 941 | } 942 | 943 | l.next() 944 | l.emit(token.ItemLeftParen) 945 | 946 | return lexFuncSignatureArg 947 | } 948 | 949 | func lexFuncSignatureArg(l *Lexer) stateFn { 950 | if l.peek(1) == '[' { 951 | l.next() 952 | l.next() 953 | l.emit(token.ItemTypesList) 954 | } 955 | 956 | l.acceptRun(stringAlpha) 957 | l.emit(token.ItemTypes) 958 | 959 | if l.peek(1) == ',' { 960 | l.next() 961 | l.emit(token.ItemComma) 962 | if l.peek(1) == ' ' { 963 | l.next() 964 | l.ignore() 965 | } 966 | return lexFuncSignatureArg 967 | } 968 | 969 | if l.peek(1) == ')' { 970 | l.next() 971 | l.emit(token.ItemRightParen) 972 | return lexType 973 | } 974 | 975 | return lexDefault 976 | } 977 | 978 | func lexStruct(l *Lexer) stateFn { 979 | if l.peek(1) == ' ' { 980 | l.next() 981 | l.ignore() 982 | } 983 | 984 | if l.peek(1) == '\t' { 985 | l.next() 986 | l.ignore() 987 | } 988 | 989 | r := l.peek(1) 990 | if r != '{' { 991 | l.errorf("expecting `{`, got `%c`", r) 992 | } 993 | 994 | // skip curly 995 | l.next() 996 | l.emit(token.ItemLeftCurly) 997 | 998 | for l.peek(1) == '\n' || l.peek(1) == ' ' || l.peek(1) == '\t' { 999 | if l.peek(1) == '\n' { 1000 | l.line++ 1001 | l.char = 0 1002 | } 1003 | l.next() 1004 | l.ignore() 1005 | } 1006 | 1007 | return lexType 1008 | } 1009 | 1010 | func lexAttribute(l *Lexer) stateFn { 1011 | l.acceptRun(stringAlpha + "._") 1012 | 1013 | l.emit(token.ItemAttribute) 1014 | 1015 | return lexDefault 1016 | } 1017 | 1018 | func lexLet(l *Lexer) stateFn { 1019 | r := l.peek(1) 1020 | 1021 | if r != ' ' { 1022 | l.errorf("expecting a space, got `%c`", r) 1023 | return lexDefault 1024 | } 1025 | 1026 | // ignore the space 1027 | l.next() 1028 | l.ignore() 1029 | 1030 | l.acceptRun(stringAlphaNum + "_.") 1031 | 1032 | l.emit(token.ItemIdent) 1033 | 1034 | r = l.peek(1) 1035 | 1036 | if r != ':' { 1037 | l.errorf("expecting `:` got `%c`", r) 1038 | return nil 1039 | } 1040 | 1041 | // ignore the colon 1042 | l.next() 1043 | l.emit(token.ItemColon) 1044 | 1045 | return lexType 1046 | } 1047 | 1048 | func lexType(l *Lexer) stateFn { 1049 | if l.peek(1) == ':' { 1050 | l.next() 1051 | l.emit(token.ItemColon) 1052 | } 1053 | 1054 | if l.peek(1) == ' ' { 1055 | l.next() 1056 | l.ignore() 1057 | } 1058 | 1059 | if l.peek(1) == '|' { 1060 | l.next() 1061 | l.emit(token.ItemOr) 1062 | return lexType 1063 | } 1064 | 1065 | if l.peek(1) == '[' && l.peek(2) == ']' { 1066 | l.next() 1067 | l.next() 1068 | l.emit(token.ItemTypesList) 1069 | } 1070 | 1071 | l.acceptRun(stringAlpha + "_.") 1072 | 1073 | if l.token() == "in" { 1074 | l.emit(token.ItemIn) 1075 | return lexDefault 1076 | } 1077 | 1078 | if l.peek(1) == ':' && l.peek(2) == ':' { 1079 | l.emit(token.ItemTypesPkg) 1080 | l.next() 1081 | l.next() 1082 | l.emit(token.ItemNamespace) 1083 | return lexType 1084 | } else { 1085 | l.acceptRun(stringAlpha + "_.") 1086 | l.emit(token.ItemTypes) 1087 | } 1088 | 1089 | if l.peek(1) == ' ' { 1090 | l.next() 1091 | l.ignore() 1092 | return lexType 1093 | } 1094 | 1095 | if l.peek(1) == '\t' { 1096 | l.next() 1097 | l.ignore() 1098 | return lexType 1099 | } 1100 | 1101 | if l.peek(1) == '|' { 1102 | l.next() 1103 | l.emit(token.ItemOr) 1104 | return lexType 1105 | } 1106 | 1107 | return lexDefault 1108 | } 1109 | 1110 | func (l *Lexer) acceptNumber() bool { 1111 | return l.accept(stringNumber) 1112 | } 1113 | 1114 | func (l *Lexer) acceptMathOp() bool { 1115 | return l.accept(stringMathOp) 1116 | } 1117 | 1118 | func (l *Lexer) acceptAlphaNumeric() bool { 1119 | return l.accept(stringAlphaNum) 1120 | } 1121 | 1122 | func (l *Lexer) accept(rs string) bool { 1123 | for strings.IndexRune(rs, l.next()) >= 0 { 1124 | return true 1125 | } 1126 | l.backup() 1127 | return false 1128 | } 1129 | 1130 | func (l *Lexer) acceptRun(valid string) { 1131 | for strings.ContainsRune(valid, l.next()) { 1132 | } 1133 | l.backup() 1134 | } 1135 | -------------------------------------------------------------------------------- /lexer/lex_test.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/vapourlang/vapour/token" 7 | ) 8 | 9 | func TestDeclare(t *testing.T) { 10 | code := `let x: int | na = 1 11 | const y: char = "hello" 12 | ` 13 | 14 | l := NewTest(code) 15 | 16 | l.Run() 17 | 18 | if len(l.Items) == 0 { 19 | t.Fatal("No Items where lexed") 20 | } 21 | 22 | tokens := 23 | []token.ItemType{ 24 | token.ItemLet, 25 | token.ItemIdent, 26 | token.ItemColon, 27 | token.ItemTypes, 28 | token.ItemOr, 29 | token.ItemTypes, 30 | token.ItemAssign, 31 | token.ItemInteger, 32 | token.ItemNewLine, 33 | token.ItemConst, 34 | token.ItemIdent, 35 | token.ItemColon, 36 | token.ItemTypes, 37 | token.ItemAssign, 38 | token.ItemDoubleQuote, 39 | token.ItemString, 40 | token.ItemDoubleQuote, 41 | } 42 | 43 | for i, token := range tokens { 44 | actual := l.Items[i].Class 45 | if actual != token { 46 | t.Fatalf( 47 | "token %v expected `%v`, got `%v`", 48 | i, 49 | token, 50 | actual, 51 | ) 52 | } 53 | } 54 | } 55 | 56 | func TestSimpleTypes(t *testing.T) { 57 | code := `type userid: int 58 | type something: char | null 59 | ` 60 | 61 | l := NewTest(code) 62 | 63 | l.Run() 64 | 65 | if len(l.Items) == 0 { 66 | t.Fatal("No Items where lexed") 67 | } 68 | 69 | tokens := 70 | []token.ItemType{ 71 | token.ItemTypesDecl, 72 | token.ItemTypes, 73 | token.ItemColon, 74 | token.ItemTypes, 75 | token.ItemNewLine, 76 | token.ItemTypesDecl, 77 | token.ItemTypes, 78 | token.ItemColon, 79 | token.ItemTypes, 80 | token.ItemOr, 81 | token.ItemTypes, 82 | } 83 | 84 | for i, token := range tokens { 85 | actual := l.Items[i].Class 86 | if actual != token { 87 | t.Fatalf( 88 | "token %v expected `%v`, got `%v`", 89 | i, 90 | token, 91 | actual, 92 | ) 93 | } 94 | } 95 | } 96 | 97 | func TestObjectTypes(t *testing.T) { 98 | code := `type thing: object { 99 | id: int, 100 | name: char 101 | } 102 | 103 | type lst: list { num | na } 104 | 105 | type df: dataframe { 106 | name: char, 107 | id: int 108 | } 109 | 110 | type multiple: []int 111 | ` 112 | 113 | l := NewTest(code) 114 | 115 | l.Run() 116 | 117 | if len(l.Items) == 0 { 118 | t.Fatal("No Items where lexed") 119 | } 120 | 121 | tokens := 122 | []token.ItemType{ 123 | token.ItemTypesDecl, 124 | token.ItemTypes, 125 | token.ItemColon, 126 | token.ItemObjObject, 127 | token.ItemLeftCurly, 128 | token.ItemNewLine, 129 | token.ItemIdent, 130 | token.ItemColon, 131 | token.ItemTypes, 132 | token.ItemComma, 133 | token.ItemNewLine, 134 | token.ItemIdent, 135 | token.ItemColon, 136 | token.ItemTypes, 137 | token.ItemNewLine, 138 | token.ItemRightCurly, 139 | token.ItemNewLine, 140 | token.ItemNewLine, 141 | token.ItemTypesDecl, 142 | token.ItemTypes, 143 | token.ItemColon, 144 | } 145 | 146 | for i, token := range tokens { 147 | actual := l.Items[i].Class 148 | if actual != token { 149 | t.Fatalf( 150 | "token %v expected `%v`, got `%v`", 151 | i, 152 | token, 153 | actual, 154 | ) 155 | } 156 | } 157 | } 158 | 159 | func TestComment(t *testing.T) { 160 | code := `# this is a comment 161 | 162 | # this is another comment 163 | ` 164 | 165 | l := NewTest(code) 166 | 167 | l.Run() 168 | 169 | if len(l.Items) == 0 { 170 | t.Fatal("No Items where lexed") 171 | } 172 | 173 | tokens := 174 | []token.ItemType{ 175 | token.ItemComment, 176 | token.ItemNewLine, 177 | token.ItemNewLine, 178 | token.ItemComment, 179 | token.ItemNewLine, 180 | } 181 | 182 | for i, token := range tokens { 183 | actual := l.Items[i].Class 184 | if actual != token { 185 | t.Fatalf( 186 | "token %v expected `%v`, got `%v`", 187 | i, 188 | token, 189 | actual, 190 | ) 191 | } 192 | } 193 | } 194 | 195 | func TestCall(t *testing.T) { 196 | code := `print(1) 197 | 198 | sum(1, 2.3, 3) 199 | 200 | foo(x = 1, y = 2, 'hello') 201 | ` 202 | 203 | l := NewTest(code) 204 | 205 | l.Run() 206 | 207 | if len(l.Items) == 0 { 208 | t.Fatal("No Items where lexed") 209 | } 210 | 211 | tokens := 212 | []token.ItemType{ 213 | token.ItemIdent, 214 | token.ItemLeftParen, 215 | token.ItemInteger, 216 | token.ItemRightParen, 217 | token.ItemNewLine, 218 | token.ItemNewLine, 219 | token.ItemIdent, 220 | token.ItemLeftParen, 221 | token.ItemInteger, 222 | token.ItemComma, 223 | token.ItemFloat, 224 | token.ItemComma, 225 | token.ItemInteger, 226 | token.ItemRightParen, 227 | token.ItemNewLine, 228 | token.ItemNewLine, 229 | token.ItemIdent, 230 | token.ItemLeftParen, 231 | token.ItemIdent, 232 | token.ItemAssign, 233 | token.ItemInteger, 234 | token.ItemComma, 235 | token.ItemIdent, 236 | token.ItemAssign, 237 | token.ItemInteger, 238 | token.ItemComma, 239 | token.ItemSingleQuote, 240 | token.ItemString, 241 | token.ItemSingleQuote, 242 | token.ItemRightParen, 243 | token.ItemNewLine, 244 | } 245 | 246 | for i, token := range tokens { 247 | actual := l.Items[i].Class 248 | if actual != token { 249 | t.Fatalf( 250 | "token %v expected `%v`, got `%v`", 251 | i, 252 | token, 253 | actual, 254 | ) 255 | } 256 | } 257 | } 258 | 259 | func TestForWhile(t *testing.T) { 260 | code := `for(let i: int in 1..10) {} 261 | 262 | while(i < 10) {} 263 | ` 264 | 265 | l := NewTest(code) 266 | 267 | l.Run() 268 | 269 | if len(l.Items) == 0 { 270 | t.Fatal("No Items where lexed") 271 | } 272 | 273 | tokens := 274 | []token.ItemType{ 275 | token.ItemFor, 276 | token.ItemLeftParen, 277 | token.ItemLet, 278 | token.ItemIdent, 279 | token.ItemColon, 280 | token.ItemTypes, 281 | token.ItemIn, 282 | token.ItemInteger, 283 | token.ItemRange, 284 | token.ItemInteger, 285 | token.ItemRightParen, 286 | token.ItemLeftCurly, 287 | token.ItemRightCurly, 288 | token.ItemNewLine, 289 | token.ItemNewLine, 290 | token.ItemWhile, 291 | token.ItemLeftParen, 292 | token.ItemIdent, 293 | token.ItemLessThan, 294 | token.ItemInteger, 295 | token.ItemRightParen, 296 | token.ItemLeftCurly, 297 | token.ItemRightCurly, 298 | } 299 | 300 | for i, token := range tokens { 301 | actual := l.Items[i].Class 302 | if actual != token { 303 | t.Fatalf( 304 | "token %v expected `%v`, got `%v`", 305 | i, 306 | token, 307 | actual, 308 | ) 309 | } 310 | } 311 | } 312 | 313 | func TestFunctionLiteral(t *testing.T) { 314 | code := `func foo(x: int, y: num = 1): num { 315 | return x + y 316 | } 317 | ` 318 | 319 | l := NewTest(code) 320 | 321 | l.Run() 322 | 323 | if len(l.Items) == 0 { 324 | t.Fatal("No Items where lexed") 325 | } 326 | 327 | tokens := 328 | []token.ItemType{ 329 | token.ItemFunction, 330 | token.ItemIdent, 331 | token.ItemLeftParen, 332 | token.ItemIdent, 333 | token.ItemColon, 334 | token.ItemTypes, 335 | token.ItemComma, 336 | token.ItemIdent, 337 | token.ItemColon, 338 | token.ItemTypes, 339 | token.ItemAssign, 340 | token.ItemInteger, 341 | token.ItemRightParen, 342 | token.ItemColon, 343 | token.ItemTypes, 344 | token.ItemLeftCurly, 345 | token.ItemNewLine, 346 | token.ItemReturn, 347 | token.ItemIdent, 348 | token.ItemPlus, 349 | token.ItemIdent, 350 | token.ItemNewLine, 351 | token.ItemRightCurly, 352 | token.ItemNewLine, 353 | } 354 | 355 | for i, token := range tokens { 356 | actual := l.Items[i].Class 357 | if actual != token { 358 | t.Fatalf( 359 | "token %v expected `%v`, got `%v`", 360 | i, 361 | token, 362 | actual, 363 | ) 364 | } 365 | } 366 | } 367 | 368 | func TestIf(t *testing.T) { 369 | code := `if(x > 2) { 370 | print(1) 371 | } else if (TRUE) { 372 | # nothing 373 | } else { 374 | # nothing 375 | } 376 | ` 377 | 378 | l := NewTest(code) 379 | 380 | l.Run() 381 | 382 | if len(l.Items) == 0 { 383 | t.Fatal("No Items where lexed") 384 | } 385 | 386 | tokens := 387 | []token.ItemType{ 388 | token.ItemIf, 389 | token.ItemLeftParen, 390 | token.ItemIdent, 391 | token.ItemGreaterThan, 392 | token.ItemInteger, 393 | token.ItemRightParen, 394 | token.ItemLeftCurly, 395 | token.ItemNewLine, 396 | token.ItemIdent, 397 | token.ItemLeftParen, 398 | token.ItemInteger, 399 | token.ItemRightParen, 400 | token.ItemNewLine, 401 | token.ItemRightCurly, 402 | token.ItemElse, 403 | token.ItemIf, 404 | token.ItemLeftParen, 405 | token.ItemBool, 406 | token.ItemRightParen, 407 | token.ItemLeftCurly, 408 | token.ItemNewLine, 409 | token.ItemComment, 410 | token.ItemNewLine, 411 | token.ItemRightCurly, 412 | token.ItemElse, 413 | token.ItemLeftCurly, 414 | token.ItemNewLine, 415 | token.ItemComment, 416 | token.ItemNewLine, 417 | token.ItemRightCurly, 418 | } 419 | 420 | for i, token := range tokens { 421 | actual := l.Items[i].Class 422 | if actual != token { 423 | t.Fatalf( 424 | "token %v expected `%v`, got `%v`", 425 | i, 426 | token, 427 | actual, 428 | ) 429 | } 430 | } 431 | } 432 | 433 | func TestIncrement(t *testing.T) { 434 | code := `let x: int = 1 435 | 436 | x += 1 437 | ` 438 | 439 | l := NewTest(code) 440 | 441 | l.Run() 442 | 443 | if len(l.Items) == 0 { 444 | t.Fatal("No Items where lexed") 445 | } 446 | 447 | tokens := 448 | []token.ItemType{ 449 | token.ItemLet, 450 | token.ItemIdent, 451 | token.ItemColon, 452 | token.ItemTypes, 453 | token.ItemAssign, 454 | token.ItemInteger, 455 | token.ItemNewLine, 456 | token.ItemNewLine, 457 | token.ItemIdent, 458 | token.ItemAssignInc, 459 | token.ItemInteger, 460 | } 461 | 462 | for i, token := range tokens { 463 | actual := l.Items[i].Class 464 | if actual != token { 465 | t.Fatalf( 466 | "token %v expected `%v`, got `%v`", 467 | i, 468 | token, 469 | actual, 470 | ) 471 | } 472 | } 473 | } 474 | 475 | func TestFuncType(t *testing.T) { 476 | code := `type fn: func(int, []num) int` 477 | 478 | l := NewTest(code) 479 | 480 | l.Run() 481 | 482 | if len(l.Items) == 0 { 483 | t.Fatal("No Items where lexed") 484 | } 485 | 486 | tokens := 487 | []token.ItemType{ 488 | token.ItemTypesDecl, 489 | token.ItemTypes, 490 | token.ItemColon, 491 | token.ItemObjFunc, 492 | token.ItemLeftParen, 493 | token.ItemTypes, 494 | token.ItemComma, 495 | token.ItemTypesList, 496 | token.ItemTypes, 497 | token.ItemRightParen, 498 | token.ItemTypes, 499 | } 500 | 501 | for i, token := range tokens { 502 | actual := l.Items[i].Class 503 | if actual != token { 504 | t.Fatalf( 505 | "token %v expected `%v`, got `%v`", 506 | i, 507 | token, 508 | actual, 509 | ) 510 | } 511 | } 512 | } 513 | 514 | func TestTypeImport(t *testing.T) { 515 | code := `let x: tibble::tbl = as.tibble(cars) 516 | let y: tibble::[]tbl | int` 517 | 518 | l := NewTest(code) 519 | 520 | l.Run() 521 | 522 | if len(l.Items) == 0 { 523 | t.Fatal("No Items where lexed") 524 | } 525 | 526 | tokens := 527 | []token.ItemType{ 528 | token.ItemLet, 529 | token.ItemIdent, 530 | token.ItemColon, 531 | token.ItemTypesPkg, 532 | token.ItemNamespace, 533 | token.ItemTypes, 534 | token.ItemAssign, 535 | token.ItemIdent, 536 | token.ItemLeftParen, 537 | token.ItemIdent, 538 | token.ItemRightParen, 539 | token.ItemNewLine, 540 | token.ItemLet, 541 | token.ItemIdent, 542 | token.ItemColon, 543 | token.ItemTypesPkg, 544 | token.ItemNamespace, 545 | token.ItemTypesList, 546 | token.ItemTypes, 547 | token.ItemOr, 548 | token.ItemTypes, 549 | } 550 | 551 | for i, token := range tokens { 552 | actual := l.Items[i].Class 553 | if actual != token { 554 | t.Fatalf( 555 | "token %v expected `%v`, got `%v`", 556 | i, 557 | token, 558 | actual, 559 | ) 560 | } 561 | } 562 | } 563 | -------------------------------------------------------------------------------- /lexer/print.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import "fmt" 4 | 5 | func (l *Lexer) Print() { 6 | fmt.Printf("Lexer with %v tokens\n", len(l.Items)) 7 | l.Items.Print() 8 | } 9 | -------------------------------------------------------------------------------- /lsp/lsp.go: -------------------------------------------------------------------------------- 1 | package lsp 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "strings" 7 | 8 | "github.com/tliron/glsp" 9 | protocol "github.com/tliron/glsp/protocol_3_16" 10 | "github.com/tliron/glsp/server" 11 | "github.com/vapourlang/vapour/config" 12 | "github.com/vapourlang/vapour/diagnostics" 13 | "github.com/vapourlang/vapour/lexer" 14 | "github.com/vapourlang/vapour/parser" 15 | "github.com/vapourlang/vapour/walker" 16 | ) 17 | 18 | var src string = "Vapour" 19 | 20 | type LSP struct { 21 | files []lexer.File 22 | conf *config.Config 23 | } 24 | 25 | type walkParams struct { 26 | TextDocument protocol.DocumentUri 27 | } 28 | 29 | var handler protocol.Handler 30 | var version string = "0.0.1" 31 | var code protocol.IntegerOrString = protocol.IntegerOrString{Value: 2} 32 | 33 | func contains(value string, values []string) bool { 34 | for _, v := range values { 35 | if v == value { 36 | return true 37 | } 38 | } 39 | 40 | return false 41 | } 42 | 43 | func New() *LSP { 44 | return &LSP{ 45 | files: []lexer.File{}, 46 | } 47 | } 48 | 49 | func Run(conf *config.Config, tcp bool, port string) { 50 | l := New() 51 | 52 | l.conf = conf 53 | 54 | handler = protocol.Handler{ 55 | Initialize: l.initialize, 56 | Initialized: l.initialized, 57 | Shutdown: l.shutdown, 58 | SetTrace: l.setTrace, 59 | } 60 | 61 | if contains("open", conf.Lsp.When) { 62 | handler.TextDocumentDidOpen = l.textDocumentDidOpen 63 | } 64 | 65 | if contains("save", conf.Lsp.When) { 66 | handler.TextDocumentDidSave = l.textDocumentDidSave 67 | } 68 | 69 | if contains("close", conf.Lsp.When) { 70 | handler.TextDocumentDidClose = l.textDocumentDidClose 71 | } 72 | 73 | if contains("change", conf.Lsp.When) { 74 | handler.TextDocumentDidChange = l.textDocumentDidChange 75 | } 76 | 77 | server := server.NewServer(&handler, "Vapour", false) 78 | 79 | var err error 80 | 81 | if tcp { 82 | err = server.RunTCP(port) 83 | 84 | if err != nil { 85 | panic(err) 86 | } 87 | } 88 | 89 | err = server.RunStdio() 90 | 91 | if err != nil { 92 | panic(err) 93 | } 94 | } 95 | 96 | func (l *LSP) initialize(context *glsp.Context, params *protocol.InitializeParams) (any, error) { 97 | capabilities := handler.CreateServerCapabilities() 98 | 99 | // incremental changes + whole document on open 100 | capabilities.TextDocumentSync = 1 101 | 102 | var supported bool = true 103 | 104 | capabilities.Workspace = &protocol.ServerCapabilitiesWorkspace{ 105 | WorkspaceFolders: &protocol.WorkspaceFoldersServerCapabilities{ 106 | Supported: &supported, 107 | }, 108 | } 109 | 110 | return protocol.InitializeResult{ 111 | Capabilities: capabilities, 112 | ServerInfo: &protocol.InitializeResultServerInfo{ 113 | Name: "vapour", 114 | Version: &version, 115 | }, 116 | }, nil 117 | } 118 | 119 | func (l *LSP) initialized(context *glsp.Context, params *protocol.InitializedParams) error { 120 | context.Notify(protocol.ServerWindowShowMessage, protocol.ShowMessageParams{ 121 | Message: "Vapour initialised", 122 | Type: protocol.MessageTypeInfo, 123 | }) 124 | return nil 125 | } 126 | 127 | func (l *LSP) shutdown(context *glsp.Context) error { 128 | protocol.SetTraceValue(protocol.TraceValueOff) 129 | return nil 130 | } 131 | 132 | func (l *LSP) setTrace(context *glsp.Context, params *protocol.SetTraceParams) error { 133 | protocol.SetTraceValue(params.Value) 134 | return nil 135 | } 136 | 137 | func (l *LSP) walkFiles(context *glsp.Context, params *walkParams) error { 138 | diagnostics := []protocol.Diagnostic{} 139 | 140 | // remove that in the future to leverage workspace 141 | // and only process files once 142 | l.files = []lexer.File{} 143 | 144 | // read directory 145 | file := strings.Replace(params.TextDocument, "file://", "", 1) 146 | root := filepath.Dir(file) 147 | err := l.readDir(root) 148 | 149 | if err != nil { 150 | context.Notify(protocol.ServerWindowShowMessage, protocol.ShowMessageParams{ 151 | Message: fmt.Sprintf("Error reading files: %v", err.Error()), 152 | Type: protocol.MessageTypeError, 153 | }) 154 | return err 155 | } 156 | 157 | // lex 158 | le := lexer.New(l.files) 159 | le.Run() 160 | 161 | if le.HasError() { 162 | diagnostics = addError(diagnostics, le.Errors(), file, l.conf.Lsp.Severity) 163 | ds := protocol.PublishDiagnosticsParams{ 164 | URI: params.TextDocument, 165 | Diagnostics: diagnostics, 166 | } 167 | context.Notify(protocol.ServerTextDocumentPublishDiagnostics, ds) 168 | return nil 169 | } 170 | 171 | // parse 172 | p := parser.New(le) 173 | prog := p.Run() 174 | 175 | if p.HasError() { 176 | diagnostics = addError(diagnostics, p.Errors(), file, l.conf.Lsp.Severity) 177 | ds := protocol.PublishDiagnosticsParams{ 178 | URI: params.TextDocument, 179 | Diagnostics: diagnostics, 180 | } 181 | context.Notify(protocol.ServerTextDocumentPublishDiagnostics, ds) 182 | return nil 183 | } 184 | 185 | // walk tree 186 | w := walker.New() 187 | w.Walk(prog) 188 | 189 | diagnostics = addError(diagnostics, w.Errors(), file, l.conf.Lsp.Severity) 190 | ds := protocol.PublishDiagnosticsParams{ 191 | URI: params.TextDocument, 192 | Diagnostics: diagnostics, 193 | } 194 | context.Notify(protocol.ServerTextDocumentPublishDiagnostics, ds) 195 | return nil 196 | } 197 | 198 | func (l *LSP) textDocumentDidOpen(context *glsp.Context, params *protocol.DidOpenTextDocumentParams) error { 199 | p := &walkParams{ 200 | TextDocument: params.TextDocument.URI, 201 | } 202 | return l.walkFiles(context, p) 203 | } 204 | 205 | func (l *LSP) textDocumentDidSave(context *glsp.Context, params *protocol.DidSaveTextDocumentParams) error { 206 | p := &walkParams{ 207 | TextDocument: params.TextDocument.URI, 208 | } 209 | return l.walkFiles(context, p) 210 | } 211 | 212 | func (l *LSP) textDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error { 213 | p := &walkParams{ 214 | TextDocument: params.TextDocument.URI, 215 | } 216 | return l.walkFiles(context, p) 217 | } 218 | 219 | func (l *LSP) textDocumentDidChange(context *glsp.Context, params *protocol.DidChangeTextDocumentParams) error { 220 | p := &walkParams{ 221 | TextDocument: params.TextDocument.URI, 222 | } 223 | return l.walkFiles(context, p) 224 | } 225 | 226 | func validSeverity(severity diagnostics.Severity, valid []string) bool { 227 | if severity == 0 && contains("fatal", valid) { 228 | return true 229 | } 230 | 231 | if severity == 1 && contains("warn", valid) { 232 | return true 233 | } 234 | 235 | if severity == 2 && contains("hint", valid) { 236 | return true 237 | } 238 | 239 | if severity == 3 && contains("info", valid) { 240 | return true 241 | } 242 | 243 | return false 244 | } 245 | 246 | func addError(ds []protocol.Diagnostic, ns diagnostics.Diagnostics, file string, include []string) []protocol.Diagnostic { 247 | for _, e := range ns { 248 | if e.Token.File != file { 249 | continue 250 | } 251 | 252 | if !validSeverity(e.Severity, include) { 253 | continue 254 | } 255 | 256 | s := protocol.DiagnosticSeverity(e.Severity) 257 | 258 | ds = append( 259 | ds, 260 | protocol.Diagnostic{ 261 | Range: protocol.Range{ 262 | Start: protocol.Position{ 263 | Line: uint32(e.Token.Line), 264 | Character: uint32(e.Token.Char - len(e.Token.Value)), 265 | }, 266 | End: protocol.Position{ 267 | Line: uint32(e.Token.Line), 268 | Character: uint32(e.Token.Char), 269 | }, 270 | }, 271 | Severity: &s, 272 | Code: &code, 273 | Source: &src, 274 | Message: e.Message, 275 | }, 276 | ) 277 | } 278 | return ds 279 | } 280 | -------------------------------------------------------------------------------- /lsp/read.go: -------------------------------------------------------------------------------- 1 | package lsp 2 | 3 | import ( 4 | "io/fs" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/vapourlang/vapour/lexer" 9 | ) 10 | 11 | func (l *LSP) readDir(root string) error { 12 | err := filepath.WalkDir(root, l.walk) 13 | 14 | if err != nil { 15 | return err 16 | } 17 | 18 | return nil 19 | } 20 | 21 | func (l *LSP) walk(path string, directory fs.DirEntry, err error) error { 22 | if err != nil { 23 | return err 24 | } 25 | 26 | if directory.IsDir() { 27 | return nil 28 | } 29 | 30 | ext := filepath.Ext(path) 31 | 32 | if ext != ".vp" { 33 | return nil 34 | } 35 | 36 | fl, err := os.ReadFile(path) 37 | 38 | if err != nil { 39 | return err 40 | } 41 | 42 | rfl := lexer.File{ 43 | Path: path, 44 | Content: fl, 45 | } 46 | 47 | l.files = append(l.files, rfl) 48 | 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/vapourlang/vapour/cli" 5 | ) 6 | 7 | func main() { 8 | v := New() 9 | args := cli.Cli() 10 | v.Run(args) 11 | } 12 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | default: rinstall 2 | go build . 3 | 4 | rinstall: rcheck 5 | cd package && R -s -e "devtools::install()" 6 | 7 | rcheck: rdocument 8 | cd package && R -s -e "devtools::check()" 9 | 10 | rdocument: 11 | cd package && R -s -e "devtools::document()" 12 | 13 | dev: rinstall 14 | go run . 15 | 16 | lsp: 17 | go build . 18 | 19 | test: 20 | go test 21 | -------------------------------------------------------------------------------- /parser/parser_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/vapourlang/vapour/lexer" 8 | ) 9 | 10 | func TestBasic(t *testing.T) { 11 | fmt.Println("---------------------------------------------------------- basic") 12 | code := `let x: int | num = 1 ` 13 | 14 | l := lexer.NewTest(code) 15 | 16 | l.Run() 17 | p := New(l) 18 | 19 | prog := p.Run() 20 | 21 | fmt.Println(prog.String()) 22 | } 23 | 24 | func TestFunc(t *testing.T) { 25 | fmt.Println("---------------------------------------------------------- func") 26 | code := `func add(x: int = 1, y: int = 2): int { 27 | let total: int = x + y * 2 28 | return total 29 | } ` 30 | 31 | l := lexer.NewTest(code) 32 | 33 | l.Run() 34 | p := New(l) 35 | 36 | prog := p.Run() 37 | 38 | fmt.Println(prog.String()) 39 | } 40 | 41 | func TestPipe(t *testing.T) { 42 | fmt.Println("---------------------------------------------------------- pipe") 43 | code := `func add(): null { 44 | df |> 45 | mutate(x = 1) 46 | } ` 47 | 48 | l := lexer.NewTest(code) 49 | 50 | l.Run() 51 | p := New(l) 52 | 53 | prog := p.Run() 54 | 55 | fmt.Println(prog.String()) 56 | } 57 | 58 | func TestString(t *testing.T) { 59 | fmt.Println("---------------------------------------------------------- string") 60 | code := `let x: string = "a \"string\"" 61 | let y: string = 'single quotes'` 62 | 63 | l := lexer.NewTest(code) 64 | 65 | l.Run() 66 | p := New(l) 67 | 68 | prog := p.Run() 69 | 70 | fmt.Println(prog.String()) 71 | } 72 | 73 | func TestComment(t *testing.T) { 74 | fmt.Println("---------------------------------------------------------- comment") 75 | code := `#' @return something 76 | func add(): int | number { 77 | # compute stuff 78 | let x: tibble = df |> 79 | mutate( 80 | x = "hello", 81 | y = na, 82 | b = true 83 | ) |> 84 | select(x) 85 | 86 | return x 87 | }` 88 | 89 | l := lexer.NewTest(code) 90 | 91 | l.Run() 92 | p := New(l) 93 | 94 | prog := p.Run() 95 | 96 | fmt.Println(prog.String()) 97 | } 98 | 99 | func TestMethod(t *testing.T) { 100 | fmt.Println("---------------------------------------------------------- method") 101 | code := `func (o: obj) method(n: int): char { 102 | return "hello" 103 | }` 104 | 105 | l := lexer.NewTest(code) 106 | 107 | l.Run() 108 | p := New(l) 109 | 110 | prog := p.Run() 111 | 112 | fmt.Println(prog.String()) 113 | } 114 | 115 | func TestTypeDeclaration(t *testing.T) { 116 | fmt.Println("---------------------------------------------------------- type declaration") 117 | code := `type userId: int 118 | 119 | type obj: struct { 120 | int | string, 121 | name: string, 122 | id: int 123 | } ` 124 | 125 | l := lexer.NewTest(code) 126 | 127 | l.Run() 128 | p := New(l) 129 | 130 | prog := p.Run() 131 | 132 | if p.HasError() { 133 | for _, e := range p.Errors() { 134 | fmt.Println(e) 135 | } 136 | return 137 | } 138 | 139 | fmt.Println(prog.String()) 140 | } 141 | 142 | func TestIdent(t *testing.T) { 143 | fmt.Println("---------------------------------------------------------- ident") 144 | code := `let x: int = (1,2,3) 145 | 146 | x[1, 2] = 15 147 | 148 | x[[3]] = 15 149 | 150 | df.x = 23 151 | 152 | print(x) ` 153 | 154 | l := lexer.NewTest(code) 155 | 156 | l.Run() 157 | p := New(l) 158 | 159 | prog := p.Run() 160 | 161 | fmt.Println(prog.String()) 162 | } 163 | 164 | func TestTElipsis(t *testing.T) { 165 | fmt.Println("---------------------------------------------------------- ellipsis") 166 | code := `func foo(...: any): char { 167 | paste0(..., collapse = ", ") 168 | } ` 169 | 170 | l := lexer.NewTest(code) 171 | 172 | l.Run() 173 | p := New(l) 174 | 175 | prog := p.Run() 176 | 177 | fmt.Println(prog.String()) 178 | } 179 | 180 | func TestS3(t *testing.T) { 181 | fmt.Println("---------------------------------------------------------- s3") 182 | code := ` 183 | type person: struct { 184 | int | num, 185 | name: string, 186 | age: int 187 | } 188 | 189 | func (p: person) getAge(): int { 190 | return p$age 191 | } 192 | 193 | func (p: person) setAge(n: int): null { 194 | p$age = n 195 | } 196 | 197 | func create(name: string, age: int): person { 198 | return person(0, name = name, age = age) 199 | } 200 | 201 | type persons: []person 202 | 203 | create(name = "hello") 204 | ` 205 | 206 | l := lexer.NewTest(code) 207 | 208 | l.Run() 209 | p := New(l) 210 | 211 | prog := p.Run() 212 | if p.HasError() { 213 | for _, e := range p.Errors() { 214 | fmt.Println(e) 215 | } 216 | return 217 | } 218 | 219 | fmt.Println(prog.String()) 220 | } 221 | 222 | func TestRange(t *testing.T) { 223 | fmt.Println("---------------------------------------------------------- range") 224 | code := `let x: int | na = 1..10 225 | ` 226 | 227 | l := lexer.NewTest(code) 228 | 229 | l.Run() 230 | p := New(l) 231 | 232 | prog := p.Run() 233 | 234 | fmt.Println(prog.String()) 235 | } 236 | 237 | func TestWhile(t *testing.T) { 238 | fmt.Println("---------------------------------------------------------- while") 239 | code := `while(i < 10) { 240 | print(i) 241 | } 242 | ` 243 | 244 | l := lexer.NewTest(code) 245 | 246 | l.Run() 247 | p := New(l) 248 | 249 | prog := p.Run() 250 | 251 | fmt.Println(prog.String()) 252 | } 253 | 254 | func TestNamespace(t *testing.T) { 255 | fmt.Println("---------------------------------------------------------- namespace") 256 | code := `let x: dataframe = cars |> 257 | dplyr::mutate(speed > 2) ` 258 | 259 | l := lexer.NewTest(code) 260 | 261 | l.Run() 262 | p := New(l) 263 | 264 | prog := p.Run() 265 | 266 | fmt.Println(prog.String()) 267 | } 268 | 269 | func TestIf(t *testing.T) { 270 | fmt.Println("---------------------------------------------------------- if") 271 | code := `let x: bool = (1,2,3) 272 | 273 | if (x) { 274 | print( 275 | "true", 276 | 1 277 | ) 278 | } else { 279 | print("false") 280 | } 281 | 282 | func foo(n: int): null { 283 | # comment 284 | if(n == 1) { 285 | print(true) 286 | } 287 | } 288 | ` 289 | 290 | l := lexer.NewTest(code) 291 | 292 | l.Run() 293 | 294 | p := New(l) 295 | 296 | prog := p.Run() 297 | 298 | fmt.Println(prog.String()) 299 | } 300 | 301 | func TestFunctionParam(t *testing.T) { 302 | fmt.Println("---------------------------------------------------------- function param") 303 | code := `func foo(fn: function = (x: int): int => {return x + 1}, y: int = 2): int { 304 | return sapply(x, fn) + y 305 | } 306 | 307 | func bar( 308 | x: int, 309 | y: int = 1 310 | ): int { 311 | return x + y 312 | } 313 | ` 314 | 315 | l := lexer.NewTest(code) 316 | 317 | l.Run() 318 | p := New(l) 319 | 320 | prog := p.Run() 321 | 322 | if p.HasError() { 323 | for _, e := range p.Errors() { 324 | fmt.Println(e) 325 | } 326 | return 327 | } 328 | 329 | fmt.Println(prog.String()) 330 | } 331 | 332 | func TestCall(t *testing.T) { 333 | fmt.Println("---------------------------------------------------------- call") 334 | code := ` 335 | bar(1, x = 2, "hello") 336 | 337 | bar( 338 | 1, 339 | x = 2, 340 | "hello" 341 | ) 342 | 343 | foo(z = 2) 344 | 345 | foo(1, 2, 3) 346 | 347 | foo( 348 | z = "hello" 349 | ) 350 | 351 | foo("hello") 352 | ` 353 | 354 | l := lexer.NewTest(code) 355 | 356 | l.Run() 357 | p := New(l) 358 | 359 | prog := p.Run() 360 | 361 | if p.HasError() { 362 | for _, e := range p.Errors() { 363 | fmt.Println(e) 364 | } 365 | return 366 | } 367 | 368 | fmt.Println(prog.String()) 369 | } 370 | 371 | func TestNestedCall(t *testing.T) { 372 | fmt.Println("---------------------------------------------------------- nested call") 373 | code := ` 374 | x$val = list( 375 | list( 376 | arg = parts[1] |> trimws(), 377 | types = types |> trimws() 378 | ) 379 | ) 380 | ` 381 | 382 | l := lexer.NewTest(code) 383 | 384 | l.Run() 385 | p := New(l) 386 | 387 | prog := p.Run() 388 | 389 | if p.HasError() { 390 | for _, e := range p.Errors() { 391 | fmt.Println(e) 392 | } 393 | return 394 | } 395 | 396 | fmt.Println(prog.String()) 397 | } 398 | 399 | func TestMissing(t *testing.T) { 400 | fmt.Println("---------------------------------------------------------- missing") 401 | code := ` 402 | # can be missing 403 | func hello(what: char): char { 404 | if(missing(what)) { 405 | what = "Vapour" 406 | } 407 | return sprintf("hello, %s!", what) 408 | } 409 | 410 | hello() 411 | ` 412 | 413 | l := lexer.NewTest(code) 414 | 415 | l.Run() 416 | p := New(l) 417 | 418 | prog := p.Run() 419 | 420 | p.Errors().Print() 421 | 422 | p.Errors().Print() 423 | fmt.Println(prog.String()) 424 | } 425 | 426 | func TestAnonymous(t *testing.T) { 427 | fmt.Println("---------------------------------------------------------- anonymous") 428 | code := `let y: int = (1,2,3) 429 | 430 | const x: char = "world" 431 | lapply(("hello", x), (z: char): null => { 432 | print(z) 433 | }) 434 | 435 | lapply(1..10, (v: char): null => { 436 | print(z) 437 | }) 438 | 439 | apply_math((1, 2, 3), (t: int): int => { 440 | return x * 3 441 | }) 442 | ` 443 | 444 | l := lexer.NewTest(code) 445 | 446 | l.Run() 447 | p := New(l) 448 | 449 | prog := p.Run() 450 | 451 | p.Errors().Print() 452 | fmt.Println(prog.String()) 453 | } 454 | 455 | func TestList(t *testing.T) { 456 | fmt.Println("---------------------------------------------------------- list") 457 | code := ` 458 | type simple: int | na 459 | 460 | type simpleList: []int | num 461 | 462 | type lst: list { int | na } 463 | 464 | type alst: list { 465 | int | na 466 | } 467 | 468 | type str: struct { 469 | int, 470 | name: char, 471 | val: num 472 | } 473 | 474 | type str1: struct { 475 | int 476 | } 477 | 478 | type obj: object { 479 | wheels: bool, 480 | vehicle: char 481 | } 482 | 483 | type obj: dataframe { 484 | speed: num, 485 | dist: int 486 | } 487 | ` 488 | 489 | l := lexer.NewTest(code) 490 | 491 | l.Run() 492 | l.Errors().Print() 493 | p := New(l) 494 | 495 | prog := p.Run() 496 | 497 | p.Errors().Print() 498 | fmt.Println(prog.String()) 499 | } 500 | 501 | func TestDecorators(t *testing.T) { 502 | fmt.Println("---------------------------------------------------------- decorator") 503 | code := ` 504 | @class(x, y, z) 505 | type custom: object { 506 | x: char, 507 | id: int 508 | } 509 | 510 | @generic 511 | func (p: person) myMethod(x: int): int 512 | ` 513 | 514 | l := lexer.NewTest(code) 515 | 516 | l.Run() 517 | p := New(l) 518 | 519 | prog := p.Run() 520 | 521 | p.Errors().Print() 522 | 523 | fmt.Println(prog.String()) 524 | } 525 | 526 | func TestEx(t *testing.T) { 527 | fmt.Println("-------------------------------------------- type") 528 | code := ` 529 | let globals: any = new.env(parent = emptyenv(), hash = TRUE) 530 | 531 | type Config: object { 532 | types: char, 533 | yields: char 534 | } 535 | 536 | func write_config(): null { 537 | let config: Config = Config( 538 | types = globals$types, 539 | yields = globals$yields 540 | ) 541 | 542 | print(config) 543 | } 544 | 545 | ` 546 | 547 | l := lexer.NewTest(code) 548 | 549 | l.Run() 550 | p := New(l) 551 | 552 | prog := p.Run() 553 | 554 | p.Errors().Print() 555 | 556 | fmt.Println(prog.String()) 557 | } 558 | 559 | func TestPipeNest(t *testing.T) { 560 | fmt.Println("----------------------------- method") 561 | code := ` 562 | x$val = list( 563 | list( 564 | arg = parts[1] |> trimws(), 565 | types = types |> trimws() 566 | ) 567 | ) 568 | ` 569 | 570 | l := lexer.NewTest(code) 571 | 572 | l.Run() 573 | p := New(l) 574 | 575 | prog := p.Run() 576 | 577 | p.Errors().Print() 578 | 579 | fmt.Println(prog.String()) 580 | } 581 | 582 | func TestInc(t *testing.T) { 583 | fmt.Println("----------------------------- increment") 584 | code := ` 585 | let x: int = 10 586 | 587 | x += 2 588 | ` 589 | 590 | l := lexer.NewTest(code) 591 | 592 | l.Run() 593 | p := New(l) 594 | 595 | prog := p.Run() 596 | 597 | p.Errors().Print() 598 | 599 | fmt.Println(prog.String()) 600 | } 601 | 602 | func TestReal(t *testing.T) { 603 | fmt.Println("----------------------------- increment") 604 | code := ` 605 | parts = gsub("\\n|\\t", "", parts) 606 | let types: any = strsplit(parts[2], "\\|")[[1]] 607 | ` 608 | 609 | l := lexer.NewTest(code) 610 | 611 | l.Run() 612 | p := New(l) 613 | 614 | prog := p.Run() 615 | 616 | p.Errors().Print() 617 | 618 | fmt.Println(prog.String()) 619 | } 620 | 621 | func TestFuncType(t *testing.T) { 622 | fmt.Println("----------------------------- func type") 623 | code := ` 624 | type math: func(int, int) int 625 | 626 | func apply_math(x: int = 2, y: int = 2, cb: math): int { 627 | return cb(x, y) 628 | } 629 | 630 | func multiply(x: int = 2, y: int = 1): int { 631 | return x * y 632 | } 633 | 634 | apply_math(1, 2, multiply) 635 | 636 | apply_math(1, 2, (x: int, y: int): int => { 637 | return x + y 638 | }) 639 | 640 | func bar(): math { 641 | return multiply 642 | } 643 | 644 | let x: math = bar() 645 | ` 646 | 647 | l := lexer.NewTest(code) 648 | 649 | l.Run() 650 | p := New(l) 651 | 652 | prog := p.Run() 653 | 654 | p.Errors().Print() 655 | 656 | fmt.Println(prog.String()) 657 | } 658 | 659 | func TestSquare(t *testing.T) { 660 | fmt.Println("---------------------------------------------------------- square") 661 | code := `let x: int = (1,2,3) 662 | 663 | x[2, 1] = 3 664 | 665 | let y: int = list(1,2,3) 666 | 667 | y[[1]] = 1 668 | 669 | let zz: string = ("hello|world", "hello|again") 670 | let z: char = strsplit(zz[2], "\\|")[[1]] 671 | ` 672 | 673 | l := lexer.NewTest(code) 674 | 675 | l.Run() 676 | p := New(l) 677 | 678 | prog := p.Run() 679 | 680 | fmt.Println(prog.String()) 681 | } 682 | 683 | func TestIt(t *testing.T) { 684 | fmt.Println("---------------------------------------------------------- it") 685 | code := `type rules: object { 686 | selector: char, 687 | rule: char 688 | } 689 | 690 | type linne: object { 691 | css: char, 692 | rules: []rules 693 | } 694 | 695 | #' @export 696 | func create(): linne { 697 | return linne() 698 | } 699 | 700 | #' @export 701 | func(l: linne) addRule(selector: char, ...: char): linne { 702 | l$rules = append(l$rules, rule(selector = selector, rule = "")) 703 | return l 704 | } 705 | ` 706 | 707 | l := lexer.NewTest(code) 708 | 709 | l.Run() 710 | p := New(l) 711 | 712 | prog := p.Run() 713 | 714 | if len(p.errors) > 0 { 715 | p.errors.Print() 716 | return 717 | } 718 | 719 | fmt.Println(prog.String()) 720 | } 721 | 722 | func TestTypePackage(t *testing.T) { 723 | fmt.Println("---------------------------------------------------------- type package") 724 | code := `let x: tibble::tbl | int` 725 | 726 | l := lexer.NewTest(code) 727 | 728 | l.Run() 729 | p := New(l) 730 | 731 | prog := p.Run() 732 | 733 | if len(p.errors) > 0 { 734 | p.errors.Print() 735 | return 736 | } 737 | 738 | fmt.Println(prog.String()) 739 | } 740 | 741 | func TestInline(t *testing.T) { 742 | fmt.Println("---------------------------------------------------------- type package") 743 | code := `type t: object { x: int, y: num }` 744 | 745 | l := lexer.NewTest(code) 746 | 747 | l.Run() 748 | p := New(l) 749 | 750 | prog := p.Run() 751 | 752 | if len(p.errors) > 0 { 753 | p.errors.Print() 754 | return 755 | } 756 | 757 | fmt.Println(prog.String()) 758 | } 759 | 760 | func TestFor(t *testing.T) { 761 | fmt.Println("---------------------------------------------------------- for") 762 | code := `for(let i: int in 1..nrow(df)) { 763 | print(i) 764 | } 765 | 766 | for(let x: int in 1..10) { 767 | print(x) 768 | } 769 | 770 | const zap: int = (10, 20, 30) 771 | 772 | for(let y:int in zap) { 773 | print(y) 774 | } 775 | 776 | 777 | func foo(...: int): int { 778 | sum(...) 779 | } 780 | 781 | let x: int = (1, 20, 23) ` 782 | 783 | l := lexer.NewTest(code) 784 | 785 | l.Run() 786 | p := New(l) 787 | 788 | prog := p.Run() 789 | 790 | fmt.Println(prog.String()) 791 | } 792 | -------------------------------------------------------------------------------- /r/args.go: -------------------------------------------------------------------------------- 1 | package r 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/vapourlang/vapour/ast" 8 | ) 9 | 10 | type arg struct { 11 | Name string `json:"name"` 12 | Value string `json:"value"` 13 | } 14 | 15 | type args []arg 16 | 17 | func GetFunctionArguments(pkg, operator, function string) *ast.FunctionLiteral { 18 | obj := &ast.FunctionLiteral{} 19 | function = pkg + operator + function 20 | 21 | output, err := Callr( 22 | fmt.Sprintf(`args <- as.list(args(%v)) 23 | if(length(args) == 0){ 24 | cat("[]") 25 | return() 26 | } 27 | 28 | N <- length(args) - 1L 29 | args <- args[1:N] 30 | 31 | json_string <- c() 32 | for(i in seq_along(args)) { 33 | arg_string <- paste0( 34 | '{"name": "', names(args)[i], '", "value": "', deparse(args[i]), '"}' 35 | ) 36 | json_string <- c(json_string, arg_string) 37 | } 38 | 39 | json_string <- paste0(json_string, collapse = ",") 40 | cat(paste0("[", json_string, "]"))`, 41 | function, 42 | ), 43 | ) 44 | 45 | if err != nil { 46 | return obj 47 | } 48 | 49 | var args args 50 | 51 | err = json.Unmarshal(output, &args) 52 | 53 | if err != nil { 54 | return obj 55 | } 56 | 57 | return obj 58 | } 59 | -------------------------------------------------------------------------------- /r/base.go: -------------------------------------------------------------------------------- 1 | package r 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/vapourlang/vapour/cache" 8 | ) 9 | 10 | type Package struct { 11 | Name string `json:"name"` 12 | Functions []string `json:"functions"` 13 | } 14 | 15 | const ( 16 | BASEPACKAGES = "BASEPACKAGES" 17 | FUNCTION = "FUNCTION" 18 | ) 19 | 20 | func ListBaseFunctions() ([]Package, error) { 21 | c, ok := cache.Get(BASEPACKAGES) 22 | 23 | if ok { 24 | return c.([]Package), nil 25 | } 26 | 27 | var packages []Package 28 | 29 | output, err := Callr( 30 | `base_packages = getOption('defaultPackages') 31 | base_packages <- c(base_packages, "base") 32 | pkgs <- lapply(base_packages, function (pkg){ 33 | fns <- ls(paste0('package:', pkg)) 34 | fns <- fns[!grepl('<-', fns)] 35 | fns <- paste0(fns, collapse = '","') 36 | fns <- paste0('"functions":["', fns, '"]') 37 | pkg <- paste0('"name":"', pkg, '"') 38 | paste0('{', pkg, ',', fns, '}') 39 | }) 40 | 41 | json <- paste0(pkgs, collapse = ",") 42 | cat(paste0("[", json, "]"))`, 43 | ) 44 | 45 | if err != nil { 46 | return packages, err 47 | } 48 | 49 | err = json.Unmarshal(output, &packages) 50 | 51 | if err != nil { 52 | return packages, err 53 | } 54 | 55 | cache.Set(BASEPACKAGES, packages) 56 | 57 | return packages, err 58 | } 59 | 60 | func PackageHasFunction(pkg, operator, fn string) (bool, error) { 61 | key := pkg + fn 62 | c, ok := cache.Get(key) 63 | 64 | if ok { 65 | return c.(bool), nil 66 | } 67 | 68 | output, err := Callr( 69 | fmt.Sprintf("res <- tryCatch(%v%v%v);cat(inherits(res, 'error'))", pkg, operator, fn), 70 | ) 71 | 72 | if err != nil { 73 | return false, err 74 | } 75 | 76 | ok = string(output) == "FALSE" 77 | 78 | cache.Set(key, ok) 79 | 80 | return ok, err 81 | } 82 | 83 | func PackageIsInstalled(pkg string) (bool, error) { 84 | key := "package::" + pkg 85 | c, ok := cache.Get(key) 86 | 87 | if ok { 88 | return c.(bool), nil 89 | } 90 | 91 | output, err := Callr( 92 | fmt.Sprintf("res <- requireNamespace('%v');cat(res)", pkg), 93 | ) 94 | 95 | if err != nil { 96 | return false, err 97 | } 98 | 99 | ok = string(output) == "TRUE" 100 | 101 | cache.Set(key, ok) 102 | 103 | return ok, err 104 | } 105 | -------------------------------------------------------------------------------- /r/call.go: -------------------------------------------------------------------------------- 1 | package r 2 | 3 | import "os/exec" 4 | 5 | func Callr(cmd string) ([]byte, error) { 6 | out, err := exec.Command( 7 | "R", 8 | "-s", 9 | "-e", 10 | cmd, 11 | ).Output() 12 | 13 | return out, err 14 | } 15 | -------------------------------------------------------------------------------- /r/libpath.go: -------------------------------------------------------------------------------- 1 | package r 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | func LibPath() []string { 8 | var paths []string 9 | output, err := Callr(`paths <- paste0(.libPaths(), collapse = "\",\"") 10 | paths <- paste0("[\"", paths, "\"]") 11 | cat(paths)`) 12 | 13 | if err != nil { 14 | return paths 15 | } 16 | 17 | err = json.Unmarshal(output, &paths) 18 | 19 | if err != nil { 20 | return paths 21 | } 22 | 23 | return paths 24 | } 25 | -------------------------------------------------------------------------------- /read.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/fs" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/vapourlang/vapour/lexer" 9 | ) 10 | 11 | func (v *vapour) readDir() error { 12 | err := filepath.WalkDir(*v.root, v.walk) 13 | 14 | if err != nil { 15 | return err 16 | } 17 | 18 | return nil 19 | } 20 | 21 | func (v *vapour) walk(path string, directory fs.DirEntry, err error) error { 22 | if err != nil { 23 | return err 24 | } 25 | 26 | if directory.IsDir() { 27 | return nil 28 | } 29 | 30 | ext := filepath.Ext(path) 31 | 32 | if ext != ".vp" { 33 | return nil 34 | } 35 | 36 | fl, err := os.ReadFile(path) 37 | 38 | if err != nil { 39 | return err 40 | } 41 | 42 | rfl := lexer.File{ 43 | Path: path, 44 | Content: fl, 45 | } 46 | 47 | v.files = append(v.files, rfl) 48 | 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /repl.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os/exec" 9 | 10 | "github.com/vapourlang/vapour/lexer" 11 | "github.com/vapourlang/vapour/parser" 12 | "github.com/vapourlang/vapour/transpiler" 13 | ) 14 | 15 | const PROMPT = "> " 16 | 17 | func (v *vapour) replIntro() string { 18 | return "Vapour " + v.version + "\n" 19 | } 20 | 21 | func (v *vapour) repl(in io.Reader, out io.Writer, er io.Writer) { 22 | cmd := exec.Command( 23 | "R", 24 | "-s", 25 | "-e", 26 | `f <- file("stdin") 27 | open(f) 28 | while(length(line <- readLines(f, n = 1)) > 0) { 29 | cat(eval(parse(text = line))) 30 | }`, 31 | ) 32 | 33 | //cmd.Stdout = out 34 | cmd.Stderr = er 35 | 36 | cmdIn, err := cmd.StdinPipe() 37 | 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | 42 | defer cmdIn.Close() 43 | 44 | cmdOut, err := cmd.StdoutPipe() 45 | 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | defer cmdOut.Close() 51 | 52 | scanner := bufio.NewScanner(in) 53 | 54 | fmt.Fprint(out, v.replIntro()) 55 | 56 | err = cmd.Start() 57 | 58 | if err != nil { 59 | log.Fatal(err) 60 | } 61 | 62 | for { 63 | fmt.Fprint(out, PROMPT) 64 | 65 | scanned := scanner.Scan() 66 | 67 | if !scanned { 68 | continue 69 | } 70 | 71 | line := scanner.Text() 72 | 73 | // lex 74 | l := lexer.NewCode("repl", line+"\n") 75 | l.Run() 76 | 77 | if l.HasError() { 78 | fmt.Fprintln(out, l.Errors().String()) 79 | continue 80 | } 81 | 82 | // parse 83 | p := parser.New(l) 84 | prog := p.Run() 85 | 86 | if p.HasError() { 87 | for _, e := range p.Errors() { 88 | fmt.Fprintln(out, e.Message) 89 | } 90 | continue 91 | } 92 | 93 | trans := transpiler.New() 94 | trans.Transpile(prog) 95 | code := trans.GetCode() 96 | 97 | _, err := io.WriteString(cmdIn, code) 98 | 99 | if err != nil { 100 | fmt.Fprint(out, err.Error()) 101 | continue 102 | } 103 | 104 | res := make([]byte, 1024) 105 | _, err = cmdOut.Read(res) 106 | 107 | if err != nil { 108 | fmt.Fprint(out, err.Error()) 109 | continue 110 | } 111 | 112 | fmt.Fprint(out, string(res)+"\n") 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /run.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "os/exec" 8 | 9 | "github.com/vapourlang/vapour/cli" 10 | "github.com/vapourlang/vapour/config" 11 | "github.com/vapourlang/vapour/devtools" 12 | "github.com/vapourlang/vapour/environment" 13 | "github.com/vapourlang/vapour/lsp" 14 | "github.com/vapourlang/vapour/r" 15 | ) 16 | 17 | func run(code string) { 18 | cmd := exec.Command( 19 | "R", 20 | "--no-save", 21 | "--slave", 22 | "-e", 23 | code, 24 | ) 25 | 26 | output, err := cmd.CombinedOutput() 27 | 28 | if err != nil { 29 | log.Fatal("Failed to run") 30 | } 31 | 32 | fmt.Println(string(output)) 33 | } 34 | 35 | func (v *vapour) Run(args cli.CLI) { 36 | v.config = config.ReadConfig() 37 | 38 | environment.SetLibrary(r.LibPath()) 39 | 40 | if *args.Indir != "" { 41 | ok := v.transpile(args) 42 | devtools.Run(ok, args) 43 | return 44 | } 45 | 46 | if *args.Infile != "" { 47 | ok := v.transpileFile(args) 48 | devtools.Run(ok, args) 49 | return 50 | } 51 | 52 | if *args.Repl { 53 | v.repl(os.Stdin, os.Stdout, os.Stderr) 54 | return 55 | } 56 | 57 | if *args.LSP { 58 | lsp.Run(v.config, *args.TCP, *args.Port) 59 | return 60 | } 61 | 62 | if *args.Version { 63 | fmt.Printf("v%v\n", v.version) 64 | return 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /token/print.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | var ItemName = map[ItemType]string{ 8 | ItemError: "error", 9 | ItemDefer: "defer", 10 | ItemIdent: "identifier", 11 | ItemDoubleQuote: "double quote", 12 | ItemSingleQuote: "single quote", 13 | ItemAssign: "assign", 14 | ItemLeftCurly: "curly left", 15 | ItemRightCurly: "curly right", 16 | ItemLeftParen: "paren left", 17 | ItemRightParen: "paren right", 18 | ItemLeftSquare: "square left", 19 | ItemRightSquare: "square right", 20 | ItemString: "string", 21 | ItemInteger: "integer", 22 | ItemFloat: "float", 23 | ItemNamespace: "namespace", 24 | ItemNamespaceInternal: "namespace internal", 25 | ItemComment: "comment", 26 | ItemDoubleEqual: "double equal", 27 | ItemLessThan: "less than", 28 | ItemGreaterThan: "greater than", 29 | ItemNotEqual: "not equal", 30 | ItemLessOrEqual: "less or equal", 31 | ItemGreaterOrEqual: "greater or equal", 32 | ItemBool: "boolean", 33 | ItemDollar: "dollar sign", 34 | ItemComma: "comma", 35 | ItemColon: "colon", 36 | ItemQuestion: "question mark", 37 | ItemBacktick: "backtick", 38 | ItemInfix: "infix", 39 | ItemIf: "if", 40 | ItemBreak: "break", 41 | ItemElse: "else", 42 | ItemAnd: "ampersand", 43 | ItemOr: "vertical bar", 44 | ItemReturn: "return", 45 | ItemNULL: "null", 46 | ItemNA: "NA", 47 | ItemNan: "NaN", 48 | ItemNAString: "NA string", 49 | ItemNAReal: "NA real", 50 | ItemNAComplex: "NA complex", 51 | ItemNAInteger: "NA integer", 52 | ItemPipe: "native pipe", 53 | ItemModulus: "modulus", 54 | ItemDoubleLeftSquare: "double left square", 55 | ItemDoubleRightSquare: "double right square", 56 | ItemFor: "for loop", 57 | ItemRepeat: "repeat", 58 | ItemWhile: "while loop", 59 | ItemNext: "next", 60 | ItemIn: "in", 61 | ItemFunction: "function", 62 | ItemPlus: "plus", 63 | ItemMinus: "minus", 64 | ItemMultiply: "multiply", 65 | ItemDivide: "divide", 66 | ItemPower: "power", 67 | ItemNewLine: "new line", 68 | ItemEOF: "end of file", 69 | ItemTypes: "type", 70 | ItemTypesPkg: "type package", 71 | ItemTypesList: "list type", 72 | ItemTypesDecl: "type declaration", 73 | ItemRange: "range", 74 | ItemLet: "let", 75 | ItemConst: "const", 76 | ItemBang: "bang", 77 | ItemArrow: "arrow function", 78 | ItemThreeDot: "elipsis", 79 | ItemDecorator: "decorator", 80 | ItemDecoratorClass: "decorator class", 81 | ItemDecoratorGeneric: "decorator generic", 82 | ItemDecoratorDefault: "decorator default", 83 | ItemDecoratorFactor: "decorator factor", 84 | ItemDecoratorEnvironment: "decorator env", 85 | ItemAttribute: "attribute", 86 | ItemObjList: "object list", 87 | ItemObjFunc: "object function", 88 | ItemObjDataframe: "object dataframe", 89 | ItemObjStruct: "object struct", 90 | ItemObjObject: "object object", 91 | ItemObjMatrix: "object matrix", 92 | ItemObjFactor: "object factor", 93 | } 94 | 95 | func (t ItemType) String() string { 96 | k, _ := ItemName[t] 97 | return k 98 | } 99 | 100 | func (item Item) String() string { 101 | return ItemName[item.Class] 102 | } 103 | 104 | func pad(str string, min int) string { 105 | out := str 106 | l := len(str) 107 | 108 | var i int 109 | for l < min { 110 | pad := "-" 111 | 112 | if i == 0 || i == min { 113 | pad = " " 114 | } 115 | out = out + pad 116 | l = len(out) 117 | i++ 118 | } 119 | 120 | return out 121 | } 122 | 123 | func (i Item) Print() { 124 | name := i.String() 125 | val := i.Value 126 | if val == "\n" { 127 | val = "\\_n" 128 | } 129 | 130 | name = pad(name, 30) 131 | fmt.Printf( 132 | "%s `%v` \t [file: %v line: %v, char: %v]\n", 133 | name, val, i.File, i.Line, i.Char, 134 | ) 135 | } 136 | 137 | func (i Items) Print() { 138 | for _, v := range i { 139 | v.Print() 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /token/token.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | type ItemType int 4 | 5 | type Item struct { 6 | Class ItemType 7 | Value string 8 | Line int 9 | Pos int 10 | Char int 11 | File string 12 | } 13 | 14 | type Items []Item 15 | 16 | const ( 17 | ItemError ItemType = iota 18 | 19 | // end of file 20 | ItemEOF 21 | 22 | // identifiers 23 | ItemIdent 24 | 25 | // quotes 26 | ItemDoubleQuote 27 | ItemSingleQuote 28 | 29 | // $ 30 | ItemDollar 31 | 32 | // backtick 33 | ItemBacktick 34 | 35 | // infix %>% 36 | ItemInfix 37 | 38 | // comma, 39 | ItemComma 40 | 41 | // question mark? 42 | ItemQuestion 43 | 44 | // boolean 45 | ItemBool 46 | 47 | // boolean 48 | ItemReturn 49 | 50 | // ... 51 | ItemThreeDot 52 | 53 | // native pipe 54 | ItemPipe 55 | 56 | // = 57 | ItemAssign 58 | ItemAssignParent 59 | 60 | // NULL 61 | ItemNULL 62 | 63 | // NA 64 | ItemNA 65 | ItemNan 66 | ItemNAString 67 | ItemNAReal 68 | ItemNAComplex 69 | ItemNAInteger 70 | 71 | // parens and brackets 72 | ItemLeftCurly 73 | ItemRightCurly 74 | ItemLeftParen 75 | ItemRightParen 76 | ItemLeftSquare 77 | ItemRightSquare 78 | ItemDoubleLeftSquare 79 | ItemDoubleRightSquare 80 | 81 | // "strings" 82 | ItemString 83 | 84 | // numbers 85 | ItemInteger 86 | ItemFloat 87 | 88 | // namespace:: 89 | ItemNamespace 90 | // namespace::: 91 | ItemNamespaceInternal 92 | 93 | // colon 94 | ItemColon 95 | 96 | // New line \n 97 | ItemNewLine 98 | 99 | // + - / * ^ 100 | ItemPlus 101 | ItemMinus 102 | ItemDivide 103 | ItemMultiply 104 | ItemPower 105 | ItemModulus 106 | 107 | // bang! 108 | ItemBang 109 | 110 | // comment 111 | ItemComment 112 | 113 | // compare 114 | ItemDoubleEqual 115 | ItemLessThan 116 | ItemGreaterThan 117 | ItemNotEqual 118 | ItemLessOrEqual 119 | ItemGreaterOrEqual 120 | 121 | // if else 122 | ItemIf 123 | ItemElse 124 | ItemAnd 125 | ItemOr 126 | ItemBreak 127 | 128 | // Infinite 129 | ItemInf 130 | 131 | // loop 132 | ItemFor 133 | ItemRepeat 134 | ItemWhile 135 | ItemNext 136 | ItemIn 137 | 138 | // func 139 | ItemFunction 140 | 141 | // arrow function => 142 | ItemArrow 143 | 144 | // declare 145 | ItemLet 146 | ItemConst 147 | 148 | // types 149 | ItemTypes 150 | ItemTypesPkg 151 | ItemTypesList 152 | ItemTypesDecl 153 | 154 | // range.. 155 | ItemRange 156 | 157 | // objects 158 | ItemObjDataframe 159 | ItemObjList // list() 160 | ItemObjObject // named list() 161 | ItemObjStruct 162 | ItemObjMatrix 163 | ItemObjFunc 164 | ItemObjFactor 165 | ItemObjEnvironment 166 | 167 | // @decorators 168 | ItemDecorator 169 | ItemDecoratorClass 170 | ItemDecoratorGeneric 171 | ItemDecoratorDefault 172 | ItemDecoratorMatrix 173 | ItemDecoratorFactor 174 | ItemDecoratorEnvironment 175 | 176 | // attribute 177 | ItemAttribute 178 | 179 | // defer 180 | ItemDefer 181 | 182 | // += and -= 183 | ItemAssignInc 184 | ItemAssignDec 185 | ) 186 | 187 | const EOF = -1 188 | -------------------------------------------------------------------------------- /transpile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/vapourlang/vapour/cli" 9 | "github.com/vapourlang/vapour/lexer" 10 | "github.com/vapourlang/vapour/parser" 11 | "github.com/vapourlang/vapour/transpiler" 12 | "github.com/vapourlang/vapour/walker" 13 | ) 14 | 15 | func (v *vapour) transpile(conf cli.CLI) bool { 16 | v.root = conf.Indir 17 | err := v.readDir() 18 | 19 | if err != nil { 20 | log.Fatal("Failed to read vapour files") 21 | } 22 | 23 | // lex 24 | l := lexer.New(v.files) 25 | l.Run() 26 | 27 | if l.HasError() { 28 | l.Errors().Print() 29 | transpileFailed() 30 | return false 31 | } 32 | 33 | // parse 34 | p := parser.New(l) 35 | prog := p.Run() 36 | 37 | if p.HasError() { 38 | p.Errors().Print() 39 | transpileFailed() 40 | return false 41 | } 42 | 43 | // walk tree 44 | w := walker.New() 45 | w.Walk(prog) 46 | 47 | if w.HasDiagnostic() { 48 | w.Errors().Print() 49 | } 50 | 51 | if w.HasError() { 52 | transpileFailed() 53 | return false 54 | } 55 | 56 | if *conf.Check { 57 | return false 58 | } 59 | 60 | // transpile 61 | trans := transpiler.New() 62 | trans.Transpile(prog) 63 | code := trans.GetCode() 64 | 65 | transpileSuccessful() 66 | 67 | if *conf.Run { 68 | run(code) 69 | return false 70 | } 71 | 72 | code = addHeader(code) 73 | 74 | // write 75 | path := *conf.Outdir + "/" + *conf.Outfile 76 | f, err := os.Create(path) 77 | 78 | if err != nil { 79 | log.Fatalf("Failed to create output file: %v", err.Error()) 80 | } 81 | 82 | defer f.Close() 83 | 84 | _, err = f.WriteString(code) 85 | 86 | if err != nil { 87 | log.Fatalf("Failed to write output file: %v", err.Error()) 88 | } 89 | 90 | // we only generate types if it's an R package 91 | if *conf.Outdir != "R" { 92 | return false 93 | } 94 | 95 | // write types 96 | lines := w.Env().GenerateTypes().String() 97 | f, err = os.Create(*conf.Types) 98 | 99 | if err != nil { 100 | log.Fatalf("Failed to create type file: %v", err.Error()) 101 | } 102 | 103 | defer f.Close() 104 | 105 | _, err = f.WriteString(lines) 106 | 107 | if err != nil { 108 | log.Fatalf("Failed to write to types file: %v", err.Error()) 109 | } 110 | 111 | return true 112 | } 113 | 114 | func (v *vapour) transpileFile(conf cli.CLI) bool { 115 | content, err := os.ReadFile(*conf.Infile) 116 | 117 | if err != nil { 118 | log.Fatal("Could not read vapour file") 119 | } 120 | 121 | // lex 122 | l := lexer.NewCode(*conf.Infile, string(content)) 123 | l.Run() 124 | 125 | if l.HasError() { 126 | l.Errors().Print() 127 | transpileFailed() 128 | return false 129 | } 130 | 131 | // parse 132 | p := parser.New(l) 133 | prog := p.Run() 134 | 135 | if p.HasError() { 136 | p.Errors().Print() 137 | transpileFailed() 138 | return false 139 | } 140 | 141 | // walk tree 142 | w := walker.New() 143 | w.Walk(prog) 144 | if w.HasDiagnostic() { 145 | w.Errors().Print() 146 | transpileFailed() 147 | } 148 | 149 | if w.HasError() { 150 | return false 151 | } 152 | 153 | if *conf.Check { 154 | return false 155 | } 156 | 157 | // transpile 158 | trans := transpiler.New() 159 | trans.Transpile(prog) 160 | code := trans.GetCode() 161 | 162 | transpileSuccessful() 163 | 164 | if *conf.Run { 165 | run(code) 166 | return false 167 | } 168 | 169 | code = addHeader(code) 170 | 171 | // write 172 | f, err := os.Create(*conf.Outfile) 173 | 174 | if err != nil { 175 | log.Fatal("Failed to create output file") 176 | } 177 | 178 | defer f.Close() 179 | 180 | _, err = f.WriteString(code) 181 | 182 | if err != nil { 183 | log.Fatal("Failed to write to output file") 184 | } 185 | 186 | return true 187 | } 188 | 189 | func transpileSuccessful() { 190 | fmt.Println(cli.Green + "✓" + cli.Reset + " files successfully transpiled!") 191 | } 192 | 193 | func transpileFailed() { 194 | fmt.Println(cli.Red + "x" + cli.Reset + " failed to transpile files!") 195 | } 196 | -------------------------------------------------------------------------------- /transpiler/transpile.go: -------------------------------------------------------------------------------- 1 | package transpiler 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/vapourlang/vapour/ast" 7 | "github.com/vapourlang/vapour/environment" 8 | ) 9 | 10 | type Transpiler struct { 11 | code []string 12 | env *environment.Environment 13 | opts options 14 | } 15 | 16 | type options struct { 17 | inGeneric bool 18 | inDefault bool 19 | } 20 | 21 | func (t *Transpiler) Env() *environment.Environment { 22 | return t.env 23 | } 24 | 25 | func New() *Transpiler { 26 | env := environment.New() 27 | 28 | return &Transpiler{ 29 | env: env, 30 | } 31 | } 32 | 33 | func (t *Transpiler) Transpile(node ast.Node) ast.Node { 34 | switch node := node.(type) { 35 | 36 | // Statements 37 | case *ast.Program: 38 | return t.transpileProgram(node) 39 | 40 | case *ast.ExpressionStatement: 41 | if node.Expression != nil { 42 | return t.Transpile(node.Expression) 43 | } 44 | 45 | case *ast.LetStatement: 46 | t.env.SetVariable( 47 | node.Name, 48 | environment.Variable{ 49 | Token: node.Token, 50 | Value: node.Type, 51 | }, 52 | ) 53 | if node.Value != nil { 54 | t.transpileLetStatement(node) 55 | t.Transpile(node.Value) 56 | t.addNewLine() 57 | } 58 | 59 | case *ast.NewLine: 60 | t.addNewLine() 61 | 62 | case *ast.Comma: 63 | if t.code[len(t.code)-1] == "\n" { 64 | t.popCode() 65 | } 66 | t.addCode(",") 67 | 68 | case *ast.ConstStatement: 69 | t.env.SetVariable( 70 | node.Name, 71 | environment.Variable{ 72 | Token: node.Token, 73 | Value: node.Type, 74 | IsConst: true, 75 | }, 76 | ) 77 | if node.Value != nil { 78 | t.transpileConstStatement(node) 79 | t.Transpile(node.Value) 80 | t.addNewLine() 81 | } 82 | 83 | case *ast.ReturnStatement: 84 | t.addNewLine() 85 | t.addCode("return(") 86 | t.Transpile(node.ReturnValue) 87 | t.addCode(")") 88 | 89 | case *ast.DeferStatement: 90 | t.addCode("on.exit((") 91 | t.Transpile(node.Func) 92 | t.addCode(")())") 93 | 94 | case *ast.TypeStatement: 95 | t.env.SetType( 96 | environment.Type{ 97 | Token: node.Token, 98 | Type: node.Type, 99 | Attributes: node.Attributes, 100 | Object: node.Object, 101 | Name: node.Name, 102 | }, 103 | ) 104 | 105 | case *ast.Null: 106 | t.addCode("NULL") 107 | 108 | case *ast.Keyword: 109 | t.addCode(node.Value) 110 | 111 | case *ast.CommentStatement: 112 | t.addCode(node.TokenLiteral()) 113 | 114 | case *ast.BlockStatement: 115 | for _, s := range node.Statements { 116 | t.Transpile(s) 117 | } 118 | 119 | case *ast.Attribute: 120 | t.addCode(node.Value) 121 | return node 122 | 123 | case *ast.Identifier: 124 | t.addCode(node.Value) 125 | return node 126 | 127 | case *ast.Boolean: 128 | t.addCode(strings.ToUpper(node.String())) 129 | 130 | case *ast.IntegerLiteral: 131 | t.addCode(node.Value) 132 | 133 | case *ast.FloatLiteral: 134 | t.addCode(node.Value) 135 | 136 | case *ast.VectorLiteral: 137 | t.addCode("c(") 138 | for i, s := range node.Value { 139 | t.Transpile(s) 140 | if i < len(node.Value)-1 { 141 | t.addCode(", ") 142 | } 143 | } 144 | t.addCode(")") 145 | 146 | case *ast.StringLiteral: 147 | t.addCode(node.Token.Value + node.Str + node.Token.Value) 148 | 149 | case *ast.PrefixExpression: 150 | t.addCode("(") 151 | t.addCode(node.Operator) 152 | t.Transpile(node.Right) 153 | t.addCode(")") 154 | 155 | case *ast.For: 156 | t.addCode("for(") 157 | t.addCode(node.Name.Name) 158 | t.addCode(" in ") 159 | t.Transpile(node.Vector) 160 | t.addCode(") {") 161 | t.env = environment.Enclose(t.env, nil) 162 | t.Transpile(node.Value) 163 | t.addCode("}") 164 | t.env = environment.Open(t.env) 165 | 166 | case *ast.While: 167 | t.addCode("while(") 168 | t.Transpile(node.Statement) 169 | t.addCode(") {") 170 | t.env = environment.Enclose(t.env, nil) 171 | t.Transpile(node.Value) 172 | t.addCode("}") 173 | t.env = environment.Open(t.env) 174 | 175 | case *ast.InfixExpression: 176 | n := t.Transpile(node.Left) 177 | 178 | transpiled := false 179 | if node.Operator == "$" { 180 | switch n := n.(type) { 181 | case *ast.Identifier: 182 | v, exists := t.env.GetVariable(n.Value, true) 183 | 184 | if !exists { 185 | break 186 | } 187 | 188 | isStruct := false 189 | for _, ty := range v.Value { 190 | if ty.Name == "struct" { 191 | isStruct = true 192 | } 193 | } 194 | 195 | if !isStruct { 196 | break 197 | } 198 | 199 | t.popCode() 200 | transpiled = true 201 | t.addCode("attr(" + n.Value + ", \"") 202 | t.Transpile(node.Right) 203 | t.addCode("\")") 204 | } 205 | } 206 | 207 | if transpiled { 208 | break 209 | } 210 | 211 | if t.code[len(t.code)-1] == "\n" { 212 | t.popCode() 213 | } 214 | 215 | if node.Operator == "in" { 216 | t.addCode(" ") 217 | } 218 | 219 | if node.Operator == ".." { 220 | node.Operator = ":" 221 | } 222 | 223 | if node.Operator == "<-" { 224 | node.Operator = "<<-" 225 | } 226 | 227 | if node.Operator != "+=" && node.Operator != "-=" { 228 | t.addCode(node.Operator) 229 | } 230 | 231 | if node.Operator == "+=" { 232 | t.addCode("=") 233 | t.Transpile(node.Left) 234 | t.addCode("+") 235 | } 236 | 237 | if node.Operator == "-=" { 238 | t.addCode("=") 239 | t.Transpile(node.Left) 240 | t.addCode("-") 241 | } 242 | 243 | if node.Operator == "in" { 244 | t.addCode(" ") 245 | } 246 | 247 | if node.Right != nil { 248 | t.Transpile(node.Right) 249 | t.addNewLine() 250 | } 251 | 252 | case *ast.Square: 253 | if t.code[len(t.code)-1] == "\n" { 254 | t.popCode() 255 | } 256 | t.addCode(node.Token.Value) 257 | 258 | case *ast.IfExpression: 259 | t.addCode("if(isTRUE(") 260 | t.Transpile(node.Condition) 261 | t.addCode(")){") 262 | t.env = environment.Enclose(t.env, nil) 263 | t.Transpile(node.Consequence) 264 | t.env = environment.Open(t.env) 265 | t.addCode("}") 266 | 267 | if node.Alternative != nil { 268 | t.addCode(" else {") 269 | t.env = environment.Enclose(t.env, nil) 270 | t.Transpile(node.Alternative) 271 | t.env = environment.Open(t.env) 272 | t.addCode("}") 273 | } 274 | 275 | case *ast.FunctionLiteral: 276 | t.env = environment.Enclose(t.env, node.ReturnType) 277 | 278 | if node.Name != "" { 279 | t.addCode(node.Name) 280 | } 281 | 282 | if t.opts.inDefault { 283 | node.Method = &ast.Type{Name: "default"} 284 | } 285 | 286 | if node.Method != nil && node.Method.Name != "any" { 287 | t.addCode("." + node.Method.Name) 288 | } 289 | 290 | if node.Operator != "" { 291 | t.addCode(" " + node.Operator + " ") 292 | } 293 | 294 | t.addCode("function(") 295 | 296 | if node.MethodVariable != "" { 297 | t.addCode(node.MethodVariable) 298 | if len(node.Parameters) > 0 { 299 | t.addCode(", ") 300 | } 301 | } 302 | 303 | for i, p := range node.Parameters { 304 | t.env.SetVariable( 305 | p.Token.Value, 306 | environment.Variable{ 307 | Token: p.Token, 308 | Value: p.Type, 309 | CanMiss: p.Default == nil, 310 | IsConst: false, 311 | Used: true, 312 | }, 313 | ) 314 | 315 | t.addCode(p.Name) 316 | 317 | if p.Operator == "=" { 318 | t.addCode(" = ") 319 | t.Transpile(p.Default) 320 | } 321 | 322 | if i < len(node.Parameters)-1 { 323 | t.addCode(",") 324 | } 325 | } 326 | 327 | t.addCode(") {") 328 | if node.Body != nil { 329 | t.Transpile(node.Body) 330 | } 331 | 332 | if node.Body == nil { 333 | t.addCode("UseMethod(\"" + node.Name + "\")") 334 | } 335 | 336 | t.env = environment.Open(t.env) 337 | t.addCode("}") 338 | 339 | case *ast.DecoratorEnvironment: 340 | t.env.SetEnv( 341 | node.Type.Name, 342 | environment.Env{ 343 | Token: node.Token, 344 | Value: node, 345 | }, 346 | ) 347 | t.Transpile(node.Type) 348 | 349 | case *ast.DecoratorFactor: 350 | t.env.SetFactor( 351 | node.Type.Name, 352 | environment.Factor{ 353 | Token: node.Token, 354 | Value: node, 355 | }, 356 | ) 357 | t.Transpile(node.Type) 358 | 359 | case *ast.DecoratorMatrix: 360 | t.env.SetMatrix( 361 | node.Type.Name, 362 | environment.Matrix{ 363 | Token: node.Token, 364 | Value: node, 365 | }, 366 | ) 367 | t.Transpile(node.Type) 368 | 369 | case *ast.DecoratorClass: 370 | t.env.SetClass( 371 | node.Type.Name, 372 | environment.Class{ 373 | Token: node.Token, 374 | Value: node, 375 | }, 376 | ) 377 | t.Transpile(node.Type) 378 | 379 | case *ast.DecoratorGeneric: 380 | t.opts.inGeneric = true 381 | t.Transpile(node.Func) 382 | t.opts.inGeneric = false 383 | 384 | case *ast.DecoratorDefault: 385 | t.opts.inDefault = true 386 | n := t.Transpile(node.Func) 387 | t.opts.inDefault = false 388 | return n 389 | 390 | case *ast.CallExpression: 391 | t.transpileCallExpression(node) 392 | t.addNewLine() 393 | } 394 | 395 | return node 396 | } 397 | 398 | func (t *Transpiler) transpileProgram(program *ast.Program) ast.Node { 399 | var node ast.Node 400 | 401 | for _, statement := range program.Statements { 402 | node := t.Transpile(statement) 403 | 404 | switch n := node.(type) { 405 | case *ast.ReturnStatement: 406 | t.addCode("") 407 | t.addCode("return(") 408 | if n.ReturnValue != nil { 409 | t.Transpile(n.ReturnValue) 410 | } 411 | t.addCode(")") 412 | } 413 | } 414 | 415 | return node 416 | } 417 | 418 | func (t *Transpiler) transpileCallExpression(node *ast.CallExpression) { 419 | typ, _ := t.env.GetType("", node.Name) 420 | 421 | if typ.Object == "struct" { 422 | t.transpileCallExpressionStruct(node, typ) 423 | return 424 | } 425 | 426 | if typ.Object == "object" { 427 | t.transpileCallExpressionObject(node, typ) 428 | return 429 | } 430 | 431 | if typ.Object == "environment" { 432 | t.transpileCallExpressionEnvironment(node, typ) 433 | return 434 | } 435 | 436 | if typ.Object == "dataframe" { 437 | t.transpileCallExpressionDataframe(node, typ) 438 | return 439 | } 440 | 441 | if typ.Object == "list" { 442 | t.transpileCallExpressionObject(node, typ) 443 | return 444 | } 445 | 446 | if typ.Object == "vector" { 447 | t.transpileCallExpressionVector(node, typ) 448 | return 449 | } 450 | 451 | if typ.Object == "matrix" { 452 | t.transpileCallExpressionMatrix(node, typ) 453 | return 454 | } 455 | 456 | if typ.Object == "factor" { 457 | t.transpileCallExpressionFactor(node, typ) 458 | return 459 | } 460 | 461 | t.addCode(node.Function + "(") 462 | for i, a := range node.Arguments { 463 | t.Transpile(a.Value) 464 | if i < len(node.Arguments)-1 { 465 | t.addCode(", ") 466 | } 467 | } 468 | t.addCode(")") 469 | } 470 | 471 | func (t *Transpiler) transpileCallExpressionEnvironment(node *ast.CallExpression, typ environment.Type) { 472 | t.addCode("structure(new.env(") 473 | for i, a := range node.Arguments { 474 | t.Transpile(a.Value) 475 | if i < len(node.Arguments)-1 { 476 | t.addCode(", ") 477 | } 478 | } 479 | 480 | cl, exists := t.env.GetClass(typ.Name) 481 | 482 | if exists { 483 | t.addCode(", class=c(\"" + strings.Join(cl.Value.Classes, "\", \"") + "\")") 484 | return 485 | } 486 | 487 | fct, exists := t.env.GetEnv(typ.Name) 488 | if exists { 489 | t.addCode(", ") 490 | for _, a := range fct.Value.Arguments { 491 | t.Transpile(a.Value) 492 | } 493 | } 494 | t.addCode(")") 495 | 496 | t.addCode(", class=c(\"" + typ.Name + "\", \"environment\")") 497 | 498 | t.addCode(")") 499 | } 500 | 501 | func (t *Transpiler) transpileCallExpressionFactor(node *ast.CallExpression, typ environment.Type) { 502 | t.addCode("structure(factor(") 503 | for i, a := range node.Arguments { 504 | t.Transpile(a.Value) 505 | if i < len(node.Arguments)-1 { 506 | t.addCode(", ") 507 | } 508 | } 509 | 510 | cl, exists := t.env.GetClass(typ.Name) 511 | 512 | if exists { 513 | t.addCode(", class=c(\"" + strings.Join(cl.Value.Classes, "\", \"") + "\")") 514 | return 515 | } 516 | 517 | fct, exists := t.env.GetFactor(typ.Name) 518 | if exists { 519 | t.addCode(", ") 520 | for _, a := range fct.Value.Arguments { 521 | t.Transpile(a.Value) 522 | } 523 | } 524 | t.addCode(")") 525 | 526 | t.addCode(", class=c(\"" + typ.Name + "\", \"factor\")") 527 | 528 | t.addCode(")") 529 | } 530 | 531 | func (t *Transpiler) transpileCallExpressionMatrix(node *ast.CallExpression, typ environment.Type) { 532 | t.addCode("structure(matrix(") 533 | for i, a := range node.Arguments { 534 | t.Transpile(a.Value) 535 | if i < len(node.Arguments)-1 { 536 | t.addCode(", ") 537 | } 538 | } 539 | 540 | cl, exists := t.env.GetClass(typ.Name) 541 | 542 | if exists { 543 | t.addCode(", class=c(\"" + strings.Join(cl.Value.Classes, "\", \"") + "\")") 544 | return 545 | } 546 | 547 | mat, exists := t.env.GetMatrix(typ.Name) 548 | if exists { 549 | t.addCode(", ") 550 | for _, a := range mat.Value.Arguments { 551 | t.Transpile(a.Value) 552 | } 553 | } 554 | t.addCode(")") 555 | 556 | t.addCode(", class=c(\"" + typ.Name + "\", \"matrix\")") 557 | 558 | t.addCode(")") 559 | } 560 | 561 | func (t *Transpiler) transpileCallExpressionVector(node *ast.CallExpression, typ environment.Type) { 562 | t.addCode("c(") 563 | for i, a := range node.Arguments { 564 | t.Transpile(a.Value) 565 | if i < len(node.Arguments)-1 { 566 | t.addCode(", ") 567 | } 568 | } 569 | t.addCode(")") 570 | } 571 | 572 | func (t *Transpiler) transpileCallExpressionDataframe(node *ast.CallExpression, typ environment.Type) { 573 | names := []string{} 574 | t.addCode("structure(data.frame(") 575 | for i, a := range node.Arguments { 576 | t.Transpile(a.Value) 577 | names = append(names, a.Name) 578 | if i < len(node.Arguments)-1 { 579 | t.addCode(", ") 580 | } 581 | } 582 | t.addCode("), names = c(\"" + strings.Join(names, "\", \"") + "\")") 583 | 584 | cl, exists := t.env.GetClass(typ.Name) 585 | 586 | if exists { 587 | t.addCode(", class=c(\"" + strings.Join(cl.Value.Classes, "\", \"") + "\")") 588 | return 589 | } 590 | 591 | t.addCode(", class=c(\"" + typ.Name + "\", \"data.frame\")") 592 | 593 | t.addCode(")") 594 | } 595 | 596 | func (t *Transpiler) transpileCallExpressionObject(node *ast.CallExpression, typ environment.Type) { 597 | t.addCode("structure(new.env(") 598 | for i, a := range node.Arguments { 599 | t.Transpile(a.Value) 600 | if i < len(node.Arguments)-1 { 601 | t.addCode(", ") 602 | } 603 | } 604 | t.addCode(")") 605 | 606 | cl, exists := t.env.GetClass(typ.Name) 607 | 608 | if exists { 609 | t.addCode(", class=c(\"" + strings.Join(cl.Value.Classes, "\", \"") + "\")") 610 | return 611 | } 612 | 613 | t.addCode(", class=c(\"" + typ.Name + "\", \"list\")") 614 | 615 | t.addCode(")") 616 | } 617 | 618 | func (t *Transpiler) transpileCallExpressionStruct(node *ast.CallExpression, typ environment.Type) { 619 | t.addCode("structure(") 620 | for i, a := range node.Arguments { 621 | t.Transpile(a.Value) 622 | if i < len(node.Arguments)-1 { 623 | t.addCode(", ") 624 | } 625 | } 626 | cl, exists := t.env.GetClass(typ.Name) 627 | 628 | if exists { 629 | t.addCode(", class=c(\"" + strings.Join(cl.Value.Classes, "\", \"") + "\")") 630 | return 631 | } 632 | 633 | t.addCode(", class=\"" + typ.Name + "\"") 634 | 635 | t.addCode(")") 636 | } 637 | 638 | func (t *Transpiler) GetCode() string { 639 | return strings.Join(t.code, "") 640 | } 641 | 642 | func (t *Transpiler) addCode(code string) { 643 | t.code = append(t.code, code) 644 | } 645 | 646 | func (t *Transpiler) popCode() { 647 | t.code = t.code[:len(t.code)-1] 648 | } 649 | 650 | func (t *Transpiler) transpileLetStatement(l *ast.LetStatement) { 651 | t.addCode(l.Name + " = ") 652 | } 653 | 654 | func (t *Transpiler) transpileConstStatement(c *ast.ConstStatement) { 655 | t.addCode(c.Name + " = ") 656 | } 657 | 658 | func (t *Transpiler) addNewLine() { 659 | if len(t.code) > 0 && t.code[len(t.code)-1] != "\n" { 660 | t.addCode("\n") 661 | } 662 | } 663 | -------------------------------------------------------------------------------- /transpiler/transpile_test.go: -------------------------------------------------------------------------------- 1 | package transpiler 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/vapourlang/vapour/lexer" 7 | "github.com/vapourlang/vapour/parser" 8 | ) 9 | 10 | func (trans *Transpiler) testOutput(t *testing.T, expected string) { 11 | if trans.GetCode() == expected { 12 | return 13 | } 14 | t.Fatalf("expected:\n`%v`\ngot:\n`%v`", expected, trans.GetCode()) 15 | } 16 | 17 | func TestBasic(t *testing.T) { 18 | code := `let x: int | num = 1 19 | const y: int = 1` 20 | 21 | l := lexer.NewTest(code) 22 | 23 | l.Run() 24 | p := parser.New(l) 25 | 26 | prog := p.Run() 27 | 28 | trans := New() 29 | trans.Transpile(prog) 30 | 31 | expectations := `x = 1 32 | y = 1 33 | 34 | ` 35 | trans.testOutput(t, expectations) 36 | } 37 | 38 | func TestFunc(t *testing.T) { 39 | code := `func add(x: int = 1, y: int = 2): int { 40 | let total: int = x + y * 2 41 | return total 42 | } 43 | 44 | # did not intend on this to work 45 | const anonymous: int = (): int => { 46 | return 2 47 | } 48 | ` 49 | 50 | l := lexer.NewTest(code) 51 | 52 | l.Run() 53 | p := parser.New(l) 54 | 55 | prog := p.Run() 56 | 57 | trans := New() 58 | trans.Transpile(prog) 59 | 60 | expected := `add = function(x = 1,y = 2) { 61 | total = x+y*2 62 | return(total) 63 | } 64 | # did not intend on this to work 65 | anonymous = function() { 66 | return(2) 67 | } 68 | ` 69 | 70 | trans.testOutput(t, expected) 71 | } 72 | 73 | func TestPipe(t *testing.T) { 74 | code := `func add(): null { 75 | df |> 76 | mutate(x = 1) 77 | } ` 78 | 79 | l := lexer.NewTest(code) 80 | 81 | l.Run() 82 | p := parser.New(l) 83 | 84 | prog := p.Run() 85 | 86 | trans := New() 87 | trans.Transpile(prog) 88 | 89 | expected := `add = function() { 90 | df|>mutate(x=1 91 | ) 92 | }` 93 | 94 | trans.testOutput(t, expected) 95 | } 96 | 97 | func TestString(t *testing.T) { 98 | code := `let x: char = "a \"char\"" 99 | let y: char = 'single quotes'` 100 | 101 | l := lexer.NewTest(code) 102 | 103 | l.Run() 104 | p := parser.New(l) 105 | 106 | prog := p.Run() 107 | 108 | trans := New() 109 | trans.Transpile(prog) 110 | 111 | expected := `x = "a \"char\"" 112 | y = 'single quotes' 113 | ` 114 | 115 | trans.testOutput(t, expected) 116 | } 117 | 118 | func TestComment(t *testing.T) { 119 | code := `#' @return something 120 | func add(): int | number { 121 | # compute stuff 122 | let x: tibble = df |> 123 | mutate( 124 | x = "hello", 125 | y = na, 126 | b = true 127 | ) |> 128 | select(x) 129 | 130 | return x 131 | }` 132 | 133 | l := lexer.NewTest(code) 134 | 135 | l.Run() 136 | p := parser.New(l) 137 | 138 | prog := p.Run() 139 | 140 | trans := New() 141 | trans.Transpile(prog) 142 | 143 | expected := `#' @return something 144 | add = function() { 145 | # compute stuff 146 | x = df|>mutate(x="hello" 147 | , y=na 148 | , b=TRUE 149 | )|>select(x) 150 | return(x) 151 | }` 152 | 153 | trans.testOutput(t, expected) 154 | } 155 | 156 | func TestTElipsis(t *testing.T) { 157 | code := `func foo(...: any): char { 158 | paste0(..., collapse = ", ") 159 | } ` 160 | 161 | l := lexer.NewTest(code) 162 | 163 | l.Run() 164 | p := parser.New(l) 165 | 166 | prog := p.Run() 167 | 168 | trans := New() 169 | trans.Transpile(prog) 170 | 171 | expected := `foo = function(...) { 172 | paste0(..., collapse=", " 173 | ) 174 | }` 175 | 176 | trans.testOutput(t, expected) 177 | } 178 | 179 | func TestRange(t *testing.T) { 180 | code := `let x: int | na = 1..10 181 | ` 182 | 183 | l := lexer.NewTest(code) 184 | 185 | l.Run() 186 | p := parser.New(l) 187 | 188 | prog := p.Run() 189 | 190 | trans := New() 191 | trans.Transpile(prog) 192 | 193 | expected := `x = 1:10 194 | ` 195 | 196 | trans.testOutput(t, expected) 197 | } 198 | 199 | func TestFor(t *testing.T) { 200 | code := ` 201 | for(let i: int in 1..nrow(df)) { 202 | print(i) 203 | } 204 | ` 205 | 206 | l := lexer.NewTest(code) 207 | 208 | l.Run() 209 | p := parser.New(l) 210 | 211 | prog := p.Run() 212 | 213 | trans := New() 214 | trans.Transpile(prog) 215 | 216 | expected := `for(i in 1:nrow(df) 217 | ) { 218 | print(i) 219 | }` 220 | 221 | trans.testOutput(t, expected) 222 | } 223 | 224 | func TestWhile(t *testing.T) { 225 | code := `while(i < 10) { 226 | print(i) 227 | } 228 | ` 229 | 230 | l := lexer.NewTest(code) 231 | 232 | l.Run() 233 | p := parser.New(l) 234 | 235 | prog := p.Run() 236 | 237 | trans := New() 238 | trans.Transpile(prog) 239 | 240 | expected := `while(i<10 241 | ) { 242 | print(i) 243 | } 244 | ` 245 | 246 | trans.testOutput(t, expected) 247 | } 248 | 249 | func TestNamespace(t *testing.T) { 250 | code := `let x: dataframe = cars |> 251 | dplyr::mutate(speed > 2) ` 252 | 253 | l := lexer.NewTest(code) 254 | 255 | l.Run() 256 | p := parser.New(l) 257 | 258 | prog := p.Run() 259 | 260 | trans := New() 261 | trans.Transpile(prog) 262 | 263 | expected := `x = cars|>dplyr::mutate(speed>2 264 | ) 265 | ` 266 | 267 | trans.testOutput(t, expected) 268 | } 269 | 270 | func TestIf(t *testing.T) { 271 | code := `let x: bool = (1,2,3) 272 | 273 | if (x) { 274 | print("true") 275 | } else { 276 | print("false") 277 | } 278 | 279 | func foo(n: int): null { 280 | # comment 281 | if(n == 1) { 282 | print(true) 283 | } 284 | } 285 | ` 286 | 287 | l := lexer.NewTest(code) 288 | 289 | l.Run() 290 | p := parser.New(l) 291 | 292 | prog := p.Run() 293 | 294 | trans := New() 295 | trans.Transpile(prog) 296 | 297 | expected := `x = c(1, 2, 3) 298 | if(x){ 299 | print("true") 300 | } else { 301 | print("false") 302 | } 303 | foo = function(n) { 304 | # comment 305 | if(n==1 306 | ){ 307 | print(TRUE) 308 | }} 309 | ` 310 | 311 | trans.testOutput(t, expected) 312 | } 313 | 314 | func TestAnonymous(t *testing.T) { 315 | code := `let y: int = (1,2,3) 316 | 317 | const x: char = "world" 318 | lapply(("hello", x), (z: char): null => { 319 | print(z) 320 | }) 321 | 322 | lapply(1..10, (z: char): null => { 323 | print(z) 324 | }) 325 | ` 326 | 327 | l := lexer.NewTest(code) 328 | 329 | l.Run() 330 | p := parser.New(l) 331 | 332 | prog := p.Run() 333 | 334 | trans := New() 335 | trans.Transpile(prog) 336 | 337 | expected := `y = c(1, 2, 3) 338 | x = "world" 339 | lapply(c("hello", x), function(z) { 340 | print(z) 341 | }) 342 | lapply(1:10 343 | , function(z) { 344 | print(z) 345 | }) 346 | ` 347 | 348 | trans.testOutput(t, expected) 349 | } 350 | 351 | func TestMethod(t *testing.T) { 352 | code := `func (o: obj) add(n: int): char { 353 | return "hello" 354 | } 355 | 356 | type person: struct{ 357 | int, 358 | name: char 359 | } 360 | 361 | func (p: person) setName(name: char): null { 362 | p$name = 2 363 | } 364 | ` 365 | 366 | l := lexer.NewTest(code) 367 | 368 | l.Run() 369 | p := parser.New(l) 370 | 371 | prog := p.Run() 372 | 373 | trans := New() 374 | trans.Transpile(prog) 375 | 376 | expected := `add.obj = function(o, n) { 377 | return("hello") 378 | } 379 | setName.person = function(p, name) { 380 | p$name=2 381 | } 382 | ` 383 | 384 | trans.testOutput(t, expected) 385 | } 386 | 387 | func TestDeclare(t *testing.T) { 388 | code := `let x: int 389 | 390 | x = 2 391 | 392 | type config: object { 393 | name: char, 394 | x: int 395 | } 396 | 397 | config(name = "hello") 398 | 399 | # should fail, does not exist 400 | let z: config = config( 401 | z = 2 402 | ) 403 | 404 | z$name = 2 405 | ` 406 | 407 | l := lexer.NewTest(code) 408 | 409 | l.Run() 410 | p := parser.New(l) 411 | 412 | prog := p.Run() 413 | 414 | trans := New() 415 | trans.Transpile(prog) 416 | 417 | expected := `x=2 418 | structure(list(name="hello" 419 | ), class=c("config", "list")) 420 | # should fail, does not exist 421 | z = structure(list(z=2 422 | ), class=c("config", "list")) 423 | z$name=2 424 | ` 425 | 426 | trans.testOutput(t, expected) 427 | } 428 | 429 | func TestList(t *testing.T) { 430 | code := ` 431 | type person: list { 432 | name: char 433 | } 434 | 435 | type persons: []person 436 | 437 | let peoples: persons = persons( 438 | person(name = "John"), 439 | person(name = "Jane") 440 | ) 441 | 442 | type ints: []ints 443 | 444 | let x: ints = ints(1,2,3) 445 | 446 | type math: func(int) int 447 | 448 | func apply_math(vector: int, cb: math): int { 449 | return cb(vector) 450 | } 451 | 452 | apply_math((1, 2, 3), (x: int): int => { 453 | return x * 3 454 | }) 455 | ` 456 | 457 | l := lexer.NewTest(code) 458 | 459 | l.Run() 460 | p := parser.New(l) 461 | 462 | prog := p.Run() 463 | 464 | trans := New() 465 | trans.Transpile(prog) 466 | 467 | expected := `peoples = persons(structure(list(name="John" 468 | ), class=c("person", "list")) 469 | , structure(list(name="Jane" 470 | ), class=c("person", "list")) 471 | ) 472 | x = ints(1, 2, 3) 473 | apply_math = function(vector,cb) { 474 | return(cb(vector) 475 | ) 476 | } 477 | apply_math(c(1, 2, 3), function(x) { 478 | return(x*3 479 | ) 480 | }) 481 | ` 482 | 483 | trans.testOutput(t, expected) 484 | } 485 | 486 | func TestSquare(t *testing.T) { 487 | code := `let x: int = (1,2,3) 488 | 489 | x[2] = 3 490 | 491 | let y: int = list(1,2,3) 492 | 493 | y[[1]] = 1 494 | 495 | let zz: char = ("hello|world", "hello|again") 496 | let z: char = strsplit(zz[2], "\\|")[[1]] 497 | ` 498 | 499 | l := lexer.NewTest(code) 500 | 501 | l.Run() 502 | p := parser.New(l) 503 | 504 | prog := p.Run() 505 | 506 | trans := New() 507 | trans.Transpile(prog) 508 | 509 | expected := `x = c(1, 2, 3) 510 | x[2]=3 511 | y = list(1, 2, 3) 512 | y[[1]]=1 513 | zz = c("hello|world", "hello|again") 514 | z = strsplit(zz[2 515 | , ], "\\|")[[1]] 516 | ` 517 | 518 | trans.testOutput(t, expected) 519 | } 520 | 521 | func TestClass(t *testing.T) { 522 | code := ` 523 | type userid: object { 524 | id: int, 525 | name: char 526 | } 527 | 528 | userid(1, "hello") 529 | ` 530 | 531 | l := lexer.NewTest(code) 532 | 533 | l.Run() 534 | p := parser.New(l) 535 | 536 | prog := p.Run() 537 | 538 | trans := New() 539 | trans.Transpile(prog) 540 | 541 | expected := `structure(list(1, "hello"), class=c("userid", "list")) 542 | ` 543 | 544 | trans.testOutput(t, expected) 545 | } 546 | 547 | func TestTypeDeclaration(t *testing.T) { 548 | code := `type userId: int 549 | 550 | type st: struct { 551 | int | char, 552 | name: char, 553 | id: int 554 | } 555 | 556 | st(42, name = "xxx") 557 | 558 | type obj: object { 559 | name: char, 560 | id: int 561 | } 562 | 563 | obj(name = "hello") 564 | 565 | @class(hello, world) 566 | type thing: object { 567 | name: char 568 | } 569 | 570 | thing(name = "hello") 571 | 572 | type df: dataframe { 573 | name: char, 574 | id: int 575 | } 576 | 577 | df(name = "hello", id = 1) 578 | ` 579 | 580 | l := lexer.NewTest(code) 581 | 582 | l.Run() 583 | p := parser.New(l) 584 | 585 | prog := p.Run() 586 | 587 | trans := New() 588 | trans.Transpile(prog) 589 | 590 | expected := `structure(42, name="xxx" 591 | , class="st") 592 | structure(list(name="hello" 593 | ), class=c("obj", "list")) 594 | structure(list(name="hello" 595 | ), class=c("hello", "world") 596 | structure(data.frame(name="hello" 597 | , id=1 598 | ), names = c("name", "id"), class=c("df", "data.frame")) 599 | ` 600 | 601 | trans.testOutput(t, expected) 602 | } 603 | 604 | func TestDefer(t *testing.T) { 605 | code := ` 606 | func foo(x: int): int { 607 | defer (): null => {print("hello")} 608 | return 1 + 1 609 | } 610 | ` 611 | 612 | l := lexer.NewTest(code) 613 | 614 | l.Run() 615 | p := parser.New(l) 616 | 617 | prog := p.Run() 618 | 619 | trans := New() 620 | trans.Transpile(prog) 621 | 622 | expected := `foo = function(x) { 623 | on.exit((function() {print("hello") 624 | })()) 625 | return(1+1 626 | ) 627 | } 628 | ` 629 | 630 | trans.testOutput(t, expected) 631 | } 632 | 633 | func TestStruct(t *testing.T) { 634 | code := ` 635 | type person: struct { 636 | int | num, 637 | name: char, 638 | age: int 639 | } 640 | 641 | func create(name: char, age: int): person { 642 | return person(0, name = name, age = age) 643 | } 644 | 645 | type thing: struct { 646 | int 647 | } 648 | 649 | func create2(): thing { 650 | return thing(1) 651 | } 652 | 653 | @class(more, classes, here) 654 | type stuff: struct { 655 | int 656 | } 657 | 658 | func create3(): thing { 659 | return stuff(2) 660 | } 661 | ` 662 | 663 | l := lexer.NewTest(code) 664 | 665 | l.Run() 666 | p := parser.New(l) 667 | 668 | prog := p.Run() 669 | 670 | trans := New() 671 | trans.Transpile(prog) 672 | 673 | expected := `create = function(name,age) { 674 | return(structure(0, name=name 675 | , age=age 676 | , class="person") 677 | ) 678 | } 679 | create2 = function() { 680 | return(structure(1, class="thing") 681 | ) 682 | } 683 | create3 = function() { 684 | return(structure(2, class=c("more", "classes", "here") 685 | ) 686 | } 687 | ` 688 | 689 | trans.testOutput(t, expected) 690 | } 691 | 692 | func TestObject(t *testing.T) { 693 | code := ` 694 | type df: dataframe { 695 | name: char, 696 | age: int 697 | } 698 | 699 | df(name = "hello", age = 1) 700 | 701 | type thing: object { 702 | wheels: bool 703 | } 704 | 705 | thing(wheels = true) 706 | ` 707 | 708 | l := lexer.NewTest(code) 709 | 710 | l.Run() 711 | p := parser.New(l) 712 | 713 | prog := p.Run() 714 | 715 | trans := New() 716 | trans.Transpile(prog) 717 | 718 | expected := `structure(data.frame(name="hello" 719 | , age=1 720 | ), names = c("name", "age"), class=c("df", "data.frame")) 721 | structure(list(wheels=TRUE 722 | ), class=c("thing", "list")) 723 | ` 724 | 725 | trans.testOutput(t, expected) 726 | } 727 | 728 | func TestVector(t *testing.T) { 729 | code := ` 730 | type userid: int 731 | 732 | userid(3) 733 | 734 | type lst: list { 735 | int | char | na 736 | } 737 | 738 | lst(1, "hello") 739 | ` 740 | 741 | l := lexer.NewTest(code) 742 | 743 | l.Run() 744 | p := parser.New(l) 745 | 746 | prog := p.Run() 747 | 748 | trans := New() 749 | trans.Transpile(prog) 750 | 751 | expected := `c(3) 752 | structure(list(1, "hello"), class=c("lst", "list")) 753 | ` 754 | 755 | trans.testOutput(t, expected) 756 | } 757 | 758 | func TestType(t *testing.T) { 759 | code := ` 760 | type person: struct { 761 | list, 762 | name: char 763 | } 764 | 765 | person(list(), name = "John") 766 | 767 | # should fail, attr not in type 768 | person(list(), age = 1) 769 | 770 | person(1) 771 | 772 | @class(x, y, z) 773 | type cl: struct { 774 | int 775 | } 776 | 777 | let z: cl = cl(2) 778 | 779 | @class(fr, lt) 780 | type lst: list { 781 | int 782 | } 783 | 784 | let zzzz: lst = lst() 785 | 786 | @generic 787 | func (p: any) set_age(age: int): any 788 | 789 | @default 790 | func (p: any) set_age(age: int): null { 791 | stop("not implemented") 792 | } 793 | 794 | type person: struct { 795 | char 796 | } 797 | ` 798 | 799 | l := lexer.NewTest(code) 800 | 801 | l.Run() 802 | p := parser.New(l) 803 | 804 | prog := p.Run() 805 | 806 | trans := New() 807 | trans.Transpile(prog) 808 | 809 | expected := `structure(list() 810 | , name="John" 811 | , class="person") 812 | # should fail, attr not in type 813 | structure(list() 814 | , age=1 815 | , class="person") 816 | structure(1, class="person") 817 | z = structure(2, class=c("x", "y", "z") 818 | zzzz = structure(list(), class=c("fr", "lt") 819 | set_age = function(p, age) {UseMethod("set_age")} 820 | set_age.default = function(p, age) { 821 | stop("not implemented") 822 | } 823 | ` 824 | 825 | trans.testOutput(t, expected) 826 | } 827 | 828 | func TestIncrement(t *testing.T) { 829 | code := ` 830 | let x: int = 10 831 | 832 | x += 2 833 | ` 834 | 835 | l := lexer.NewTest(code) 836 | 837 | l.Run() 838 | p := parser.New(l) 839 | 840 | prog := p.Run() 841 | 842 | trans := New() 843 | trans.Transpile(prog) 844 | 845 | expected := `x = 10 846 | x=x+2 847 | ` 848 | 849 | trans.testOutput(t, expected) 850 | } 851 | 852 | func TestMatrix(t *testing.T) { 853 | code := ` 854 | @matrix(nrow = 2, ncol = 4) 855 | type mat: matrix { 856 | int 857 | } 858 | 859 | mat((1, 2, 3)) 860 | ` 861 | 862 | l := lexer.NewTest(code) 863 | 864 | l.Run() 865 | p := parser.New(l) 866 | 867 | prog := p.Run() 868 | 869 | trans := New() 870 | trans.Transpile(prog) 871 | 872 | expected := `structure(matrix(c(1, 2, 3), nrow=2 873 | ncol=4 874 | ), class=c("mat", "matrix")) 875 | ` 876 | 877 | trans.testOutput(t, expected) 878 | } 879 | 880 | func TestFactor(t *testing.T) { 881 | code := ` 882 | @factor(levels = TRUE) 883 | type fac: factor { 884 | int 885 | } 886 | 887 | fac((1, 2, 3)) 888 | ` 889 | 890 | l := lexer.NewTest(code) 891 | 892 | l.Run() 893 | p := parser.New(l) 894 | 895 | prog := p.Run() 896 | 897 | trans := New() 898 | trans.Transpile(prog) 899 | 900 | expected := `structure(factor(c(1, 2, 3), levels=TRUE 901 | ), class=c("fac", "factor")) 902 | ` 903 | 904 | trans.testOutput(t, expected) 905 | } 906 | 907 | func TestCall(t *testing.T) { 908 | code := ` 909 | bar(1, x = 2, "hello") 910 | 911 | bar( 912 | 1, 913 | x = 2, 914 | "hello" 915 | ) 916 | 917 | foo(z = 2) 918 | 919 | foo(1, 2, 3) 920 | 921 | foo( 922 | z = "hello" 923 | ) 924 | 925 | foo("hello") 926 | ` 927 | 928 | l := lexer.NewTest(code) 929 | 930 | l.Run() 931 | p := parser.New(l) 932 | 933 | prog := p.Run() 934 | 935 | trans := New() 936 | trans.Transpile(prog) 937 | 938 | expected := `bar(1, x=2 939 | , "hello") 940 | bar(1, x=2 941 | , "hello") 942 | foo(z=2 943 | ) 944 | foo(1, 2, 3) 945 | foo(z="hello" 946 | ) 947 | foo("hello") 948 | ` 949 | 950 | trans.testOutput(t, expected) 951 | } 952 | 953 | func TestIdent(t *testing.T) { 954 | code := `let x: int = (1,2,3) 955 | 956 | x[1, 2] = 15 957 | 958 | x[[3]] = 15 959 | 960 | df$x = 23 961 | 962 | print(x) ` 963 | 964 | l := lexer.NewTest(code) 965 | 966 | l.Run() 967 | p := parser.New(l) 968 | 969 | prog := p.Run() 970 | 971 | trans := New() 972 | trans.Transpile(prog) 973 | 974 | expected := `x = c(1, 2, 3) 975 | x[1,2]=15 976 | x[[3]]=15 977 | df$x=23 978 | print(x) 979 | ` 980 | 981 | trans.testOutput(t, expected) 982 | } 983 | -------------------------------------------------------------------------------- /vapour.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/vapourlang/vapour/config" 5 | "github.com/vapourlang/vapour/lexer" 6 | ) 7 | 8 | type vapour struct { 9 | name string 10 | version string 11 | root *string 12 | files lexer.Files 13 | config *config.Config 14 | } 15 | 16 | func New() *vapour { 17 | return &vapour{ 18 | name: "vapour", 19 | version: "0.0.6", 20 | } 21 | } 22 | 23 | func addHeader(code string) string { 24 | return "# GENERATED BY VAPOUR\n# DO NOT EDIT\n" + code 25 | } 26 | -------------------------------------------------------------------------------- /walker/diagnostics.go: -------------------------------------------------------------------------------- 1 | package walker 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/vapourlang/vapour/diagnostics" 7 | "github.com/vapourlang/vapour/token" 8 | ) 9 | 10 | func (w *Walker) addFatalf(tok token.Item, fm string, a ...interface{}) { 11 | str := fmt.Sprintf(fm, a...) 12 | w.errors = append(w.errors, diagnostics.New(tok, str, diagnostics.Fatal)) 13 | } 14 | 15 | func (w *Walker) addWarnf(tok token.Item, fm string, a ...interface{}) { 16 | str := fmt.Sprintf(fm, a...) 17 | w.errors = append(w.errors, diagnostics.New(tok, str, diagnostics.Warn)) 18 | } 19 | 20 | func (w *Walker) addInfof(tok token.Item, fm string, a ...interface{}) { 21 | str := fmt.Sprintf(fm, a...) 22 | w.errors = append(w.errors, diagnostics.New(tok, str, diagnostics.Info)) 23 | } 24 | 25 | func (w *Walker) addHintf(tok token.Item, fm string, a ...interface{}) { 26 | str := fmt.Sprintf(fm, a...) 27 | w.errors = append(w.errors, diagnostics.New(tok, str, diagnostics.Hint)) 28 | } 29 | 30 | func (w *Walker) HasDiagnostic() bool { 31 | return len(w.errors) > 0 32 | } 33 | 34 | func (w *Walker) HasError() bool { 35 | for _, v := range w.errors { 36 | if v.Severity != diagnostics.Info && v.Severity != diagnostics.Hint { 37 | return true 38 | } 39 | } 40 | return false 41 | } 42 | 43 | func (w *Walker) Errors() diagnostics.Diagnostics { 44 | return w.errors.Unique() 45 | } 46 | -------------------------------------------------------------------------------- /walker/types.go: -------------------------------------------------------------------------------- 1 | package walker 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/vapourlang/vapour/ast" 7 | "github.com/vapourlang/vapour/environment" 8 | ) 9 | 10 | type Function struct { 11 | name string 12 | returnType ast.Types 13 | arguments []ast.Types 14 | } 15 | 16 | func (w *Walker) typesExist(types ast.Types) (*ast.Type, bool) { 17 | for _, t := range types { 18 | // bit hacky but NA, NULL, etc. have a blank type 19 | // need to fix upstream in parser 20 | if t.Name == "" { 21 | return nil, true 22 | } 23 | 24 | _, te := w.env.GetType(t.Package, t.Name) 25 | _, fe := w.env.GetSignature(t.Name) 26 | 27 | if !te && !fe { 28 | return t, false 29 | } 30 | } 31 | 32 | return nil, true 33 | } 34 | 35 | func (w *Walker) allTypesIdentical(types []*ast.Type) bool { 36 | if len(types) == 0 { 37 | return true 38 | } 39 | 40 | types, ok := w.getNativeTypes(types) 41 | 42 | if !ok { 43 | return false 44 | } 45 | 46 | var previousType *ast.Type 47 | for i, t := range types { 48 | if i == 0 { 49 | previousType = t 50 | continue 51 | } 52 | 53 | if t.Name != previousType.Name || t.List != previousType.List { 54 | return false 55 | } 56 | } 57 | 58 | return true 59 | } 60 | 61 | func typeIdentical(t1, t2 *ast.Type) bool { 62 | return t1.Name == t2.Name && t1.List == t2.List 63 | } 64 | 65 | func acceptAny(types ast.Types) bool { 66 | for _, t := range types { 67 | if t.Name == "any" { 68 | return true 69 | } 70 | } 71 | return false 72 | } 73 | 74 | func (w *Walker) typesValid(valid, actual ast.Types) bool { 75 | validNative, _ := w.getNativeTypes(valid) 76 | actualNative, _ := w.getNativeTypes(actual) 77 | 78 | validNative = append(validNative, valid...) 79 | actualNative = append(actualNative, actual...) 80 | 81 | // we don't have the type 82 | if len(validNative) == 0 { 83 | return true 84 | } 85 | 86 | if acceptAny(validNative) { 87 | return true 88 | } 89 | 90 | for _, l := range actualNative { 91 | if w.typeValid(l, validNative) { 92 | continue 93 | } 94 | 95 | return false 96 | } 97 | 98 | return true 99 | } 100 | 101 | func (w *Walker) typeValid(t *ast.Type, valid ast.Types) bool { 102 | // we just don't have the type 103 | // could be base R dataset 104 | if t.Name == "" { 105 | return true 106 | } 107 | 108 | for _, v := range valid { 109 | if typeIdentical(t, v) { 110 | return true 111 | } 112 | 113 | if v.Name == "num" && t.Name == "int" && v.List == t.List { 114 | return true 115 | } 116 | } 117 | 118 | return false 119 | } 120 | 121 | func (w *Walker) validAccessType(types ast.Types) bool { 122 | for _, t := range types { 123 | if t.Name == "any" { 124 | return true 125 | } 126 | 127 | obj, exists := w.env.GetType(t.Package, t.Name) 128 | 129 | // we don't have the type 130 | // assume it's an error on our end? 131 | if !exists { 132 | return true 133 | } 134 | 135 | if !contains(obj.Object, []string{"dataframe", "object", "struct", "environment"}) { 136 | return false 137 | } 138 | } 139 | return true 140 | } 141 | 142 | func (w *Walker) validMathTypes(types ast.Types) bool { 143 | types, ok := w.getNativeTypes(types) 144 | 145 | if !ok { 146 | return false 147 | } 148 | 149 | for _, t := range types { 150 | if !contains(t.Name, []string{"int", "num", "na", "date", "posixct", "posixlt"}) { 151 | return false 152 | } 153 | } 154 | return true 155 | } 156 | 157 | func contains(value string, arr []string) bool { 158 | for _, a := range arr { 159 | if value == a { 160 | return true 161 | } 162 | } 163 | return false 164 | } 165 | 166 | func (w *Walker) retrieveNativeTypes(types, nativeTypes ast.Types) (ast.Types, bool) { 167 | for _, t := range types { 168 | if environment.IsNativeType(t.Name) { 169 | nativeTypes = append(nativeTypes, t) 170 | continue 171 | } 172 | 173 | customType, exists := w.env.GetType(t.Package, t.Name) 174 | 175 | if customType.Object == "struct" || customType.Object == "object" { 176 | return append(nativeTypes, t), false 177 | } 178 | 179 | if exists && customType.Object == "vector" { 180 | return w.retrieveNativeTypes(customType.Type, nativeTypes) 181 | } 182 | 183 | if exists && customType.Object == "list" { 184 | return w.retrieveNativeTypes(customType.Type, nativeTypes) 185 | } 186 | 187 | if exists && customType.Object == "impliedList" { 188 | return w.retrieveNativeTypes(customType.Type, nativeTypes) 189 | } 190 | 191 | return append(nativeTypes, t), false 192 | } 193 | 194 | return nativeTypes, true 195 | } 196 | 197 | func (w *Walker) getNativeTypes(types ast.Types) (ast.Types, bool) { 198 | return w.retrieveNativeTypes(types, ast.Types{}) 199 | } 200 | 201 | func (w *Walker) validIteratorTypes(types ast.Types) bool { 202 | types, _ = w.getNativeTypes(types) 203 | var valid []bool 204 | for _, t := range types { 205 | if contains(t.Name, []string{"int", "num", "char", "any"}) { 206 | valid = append(valid, true) 207 | continue 208 | } 209 | 210 | custom, exists := w.env.GetType(t.Package, t.Name) 211 | if !exists { 212 | valid = append(valid, false) 213 | continue 214 | } 215 | 216 | if len(custom.Type) > 0 && allLists(custom.Type) { 217 | valid = append(valid, true) 218 | continue 219 | } 220 | 221 | if custom.Object == "list" { 222 | valid = append(valid, true) 223 | continue 224 | } 225 | 226 | valid = append(valid, false) 227 | } 228 | return allTrue(valid) 229 | } 230 | 231 | func allLists(types ast.Types) bool { 232 | for _, t := range types { 233 | if !t.List { 234 | return false 235 | } 236 | } 237 | 238 | return true 239 | } 240 | 241 | func allTrue(values []bool) bool { 242 | for _, b := range values { 243 | if !b { 244 | return false 245 | } 246 | } 247 | return true 248 | } 249 | 250 | func (w *Walker) checkIdentifier(node *ast.Identifier) { 251 | v, exists := w.env.GetVariable(node.Value, true) 252 | 253 | if exists { 254 | if v.CanMiss { 255 | w.addWarnf( 256 | node.Token, 257 | "`%v` might be missing", 258 | node.Token.Value, 259 | ) 260 | } 261 | 262 | return 263 | } 264 | 265 | _, exists = w.env.GetType("", node.Value) 266 | 267 | if exists { 268 | w.env.SetTypeUsed("", node.Value) 269 | return 270 | } 271 | 272 | _, exists = w.env.GetFunction(node.Value, true) 273 | 274 | if exists { 275 | // we currently don't set/check used functions 276 | // they may be exported from a package (and thus not used) 277 | return 278 | } 279 | 280 | _, exists = w.env.GetSignature(node.Value) 281 | 282 | if exists { 283 | w.env.SetSignatureUsed(node.Value) 284 | return 285 | } 286 | 287 | // we are actually declaring variable in a call 288 | if !w.isInNamespace() { 289 | w.addWarnf( 290 | node.Token, 291 | "`%v` not found", 292 | node.Value, 293 | ) 294 | } 295 | } 296 | 297 | type callback func(*ast.Identifier) 298 | 299 | func (w *Walker) callIfIdentifier(node ast.Node, fn callback) { 300 | switch n := node.(type) { 301 | case *ast.Identifier: 302 | fn(n) 303 | } 304 | } 305 | 306 | func (w *Walker) checkIfIdentifier(node ast.Node) { 307 | switch n := node.(type) { 308 | case *ast.Identifier: 309 | w.checkIdentifier(n) 310 | } 311 | } 312 | 313 | func (w *Walker) getAttribute(name string, attrs []*ast.TypeAttributesStatement) (ast.Types, bool) { 314 | for _, a := range attrs { 315 | if a.Name == name { 316 | return a.Type, true 317 | } 318 | } 319 | return nil, false 320 | } 321 | 322 | func (w *Walker) attributeMatch(arg ast.Argument, inc ast.Types, t environment.Type) bool { 323 | a, ok := w.getAttribute(arg.Name, t.Attributes) 324 | 325 | if !ok { 326 | w.addFatalf( 327 | arg.Token, 328 | "attribute `%v` not found", 329 | arg.Name, 330 | ) 331 | return false 332 | } 333 | 334 | ok = w.typesValid(a, inc) 335 | 336 | if !ok { 337 | w.addFatalf( 338 | arg.Token, 339 | "attribute `%v` expects `%v`, got `%v`", 340 | arg.Name, 341 | a, 342 | inc, 343 | ) 344 | return false 345 | } 346 | 347 | return true 348 | } 349 | 350 | func (w *Walker) warnUnusedTypes() { 351 | for k, v := range w.env.Types() { 352 | if v.Used { 353 | continue 354 | } 355 | w.addInfof( 356 | v.Token, 357 | "type `%v` is never used", 358 | k, 359 | ) 360 | } 361 | } 362 | 363 | func (w *Walker) warnUnusedVariables() { 364 | for k, v := range w.env.Variables() { 365 | if v.Used { 366 | continue 367 | } 368 | w.addInfof( 369 | v.Token, 370 | "`%v` is never used", 371 | k, 372 | ) 373 | } 374 | } 375 | 376 | func (w *Walker) canBeFunction(types ast.Types) (environment.Signature, bool) { 377 | for _, t := range types { 378 | signature, exists := w.env.GetSignature(t.Name) 379 | 380 | if exists { 381 | return signature, true 382 | } 383 | } 384 | return environment.Signature{}, false 385 | } 386 | 387 | func (w *Walker) getFunctionSignatureFromFunctionLiteral(fn *ast.FunctionLiteral) Function { 388 | fnc := Function{ 389 | name: fn.Name, 390 | returnType: fn.ReturnType, 391 | } 392 | 393 | for _, a := range fn.Parameters { 394 | fnc.arguments = append(fnc.arguments, a.Type) 395 | } 396 | 397 | return fnc 398 | } 399 | 400 | func (w *Walker) getFunctionSignatureFromIdentifier(n *ast.Identifier) Function { 401 | fnc := Function{ 402 | name: n.Value, 403 | } 404 | 405 | fn, exists := w.env.GetFunction(n.Value, true) 406 | 407 | if !exists { 408 | return fnc 409 | } 410 | 411 | return w.getFunctionSignatureFromFunctionLiteral(fn.Value) 412 | } 413 | 414 | func (w *Walker) signaturesMatch(valid environment.Signature, actual Function) (string, bool) { 415 | ok := w.typesValid(valid.Value.Return, actual.returnType) 416 | 417 | if !ok { 418 | return fmt.Sprintf("expected return `%v`, got `%v`", valid.Value.Return, actual.returnType), false 419 | } 420 | 421 | if len(valid.Value.Arguments) != len(actual.arguments) { 422 | return fmt.Sprintf("number of arguments do not match signature `%v`, expected %v, got %v", valid.Value.Name, len(valid.Value.Arguments), len(actual.arguments)), false 423 | } 424 | 425 | for index, validArg := range valid.Value.Arguments { 426 | actualArg := actual.arguments[index] 427 | 428 | ok := w.typesValid(validArg, actualArg) 429 | 430 | if !ok { 431 | return fmt.Sprintf("argument #%v, expected `%v`, got `%v`", index, validArg, actualArg), false 432 | } 433 | } 434 | 435 | return "", true 436 | } 437 | 438 | func (w *Walker) comparisonsValid(valid, actual ast.Types) bool { 439 | validNative, _ := w.getNativeTypes(valid) 440 | actualNative, _ := w.getNativeTypes(actual) 441 | 442 | validNative = append(validNative, valid...) 443 | actualNative = append(actualNative, actual...) 444 | 445 | // we don't have the type 446 | if len(validNative) == 0 { 447 | return true 448 | } 449 | 450 | if acceptAny(validNative) { 451 | return true 452 | } 453 | 454 | for _, l := range actualNative { 455 | if w.comparisonValid(l, validNative) { 456 | continue 457 | } 458 | 459 | return false 460 | } 461 | 462 | return true 463 | } 464 | 465 | func (w *Walker) comparisonValid(t *ast.Type, valid ast.Types) bool { 466 | // we just don't have the type 467 | // could be base R dataset 468 | if t.Name == "" { 469 | return true 470 | } 471 | 472 | for _, v := range valid { 473 | if typeIdentical(t, v) { 474 | return true 475 | } 476 | 477 | if v.Name == "num" && t.Name == "int" && v.List == t.List { 478 | return true 479 | } 480 | 481 | if v.Name == "int" && t.Name == "num" && v.List == t.List { 482 | return true 483 | } 484 | } 485 | 486 | return false 487 | } 488 | -------------------------------------------------------------------------------- /walker/walk_test.go: -------------------------------------------------------------------------------- 1 | package walker 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/vapourlang/vapour/diagnostics" 8 | "github.com/vapourlang/vapour/environment" 9 | "github.com/vapourlang/vapour/lexer" 10 | "github.com/vapourlang/vapour/parser" 11 | "github.com/vapourlang/vapour/r" 12 | ) 13 | 14 | func (w *Walker) testDiagnostics(t *testing.T, expected diagnostics.Diagnostics) { 15 | if len(w.Errors()) != len(expected) { 16 | w.Errors().Print() 17 | t.Fatalf( 18 | "expected %v diagnostics, got %v", 19 | len(expected), 20 | len(w.Errors()), 21 | ) 22 | } 23 | 24 | for index, e := range expected { 25 | if e.Severity != w.Errors()[index].Severity { 26 | fmt.Printf("Error at %v\n", index) 27 | fmt.Printf("%v", w.Errors()[index]) 28 | t.Fatalf( 29 | "diagnostics %v, expected severity %v, got %v", 30 | index, 31 | e.Severity, 32 | w.Errors()[index].Severity, 33 | ) 34 | } 35 | } 36 | } 37 | 38 | func TestInfix(t *testing.T) { 39 | code := `let x: char = "hello" 40 | 41 | # no longer fails on v0.0.5 42 | x = NA 43 | 44 | # should fail, types do not match 45 | let z: char = 1 46 | 47 | # should be fine since v0.0.5 48 | func add(n: int = 1, y: int = 2): int { 49 | if(n == 1){ 50 | return NA 51 | } 52 | 53 | return n + y 54 | } 55 | 56 | # no longer fails since v0.0.5 57 | let result: int = add(1, 2) 58 | 59 | # should fail, const must have single type 60 | const v: int | char = 1 61 | 62 | const c: int = 1 63 | 64 | # should fail, it's a constant 65 | c += 2 66 | ` 67 | 68 | l := lexer.NewTest(code) 69 | 70 | l.Run() 71 | p := parser.New(l) 72 | 73 | prog := p.Run() 74 | 75 | w := New() 76 | 77 | w.Run(prog) 78 | 79 | expected := diagnostics.Diagnostics{ 80 | {Severity: diagnostics.Fatal}, 81 | {Severity: diagnostics.Fatal}, 82 | {Severity: diagnostics.Fatal}, 83 | } 84 | 85 | w.testDiagnostics(t, expected) 86 | } 87 | 88 | func TestNamespace(t *testing.T) { 89 | code := ` 90 | # should fail, duplicated params 91 | func foo(x: int, x: int): int {return x + y} 92 | 93 | # should fail, duplicated params 94 | func (x: int) bar(x: int): int {return x + y} 95 | ` 96 | 97 | l := lexer.NewTest(code) 98 | 99 | l.Run() 100 | p := parser.New(l) 101 | 102 | prog := p.Run() 103 | 104 | w := New() 105 | w.Run(prog) 106 | 107 | expected := diagnostics.Diagnostics{ 108 | {Severity: diagnostics.Fatal}, 109 | {Severity: diagnostics.Warn}, 110 | {Severity: diagnostics.Warn}, 111 | {Severity: diagnostics.Fatal}, 112 | {Severity: diagnostics.Warn}, 113 | {Severity: diagnostics.Warn}, 114 | } 115 | 116 | w.testDiagnostics(t, expected) 117 | } 118 | 119 | func TestNumber(t *testing.T) { 120 | code := `let x: num = 1 121 | 122 | x = 1.1 123 | 124 | let u: int = 1e10 125 | 126 | let integer: int = 1 127 | 128 | # should fail, assign num to int 129 | integer = 2.1 130 | 131 | let s: int = sum(1,2,3) 132 | ` 133 | 134 | l := lexer.NewTest(code) 135 | 136 | l.Run() 137 | p := parser.New(l) 138 | 139 | prog := p.Run() 140 | 141 | w := New() 142 | 143 | w.Run(prog) 144 | 145 | expected := diagnostics.Diagnostics{ 146 | {Severity: diagnostics.Fatal}, 147 | } 148 | 149 | w.testDiagnostics(t, expected) 150 | } 151 | 152 | func TestCall(t *testing.T) { 153 | code := `func foo(x: int, y: char): int { 154 | print(y) 155 | return x + 1 156 | } 157 | 158 | # should fail, argument does not exist 159 | foo(z = 2) 160 | 161 | # should fail, wrong type 162 | foo( 163 | x = "hello" 164 | ) 165 | 166 | # should fail, wrong type 167 | foo("hello") 168 | 169 | # should fail, too many arguments 170 | foo(1, "hello", 3) 171 | 172 | func lg (...: char): null { 173 | print(...) 174 | } 175 | 176 | lg("hello", "world") 177 | 178 | # should fail, wrong type 179 | lg("hello", 1) 180 | lg("hello", something = 1) 181 | ` 182 | 183 | l := lexer.NewTest(code) 184 | 185 | l.Run() 186 | p := parser.New(l) 187 | 188 | prog := p.Run() 189 | 190 | w := New() 191 | w.Run(prog) 192 | 193 | expected := diagnostics.Diagnostics{ 194 | {Severity: diagnostics.Warn}, 195 | {Severity: diagnostics.Warn}, 196 | {Severity: diagnostics.Fatal}, 197 | {Severity: diagnostics.Fatal}, 198 | {Severity: diagnostics.Fatal}, 199 | {Severity: diagnostics.Fatal}, 200 | {Severity: diagnostics.Fatal}, 201 | {Severity: diagnostics.Fatal}, 202 | } 203 | 204 | w.testDiagnostics(t, expected) 205 | } 206 | 207 | func TestMissing(t *testing.T) { 208 | code := ` 209 | # should warn, can be missing 210 | func hello(what: char): char { 211 | sprintf("hello, %s!", what) 212 | } 213 | 214 | type dataset: dataframe { 215 | name: char 216 | } 217 | 218 | # should warn, can be missing 219 | func h(dat: dataset): char { 220 | dat$name = "hello" 221 | return "done" 222 | } 223 | ` 224 | 225 | l := lexer.NewTest(code) 226 | 227 | l.Run() 228 | p := parser.New(l) 229 | 230 | prog := p.Run() 231 | 232 | w := New() 233 | 234 | w.Run(prog) 235 | 236 | expected := diagnostics.Diagnostics{ 237 | {Severity: diagnostics.Warn}, 238 | {Severity: diagnostics.Fatal}, 239 | {Severity: diagnostics.Warn}, 240 | } 241 | 242 | w.testDiagnostics(t, expected) 243 | } 244 | 245 | func TestExists(t *testing.T) { 246 | code := ` 247 | # should fail, x does not exist 248 | x = 1 249 | 250 | # package not installed 251 | pkg::fn(x = 2) 252 | 253 | dplyr::filter(x = 2) 254 | 255 | # should fail, z does not exist 256 | func foo(y: int): int { 257 | return z 258 | } 259 | ` 260 | 261 | l := lexer.NewTest(code) 262 | 263 | l.Run() 264 | if l.HasError() { 265 | fmt.Printf("lexer errored") 266 | l.Errors().Print() 267 | return 268 | } 269 | p := parser.New(l) 270 | 271 | prog := p.Run() 272 | if p.HasError() { 273 | fmt.Printf("parser errored") 274 | p.Errors().Print() 275 | } 276 | 277 | w := New() 278 | 279 | w.Run(prog) 280 | 281 | expected := diagnostics.Diagnostics{ 282 | {Severity: diagnostics.Warn}, 283 | {Severity: diagnostics.Hint}, 284 | {Severity: diagnostics.Warn}, 285 | {Severity: diagnostics.Info}, 286 | } 287 | 288 | w.testDiagnostics(t, expected) 289 | } 290 | 291 | func TestTypeMatch(t *testing.T) { 292 | code := ` 293 | type userid: int 294 | 295 | let me: userid = userid(1) 296 | 297 | # should fail, wrong type 298 | let him: userid = "hello" 299 | 300 | # should fail, vector expects unnamed args 301 | me = userid(x = 1) 302 | 303 | type lst: list { int | na } 304 | 305 | let theList: lst = lst(1, 2) 306 | 307 | # should fail, wrong type 308 | theList = lst("aaaa", 1) 309 | 310 | type config: struct { 311 | char, 312 | x: int 313 | } 314 | 315 | type inline: object { 316 | first: int, 317 | second: char 318 | } 319 | 320 | config(2, x = 2) 321 | 322 | # should fail, must be named 323 | inline(1) 324 | 325 | # should fail, first arg of struct cannot be named 326 | config(u = 2, x = 2, z = 2) 327 | 328 | # should fail, struct attribute must be named 329 | config(2, 2) 330 | 331 | # should fail, does not exist 332 | inline( 333 | z = 2 334 | ) 335 | ` 336 | 337 | l := lexer.NewTest(code) 338 | 339 | l.Run() 340 | p := parser.New(l) 341 | 342 | prog := p.Run() 343 | 344 | if p.HasError() { 345 | fmt.Println(p.Errors()) 346 | return 347 | } 348 | 349 | w := New() 350 | 351 | w.Run(prog) 352 | 353 | expected := diagnostics.Diagnostics{ 354 | {Severity: diagnostics.Fatal}, 355 | {Severity: diagnostics.Fatal}, 356 | {Severity: diagnostics.Fatal}, 357 | {Severity: diagnostics.Fatal}, 358 | {Severity: diagnostics.Fatal}, 359 | {Severity: diagnostics.Fatal}, 360 | {Severity: diagnostics.Fatal}, 361 | {Severity: diagnostics.Fatal}, 362 | {Severity: diagnostics.Fatal}, 363 | {Severity: diagnostics.Fatal}, 364 | } 365 | 366 | w.testDiagnostics(t, expected) 367 | } 368 | 369 | func TestR(t *testing.T) { 370 | code := ` 371 | # should fail, package not installed 372 | xxx::foo() 373 | 374 | # should fail, wrong function 375 | dplyr::wrong_function() 376 | 377 | let x: int = 1 378 | 379 | if(x == 1){ 380 | x <- 2 381 | } 382 | 383 | # should fail does not exist 384 | y <- 2 385 | ` 386 | 387 | l := lexer.NewTest(code) 388 | 389 | l.Run() 390 | p := parser.New(l) 391 | 392 | prog := p.Run() 393 | 394 | w := New() 395 | w.Run(prog) 396 | 397 | expected := diagnostics.Diagnostics{ 398 | {Severity: diagnostics.Hint}, 399 | {Severity: diagnostics.Hint}, 400 | {Severity: diagnostics.Fatal}, 401 | } 402 | 403 | w.testDiagnostics(t, expected) 404 | } 405 | 406 | func TestFunction(t *testing.T) { 407 | code := ` 408 | func foo(n: int): int { 409 | let x: char = "hello" 410 | 411 | # should fail, returns wrong type 412 | if (n == 2) { 413 | return "hello" 414 | } 415 | 416 | if (n == 3) { 417 | return 1.2 418 | } 419 | 420 | # should fail, returns wrong type 421 | return x 422 | 423 | # should fail, returns does not exist 424 | return u 425 | } 426 | 427 | # should fail, missing return 428 | lapply(1..10, (x: int): int => {}) 429 | 430 | # should fail, string of identifiers 431 | lapply(1..10, (): int => { 432 | this is not valid but doesnt throw an error 433 | }) 434 | ` 435 | 436 | l := lexer.NewTest(code) 437 | 438 | l.Run() 439 | p := parser.New(l) 440 | 441 | prog := p.Run() 442 | 443 | w := New() 444 | 445 | w.Run(prog) 446 | 447 | expected := diagnostics.Diagnostics{ 448 | {Severity: diagnostics.Warn}, 449 | {Severity: diagnostics.Fatal}, 450 | {Severity: diagnostics.Warn}, 451 | {Severity: diagnostics.Fatal}, 452 | {Severity: diagnostics.Fatal}, 453 | {Severity: diagnostics.Warn}, 454 | {Severity: diagnostics.Fatal}, 455 | {Severity: diagnostics.Info}, 456 | {Severity: diagnostics.Fatal}, 457 | } 458 | 459 | w.testDiagnostics(t, expected) 460 | } 461 | 462 | func TestSquare(t *testing.T) { 463 | code := `let x: int = (1,2,3) 464 | 465 | # we no longer check these 466 | x[2] = 3 467 | 468 | let zz: char = ("hello|world", "hello|again") 469 | let z: any = strsplit(zz[2], "\\|")[[1]] 470 | 471 | x[1, 2] = 15 472 | 473 | x[[3]] = 15 474 | 475 | type xx: dataframe { 476 | name: int 477 | } 478 | 479 | let df: xx = xx(name = 1) 480 | 481 | # shoudl fail 482 | x$sth 483 | 484 | # should fail, wrong type 485 | df$name = "hello" 486 | 487 | # should fail, not generic 488 | func (p: any) meth(): null {} 489 | ` 490 | 491 | l := lexer.NewTest(code) 492 | 493 | l.Run() 494 | p := parser.New(l) 495 | 496 | prog := p.Run() 497 | 498 | w := New() 499 | w.Run(prog) 500 | 501 | expected := diagnostics.Diagnostics{ 502 | {Severity: diagnostics.Fatal}, 503 | {Severity: diagnostics.Fatal}, 504 | {Severity: diagnostics.Fatal}, 505 | {Severity: diagnostics.Fatal}, 506 | } 507 | 508 | w.testDiagnostics(t, expected) 509 | } 510 | 511 | func TestBasic(t *testing.T) { 512 | code := `let x: int = 1 513 | 514 | x = 2 515 | 516 | # should fail, it's already declared 517 | let x: char = "hello" 518 | 519 | type ids: struct { 520 | int, 521 | name: char 522 | } 523 | 524 | # should fail, already defined 525 | type id: int 526 | 527 | # should fail, cannot find type 528 | let z: undefinedType = "hello" 529 | 530 | # should fail, different types 531 | let v: int = (10, "hello", NA) 532 | 533 | # should fail, type mismatch 534 | let wrongType: num = "hello" 535 | 536 | if(xx == 1) { 537 | let x: int = 2 538 | } 539 | 540 | # should fail wrong types 541 | let x: int = "hello" + 2 542 | 543 | # should fail, x is int, expression coerces to num 544 | x = 1 + 1.2 545 | 546 | let Z: num = 1.2 + 3 547 | 548 | # should fail, does not exist 549 | x = 2 550 | 551 | # should fail, does not exist 552 | uu = "char" 553 | ` 554 | 555 | l := lexer.NewTest(code) 556 | 557 | l.Run() 558 | p := parser.New(l) 559 | 560 | prog := p.Run() 561 | 562 | if p.HasError() { 563 | p.Errors().Print() 564 | return 565 | } 566 | 567 | w := New() 568 | 569 | w.Run(prog) 570 | 571 | expected := diagnostics.Diagnostics{ 572 | {Severity: diagnostics.Fatal}, 573 | {Severity: diagnostics.Fatal}, 574 | {Severity: diagnostics.Fatal}, 575 | {Severity: diagnostics.Fatal}, 576 | {Severity: diagnostics.Fatal}, 577 | {Severity: diagnostics.Warn}, 578 | {Severity: diagnostics.Fatal}, 579 | {Severity: diagnostics.Fatal}, 580 | {Severity: diagnostics.Fatal}, 581 | {Severity: diagnostics.Warn}, 582 | {Severity: diagnostics.Info}, 583 | {Severity: diagnostics.Info}, 584 | } 585 | 586 | w.testDiagnostics(t, expected) 587 | } 588 | 589 | func TestRecursiveTypes(t *testing.T) { 590 | code := ` 591 | type userid: int 592 | 593 | type user: struct { 594 | userid, 595 | name: char 596 | } 597 | 598 | # should warn, might be missing 599 | func create(id: userid): user { 600 | return user(id) 601 | } 602 | 603 | create(2) 604 | 605 | type person: struct { 606 | char, 607 | name: string 608 | } 609 | 610 | # should fail, wrong type 611 | person(2) 612 | ` 613 | 614 | l := lexer.NewTest(code) 615 | 616 | l.Run() 617 | p := parser.New(l) 618 | 619 | prog := p.Run() 620 | 621 | w := New() 622 | 623 | w.Run(prog) 624 | 625 | expected := diagnostics.Diagnostics{ 626 | {Severity: diagnostics.Warn}, 627 | {Severity: diagnostics.Fatal}, 628 | } 629 | 630 | w.testDiagnostics(t, expected) 631 | } 632 | 633 | func TestListTypes(t *testing.T) { 634 | code := ` 635 | type userid: int 636 | 637 | type user: object { 638 | name: char 639 | } 640 | 641 | type users: []user 642 | 643 | #should fail, wrong type 644 | let z: users = users( 645 | user(name = "john"), 646 | 4 647 | ) 648 | 649 | # should fail, named 650 | let w: users = users( 651 | user(name = "john"), 652 | x = user(name = "john"), 653 | ) 654 | ` 655 | 656 | l := lexer.NewTest(code) 657 | 658 | l.Run() 659 | p := parser.New(l) 660 | 661 | prog := p.Run() 662 | 663 | w := New() 664 | 665 | w.Run(prog) 666 | 667 | expected := diagnostics.Diagnostics{ 668 | {Severity: diagnostics.Fatal}, 669 | {Severity: diagnostics.Fatal}, 670 | {Severity: diagnostics.Info}, 671 | } 672 | 673 | w.testDiagnostics(t, expected) 674 | } 675 | 676 | func TestFor(t *testing.T) { 677 | code := ` 678 | type userid: int 679 | 680 | let x: userid = 10 681 | 682 | for(let i: int in 1..x) { 683 | print(i) 684 | } 685 | 686 | type x: struct { 687 | int 688 | } 689 | 690 | let y: x = x(2) 691 | 692 | # should fail, range has char..int 693 | for(let i: int in y) { 694 | print(i) 695 | } 696 | ` 697 | 698 | l := lexer.NewTest(code) 699 | 700 | l.Run() 701 | p := parser.New(l) 702 | 703 | prog := p.Run() 704 | 705 | w := New() 706 | 707 | w.Run(prog) 708 | 709 | expected := diagnostics.Diagnostics{ 710 | {Severity: diagnostics.Fatal}, 711 | } 712 | 713 | w.testDiagnostics(t, expected) 714 | } 715 | 716 | func TestUnused(t *testing.T) { 717 | code := ` 718 | # should warn, x is never used 719 | func foo(x: int): int { 720 | return 1 721 | } 722 | 723 | # should warn, does not exist 724 | print(y) 725 | 726 | # should warn, might be missing 727 | func bar(x: int): int { 728 | return x 729 | } 730 | 731 | func baz(x: int): int { 732 | stopifnot(!missing(x)) 733 | return x 734 | } 735 | ` 736 | 737 | l := lexer.NewTest(code) 738 | 739 | l.Run() 740 | p := parser.New(l) 741 | 742 | prog := p.Run() 743 | 744 | w := New() 745 | 746 | w.Run(prog) 747 | 748 | expected := diagnostics.Diagnostics{ 749 | {Severity: diagnostics.Info}, 750 | {Severity: diagnostics.Warn}, 751 | {Severity: diagnostics.Warn}, 752 | } 753 | 754 | w.testDiagnostics(t, expected) 755 | } 756 | 757 | func TestIncrement(t *testing.T) { 758 | code := ` 759 | let x: int = 1 760 | 761 | x += 1 762 | 763 | # should fail 764 | x += "aaah" 765 | ` 766 | 767 | l := lexer.NewTest(code) 768 | 769 | l.Run() 770 | p := parser.New(l) 771 | 772 | prog := p.Run() 773 | 774 | w := New() 775 | 776 | w.Run(prog) 777 | 778 | expected := diagnostics.Diagnostics{ 779 | {Severity: diagnostics.Fatal}, 780 | } 781 | 782 | w.testDiagnostics(t, expected) 783 | } 784 | 785 | func TestReal(t *testing.T) { 786 | code := ` 787 | type lst: list { 788 | int | num 789 | } 790 | 791 | # should fail, multiple types 792 | type mat: matrix { 793 | int | num 794 | } 795 | 796 | # should fail, wrong arg name 797 | @matrix(nrow = 2, ncol = 4, wrong = true) 798 | type matty: matrix { 799 | int 800 | } 801 | ` 802 | 803 | l := lexer.NewTest(code) 804 | 805 | l.Run() 806 | p := parser.New(l) 807 | 808 | prog := p.Run() 809 | 810 | w := New() 811 | 812 | w.Run(prog) 813 | 814 | expected := diagnostics.Diagnostics{ 815 | {Severity: diagnostics.Fatal}, 816 | {Severity: diagnostics.Fatal}, 817 | {Severity: diagnostics.Info}, 818 | {Severity: diagnostics.Info}, 819 | } 820 | 821 | w.testDiagnostics(t, expected) 822 | } 823 | 824 | func TestFactor(t *testing.T) { 825 | code := ` 826 | # should error, wrong arg 827 | @factor(wrong = TRUE) 828 | type fac: factor { 829 | int 830 | } 831 | 832 | # should error, multiple types 833 | type fct: factor { 834 | int | num 835 | } 836 | ` 837 | 838 | l := lexer.NewTest(code) 839 | 840 | l.Run() 841 | p := parser.New(l) 842 | 843 | prog := p.Run() 844 | 845 | w := New() 846 | 847 | w.Run(prog) 848 | 849 | expected := diagnostics.Diagnostics{ 850 | {Severity: diagnostics.Fatal}, 851 | {Severity: diagnostics.Fatal}, 852 | {Severity: diagnostics.Info}, 853 | } 854 | 855 | w.testDiagnostics(t, expected) 856 | } 857 | 858 | func TestSignature(t *testing.T) { 859 | code := ` 860 | type math: func(int, int) int 861 | 862 | func apply_math(x: int = 2, y: int = 2, cb: math): int { 863 | return cb(x, y) 864 | } 865 | 866 | func multiply(x: int = 2, y: int = 1): int { 867 | return x * y 868 | } 869 | 870 | apply_math(1, 2, multiply) 871 | 872 | apply_math(1, 2, (x: int, y: int): int => { 873 | return x + y 874 | }) 875 | 876 | # should fail, wrong signatures 877 | apply_math(1, 2, (x: int): int => { 878 | return x + 1 879 | }) 880 | 881 | func zz(x: int = 2): num { 882 | return x + 2.1 883 | } 884 | 885 | apply_math(1, 2, zz) 886 | 887 | apply_math(1, 2, (x: int, y: char): int => { 888 | return x + 1 889 | }) 890 | 891 | func foo(x: int): math { 892 | return zz 893 | } 894 | ` 895 | 896 | l := lexer.NewTest(code) 897 | 898 | l.Run() 899 | p := parser.New(l) 900 | 901 | prog := p.Run() 902 | 903 | w := New() 904 | 905 | w.Run(prog) 906 | 907 | expected := diagnostics.Diagnostics{ 908 | {Severity: diagnostics.Fatal}, 909 | {Severity: diagnostics.Fatal}, 910 | {Severity: diagnostics.Info}, 911 | {Severity: diagnostics.Fatal}, 912 | {Severity: diagnostics.Info}, 913 | } 914 | 915 | w.testDiagnostics(t, expected) 916 | } 917 | 918 | func TestMethodCall(t *testing.T) { 919 | code := ` 920 | type rules: object { 921 | selector: char, 922 | rule: char 923 | } 924 | 925 | type linne: object { 926 | css: char, 927 | rules: []rules 928 | } 929 | 930 | #' @export 931 | func create(): linne { 932 | return linne() 933 | } 934 | 935 | #' @export 936 | func(l: linne) addRule(selector: char, ...: char): linne { 937 | l$rules <- append(l$rules, rule(selector = selector, rule = "")) 938 | return l 939 | } 940 | 941 | # error, already defined 942 | func(l: linne) addRule(): linne { 943 | return l 944 | } 945 | 946 | # error, wrong type 947 | addRule("wrongType", "hello") 948 | 949 | # error, no args 950 | addRule() 951 | 952 | let l: linne = create() 953 | addRule(l, "hello") 954 | 955 | # should fail, method on any 956 | func(l: any) addRule(): null {} 957 | ` 958 | 959 | l := lexer.NewTest(code) 960 | 961 | l.Run() 962 | p := parser.New(l) 963 | 964 | prog := p.Run() 965 | 966 | w := New() 967 | 968 | w.Run(prog) 969 | 970 | expected := diagnostics.Diagnostics{ 971 | {Severity: diagnostics.Warn}, 972 | {Severity: diagnostics.Warn}, 973 | {Severity: diagnostics.Info}, 974 | {Severity: diagnostics.Fatal}, 975 | {Severity: diagnostics.Fatal}, 976 | {Severity: diagnostics.Fatal}, 977 | {Severity: diagnostics.Info}, 978 | } 979 | 980 | w.testDiagnostics(t, expected) 981 | } 982 | 983 | func TestTypeInfix(t *testing.T) { 984 | code := ` 985 | let x: int = 10 986 | 987 | # cannot use $ on int 988 | # wrong unknow on x 989 | x$wrong = 2 990 | 991 | type person: object { 992 | name: char 993 | } 994 | 995 | let p: person = person() 996 | 997 | # should fail, wrong type 998 | p$name = 2 999 | ` 1000 | 1001 | l := lexer.NewTest(code) 1002 | 1003 | l.Run() 1004 | p := parser.New(l) 1005 | 1006 | prog := p.Run() 1007 | 1008 | w := New() 1009 | 1010 | w.Run(prog) 1011 | 1012 | expected := diagnostics.Diagnostics{ 1013 | {Severity: diagnostics.Fatal}, 1014 | {Severity: diagnostics.Fatal}, 1015 | {Severity: diagnostics.Fatal}, 1016 | } 1017 | 1018 | w.testDiagnostics(t, expected) 1019 | } 1020 | 1021 | func TestType(t *testing.T) { 1022 | code := ` 1023 | # should fail, duplicated attribute 1024 | type person: object { 1025 | name: char, 1026 | name: int 1027 | } 1028 | 1029 | # should fail, multiple types 1030 | type fct: factor { num | int } 1031 | ` 1032 | 1033 | l := lexer.NewTest(code) 1034 | 1035 | l.Run() 1036 | p := parser.New(l) 1037 | 1038 | prog := p.Run() 1039 | 1040 | w := New() 1041 | 1042 | w.Run(prog) 1043 | 1044 | expected := diagnostics.Diagnostics{ 1045 | {Severity: diagnostics.Fatal}, 1046 | {Severity: diagnostics.Fatal}, 1047 | {Severity: diagnostics.Info}, 1048 | } 1049 | 1050 | w.testDiagnostics(t, expected) 1051 | } 1052 | 1053 | func TestDate(t *testing.T) { 1054 | code := ` 1055 | type dt: date = as.Date("2022-02-01") 1056 | 1057 | let x: dt = date("2022-02-01") 1058 | 1059 | type person: object { 1060 | dob: date 1061 | } 1062 | 1063 | # should fail wrong type 1064 | let p: person = person(dob = 2) 1065 | 1066 | # should fail wrong type 1067 | p$dob = 1 1068 | ` 1069 | 1070 | l := lexer.NewTest(code) 1071 | 1072 | l.Run() 1073 | p := parser.New(l) 1074 | 1075 | prog := p.Run() 1076 | 1077 | w := New() 1078 | 1079 | w.Run(prog) 1080 | 1081 | expected := diagnostics.Diagnostics{ 1082 | {Severity: diagnostics.Fatal}, 1083 | {Severity: diagnostics.Fatal}, 1084 | } 1085 | 1086 | w.testDiagnostics(t, expected) 1087 | } 1088 | 1089 | func TestReturn(t *testing.T) { 1090 | code := ` 1091 | # should fail, missing return 1092 | func foo(x: int): int {} 1093 | 1094 | func (x: int) foo(y: char): null {} 1095 | 1096 | # should fail, wrong return type 1097 | func bar(x: int): int { 1098 | return "hello" 1099 | } 1100 | 1101 | func baz(): any {} 1102 | ` 1103 | 1104 | l := lexer.NewTest(code) 1105 | 1106 | l.Run() 1107 | p := parser.New(l) 1108 | 1109 | prog := p.Run() 1110 | 1111 | w := New() 1112 | 1113 | w.Run(prog) 1114 | 1115 | expected := diagnostics.Diagnostics{ 1116 | {Severity: diagnostics.Fatal}, 1117 | {Severity: diagnostics.Info}, 1118 | {Severity: diagnostics.Info}, 1119 | {Severity: diagnostics.Fatal}, 1120 | {Severity: diagnostics.Info}, 1121 | } 1122 | 1123 | w.testDiagnostics(t, expected) 1124 | } 1125 | 1126 | func TestDecorators(t *testing.T) { 1127 | code := ` 1128 | @generic 1129 | func (p: person) foo(x: int): any 1130 | 1131 | # fail already defined 1132 | @generic 1133 | func (p: any) foo(x: int): any 1134 | 1135 | @generic 1136 | func (p: any) bar(x: int): person 1137 | ` 1138 | 1139 | l := lexer.NewTest(code) 1140 | 1141 | l.Run() 1142 | p := parser.New(l) 1143 | 1144 | prog := p.Run() 1145 | 1146 | w := New() 1147 | 1148 | w.Run(prog) 1149 | 1150 | expected := diagnostics.Diagnostics{ 1151 | {Severity: diagnostics.Fatal}, 1152 | } 1153 | 1154 | w.testDiagnostics(t, expected) 1155 | } 1156 | 1157 | func TestLibrary(t *testing.T) { 1158 | code := ` 1159 | library(something) 1160 | 1161 | require(package) 1162 | ` 1163 | 1164 | l := lexer.NewTest(code) 1165 | 1166 | l.Run() 1167 | p := parser.New(l) 1168 | 1169 | prog := p.Run() 1170 | 1171 | w := New() 1172 | 1173 | w.Run(prog) 1174 | 1175 | expected := diagnostics.Diagnostics{ 1176 | {Severity: diagnostics.Hint}, 1177 | {Severity: diagnostics.Hint}, 1178 | } 1179 | 1180 | w.testDiagnostics(t, expected) 1181 | } 1182 | 1183 | func TestAccessor(t *testing.T) { 1184 | code := ` 1185 | let x: any = 1 1186 | 1187 | # should WORK 1188 | x["hello"] 1189 | x[["hello"]] 1190 | x$hello 1191 | print(x$hello) 1192 | 1193 | let globals: any = new.env(env = parent.env(), hash = TRUE) 1194 | 1195 | let y: int = (1 ,2 ,3) 1196 | y = y[3] 1197 | ` 1198 | 1199 | l := lexer.NewTest(code) 1200 | 1201 | l.Run() 1202 | p := parser.New(l) 1203 | 1204 | prog := p.Run() 1205 | 1206 | w := New() 1207 | 1208 | w.Run(prog) 1209 | 1210 | expected := diagnostics.Diagnostics{} 1211 | 1212 | w.testDiagnostics(t, expected) 1213 | } 1214 | 1215 | func TestEnvironment(t *testing.T) { 1216 | code := ` 1217 | let z: int = 1 1218 | 1219 | func addz(n: int = 1, y: int = 2): int { 1220 | if(n == 1){ 1221 | return NA 1222 | } 1223 | 1224 | return n + y + z 1225 | } 1226 | 1227 | # should fail, comparing wrong types 1228 | if (1 == "hello") { 1229 | print("1") 1230 | } 1231 | 1232 | if (1 > 2.1) { 1233 | print("1") 1234 | } 1235 | 1236 | const c: int = 1 1237 | 1238 | # should fail, is constant 1239 | c = 2 1240 | 1241 | print(c) 1242 | ` 1243 | 1244 | l := lexer.NewTest(code) 1245 | 1246 | l.Run() 1247 | p := parser.New(l) 1248 | 1249 | prog := p.Run() 1250 | 1251 | w := New() 1252 | 1253 | w.Run(prog) 1254 | 1255 | expected := diagnostics.Diagnostics{ 1256 | {Severity: diagnostics.Info}, 1257 | {Severity: diagnostics.Fatal}, 1258 | } 1259 | 1260 | w.testDiagnostics(t, expected) 1261 | } 1262 | 1263 | func TestTypeImport(t *testing.T) { 1264 | code := ` 1265 | let x: vape::user = vape::user(id = 1) 1266 | 1267 | let y: vape::user = vape::user(id = "char") 1268 | let w: vape::user = vape::user(wrong = 2) 1269 | 1270 | type custom: object { 1271 | user: vape::user 1272 | } 1273 | ` 1274 | 1275 | l := lexer.NewTest(code) 1276 | 1277 | l.Run() 1278 | p := parser.New(l) 1279 | 1280 | prog := p.Run() 1281 | 1282 | environment.SetLibrary(r.LibPath()) 1283 | w := New() 1284 | 1285 | w.Run(prog) 1286 | 1287 | expected := diagnostics.Diagnostics{ 1288 | {Severity: diagnostics.Fatal}, 1289 | {Severity: diagnostics.Fatal}, 1290 | {Severity: diagnostics.Info}, 1291 | } 1292 | 1293 | w.testDiagnostics(t, expected) 1294 | } 1295 | 1296 | func TestTypeNA(t *testing.T) { 1297 | code := ` 1298 | let x: int = 2 1299 | 1300 | x = NA 1301 | ` 1302 | 1303 | l := lexer.NewTest(code) 1304 | 1305 | l.Run() 1306 | p := parser.New(l) 1307 | 1308 | prog := p.Run() 1309 | 1310 | environment.SetLibrary(r.LibPath()) 1311 | w := New() 1312 | 1313 | w.Run(prog) 1314 | 1315 | expected := diagnostics.Diagnostics{} 1316 | 1317 | w.testDiagnostics(t, expected) 1318 | } 1319 | 1320 | func TestTypeEnvironment(t *testing.T) { 1321 | code := ` 1322 | type e: environment { 1323 | name: char, 1324 | x: int 1325 | } 1326 | 1327 | e(name = "hello") 1328 | 1329 | # should fail, unknown attribute 1330 | e(z = 2) 1331 | 1332 | # should fail, wrong type 1333 | e(x = true) 1334 | ` 1335 | 1336 | l := lexer.NewTest(code) 1337 | 1338 | l.Run() 1339 | p := parser.New(l) 1340 | 1341 | prog := p.Run() 1342 | 1343 | environment.SetLibrary(r.LibPath()) 1344 | w := New() 1345 | 1346 | w.Run(prog) 1347 | 1348 | expected := diagnostics.Diagnostics{ 1349 | {Severity: diagnostics.Fatal}, 1350 | {Severity: diagnostics.Fatal}, 1351 | } 1352 | 1353 | w.testDiagnostics(t, expected) 1354 | } 1355 | 1356 | func TestSymbols(t *testing.T) { 1357 | code := ` 1358 | # should fail, type does not exist 1359 | let foo: bar = baz 1360 | 1361 | # should fail, baz not found 1362 | foo = baz 1363 | ` 1364 | 1365 | l := lexer.NewTest(code) 1366 | 1367 | l.Run() 1368 | p := parser.New(l) 1369 | 1370 | prog := p.Run() 1371 | 1372 | environment.SetLibrary(r.LibPath()) 1373 | w := New() 1374 | 1375 | w.Run(prog) 1376 | 1377 | expected := diagnostics.Diagnostics{ 1378 | {Severity: diagnostics.Fatal}, 1379 | {Severity: diagnostics.Warn}, 1380 | } 1381 | 1382 | w.testDiagnostics(t, expected) 1383 | } 1384 | --------------------------------------------------------------------------------