/* * Copyright (c) 2006-2009 Sun Microsystems, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ ometa LKFileParser <: Parser { isLKParser -> true, /* helper productions */ log :msg -> { console.log(msg); true }, logPos -> { console.log(this.pos()); true }, whereAreYou -> { var charsBefore = 120; var charsAfter = 120; var src = this._originalInput; var startIndex = Math.max(0, this.pos() - charsBefore); var stopIndex = Math.min(src.length, this.pos() + charsAfter); console.log(src.substring(startIndex, this.pos()) + '<--I am here-->' + src.substring(this.pos(), stopIndex)); console.log('Rules: ' + this._ruleStack); console.log('Stack: ' + this.stack); true }, fromTo :x :y = seq(x) (~seq(y) char)*:cs seq(y) -> cs, stackSize -> { this.stack.length }, num :x -> { this.stack.select(function(ea) { return ea === x }).length }, getStack -> { this.stack.clone() }, assignStack :s -> { this.stack = s }, startTime -> { /*console.log('start measuring time'); new Date().getTime()*/ }, stopTime :t -> { /*console.log('time: ' + (new Date().getTime()-t) + ' ms');*/ true}, open :x = add(x) -> { x}, close :x :y = add(y) remove(y) remove(x) -> { y}, add :x = exactly(x) -> { this.stack.push(x) }, remove :x -> { if (this.stack.length == 0) { this.whereAreYou(); throw new Error('Stack is empty, cannot remove ' + x); }; var rem = this.stack.pop(); if (rem !== x) { this.whereAreYou(); throw new Error('Unmatched ' + x + 'at: ' + this.pos() + ' instead found ' + rem + '; stack: ' + this.stack); }; true }, everythingBut :x :y = ~exactly(x) ~exactly(y) anything:a -> a, nonRecursive :x :y = getStack:s open(x) everythingBut(x,y)*:a close(x, y) -> { x+ a.join('') + y } | assignStack(s) -> { this._manualFail() }, recursive :x :y = getStack:s open(x) ( everythingBut(x,y) | recursive(x,y) )*:a close(x, y) -> { x+ a.join('') + y } | assignStack(s) -> { this._manualFail() }, chunk :x :y = basicChunk(x,y):a /*~~exactly(x) (nonRecursive(x,y) | recursive(x,y)):a*/ -> a, /* chunkWith :x :y :p = ~~exactly(x) ( open(x) num(x):n ( apply(p, 'a') )*:a close(x,y) -> { x+ a.join('') + y } | remove(x) ),*/ somethingRelated = ~end (~'\n' ~'\r' ~';' anything)*, somethingBigRelated = ~end ( chunk('(', ')') | chunk('{', '}') | chunk('[', ']') | chunk('\'', '\'') | chunk('"', '"') | spaces '+' spaces | ~',' ~';' ~'(' ~'{' ~'[' ~'\'' ~'"' ~nl anything )*, defEnd = ';' '\n' | ';' spaces | "", classElemDefEnd = "," | "", /* basic string processing */ space = super(#space) | fromTo('//', '\n') | fromTo('/*', '*/'), nl = '\n' | '\r' -> '\n', spacesNoNl = (~nl space)*:spcs -> spcs, nameFirst = letter | '$' | '_', nameRest = nameFirst | digit, iName = firstAndRest(#nameFirst, #nameRest):r -> r.join(''), isKeyword :x = ?BSJSParser._isKeyword(x), name = iName:n /*~isKeyword(n)*/ -> n, keyword = iName:k isKeyword(k) -> k, namespaceIdSplitted = name:n '.' namespaceIdSplitted:r -> { [n].concat(r) } | name:n -> { [n] }, namespaceId = namespaceIdSplitted:nArr -> nArr.join('.'), nsFollowedBy :x = namespaceIdSplitted:nArr ?(nArr.last() === x) -> nArr.slice(0,nArr.length-1).join('.'), nsWith :x = namespaceIdSplitted:nArr ?nArr.include(x) -> { var i = nArr.indexOf(x); { before: nArr.slice(0,i).join('.'), after: nArr.slice(i+1,nArr.length).join('.') } }, /* functions */ basicFunction = "function" spaces (name:n | empty) chunk('(', ')') spaces chunk('{', '}') -> n, func = basicFunction:fn | "var" (space)+ name:fn spaces '=' spaces basicFunction -> fn, functionDef = pos:p ( func:fn | '(' func:fn ')' ) somethingRelated defEnd -> { this._fragment(fn, 'functionDef', p, this.pos()-1) }, staticProperty = pos:p namespaceIdSplitted:nsArr ?(nsArr.length > 1) spaces '=' somethingBigRelated defEnd -> { this._fragment( nsArr.last(), 'staticProperty', p, this.pos()-1, null, { className: nsArr.slice(0,nsArr.length-1).join('.'), _isStatic: true } )}, /* methods */ methodModificationDef = pos:p nsWith('prototype'):spec spaces '=' spaces somethingBigRelated defEnd -> {this._fragment( spec.after, 'methodModificationDef', p, this.pos()-1, null, {className: spec.before, _isStatic: false})}, /* properties */ protoDef = stackSize:s pos:p name:pName ':' spaces (basicFunction (somethingBigRelated | empty ) | somethingBigRelated ) classElemDefEnd -> { if (this.stack.length !== s) { throw new Error(Strings.format( 'sth wrong with the stack: %s expected length: %s actual length: %s', this.stack, s, this.stack.length)); }; this._fragment(pName, 'protoDef', p, this.pos()-1, null, {_isStatic: false}) }, propertyDef = (protoDef | methodModificationDef | staticProperty): spec -> {spec.type = 'propertyDef'; spec}, /* class stuff */ classElems = '{' (spaces propertyDef:pD spaces -> pD)*:a '}' -> a, restKlassDef = ',' spaces classElems:descriptors -> { {classElems: descriptors} } | ',' spaces klass:trait ',' spaces classElems:descriptors -> { {trait: trait, classElems: descriptors} } | ',' klassName:trait -> { {trait: trait, classElems: []} } | spaces -> { {classElems: []} }, klass = namespaceId, klassName = spaces ( '\'' | '"') klass:n ( '\'' | '"') spaces -> n, klassDef = pos:p nsFollowedBy("subclass"):sName '(' klassName:kName restKlassDef:spec ')' defEnd -> { spec.classElems.forEach(function(ea) { ea.className = kName }); this._fragment( kName, 'klassDef', p, this.pos()-1, spec.classElems, {trait: spec.trait, superclassName: sName}) }, basicKlassExt = "Object.extend" '(' klass:n restKlassDef:spec ')' -> { spec.classElems.forEach(function(ea) { ea.className = n; ea._isStatic = true }); {name: n, trait: spec.trait, subElements: spec.classElems } } | (nsFollowedBy("addMethods"):n | nsFollowedBy("addProperties"):n) '(' classElems:clElems ')' -> { clElems.forEach(function(ea) { ea.className = n; ea._isStatic = false }); {name: n, subElements: clElems } }, klassExtensionDef = pos:p basicKlassExt:spec defEnd -> { this._fragment(spec.name + ' (extension)', 'klassExtensionDef', p, this.pos()-1, spec.subElements, {trait: spec.trait}) }, /* 'object' stuff */ restObjDef = classElems:propsAndMethodDescrs spaces -> { propsAndMethodDescrs.forEach(function(ea) { ea._isStatic = true }); propsAndMethodDescrs }, objectDef = pos:p ( "var" spaces namespaceId:o | namespaceId:o) spaces '=' spaces restObjDef:propsAndMethodDescrs defEnd -> { this._fragment(o, 'objectDef', p, this.pos()-1, propsAndMethodDescrs) }, /* ometa stuff */ ometaParameter = ':' name:n spaces -> n, ometaParameters = ometaParameter*, ometaRuleDef = pos:p name:n spaces ometaParameters:a ('=' | "->") (~',' (chunk('(', ')') | chunk('{', '}') | chunk('\'', '\'') | chunk('"', '"') | ~'}' anything))*:body ( ',' | empty ) -> { this._fragment(n, 'ometaRuleDef', p, this.pos()-1, [], {parameters: a}) }, ometaInherit = "<:" spaces name:sn -> sn | empty -> { null }, ometaDef = pos:p "ometa" spaces name:n space ometaInherit:sn spaces '{' spaces (ometaRuleDef:d spaces -> d)*:defs spaces '}' defEnd -> { this._fragment(n, 'ometaDef', p, this.pos()-1, defs, {superclassName: sn}) }, /* otherStuff */ // comment = descriptor:d spacesNoNl /*(fromTo('//', '\n') | fromTo('/*', '*/')) spacesNoNl*/ -> { d.stopIndex = this.pos()-1; d.type = 'comment'; d }, comment = pos:p space+ -> { this._fragment(null, 'comment', p, this.pos()-1) }, blankLine = pos:p (nl | (char:c ?(c.charCodeAt(0) === 32))* nl) -> { this._fragment(null, 'blankLine', p, this.pos()-1) }, unknown = pos:p somethingBigRelated defEnd -> { this._fragment(null, 'unknown', p, this.pos()-1) } }; LKFileParser.stack = []; LKFileParser._manualFail = function() { throw Global.fail }; LKFileParser._fragment = function(name, type, startIndex, stopIndex, subElems, custom) { var klass = lively.ide.FileFragment; // to convince ometa, fix needed here var ff = new klass(name, type, startIndex, stopIndex, null, subElems); if (custom) Object.extend(ff, custom); return ff; }; LKFileParser;