ometa LKFileParser <: Parser {
    isLKParser          -> true,
  
    /* helper productions */
    log :msg            -> { console.log(msg); true },
    logPos              -> { console.log($elf.pos()); true },
    whereAreYou         -> { var charsBefore = 120; var charsAfter = 120; var src = $elf._originalInput;
                             var startIndex = Math.max(0, $elf.pos() - charsBefore);
                             var stopIndex = Math.min(src.length, $elf.pos() + charsAfter);
                             console.log(src.substring(startIndex, $elf.pos()) + '<--I am here-->' + src.substring($elf.pos(), stopIndex));
                             console.log('Rules: ' + $elf._ruleStack);
                             console.log('Stack: ' + $elf.stack);
                             true
                            },
    fromTo :x :y        = seq(x) (~seq(y) char)*:cs seq(y)                              -> cs,
    stackSize           -> { $elf.stack.length },
	num :x              -> { $elf.stack.select(function(ea) { return ea === x }).length },
	getStack            -> { $elf.stack.clone() },
	assignStack :s      -> { $elf.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)	                                                    -> { /*console.log('opening ' + x);*/  x},
	close :x :y         = add(y) remove(y) remove(x) -> { /*console.log('closing ' + x + ' with ' + y + ' stack: ' + $elf.stack);*/ y},
	add :x              = exactly(x)		                                            -> { $elf.stack.push(x) },
	remove :x           -> { if ($elf.stack.length == 0) {
	                            $elf.whereAreYou();
	                            throw new Error('Stack is empty, cannot remove ' + x);
	                         };
	                         var rem = $elf.stack.pop();
	                         if (rem !== x) {
	                            $elf.whereAreYou();
	                            throw new Error('Unmatched ' + x + 'at: ' + $elf.pos() + ' instead found ' + rem + '; stack: ' + $elf.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)            -> { $elf._manualFail() },
	recursive :x :y     = getStack:s open(x) (  everythingBut(x,y) | recursive(x,y) )*:a close(x, y)    -> { x+ a.join('') + y }
	                    | assignStack(s)                         -> { $elf._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('"', '"') | spacesNoNl '+' spaces | ~',' ~';' ~nl anything)*,
	defEnd              = ';' '\n' | ';' spacesNoNl | "",
	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 | "") chunk('(', ')') spaces chunk('{', '}')  -> n,
    func                = basicFunction:fn | "var" (space)+ name:fn spaces '=' spaces basicFunction          -> fn,
    functionDef         = pos:p func:fn defEnd
                        -> { $elf._fragment(fn, 'functionDef', p, $elf.pos()-1) },
    executedFuncDef     = stackSize:s pos:p '(' basicFunction:fn ')' somethingRelated defEnd ?(s == $elf.stack.length)
                        -> { $elf._fragment(fn, 'executedFuncDef', p, $elf.pos()-1) },
    staticFuncDef       = pos:p namespaceIdSplitted:nsArr ?(nsArr.length > 1) spaces '=' spaces basicFunction defEnd
                        -> { $elf._fragment(nsArr.last(), 'staticFuncDef', p, $elf.pos()-1, null, {klassName: nsArr.slice(0,nsArr.length-1).join('.')}) },
    
	/* methods */
	wrapEnd             = ".wrap" chunk('(', ')'),
    methodDefWithSpcs   = spaces methodDef:mD classElemDefEnd                      -> mD,
    methodDef           = pos:p name:mName ':' spaces basicFunction (somethingBigRelated | "" ) classElemDefEnd
                        -> { $elf._fragment(mName, 'methodDef', p, $elf.pos()-1) },
    methodModificationDef   = pos:p nsWith('prototype'):spec spaces '=' spaces somethingBigRelated defEnd
                            -> { $elf._fragment(spec.after, 'methodModificationDef', p, $elf.pos()-1, null, {klassName: spec.before}) },
    
    /* properties */
    propertyDefWithSpcs = spaces propertyDef:pD spaces                          -> pD,
    propertyDef         = stackSize:s pos:p name:pName ':' spaces ~"function" somethingBigRelated classElemDefEnd
                        -> { if ($elf.stack.length !== s) {
                                throw new Error('sth wrong with the stack: ' + $elf.stack + ' expected length: ' + s + ' actual length: ' + $elf.stack.length);
                             };
                             $elf._fragment(pName, 'propertyDef', p, $elf.pos()-1) },
                                    
    /* class stuff */
	classElems          = '{' spaces ( methodDefWithSpcs | propertyDefWithSpcs  )*:a spaces '}'	-> a,
	restKlassDef        = ',' spaces classElems:descriptors	                            -> { {classElems: descriptors} }
	                    | ',' spaces klass:trait ',' spaces classElems:descriptors	    -> { {trait: trait, classElems: descriptors} }
	                    | ',' klassName:trait					                        -> { {trait: trait, classElems: []} }
					    | spacesNoNl							                        -> { {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 });
                             $elf._fragment(kName, 'klassDef', p, $elf.pos()-1, spec.classElems, {trait: spec.trait, superclassName: sName}) },
    basicKlassExt       = "Object.extend" '(' klass:n restKlassDef:spec ')'
                            -> { spec.classElems.forEach(function(ea) { ea.className = n }); {name: n, trait: spec.trait, subElements: spec.classElems } }
                        | (nsFollowedBy("addMethods"):n | nsFollowedBy("addProperties"):n) '(' classElems:clElems ')'
                            -> { clElems.forEach(function(ea) { ea.className = n }); {name: n, subElements: clElems } },
    klassExtensionDef   = pos:p basicKlassExt:spec defEnd
                        -> { $elf._fragment(spec.name + ' (extension)', 'klassExtensionDef', p, $elf.pos()-1, spec.subElements, {trait: spec.trait}) },
    
    /* 'object' stuff */
    restObjDef          = classElems:propsAndMethodDescrs spaces -> propsAndMethodDescrs,
    objectDef           = pos:p ( "var" spaces namespaceId:o | namespaceId:o) spaces '=' spaces restObjDef:propsAndMethodDescrs defEnd
                        -> { $elf._fragment(o, 'objectDef', p, $elf.pos()-1, propsAndMethodDescrs) },

    /* otherStuff */
    // comment     = descriptor:d spacesNoNl /*(fromTo('//', '\n') | fromTo('/*', '*/')) spacesNoNl*/   -> { d.stopIndex = $elf.pos()-1; d.type = 'comment'; d },
    comment     = pos:p space+                                          -> { $elf._fragment(null, 'comment', p, $elf.pos()-1) },
    newline     = pos:p ('\n' | '\r' )                                  -> { $elf._fragment(null, 'newline', p, $elf.pos()-1) },
    blankLine   = pos:p (nl | (char:c ?(c.charCodeAt(0) === 32))* nl)   -> { $elf._fragment(null, 'blankLine', p, $elf.pos()-1) },
    unknown     = pos:p somethingBigRelated defEnd                      -> { $elf._fragment(null, 'unknown', p, $elf.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 f = new klass(name, type, startIndex, stopIndex, null, null, subElems);
    if (!custom) return f;
    Object.extend(f, custom);
    return f;
};
LKFileParser;