I am having trouble with this concept in building a dsl. I am not sure if it is a simple thing I am missing or something that is not an intended feature of xtext. Hopefully someone can explain it to me in the context of this example.
Given the following minimal grammar:
Model:
'ns' name=QualifiedName
classes+=Class*
instances+=Instance*
uses+=Use*
;
Class:
'class' name=ID '{'
variables+=Variable*
'}'
;
Variable:
'var' variable=PrimaryVariable
;
Instance:
variable=PrimaryVariable '=' 'new' type=[Class]
;
Use:
reference=[PrimaryVariable|QualifiedName]
;
PrimaryVariable:
name=ID
;
QualifiedName:
ID ('.' ID)*
;
I would like to be able to write the following code, which of course is not valid:
ns com.mine
class Class1 {
var var1
}
instance1 = new Class1
instance1.var1 // <- error here, can't resolve reference
With this grammar and default scoping, only this would work:
ns com.mine
class Class1 {
var var1
}
instance1 = new Class1
Class1.var1
So my question is: how would I go about implementing the concept of referencing variables by qualified name through an instance variable?
I don't think I could manage qualifiedNameProvider to achieve this because the PrimaryVariable does not have knowledge of what instance it is being used in.
I could of course create a rule which uses two reference (and is what I am currently doing), one to the instance variable and then traverse the instance variable's type to get variables in scope for the variable reference, but this seems like a hack to the way it should be and not as scalable in the case of nested objects.
This is a slightly broad question, I am hoping that I can get informed before I go off doing something completely counter productive.
Here is a sample for the scope provider
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.EReference
import org.eclipse.xtext.EcoreUtil2
import org.eclipse.xtext.naming.QualifiedName
import org.eclipse.xtext.resource.EObjectDescription
import org.eclipse.xtext.scoping.IScope
import org.eclipse.xtext.scoping.impl.SimpleScope
import org.xtext.example.mydsl6.myDsl.Model
import org.xtext.example.mydsl6.myDsl.MyDslPackage
class MyDslScopeProvider extends AbstractMyDslScopeProvider {
override getScope(EObject context, EReference reference) {
if (reference === MyDslPackage.Literals.USE__REFERENCE) {
val model = EcoreUtil2.getContainerOfType(context, Model)
if (model !== null) {
val result = newArrayList
for (i : model.instances) {
result.add( EObjectDescription.create(
QualifiedName.create( i.variable.name ), i.variable ))
for (v : i.type.variables) {
result.add( EObjectDescription.create(
QualifiedName.create( i.variable.name, v.variable.name ),
v.variable ))
}
}
println(result)
return new SimpleScope(IScope.NULLSCOPE, result)
}
}
super.getScope(context, reference)
}
}
Related
I've followed the directions from the answers to the SO question
How can classes be made parametric in Perl 6?. However, I've hit some soft roadblock; I'm trying to type an internal class's attribute using the type capture and getting the following error:
Died with X::TypeCheck::Assignment
in submethod BUILDALL at ...
in method insert at ...
in block <unit> at ...
In the following example, I've typed the class BinaryNode's $.item attribute (with T) but doing so causes the above error:
class BinarySearchTree {
my role BTSImpl[::T] {
my class BinaryNode is rw {
has T $.item;
has BinaryNode $.left;
has BinaryNode $.right;
}
method create-node( T $x ) {
BinaryNode.new(item => $x)
}
}
method ^parameterize(Mu:U \this, Mu \T) {
my $type := this.^mixin: BTSImpl[T];
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
$type
}
}
my $bst = BinarySearchTree[Int].new;
$bst.create-node(6);
First off, there's almost no need to perform the class + ^parameterize + role trick. It appears in some of the internals because it helps deal with some bootstrapping problems (the kind of fun one has when defining a language in terms of itself). However, in normal Raku code, just write a parametric role instead of a class. From the point of view of the consumer, there's usually no difference; one can:
Call .new on it to make an instance (which actually creates a class, known as a "pun", behind the scenes and makes the instance of that)
In fact, call any method on the type object with the same result; new isn't special
Inherit from it (again, it works on the automatically produced class)
With the added bonus that somebody can also compose it instead of inheriting.
Secondly, there's no relationship between a class defined inside of a role and the enclosing role (this is a general principle: nesting of one package inside of another doesn't imply any relationship between them at an object model level). Thus we need to make that separately generic and instantiate it.
These two get us to:
role BinarySearchTree[::T] {
my role BinaryNode[::T] is rw {
has T $.item;
has BinaryNode $.left;
has BinaryNode $.right;
}
method create-node( T $x ) {
BinaryNode[T].new(item => $x)
}
}
my $bst = BinarySearchTree[Int].new;
$bst.create-node(6);
Which really should work, but the compiler seems to get the timing wrong on the BinaryNode[T]. We can work around that by just forcing it to delay the paramerterization until runtime; there's many ways we may do that, but writing BinaryNode[$(T)] compact and cheap (optimizes into pretty much no extra cost). Thus giving a working solution:
role BinarySearchTree[::T] {
my role BinaryNode[::T] is rw {
has T $.item;
has BinaryNode $.left;
has BinaryNode $.right;
}
method create-node( T $x ) {
BinaryNode[$(T)].new(item => $x)
}
}
my $bst = BinarySearchTree[Int].new;
$bst.create-node(6);
I am using node --experimental-modules test.mjs (NodeJs v11.9.0).
There are many options to implement the same class, and I need to swith by terminal,
switch (process.argv[2]) {
case 'a':
import MyClass from './MyClass-v1.mjs'; break;
case 'b':
import MyClass from './MyClass-v2.mjs'; break;
default:
import MyClass from './MyClass-v3.mjs';
}
ERROR on import MyClass: Unexpected identifier
PS: working fine when using isolated import MyClass from './MyClass-v3.mjs';.
NOTE: const MyClass = require('./MyClass-v3.mjs') not works with modern Javascript. (ReferenceError: require is not defined). The file have only a class definition,
export default class MyClass { ... }
PS: there are nothing like C_preprocessor for NodeJs? An old pre-compiler (with some argv access) will be fine here.
import ClassV1 from './MyClass-v1.mjs';
import ClassV2 from './MyClass-v2.mjs';
import ClassV3 from './MyClass-v3.mjs';
const classes = {
a: ClassV1,
b: ClassV2
}
const Class = classes[process.argv[2]] || ClassV2;
You could also write an index file with all the classes and do an import * as classes from 'classes.mjs';.
Modern Javascript (ES6+) implementations are under construction, some syntax are working with NodeJS (node --experimental-modules) and good browsers, other not.
This is the only way to do without cost of "load all" (see #Markus solution), but with the cost of non-global import, needs an ugly block to define scope and assyncronous operation... First step is to understand the basic impor block syntax:
import('./MyClass-v3.mjs').then(({default: MyClass}) => {
// can use MyClass only here in this block
let x = new MyClass();
console.log("DEBUG=",x);
});
Them, putting all together, the argv selection and the optional imports.
Solution
const classes = {
a: './MyClass-v1.mjs'
,b: './MyClass-v3.mjs'
,c: './MyClass-v3.mjs'
};
import(classes[process.argv[2]] || classes.c ).then(({default: MyClass}) => {
let x = new MyClass();
console.log("DEBUG=",x);
});
PS: some of the magic (no rational) is to use ({default: MyClass}) instead (MyClass).
Thanks
To #Bergi (!), for explain this solution (see question comments).
I'm trying to create an static analysis for Groovy. As a POC for my superiors I'm just trying to parse simple code and detect SQL injections, which are the easiest kind to spot. I did it successfully on Python, which is my main language, but my company mostly uses Grails (on Groovy).
This is what I have so far:
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.ast.stmt.*;
import org.codehaus.groovy.ast.*
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.ast.CodeVisitorSupport
import org.codehaus.groovy.ast.builder.AstBuilder
public class SecurityCheck extends CodeVisitorSupport {
void visitBlockStatement(BlockStatement statement) {
println "NEW BLOCK STATEMENT:"
println statement.getText();
//keep walking...
statement.getStatements().each { ASTNode child ->
println "CHILD FOUND: "
println child.getText();
child.visit(this)
}
}
}
def code = new File('groovy_source.groovy').text // get the code from the source file
def AstBuilder astBuilder = new AstBuilder() // build an instance of the ast builder
def ast = astBuilder.buildFromString(CompilePhase.CONVERSION, code) // build from string when the compiler converts from tokens to AST
def SecurityCheck securityCheck = new SecurityCheck() // create an instance of our security check class
println ast
println ast[0]
ast[0].visit(securityCheck)
The groovy_source.groovy file is very simple, containing only a minimal file with a super easy to spot vulnerability:
def post(id) {
query = "SELECT * FROM table WHERE id = " + id;
result = sql.execute query
return result;
}
It is my understanding that, as I'm inheriting from CodeVisitorSupport, this would just visit a BlockStatement and then, for each statement inside that statement, it would visit it using the method from the supper class.
Nevertheless, when I print the text from the BlockStatement, it is an empty string, and the for each method never even gets called (which I assume must mean the AST found no children for my block statement, even when the function definitively has statements inside it.
[org.codehaus.groovy.ast.stmt.BlockStatement#363a52f[]] // println ast
org.codehaus.groovy.ast.stmt.BlockStatement#363a52f[] // println ast[0]
NEW BLOCK STATEMENT:
{ } // println statement.getText()
Any help here would be tremendously appreciated. Thanks!
I found the answer. I wasn't so hard in the end, but the horrible documentation doesn't make it easy. If you one to traverse the tree, you need to give the constructor the false boolean as a second argument, like this:
def ast = astBuilder.buildFromString(CompilePhase.CONVERSION, false, code)
Then you can use the visit* methods as you expect.
A SoapUI project can run random script upon load.
Load Script is invoked with log and project variables.
In my shared lib I have method - addAsserts() that traverses the whole project and adds schema compliance assertions to SOAP test steps. In my Load Script I call shared method
addAsserts(this)
passing 'this' as a parameter and set closure.delegate to it inside addAsserts method to make 'project' variable accessible within the closure scope
addAsserts method is defined in sharedUtil.groovy:
static def addAsserts(that){
def closure={
project.testSuites.each { testSuiteName, testSuiteObject ->
testSuiteObject.testCases.each { testCaseName, testCaseObject ->
testCaseObject.testSteps.each { testStepName, testStepObject ->
if ("class com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep" == testStepObject.getClass().toString() ) {
log.info "adding 'Schema Compliance' assertion to ${testSuiteName}/${testCaseName}/${testStepName}"
testStepObject.addAssertion('Schema Compliance')
}
}
}
}
}//closure
closure.delegate=that // <--- i would like NOT to pass 'that' as parameter
// but rather detect in runtime with some kind of
// getCallerInstance() method
return closure.call()
}
QUESTION:
Is it possible to detect caller instance in runtime with some kind of getCallerInstance() method ?
No, I don't believe this is possible. Wasn't in Java either (you can find out the name/method of the calling class using some horrible stacktrace hacking, but not the instance of the class itself)
Edit...
It might be possible with a Category (but I am not experienced with SoapUI, so I don't know if this technique would fit)
Say we have a class Example defined like so:
class Example {
String name
}
We can then write a class very similar to your example code, which in this case will set the delegate of the closure, and the closure will print out the name property of the delegate (as we have set the resolve strategy to DELEGATE_ONLY)
class AssetAddingCategory {
static def addAsserts( that ) {
def closure = {
"Name of object: $name"
}
closure.delegate = that
closure.resolveStrategy = Closure.DELEGATE_ONLY
closure.call()
}
}
Later on in our code, it is then possible to do:
def tim = new Example( name:'tim' )
use( AssetAddingCategory ) {
println tim.addAsserts()
}
And this will print out
Name of object: tim
a little question about erazor https://github.com/ciscoheat/erazor
i know this framwork is based on Razor template engine. http://weblogs.asp.net/scottgu/archive/2010/07/02/introducing-razor.aspx
i noticed the api doesn't fit exactly with Razor (ex: #for(a in p) differs from RAZOR)
this template system for haxe is very handy...
i just don't know how to setup a variable like we do in templo ( :: set mock="tada!":: )
//#scope is mycontroller;
#{var mock = scope.getMock()}
#if(mock!=null){
//display some html
}
any tips ?
thx
The following snippet works:
import erazor.Template;
import neko.Lib;
class Main {
static function main() {
var template = new Template("#{var mock = scope.getMock();} #if (mock != null) { #mock }");
Lib.print(template.execute( { scope : { getMock : function() return "hi" } } ));
}
}
What you missed is that inside a code block all the statements must be correctly closed (missing ;). Also erazor is loosely based on Razor and uses the Haxe syntax for expressions.