Skip to content

Commit

Permalink
[#60] Attribute type can be implicitly extracted from its name
Browse files Browse the repository at this point in the history
To simplify reading and writing, the attribute type definition can be implicit.
It will be extracted from the attribute name by capitalizing the name.

This commit does the following:

* The antlr definition set the attribute type as optional
* If the attribute type is not defined, set it as the attribute name
* The verifying part remains independent
* Add two tests to control the new behavior
  • Loading branch information
grizio committed Jun 23, 2018
1 parent c8af051 commit f9df82d
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/main/antlr/Definiti.g4
Expand Up @@ -105,7 +105,7 @@ definedType :

attributeDefinition:
DOC_COMMENT?
attributeName=IDENTIFIER ':' typeDeclaration verifyingList;
attributeName=IDENTIFIER (':' typeDeclaration)? verifyingList;

typeVerification
: atomicTypeVerification
Expand Down
Expand Up @@ -25,6 +25,10 @@ private[core] trait CommonParser {
ast.Location(file, getRangeFromContext(context))
}

def getLocationFromToken(token: Token): Location = {
ast.Location(file, getRangeFromToken(token))
}

def getRangeFromContext(context: ParserRuleContext): ast.Range = {
val start = Option(context.getStart)
.map(token => Position(token.getLine, token.getCharPositionInLine + 1))
Expand Down
Expand Up @@ -110,9 +110,13 @@ private[core] class DefinitiFileASTParser(
}

private def processAttributeDefinition(context: AttributeDefinitionContext): AttributeDefinition = {
val attributeName = context.attributeName.getText
val typeDeclaration = Option(context.typeDeclaration)
.map(processTypeDeclaration)
.getOrElse(TypeDeclaration(attributeName.capitalize, Seq.empty, Seq.empty, getLocationFromToken(context.attributeName)))
AttributeDefinition(
name = context.attributeName.getText,
typeDeclaration = processTypeDeclaration(context.typeDeclaration),
name = attributeName,
typeDeclaration = typeDeclaration,
comment = Option(context.DOC_COMMENT()).map(_.getText).map(extractDocComment),
verifications = processVerifyingList(context.verifyingList()),
location = getLocationFromContext(context)
Expand Down
18 changes: 18 additions & 0 deletions src/test/resources/samples/definedType/implicit-attribute-type.def
@@ -0,0 +1,18 @@
type Person {
email verifying IsNonEmptyEmail
address
}

type Email = String

type Address {
city: String
country: String
}

verification IsNonEmptyEmail {
"Please give a non empty email"
(email: Email) => {
email.nonEmpty()
}
}
@@ -0,0 +1,4 @@
type Person {
email
address
}
128 changes: 128 additions & 0 deletions src/test/scala/definiti/core/end2end/DefinedTypeSpec.scala
@@ -0,0 +1,128 @@
package definiti.core.end2end

import definiti.common.ast._
import definiti.common.program.{Ko, Ok}
import definiti.common.tests.LocationPath
import definiti.common.utils.ASTUtils._
import definiti.core.ProgramResultMatchers._
import definiti.core.validation.controls.{AttributeTypeControl, TypeDeclarationParametersControl}

class DefinedTypeSpec extends EndToEndSpec {
import DefinedTypeSpec._

"Project.generatePublicAST" should "generate the AST with a defined type with implicit attribute types" in {
val expected = Ok[Root](implicitAttributeType)
val output = processFile("definedType.implicit-attribute-type")
output should beResult(expected)
}

it should "invalid the AST when an implicit type of an attribute does not exist" in {
val expected = Ko[Root](invalidImplicitAttributeTypeErrors)
val output = processFile("definedType.invalid-implicit-attribute-type")
output should beResult(expected)
}
}

object DefinedTypeSpec {
val implicitAttributeTypeLocation = LocationPath("src/test/resources/samples/definedType/implicit-attribute-type.def")
val implicitAttributeType = root(
DefinedType(
name = "Person",
fullName = "Person",
genericTypes = Seq.empty,
parameters = Seq.empty,
attributes = Seq(
AttributeDefinition(
name = "email",
typeDeclaration = TypeDeclaration("Email", Seq.empty, Seq.empty, implicitAttributeTypeLocation(2, 3, 8)),
comment = None,
verifications = Seq(
VerificationReference(
verificationName = "IsNonEmptyEmail",
parameters = Seq.empty,
location = implicitAttributeTypeLocation(2, 9, 34)
)
),
location = implicitAttributeTypeLocation(2, 3, 34)
),
AttributeDefinition(
name = "address",
typeDeclaration = TypeDeclaration("Address", Seq.empty, Seq.empty, implicitAttributeTypeLocation(3, 3, 10)),
comment = None,
verifications = Seq.empty,
location = implicitAttributeTypeLocation(3, 3, 10)
)
),
verifications = Seq.empty,
inherited = Seq.empty,
comment = None,
location = implicitAttributeTypeLocation(1, 1, 4, 2)
),
AliasType(
name = "Email",
fullName = "Email",
genericTypes = Seq.empty,
parameters = Seq.empty,
alias = TypeDeclaration("String", Seq.empty, Seq.empty, implicitAttributeTypeLocation(6, 14, 20)),
inherited = Seq.empty,
verifications = Seq.empty,
comment = None,
location = implicitAttributeTypeLocation(6, 1, 20)
),
DefinedType(
name = "Address",
fullName = "Address",
genericTypes = Seq.empty,
parameters = Seq.empty,
attributes = Seq(
AttributeDefinition(
name = "city",
typeDeclaration = TypeDeclaration("String", Seq.empty, Seq.empty, implicitAttributeTypeLocation(9, 9, 15)),
comment = None,
verifications = Seq.empty,
location = implicitAttributeTypeLocation(9, 3, 15)
),
AttributeDefinition(
name = "country",
typeDeclaration = TypeDeclaration("String", Seq.empty, Seq.empty, implicitAttributeTypeLocation(10, 12, 18)),
comment = None,
verifications = Seq.empty,
location = implicitAttributeTypeLocation(10, 3, 18)
)
),
verifications = Seq.empty,
inherited = Seq.empty,
comment = None,
location = implicitAttributeTypeLocation(8, 1, 11, 2)
),
Verification(
name = "IsNonEmptyEmail",
fullName = "IsNonEmptyEmail",
parameters = Seq.empty,
message = LiteralMessage("Please give a non empty email", implicitAttributeTypeLocation(14, 3, 34)),
function = DefinedFunction(
parameters = Seq(ParameterDefinition("email", TypeReference("Email"), implicitAttributeTypeLocation(15, 4, 16))),
body = MethodCall(
expression = Reference("email", TypeReference("Email"), implicitAttributeTypeLocation(16, 5, 10)),
method = "nonEmpty",
parameters = Seq.empty,
generics = Seq.empty,
returnType = TypeReference("Boolean"),
location = implicitAttributeTypeLocation(16, 5, 21)
),
genericTypes = Seq.empty,
location = implicitAttributeTypeLocation(15, 3, 17, 4)
),
comment = None,
location = implicitAttributeTypeLocation(13, 1, 18, 2)
)
)

val invalidImplicitAttributeTypeErrorsLocation = LocationPath("src/test/resources/samples/definedType/invalid-implicit-attribute-type.def")
val invalidImplicitAttributeTypeErrors = Seq(
AttributeTypeControl.errorUnknownType("Email", invalidImplicitAttributeTypeErrorsLocation(2, 3, 8)),
AttributeTypeControl.errorUnknownType("Address", invalidImplicitAttributeTypeErrorsLocation(3, 3, 10)),
TypeDeclarationParametersControl.errorUnknownType("Email", invalidImplicitAttributeTypeErrorsLocation(2, 3, 8)),
TypeDeclarationParametersControl.errorUnknownType("Address", invalidImplicitAttributeTypeErrorsLocation(3, 3, 10))
)
}

0 comments on commit f9df82d

Please sign in to comment.