" Vim autoload script for a JAVA PARSER and more. " Language: Java " Maintainer: cheng fang " Last Changed: 2007-09-16 " Version: 0.67 " Copyright: Copyright (C) 2007 cheng fang. All rights reserved. " License: Vim License (see vim's :help license) if exists("g:loaded_javaparser") || version < 700 || &cp finish endif let g:loaded_javaparser = 'v0.67' " Constants used by scanner and parser {{{1 let s:EOI = '' let s:keywords = {'+': 'PLUS', '-': 'SUB', '!': 'BANG', '%': 'PERCENT', '^': 'CARET', '&': 'AMP', '*': 'STAR', '|': 'BAR', '~': 'TILDE', '/': 'SLASH', '>': 'GT', '<': 'LT', '?': 'QUES', ':': 'COLON', '=': 'EQ', '++': 'PLUSPLUS', '--': 'SUBSUB', '==': 'EQEQ', '<=': 'LTEQ', '>=': 'GTEQ', '!=': 'BANGEQ', '<<': 'LTLT', '>>': 'GTGT', '>>>': 'GTGTGT', '+=': 'PLUSEQ', '-=': 'SUBEQ', '*=': 'STAREQ', '/=': 'SLASHEQ', '&=': 'AMPEQ', '|=': 'BAREQ', '^=': 'CARETEQ', '%=': 'PERCENTEQ', '<<=': 'LTLTEQ', '>>=': 'GTGTEQ', '>>>=': 'GTGTGTEQ', '||': 'BARBAR', '&&': 'AMPAMP', 'abstract': 'ABSTRACT', 'assert': 'ASSERT', 'boolean': 'BOOLEAN', 'break': 'BREAK', 'byte': 'BYTE', 'case': 'CASE', 'catch': 'CATCH', 'char': 'CHAR', 'class': 'CLASS', 'const': 'CONST', 'continue': 'CONTINUE', 'default': 'DEFAULT', 'do': 'DO', 'double': 'DOUBLE', 'else': 'ELSE', 'extends': 'EXTENDS', 'final': 'FINAL', 'finally': 'FINALLY', 'float': 'FLOAT', 'for': 'FOR', 'goto': 'GOTO', 'if': 'IF', 'implements': 'IMPLEMENTS', 'import': 'IMPORT', 'instanceof': 'INSTANCEOF', 'int': 'INT', 'interface': 'INTERFACE', 'long': 'LONG', 'native': 'NATIVE', 'new': 'NEW', 'package': 'PACKAGE', 'private': 'PRIVATE', 'protected': 'PROTECTED', 'public': 'PUBLIC', 'return': 'RETURN', 'short': 'SHORT', 'static': 'STATIC', 'strictfp': 'STRICTFP', 'super': 'SUPER', 'switch': 'SWITCH', 'synchronized': 'SYNCHRONIZED', 'this': 'THIS', 'throw': 'THROW', 'throws': 'THROWS', 'transient': 'TRANSIENT', 'try': 'TRY', 'void': 'VOID', 'volatile': 'VOLATILE', 'while': 'WHILE', 'true': 'TRUE', 'false': 'FALSE', 'null': 'NULL', '(': 'LPAREN', ')': 'RPAREN', '{': 'LBRACE', '}': 'RBRACE', '[': 'LBRACKET', ']': 'RBRACKET', ';': 'SEMI', ',': 'COMMA', '.': 'DOT', 'enum': 'ENUM', '...': 'ELLIPSIS', '@': 'MONKEYS_AT'} let s:Flags = {'PUBLIC': 0x1, 'PRIVATE': 0x2, 'PROTECTED': 0x4, 'STATIC': 0x8, 'FINAL': 0x10, 'SYNCHRONIZED': 0x20, 'VOLATILE': 0x40, 'TRANSIENT': 0x80, 'NATIVE': 0x100, 'INTERFACE': 0x200, 'ABSTRACT': 0x400, 'STRICTFP': 0x800, 'SYNTHETIC': 0x1000, 'ANNOTATION': 0x2000, 'ENUM': 0x4000, 'StandardFlags':0x0fff, 'ACC_SUPER': 0x20, 'ACC_BRIDGE': 0x40, 'ACC_VARARGS': 0x80, 'DEPRECATED': 0x20000, 'HASINIT': 0x40000, 'BLOCK': 0x100000, 'IPROXY': 0x200000, 'NOOUTERTHIS': 0x400000, 'EXISTS': 0x800000, 'COMPOUND': 0x1000000, 'CLASS_SEEN': 0x2000000, 'SOURCE_SEEN': 0x4000000, 'LOCKED': 0x8000000, 'UNATTRIBUTED': 0x10000000, 'ANONCONSTR': 0x20000000, 'ACYCLIC': 0x40000000, 'BRIDGE': 1.repeat('0', 31), 'PARAMETER': 1.repeat('0', 33), 'VARARGS': 1.repeat('0', 34), 'ACYCLIC_ANN': 1.repeat('0', 35), 'GENERATEDCONSTR': 1.repeat('0', 36), 'HYPOTHETICAL': 1.repeat('0', 37), 'PROPRIETARY': 1.repeat('0', 38)} let s:RE_ANYTHING_AND_NEWLINE = '\(\(.\|\n\)*\)' let s:RE_LINE_COMMENT = '//.*$' let s:RE_COMMENT_SP = '/\*\*/' let s:RE_COMMENT = '' let s:RE_BRACKETS = '\(\s*\[\s*\]\)' let s:RE_IDENTIFIER = '[a-zA-Z_$][a-zA-Z0-9_$]*' let s:RE_QUALID = s:RE_IDENTIFIER. '\(\s*\.\s*' .s:RE_IDENTIFIER. '\)*' let s:RE_TYPE_NAME = s:RE_QUALID let s:RE_REFERENCE_TYPE = s:RE_QUALID . s:RE_BRACKETS . '*' " TypeName || Type[] let s:RE_TYPE = s:RE_REFERENCE_TYPE " PrimitiveType || ReferenceType let s:RE_TYPE_VARIABLE = s:RE_IDENTIFIER let s:RE_VAR_DECL_ID = s:RE_IDENTIFIER . s:RE_BRACKETS . '*' let s:RE_TYPE_PARAMS = '' let s:RE_THROWS = 'throws\s\+' . s:RE_TYPE_NAME . '\(\s*,\s*' . s:RE_TYPE_NAME . '\)*' let s:RE_FORMAL_PARAM = '\(final\s*\)\='. s:RE_TYPE . '\s\+' . s:RE_VAR_DECL_ID let s:RE_FORMAL_PARAM_LIST = s:RE_FORMAL_PARAM . '\(\s*,\s*' . s:RE_FORMAL_PARAM . '\)*' let s:RE_FORMAL_PARAM2 = '^\s*\(final\s*\)\=\('. s:RE_TYPE . '\)\s\+\(' . s:RE_IDENTIFIER . '\)' . s:RE_BRACKETS . '*' let s:RE_MEMBER_MODS = '\%(PUBLIC\|PROTECTED\|PRIVATE\|ABSTRACT\|STATIC\|FINAL\|TRANSIENT\|VOLATILE\|SYNCHRONIZED\|NATIVE\|STRICTFP\)' let s:RE_MEMBER_HEADER = '\s*\(\%(' .s:RE_MEMBER_MODS. '\s\+\)\+\)\(' .s:RE_IDENTIFIER. '\%(\s*\.\s*' .s:RE_IDENTIFIER. '\)*\%(\s*\[\s*\]\)*\)\s\+\(' .s:RE_IDENTIFIER. '\)' " API {{{1 let s:PROTOTYPE = {'s:options': {}, 'b:buf': '', 'b:buflen': 0, 'b:lines': [], 'b:idxes': [0], 'b:bp': -1, 'b:ch': '', 'b:line': 0, 'b:col': 0, 'b:pos': 0, 'b:endPos': 0, 'b:prevEndPos': 0, 'b:errPos': -1, 'b:errorEndPos': -1, 'b:sbuf': '', 'b:name': '', 'b:token': '', 'b:docComment': '', 'b:radix': 0, 'b:unicodeConversionBp': -1, 'b:scanStrategy': 0, 'b:allowGenerics': 1, 'b:allowVarargs': 1, 'b:allowAsserts': 1, 'b:allowEnums': 1, 'b:allowForeach': 1, 'b:allowStaticImport': 1, 'b:allowAnnotations': 1, 'b:keepDocComments': 1, 'b:mode': 0, 'b:lastmode': 0, 'b:log': [], 'b:et_perf': '', 'b:et_nextToken_count': 0} " Function to initialize the parser " parameters: " - lines List of code text " - options A set of options fu! java_parser#InitParser(lines, ...) let s:options = a:0 == 0 ? {} : a:1 " internal variables for scanning " let b:buf = '' " The input buffer let b:buflen = 0 " index of one past last character in buffer. also eofPos let b:lines = a:lines " The input buffer let b:idxes = [0] " Begin index of every lines let b:bp = -1 " index of next character to be read. let b:ch = '' " The current character. let b:line = 0 " The line number position of the current character. let b:col = 0 " The column number position of the current character. let b:pos = 0 " The token's position, 0-based offset from beginning of text. let b:endPos = 0 " Character position just after the last character of the token. let b:prevEndPos = 0 " The last character position of the previous token. let b:errPos = -1 " The position where a lexical error occurred let b:errorEndPos = -1 " let b:sbuf = '' " A character buffer for literals. let b:name = '' " The name of an identifier or token: let b:token = 0 " The token, set by s:nextToken(). let b:docComment = '' let b:radix = 0 " The radix of a numeric literal token. let b:unicodeConversionBp =-1 " The buffer index of the last converted unicode character let b:scanStrategy = get(s:options, 'scanStrategy', -1) " 0 - only class members when parse full file; " 1 - keep statement as a whole string; " 2 - all " -1 - enable quick recognition of declarations in common form. " language feature options. let b:allowGenerics = get(s:options, 'allowGenerics', 1) let b:allowVarargs = get(s:options, 'allowVarargs', 1) let b:allowAsserts = get(s:options, 'allowAsserts', 1) let b:allowEnums = get(s:options, 'allowEnums', 1) let b:allowForeach = get(s:options, 'allowForeach', 1) let b:allowStaticImport = get(s:options, 'allowStaticImport', 1) let b:allowAnnotations = get(s:options, 'allowAnnotations', 1) let b:keepDocComments = get(s:options, 'keepDocComments', 1) let b:mode = 0 " The current mode. let b:lastmode = 0 " The mode of the term that was parsed last. let b:log = [] let b:et_perf = '' let b:et_nextToken_count = 0 " let b:buf = join(a:lines, "\r") " let b:buflen = strlen(b:buf) for line in a:lines let b:buflen += strlen(line) + 1 call add(b:idxes, b:buflen) endfor call add(b:lines, s:EOI) " avoid 'list index out of range' error from lines[] in java_scanChar " if b:bp >= b:buflen " return s:EOI " endif " initialize scanner call s:scanChar() " be ready for scanning call s:nextToken() " prime the pump endfu fu! java_parser#FreeParser() for varname in keys(s:PROTOTYPE) exe "if exists(" . string(varname) . ") | unlet " . varname . " | endif" endfor endfu fu! java_parser#GetSnapshot() let snapshot = {} for varname in keys(s:PROTOTYPE) exe "let snapshot[" . string(varname) . "] = " . varname endfor return snapshot endfu fu! java_parser#Restore(snapshot) for key in keys(a:snapshot) exe "let " . key . "=" . string(a:snapshot[key]) endfor endfu " move b:bp and b:pos to the specified position fu! java_parser#GotoPosition(pos) let b:bp = a:pos-1 let b:pos = a:pos let p = java_parser#DecodePos(b:bp) let b:line = p.line let b:col = p.col call s:scanChar() call s:nextToken() endfu fu! java_parser#compilationUnit() return s:compilationUnit() endfu fu! java_parser#block() return s:block() endfu fu! java_parser#statement() return s:blockStatements() endfu fu! java_parser#expression() return s:expression() endfu fu! java_parser#nextToken() return s:nextToken() endfu " Tree {{{1 let s:TTree = {'tag': '', 'pos': 0} " Root class for AST nodes. " Tree maker functions {{{2 fu! s:ClassDef(pos, mods, ...) return {'tag': 'CLASSDEF', 'pos': a:pos, 'mods': a:mods, 'name': ''} endfu fu! s:VarDef(pos, mods, name, vartype) return {'tag': 'VARDEF', 'pos': a:pos, 'mods': a:mods, 'name': a:name, 'vartype': a:vartype} endfu fu! s:Unary(pos, opcode, arg) return {'tag': a:opcode, 'pos': a:pos, 'arg': a:arg} endfu fu! s:Binary(pos, opcode, lhs, rhs, ...) return {'tag': a:opcode, 'pos': a:pos, 'lhs': a:lhs, 'rhs': a:rhs} endfu fu! s:TypeCast(pos, clazz, expr) return {'tag': 'TYPECAST', 'pos': a:pos, 'clazz': a:clazz, 'expr': a:expr} endfu fu! s:Select(pos, selected, name) return {'tag': 'SELECT', 'pos': a:pos, 'selected': a:selected, 'name': a:name} endfu fu! s:Ident(pos, name) return {'tag': 'IDENT', 'pos': a:pos, 'name': a:name} endfu fu! s:TypeArray(pos, elementtype) return {'tag': 'TYPEARRAY', 'pos': a:pos, 'elementtype': a:elementtype} endfu fu! s:Modifiers(pos, flags, annotations) let noFlags = s:BitAnd(a:flags, s:Flags.StandardFlags) == 0 let mods = {'tag': 'MODIFIERS', 'flags': a:flags} let mods.pos = noFlags && empty(a:annotations) ? -1 : a:pos if !empty(a:annotations) let mods.annotations = a:annotations endif return mods endfu " {{{2 fu! java_parser#IsStatement(tree) return has_key(a:tree, 'tag') && a:tree.tag =~# '^\(CLASSDEF\|VARDEF\|BLOCK\|IF\|DOLOOP\|WHILELOOP\|FORLOOP\|FOREACHLOOP\|SWITCH\|TRY\|EXEC\|LABELLED\|SYNCHRONIZED\|CASE\|BREAK\|RETURN\|SKIP\|THROW\|ASSERT\|CONTINUE\)$' endfu " Tree Helper {{{1 fu! java_parser#type2Str(type) if type(a:type) == type("") return a:type endif let t = a:type if t.tag == 'IDENTIFIER' return t.value elseif t.tag == 'IDENT' return t.name elseif t.tag == 'TYPEIDENT' return t.typetag elseif t.tag == 'SELECT' return java_parser#type2Str(t.selected) . '.' . t.name elseif t.tag == 'TYPEARRAY' return java_parser#type2Str(t.elementtype) . '[]' elseif t.tag == 'TYPEAPPLY' return t.clazz.name elseif t.tag == 'TEMPLATE' let s = t.clazz.value . '<' for arg in t.arguments let s .= arg.value endfor let s .= '>' return s endif endfu fu! s:vardef2Str(vardef) return java_parser#type2Str(a:vardef.vartype) . ' ' . a:vardef.name endfu fu! s:method2Str(methoddef) let m = a:methoddef let desc = m.r . ' ' . m.n . '(' for item in m.params let desc .= s:vardef2Str(item) . ',' endfor let desc = substitute(desc, ',$', '', '') let desc .= ')' return desc endfu " Scanner {{{1 " nextToken() {{{2 fu! s:nextToken() let b:prevEndPos = b:endPos let b:et_nextToken_count += 1 let b:sbuf = '' while 1 let b:pos = b:bp if b:ch =~ '[ \t\r\n ]' " - FF " OAO optimized code: skip spaces let b:col = match(b:lines[b:line], '[^ \t\r\n ]\|$', b:col) let b:bp = b:idxes[b:line] + b:col call s:scanChar() let b:endPos = b:bp continue elseif b:ch =~ '[a-zA-Z$_]' " read a identifier call s:scanIdent() return elseif b:ch == '0' call s:scanChar() " hex if b:ch == 'x' || b:ch == 'X' call s:scanChar() if b:ch == '.' call s:scanHexFractionAndSuffix(0) elseif s:isDigit(16) call s:scanNumber(16) else call s:LexError("invalid.hex.number") endif " oct else let b:sbuf .= '0' call s:scanNumber(8) endif return elseif b:ch =~ '[1-9]' " read number call s:scanNumber(10) return elseif b:ch == '.' call s:scanChar() if b:ch =~ '[0-9]' let b:sbuf .= '.' call s:scanFractionAndSuffix() " JAVA5 ELLIPSIS elseif b:ch == '.' let b:sbuf .= '..' call s:scanChar() if b:ch == '.' call s:scanChar() let b:sbuf .= '.' let b:token = 'ELLIPSIS' else call s:LexError('malformed.fp.lit') endif else let b:token = 'DOT' endif return elseif b:ch =~ '[,;(){}[\]]' let b:token = s:keywords[b:ch] call s:scanChar() return elseif b:ch == '/' let status = s:scanComment() if status == 1 continue elseif status == 2 return elseif b:ch == '=' let b:name = '/=' let b:token = 'SLASHEQ' call s:scanChar() else let b:name = '/' let b:token = 'SLASH' endif return elseif b:ch == "'" call s:scanSingleQuote() return elseif b:ch == '"' call s:scanDoubleQuote() return else if s:IsSpecial(b:ch) call s:scanOperator() elseif b:ch =~ '[a-zA-Z_]' call s:scanIdent() elseif b:bp >= b:buflen let b:token = 'EOF' else call s:LexError("illegal.char '" . b:ch . "'") call s:scanChar() endif return endif endwhile endfu " scanChar() Read next character. {{{2 " one buf version "fu! s:scanChar() " let b:bp += 1 " if b:bp % 256 == 0 " let b:buf2 = strpart(b:buf, b:bp, 256) " endif " let b:ch = b:buf2[b:bp % 256] "" let b:ch = b:buf[b:bp] " it will be extremely slow when buf is large " "call s:Trace( "'" . b:ch . "'" ) " " if b:ch == "\r" " let b:line += 1 " let b:col = 0 " elseif b:ch == "\n" " if b:bp == 0 || b:buf[b:bp-1] == "\r" " let b:line += 1 " let b:col = 0 " endif " else " let b:col += 1 " endif "endfu " Read next character. multiple lines version fu! s:scanChar() let b:bp+=1 let b:ch=b:lines[b:line][b:col] let b:col+=1 if b:ch=='' let b:ch="\r" let b:line+=1 let b:col=0 endif if b:ch == '\' call s:convertUnicode() endif endfu fu! java_parser#CharAt(line, col) let ch=b:lines[a:line][a:col] if ch == '' let ch = "\r" endif return ch endfu fu! s:convertUnicode() if b:ch == '\' && b:unicodeConversionBp != b:bp "if java_parser#CharAt(b:bp+1) == 'u' "call s:scanChar() "endif endif endfu " putChar() is substituted with " let b:sbuf .= '.' " scanIdent() {{{2 " OAO optimized code fu! s:scanIdent() let col_old = b:col let b:col = match(b:lines[b:line], '[^a-zA-Z0-9$_]\|$', b:col) let b:name = strpart(b:lines[b:line], col_old-1, b:col-col_old+1) let b:token = get(s:keywords, b:name, 'IDENTIFIER') call s:Debug('name: "' . b:name . '" of token type ' . b:token ) let b:bp = b:idxes[b:line] + b:col call s:scanChar() endfu " Standard implementation fu! s:scanIdent_old() " do ... while () let b:sbuf .= b:ch call s:scanChar() if b:ch !~ '[a-zA-Z0-9$_]' || b:bp >= b:buflen let b:name = b:sbuf let b:token = has_key(s:keywords, b:name) ? s:keywords[b:name] : 'IDENTIFIER' call s:Debug('name: "' . b:name . '" of token type ' . b:token ) return endif while (1) let b:sbuf .= b:ch call s:scanChar() if b:ch !~ '[a-zA-Z0-9$_]' || b:bp >= b:buflen let b:name = b:sbuf let b:token = has_key(s:keywords, b:name) ? s:keywords[b:name] : 'IDENTIFIER' call s:Debug('name: "' . b:name . '" of token type ' . b:token ) break endif endwhile endfu " digit() {{{2 " Convert an ASCII digit from its base (8, 10, or 16) to its value. " NOTE: This only implement isdigit() check fu! s:digit(base) let c = b:ch "let result = endfu fu! s:isDigit(base) if a:base == 8 return b:ch =~ '[0-7]' elseif a:base == 16 return b:ch =~ '[0-9a-fA-F]' elseif a:base == 10 return b:ch =~ '[0-9]' endif endfu " scanNumber() {{{2 fu! s:scanNumber(radix) let b:radix = a:radix let digitRadix = a:radix <= 10 ? 10 : 16 let seendigit = 0 while s:isDigit(a:radix) let seendigit = 1 let b:sbuf .= b:ch call s:scanChar() endwhile if a:radix == 16 && b:ch == '.' call s:scanHexFractionAndSuffix(seendigit) elseif seendigit && a:radix == 16 && (b:ch == 'p' || b:ch == 'P') call s:scanHexExponentAndSuffix() elseif a:radix <= 10 && b:ch == '.' let b:sbuf .= b:ch call s:scanChar() call s:scanFractionAndSuffix() elseif a:radix <= 10 && b:ch =~ '[eEfFdD]' call s:scanFractionAndSuffix() else if b:ch == 'l' || b:ch == 'L' call s:scanChar() let b:token = 'LONGLITERAL' else let b:token = 'INTLITERAL' endif endif endfu fu! s:scanHexExponentAndSuffix() if b:ch == 'p' || b:ch == 'P' let b:sbuf .= b:ch call s:scanChar() if b:ch == '+' || b:ch == '-' let b:sbuf .= b:ch call s:scanChar() endif if '0' <= b:ch && b:ch <= '9' let b:sbuf .= b:ch call s:scanChar() while '0' <= b:ch && b:ch <= '9' let b:sbuf .= b:ch call s:scanChar() endwhile "if !b:allowHexFloats "elseif !b:hexFloatsWork " call s:LexError("unsupported.cross.fp.lit") "endif else call s:LexError("malformed.fp.lit") endif else call s:LexError("malformed.fp.lit") endif if b:ch == 'f' || b:ch == 'F' let b:sbuf .= b:ch call s:scanChar() let b:token = 'FLOATLITERAL' else if b:ch == 'f' || b:ch == 'F' let b:sbuf .= b:ch call s:scanChar() endif let b:token = 'DOUBLELITERAL' endif endfu fu! s:scanFraction() " scan fraction while b:ch =~ '[0-9]' let b:sbuf .= b:ch call s:scanChar() endwhile " floating point number if b:ch == 'e' || b:ch == 'E' let b:sbuf .= b:ch call s:scanChar() if b:ch == '+' || b:ch == '-' let b:sbuf .= b:ch call s:scanChar() endif if b:ch =~ '[0-9]' let b:sbuf .= b:ch call s:scanChar() while b:ch =~ '[0-9]' let b:sbuf .= b:ch call s:scanChar() endwhile return endif call s:LexError("malformed.fp.lit") endif endfu " Read fractional part and 'd' or 'f' suffix of floating point number. fu! s:scanFractionAndSuffix() call s:scanFraction() if b:ch == 'f' || b:ch == 'F' let b:sbuf .= b:ch call s:scanChar() let b:token = 'FLOATLITERAL' else if b:ch == 'd' || b:ch == 'D' let b:sbuf .= b:ch call s:scanChar() endif let b:token = 'DOUBLELITERAL' endif endfu fu! s:scanHexFractionAndSuffix(seendigit) let seendigit = a:seendigit let b:radix = 16 if b:ch != '.' | echoerr "b:ch != '.'" | endif let b:sbuf .= b:ch call s:scanChar() while s:isDigit(16) let seendigit = 1 let b:sbuf .= b:ch call s:scanChar() endwhile if !seendigit call s:LexError("invalid.hex.number") else call s:scanHexExponentAndSuffix() endif endfu " scanLitChar() {{{2 fu! s:scanLitChar() if b:ch == '\' call s:scanChar() if b:ch =~ '[0-7]' let leadch = b:ch let oct = b:ch call s:scanChar() if b:ch =~ '[0-7]' let oct = oct * 8 + b:ch call s:scanChar() if leadch <= '3' && '0' <= b:ch && b:ch <= '7' let oct = oct * 8 + b:ch call s:scanChar() endif endif let b:sbuf .= oct elseif b:ch == "'" || b:ch =~ '[btnfr"\\]' let b:sbuf .= b:ch call s:scanChar() " unicode escape elseif b:ch == 'u' while b:ch =~ '[a-zA-Z0-9]' call s:scanChar() endwhile else call s:LexError("illegal.esc.char") endif elseif b:bp < b:buflen let b:sbuf .= b:ch call s:scanChar() endif endfu " scanOperator() {{{2 fu! s:scanOperator() while 1 if !has_key(s:keywords, b:sbuf . b:ch) break endif let b:sbuf .= b:ch let b:token = get(s:keywords, b:sbuf, 'IDENTIFIER') call s:Debug('sbuf: "' . b:sbuf . '" of token type ' . b:token ) call s:scanChar() if !s:IsSpecial(b:ch) break endif endwhile endfu " NOTE: add @ for JAVA5 fu! s:IsSpecial(ch) return a:ch =~ '[!%&*?+-:<=>^|~@]' endfu " scan comment {{{2 " return 0 - not comment, 1 - succeeded to scan comment, 2 - unclosed comment fu! s:scanComment() call s:scanChar() " line comment if b:ch == '/' let b:token = 'LINECOMMENT' call s:Info('line comment') call s:SkipLineComment() let b:endPos = b:bp return 1 " classic comment " test cases: /**/, /***/, /*******/, /*** astatement; /***/, /*/ elseif b:ch == '*' let b:token = 'BLOCKCOMMENT' call s:Info('block comment') call s:scanChar() let time = reltime() " javadoc if b:ch == '*' let b:docComment = s:scanDocComment() " normal comment else call s:skipComment() endif let b:et_perf .= "\r" . 'comment ' . reltimestr(reltime(time)) if b:ch == '/' call s:Info('end block comment') call s:scanChar() let b:endPos = b:bp return 1 else call s:LexError('unclosed.comment') return 2 endif endif return 0 endfu fu! s:SkipLineComment() " OAO optimized code let b:ch = "\r" let b:line += 1 let b:col = 0 let b:bp = b:idxes[b:line] + b:col " OLD "call s:scanChar() "while (b:ch != "\r") " call s:scanChar() "endwhile endfu fu! s:skipComment() if b:ch == '*' call s:scanChar() if b:ch == '/' return else " NOTE: go back let b:ch = '*' let b:bp -= 1 let b:col -= 1 endif endif " OAO optimized code if s:Stridx('*/') > -1 call s:scanChar() endif " " Standard implementation " while b:bp < b:buflen " if b:ch == '*' " call s:scanChar() " if b:ch == '/' " break " endif " else " call s:scanChar() " endif " endwhile endfu fu! s:scanDocComment() call s:Info('It is javadoc') return s:skipComment() " skip star '*' while (b:bp < b:buflen && b:ch == '*') call s:scanChar() endwhile if b:bp < b:buflen && b:ch == '/' return '' endif let result = '' while b:bp < b:buflen if b:ch == '*' call s:scanChar() if b:ch == '/' break else let result .= b:ch endif else call s:scanChar() let result .= b:ch endif endwhile return result endfu " scan single quote {{{2 fu! s:scanSingleQuote() call s:scanChar() if (b:ch == "'") call s:LexError("empty.char.lit") else if (b:ch =~ '[\r\n]') call s:LexError("illegal.line.end.in.char.lit") endif call s:scanLitChar() if b:ch == "'" call s:scanChar() let b:token = 'CHARLITERAL' else call s:LexError("unclosed.char.lit") endif endif endfu " scan double quote {{{2 " test cases: " 'a"";' " 'a"b,c";' " 'a"b,\"c";' " 'a"b,\\"c";' " 'a"b,\\\"c";' " 'a"b,\\\\"c";' " 'a"b,\\\"c;' " NOTE: cannot handle fu! s:scanDoubleQuote() if match(b:lines[b:line], '\\"', b:col) == -1 let idx = matchend(b:lines[b:line], '\(\\\(["\\''ntbrf]\)\|[^"]\)*"', b:col) if idx != -1 let b:sbuf = strpart(b:lines[b:line], b:col, idx-b:col-1) let b:col = idx-1 " back to the end let b:bp = b:idxes[b:line] + b:col call s:scanChar() let b:token = 'STRINGLITERAL' else call s:LexError("unclosed.str.lit") endif call s:scanChar() return endif call s:scanChar() while b:ch !~ '["\r\n]' && b:bp < b:buflen call s:scanLitChar() endwhile if b:ch == '"' let b:token = 'STRINGLITERAL' call s:scanChar() else call s:LexError("unclosed.str.lit") endif endfu " lex errors {{{2 fu! s:LexError(key, ...) let pos = a:0 == 0 ? b:pos : a:1 let b:token = 'ERROR' let b:errPos = pos call s:Log(4, b:pos, '[lex error]:' . s:Pos2Str(pos) . ': ' . a:key) endfu " Scanner Helper {{{1 " gotoMatchEnd {{{2 fu! s:gotoMatchEnd(one, another, ...) while b:bp < b:buflen if b:ch == a:another call s:scanChar() if has_key(s:keywords, a:another) let b:token = s:keywords[a:another] else echoerr '' endif break elseif b:ch == a:one call s:scanChar() call s:gotoMatchEnd(a:one, a:another) " skip commment elseif b:ch == '/' call s:scanComment() " skip literal character elseif b:ch == "'" call s:scanSingleQuote() " skip literal string elseif b:ch == '"' call s:scanDoubleQuote() else " OAO call s:Match('[' . a:one . a:another . '/"'']') " OLD "call s:scanChar() endif endwhile " For such situation: after accept one token, the next token is just the same. let nextTokenIsLBRACE = a:0 == 0 ? 0 : a:1 if nextTokenIsLBRACE call s:gotoMatchEnd(a:one, a:another) endif return b:bp endfu " gotoSemi {{{2 fu! s:gotoSemi() while b:bp < b:buflen if b:ch == ';' let b:pos = b:bp call s:scanChar() let b:token = 'SEMI' return " skip commment elseif b:ch == '/' call s:scanComment() " skip literal character elseif b:ch == "'" call s:scanSingleQuote() " skip literal string elseif b:ch == '"' call s:scanDoubleQuote() elseif b:ch == '{' call s:scanChar() call s:gotoMatchEnd('{', '}') elseif b:ch == '(' call s:scanChar() call s:gotoMatchEnd('(', ')') elseif b:ch == '[' call s:scanChar() call s:gotoMatchEnd('[', ']') else " OAO call s:Match('[;({[/"'']') " OLD "call s:scanChar() endif endwhile endfu " s:Strpart(), s:Stridx(), s:Match() {{{2 fu! Strpart(start, len) let startline = java_parser#DecodePos(a:start).line let endline = java_parser#DecodePos(a:start + a:len).line let str = join(b:lines[startline:endline-1]) . b:lines[endline] return strpart(str, a:start-b:idxes[startline], a:len) endfu fu! s:Stridx(needle) let found = 0 while b:line < len(b:lines)-1 let idx = stridx(b:lines[b:line], a:needle, b:col) if idx > -1 let found = 1 let b:col = idx break endif let b:line += 1 let b:col = 0 endwhile if found let b:bp = b:idxes[b:line] + b:col call s:scanChar() return b:bp else let b:bp = b:buflen let b:ch = s:EOI return -1 endif endfu fu! s:Match(pat) let bp_old = b:bp let line_old = b:line let col_old = b:col let found = 0 while b:line < len(b:lines)-1 let idx = match(b:lines[b:line], a:pat, b:col) if idx > -1 let found = 1 let b:col = idx break endif let b:line += 1 let b:col = 0 endwhile if found let b:bp = b:idxes[b:line] + b:col-1 call s:scanChar() return b:bp else let b:bp = bp_old let b:line = line_old let b:col = col_old call s:scanChar() return -1 endif endfu " conversion between position and (line, col) {{{2 fu! java_parser#MakePos(line, col) return b:idxes[a:line] + a:col endfu fu! java_parser#DecodePos(pos) let line = -1 for idx in b:idxes if idx > a:pos break endif let line += 1 endfor let col = a:pos - b:idxes[line] return {'line': line, 'col': col} endfu fu! s:Pos2Str(pos) let o = java_parser#DecodePos(a:pos) return '(' . (o.line+1) . ',' . (o.col+1) . ')' endfu " Bitwise operator emulators {{{1 " NOTE: For more than 32 bit number, use the string bits form. " bit operator ~ fu! s:BitNot(v) return s:Bits2Number( s:BitNot_binary(s:Number2Bits(a:v)) ) endfu fu! s:BitNot_binary(v) let v = substitute(a:v, '^0*\([01]\+\)', '\1', 'g') let v = substitute(v, '1', '2', 'g') let v = substitute(v, '0', '1', 'g') let v = substitute(v, '2', '0', 'g') return v endfu " bit operator & fu! s:BitAnd(n1, n2) if a:n1 == 0 || a:n2 == 0 | return 0 | endif if a:n1 == a:n2 | return 1 | endif return s:Bits2Number( s:BitOperator_binary(s:Number2Bits(a:n1), s:Number2Bits(a:n2), 'n1[i] == "1" && n2[i] == "1"') ) endfu " bit operator | fu! s:BitOr(n1, n2, ...) if a:0 == 0 if a:n1 == 0 return a:n2 elseif a:n2 == 0 return a:n1 endif endif let result = s:BitOperator_binary(s:Number2Bits(a:n1), s:Number2Bits(a:n2), 'n1[i] == "1" || n2[i] == "1"') for a in a:000 let result = s:BitOperator_binary(result, s:Number2Bits(a), 'n1[i] == "1" || n2[i] == "1"') endfor return s:Bits2Number( result ) endfu " bit operator ^ fu! s:BitXor(n1, n2) if a:n1 == a:n2 | return 0 | endif return s:Bits2Number( s:BitOperator_binary(s:Number2Bits(a:n1), s:Number2Bits(a:n2), 'n1[i] != n2[i]') ) endfu fu! s:BitOperator_binary(n1, n2, comparator) let n1 = a:n1 let n2 = a:n2 let len1 = len(n1) let len2 = len(n2) let len = len1 if len1 > len2 let n2 = repeat('0', len1-len2) . n2 else let len = len2 let n1 = repeat('0', len2-len1) . n1 endif let i = 0 let bits = '' while i < len let bits .= eval(a:comparator) ? '1' : '0' let i += 1 endwhile return bits endfu " bit operator << fu! s:BitMoveLeft() endfu " bit operator >> fu! s:BitMoveRight() endfu " helper function: convert a number to a string consisted of '0' or '1' indicating number fu! s:Number2Bits(n, ...) if type(a:n) == type("") | return a:n | endif if a:n == 0 | return '0' | endif let n = a:n let bits = '' while n != 0 let bit = n % 2 "echo 'n: ' . n . ' bit: ' . bit let bits = bit . bits let n = (n-bit)/ 2 endwhile if a:0 > 0 let bits = repeat(a:1 - len(bits)) . bits endif return bits endfu " helper function: convert a string consisted of '0' or '1' indicating number to a number " precondition: bits must not be empty string fu! s:Bits2Number(bits) let len = len(a:bits) let n = a:bits[0] let i = 1 while i < len let n = n * 2 + a:bits[i] let i += 1 endwhile return n endfu let s:modifier_keywords = ['strictfp', 'abstract', 'interface', 'native', 'transient', 'volatile', 'synchronized', 'final', 'static', 'protected', 'private', 'public'] fu! s:String2Flags(str) let mod = [0,0,0,0,0,0,0,0,0,0,0,0,] for item in split(a:str, '\s\+') if index(s:modifier_keywords, item) != -1 let mod[index(s:modifier_keywords, item)] = '1' endif endfor return join(mod[index(mod, '1'):], '') endfu " Log utilities {{{1 " level " 5 off/fatal " 4 error " 3 warn " 2 info " 1 debug " 0 trace fu! java_parser#SetLogLevel(level) let b:loglevel = a:level endfu fu! java_parser#GetLogLevel() return exists('b:loglevel') ? b:loglevel : 3 endfu fu! java_parser#GetLogContent() return b:log endfu fu! s:Trace(msg) call s:Log(0, b:pos, a:msg) endfu fu! s:Debug(msg) call s:Log(1, b:pos, a:msg) endfu fu! s:Info(msg) call s:Log(2, b:pos, a:msg) endfu fu! s:Log(level, pos, key, ...) if a:level >= java_parser#GetLogLevel() echo a:key call add(b:log, a:key) endif endfu fu! s:ShowWatch(...) let at = a:0 > 0 ? a:1 : '' echo '-- b:bp ' . b:bp . string(java_parser#DecodePos(b:bp)) . ' b:ch "' . b:ch . '" b:name ' . b:name . ' b:token ' . b:token . ' b:pos ' .b:pos . ' endPos ' . b:endPos . ' prevEndPos ' . b:prevEndPos . ' errPos ' . b:errPos . ' errorEndPos ' . b:errorEndPos . at endfu fu! java_parser#Exe(cmd) exe a:cmd endfu " Parser {{{1 " skip() Skip forward until a suitable stop token is found. {{{2 fu! s:skip(stopAtImport, stopAtMemberDecl, stopAtIdentifier, stopAtStatement) while 1 if b:token == 'SEMI' call s:nextToken() return elseif b:token =~# '^\(PUBLIC\|FINAL\|ABSTRACT\|MONKEYS_AT\|EOF\|CLASS\|INTERFACE\|ENUM\)$' return elseif b:token == 'IMPORT' if a:stopAtImport return endif elseif b:token =~# '^\(LBRACE\|RBRACE\|PRIVATE\|PROTECTED\|STATIC\|TRANSIENT\|NATIVE\|VOLATILE\|SYNCHRONIZED\|STRICTFP\|LT\|BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\|VOID\)$' if a:stopAtMemberDecl return endif elseif b:token == 'IDENTIFIER' if a:stopAtIdentifier return endif elseif b:token =~# '^\(CASE\|DEFAULT\|IF\|FOR\|WHILE\|DO\|TRY\|SWITCH\|RETURN\|THROW\|BREAK\|CONTINUE\|ELSE\|FINALLY\|CATCH\)$' if a:stopAtStatement return endif endif call s:nextToken() endwhile endfu " syntax errors {{{2 fu! s:SyntaxError(key, ...) let pos = a:0 == 0 ? b:pos : a:1 let errs = a:0 > 1 ? a:2 : [] call s:setErrorEndPos(pos) call s:ReportSyntaxError(pos, a:key) return {'tag': 'ERRONEOUS', 'pos': pos, 'errs': errs} endfu fu! s:ReportSyntaxError(pos, key, ...) if a:pos > b:errPos || a:pos == -1 let key = a:key . (b:token == 'EOF' ? ' and premature.eof' : '') call s:Log(4, a:pos, '[syntax error]' . s:Pos2Str(a:pos) . ': ' . key) endif let b:errPos = a:pos endfu " accept() {{{2 fu! s:accept(token_type) "call s:Debug(b:token . ' == ' . a:token_type . (b:token == a:token_type)) if b:token == a:token_type call s:nextToken() else call s:setErrorEndPos(b:pos) call s:ReportSyntaxError(b:prevEndPos, s:token2string(a:token_type) . " expected") "call s:nextToken() endif endfu fu! s:token2string(token) if a:token =~# '^\(DOT\|COMMA\|SEMI\|LPAREN\|RPAREN\|LBRACKET\|RBRACKET\|LBRACE\|RBRACE\)$' return "'" . a:token . "'" endif return a:token endfu " illegal() {{{2 fu! s:illegal(...) call s:setErrorEndPos(b:pos) return s:SyntaxError(s:modeAndEXPR() ? 'illegal.start.of.expr' : 'illegal.start.of.type', a:0 == 0 ? b:pos : a:1) endfu " setErrorEndPos() {{{2 fu! s:setErrorEndPos(errPos) if a:errPos > b:errorEndPos let b:errorEndPos = a:errPos endif endfu " ident() {{{2 " Ident = IDENTIFIER fu! s:ident() call s:Trace('s:ident ' . b:token) if b:token == 'IDENTIFIER' let name = b:name call s:nextToken() return name elseif b:token == 'ASSERT' if s:allowAsserts call s:Log(4, b:pos, 'assert.as.identifier') call s:nextToken() return '' else call s:Log(3, b:pos, 'assert.as.identifier') let name = b:name call s:nextToken() return name endif elseif b:token == 'ENUM' if b:allowEnums call s:Log(4, b:pos, 'enum.as.identifier') call s:nextToken() return '' else call s:Log(3, b:pos, 'enum.as.identifier') let name = b:name call s:nextToken() return name endif else call s:accept('IDENTIFIER') return '' endif endfu " qualident() {{{2 " Qualident = Ident { DOT Ident } fu! s:qualident() let t = s:Ident(b:pos, s:ident()) while b:token == 'DOT' let pos = b:pos call s:nextToken() let t = s:Select(b:pos, t, s:ident()) "let t.name .= '.' . s:ident() " FIXME endwhile return t endfu " literal() {{{2 " Literal = INTLITERAL | LONGLITERAL | FLOATLITERAL | DOUBLELITERAL | CHARLITERAL | STRINGLITERAL | TRUE | FALSE | NULL fu! s:literal(prefix) let t = {'tag': 'LITERAL', 'pos': b:pos} if b:token == 'INTLITERAL' let t.typetag = 'INT' let t.value = b:sbuf elseif b:token == 'LONGLITERAL' let t.typetag = 'LONG' let t.value = b:sbuf elseif b:token == 'FLOATLITERAL' let t.typetag = 'FLOAT' let t.value = b:sbuf elseif b:token == 'DOUBLELITERAL' let t.typetag = 'DOUBLE' let t.value = b:sbuf elseif b:token == 'CHARLITERAL' let t.typetag = 'CHAR' let t.value = b:sbuf elseif b:token == 'STRINGLITERAL' let t.typetag = 'CLASS' let t.value = b:sbuf elseif b:token == 'TRUE' let t.typetag = 'BOOLEAN' let t.value = 1 elseif b:token == 'FALSE' let t.typetag = 'BOOLEAN' let t.value = 0 elseif b:token == 'NULL' let t.typetag = 'BOT' let t.value = 'null' else echoerr 'can not reach here' endif call s:nextToken() return t endfu " {{{2 " terms, expression, type {{{2 " When terms are parsed, the mode determines which is expected: " mode = EXPR : an expression " mode = TYPE : a type " mode = NOPARAMS : no parameters allowed for type " mode = TYPEARG : type argument let s:EXPR = 1 let s:TYPE = 2 let s:NOPARAMS = 4 let s:TYPEARG = 8 let s:EXPR_OR_TYPE = 3 let s:EXPR_OR_TYPE_OR_NOPARAMS = 7 let s:TYPEARG_OR_NOPARAMS = 12 fu! s:modeAndEXPR() return b:mode % 2 endfu fu! s:modeAndTYPE() return s:BitAnd(b:mode, s:TYPE) endfu " terms can be either expressions or types. fu! s:expression() return s:term(s:EXPR) endfu fu! s:type() return s:term(s:TYPE) endfu fu! s:typeList() let ts = [] call add(ts, s:type()) while b:token == 'COMMA' call s:nextToken() call add(ts, s:type()) endwhile return ts endfu " Expression = Expression1 [ExpressionRest] " ExpressionRest = [AssignmentOperator Expression1] " AssignmentOperator = "=" | "+=" | "-=" | "*=" | "/=" | "&=" | "|=" | "^=" | " "%=" | "<<=" | ">>=" | ">>>=" " Type = Type1 " TypeNoParams = TypeNoParams1 " StatementExpression = Expression " ConstantExpression = Expression fu! s:term(...) let prevmode = b:mode let b:mode = a:0 == 0 ? b:mode : a:1 let t = s:term1() if s:modeAndEXPR() && b:token == 'EQ' || b:token =~# '^\(PLUSEQ\|SUBEQ\|STAREQ\|SLASHEQ\|AMPEQ\|BAREQ\|CARETEQ\|PERCENTEQ\|LTLTEQ\|GTGTEQ\|GTGTGTEQ\)$' let t = s:termRest(t) endif let b:lastmode = b:mode let b:mode = prevmode return t endfu fu! s:termRest(t) if b:token == 'EQ' let pos = b:pos call s:nextToken() let b:mode = s:EXPR return {'tag': 'ASSIGN', 'pos': pos, 'lhs': a:t, 'rhs': s:term()} elseif b:token =~# '^\(PLUSEQ\|SUBEQ\|STAREQ\|SLASHEQ\|PERCENTEQ\|AMPEQ\|BAREQ\|CARETEQ\|LTLTEQ\|GTGTEQ\|GTGTGTEQ\)$' let pos = b:pos let token = b:token call s:nextToken() let b:mode = s:EXPR return {'tag': s:optag(token), 'pos': pos, 'lhs': a:t, 'rhs': s:term()} else return a:t endif endfu " Expression1 = Expression2 [Expression1Rest] " Type1 = Type2 " TypeNoParams1 = TypeNoParams2 fu! s:term1() let t = s:term2() if s:modeAndEXPR() && b:token == 'QUES' let b:mode = s:EXPR return s:term1Rest(t) else return t endif endfu " Expression1Rest = ["?" Expression ":" Expression1] fu! s:term1Rest(t) if b:token == 'QUES' let t = {'tag': 'CONDEXPR', 'pos': b:pos, 'cond': a:t} call s:nextToken() let t.truepart = s:term() call s:accept('COLON') let t.falsepart = s:term1() return t else return a:t endif endfu " Expression2 = Expression3 [Expression2Rest] " Type2 = Type3 " TypeNoParams2 = TypeNoParams3 fu! s:term2() let t = s:term3() if s:modeAndEXPR() && s:prec(b:token) >= s:opprecedences.orPrec let b:mode = s:EXPR return s:term2Rest(t, s:opprecedences.orPrec) else return t endif endfu " Expression2Rest = {infixop Expression3} "" | Expression3 instanceof Type " infixop = "||" " | "&&" " | "|" " | "^" " | "&" " | "==" | "!=" " | "<" | ">" | "<=" | ">=" " | "<<" | ">>" | ">>>" " | "+" | "-" " | "*" | "/" | "%" fu! s:term2Rest(t, minprec) let odStack = [a:t] " for expressions let opStack = [] " for tokens let top = 0 let startPos = b:pos let topOp = 'ERROR' while s:prec(b:token) >= a:minprec call add(opStack, topOp) let top += 1 let topOp = b:token let pos = b:pos call s:nextToken() call add(odStack, topOp == 'INSTANCEOF' ? s:type() : s:term3()) while top > 0 && s:prec(topOp) >= s:prec(b:token) let odStack[top-1] = s:makeOp(pos, topOp, odStack[top-1], odStack[top]) let top -= 1 let topOp = opStack[top] endwhile endwhile "assert top == 0 let t = odStack[0] if t.tag == 'PLUS' let buf = s:foldStrings(t) if buf != '' let t = {'tag': 'LITERAL', 'pos': startPos, 'typetag': 'CLASS', 'value': t} endif endif return t endfu fu! s:makeOp(pos, topOp, od1, od2) if a:topOp == 'INSTANCEOF' return {'tag': 'TYPETEST', 'pos': a:pos, 'expr': a:od1, 'clazz': a:od2} else return s:Binary(a:pos, s:optag(a:topOp), a:od1, a:od2) endif endfu fu! s:foldStrings(tree) let tree = a:tree let buf = '' while 1 if tree.tag == 'LITERAL' let lit = tree if lit.typetag == 'CLASS' let sbuf = lit.value if buf != '' let sbuf .= buf endif return sbuf endif elseif tree.tag == 'PLUS' let op = tree if op.rhs.tag == 'LITERAL' let lit = op.rhs if lit.typetag == 'CLASS' let buf = lit.value . buf let tree = op.lhs continue endif endif endif return '' endwhile endfu " Expression3 = PrefixOp Expression3 {{{2 " | "(" Expr | TypeNoParams ")" Expression3 " | Primary {Selector} {PostfixOp} " Primary = "(" Expression ")" " | Literal " | [TypeArguments] THIS [Arguments] " | [TypeArguments] SUPER SuperSuffix " | NEW [TypeArguments] Creator " | Ident { "." Ident } " [ "[" ( "]" BracketsOpt "." CLASS | Expression "]" ) " | Arguments " | "." ( CLASS | THIS | [TypeArguments] SUPER Arguments | NEW [TypeArguments] InnerCreator ) " ] " | BasicType BracketsOpt "." CLASS " PrefixOp = "++" | "--" | "!" | "~" | "+" | "-" " PostfixOp = "++" | "--" " Type3 = Ident { "." Ident } [TypeArguments] {TypeSelector} BracketsOpt " | BasicType " TypeNoParams3 = Ident { "." Ident } BracketsOpt " Selector = "." [TypeArguments] Ident [Arguments] " | "." THIS " | "." [TypeArguments] SUPER SuperSuffix " | "." NEW [TypeArguments] InnerCreator " | "[" Expression "]" " TypeSelector = "." Ident [TypeArguments] " SuperSuffix = Arguments | "." Ident [Arguments] " NOTE: We need only type expression. fu! s:term3() let pos = b:pos let t = copy(s:TTree) call s:Debug('term3() b:token is ' . b:token) let typeArgs = s:typeArgumentsOpt(s:EXPR) if b:token == 'QUES' if s:modeAndTYPE() && s:BitAnd(b:mode, s:TYPEARG_OR_NOPARAMS) == s:TYPEARG let b:mode = s:TYPE return s:typeArgument() else return s:illegal() endif elseif b:token =~# '^\(PLUSPLUS\|SUBSUB\|BANG\|TILDE\|PLUS\|SUB\)$' if typeArgs == [] && s:modeAndEXPR() let token = b:token call s:nextToken() let b:mode = s:EXPR if token == 'SUB' && (b:token == 'INTLITERAL' || b:token == 'LONGLITERAL') && b:radix == 10 let b:mode = s:EXPR let t = s:literal('-') else let t = s:term3() return s:Unary(pos, s:unoptag(token), t) endif else return s:illegal() endif elseif b:token == 'LPAREN' if typeArgs == [] && s:modeAndEXPR() call s:nextToken() let b:mode = s:EXPR_OR_TYPE_OR_NOPARAMS let t = s:term3() if s:modeAndEXPR() && b:token == 'LT' let op = 'LT' let pos1 = b:pos call s:nextToken() let b:mode = s:BitAnd(b:mode, s:EXPR_OR_TYPE) let b:mode = s:BitOr(b:mode, s:TYPEARG) let t1 = s:term3() if s:modeAndTYPE() && (b:token == 'COMMA' || b:token == 'GT') let b:mode = s:TYPE let args = [] call add(args, t1) while b:token == 'COMMA' call s:nextToken() call add(args, s:typeArgument()) endwhile call s:accept('GT') let t = {'tag': 'TYPEAPPLY', 'pos': pos1, 'clazz': t, 'arguments': args} " checkGenerics let t = s:bracketsOpt(t) elseif s:modeAndEXPR() let b:mode = s:EXPR let t = s:Binary(pos1, op, t, s:term2Rest(t1, s:opprecedences.shiftPrec)) let t = s:termRest(s:term1Rest(s:term2Rest(t, s:opprecedences.orPrec))) else call s:accept('GT') endif else let t = s:termRest(s:term1Rest(s:term2Rest(t, s:opprecedences.orPrec))) endif call s:accept('RPAREN') let b:lastmode = b:mode let b:mode = s:EXPR if s:BitAnd(b:lastmode, s:EXPR) == 0 return s:TypeCast(pos, t, s:term3()) elseif s:BitAnd(b:lastmode, s:TYPE) != 0 if b:token =~# '^\(BANG\|TILDE\|LPAREN\|THIS\|SUPER\|INTLITERAL\|LONGLITERAL\|FLOATLITERAL\|DOUBLELITERAL\|CHARLITERAL\|STRINGLITERAL\|TRUE\|FALSE\|NULL\|NEW\|IDENTIFIER\|ASSERT\|ENUM\|BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\|VOID\)$' return s:TypeCast(pos, t, s:term3()) endif endif else return s:illegal() endif let t = {'tag': 'PARENS', 'pos': pos, 'expr': t} elseif b:token == 'THIS' if s:modeAndEXPR() let b:mode = s:EXPR let t = s:Ident(pos, 'this') call s:nextToken() if typeArgs == [] let t = s:argumentsOpt([], t) else let t = s:arguments(typeArgs, t) endif let typeArgs = [] else return s:illegal() endif elseif b:token == 'SUPER' if s:modeAndEXPR() let b:mode = s:EXPR let t = s:superSuffix(typeArgs, s:Ident(pos, 'super')) let typeArgs = [] else return s:illegal() endif elseif b:token =~# '^\(INTLITERAL\|LONGLITERAL\|FLOATLITERAL\|DOUBLELITERAL\|CHARLITERAL\|STRINGLITERAL\|TRUE\|FALSE\|NULL\)$' if typeArgs == [] && s:modeAndEXPR() let b:mode = s:EXPR let t = s:literal('') else return s:illegal() endif elseif b:token == 'NEW' if typeArgs != [] return s:illegal() endif if s:modeAndEXPR() let b:mode = s:EXPR call s:nextToken() if b:token == 'LT' let typeArgs = s:typeArguments() endif let t = s:creator(pos, typeArgs) let typeArgs = [] else return s:illegal() endif elseif b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$' if typeArgs != [] return s:illegal() endif let t = s:Ident(pos, s:ident()) while 1 if b:token == 'LBRACKET' "let t.value = '[' " FIXME call s:nextToken() if b:token == 'RBRACKET' "let t.value .= ']' call s:nextToken() let t = s:bracketsSuffix(s:TypeArray(pos, s:bracketsOpt(t))) else if s:modeAndEXPR() let b:mode = s:EXPR let t = {'tag': 'INDEXED', 'pos': pos, 'indexed': t, 'index': s:term()} endif call s:accept('RBRACKET') "let t.value .= ']' endif break elseif b:token == 'LPAREN' if s:modeAndEXPR() let b:mode = s:EXPR let t = s:arguments(typeArgs, t) let typeArgs = [] "call s:accept('LPAREN') "call s:gotoMatchEnd('(', ')', b:token == 'LPAREN') "call s:accept('RPAREN') endif break elseif b:token == 'DOT' call s:nextToken() let typeArgs = s:typeArgumentsOpt(s:EXPR) if s:modeAndEXPR() if b:token == 'CLASS' || b:token == 'THIS' if typeArgs != [] return s:illegal() endif let b:mode = s:EXPR let t = s:Select(pos, t, b:token == 'CLASS' ? 'class' : 'this') call s:nextToken() break elseif b:token == 'SUPER' let b:mode = s:EXPR let t = s:Select(pos, t, 'super') let t = s:superSuffix(typeArgs, t) let typeArgs = [] break elseif b:token == 'NEW' if typeArgs != [] return s:illegal() endif let b:mode = s:EXPR let pos1 = b:pos call s:nextToken() if b:token == 'LT' let typeArgs = s:typeArguments() endif let t = s:innerCreator(pos1, typeArgs, t) let typeArgs = [] break endif endif let t = s:Select(pos, t, s:ident()) else break endif endwhile if typeArgs != [] | call s:illegal() | endif let t = s:typeArgumentsOpt2(t) elseif b:token =~# '^\(BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\)$' if typeArgs != [] | call s:illegal() | endif let t = s:bracketsSuffix(s:bracketsOpt(s:basicType())) elseif b:token == 'VOID' if typeArgs != [] | call s:illegal() | endif if s:modeAndEXPR() call s:nextToken() if b:token == 'DOT' let ti = {'tag': 'TYPEIDENT', 'pos': pos, 'typetag': 'void'} " FIXME let t = s:bracketsSuffix(ti) else return s:illegal(pos) endif else return s:illegal() endif else return s:illegal() endif if typeArgs != [] return s:illegal() endif while 1 let pos1 = b:pos if b:token == 'LBRACKET' call s:nextToken() if s:modeAndEXPR() let oldmode = b:mode let b:mode = s:TYPE if b:token == 'RBRACKET' call s:nextToken() let t = s:bracketsOpt(t) let t = s:TypeArray(pos1, t) return t endif let b:mode = oldmode endif if s:modeAndEXPR() let b:mode = s:EXPR let t = {'tag': 'INDEXED', 'pos': pos1, 'indexed': t, 'index': s:term()} endif call s:accept('RBRACKET') elseif b:token == 'DOT' call s:nextToken() let typeArgs = s:typeArgumentsOpt(s:EXPR) if b:token == 'SUPER' && s:modeAndEXPR() let b:mode = s:EXPR let t = s:Select(pos1, t, 'super') call s:nextToken() let t = s:arguments(typeArgs, t) let typeArgs = [] elseif b:token == 'NEW' && s:modeAndEXPR() if typeArgs != [] return s:illegal() endif let b:mode = s:EXPR let pos2 = b:pos call s:nextToken() if b:token == 'LT' let typeArgs = s:typeArguments() endif let t = s:innerCreator(pos2, typeArgs, t) let typeArgs = [] else let t = s:Select(pos1, t, s:ident()) let t = s:argumentsOpt(typeArgs, s:typeArgumentsOpt2(t)) let typeArgs = [] endif else break endif endwhile while (b:token == 'PLUSPLUS' || b:token == 'SUBSUB') && s:modeAndEXPR() let b:mode = s:EXPR let t = s:Unary(b:pos, b:token == 'PLUSPLUS' ? 'POSTINC' : 'POSTDEC', t) call s:nextToken() endwhile return t endfu fu! s:superSuffix(typeArgs, t) let typeArgs = a:typeArgs let t = a:t call s:nextToken() if b:token == 'LPAREN' || typeArgs != [] let t = s:arguments(typeArgs, t) else let pos = b:pos call s:accept('DOT') let typeArgs = b:token == 'LT' ? s:typeArguments() : [] let t = s:Select(pos, t, s:ident()) let t = s:argumentsOpt(typeArgs, t) endif return t endfu " BasicType = BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE | BOOLEAN {{{2 fu! s:basicType() let t = {'tag': 'TYPEIDENT', 'pos': b:pos, 'typetag': s:typetag(b:token)} call s:nextToken() return t endfu " ArgumentsOpt = [ Arguments ] {{{2 fu! s:argumentsOpt(typeArgs, t) if s:modeAndEXPR() && b:token == 'LPAREN' || a:typeArgs != [] let b:mode = s:EXPR return s:arguments(a:typeArgs, a:t) else return a:t endif endfu " Arguments = "(" [Expression { COMMA Expression }] ")" fu! s:arguments(...) let pos = b:pos let args = [] if b:token == 'LPAREN' call s:nextToken() if b:token != 'RPAREN' call add(args, s:expression()) while b:token == 'COMMA' call s:nextToken() call add(args, s:expression()) endwhile endif call s:accept('RPAREN') else call s:SyntaxError(') expected') endif if a:0 == 0 return args else let typeArgs = a:1 let t = a:2 return {'tag': 'APPLY', 'pos': pos, 'typeargs': typeArgs, 'meth': t, 'args': args} endif endfu " typeArgument generic type {{{2 fu! s:typeArgumentsOpt2(t) if b:token == 'LT' && s:modeAndTYPE() && s:BitAnd(b:mode, s:NOPARAMS) == 0 let b:mode = s:TYPE " checkGenerics() return s:typeArguments(a:t) else return a:t endif endfu fu! s:typeArgumentsOpt(...) let useMode = a:0 == 0 ? s:TYPE : a:1 if b:token == 'LT' " checkGenerics() if s:BitAnd(b:mode, useMode) == 0 || s:BitAnd(b:mode, s:NOPARAMS) != 0 return s:illegal() endif let b:mode = useMode return s:typeArguments() endif return [] endfu " TypeArguments = "<" TypeArgument {"," TypeArgument} ">" fu! s:typeArguments(...) let pos = b:pos let args = [] if b:token == 'LT' call s:nextToken() call add(args, s:modeAndEXPR() ? s:type() : s:typeArgument()) while b:token == 'COMMA' call s:nextToken() call add(args, s:modeAndEXPR() ? s:type() : s:typeArgument()) endwhile if b:token == 'GTGTGTEQ' let b:token = 'GTGTEQ' elseif b:token == 'GTGTEQ' let b:token = 'GTEQ' elseif b:token == 'GTEQ' let b:token = 'EQ' elseif b:token == 'GTGTGT' let b:token = 'GTGT' elseif b:token == 'GTGT' let b:token = 'GT' else call s:accept('GT') endif else call s:SyntaxError("LT expected") endif if a:0 == 0 return args else return {'tag': 'TYPEAPPLY', 'pos': pos, 'clazz': a:1, 'arguments': args} endif endfu " TypeArgument = Type " | "?" " | "?" EXTENDS Type {"&" Type} " | "?" SUPER Type fu! s:typeArgument() if b:token != 'QUES' return s:type() endif call s:nextToken() if b:token == 'EXTENDS' call s:nextToken() return s:type() elseif b:token == 'SUPER' call s:nextToken() return s:type() elseif b:token == 'IDENTIFIER' let id = ident() else endif endfu " BracketsOpt = {"[" "]"} {{{2 fu! s:bracketsOpt(t) let t = a:t while b:token == 'LBRACKET' let pos = b:pos call s:nextToken() let t = s:bracketsOptCont(t, pos) endwhile return t endfu fu! s:bracketsOptCont(t, pos) let t = a:t call s:accept('RBRACKET') let t = s:bracketsOpt(t) return s:TypeArray(a:pos, t) endfu " BracketsSuffixExpr = "." CLASS " BracketsSuffixType = fu! s:bracketsSuffix(t) let t = a:t if s:modeAndEXPR() && b:token == 'DOT' let b:mode = s:EXPR let pos = b:pos call s:nextToken() call s:accept('CLASS') if b:pos == b:errorEndPos let name = '' if b:token == 'IDENTIFIER' let name = b:name call s:nextToken() else let name = '' endif let t = {'tag': 'ERRONEOUS', 'pos': pos, 'errs': [s:Select(pos, t, name)]} else let t = s:Select(pos, t, 'class') endif elseif s:modeAndTYPE() let b:mode = s:TYPE else call s:SyntaxError('dot.class.expected') endif return t endfu " creator {{{2 fu! s:creator(newpos, typeArgs) if b:token =~# '^\(BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\)$' if a:typeArgs == [] return s:arrayCreatorRest(a:newpos, s:basicType()) endif endif let t = s:qualident() let oldmode = b:mode let b:mode = s:TYPE if b:token == 'LT' "checkGenerics let t = s:typeArguments(t) endif let b:mode = oldmode if b:token == 'LBRACKET' return s:arrayCreatorRest(a:newpos, t) elseif b:token == 'LPAREN' return s:classCreatorRest(a:newpos, {}, a:typeArgs, t) else call s:ReportSyntaxError(b:pos, '( or [ expected') let t = {'tag': 'NEWCLASS', 'encl': {}, 'typeargs': a:typeArgs, 'clazz': t, 'args': [], 'def': {}} return {'tag': 'ERRONEOUS', 'pos': a:newpos, 'errs': [t]} endif endfu fu! s:innerCreator(newpos, typeArgs, encl) let t = s:Ident(b:pos, s:ident()) if b:token == 'LT' " checkGenerics let t = TypeArguments(t) endif return s:classCreatorRest(a:newpos, a:encl, a:typeArgs, t) endfu fu! s:arrayCreatorRest(newpos, elemtype) let elemtype = a:elemtype call s:accept('LBRACKET') if b:token == 'RBRACKET' call s:accept('RBRACKET') let elemtype = s:bracketsOpt(elemtype) if b:token == 'LBRACE' return s:arrayInitializer(a:newpos, elemtype) else return s:SyntaxError('array.dimension.missing') endif else let dims = [s:expression()] call s:accept('RBRACKET') while b:token == 'LBRACKET' let pos = b:pos call s:nextToken() if b:token == 'RBRACKET' let elemtype = s:bracketsOptCont(elemtype, pos) else call add(dims, s:expression()) call s:accept('RBRACKET') endif endwhile return {'tag': 'NEWARRAY', 'pos': a:newpos, 'elemtype': elemtype, 'dims': dims, 'elems': {}} endif endfu fu! s:classCreatorRest(newpos, encl, typeArgs, t) let args = s:arguments() let body = {} if b:token == 'LBRACE' let body = s:ClassDef(b:pos, {}) let body.defs = s:classOrInterfaceBody('', 0) let body.endpos = b:pos endif return {'tag': 'NEWCLASS', 'encl': a:encl, 'typeargs': a:typeArgs, 'clazz': a:t, 'args': args, 'def': body} endfu " ArrayInitializer = "{" [VariableInitializer {"," VariableInitializer}] [","] "}" {{{2 fu! s:arrayInitializer(newpos, t) call s:accept('LBRACE') let elems = [] if b:token == 'COMMA' call s:nextToken() elseif b:token != 'RBRACE' call add(elems, s:variableInitializer()) while b:token == 'COMMA' call s:nextToken() if b:token == 'RBRACE' break endif call add(elems, s:variableInitializer()) endwhile endif call s:accept('RBRACE') return {'tag': 'NEWARRAY', 'pos': a:newpos, 'elemtype': a:t, 'dims': [], 'elems': elems} endfu " VariableInitializer = ArrayInitializer | Expression {{{2 fu! s:variableInitializer() return b:token == 'LBRACE' ? s:arrayInitializer(b:pos, {}) : s:expression() endfu " ParExpression = "(" Expression ")" {{{2 fu! s:parExpression() call s:accept('LPAREN') let t = s:expression() call s:accept('RPAREN') return t endfu " {{{2 " Block = "{" BlockStatements "}" {{{2 fu! s:block(...) let t = {'tag': 'BLOCK', 'stats': []} let t.pos = a:0 > 0 ? a:1 : b:pos let t.flags = a:0 > 1 ? a:2 : 0 call s:accept('LBRACE') " scan strategy: ignore statements? if a:0 > 2 && a:3 if b:token !=# 'RBRACE' let b:pos = s:gotoMatchEnd('{', '}', b:token == 'LBRACE') endif "let t.stats = Strpart(t.pos, t.endpos-t.pos-1) else let t.stats = s:blockStatements() while b:token == 'CASE' || b:token == 'DEFAULT' call s:SyntaxError("orphaned") call s:switchBlockStatementGroups() endwhile endif let t.endpos = b:pos call s:accept('RBRACE') return t endfu " BlockStatements = { BlockStatement } {{{2 " BlockStatement = LocalVariableDeclarationStatement " | ClassOrInterfaceOrEnumDeclaration " | [Ident ":"] Statement " LocalVariableDeclarationStatement " = { FINAL | '@' Annotation } Type VariableDeclarators ";" fu! s:blockStatements() let lastErrPos = -1 let stats = [] while 1 let pos = b:pos if b:token =~# '^\(RBRACE\|CASE\|DEFAULT\|EOF\)$' return stats elseif b:token =~# '^\(LBRACE\|IF\|FOR\|WHILE\|DO\|TRY\|SWITCH\|SYNCHRONIZED\|RETURN\|THROW\|BREAK\|CONTINUE\|SEMI\|ELSE\|FINALLY\|CATCH\)$' call add(stats, s:statement()) elseif b:token =~# '^\(MONKEYS_AT\|FINAL\)' let dc = b:docComment let mods = s:modifiersOpt() if b:token =~# '^\(INTERFACE\|CLASS\|ENUM\)$' call add(stats, s:classOrInterfaceOrEnumDeclaration(mods, dc)) else let t = s:type() let stats = stats + s:variableDeclarators(mods, t, []) call s:accept('SEMI') endif elseif b:token =~# '^\(ABSTRACT\|STRICTFP\|CLASS\|INTERFACE\|ENUM\)$' if b:token == 'ENUM' call s:Log(4, b:pos, 'local.enum') endif call add(stats, s:classOrInterfaceOrEnumDeclaration(s:modifiersOpt(), b:docComment)) elseif b:token == 'ASSERT' call add(stats, s:statement()) else let name = b:name let t = s:term(s:EXPR_OR_TYPE) if b:token == 'COLON' && t.tag == 'IDENT' call s:nextToken() let stat = s:statement() call add(stats, {'tag': 'LABELLED', 'pos': b:pos, 'label': name, 'body': stat}) elseif s:BitAnd(b:lastmode, s:TYPE) && b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$' let pos = b:pos let mods = {} " {'tag': 'MODIFIERS', 'pos': -1, 'flags': 0} let stats = stats + s:variableDeclarators(mods, t, []) call s:accept('SEMI') else call add(stats, {'tag': 'EXEC', 'pos': pos, 'expr': s:checkExprStat(t)}) " TODO call s:accept('SEMI') endif endif if b:pos == lastErrPos return stats endif if b:pos <= b:errorEndPos call s:skip(0, 1, 1, 1) let lastErrPos = b:pos endif " resetDeprecatedFlag() endwhile endfu " Statement = Block {{{2 " | IF ParExpression Statement [ELSE Statement] " | FOR "(" ForInitOpt ";" [Expression] ";" ForUpdateOpt ")" Statement " | FOR "(" FormalParameter : Expression ")" Statement " | WHILE ParExpression Statement " | DO Statement WHILE ParExpression ";" " | TRY Block ( Catches | [Catches] FinallyPart ) " | SWITCH ParExpression "{" SwitchBlockStatementGroups "}" " | SYNCHRONIZED ParExpression Block " | RETURN [Expression] ";" " | THROW Expression ";" " | BREAK [Ident] ";" " | CONTINUE [Ident] ";" " | ASSERT Expression [ ":" Expression ] ";" " | ";" " | ExpressionStatement " | Ident ":" Statement " called only by BlockStatements or self fu! s:statement() let pos = b:pos if b:token == 'LBRACE' return s:block() elseif b:token == 'IF' call s:nextToken() let t = {'tag': 'IF', 'pos': pos, 'cond': s:parExpression(), 'thenpart': s:statement()} if b:token == 'ELSE' call s:nextToken() let t.elsepart = s:statement() endif let t.endpos = b:pos return t elseif b:token == 'FOR' call s:nextToken() call s:accept('LPAREN') let inits = b:token == 'SEMI' ? [] : s:forInit() " foreach if len(inits) == 1 && inits[0].tag == 'VARDEF' && (!has_key(inits[0], 'init') || inits[0].init == {}) && b:token == 'COLON' " checkForeach let var = inits[0] call s:accept('COLON') let expr = s:expression() call s:accept('RPAREN') let body = s:statement() return {'tag': 'FOREACHLOOP', 'pos': pos, 'endpos': b:pos, 'var': var, 'expr': expr, 'body': body} else call s:accept('SEMI') let cond = b:token == 'SEMI' ? {} : s:expression() call s:accept('SEMI') let steps = b:token == 'RPAREN' ? [] : s:forUpdate() call s:accept('RPAREN') let body = s:statement() return {'tag': 'FORLOOP', 'pos': pos, 'endpos': b:pos, 'init': inits, 'cond': cond, 'step': steps, 'body': body} endif elseif b:token == 'WHILE' call s:nextToken() let cond = s:parExpression() let body = s:statement() return {'tag': 'WHILELOOP', 'pos': pos, 'endpos': b:pos, 'cond': cond, 'body': body} elseif b:token == 'DO' call s:nextToken() let body = s:statement() call s:accept('WHILE') let cond = s:parExpression() let t = {'tag': 'DOLOOP', 'pos': pos, 'endpos': b:pos, 'cond': cond, 'body': body} call s:accept('SEMI') return t elseif b:token == 'TRY' call s:nextToken() let body = s:block() let catchers = [] let finalizer = {} if b:token == 'CATCH' || b:token == 'FINALLY' while b:token == 'CATCH' call add(catchers, s:catchClause()) endwhile if b:token == 'FINALLY' call s:nextToken() let finalizer = s:block() endif else call s:Log(4, b:pos, 'try.without.catch.or.finally') endif return {'tag': 'TRY', 'pos': pos, 'endpos': b:pos, 'body': body, 'catchers': catchers, 'finalizer': finalizer} elseif b:token == 'SWITCH' call s:nextToken() let selector = s:parExpression() call s:accept('LBRACE') let cases = s:switchBlockStatementGroups() call s:accept('RBRACE') return {'tag': 'SWITCH', 'pos': pos, 'endpos': b:pos, 'selector': selector, 'cases': cases} elseif b:token == 'SYNCHRONIZED' call s:nextToken() let lock = s:parExpression() let body = s:block() return {'tag': 'SYNCHRONIZED', 'pos': pos, 'endpos': b:pos, 'lock': lock, 'cases': body} elseif b:token == 'RETURN' call s:nextToken() let result = b:token == 'SEMI' ? {} : s:expression() call s:accept('SEMI') return {'tag': 'RETURN', 'pos': pos, 'endpos': b:pos, 'expr': result} elseif b:token == 'THROW' call s:nextToken() let exc = s:expression() call s:accept('SEMI') return {'tag': 'THROW', 'pos': pos, 'endpos': b:pos, 'expr': exc} elseif b:token == 'BREAK' || b:token == 'CONTINUE' let token = b:token call s:nextToken() let label = b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$' ? s:ident() : '' call s:accept('SEMI') return {'tag': token, 'pos': pos, 'endpos': b:pos, 'label': label} elseif b:token == 'SEMI' call s:nextToken() return {'tag': 'SKIP', 'pos': pos} elseif b:token == 'ELSE' return s:SyntaxError("else.without.if") elseif b:token == 'FINALLY' return s:SyntaxError("finally.without.try") elseif b:token == 'CATCH' return s:SyntaxError("catch.without.try") elseif b:token == 'ASSERT' "if b:allowAsserts && b:token == 'ASSERT' call s:nextToken() let t = {'tag': 'ASSERT', 'pos': pos, 'cond': s:expression()} if b:token == 'COLON' call s:nextToken() let t.detail = s:expression() endif call s:accept('SEMI') let t.endpos = b:pos return t "endif else " also ENUM let name = b:name let expr = s:expression() if b:token == 'COLON' && expr.tag == 'IDENT' call s:nextToken() let stat = s:statement() return {'tag': 'LABELLED', 'pos': pos, 'endpos': b:pos, 'label': name, 'body': stat} else let stat = {'tag': 'EXEC', 'pos': pos, 'endpos': b:pos, 'expr': s:checkExprStat(expr)} call s:accept('SEMI') return stat endif endif endfu " CatchClause = CATCH "(" FormalParameter ")" Block fu! s:catchClause() let pos = b:pos call s:accept('CATCH') call s:accept('LPAREN') let formal = s:variableDeclaratorId(s:optFinalParameter(), s:qualident()) call s:accept('RPAREN') let body = s:block() return {'tag': 'CATCH', 'pos': pos, 'endpos': b:pos, 'param': formal, 'body': body} endfu " SwitchBlockStatementGroups = { SwitchBlockStatementGroup } " SwitchBlockStatementGroup = SwitchLabel BlockStatements " SwitchLabel = CASE ConstantExpression ":" | DEFAULT ":" fu! s:switchBlockStatementGroups() let cases = [] while 1 let pos = b:pos if b:token == 'CASE' || b:token == 'DEFAULT' let token = b:token call s:nextToken() let pat = token == 'CASE' ? s:expression() : {} call s:accept('COLON') let stats = s:blockStatements() call add(cases, {'tag': 'CASE', 'pos': pos, 'pat': pat, 'stats': stats}) elseif b:token == 'RBRACE' || b:token == 'EOF' return cases else call s:nextToken() call s:SyntaxError('case.default.rbrace.expected') endif endwhile endfu " MoreStatementExpressions = { COMMA StatementExpression } fu! s:moreStatementExpressions(pos, first, stats) call add(a:stats, {'tag': 'EXEC', 'pos': a:pos, 'expr': s:checkExprStat(a:first)}) while b:token == 'COMMA' call s:nextToken() let pos = b:pos let t = s:expression() call add(a:stats, {'tag': 'EXEC', 'pos': pos, 'expr': s:checkExprStat(t)}) endwhile return a:stats endfu " ForInit = StatementExpression MoreStatementExpressions " | { FINAL | '@' Annotation } Type VariableDeclarators fu! s:forInit() let stats = [] let pos = b:pos if b:token == 'FINAL' || b:token == 'MONKEYS_AT' return s:variableDeclarators(s:optFinal(0), s:type(), stats) else let t = s:term(s:EXPR_OR_TYPE) if s:BitAnd(b:lastmode, s:TYPE) && b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$' return s:variableDeclarators(s:modifiersOpt(), t, stats) else return s:moreStatementExpressions(pos, t, stats) endif endif endfu " ForUpdate = StatementExpression MoreStatementExpressions fu! s:forUpdate() return s:moreStatementExpressions(b:pos, s:expression(), []) endfu " ModifiersOpt = { Modifier } {{{2 " Modifier = PUBLIC | PROTECTED | PRIVATE | STATIC | ABSTRACT | FINAL " | NATIVE | SYNCHRONIZED | TRANSIENT | VOLATILE | "@" " | "@" Annotation " NOTE: flags is a string, not a long number fu! s:modifiersOpt(...) let partial = a:0 == 0 ? {} : a:1 let flags = partial == {} ? 0 : partial.flags let annotations = partial == {} ? [] : partial.annotations " TODO: deprecatedFlag let pos = b:pos let lastPos = -1 while 1 let flag = 0 if b:token =~# '^\(PUBLIC\|PROTECTED\|PRIVATE\|STATIC\|ABSTRACT\|FINAL\|NATIVE\|SYNCHRONIZED\|TRANSIENT\|VOLATILE\|STRICTFP\|MONKEYS_AT\)$' let flag = b:token == 'MONKEYS_AT' ? s:Flags.ANNOTATION : get(s:Flags, b:token, 0) else break endif "if s:BitAnd(flags, flag) != 0 " "log.error(S.pos(), "repeated.modifier") "endif let lastPos = b:pos call s:nextToken() if flag == s:Flags.ANNOTATION "call s:checkAnnotations() if b:token != 'INTERFACE' let ann = s:annotation(lastPos) if flags == 0 && annotations == [] let pos = ann.pos endif call add(annotations, ann) let lastPos = ann.pos let flag = 0 endif endif let flags = s:BitOr(flags, flag) endwhile if b:token == 'ENUM' let flags = s:BitOr(flags, s:Flags.ENUM) elseif b:token == 'INTERFACE' let flags = s:BitOr(flags, s:Flags.INTERFACE) endif if flags == 0 && empty(annotations) let pos = -1 endif return {'tag': 'MODIFIERS', 'pos': pos, 'flags': flags, 'annotations': annotations} endfu " Annotation = "@" Qualident [ "(" AnnotationFieldValues ")" ] {{{2 fu! s:annotation(pos) "call s:checkAnnotations() let ident = s:qualident() "let endPos = b:prevEndPos let fieldValues = s:annotationFieldValuesOpt() return {'tag': 'ANNOTATION', 'pos': a:pos, 'annotationType': ident, 'args': fieldValues} endfu fu! s:annotationFieldValuesOpt() return b:token == 'LPAREN' ? s:annotationFieldValues() : [] endfu " AnnotationFieldValues = "(" [ AnnotationFieldValue { "," AnnotationFieldValue } ] ")" fu! s:annotationFieldValues() let buf = [] call s:accept('LPAREN') if b:token != 'RPAREN' call add(buf, s:annotationFieldValue()) while b:token == 'COMMA' call s:nextToken() call add(buf, s:annotationFieldValue()) endwhile endif call s:accept('RPAREN') return buf endfu " AnnotationFieldValue = AnnotationValue | Identifier "=" AnnotationValue fu! s:annotationFieldValue() call s:Trace('s:annotationFieldValue ' . b:token) if b:token == 'IDENTIFIER' let b:mode = s:EXPR let t1 = s:term1() if t1.tag == 'IDENT' && b:token == 'EQ' let pos = b:pos call s:accept('EQ') return {'tag': 'ASSIGN', 'pos': pos, 'lhs': t1, 'rhs': s:annotationValue()} else return t1 endif endif return s:annotationValue() endfu " AnnotationValue = ConditionalExpression | Annotation | "{" [ AnnotationValue { "," AnnotationValue } ] "}" fu! s:annotationValue() let pos = 0 if b:token == 'MONKEYS_AT' let pos = b:bp call s:nextToken() return s:annotation(pos) elseif b:token == 'LBRACE' let pos = b:pos call s:accept('LBRACE') let buf = [] if b:token != 'RBRACE' call add(buf, s:annotationValue()) while b:token == 'COMMA' call s:nextToken() if b:token == 'RPAREN' break endif call add(buf, s:annotationValue()) endwhile endif call s:accept('RBRACE') return buf else let b:mode = s:EXPR return s:term1() endif endfu " AnnotationsOpt = { '@' Annotation } fu! s:annotationsOpt() if b:token != 'MONKEYS_AT' return [] endif let buf = [] while b:token != 'MONKEYS_AT' let pos = b:pos call s:nextToken() call add(buf, s:annotation(pos)) endwhile return buf endfu " {{{2 " CompilationUnit {{{2 " CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"] {ImportDeclaration} {TypeDeclaration} fu! s:compilationUnit() let unit = {'tag': 'TOPLEVEL', 'pos': b:pos} let mods = {} if b:token == 'MONKEYS_AT' let mods = s:modifiersOpt() endif if b:token == 'PACKAGE' if mods != {} " checkNoMods(mods.flags) let unit.packageAnnotations = mods.annotations let mods = {} endif call s:nextToken() let unit.pid = s:qualident() let unit.package = java_parser#type2Str(unit.pid) call s:accept('SEMI') endif let imports = [] let s:types = [] let checkForImports = 1 while b:token != 'EOF' if b:pos <= b:errorEndPos call s:skip(checkForImports, 0, 0, 0) if b:token == 'EOF' break endif endif if checkForImports && mods == {} && b:token == 'IMPORT' call add(imports, s:importDeclaration()) else let def = s:typeDeclaration(mods) "if (def instanceof JCExpressionStatement) " def = ((JCExpressionStatement)def).expr "endif call add(s:types, def) if def.tag == 'CLASSDEF' let checkForImports = 0 endif let mods = {} endif endwhile let unit.imports = imports let unit.types = s:types unlet s:types return unit endfu " ImportDeclaration = IMPORT [ STATIC ] Ident { "." Ident } [ "." "*" ] ";" {{{2 " return fqn fu! s:importDeclaration() " OAO: Usualy it is in one line. if b:scanStrategy < 0 let idx = matchend(b:lines[b:line], '\(\s\+static\>\)\?\s\+\([_$a-zA-Z][_$a-zA-Z0-9_]*\)\(\s*\.\s*[_$a-zA-Z][_$a-zA-Z0-9_]*\)*\(\s*\.\s*\*\)\?;') if idx != -1 let fqn = strpart(b:lines[b:line], b:col, idx-b:col-1) let b:col = idx let b:bp = b:idxes[b:line] + b:col call s:nextToken() return fqn endif endif call s:Info('==import==') let pos = b:pos call s:nextToken() let importStatic = 0 if b:token == 'STATIC' " checkStaticImports() let importStatic = 1 call s:nextToken() endif let pid = s:Ident(b:pos, s:ident()) " let pos1 = b:pos call s:accept('DOT') if b:token == 'STAR' let pid = s:Select(pos1, pid, '*') call s:nextToken() else let pid = s:Select(pos1, pid, s:ident()) endif while b:token == 'DOT' let pos1 = b:pos call s:accept('DOT') if b:token == 'STAR' let pid = s:Select(pos1, pid, '*') call s:nextToken() break else let pid = s:Select(pos1, pid, s:ident()) endif endwhile let fqn = java_parser#type2Str(pid) if b:token != 'SEMI' let fqn .= '' endif call s:accept('SEMI') "return {'tag': 'IMPORT', 'pos': b:pos, 'qualid': pid, 'staticImport': importStatic} return fqn endfu " TypeDeclaration = ClassOrInterfaceOrEnumDeclaration | ";" {{{2 fu! s:typeDeclaration(mods) let pos = b:pos if a:mods == {} && b:token == 'SEMI' call s:nextToken() return {'tag': 'SKIP', 'pos': pos} else let dc = b:docComment let mods = s:modifiersOpt(a:mods) return s:classOrInterfaceOrEnumDeclaration(mods, dc) endif endfu fu! s:classOrInterfaceOrEnumDeclaration(mods, dc) call s:Info('== type ==') if b:token == 'CLASS' return s:classDeclaration(a:mods, a:dc) elseif b:token == 'INTERFACE' return s:interfaceDeclaration(a:mods, a:dc) elseif b:token == 'ENUM' "if !exists('b:allowEnums') || !b:allowEnums " call s:SyntaxError("enums.not.supported.in.source") "endif return s:enumDeclaration(a:mods, a:dc) else let pos = b:pos let errs = [] if b:token == 'IDENTIFIER' call add(errs, s:ident()) call s:setErrorEndPos(b:bp) endif return s:SyntaxError("class.or.intf.or.enum.expected", pos, errs) endif endfu " ClassDeclaration = CLASS Ident TypeParametersOpt [EXTENDS Type] [IMPLEMENTS TypeList] ClassBody {{{2 fu! s:classDeclaration(mods, dc) let type = s:ClassDef(b:pos, a:mods) call s:accept('CLASS') let type.name = s:ident() let type.typarams = s:typeParametersOpt() " extends if b:token == 'EXTENDS' call s:nextToken() let type.extends = [s:type()] endif " implements if b:token == 'IMPLEMENTS' call s:nextToken() let type.implements = s:typeList() endif let type.defs = s:classOrInterfaceBody(type.name, 0) let type.endpos = b:pos return type endfu " InterfaceDeclaration = INTERFACE Ident TypeParametersOpt [EXTENDS TypeList] InterfaceBody {{{2 fu! s:interfaceDeclaration(mods, dc) let type = s:ClassDef(b:pos, a:mods) call s:accept('INTERFACE') let type.name = s:ident() let type.typarams = s:typeParametersOpt() " extends if b:token == 'EXTENDS' call s:nextToken() let type.extends = s:typeList() endif let type.defs = s:classOrInterfaceBody(type.name, 1) let type.endpos = b:pos return type endfu " EnumDeclaration = ENUM Ident [IMPLEMENTS TypeList] EnumBody {{{2 fu! s:enumDeclaration(mods, dc) let type = s:ClassDef(b:pos, a:mods) call s:accept('ENUM') let type.name = s:ident() if b:token == 'IMPLEMENTS' call s:nextToken() let type.implements = s:typeList() endif let type.defs = s:enumBody(type.name) let type.endpos = b:pos return type endfu " EnumBody = "{" { EnumeratorDeclarationList } [","] " [ ";" {ClassBodyDeclaration} ] "}" fu! s:enumBody(enumName) let defs = [] call s:accept('LBRACE') if b:token == 'COMMA' call s:nextToken() elseif b:token != 'RBRACE' && b:token != 'SEMI' call add(defs, s:enumeratorDeclaration(a:enumName)) while b:token == 'COMMA' call s:nextToken() if b:token == 'RBRACE' || b:token == 'SEMI' break endif call add(defs, s:enumeratorDeclaration(a:enumName)) endwhile if b:token != 'RBRACE' && b:token != 'SEMI' call s:SyntaxError('comma.or.rbrace.or.semi. expected') call s:nextToken() endif endif if b:token == 'SEMI' call s:nextToken() while b:token != 'RBRACE' && b:token != 'EOF' call add(defs, s:classOrInterfaceBodyDeclaration(a:enumName, 0)) if b:pos <= b:errorEndPos call s:skip(0, 1, 1, 0) endif endwhile endif call s:accept('RBRACE') return defs endfu " EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [ Arguments ] [ "{" ClassBody "}" ] fu! s:enumeratorDeclaration(enumName) let vardef = {'tag': 'VARDEF', 'pos': b:pos} let dc = b:docComment let flags = 16409 " s:BitOr(s:Flags.PUBLIC, s:Flags.STATIC, s:Flags.FINAL, s:Flags.ENUM) " if b:deprecatedFlag " let flags = 147481 " s:BitOr(flags, s:Flags.DEPRECATED) " let b:deprecatedFlag = 1 " endif let pos = b:pos let annotations = s:annotationsOpt() let vardef.mods = s:Modifiers(pos, flags, annotations) let vardef.mods.pos = empty(annotations) ? -1 : pos let vardef.m = s:Number2Bits(flags) let typeArgs = s:typeArgumentsOpt() let identPos = b:pos let vardef.name = s:ident() let vardef.n = vardef.name let args = b:token == 'LPAREN' ? s:arguments() : [] " NOTE: It may be either a class body or a method body. I dont care, just record it if b:token == 'LBRACE' "call s:accept('LBRACE') "if b:token !=# 'RBRACE' " call s:gotoMatchEnd('{', '}') "endif "call s:accept('RBRACE') "let mods1 = s:Modifiers(-1, s:BitOr(s:Flags.ENUM, s:Flags.STATIC), []) let defs = s:classOrInterfaceBody('', 0) "let body = s:ClassDef(identPos, mods1) "let body.defs = defs endif let vardef.endpos = b:bp " TODO: create new class return vardef endfu " classOrInterfaceBody {{{2 " ClassBody = "{" {ClassBodyDeclaration} "}" " InterfaceBody = "{" {InterfaceBodyDeclaration} "}" fu! s:classOrInterfaceBody(classname, isInterface) call s:Info('== type definition body ==') call s:accept('LBRACE') if b:pos <= b:errorEndPos call s:skip(0, 1, 0, 0) if b:token == 'LBRACE' call s:nextToken() endif endif let defs = [] while b:token != 'RBRACE' && b:token != 'EOF' let defs += s:classOrInterfaceBodyDeclaration(a:classname, a:isInterface) if b:pos <= b:errorEndPos call s:skip(0, 1, 1, 0) endif endwhile call s:accept('RBRACE') return defs endfu " classOrInterfaceBodyDeclaration {{{2 " ClassBodyDeclaration = " ";" " | [STATIC] Block " | ModifiersOpt " ( Type Ident " ( VariableDeclaratorsRest ";" | MethodDeclaratorRest ) " | VOID Ident MethodDeclaratorRest " | TypeParameters (Type | VOID) Ident MethodDeclaratorRest " | Ident ConstructorDeclaratorRest " | TypeParameters Ident ConstructorDeclaratorRest " | ClassOrInterfaceOrEnumDeclaration " ) " InterfaceBodyDeclaration = " ";" " | ModifiersOpt Type Ident " ( ConstantDeclaratorsRest | InterfaceMethodDeclaratorRest ";" ) fu! s:classOrInterfaceBodyDeclaration(classname, isInterface) call s:Info('s:classOrInterfaceBodyDeclaration') if b:token == 'SEMI' call s:nextToken() return [{'tag': 'BLOCK', 'pos': -1, 'endpos': -1, 'stats': []}] else if b:scanStrategy < 0 let result = s:classOrInterfaceBodyDeclaration_opt(a:classname, a:isInterface) if !empty(result) return result endif endif let dc = b:docComment let pos = b:bp let mods = s:modifiersOpt() if b:token =~# '^\(CLASS\|INTERFACE\|ENUM\)$' return [s:classOrInterfaceOrEnumDeclaration(mods, dc)] " [STATIC] block elseif b:token == 'LBRACE' && !a:isInterface return [s:block(pos, mods.flags, b:scanStrategy < 1)] else let typarams = s:typeParametersOpt() let token = b:token let name = b:name let type = copy(s:TTree) let isVoid = b:token == 'VOID' if isVoid let type = {'tag': 'TYPEIDENT', 'pos': pos, 'typetag': 'void'} " FIXME let type.value = '' call s:nextToken() else let time = reltime() let type = s:type() let b:et_perf .= "\r" . reltimestr(reltime(time)) . ' s:type() ' endif " ctor if b:token == 'LPAREN' && !a:isInterface && type.tag == 'IDENT' if a:isInterface || name != a:classname call s:SyntaxError('invalid.meth.decl.ret.type.req') endif return [s:methodDeclaratorRest(pos, mods, type, name, typarams, a:isInterface, 1, dc)] else let name = s:ident() " method if b:token == 'LPAREN' return [s:methodDeclaratorRest(pos, mods, type, name, typarams, a:isInterface, isVoid, dc)] " field elseif !isVoid && len(typarams) == 0 let defs = s:variableDeclaratorsRest(pos, mods, type, name, a:isInterface, dc, copy([])) call s:accept('SEMI') return defs else call s:SyntaxError("LPAREN expected") return [{}] endif endif endif endif endfu " OAO: short way for common declaration of field or method, not for generic type yet. fu! s:classOrInterfaceBodyDeclaration_opt(classname, isInterface) let str = b:lines[b:line] let idx = matchend(str, s:RE_MEMBER_HEADER) if idx != -1 let subs = split(substitute(strpart(str, 0, idx), s:RE_MEMBER_HEADER, '\1;\2;\3', ''), ';') let name_ = subs[2] let type_ = subs[1] let flag_ = s:String2Flags(subs[0]) " if type_ =~# '^\(class\|interface\|enum\)$' " return [s:classOrInterfaceOrEnumDeclaration(mods, dc)] " else " methodDeclarator let idx = matchend(str, '^\s*[,=;(]', idx)-1 if str[idx] == '(' let methoddef = s:methodDeclaratorRest_opt(b:pos, flag_, type_, name_, [], a:isInterface, type_ == 'void', '', str, idx) if !empty(methoddef) return [methoddef] endif " variableDeclarator elseif str[idx] =~ '[=;]' let vardef = {'tag': 'VARDEF', 'pos': b:pos, 'name': name_, 'n': name_, 'vartype': type_, 't': type_, 'm': flag_} call s:gotoSemi() call s:accept('SEMI') let vardef.pos_end = b:pos return [vardef] " variableDeclarators elseif str[idx] == ',' let ie = matchend(str, '^\(,\s*'. s:RE_IDENTIFIER .'\s*\)*;', idx) if ie != -1 let vardef = {'tag': 'VARDEF', 'pos': b:pos, 'name': name_, 'n': name_, 'vartype': type_, 't': type_, 'm': flag_} let vars = [vardef] for item in split(substitute(strpart(str, idx+1, ie-idx-2), '\s', '', 'g'), ',') let vardef = copy(vardef) let vardef.name = item let vardef.n = item call add(vars, vardef) endfor let b:col = ie let b:bp = b:idxes[b:line] + b:col call s:nextToken() return vars endif endif " endif endif let RE_CTOR_HEADER = '^\s*\(\(public\|protected\|private\)\s\+\)\=\C' .a:classname. '\s*(' let ie = matchend(str, RE_CTOR_HEADER) if ie != -1 && !a:isInterface let flag_ = s:String2Flags(substitute(strpart(str, 0, ie), RE_CTOR_HEADER, '\1', '')) let methoddef = s:methodDeclaratorRest_opt(b:pos, flag_, a:classname, a:classname, [], a:isInterface, 1, '', str, ie-1) if !empty(methoddef) return [methoddef] endif endif let RE_METHOD_HEADER = '^\s*\(' .s:RE_IDENTIFIER. '\%(\s*\.\s*' .s:RE_IDENTIFIER. '\)*\%(\s*\[\s*\]\)\=\)\s\+\(' .s:RE_IDENTIFIER. '\)\s*(' let ie = matchend(str, RE_METHOD_HEADER) if ie != -1 let subs = split(substitute(strpart(str, 0, ie), RE_METHOD_HEADER, '\1;\2', ''), ';') let methoddef = s:methodDeclaratorRest_opt(b:pos, 0, subs[0], subs[1], [], a:isInterface, subs[0] == 'void', '', str, ie-1) if !empty(methoddef) return [methoddef] endif endif endfu " MethodDeclaratorRest = {{{2 " FormalParameters BracketsOpt [Throws TypeList] ( MethodBody | [DEFAULT AnnotationValue] ";") " VoidMethodDeclaratorRest = FormalParameters [Throws TypeList] ( MethodBody | ";") " InterfaceMethodDeclaratorRest = FormalParameters BracketsOpt [THROWS TypeList] ";" " VoidInterfaceMethodDeclaratorRest = FormalParameters [THROWS TypeList] ";" " ConstructorDeclaratorRest = "(" FormalParameterListOpt ")" [THROWS TypeList] MethodBody fu! s:methodDeclaratorRest(pos, mods, type, name, typarams, isInterface, isVoid, dc) let time = reltime() let methoddef = {'tag': 'METHODDEF', 'pos': a:pos, 'name': a:name, 'mods': a:mods, 'restype': a:type, 'typarams': a:typarams} let methoddef.n = a:name let methoddef.m = s:Number2Bits(a:mods.flags) let methoddef.r = java_parser#type2Str(a:type) " parameters let methoddef.params = s:formalParameters() " BracketsOpt if !a:isVoid let methoddef.r = java_parser#type2Str(s:bracketsOpt(a:type)) endif " throws if b:token == 'THROWS' call s:nextToken() " thrown = qualidentList() let ts = [s:qualident()] while b:token == 'COMMA' call s:nextToken() call add(ts, s:qualident()) endwhile let methoddef.throws = ts endif " method body if b:token == 'LBRACE' let methoddef.body = s:block(b:pos, 0, b:scanStrategy < 1) else if b:token == 'DEFAULT' call s:accept('DEFAULT') let methoddef.defaultValue = s:annotationValue() endif call s:accept('SEMI') if b:pos <= b:errorEndPos call s:skip(0, 1, 0, 0) if b:token == 'LBRACE' let methoddef.body = s:block(b:pos, 0, b:scanStrategy < 1) endif endif endif let methoddef.d = s:method2Str(methoddef) let b:et_perf .= "\r" . reltimestr(reltime(time)) . ' methodrest() ' return methoddef endfu " method header declared in one line, " NOTE: RE_FORMAL_PARAM_LIST do not recognize varargs and nested comments fu! s:methodDeclaratorRest_opt(pos, mods, type, name, typarams, isInterface, isVoid, dc, str, idx) let str = a:str let idx = a:idx " params let idxend = matchend(str, '^(\s*)', idx) " no params if idxend == -1 let idxend = matchend(str, '^(\s*' . s:RE_FORMAL_PARAM_LIST . '\s*)', idx) endif if idxend == -1 return endif let methoddef = {'tag': 'METHODDEF', 'pos': a:pos, 'name': a:name, 'n': a:name, 'm': a:mods, 'r': a:type} " params let methoddef.params = [] let s = strpart(str, idx+1, idxend-idx-2) if s !~ '^\s*$' for item in split(s, ',') let subs = split(substitute(item, s:RE_FORMAL_PARAM2, '\2;\5', ''), ';') let param = {'tag': 'VARDEF', 'pos': -1} let param.name = subs[1] let param.vartype = substitute(subs[0], ' ', '', 'g') let param.m = s:Flags.PARAMETER call add(methoddef.params, param) endfor endif " throws let idx2 = matchend(str, '^\s*' . s:RE_THROWS, idxend) let idx = idx2 == -1 ? idxend : idx2 if idx2 != -1 "let throws = strpart(str, idxend, idx-idxend) endif " in interface if a:isInterface let idx = matchend(str, '^\s*;', idx) if idx != -1 let b:token = 'SEMI' let b:col = idx let b:bp = b:idxes[b:line] + b:col let b:pos = b:bp - 1 let methoddef.d = substitute(str, '^\s*\([^{]*\)\s*;\=$', '\1', '') return methoddef endif endif let idx = matchend(str, '^\s*{', idx) if idx == -1 let idx = matchend(b:lines[b:line+1], '^\s*{') if idx != -1 let b:line += 1 endif endif if idx != -1 let b:token = 'LBRACE' let b:col = idx let b:bp = b:idxes[b:line] + b:col let b:pos = b:bp - 1 let methoddef.d = substitute(str, '^\s*\([^{]*\)\s*{\=$', '\1', '') let methoddef.body = s:block(b:pos, 0, b:scanStrategy < 1) return methoddef endif endfu " VariableDeclarators = VariableDeclarator { "," VariableDeclarator } {{{2 fu! s:variableDeclarators(mods, type, vdefs) return s:variableDeclaratorsRest(b:pos, a:mods, a:type, s:ident(), 0, '', a:vdefs) endfu " VariableDeclaratorsRest = VariableDeclaratorRest { "," VariableDeclarator } " ConstantDeclaratorsRest = ConstantDeclaratorRest { "," ConstantDeclarator } fu! s:variableDeclaratorsRest(pos, mods, type, name, reqInit, dc, vdefs) call add(a:vdefs, s:variableDeclaratorRest(a:pos, a:mods, a:type, a:name, a:reqInit, a:dc)) while b:token == 'COMMA' call s:nextToken() call add(a:vdefs, s:variableDeclarator(a:mods, a:type, a:reqInit, a:dc)) endwhile return a:vdefs endfu " VariableDeclarator = Ident VariableDeclaratorRest " ConstantDeclarator = Ident ConstantDeclaratorRest fu! s:variableDeclarator(mods, type, reqInit, dc) return s:variableDeclaratorRest(b:pos, a:mods, a:type, s:ident(), a:reqInit, a:dc) endfu " VariableDeclaratorRest = BracketsOpt ["=" VariableInitializer] " ConstantDeclaratorRest = BracketsOpt "=" VariableInitializer fu! s:variableDeclaratorRest(pos, mods, type, name, reqInit, dc) let vardef = s:VarDef(a:pos, a:mods, a:name, s:bracketsOpt(a:type)) let vardef.n = vardef.name let vardef.m = a:mods == {} ? '0' : s:Number2Bits(a:mods.flags) let vardef.t = java_parser#type2Str(vardef.vartype) if b:token == 'EQ' call s:nextToken() call s:Info('field init ' . b:token) let vardef.init = s:variableInitializer() elseif a:reqInit echo '[syntax error]:' . s:token2string('EQ') . " expected" endif let vardef.endpos = b:pos return vardef endfu fu! s:variableDeclaratorId(mods, type) let vardef = s:VarDef(b:pos, a:mods, s:ident(), a:type) if len(a:mods.flags) <= 34 " (a:mods.flags & s:Flags.VARARGS) == 0 let vardef.type = s:bracketsOpt(vardef.vartype) endif return vardef endfu " {{{2 " TypeParametersOpt = ["<" TypeParameter {"," TypeParameter} ">"] {{{2 fu! s:typeParametersOpt() if b:token == 'LT' "call checkGenerics() let typarams = [] call s:nextToken() call add(typarams, s:typeParameter()) while b:token == 'COMMA' call s:nextToken() call add(typarams, s:typeParameter()) endwhile call s:accept('GT') return typarams else return [] endif endfu " TypeParameter = TypeVariable [TypeParameterBound] {{{2 " TypeParameterBound = EXTENDS Type {"&" Type} " TypeVariable = Ident fu! s:typeParameter() let pos = b:pos let name = s:ident() let bounds = [] if b:token == 'EXTENDS' call s:nextToken() call add(bounds, s:type()) while b:token == 'AMP' call s:nextToken() call add(bounds, s:type()) endwhile endif return {'tag': 'TYPEPARAMETER', 'pos': pos, 'name': name, 'bounds': bounds} endfu " FormalParameters = "(" [ FormalParameterList ] ")" {{{2 " FormalParameterList = [ FormalParameterListNovarargs , ] LastFormalParameter " FormalParameterListNovarargs = [ FormalParameterListNovarargs , ] FormalParameter fu! s:formalParameters() let params = [] let lastParam = {} call s:accept('LPAREN') if b:token != 'RPAREN' let lastParam = s:formalParameter() call add(params, lastParam) while b:token == 'COMMA' && len(lastParam.mods.flags) <= 34 " (lastParam.mods.flags & s:Flags.VARARGS) == 0 call s:nextToken() let lastParam = s:formalParameter() call add(params, lastParam) endwhile endif call s:accept('RPAREN') return params endfu " FormalParameter = { FINAL | '@' Annotation } Type VariableDeclaratorId {{{2 " LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident | FormalParameter fu! s:optFinal(flags) let mods = s:modifiersOpt() " checkNoMods(mods.flags & ~(Flags.FINAL | Flags.DEPRECATED)) let mods.flags = s:BitOr(mods.flags, a:flags) return mods endfu " OAO: optional FINAL for parameter fu! s:optFinalParameter() let mods = {'tag': 'MODIFIERS', 'pos': b:pos, 'flags': s:Flags.PARAMETER, 'annotations': []} if b:token == 'FINAL' let mods.flags = '1000000000000000000000000000010000' call s:nextToken() endif return mods endfu fu! s:formalParameter() let mods = s:optFinalParameter() let type = s:type() if b:token == 'ELLIPSIS' " checkVarargs() let mods.flags = '1' . mods.flags " s:BitOr_binary(mods.flags, s:Flags.VARARGS) let type = s:TypeArray(b:pos, type) call s:nextToken() endif return s:variableDeclaratorId(mods, type) endfu " {{{2 " auxiliary methods {{{2 let s:MapToken2Tag = {'BARBAR': 'OR', 'AMPAMP': 'AND', 'BAR': 'BITOR', 'BAREQ': 'BITOR_ASG', 'CARET': 'BITXOR', 'CARETEQ': 'BITXOR_ASG', 'AMP': 'BITAND', 'AMPEQ': 'BITAND_ASG', 'EQEQ': 'EQ', 'BANGEQ': 'NE', 'LT': 'LT', 'GT': 'GT', 'LTEQ': 'LE', 'GTEQ': 'GE', 'LTLT': 'SL', 'LTLTEQ': 'SL_ASG', 'GTGT': 'SR', 'GTGTEQ': 'SR_ASG', 'GTGTGT': 'USR', 'GTGTGTEQ': 'USR_ASG', 'PLUS': 'PLUS', 'PLUSEQ': 'PLUS_ASG', 'SUB': 'MINUS', 'SUBEQ': 'MINUS_ASG', 'STAR': 'MUL', 'STAREQ': 'MUL_ASG', 'SLASH': 'DIV', 'SLASHEQ': 'DIV_ASG', 'PERCENT': 'MOD', 'PERCENTEQ': 'MOD_ASG', 'INSTANCEOF': 'TYPETEST'} let s:opprecedences = {'notExpression': -1, 'noPrec': 0, 'assignPrec': 1, 'assignopPrec': 2, 'condPrec': 3, 'orPrec': 4, 'andPrec': 5, 'bitorPrec': 6, 'bitxorPrec': 7, 'bitandPrec': 8, 'eqPrec': 9, 'ordPrec': 10, 'shiftPrec': 11, 'addPrec': 12, 'mulPrec': 13, 'prefixPrec': 14, 'postfixPrec': 15, 'precCount': 16} fu! s:checkExprStat(t) if a:t.tag =~# '^\(PREINC\|PREDEC\|POSTINC\|POSTDEC\|ASSIGN\|BITOR_ASG\|BITXOR_ASG\|BITAND_ASG\|SL_ASG\|SR_ASG\|USR_ASG\|PLUS_ASG\|MINUS_ASG\|MUL_ASG\|DIV_ASG\|MOD_ASG\|APPLY\|NEWCLASS\|ERRONEOUS\)$' return a:t else call s:SyntaxError('not.stmt') return {'tag': 'ERRONEOUS', 'pos': b:pos, 'errs': [a:t]} endif endfu fu! s:prec(token) let oc = s:optag(a:token) return oc == -1 ? -1 : s:opPrec(oc) endfu fu! s:opPrec(tag) if a:tag =~# '^\(POS\|NEG\|NOT\|COMPL\|PREINC\|PREDEC\)$' return s:opprecedences.prefixPrec elseif a:tag =~# '^\(POSTINC\|POSTDEC\|NULLCHK\)$' return s:opprecedences.postfixPrec elseif a:tag == 'ASSIGN' return s:opprecedences.assignPrec elseif a:tag =~# '^\(BITOR_ASG\|BITXOR_ASG\|BITAND_ASG\|SL_ASG\|SR_ASG\|USR_ASG\|PLUS_ASG\|MINUS_ASG\|MUL_ASG\|DIV_ASG\|MOD_ASG\)$' return s:opprecedences.assignopPrec elseif a:tag == 'OR' return s:opprecedences.orPrec elseif a:tag == 'AND' return s:opprecedences.andPrec elseif a:tag =~# '^\(EQ\|NE\)$' return s:opprecedences.eqPrec elseif a:tag =~# '^\(LT\|GT\|LE\|GE\)$' return s:opprecedences.ordPrec elseif a:tag == 'BITOR' return s:opprecedences.bitorPrec elseif a:tag == 'BITXOR' return s:opprecedences.bitxorPrec elseif a:tag == 'BITAND' return s:opprecedences.bitandPrec elseif a:tag =~# '^\(SL\|SR\|USR\)$' return s:opprecedences.shiftPrec elseif a:tag =~# '^\(PLUS\|MINUS\)$' return s:opprecedences.addPrec elseif a:tag =~# '^\(MUL\|DIV\|MOD\)$' return s:opprecedences.mulPrec elseif a:tag == 'TYPETEST' return s:opprecedences.ordPrec else return -1 endif endfu fu! s:optag(token) return get(s:MapToken2Tag, a:token, -1) endfu fu! s:unoptag(token) if a:token == 'PLUS' return 'POS' elseif a:token == 'SUB' return 'NEG' elseif a:token == 'BANG' return 'NOT' elseif a:token == 'TILDE' return 'COMPL' elseif a:token == 'PLUSPLUS' return 'PREINC' elseif a:token == 'SUBSUB' return 'PREDEC' else return -1 endif endfu fu! s:typetag(token) if a:token =~# '\(BYTE\|CHAR\|SHORT\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\)' return tolower(a:token) else return -1 endif endfu "}}}1 " vim:set fdm=marker sw=2 nowrap: