Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » How to implement the type validation test for my grammar
How to implement the type validation test for my grammar [message #1796405] Fri, 12 October 2018 13:08 Go to next message
kozhaev Vladimir is currently offline kozhaev VladimirFriend
Messages: 108
Registered: July 2009
Senior Member
Hi, all.
I have written the test for my grammar(please, see it below) and the method
Quote:
@Test def void stringConstant() { "'foo'".assertStringType }

Fails with the following error
Quote:
java.lang.NullPointerException
at org.blockchain.rell.tests.ExpressionsTypeProviderTest.assertType(ExpressionsTypeProviderTest.java:78)
at org.blockchain.rell.tests.ExpressionsTypeProviderTest.assertStringType(ExpressionsTypeProviderTest.java:58)
at org.blockchain.rell.tests.ExpressionsTypeProviderTest.stringConstant(ExpressionsTypeProviderTest.java:39)


I understand why it's happened - because of strings what we have tested are not valid with my grammar. But how to test part of the grammar. In the ANTLR I can check only one rule. But how to do something like it with XText?

Regards,
Vladimir

My grammar

grammar org.blockchain.rell.Rell with org.eclipse.xtext.common.Terminals

generate rell "http://www.blockchain.org/rell/Rell"

Model:
	entities+=TheClass*
	operations+=Operation*;

TheClass:
	'class' name=ID ('extends' superType=[TheClass])? '{'
	attributes+=Attribute*
	'}';

Operation:
	"operation" name=ID "(" parameters=RelAttrubutesList? ")" "{" statements+=Statement* "}";

Statement:
	(Relational | Variable) ';';

Variable:
	declaration=VariableDeclaration '=' expression=Expression;

Relational:
	Update | Delete | Create;

Update:
	'update' entity=ID '(' expressions=ExpressionsModel? ')' '{' variableList+=VariableInit
	(',' variableList+=VariableInit*)? '}';

Delete:
	'delete' entity=ID '(' expressions=ExpressionsModel? ')';

Create:
	'create' entity=ID '(' expressions=ExpressionsModel? ')';

ExpressionsModel:
	elements+=ConditionElements (',' elements+=ConditionElements*)?;

ConditionElements:
	compareName=ID ('==' | '!=' | '>' | '<' | '>=' | '<=') expr=Expression;


VariableInit:
	name=ID '=' expression=Expression;

Expression:
	Or;

Or returns Expression:
	And ({Or.left=current} "or" right=And)*;

And returns Expression:
	Equality ({And.left=current} "and" right=Equality)*;

Equality returns Expression:
	Comparison ({Equality.left=current} op=("==" | "!=")
	right=Comparison)*;

Comparison returns Expression:
	PlusOrMinus ({Comparison.left=current} op=(">=" | "<=" | ">" | "<")
	right=PlusOrMinus)*;

PlusOrMinus returns Expression:
	MulOrDiv (({Plus.left=current} '+' | {Minus.left=current} '-')
	right=MulOrDiv)*;

Atomic returns Expression:
	{IntConstant} value=INT |
	{StringConstant} value=STRING |
	{BoolConstant} value=('true' | 'false') |
	{VariableRef} variable=[VariableDeclaration];

MulOrDiv returns Expression:
	Primary ({MulOrDiv.left=current} op=('*' | '/')
	right=Primary)*;

Primary returns Expression:
	'(' Expression ')' |
	{Not} "not" expression=Primary |
	Atomic;

RelAttrubutesList:
	value+=VariableDeclaration (',' value+=VariableDeclaration)*;



Attribute:
	modificator=Prefix? variable=VariableDeclaration ';';

VariableDeclaration:
	name=ID ':' type=TypeReference;

TypeReference:
	primitive=PrimitiveType | entity=EntityType;

PrimitiveType:
	Text | Integer | Json | ByteArray;

EntityType:
	entity=[TheClass];

Json:
	'json';

Integer:
	'integer' | 'timestamp';

Text:
	'text' | 'tuid' | 'name';

ByteArray:
	'byte_array' | 'signer' | 'guid' | 'pubkey';

Prefix:
	'key' | 'index';


My test
grammar org.blockchain.rell.Rell with org.eclipse.xtext.common.Terminals

generate rell "http://www.blockchain.org/rell/Rell"

Model:
	entities+=TheClass*
	operations+=Operation*;

TheClass:
	'class' name=ID ('extends' superType=[TheClass])? '{'
	attributes+=Attribute*
	'}';

Operation:
	"operation" name=ID "(" parameters=RelAttrubutesList? ")" "{" statements+=Statement* "}";

Statement:
	(Relational | Variable) ';';

Variable:
	declaration=VariableDeclaration '=' expression=Expression;

Relational:
	Update | Delete | Create;

Update:
	'update' entity=ID '(' expressions=ExpressionsModel? ')' '{' variableList+=VariableInit
	(',' variableList+=VariableInit*)? '}';

Delete:
	'delete' entity=ID '(' expressions=ExpressionsModel? ')';

Create:
	'create' entity=ID '(' expressions=ExpressionsModel? ')';

ExpressionsModel:
	elements+=ConditionElements (',' elements+=ConditionElements*)?;

ConditionElements:
	compareName=ID ('==' | '!=' | '>' | '<' | '>=' | '<=') expr=Expression;


VariableInit:
	name=ID '=' expression=Expression;

Expression:
	Or;

Or returns Expression:
	And ({Or.left=current} "or" right=And)*;

And returns Expression:
	Equality ({And.left=current} "and" right=Equality)*;

Equality returns Expression:
	Comparison ({Equality.left=current} op=("==" | "!=")
	right=Comparison)*;

Comparison returns Expression:
	PlusOrMinus ({Comparison.left=current} op=(">=" | "<=" | ">" | "<")
	right=PlusOrMinus)*;

PlusOrMinus returns Expression:
	MulOrDiv (({Plus.left=current} '+' | {Minus.left=current} '-')
	right=MulOrDiv)*;

Atomic returns Expression:
	{IntConstant} value=INT |
	{StringConstant} value=STRING |
	{BoolConstant} value=('true' | 'false') |
	{VariableRef} variable=[VariableDeclaration];

MulOrDiv returns Expression:
	Primary ({MulOrDiv.left=current} op=('*' | '/')
	right=Primary)*;

Primary returns Expression:
	'(' Expression ')' |
	{Not} "not" expression=Primary |
	Atomic;

RelAttrubutesList:
	value+=VariableDeclaration (',' value+=VariableDeclaration)*;



Attribute:
	modificator=Prefix? variable=VariableDeclaration ';';

VariableDeclaration:
	name=ID ':' type=TypeReference;

TypeReference:
	primitive=PrimitiveType | entity=EntityType;

PrimitiveType:
	Text | Integer | Json | ByteArray;

EntityType:
	entity=[TheClass];

Json:
	'json';

Integer:
	'integer' | 'timestamp';

Text:
	'text' | 'tuid' | 'name';

ByteArray:
	'byte_array' | 'signer' | 'guid' | 'pubkey';

Prefix:
	'key' | 'index';


Re: How to implement the type validation test for my grammar [message #1796406 is a reply to message #1796405] Fri, 12 October 2018 13:11 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
Hi,

you can use ParseHelper and ValidationtestHelper to test parsing and validation.
a parser test is pregenerated by the wizard


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: How to implement the type validation test for my grammar [message #1796412 is a reply to message #1796406] Fri, 12 October 2018 14:03 Go to previous messageGo to next message
kozhaev Vladimir is currently offline kozhaev VladimirFriend
Messages: 108
Registered: July 2009
Senior Member
Hmm, may I ask you to tell me something more details
Re: How to implement the type validation test for my grammar [message #1796416 is a reply to message #1796412] Fri, 12 October 2018 14:36 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
here is a simple example for a unit test

import com.google.inject.Inject
import org.eclipse.xtext.testing.InjectWith
import org.eclipse.xtext.testing.extensions.InjectionExtension
import org.eclipse.xtext.testing.util.ParseHelper
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.^extension.ExtendWith
import org.xtext.example.mydsl2.myDsl.Model
import org.eclipse.xtext.testing.validation.ValidationTestHelper
import org.xtext.example.mydsl2.myDsl.MyDslPackage
import org.xtext.example.mydsl2.validation.MyDslValidator

@ExtendWith(InjectionExtension)
@InjectWith(MyDslInjectorProvider)
class MyDslParsingTest {
	@Inject
	ParseHelper<Model> parseHelper
	
	@Inject extension ValidationTestHelper
	
	@Test
	def void loadModel() {
		val result = parseHelper.parse('''
			Hello Xtext!
		''')
		Assertions.assertNotNull(result)
		val errors = result.eResource.errors
		Assertions.assertTrue(errors.isEmpty, '''Unexpected errors: «errors.join(", ")»''')
		
		val result2 = parseHelper.parse('''
			Hello xtext!
		''')
		Assertions.assertNotNull(result2)
		val errors2 = result2.eResource.errors
		Assertions.assertTrue(errors2.isEmpty, '''Unexpected errors: «errors2.join(", ")»''')
		result2.assertWarning(MyDslPackage.Literals.GREETING, MyDslValidator.INVALID_NAME, "Name should start with a capital")
	}
}




Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: How to implement the type validation test for my grammar [message #1796540 is a reply to message #1796416] Mon, 15 October 2018 22:33 Go to previous messageGo to next message
kozhaev Vladimir is currently offline kozhaev VladimirFriend
Messages: 108
Registered: July 2009
Senior Member
Oh, look like I have posted the grammar twice instead of showing the test.

Please, see my test below
package org.blockchain.rell.tests

import com.google.inject.Inject
import org.blockchain.rell.rell.ExpressionsModel
import org.blockchain.rell.typing.ExpressionsType
import org.blockchain.rell.typing.ExpressionsTypeProvider
import org.eclipse.xtext.testing.InjectWith
import org.eclipse.xtext.testing.XtextRunner
import org.eclipse.xtext.testing.util.ParseHelper
import org.junit.Test
import org.junit.runner.RunWith

import static extension org.junit.Assert.*

@RunWith(typeof(XtextRunner))
@InjectWith(typeof(RellInjectorProvider))
class ExpressionsTypeProviderTest {
	@Inject extension ParseHelper<ExpressionsModel>
	@Inject extension ExpressionsTypeProvider
	
	@Test def void intConstant() { "10".assertIntType }
	@Test def void stringConstant() { "'foo'".assertStringType }
	
	@Test def void testIsInt() { 
		(ExpressionsTypeProvider::intType).isInt.assertTrue
	}

	@Test def void testIsString() { 
		(ExpressionsTypeProvider::stringType).isString.assertTrue
	}

	@Test def void testIsBool() { 
		(ExpressionsTypeProvider::boolType).isBoolean.assertTrue
	}
	
	def assertStringType(CharSequence input) {
		input.assertType(ExpressionsTypeProvider::stringType)
	}
	
	def assertIntType(CharSequence input) {
		input.assertType(ExpressionsTypeProvider::intType)
	}

	def assertBoolType(CharSequence input) {
		input.assertType(ExpressionsTypeProvider::boolType)		
	}

	def assertUnknownType(CharSequence input) {
		input.assertType(null)		
	}

	def assertType(CharSequence input, ExpressionsType expectedType) {
		
		println("expectedType:"+expectedType+", input:"+input+", input.parse:"+input.parse)
		expectedType.assertSame
			(input.parse.elements.last.typeFor)
	}
}


Tests stringConstant and intConstant fails with null pointer exception. Look like it's because of sentences "10" and "'foo'" is not valid with my grammar. But how to fix it?



Re: How to implement the type validation test for my grammar [message #1796541 is a reply to message #1796540] Mon, 15 October 2018 22:54 Go to previous messageGo to next message
kozhaev Vladimir is currently offline kozhaev VladimirFriend
Messages: 108
Registered: July 2009
Senior Member
I.e. problem is that method

ParseHelper<ExpressionsModel>.parse return null on the following strings "10", "'foo'". How to solve this problem?
Re: How to implement the type validation test for my grammar [message #1796557 is a reply to message #1796541] Tue, 16 October 2018 05:15 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
You can't.
(Using a debugger might help in such cases)
You need to do manual parsing instead. // pseudo code
@Inject
Provider<ResourceSet> rsp
....
ResourceSet rs = rsp.get()
Resource r = rs.createResource(URI.createUri("dummy.yourdsl"))
R.load(new StringInputStream(modeltext),null)
r.getErrors // test if empty
Model= r.getContents.get(0)


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: How to implement the type validation test for my grammar [message #1796558 is a reply to message #1796557] Tue, 16 October 2018 05:16 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
P.s. your grammar does not sllow expressions only
So you have to stick together a valid model

Or you use XtextResource.setEntryPoint + grammarAccess to set the parsers entry rule


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de

[Updated on: Tue, 16 October 2018 05:22]

Report message to a moderator

Re: How to implement the type validation test for my grammar [message #1796587 is a reply to message #1796558] Tue, 16 October 2018 11:08 Go to previous messageGo to next message
kozhaev Vladimir is currently offline kozhaev VladimirFriend
Messages: 108
Registered: July 2009
Senior Member
Unfortunately it's not working

Method r.getContents return empty list. Also, there is no method createURI in my version of the URI. Please, see my test below. May I ask you to correct me?

package org.blockchain.rell.tests

import com.google.inject.Inject
import com.google.inject.Provider
import org.blockchain.rell.rell.ExpressionsModel
import org.blockchain.rell.typing.ExpressionsType
import org.blockchain.rell.typing.ExpressionsTypeProvider
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.xtext.testing.InjectWith
import org.eclipse.xtext.testing.XtextRunner
import org.eclipse.xtext.testing.util.ParseHelper
import org.eclipse.xtext.util.StringInputStream
import org.junit.Test
import org.junit.runner.RunWith

import static extension org.junit.Assert.*

@RunWith(typeof(XtextRunner))
@InjectWith(typeof(RellInjectorProvider))
class ExpressionsTypeProviderTest {
	@Inject extension ParseHelper<ExpressionsModel>
	@Inject extension ExpressionsTypeProvider
	@Inject Provider<ResourceSet> rsp

	@Test def void intConstant() { "10".assertIntType }

	@Test def void stringConstant() { "'foo'".assertStringType }

	@Test def void testIsInt() {
		(ExpressionsTypeProvider::intType).isInt.assertTrue
	}

	@Test def void testIsString() {
		(ExpressionsTypeProvider::stringType).isString.assertTrue
	}

	@Test def void testIsBool() {
		(ExpressionsTypeProvider::boolType).isBoolean.assertTrue
	}

	def assertStringType(CharSequence input) {
		input.assertType(ExpressionsTypeProvider::stringType)
	}

	def assertIntType(CharSequence input) {
		input.assertType(ExpressionsTypeProvider::intType)
	}

	def assertBoolType(CharSequence input) {
		input.assertType(ExpressionsTypeProvider::boolType)
	}

	def assertUnknownType(CharSequence input) {
		input.assertType(null)
	}

	def assertType(CharSequence input, ExpressionsType expectedType) {
		var rs = rsp.get()
		var r = rs.createResource(URI.createDeviceURI("test.rell"));
		r.load(new StringInputStream(input.toString()),null)
		r.getErrors // test if empty
		var  model = r.getContents.get(0) 
		model.assertSame(expectedType)
	}
}



Re: How to implement the type validation test for my grammar [message #1796591 is a reply to message #1796587] Tue, 16 October 2018 11:28 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
Should be URI.createURI

And you still should either set the parser rule
Or create a compile model

Like modelStubBeforExpression + expression + modelStubafterExpression


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: How to implement the type validation test for my grammar [message #1796606 is a reply to message #1796591] Tue, 16 October 2018 13:11 Go to previous messageGo to next message
kozhaev Vladimir is currently offline kozhaev VladimirFriend
Messages: 108
Registered: July 2009
Senior Member
Should be URI.createURI


But, I haven't this method.

Or create a compile model

Like modelStubBeforExpression + expression + modelStubafterExpression

Maybe you can to show me an example?
Re: How to implement the type validation test for my grammar [message #1796610 is a reply to message #1796606] Tue, 16 October 2018 13:34 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
http://download.eclipse.org/modeling/emf/emf/javadoc/2.4.3/org/eclipse/emf/common/util/URI.html#createURI(java.lang.String)

Consider a grammar with

var x = 1;

Then it could be

„var x = „ + expression + „;"


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: How to implement the type validation test for my grammar [message #1796635 is a reply to message #1796610] Tue, 16 October 2018 18:05 Go to previous message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
test that shows your problem

import com.google.inject.Inject
import com.google.inject.Provider
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.xtext.testing.InjectWith
import org.eclipse.xtext.testing.XtextRunner
import org.eclipse.xtext.util.StringInputStream
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(XtextRunner)
@InjectWith(MyDslInjectorProvider)
class MyDslParsingTest {
	
	@Inject
	Provider<ResourceSet> rsp
	
	@Test
	def void loadModel() {
		val input = '''1'''
		var rs = rsp.get()
		var r = rs.createResource(URI.createURI("test.mydsl2"));
		r.load(new StringInputStream(input.toString()),null)
		r.getErrors // test if empty
		var errors = r.errors
		Assert.assertTrue('''Unexpected errors: «errors.join(", ")»''', errors.isEmpty)
		var model = r.getContents.get(0)
	}
}


test that shows solution (1)

import com.google.inject.Inject
import com.google.inject.Provider
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.xtext.testing.InjectWith
import org.eclipse.xtext.testing.XtextRunner
import org.eclipse.xtext.util.StringInputStream
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(XtextRunner)
@InjectWith(MyDslInjectorProvider)
class MyDslParsingTest {
	
	@Inject
	Provider<ResourceSet> rsp
	
	@Test
	def void loadModel() {
		val input = '''1'''
		val completeInput = '''operation myOp() {
			x : integer = «input»;
		}'''
		var rs = rsp.get()
		var r = rs.createResource(URI.createURI("test.mydsl2"));
		r.load(new StringInputStream(completeInput.toString()),null)
		r.getErrors // test if empty
		var errors = r.errors
		Assert.assertTrue('''Unexpected errors: «errors.join(", ")»''', errors.isEmpty)
		var model = r.getContents.get(0)
	}
}


test that shows solution (2)


import com.google.inject.Inject
import com.google.inject.Provider
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.xtext.testing.InjectWith
import org.eclipse.xtext.testing.XtextRunner
import org.eclipse.xtext.util.StringInputStream
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.eclipse.xtext.resource.XtextResource
import org.xtext.example.mydsl2.services.MyDslGrammarAccess

@RunWith(XtextRunner)
@InjectWith(MyDslInjectorProvider)
class MyDslParsingTest {
	
	@Inject
	Provider<ResourceSet> rsp
	
	@Inject
	MyDslGrammarAccess ga;
	
	@Test
	def void loadModel() {
		val input = '''1'''
		var rs = rsp.get()
		var r = rs.createResource(URI.createURI("test.mydsl2"));
		(r as XtextResource).entryPoint = ga.expressionRule
		
		r.load(new StringInputStream(input.toString()),null)
		r.getErrors // test if empty
		var errors = r.errors
		Assert.assertTrue('''Unexpected errors: «errors.join(", ")»''', errors.isEmpty)
		var model = r.getContents.get(0)
	}
}



Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Previous Topic:Language Server Protocol Syntax Highlighting
Next Topic:Update site down
Goto Forum:
  


Current Time: Fri Mar 29 06:22:52 GMT 2024

Powered by FUDForum. Page generated in 0.03086 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top