pmacs3/mode/c.py

340 lines
14 KiB
Python
Raw Normal View History

import os, re
from subprocess import Popen, PIPE, STDOUT
import color, default, method, method.shell, mode, tab
from lex import Grammar, PatternRule, RegionRule, PatternGroupRule, OverridePatternRule
from mode.python import StringGrammar2
class CommentGrammar(Grammar):
rules = [
PatternRule(r'data', r'(?:[^\*]|\*(?!/))+'),
]
2007-07-21 11:40:53 -04:00
# this might not be complete...
# see http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_3.html#SEC44
class MacroGrammar(Grammar):
rules = [
PatternRule(r'spaces', r' +'),
RegionRule(r'comment', r'/\*', CommentGrammar, r'\*/'),
PatternRule(r'comment', r'//.*$'),
2007-07-21 11:40:53 -04:00
PatternRule('name', r'(?:(?<=#define )) *[a-zA-Z_][a-zA-Z0-9_]*'),
PatternRule(r"unop", r"!(?!=)|\+=|-=|\*=|/=|//=|%=|&=\|\^=|>>=|<<=|\*\*="),
2007-07-21 11:40:53 -04:00
PatternRule(r'binop', r"\+|<>|<<|<=|<|-|>>|>=|>|\*\*|&|\*|\||/|\^|==|//|~|!=|%"),
PatternRule(r"delimiter", r"->|\.|\(|\)|\[|\]|{|}|@|,|:|`|;|=|\?"),
PatternRule(r"identifier", r"[a-zA-Z_][a-zA-Z0-9_]*"),
2007-07-21 11:40:53 -04:00
PatternRule(r"integer", r"-?(?:0(?![x0-9])|[1-9][0-9]*|0[0-7]+|0[xX][0-9a-fA-F]+)[lL]?"),
PatternRule(r"float", r"-?(?:[0-9]+\.[0-9]*|\.[0-9]+|(?:[0-9]|[0-9]+\.[0-9]*|\.[0-9]+)[eE][\+-]?[0-9]+)"),
RegionRule(r'string', '"', StringGrammar2, '"'),
2007-07-21 11:40:53 -04:00
PatternRule(r'char', r"'.'|'\\.'|'\\[0-7]{3}'"),
PatternRule(r"continued", r"\\\n$"),
]
class CGrammar(Grammar):
rules = [
PatternRule(r'spaces', r' +'),
2007-07-21 11:40:53 -04:00
PatternRule(r"delimiter", r"\.|\(|\)|\[|\]|{|}|@|,|:|`|;|=(?!=)|\?|->"),
PatternRule(r'eol', r"\n$"),
2007-07-21 11:40:53 -04:00
PatternGroupRule(r'structgroup', r'keyword', r'struct', r'spaces',
r' +', r'structname', r'[a-zA-Z_][a-zA-Z0-9_]*'),
PatternGroupRule(r'enumgroup', r'keyword', r'enum', r'spaces',
r' +', r'enumname', r'[a-zA-Z_][a-zA-Z0-9_]*'),
2007-08-02 17:43:04 -04:00
PatternRule(r'keyword', r"(?:auto|break|case|char|const|continue|default|double|do|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)(?![a-zA-Z_])"),
2007-07-21 11:40:53 -04:00
2008-10-02 12:49:22 -04:00
PatternRule(r'function', r'[a-zA-Z_][a-zA-Z0-9_]*(?= *\()'),
2007-07-21 11:40:53 -04:00
PatternRule(r'builtin', r"(?:NULL|TRUE|FALSE)"),
2008-09-25 20:57:15 -04:00
PatternRule(r'label', r'[a-zA-Z_][a-zA-Z0-9_]*(?=:)'),
2007-07-21 11:40:53 -04:00
RegionRule(r'macro', r'# *(?:assert|cpu|define|elif|else|endif|error|ident|ifdef|ifndef|if|import|include_next|line|machine|pragma|pragma_once|system|unassert|undef|warning)(?!=[a-zA-Z0-9_])', MacroGrammar, r'\n$'),
RegionRule(r'comment', r'/\*', CommentGrammar, r'\*/'),
PatternRule(r'comment', r'//.*$'),
RegionRule(r'string', '"', StringGrammar2, '"'),
2007-07-21 11:40:53 -04:00
PatternRule(r"unop", r"!(?!=)|\+=|-=|\*=|/=|//=|%=|&=\|\^=|>>=|<<=|\*\*="),
PatternRule(r'binop', r"\+|<>|<<|<=|<|-|>>|>=|>|\*\*|&|\*|\||/|\^|==|//|~|!=|%"),
2007-07-21 11:40:53 -04:00
PatternRule(r"integer", r"(?:0(?![x0-9])|[1-9][0-9]*|0[0-7]+|0[xX][0-9a-fA-F]+)[lL]?"),
PatternRule(r"float", r"[0-9]+\.[0-9]*|\.[0-9]+|(?:[0-9]|[0-9]+\.[0-9]*|\.[0-9]+)[eE][\+-]?[0-9]+"),
RegionRule(r'macrocomment', r'#if +(?:0|NULL|FALSE)', Grammar, r'#endif'),
2007-07-21 11:40:53 -04:00
PatternRule(r'char', r"'.'|'\\.'|'\\[0-7]{3}'"),
PatternGroupRule(r'includegrp', r'macro.start', r'# *include', r'spaces',
r' +', r'header', r'< *[-A-Za-z/0-9_.]+ *>|" *[-A-Za-z/0-9_.]+ *"'),
PatternRule(r'identifier', r"[a-zA-Z_][a-zA-Z0-9_]*"),
OverridePatternRule(r'comment', r'/\* *@@:(?P<token>[.a-zA-Z0-9_]+):(?P<mode>[.a-zA-Z0-9_]+) *\*/$'),
OverridePatternRule(r'comment', r'// *@@:(?P<token>[.a-zA-Z0-9_]+):(?P<mode>[.a-zA-Z0-9_]+) *$'),
2007-07-21 11:40:53 -04:00
]
2008-10-02 12:49:22 -04:00
class CTabber2(tab.StackTabber2):
open_tokens = {'delimiter': {'{': '}', '(': ')', '[': ']'}}
close_tokens = {'delimiter': {'}': '{', ')': '(', ']': '['}}
control_tokens = {'keyword': {'if': 1, 'else': 1, 'while': 1, 'do': 1, 'for': 1}}
end_at_eof = False
end_at_tokens = {'delimiter': {';': 1}}
nocontinue_tokens = {'delimiter': {';': 1}}
start_free_tokens = {'string.start': 'string.end'}
end_free_tokens = {'string.end': 'string.start'}
def is_base(self, y):
if y == 0:
return True
tokens = self._get_tokens(y)
# this assumes that people aren't gonna use these macros inside of
# blocks, which is probably ok.
t0 = tokens[0]
if t0.fqname() == 'macro.start' and t0.string in ('#define', '#include'):
return True
# detecting function declarations is annoying; this assumes that people
# won't put a variable type and name on different lines, but that they
# might do that for function return type and name.
#
# unfortunately, valid function return types might include any of the
# four types of tokens below
decl = False
for t in tokens:
if t.name in ('keyword', 'identifier', 'structname', 'enumname'):
decl = True
continue
if decl and t.name == 'function':
break
else:
decl = False
break
return decl
def _is_indent(self, t):
return t.name == 'spaces'
def _is_ignored(self, t):
if t.name in ('spaces', 'eol', 'comment'): return True
elif t.fqname() in ('comment.start', 'comment.null', 'comment.end'): return True
else: return False
2007-10-21 20:55:29 -04:00
class CTabber(tab.StackTabber):
wst = ('spaces', 'eol', 'comment', 'comment.start', 'comment.null', 'comment.end')
def token_is_whitespace(self, y, i):
token = self.get_token(y, i)
return token.fqname() in self.wst
2007-07-21 11:40:53 -04:00
def is_base(self, y):
if y == 0:
return True
highlighter = self.mode.window.buffer.highlights[self.mode.name()]
if not highlighter.tokens[y]:
return False
# this assumes that people aren't gonna use these macros inside of
# blocks, which is probably ok.
t0 = highlighter.tokens[y][0]
if t0.name == 'macro.start' and t0.string in ('#define', '#include'):
return True
# detecting function declarations is annoying; this assumes that people
# won't put a variable type and name on different lines, but that they
# might do that for function return type and name.
#
# unfortunately, valid function return types might include any of the
# four types of tokens below
decl = False
for t in highlighter.tokens[y]:
if t.name in ('keyword', 'identifier', 'structname', 'enumname'):
decl = True
continue
if decl and t.name == 'function':
break
else:
decl = False
break
if decl:
return True
return False
def _handle_open_token(self, currlvl, y, i):
self._opt_pop('cont')
token = self.get_token(y, i)
if token.string == '{':
self._pop_while('cond', 'cont', 'case')
if self.is_leftmost_token(y, i):
currlvl = self.get_curr_level()
2007-10-21 20:55:29 -04:00
tab.StackTabber._handle_open_token(self, currlvl, y, i)
2007-07-21 11:40:53 -04:00
return currlvl
def _handle_close_token(self, currlvl, y, i):
2008-04-02 19:06:52 -04:00
w = self.mode.tabwidth
2007-07-21 11:40:53 -04:00
self._opt_pop('cont')
token = self.get_token(y, i)
if token.string == '}':
self._opt_pop('case')
currlvl = tab.StackTabber._handle_close_token(self, currlvl, y, i)
2007-07-21 11:40:53 -04:00
if self.is_rightmost_token(y, i):
if token.string == '}':
self._pop_while('cond', 'cont', 'case')
elif self._has_markers() and self._peek_name() == 'cond':
2007-07-21 11:40:53 -04:00
pass
else:
if token.fqname() != 'macro.delimiter':
2008-04-02 19:06:52 -04:00
self._opt_append('cont', currlvl + w)
2007-07-21 11:40:53 -04:00
return currlvl
def _handle_other_token(self, currlvl, y, i):
2008-04-02 19:06:52 -04:00
w = self.mode.tabwidth
2007-07-21 11:40:53 -04:00
token = self.get_token(y, i)
fqname = token.fqname()
if fqname == 'delimiter' and token.string == ';':
2008-07-19 16:00:11 -04:00
self._pop_while('cond', 'cont')
2007-07-21 11:40:53 -04:00
elif fqname == 'keyword':
if token.string in ('do', 'else', 'for', 'if', 'while'):
2008-04-02 19:06:52 -04:00
self._append('cond', currlvl + w)
2007-07-21 11:40:53 -04:00
elif token.string == 'break':
self._opt_pop('case', 'while', 'for')
elif token.string == 'continue':
self._opt_pop('while', 'for')
elif token.string == 'case':
self._opt_pop('case')
currlvl = self.get_curr_level()
2008-04-02 19:06:52 -04:00
self._opt_append('case', currlvl + w)
2007-07-21 11:40:53 -04:00
elif fqname == 'string.start':
self._opt_append('string', None)
elif fqname == 'string.end':
self._opt_pop('string')
if self.is_rightmost_token(y, i):
2008-04-02 19:06:52 -04:00
self._opt_append('cont', currlvl + w)
2007-07-21 11:40:53 -04:00
# TODO: this could be a lot better
elif fqname == 'macro':
currlvl = 0
elif fqname.startswith('macro.start'):
self._opt_append('macro', None)
currlvl = 0
elif fqname.startswith('macro.end'):
self._opt_pop('macro', None)
elif fqname.startswith('macroblock.start'):
self._opt_append('macroblock', None)
currlvl = 0
elif fqname.startswith('macroblock.end'):
self._opt_pop('macroblock', None)
if self.is_rightmost_token(y, i):
if self._has_markers() and self._peek_name() == 'cond':
pass
elif(not fqname.startswith('string') and
not fqname.startswith('comment') and
not fqname.startswith('macro') and
not fqname == 'delimiter' and
not fqname == 'header' and
#not fqname == 'null' and
not fqname == 'spaces' and
2007-07-21 11:40:53 -04:00
not fqname == 'eol' and
token.string not in ('}', ';', '(', '{', '[', ',')):
2008-04-02 19:06:52 -04:00
self._opt_append('cont', currlvl + w)
2007-07-21 11:40:53 -04:00
return currlvl
2008-04-18 23:32:08 -04:00
class CCheckSyntax(method.shell.Exec):
'''Build this C program (using the mode's make cmd)'''
show_success = False
args = []
def _execute(self, w, **vargs):
if w.application.config['c.syntax-rel-dir']:
d = os.path.dirname(w.buffer.path)
self._doit(w, w.buffer.path, w.application.config['c.syntax-cmd'],
cmdname='c-check-syntax', cmddir=d)
else:
self._doit(w, w.buffer.path, w.application.config['c.syntax-cmd'],
cmdname='c-check-syntax')
class CMake(method.shell.Exec):
'''Build this C program (using the mode's make cmd)'''
show_success = False
args = []
def _execute(self, w, **vargs):
if w.application.config['c.make-rel-dir']:
d = os.path.dirname(w.buffer.path)
self._doit(w, w.buffer.path, w.application.config['c.make-cmd'],
cmdname='c-make', cmddir=d)
else:
self._doit(w, w.buffer.path, w.application.config['c.make-cmd'],
cmdname='c-make')
2007-10-21 20:55:29 -04:00
class C(mode.Fundamental):
2007-10-18 17:07:35 -04:00
modename = 'C'
extensions = ['.c', '.h', '.cpp']
2008-10-02 12:49:22 -04:00
#tabbercls = CTabber
tabbercls = CTabber2
2007-10-18 17:07:35 -04:00
grammar = CGrammar
2007-07-21 11:40:53 -04:00
opentokens = ('delimiter',)
2007-10-18 17:07:35 -04:00
opentags = {'(': ')', '[': ']', '{': '}'}
2007-07-21 11:40:53 -04:00
closetokens = ('delimiter',)
2007-10-18 17:07:35 -04:00
closetags = {')': '(', ']': '[', '}': '{'}
colors = {
'macrocomment.start': ('red', 'default', 'bold'),
'macrocomment.null': ('red', 'default', 'bold'),
'macrocomment.end': ('red', 'default', 'bold'),
'macro': ('blue', 'default', 'bold'),
'macro.start': ('blue', 'default', 'bold'),
'macro.name': ('yellow', 'default', 'bold'),
'macro.null': ('magenta', 'default', 'bold'),
'macro.continued': ('red', 'default', 'bold'),
'macro.delimiter': ('default', 'default', 'bold'),
'macro.identifier': ('yellow', 'default', 'bold'),
'macro.bareword': ('yellow', 'default', 'bold'),
'macro.integer': ('green', 'default', 'bold'),
'macro.float': ('green', 'default', 'bold'),
'macro.char': ('green', 'default', 'bold'),
'macro.string.start': ('green', 'default', 'bold'),
'macro.string.escaped': ('magenta', 'default', 'bold'),
'macro.string.octal': ('magenta', 'default', 'bold'),
'macro.string.null': ('green', 'default', 'bold'),
'macro.string.end': ('green', 'default', 'bold'),
'macro.end': ('magenta', 'default', 'bold'),
'include': ('blue', 'default', 'bold'),
'header': ('green', 'default', 'bold'),
#'structname': ('yellow', 'default', 'bold'),
#'enumname': ('yellow', 'default', 'bold'),
#'c_type': ('green', 'default', 'bold'),
2007-07-21 11:40:53 -04:00
}
config = {
'c.syntax-cmd': "gcc -x c -fsyntax-only %(path)s",
'c.syntax-rel-dir': False,
'c.make-cmd': "make",
'c.make-rel-dir': True,
}
2008-04-18 23:32:08 -04:00
actions = [CCheckSyntax, CMake]
format = "%(flag)s %(bname)-18s (%(mname)s) %(indent)s %(cursor)s/%(mark)s %(perc)s [%(func)s]"
def get_status_names(self):
names = mode.Fundamental.get_status_names(self)
c = self.window.logical_cursor()
names['func'] = self.get_line_function(c.y)
return names
2007-07-21 11:40:53 -04:00
def __init__(self, w):
2007-10-21 20:55:29 -04:00
mode.Fundamental.__init__(self, w)
2007-07-21 11:40:53 -04:00
self.add_bindings('close-paren', (')',))
self.add_bindings('close-brace', ('}',))
self.add_bindings('close-bracket', (']',))
2008-04-18 23:32:08 -04:00
self.add_bindings('c-check-syntax', ('C-c s',))
self.add_bindings('c-make', ('C-c C-c',))
def get_functions(self):
2008-09-22 09:45:52 -04:00
#return self.context.get_names()
return {}
def get_function_names(self):
2008-09-22 09:45:52 -04:00
#return self.context.get_name_list()
return []
def get_line_function(self, y):
2008-09-22 09:45:52 -04:00
#return self.context.get_line_name(y)
return None
2007-10-19 02:41:33 -04:00
install = C.install