Skip to content

Commit

Permalink
feat: describe schemas in the DSL (#78)
Browse files Browse the repository at this point in the history
* feat: Describe schemas in the DSL (#13)

* style: apply automatic fixes of linters

Co-authored-by: MShahzaib <MShahzaib@users.noreply.github.com>
Co-authored-by: Lars Reimann <mail@larsreimann.com>
  • Loading branch information
3 people authored Jun 29, 2022
1 parent a6a5c5d commit 82b1b41
Show file tree
Hide file tree
Showing 30 changed files with 331 additions and 103 deletions.
2 changes: 1 addition & 1 deletion DSL/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ tasks.register("generateXtextLanguage") {

standardLanguage {
setName("com.larsreimann.safeds.SafeDS")
setFileExtensions("sdsflow,sdsstub,sdstest")
setFileExtensions("sdsflow,sdsschema,sdsstub,sdstest")
addReferencedResource("platform:/resource/com.larsreimann.safeds/model/SafeDS.genmodel")

setFormatter(
Expand Down
1 change: 1 addition & 0 deletions DSL/com.larsreimann.safeds.vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
],
"extensions": [
".sdsflow",
".sdsschema",
".sdsstub",
".sdstest"
],
Expand Down
13 changes: 12 additions & 1 deletion DSL/com.larsreimann.safeds/model/SafeDS.ecore
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<eStructuralFeatures xsi:type="ecore:EReference" name="type" eType="#//SdsAbstractType" containment="true" />
</eClassifiers>
<!-- Class -->
<eClassifiers xsi:type="ecore:EClass" name="SdsClass" abstract="false" eSuperTypes="#//SdsAbstractCallable #//SdsAbstractClassMember #//SdsAbstractCompilationUnitMember #//SdsAbstractNamedTypeDeclaration">
<eClassifiers xsi:type="ecore:EClass" name="SdsClass" eSuperTypes="#//SdsAbstractCallable #//SdsAbstractClassMember #//SdsAbstractCompilationUnitMember #//SdsAbstractNamedTypeDeclaration">
<eStructuralFeatures xsi:type="ecore:EReference" name="typeParameterList" eType="#//SdsTypeParameterList" containment="true" />
<eStructuralFeatures xsi:type="ecore:EReference" name="parentTypeList" eType="#//SdsParentTypeList" containment="true" />
<eStructuralFeatures xsi:type="ecore:EReference" name="body" eType="#//SdsClassBody" containment="true" />
Expand Down Expand Up @@ -183,6 +183,17 @@
<eClassifiers xsi:type="ecore:EClass" name="SdsFunctionBody" eSuperTypes="#//SdsAbstractObject">
<eStructuralFeatures xsi:type="ecore:EReference" name="statements" upperBound="-1" eType="#//SdsAbstractObject" containment="true" />
</eClassifiers>
<!-- Schema -->
<eClassifiers xsi:type="ecore:EClass" name="SdsSchema" eSuperTypes="#//SdsAbstractCompilationUnitMember">
<eStructuralFeatures xsi:type="ecore:EReference" name="columnList" eType="#//SdsColumnList" containment="true" />
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="SdsColumnList" eSuperTypes="#//SdsAbstractObject">
<eStructuralFeatures xsi:type="ecore:EReference" name="columns" upperBound="-1" eType="#//SdsColumn" containment="true" />
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="SdsColumn" eSuperTypes="#//SdsAbstractObject">
<eStructuralFeatures xsi:type="ecore:EReference" name="columnName" eType="#//SdsString" containment="true" />
<eStructuralFeatures xsi:type="ecore:EReference" name="columnType" eType="#//SdsAbstractType" containment="true" />
</eClassifiers>
<!-- Step -->
<eClassifiers xsi:type="ecore:EClass" name="SdsStep" eSuperTypes="#//SdsAbstractCallable #//SdsAbstractCompilationUnitMember">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="visibility" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" />
Expand Down
11 changes: 11 additions & 0 deletions DSL/com.larsreimann.safeds/model/SafeDS.genmodel
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,17 @@
<genClasses ecoreClass="SafeDS.ecore#//SdsFunctionBody">
<genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference SafeDS.ecore#//SdsFunctionBody/statements" />
</genClasses>
<!-- Schema -->
<genClasses ecoreClass="SafeDS.ecore#//SdsSchema">
<genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference SafeDS.ecore#//SdsSchema/columnList" />
</genClasses>
<genClasses ecoreClass="SafeDS.ecore#//SdsColumnList">
<genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference SafeDS.ecore#//SdsColumnList/columns" />
</genClasses>
<genClasses ecoreClass="SafeDS.ecore#//SdsColumn">
<genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference SafeDS.ecore#//SdsColumn/columnName" />
<genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference SafeDS.ecore#//SdsColumn/columnType" />
</genClasses>
<!-- Step -->
<genClasses ecoreClass="SafeDS.ecore#//SdsStep">
<genFeatures createChild="false" ecoreFeature="ecore:EAttribute SafeDS.ecore#//SdsStep/visibility" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ SdsCompilationUnitMember returns SdsAbstractAnnotatedObject
resultList=SdsResultList?
body=SdsFunctionBody?

| {SdsPredicate.annotationCallList=current}
'predicate' name=ID
parameterList=SdsParameterList
resultList=SdsResultList?
goalList=SdsGoalList

| {SdsSchema.annotationCallList=current}
'schema' name=ID
columnList=SdsColumnList

| {SdsStep.annotationCallList=current}
visibility=('internal'|'private')?
'step' name=ID
Expand All @@ -59,12 +69,6 @@ SdsCompilationUnitMember returns SdsAbstractAnnotatedObject
| {SdsWorkflow.annotationCallList=current}
'workflow' name=ID
body=SdsBlock

| {SdsPredicate.annotationCallList=current}
'predicate' name=ID
parameterList=SdsParameterList
resultList=SdsResultList?
goalList=SdsGoalList
)
;

Expand Down Expand Up @@ -516,7 +520,7 @@ SdsTemplateStringEnd


/**********************************************************************************************************************
* Predicates
* Predicates / Goals
**********************************************************************************************************************/

SdsGoalList
Expand Down Expand Up @@ -576,6 +580,19 @@ SdsParenthesizedGoalExpression
;


/**********************************************************************************************************************
* Schemas
**********************************************************************************************************************/

SdsColumnList
: {SdsColumnList} '{' ( columns+=SdsColumn (',' columns+=SdsColumn)* ','?)? '}'
;

SdsColumn
: columnName=SdsString ":" columnType=SdsType
;


/**********************************************************************************************************************
* Names
**********************************************************************************************************************/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ enum class SdsFileExtension(val extension: String) {
*/
Flow("sdsflow"),

/**
* Marks the file as a schema file.
*
* @see isInSchemaFile
* @see isSchemaFile
*/
Schema("sdsschema"),

/**
* Marks the file as a stub file, which describes an external API.
*
Expand Down Expand Up @@ -45,6 +53,11 @@ enum class SdsFileExtension(val extension: String) {
*/
fun EObject.isInFlowFile() = this.eResource().isFlowFile()

/**
* Returns whether the object is contained in schema file.
*/
fun EObject.isInSchemaFile() = this.eResource().isSchemaFile()

/**
* Returns whether the object is contained in stub file.
*/
Expand All @@ -60,6 +73,11 @@ fun EObject.isInTestFile() = this.eResource().isTestFile()
*/
fun Resource.isFlowFile() = this.hasExtension(SdsFileExtension.Flow)

/**
* Returns whether the resource represents a schema file.
*/
fun Resource.isSchemaFile() = this.hasExtension(SdsFileExtension.Schema)

/**
* Returns whether the resource represents a stub file.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import com.larsreimann.safeds.safeDS.SdsBoolean
import com.larsreimann.safeds.safeDS.SdsCall
import com.larsreimann.safeds.safeDS.SdsCallableType
import com.larsreimann.safeds.safeDS.SdsClass
import com.larsreimann.safeds.safeDS.SdsColumn
import com.larsreimann.safeds.safeDS.SdsCompilationUnit
import com.larsreimann.safeds.safeDS.SdsConstraint
import com.larsreimann.safeds.safeDS.SdsEnum
Expand Down Expand Up @@ -90,6 +91,7 @@ import com.larsreimann.safeds.safeDS.SdsProtocolTokenClass
import com.larsreimann.safeds.safeDS.SdsReference
import com.larsreimann.safeds.safeDS.SdsResult
import com.larsreimann.safeds.safeDS.SdsResultList
import com.larsreimann.safeds.safeDS.SdsSchema
import com.larsreimann.safeds.safeDS.SdsStarProjection
import com.larsreimann.safeds.safeDS.SdsStep
import com.larsreimann.safeds.safeDS.SdsString
Expand Down Expand Up @@ -548,6 +550,19 @@ fun createSdsConstraint(goals: List<SdsAbstractConstraintGoal>): SdsConstraint {
}
}

/**
* Returns a new object of class [SdsColumn].
*/
fun createSdsColumn(
columnName: SdsString,
columnType: SdsAbstractType
): SdsColumn {
return factory.createSdsColumn().apply {
this.columnName = columnName
this.columnType = columnType
}
}

/**
* Returns a new object of class [SdsEnum].
*/
Expand Down Expand Up @@ -1295,6 +1310,48 @@ fun createSdsStarProjection(): SdsStarProjection {
return factory.createSdsStarProjection()
}

/**
* Returns a new object of class [SdsSchema].
*/
fun createSdsSchema(
name: String,
annotationCalls: List<SdsAnnotationCall> = emptyList(),
columns: List<SdsColumn> = emptyList()
): SdsSchema {
return factory.createSdsSchema().apply {
this.name = name
this.annotationCallList = createSdsAnnotationCallList(annotationCalls)
columns.forEach { addColumn(it) }
}
}

/**
* Adds a new object of class [SdsSchema] to the receiver.
*/
fun SdsCompilationUnit.sdsSchema(
name: String,
annotationCalls: List<SdsAnnotationCall> = emptyList(),
columns: List<SdsColumn> = emptyList()
) {
this.addMember(
createSdsSchema(
name,
annotationCalls,
columns
)
)
}

/**
* Adds a new column to the receiver.
*/
private fun SdsSchema.addColumn(column: SdsColumn) {
if (this.columnList == null) {
this.columnList = factory.createSdsColumnList()
}
this.columnList.columns += column
}

/**
* Returns a new object of class [SdsStep].
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import com.larsreimann.safeds.safeDS.SdsCall
import com.larsreimann.safeds.safeDS.SdsCallableType
import com.larsreimann.safeds.safeDS.SdsClass
import com.larsreimann.safeds.safeDS.SdsClassBody
import com.larsreimann.safeds.safeDS.SdsColumn
import com.larsreimann.safeds.safeDS.SdsColumnList
import com.larsreimann.safeds.safeDS.SdsCompilationUnit
import com.larsreimann.safeds.safeDS.SdsConstraint
import com.larsreimann.safeds.safeDS.SdsEnum
Expand Down Expand Up @@ -80,6 +82,7 @@ import com.larsreimann.safeds.safeDS.SdsProtocolSubterm
import com.larsreimann.safeds.safeDS.SdsProtocolSubtermList
import com.larsreimann.safeds.safeDS.SdsResult
import com.larsreimann.safeds.safeDS.SdsResultList
import com.larsreimann.safeds.safeDS.SdsSchema
import com.larsreimann.safeds.safeDS.SdsStep
import com.larsreimann.safeds.safeDS.SdsTemplateString
import com.larsreimann.safeds.safeDS.SdsTypeArgument
Expand Down Expand Up @@ -901,6 +904,67 @@ class SafeDSFormatter : AbstractFormatter2() {
}
}

/**********************************************************************************************************
* Schema
**********************************************************************************************************/

is SdsSchema -> {

// Features "annotations"
doc.formatAnnotations(obj)

// Keyword "schema"
if (obj.annotationCallsOrEmpty().isEmpty()) {
doc.formatKeyword(obj, "schema", noSpace, oneSpace)
} else {
doc.formatKeyword(obj, "schema", oneSpace, oneSpace)
}

// Feature "name"
doc.formatFeature(obj, SDS_ABSTRACT_DECLARATION__NAME, null, oneSpace)

// EObject "columnList"
doc.formatObject(obj.columnList, oneSpace, null)
}
is SdsColumnList -> {

// Keyword "{"
val openingBrace = obj.regionForKeyword("{")
if (obj.columns.isEmpty()) {
doc.append(openingBrace, noSpace)
} else {
doc.append(openingBrace, newLine)
}

// Feature "columns"
obj.columns.forEach {
doc.formatObject(it, newLine, noSpace)
}

// Keywords ","
doc.formatKeyword(obj, ",", noSpace, newLine)

// Keyword "}"
val closingBrace = obj.regionForKeyword("}")
if (obj.columns.isEmpty()) {
doc.prepend(closingBrace, noSpace)
} else {
doc.prepend(closingBrace, newLine)
}
doc.interior(openingBrace, closingBrace, indent)
}
is SdsColumn -> {

// EObject "columnName"
doc.formatObject(obj.columnName, null, oneSpace)

// Keyword ":"
doc.formatKeyword(obj, ":", oneSpace, oneSpace)

// EObject "columnType"
doc.formatObject(obj.columnType)
}

/**********************************************************************************************************
* Statements
**********************************************************************************************************/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ enum class ErrorCode {
REDECLARATION,

FileMustDeclarePackage,
StubFileMustNotDeclareWorkflowsOrSteps,
StubFileMustNotDeclareWorkflowsSchemasOrSteps,
WorkflowFileMustOnlyDeclareWorkflowsAndSteps,
SchemaFileMustOnlyDeclareSchemas,

ANNOTATION_IS_SINGLE_USE,
DEPRECATED_REQUIRED_PARAMETER,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.larsreimann.safeds.validation.declarations

import com.larsreimann.safeds.constant.isInFlowFile
import com.larsreimann.safeds.constant.isInSchemaFile
import com.larsreimann.safeds.constant.isInStubFile
import com.larsreimann.safeds.constant.isInTestFile
import com.larsreimann.safeds.emf.compilationUnitMembersOrEmpty
Expand All @@ -10,6 +12,7 @@ import com.larsreimann.safeds.safeDS.SafeDSPackage.Literals
import com.larsreimann.safeds.safeDS.SdsAbstractDeclaration
import com.larsreimann.safeds.safeDS.SdsCompilationUnit
import com.larsreimann.safeds.safeDS.SdsImport
import com.larsreimann.safeds.safeDS.SdsSchema
import com.larsreimann.safeds.safeDS.SdsStep
import com.larsreimann.safeds.safeDS.SdsWorkflow
import com.larsreimann.safeds.scoping.externalGlobalDeclarations
Expand All @@ -25,16 +28,16 @@ class CompilationUnitChecker : AbstractSafeDSChecker() {
fun members(sdsCompilationUnit: SdsCompilationUnit) {
if (sdsCompilationUnit.isInStubFile()) {
sdsCompilationUnit.compilationUnitMembersOrEmpty()
.filter { it is SdsWorkflow || it is SdsStep }
.filter { it is SdsWorkflow || it is SdsStep || it is SdsSchema }
.forEach {
error(
"A stub file must not declare workflows or steps.",
"A stub file must not declare workflows, schemas or steps.",
it,
Literals.SDS_ABSTRACT_DECLARATION__NAME,
ErrorCode.StubFileMustNotDeclareWorkflowsOrSteps
ErrorCode.StubFileMustNotDeclareWorkflowsSchemasOrSteps
)
}
} else if (!sdsCompilationUnit.isInTestFile()) {
} else if (sdsCompilationUnit.isInFlowFile()) {
sdsCompilationUnit.compilationUnitMembersOrEmpty()
.filter { it !is SdsWorkflow && it !is SdsStep }
.forEach {
Expand All @@ -45,6 +48,17 @@ class CompilationUnitChecker : AbstractSafeDSChecker() {
ErrorCode.WorkflowFileMustOnlyDeclareWorkflowsAndSteps
)
}
} else if (sdsCompilationUnit.isInSchemaFile()) {
sdsCompilationUnit.compilationUnitMembersOrEmpty()
.filter { it !is SdsSchema }
.forEach {
error(
"A schema file must only declare schemas.",
it,
Literals.SDS_ABSTRACT_DECLARATION__NAME,
ErrorCode.SchemaFileMustOnlyDeclareSchemas
)
}
}
}

Expand Down
Loading

0 comments on commit 82b1b41

Please sign in to comment.