Skip to content

Commit 32aca34

Browse files
feat: syntax and formatting for literal types (#529)
Closes partially #80. ### Summary of Changes * Add syntax for literal types: ``` literal<1, 1.0, true, false, null, ""> ``` * Update formatter to handle literal types --------- Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com>
1 parent e204fe9 commit 32aca34

20 files changed

+143
-85
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ coverage/
1313
dist/
1414
dist-ssr/
1515
out/
16+
DSL/syntaxes/safe-ds.tmLanguage.json
1617

1718
# Node
1819
.npm/

DSL/src/language-server/formatting/safe-ds-formatter.ts

+28-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { AbstractFormatter, AstNode, CstNode, findCommentNode, Formatting, isAstNode } from 'langium';
22
import * as ast from '../generated/ast';
33
import { SdsImport, SdsImportAlias, SdsModule } from '../generated/ast';
4-
import { annotationCallsOrEmpty, typeArgumentsOrEmpty } from '../helpers/astShortcuts';
4+
import { annotationCallsOrEmpty, literalsOrEmpty, typeArgumentsOrEmpty } from '../helpers/astShortcuts';
55
import { FormattingAction, FormattingActionOptions } from 'langium/src/lsp/formatter';
66
import noSpace = Formatting.noSpace;
77
import newLine = Formatting.newLine;
@@ -149,6 +149,10 @@ export class SafeDSFormatter extends AbstractFormatter {
149149
this.formatSdsMemberType(node);
150150
} else if (ast.isSdsCallableType(node)) {
151151
this.formatSdsCallableType(node);
152+
} else if (ast.isSdsLiteralType(node)) {
153+
this.formatSdsLiteralType(node);
154+
} else if (ast.isSdsLiteralList(node)) {
155+
this.formatSdsLiteralList(node);
152156
} else if (ast.isSdsNamedType(node)) {
153157
this.formatSdsNamedType(node);
154158
} else if (ast.isSdsUnionType(node)) {
@@ -761,6 +765,25 @@ export class SafeDSFormatter extends AbstractFormatter {
761765
formatter.keyword('->').surround(oneSpace());
762766
}
763767

768+
private formatSdsLiteralType(node: ast.SdsLiteralType): void {
769+
const formatter = this.getNodeFormatter(node);
770+
771+
formatter.keyword('literal').append(noSpace());
772+
}
773+
774+
private formatSdsLiteralList(node: ast.SdsLiteralList): void {
775+
const formatter = this.getNodeFormatter(node);
776+
const literals = node.literals ?? [];
777+
778+
if (literals.length > 0) {
779+
formatter.node(literals[0]).prepend(noSpace());
780+
formatter.nodes(...literals.slice(1)).prepend(oneSpace());
781+
}
782+
783+
formatter.keywords(',').prepend(noSpace());
784+
formatter.keyword('>').prepend(noSpace());
785+
}
786+
764787
private formatSdsNamedType(node: ast.SdsNamedType) {
765788
const formatter = this.getNodeFormatter(node);
766789

@@ -847,10 +870,12 @@ export class SafeDSFormatter extends AbstractFormatter {
847870

848871
if (ast.isSdsCallableType(node) || ast.isSdsMemberType(node)) {
849872
return true;
850-
} else if (ast.isSdsUnionType(node)) {
851-
return typeArgumentsOrEmpty(node.typeArgumentList).length > 0;
873+
} else if (ast.isSdsLiteralType(node)) {
874+
return literalsOrEmpty(node).length > 0;
852875
} else if (ast.isSdsNamedType(node)) {
853876
return typeArgumentsOrEmpty(node.typeArgumentList).length > 0;
877+
} else if (ast.isSdsUnionType(node)) {
878+
return typeArgumentsOrEmpty(node.typeArgumentList).length > 0;
854879
} else {
855880
return false;
856881
}

DSL/src/language-server/grammar/safe-ds.langium

+25
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,7 @@ SdsType returns SdsType:
841841

842842
SdsPrimaryType returns SdsType:
843843
SdsCallableType
844+
| SdsLiteralType
844845
| SdsNamedType
845846
| SdsUnionType
846847
;
@@ -854,6 +855,29 @@ SdsCallableType returns SdsCallableType:
854855
resultList=SdsResultList
855856
;
856857

858+
interface SdsLiteralType extends SdsType {
859+
literalList: SdsLiteralList
860+
}
861+
862+
SdsLiteralType returns SdsLiteralType:
863+
'literal' literalList=SdsLiteralList
864+
;
865+
866+
interface SdsLiteralList extends SdsObject {
867+
literals: SdsLiteral[]
868+
}
869+
870+
SdsLiteralList returns SdsLiteralList:
871+
{SdsLiteralList}
872+
(LESS_THAN | CALL_TYPE_ARGUMENT_LIST_START)
873+
(
874+
literals+=SdsLiteral
875+
(',' literals+=SdsLiteral)*
876+
','?
877+
)?
878+
'>'
879+
;
880+
857881
interface SdsNamedType extends SdsType {
858882
declaration: @SdsNamedTypeDeclaration
859883
typeArgumentList?: SdsTypeArgumentList
@@ -1038,6 +1062,7 @@ terminal CALL_TYPE_ARGUMENT_LIST_START:
10381062
( '*' // Star projection as positional type argument
10391063
| 'in' // Contravariant type projection as positional type argument
10401064
| 'out' // Covariant type projection as positional type argument
1065+
| 'literal' // Invariant literal type as positional type argument
10411066
| 'union' // Invariant union type as positional type argument
10421067
| '>' // Empty type argument list
10431068
| ID /\s*/

DSL/src/language-server/helpers/astShortcuts.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {
33
isSdsDeclaration,
44
SdsAnnotatedObject,
55
SdsAnnotationCall,
6-
SdsClass,
7-
SdsObject,
6+
SdsLiteral,
7+
SdsLiteralType,
88
SdsTypeArgument,
99
SdsTypeArgumentList,
1010
} from '../generated/ast';
@@ -17,8 +17,8 @@ export const annotationCallsOrEmpty = function (node: SdsAnnotatedObject): SdsAn
1717
}
1818
};
1919

20-
export const classMembersOrEmpty = function (node: SdsClass): SdsObject[] {
21-
return node.body?.members ?? [];
20+
export const literalsOrEmpty = function (node: SdsLiteralType | undefined): SdsLiteral[] {
21+
return node?.literalList?.literals ?? [];
2222
};
2323

2424
export const typeArgumentsOrEmpty = function (node: SdsTypeArgumentList | undefined): SdsTypeArgument[] {

DSL/syntaxes/safe-ds.tmLanguage.json

-78
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
segment s(
2+
f: literal<1.0 , null , >
3+
) {}
4+
5+
// -----------------------------------------------------------------------------
6+
7+
segment s(
8+
f: literal<1.0, null,>
9+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
segment mySegment(x: literal < >) {}
2+
3+
// -----------------------------------------------------------------------------
4+
5+
segment mySegment(x: literal<>) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
segment mySegment(
2+
x: literal < 1.0 , null >
3+
) {}
4+
5+
// -----------------------------------------------------------------------------
6+
7+
segment mySegment(
8+
x: literal<1.0, null>
9+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
segment mySegment(
2+
x: literal < > . InnerClass
3+
) {}
4+
5+
// -----------------------------------------------------------------------------
6+
7+
segment mySegment(
8+
x: literal<>.InnerClass
9+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
segment mySegment(
2+
x: literal < > . InnerClass ?
3+
) {}
4+
5+
// -----------------------------------------------------------------------------
6+
7+
segment mySegment(
8+
x: literal<>.InnerClass?
9+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// $TEST$ syntax_error
2+
3+
class literal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// $TEST$ no_syntax_error
2+
3+
segment s(
4+
f: literal<true, false, >
5+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// $TEST$ syntax_error
2+
3+
segment mySegment(
4+
x: literal<literal<>>
5+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// $TEST$ syntax_error
2+
3+
segment mySegment(
4+
x: literal<
5+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// $TEST$ no_syntax_error
2+
3+
segment mySegment(
4+
x: literal<>
5+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// $TEST$ no_syntax_error
2+
3+
segment mySegment(
4+
x: literal<null, true, false, 1, 1.0, "s">
5+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// $TEST$ syntax_error
2+
3+
segment mySegment(
4+
x: OuterClass.literal<>
5+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// $TEST$ no_syntax_error
2+
3+
segment mySegment(
4+
x: literal<>.InnerClass
5+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// $TEST$ no_syntax_error
2+
3+
segment mySegment(
4+
x: literal<>.InnerClass?
5+
) {}

DSL/vitest.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ export default defineConfig({
1010
include: ['src'],
1111
exclude: ['**/generated'],
1212
},
13+
exclude: ['out'],
1314
},
1415
});

0 commit comments

Comments
 (0)