فهرست منبع

Refining VMD parser and properties

yakomaxa 2 سال پیش
والد
کامیت
d45367e840

+ 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: 'vmd', expression: 'all' }),
         label: PD.Optional(PD.Text(''))
     })
 })({

+ 7 - 1
src/mol-script/transpilers/vmd/parser.ts

@@ -121,12 +121,18 @@ const lang = P.MonadicParser.createLanguage({
 
     Expression: function (r:any) {
         return P.MonadicParser.alt(
-            r.RangeListProperty,
+	    r.NamedAtomProperties,
+	    r.RangeListProperty,
             r.ValueQuery,
             r.Keywords,
+	    r.ValueRange,
         );
     },
 
+    NamedAtomProperties: function () {
+        return P.MonadicParser.alt(...h.getNamedPropertyRules(properties));
+    },
+
     Keywords: () => P.MonadicParser.alt(...h.getKeywordRules(keywords)),
 
     ValueRange: function (r:any) {

+ 26 - 13
src/mol-script/transpilers/vmd/properties.ts

@@ -11,7 +11,20 @@ import { PropertyDict } from '../types';
 
 const reFloat = /[-+]?[0-9]*\.?[0-9]+/;
 const rePosInt = /[+]?[0-9]+/;
-const reInt = /[-+]?[0-9]+/;
+const reInt = /[-+]?[0-9] +/;
+
+function atomNameListMapVMD(x: string) { return x.split(' ').map(B.atomName); };
+function listMapVMD(x: string) { return x.split(' ').map(x => x.replace(/^["']|["']$/g, '')); }
+function rangeMapVMD(x: string) {
+    const [min, max] = x.split('\sto\s').map(x => parseInt(x));
+    return { min, max };
+}
+function listOrRangeMapVMD(x: string) {
+    return x.includes('to') ? rangeMapVMD(x) : listMapVMD(x).map(x => parseInt(x));
+}
+//function elementListMap(x: string) {
+//    return x.split('+').map(B.struct.type.elementSymbol); 
+//}
 
 function str(x: string) { return x; }
 
@@ -34,7 +47,7 @@ export const properties: PropertyDict = {
     name: {
         '@desc': 'str    atom name',
         '@examples': ['name CA'],
-        regex: /[a-zA-Z0-9]+/, map: B.atomName,
+        regex: /[a-zA-Z0-9 ]+/, map: atomNameListMapVMD ,
         level: 'atom-test', property: B.ammp('label_atom_id')
     },
     type: {
@@ -80,16 +93,23 @@ export const properties: PropertyDict = {
     chain: {
         '@desc': 'str  the one-character chain identifier',
         '@examples': ['chain A'],
-        regex: /[a-zA-Z0-9]+/, map: str,
-        level: 'residue-test', property: B.ammp('auth_asym_id')
+        regex: /[a-zA-Z0-9 ]+/, map :listMapVMD,
+        level: 'chain-test', property: B.ammp('auth_asym_id')
     },
     residue: {
         '@desc': 'num  a set of connected atoms with the same residue number',
         '@examples': ['residue < 11', 'residue 11'],
         isNumeric: true,
-        regex: reInt, map: x => parseInt(x),
+        regex: reInt, map: x => listOrRangeMapVMD,
         level: 'residue-test', property: B.ammp('auth_seq_id')
     },
+    resid: {
+        '@desc': 'num  residue id',
+        '@examples': ['resid 42'],
+        isNumeric: true,
+        regex: reInt, map: x => parseInt(x),
+        level: 'residue-test', property: B.ammp('auth_seq_id')
+    },  
     fragment: {
         '@desc': 'num  a set of connected residues',
         '@examples': ['fragment 42'],
@@ -131,16 +151,9 @@ export const properties: PropertyDict = {
     resname: {
         '@desc': 'str  residue name',
         '@examples': ['resname ALA'],
-        regex: /[a-zA-Z0-9]+/, map: str,
+        regex: /[a-zA-Z0-9 ]+/, map: atomNameListMapVMD,
         level: 'residue-test', property: B.ammp('auth_comp_id')
     },
-    resid: {
-        '@desc': 'num  residue id',
-        '@examples': ['resid 42'],
-        isNumeric: true,
-        regex: reInt, map: x => parseInt(x),
-        level: 'residue-test', property: B.ammp('auth_seq_id')
-    },
     segname: {
         '@desc': 'str  segment name',
         '@examples': ['segname B'],

+ 41 - 1
src/mol-util/monadic-parser.ts

@@ -339,7 +339,13 @@ export namespace MonadicParser {
     }
 
    
-    export function regexp(re: RegExp, group = 0) {
+    export function regexp(re: RegExp, group=0) {
+	assertRegexp(re);
+	if (arguments.length >= 2) {
+	    assertNumber(group);
+	} else {
+	    group = 0;
+	}
         const anchored = anchoredRegexp(re);
         const expected = '' + re;
         return new MonadicParser<any>( function (input:any, i:any){
@@ -589,4 +595,38 @@ function assertFunction(x:any) {
     }
 }
 
+function assertNumber(x:any) {
+  if (typeof x !== "number") {
+    throw new Error("not a number: " + x);
+  }
+}
 
+function assertRegexp(x:any) {
+  if (!(x instanceof RegExp)) {
+    throw new Error("not a regexp: " + x);
+  }
+  var f = flags(x);
+  for (var i = 0; i < f.length; i++) {
+    var c = f.charAt(i);
+    // Only allow regexp flags [imus] for now, since [g] and [y] specifically
+    // mess up Parsimmon. If more non-stateful regexp flags are added in the
+    // future, this will need to be revisited.
+    if (c !== "i" && c !== "m" && c !== "u" && c !== "s") {
+      throw new Error('unsupported regexp flag "' + c + '": ' + x);
+    }
+  }
+}
+
+function flags(re:RegExp) {
+  if (re.flags !== undefined) {
+    return re.flags;
+  }
+  // legacy browser support
+  return [
+    re.global ? "g" : "",
+    re.ignoreCase ? "i" : "",
+    re.multiline ? "m" : "",
+    re.unicode ? "u" : "",
+    re.sticky ? "y" : ""
+  ].join("");
+}