/* Copyright 2011 University of Surrey Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ //A parser for Semantics of Business Vocabulary and Rules (SBVR), an OMG standard. //Converts from Structured English (SBVR-SE) to Logical Formulation (SBVR-LF) //Specification: http://www.omg.org/spec/SBVR/1.0/ ometa SBVRParser { isTerm :x = ?this._isTerm(x), isVerb :x = ?this._isVerb(x), isKwrd :x = ?SBVRParser._isKwrd(x), isFctp :x = ?this._isFctp(x), isKwrdSt :p :q = &(spaces seq(q) word:w) {q!=''?q+' '+w:w}:w {p+' '+w}:n (isKwrd(n) -> true | isKwrdSt(p, w) -> true), isTermSt :p :q = &(spaces seq(q) word:w) {q!=''?q+' '+w:w}:w {p+' '+w}:n (isTerm(n) -> true | isTermSt(p, w) -> true), isVerbSt :p :q = &(spaces seq(q) word:w) {q!=''?q+' '+w:w}:w {p+' '+w}:n (isVerb(n) -> true | isVerbSt(p, w) -> true), findVar :x = {this.ruleVars[x[1]]}, bind :x = findVar(x):y -> [`bind, x, y], spaces = (' '|'\t')*, letters = letter+:l -> l.join(''), num = spaces? digit+:n -> ['num', parseInt(n.join(''))] | "one" -> ['num', 1], word = spaces? letters:w ~isVerb(w) ~isTerm(w) ~isKwrd(w) -> w, nrText = (word:w ~isKwrdSt(w,'')~isTermSt(w,'')~isVerbSt(w,'') ->w)+:w -> w.join(' '), text = (spaces letters:w)+:w -> w.join(' '), toEOL = (anything:w ?{w!='\n'&&w!='\r'}->w)*:w -> $.trim(w.join('')), kwrd :x = spaces? letters:w {x!=''?x+' '+w:w}:w (isKwrd(w) -> w | kwrd(w):a -> a), token :x = kwrd(''):t ?(t==x) -> [`kwrd, t], mkTerm = nrText:t {this.terms[t] = true}, termR :x = spaces? letters:w {x!=''?x+' '+w:w}:w (isTerm(w) -> w | termR(w):a -> a), term = termR(''):t -> ['term', this._termForm(t)], mkVerb = nrText:x {this.verbs[x] = true}, verbR :x = spaces? letters:w {x!=''?x+' '+w:w}:w (isVerb(w) -> w | verbR(w):a -> a), verb = verbR(''):v -> ['verb', this._verbForm(v)], quant = "each" -> [`univQ] | ("a"|"an"|"some") -> [`existQ] | "at" "most" num:n -> [`atMostQ, [`maxCard, n]] | "at" "least" num:n ( "and" "at" "most" num:m -> [`numRngQ, [`minCard, n], [`maxCard, m]] | empty -> [`atLeastQ, [`minCard, n]]) | "more" "than" num:n {++n[1]} -> [`atLeastQ, [`minCard, n]] | "exactly" num:n -> [`exactQ, [`card, n]], keyword :x = token(x), adVar :x = {this.ruleVars[x[1]] = this.ruleVarsCount++} {['var', ['num', this.ruleVars[x[1]]], x]}:v ( ( keyword("that") keyword("the") terbRi([[]], x) | keyword("that") qTerbRi([[]], x) ):q {v.push(q)} | empty ) -> v, atfo :c = {alert(c[0])} isFctp(c[0]) {c[0] = [`fcTp].concat(c[0])} -> {d = [`aFrm]; d.concat(c)}, terbRi :c :i = term:t verb:v bind(t):b {c[0]=c[0].concat([t,v]);c.concat([b])}:c (qTerbRi(c,i)), qTerbRi :c :i = (quant:q term:t adVar(t):a verb:v bind(t):b {q=q.concat([a]);c[0]=c[0].concat([t,v]);c.concat([b])}:c (qTerbRi(c,i)):r -> q.concat([r]) | verb:v bind(i):b {c[0]=c[0].concat([i,v]);c.concat([b])}:c (atfo(c)|qTerm(c)|qTerbR(c)) | bind(i):b {c[0]=c[0].concat([i]);c.concat([b])}:c atfo(c) ), qTerm :c = quant:q term:t adVar(t):a bind(t):b {q=q.concat([a]);c[0]=c[0].concat([t]);c.concat([b])}:c atfo(c):r -> q.concat([r]), qTerbR :c = {alert('in ' + c)} quant:q term:t adVar(t):a verb:v bind(t):b {q=q.concat([a]);c[0]=c[0].concat([t,v]);c.concat([b])}:c {c.concat([])}:d {alert('end ' + d)} (atfo(c)|qTerm(c)| {alert('out ' + d)} qTerbR(d)):r -> q.concat([r]), modRule = seq('It is obligatory that') -> ['obl'] | seq('It is necessary that') -> ['nec'] | seq('It is prohibited that') -> ['obl', ['neg']] | seq('It is impossible that') -> ['nec', ['neg']] | seq('It is not possible that') -> ['nec', ['neg']] | seq('It is possible that') -> ['pos'] | seq('It is permissible that') -> ['prm'], ruleDecl = seq('R:'), newRule = ruleDecl spaces? &((~'\n' char)*):a {this.ruleVarsCount=1} modRule:r qTerbR([[]]):q {r.length == 2 ? r[1][1] = q : r[1] = q} -> ['rule', r, [`text, a.join('')]], terb = term:t &mkVerb? verb:v -> [t, v], fcTpDecl = seq('F:'), fcTp = fcTpDecl spaces? {t=[]} (terb:b {t.concat(b)}:t)+ (term:e {t.concat([e])}:t)? {this.fctps[t] = true} -> [`fcTp].concat(t), termDecl = seq('T:'), newTerm = termDecl spaces? &mkTerm? term:t {t.push([])} -> t, attribute = ?{this.lines.length>0 && this.lines[this.lines.length-1][0] == 'term'} allowedAttrs:attrName toEOL:attrVal -> {var lastLine = this.lines.pop(); lastLine[2].push([attrName.replace(new RegExp(' ','g'),''), attrVal]); lastLine}, allowedAttrs = ( seq('Concept Type') | /* Custom Attributes */ seq('Database ID Field') | seq('Database Name Field') | seq('Database Table Name') | /* End Custom */ seq('Definition') | seq('Dictionary Basis') | seq('Example') | seq('General Concept') | seq('Namespace URI') | seq('Necessity') | seq('Note') | seq('Possibility') | seq('Reference Scheme') | seq('See') | seq('Source') | seq('Subject Field') | seq('Synonymous Form') | seq('Synonym') ):a ':' -> a, line = spaces? (newTerm | fcTp | newRule | attribute):l spaces? {this.lines.push(l)} -> l, linef = line (('\r'|'\n')*|end), expr = linef* -> [`model].concat(this.lines) } SBVRParser.keyTokens = ["termDecl", "fcTpDecl", "ruleDecl", "term", "modRule", "quant", "verb", "keyword", "allowedAttrs"]; SBVRParser.kwrds = {} kwrds = ["a", "an", "each", "at", "most", "least", "exactly", "that", "the", "one", "more", "than", "and", "some"] for (var idx = 0; idx < kwrds.length; idx++){SBVRParser.kwrds[kwrds[idx]] = true} SBVRParser._isKwrd = function(k) { return this.kwrds.hasOwnProperty(k) } /** Following are "instance" variables/functions rather than shared. */ SBVRParser.initialize = function() { this.reset(); } SBVRParser._isTerm = function(k) { return this.terms.hasOwnProperty(k) } SBVRParser._termForm = function(k) { return k } SBVRParser._isVerb = function(k) { if(this.verbs.hasOwnProperty(k)) { return true } else if( k.slice(0,3)=='are' && this.verbs.hasOwnProperty('is' + k.slice(3)) ) { return true } else if(k=='have' && this.verbs.hasOwnProperty('has')) { return true } else return false } SBVRParser._verbForm = function(k) { if(k.slice(0,3)=='are' && this.verbs.hasOwnProperty('is' + k.slice(3))){ return 'is' + k.slice(3) } else if(k=='have' && this.verbs.hasOwnProperty('has')) { return 'has'} else return k } SBVRParser._isFctp = function(k) { return this.fctps.hasOwnProperty(k) } SBVRParser.reset = function() { this.terms = {} this.verbs = {} this.fctps = {} this.ruleVars = {} this.ruleVarsCount = 0 this.lines = [] } model = 'T: student T: module T: lecturer F: student is registered for module with lecturer R: It is obligatory that each student is registered for a module with a lecturer' tree = SBVRParser.matchAll(model, "expr") eval(translateCode(readFile('PrettyTree'))) Prettify.match(tree,'elem') [model, [term, student, []], [term, module, []], [term, lecturer, []], [fcTp, [term, student], [verb, is registered for], [term, module], [verb, with], [term, lecturer]], [rule, [obl, [univQ, [var, [num, 1], [term, student]], [existQ, [var, [num, 3], [term, module]], [existQ, [var, [num, 4], [term, lecturer]], [aFrm, [fcTp, [term, student], [verb, is registered for], [term, module], [verb, with], [term, lecturer]], [bind, [term, student], 1], [bind, [term, module], 3], [bind, [term, lecturer], 4]]]]]], [text, It is obligatory that each student is registered for a module with a lecturer]]]