Browse Source

Merge pull request #17 from yakomaxa/molql_integration_PR

Molql integration pr
KoyaS 2 years ago
parent
commit
e6d01ca246

+ 1 - 1
src/mol-plugin-state/transforms/model.ts

@@ -786,7 +786,7 @@ const StructureSelectionFromScript = PluginStateTransform.BuiltIn({
     from: SO.Molecule.Structure,
     to: SO.Molecule.Structure,
     params: () => ({
-        script: PD.Script({ language: 'pymol', expression: 'all' }),
+        script: PD.Script({ language: 'mol-script', expression: '(sel.atom.atom-groups :residue-test (= atom.resname ALA))' }),
         label: PD.Optional(PD.Text(''))
     })
 })({

+ 26 - 21
src/mol-plugin-ui/controls/parameters.tsx

@@ -7,6 +7,7 @@
 
 import * as React from 'react';
 import { Mat4, Vec2, Vec3 } from '../../mol-math/linear-algebra';
+import { Script } from '../../mol-script/script';
 import { Asset } from '../../mol-util/assets';
 import { Color } from '../../mol-util/color';
 import { ColorListEntry } from '../../mol-util/color/color';
@@ -1466,31 +1467,35 @@ export class ConvertedControl extends React.PureComponent<ParamProps<PD.Converte
     }
 }
 
-export class ScriptControl extends SimpleParam<PD.Script> {
-    onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-        const value = e.target.value;
-        if (value !== this.props.value.expression) {
-            this.update({ language: this.props.value.language, expression: value });
+export class ScriptControl extends React.PureComponent<ParamProps<PD.Script>> {
+    onChange: ParamOnChange = ({ name, value }) => {
+        const k = name as 'language' | 'expression';
+        if (value !== this.props.value[k]) {
+            this.props.onChange({ param: this.props.param, name: this.props.name, value: { ...this.props.value, [k]: value } });
         }
     };
 
-    onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if ((e.keyCode === 13 || e.charCode === 13 || e.key === 'Enter')) {
-            if (this.props.onEnter) this.props.onEnter();
-        }
-        e.stopPropagation();
-    };
-
-    renderControl() {
+    render() {
         // TODO: improve!
 
-        const placeholder = this.props.param.label || camelCaseToWords(this.props.name);
-        return <input type='text'
-            value={this.props.value.expression || ''}
-            placeholder={placeholder}
-            onChange={this.onChange}
-            onKeyPress={this.props.onEnter ? this.onKeyPress : void 0}
-            disabled={this.props.isDisabled}
-        />;
+        const selectParam: PD.Select<PD.Script['defaultValue']['language']> = {
+            defaultValue: this.props.value.language,
+            options: PD.objectToOptions(Script.Info),
+            type: 'select',
+        };
+        const select = <SelectControl param={selectParam}
+            isDisabled={this.props.isDisabled} onChange={this.onChange} onEnter={this.props.onEnter}
+            name='language' value={this.props.value.language} />;
+
+        const textParam: PD.Text = {
+            defaultValue: this.props.value.language,
+            type: 'text',
+        };
+        const text = <TextControl param={textParam} isDisabled={this.props.isDisabled} onChange={this.onChange} name='expression' value={this.props.value.expression} />;
+
+        return <>
+            {select}
+            {text}
+        </>;
     }
 }

+ 15 - 19
src/mol-script/script.ts

@@ -11,15 +11,7 @@ import { Expression } from './language/expression';
 import { StructureElement, QueryContext, StructureSelection, Structure, QueryFn, QueryContextOptions } from '../mol-model/structure';
 import { compile } from './runtime/query/compiler';
 import { MolScriptBuilder } from './language/builder';
-
-
-
-
-
-
-
-
-
+import { assertUnreachable } from '../mol-util/type-helpers';
 
 export { Script };
 
@@ -30,7 +22,14 @@ function Script(expression: string = '(sel.atom.all)', language: Script.Language
 }
 
 namespace Script {
-    export type Language = 'mol-script' | 'pymol' | 'vmd' | 'jmol' | 'rasmol'
+    export const Info = {
+        'mol-script': 'Mol-Script',
+        'pymol': 'PyMOL',
+        'vmd': 'VMD',
+        'jmol': 'Jmol',
+        'rasmol': 'Rasmol'
+    };
+    export type Language = keyof typeof Info;
 
     export function is(x: any): x is Script {
         return !!x && typeof (x as Script).expression === 'string' && !!(x as Script).language;
@@ -43,20 +42,17 @@ namespace Script {
     export function toExpression(script: Script): Expression {
         switch (script.language) {
             case 'mol-script':
-	    const parsed = parseMolScript(script.expression);
-	    if (parsed.length === 0) throw new Error('No query');
-	    return transpileMolScript(parsed[0]);
+                const parsed = parseMolScript(script.expression);
+                if (parsed.length === 0) throw new Error('No query');
+                return transpileMolScript(parsed[0]);
             case 'pymol':
-	    return parse('pymol', script.expression) as Expression;
             case 'jmol':
-	    return parse('jmol', script.expression) as Expression;
             case 'vmd':
-	    return parse('vmd', script.expression) as Expression;
             case 'rasmol':
-	    return parse('rasmol', script.expression) as Expression;
+                return parse(script.language, script.expression);
+            default:
+                assertUnreachable(script.language);
         }
-
-        throw new Error('unsupported script language');
     }
 
     export function toQuery(script: Script): QueryFn<StructureSelection> {

+ 5 - 2
src/mol-script/transpile.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Koya Sakuma
  */
@@ -9,9 +9,11 @@
 
 import { Transpiler } from './transpilers/transpiler';
 import { _transpiler } from './transpilers/all';
+import { Expression } from './language/expression';
+import { Script } from './script';
 const transpiler: {[index: string]: Transpiler} = _transpiler;
 
-export function parse(lang: string, str: string) {
+export function parse(lang: Script.Language, str: string): Expression {
     try {
         const query = transpiler[lang](str);
 
@@ -25,5 +27,6 @@ export function parse(lang: string, str: string) {
         console.log(str);
         console.log(e.message);
         console.log('\n');
+        throw e;
     }
 }

+ 1 - 0
src/mol-script/transpilers/helper.ts

@@ -3,6 +3,7 @@
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
+ * @author Koya Sakuma
  */
 // import * as Q from 'parsimmon';
 import * as P from '../../mol-util/monadic-parser';

+ 14 - 13
src/mol-script/transpilers/pymol/properties.ts

@@ -3,6 +3,7 @@
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
+ * @author Koya Sakuma
  */
 
 import { MolScriptBuilder } from '../../../mol-script/language/builder';
@@ -19,26 +20,26 @@ function rangeMap(x: string) {
     return { min, max };
 }
 function listOrRangeMap(x: string) {
-    if (x.includes('-') && x.includes('+')){
-	const pSplit = x.split('+').map(x => x.replace(/^["']|["']$/g, ''));
-        const res : number[] =[];
-        pSplit.forEach( x => {
-            if (x.includes('-')){
+    if (x.includes('-') && x.includes('+')) {
+        const pSplit = x.split('+').map(x => x.replace(/^["']|["']$/g, ''));
+        const res: number[] = [];
+        pSplit.forEach(x => {
+            if (x.includes('-')) {
                 const [min, max] = x.split('-').map(x=>parseInt(x));
-                for (var i = min;  i <= max;  i++){
+                for (let i = min; i <= max; i++) {
                     res.push(i);
                 }
-            }else{
+            } else {
                 res.push(parseInt(x));
             }
         });
         return res;
-    }else if(x.includes('-') && !x.includes('+')){
-        return rangeMap(x)
-    }else if(!x.includes('-') && x.includes('+')){
-        return listMap(x).map(x => parseInt(x))
-    }else{
-	return listMap(x).map(x => parseInt(x))
+    } else if (x.includes('-') && !x.includes('+')) {
+        return rangeMap(x);
+    } else if (!x.includes('-') && x.includes('+')) {
+        return listMap(x).map(x => parseInt(x));
+    } else {
+        return listMap(x).map(x => parseInt(x));
     }
 }
 

+ 5 - 5
src/mol-script/transpilers/rasmol/keywords.ts

@@ -20,7 +20,7 @@ function nucleicExpr() {
     return B.struct.combinator.merge([
         B.struct.generator.atomGroups({
 	  'residue-test': B.core.set.has([
-              B.core.type.set(['G', 'C', 'A', 'T', 'U', 'I', 'DG', 'DC', 'DA', 'DT', 'DU', 'DI', '+G', '+C', '+A', '+T', '+U', '+I']),
+                B.core.type.set(['G', 'C', 'A', 'T', 'U', 'I', 'DG', 'DC', 'DA', 'DT', 'DU', 'DI', '+G', '+C', '+A', '+T', '+U', '+I']),
                 B.ammp('label_comp_id')
 	  ])
         }),
@@ -29,11 +29,11 @@ function nucleicExpr() {
                 'group-by': B.ammp('residueKey')
 	    }),
 	    test: B.core.logic.and([
-              B.core.set.isSubset([
+                B.core.set.isSubset([
                 // B.core.type.set([ 'P', 'O1P', 'O2P' ]),
-                h.atomNameSet(['P']),
-                B.ammpSet('label_atom_id')
-              ]),
+                    h.atomNameSet(['P']),
+                    B.ammpSet('label_atom_id')
+                ]),
 	    ])
         }),
         B.struct.filter.pick({

+ 45 - 44
src/mol-script/transpilers/rasmol/parser.ts

@@ -19,7 +19,7 @@ import { operators } from './operators';
 import { keywords } from './keywords';
 import { AtomGroupArgs } from '../types';
 import { Transpiler } from '../transpiler';
-//import { OperatorList } from '../types';
+// import { OperatorList } from '../types';
 
 // const propertiesDict = h.getPropertyRules(properties);
 
@@ -27,10 +27,10 @@ import { Transpiler } from '../transpiler';
 
 const propertiesDict = h.getPropertyRules(special_properties);
 
-//const slash = P.MonadicParser.string('/');
+// const slash = P.MonadicParser.string('/');
 const dot = P.MonadicParser.string('.');
 const colon = P.MonadicParser.string(':');
-//const comma = P.MonadicParser.string(',');
+// const comma = P.MonadicParser.string(',');
 const star = P.MonadicParser.string('*');
 const bra = P.MonadicParser.string('(');
 const ket = P.MonadicParser.string(')');
@@ -97,87 +97,88 @@ const lang = P.MonadicParser.createLanguage({
         return P.MonadicParser.alt(
 	    // :A.CA :.CA :A
             colon.then(P.MonadicParser.alt(
-                P.MonadicParser.seq(                
+                P.MonadicParser.seq(
                     orNull(propertiesDict.chain).skip(dot),
                     orNull(propertiesDict.name)
-                ).map(x => { return { chain: x[0], name: x[1]}; }),
+                ).map(x => { return { chain: x[0], name: x[1] }; }),
                 P.MonadicParser.seq(
                     orNull(propertiesDict.name).skip(dot)
-                ).map(x => { return {name: x[0] }; }),
-		P.MonadicParser.seq(
+                ).map(x => { return { name: x[0] }; }),
+                P.MonadicParser.seq(
                     orNull(propertiesDict.chain)
-                ).map(x => { return {chain: x[0] }; }),
+                ).map(x => { return { chain: x[0] }; }),
             )),
 	    // *A.CA *.CA
 	    star.then(P.MonadicParser.alt(
-                P.MonadicParser.seq(                
+                P.MonadicParser.seq(
                     orNull(propertiesDict.chain).skip(dot),
                     orNull(propertiesDict.name)
-                ).map(x => { return { chain: x[0], name: x[1]}; }),
+                ).map(x => { return { chain: x[0], name: x[1] }; }),
                 P.MonadicParser.seq(
                     orNull(propertiesDict.name).skip(dot)
-                ).map(x => { return {name: x[0] }; }),
-		P.MonadicParser.seq(
+                ).map(x => { return { name: x[0] }; }),
+                P.MonadicParser.seq(
                     orNull(propertiesDict.chain)
-                ).map(x => { return {chain: x[0] }; }),
+                ).map(x => { return { chain: x[0] }; }),
             )),
 	    // 1-100,201
 	    bra.then(P.MonadicParser.alt(
-		P.MonadicParser.alt(
+                P.MonadicParser.alt(
 		    P.MonadicParser.seq(
-			propertiesDict.resi.skip(ket),
+                        propertiesDict.resi.skip(ket),
 		    ).map(x => {
-			return { resi: x[0] }		
-			;})
-		)
+                        return { resi: x[0] }
+                        ;
+                    })
+                )
 	    )),
 	    //  lys:a.ca lys:a lys lys.ca
 	    P.MonadicParser.alt(
-		P.MonadicParser.alt(
+                P.MonadicParser.alt(
                     P.MonadicParser.seq(
-			orNull(propertiesDict.resn).skip(colon),
-			orNull(propertiesDict.chain).skip(dot),
-			orNull(propertiesDict.name)
+                        orNull(propertiesDict.resn).skip(colon),
+                        orNull(propertiesDict.chain).skip(dot),
+                        orNull(propertiesDict.name)
                     ).map(x => { return { resn: x[0], chain: x[1], name: x[2] }; }),
 		    P.MonadicParser.seq(
-			orNull(propertiesDict.resn).skip(star),
-			orNull(propertiesDict.chain).skip(dot),
-			orNull(propertiesDict.name)
+                        orNull(propertiesDict.resn).skip(star),
+                        orNull(propertiesDict.chain).skip(dot),
+                        orNull(propertiesDict.name)
                     ).map(x => { return { resn: x[0], chain: x[1], name: x[2] }; }),
                     P.MonadicParser.seq(
-			orNull(propertiesDict.resn).skip(colon),
-			orNull(propertiesDict.chain),
+                        orNull(propertiesDict.resn).skip(colon),
+                        orNull(propertiesDict.chain),
                     ).map(x => { return { resn: x[0], chain: x[1] }; }),
 		    P.MonadicParser.seq(
-			orNull(propertiesDict.resn).skip(star),
-			orNull(propertiesDict.chain),
+                        orNull(propertiesDict.resn).skip(star),
+                        orNull(propertiesDict.chain),
                     ).map(x => { return { resn: x[0], chain: x[1] }; }),
 		    P.MonadicParser.seq(
-			orNull(propertiesDict.resn).skip(dot),
-			orNull(propertiesDict.name),
+                        orNull(propertiesDict.resn).skip(dot),
+                        orNull(propertiesDict.name),
                     ).map(x => { return { resn: x[0], name: x[1] }; }),
 		    P.MonadicParser.seq(
-			orNull(propertiesDict.resn),
-		    ).map(x => {  return { resn: x[0] };}),
-		)
-	    )		  
-	    
-	)
-		    
+                        orNull(propertiesDict.resn),
+		    ).map(x => { return { resn: x[0] }; }),
+                )
+	    )
+
+        );
+
     },
 
     ObjectProperty: () => {
-	const w = h.getReservedWords(special_properties, special_keywords, special_operators)
-              .sort(h.strLenSortFn).map(h.escapeRegExp).join('|');
+        const w = h.getReservedWords(special_properties, special_keywords, special_operators)
+            .sort(h.strLenSortFn).map(h.escapeRegExp).join('|');
         return P.MonadicParser.regexp(new RegExp(`(?!(${w}))[A-Z0-9_]+`, 'i'));
     },
 
     ObjectProperty2: () => {
-	const w = h.getReservedWords(properties, keywords, operators)
-              .sort(h.strLenSortFn).map(h.escapeRegExp).join('|');
+        const w = h.getReservedWords(properties, keywords, operators)
+            .sort(h.strLenSortFn).map(h.escapeRegExp).join('|');
         return P.MonadicParser.regexp(new RegExp(`(?!(${w}))[A-Z0-9_]+`, 'i'));
     },
-    
+
     Object: (r: any) => {
         return r.ObjectProperty2
             .map((x: any) => { throw new Error(`property 'object' not supported, value '${x}'`); });
@@ -201,7 +202,7 @@ const lang = P.MonadicParser.createLanguage({
             r.Expression
         ).trim(P.MonadicParser.optWhitespace);
     }
-    
+
 });
 
 export const transpiler: Transpiler = str => lang.Query.tryParse(str);

+ 1 - 1
src/mol-script/transpilers/rasmol/properties.ts

@@ -456,7 +456,7 @@ export const properties: PropertyDict = {
     hoge: {
         '@desc': 'PDB residue number, not including insertion code (see also seqcode, below)',
         '@examples': ['resno = 100'],
-//        isNumeric: true,
+        //        isNumeric: true,
         regex: /-?[0-9]+/, map: x => parseInt(x),
         level: 'residue-test', property: B.ammp('auth_seq_id')
 

+ 25 - 24
src/mol-script/transpilers/rasmol/special_properties.ts

@@ -3,13 +3,14 @@
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Panagiotis Tourlas <panagiot_tourlov@hotmail.com>
+ * @author Koya Sakuma
  */
 
 import { MolScriptBuilder } from '../../../mol-script/language/builder';
 const B = MolScriptBuilder;
 import { PropertyDict } from '../types';
 
-//const reFloat = /[-+]?[0-9]*\.?[0-9]+/;
+// const reFloat = /[-+]?[0-9]*\.?[0-9]+/;
 // const rePosInt = /[0-9]+/;
 
 function atomNameListMap(x: string) { return x.split(',').map(B.atomName); }
@@ -19,45 +20,45 @@ function rangeMap(x: string) {
     return { min, max };
 }
 function listOrRangeMap(x: string) {
-    if (x.includes('-') && x.includes(',')){
-	const pSplit = x.split(',').map(x => x.replace(/^["']|["']$/g, ''));
-	console.log(pSplit)
-	const res : number[] =[];
-	pSplit.forEach( x => {
-	    if (x.includes('-')){
-		const [min, max] = x.split('-').map(x=>parseInt(x));
-		for (var i = min;  i <= max;  i++){
+    if (x.includes('-') && x.includes(',')) {
+        const pSplit = x.split(',').map(x => x.replace(/^["']|["']$/g, ''));
+        console.log(pSplit);
+        const res: number[] = [];
+        pSplit.forEach(x => {
+	    if (x.includes('-')) {
+                const [min, max] = x.split('-').map(x=>parseInt(x));
+                for (let i = min; i <= max; i++) {
 		    res.push(i);
-		}		 
-	    }else{
-		res.push(parseInt(x));
+                }
+	    } else {
+                res.push(parseInt(x));
 	    }
-	});
-	return res;		    	
-    }else if(x.includes('-') && !x.includes(',')){
-	return rangeMap(x)
-    }else if(!x.includes('-') && x.includes(',')){
-	return listMap(x).map(x => parseInt(x));
-    }else{	
-	return parseInt(x);
+        });
+        return res;
+    } else if (x.includes('-') && !x.includes(',')) {
+        return rangeMap(x);
+    } else if (!x.includes('-') && x.includes(',')) {
+        return listMap(x).map(x => parseInt(x));
+    } else {
+        return parseInt(x);
     }
 }
 function elementListMap(x: string) {
     return x.split(',').map(B.struct.type.elementSymbol);
 }
 
-//const sstrucDict: { [k: string]: string } = {
+// const sstrucDict: { [k: string]: string } = {
 //    H: 'helix',
 //    S: 'beta',
 //    L: 'none'
-//};
-//function sstrucListMap(x: string) {
+// };
+// function sstrucListMap(x: string) {
 //    return {
 //        flags: B.struct.type.secondaryStructureFlags(
 //            x.toUpperCase().split('+').map(ss => sstrucDict[ss] || 'none')
 //        )
 //    };
-//}
+// }
 
 export const special_properties: PropertyDict = {
     symbol: {

+ 8 - 15
src/mol-util/monadic-parser.ts

@@ -23,7 +23,6 @@ export class MonadicParser<A> {
         return { success: false, index: makeLineColumnIndex(input, result.furthest), expected: result.expected };
     };
 
-
     tryParse(str: string) {
         const result = this.parse(str);
         if (result.success) {
@@ -121,7 +120,6 @@ export class MonadicParser<A> {
         return MonadicParser.seq(this.times(n), this.many()).map(r => [...r[0], ...r[1]]);
     };
 
-
     map<B>(f: (a: A) => B): MonadicParser<B> {
         return new MonadicParser((input, i) => {
             const result = this._(input, i);
@@ -239,16 +237,15 @@ export namespace MonadicParser {
 
     export type Result<T> = Success<T> | Failure
 
-
-    export function seqMap(a: MonadicParser<any>, b: MonadicParser<any>, c: any) {
+    export function seqMap<A, B>(a: MonadicParser<A>, b: MonadicParser<B>, c: any) {
         const args = [].slice.call(arguments);
         if (args.length === 0) {
-	    throw new Error('seqMap needs at least one argument');
+            throw new Error('seqMap needs at least one argument');
         }
         const mapper = args.pop();
         assertFunction(mapper);
         return seq.apply(null, args).map(function (results: any) {
-	    return mapper.apply(null, results);
+            return mapper.apply(null, results);
         });
     }
 
@@ -262,7 +259,6 @@ export namespace MonadicParser {
         return language;
     }
 
-
     export function seq<A>(a: MonadicParser<A>): MonadicParser<[A]>
     export function seq<A, B>(a: MonadicParser<A>, b: MonadicParser<B>): MonadicParser<[A, B]>
     export function seq<A, B, C>(a: MonadicParser<A>, b: MonadicParser<B>, c: MonadicParser<C>): MonadicParser<[A, B, C]>
@@ -342,11 +338,10 @@ export namespace MonadicParser {
         return RegExp('^(?:' + re.source + ')', flags(re));
     }
 
-
     export function regexp(re: RegExp, group = 0) {
         const anchored = anchoredRegexp(re);
         const expected = '' + re;
-        return new MonadicParser<any>(function (input: any, i: any) {
+        return new MonadicParser((input, i) => {
             const match = anchored.exec(input.slice(i));
             if (match) {
                 if (0 <= group && group <= match.length) {
@@ -476,12 +471,12 @@ export namespace MonadicParser {
     export const newline = alt(crlf, lf, cr).desc('newline');
     export const end = alt(newline, eof);
 
-    export function of(A: any) {
-        return succeed(A);
+    export function of<A>(value: A) {
+        return succeed(value);
     }
 
-    export function regex(A: any) {
-        return regexp(A);
+    export function regex(re: RegExp) {
+        return regexp(re);
     }
 
     MonadicParser.createLanguage = createLanguage;
@@ -594,5 +589,3 @@ function assertFunction(x: any) {
         throw new Error('not a function: ' + x);
     }
 }
-
-