pmacs3/mode/c.py

271 lines
10 KiB
Python

import os.path
from subprocess import Popen, PIPE, STDOUT
from method.shell import Exec
from mode import Fundamental
import tab
from lex import Grammar, PatternRule, RegionRule, PatternMatchRule, OverridePatternRule
from mode.python import StringGrammar2
class CommentGrammar(Grammar):
rules = [
PatternRule(r'data', r'(?:[^\*]|\*(?!/))+'),
]
class ErrorGrammar(Grammar):
rules = [
PatternRule(r'data', r'[^\\\n]+'),
PatternRule('continuation', r'\\\n$'),
]
chr1 = '[a-zA-Z_]'
chr2 = '[a-zA-Z0-9_]'
word = chr1 + chr2 + '*'
class MacroGrammar(Grammar):
rules = [
PatternRule('continuation', r'\\\n$'),
PatternRule('name', '(?<=#define ) *' + word),
PatternRule('name', '(?<=#ifdef ) *' + word),
PatternRule('name', '(?<=#ifndef ) *' + word),
PatternRule('name', '(?<=#undef ) *' + word),
PatternRule('concat', '##' + chr2 + '+'),
PatternRule('quoted', '#' + chr2 + '+'),
PatternMatchRule('x', r'(defined)(\()(' + word + r')(\))',
'function', 'delimiter', 'name', 'delimiter'),
]
class CGrammar(Grammar):
rules = [
PatternRule('spaces', r' +'),
PatternMatchRule('x', r'(\()( *)(' + word + r')(\**)( *)(\))( *)(?=[a-zA-Z0-9_\(])',
'delimiter', 'spaces', 'c.type', 'c.operator',
'spaces', 'delimiter', 'spaces'),
PatternRule("delimiter", r"\.|\(|\)|\[|\]|{|}|@|,|:|`|;|=(?!=)|\?|->"),
PatternRule('eol', r"\n$"),
PatternMatchRule('x', r'(struct|enum|union)( +)(' + word + ')',
'c.builtin', 'spaces', 'c.type'),
PatternRule('c.builtin', r"(?:break|case|continue|default|do|else|for|goto|if|return|sizeof|switch|while)(?!" + chr2 + ")"),
PatternRule('c.builtin', r"(?:signed|register|extern|const|static|enum|struct|typedef|union|unsigned|volatile)(?!" + chr2 + ")"),
PatternRule('c.type', r"(?:auto|char|double|float|int|long|short|void|volatile)(?!" + chr2 + ")"),
PatternMatchRule('x', '(' + word + ')( +)(\**)(' + word + ')( *)(\()',
'c.type', 'spaces', 'c.operator', 'c.function',
'spaces', 'delimiter'),
PatternMatchRule('x', '(' + word + ')(\*+)( +)(' + word + ')( *)(\()',
'c.type', 'c.operator', 'spaces', 'c.function',
'spaces', 'delimiter'),
PatternMatchRule('x', '(' + word + ')( +)(\**)(' + word + ')',
'c.type', 'spaces', 'c.operator', 'c.identifier'),
PatternMatchRule('x', '(' + word + ')(\*+)( +)(' + word + ')',
'c.type', 'c.operator', 'spaces', 'c.identifier'),
PatternRule('c.function', word + r'(?= *\()'),
PatternRule('c.constant', "[A-Z_][A-Z0-9_]+(?!" + chr2 + ")"),
PatternRule('c.label', word + '(?=:)'),
RegionRule('c.error', '# *error', ErrorGrammar, r'\n$'),
RegionRule('c.macro', '# *(?:assert|cpu|define|elif|else|endif|error|ident|ifdef|ifndef|if|import|include_next|line|machine|pragma_once|pragma|system|unassert|undef|warning)(?!=' + chr2 + ')', MacroGrammar, r'\n$'),
RegionRule('c.comment', r'/\*', CommentGrammar, r'\*/'),
PatternRule('c.comment', '//.*$'),
RegionRule('c.string', '"', StringGrammar2, '"'),
PatternRule("c.float", r"-?[0-9]+\.[0-9]*|-?\.[0-9]+|-?(?:[0-9]|[0-9]+\.[0-9]*|-?\.[0-9]+)[eE][\+-]?[0-9]+"),
PatternRule("c.integer", r"(?:0(?![x0-9])|-?[1-9][0-9]*|0[0-7]+|0[xX][0-9a-fA-F]+)[lL]?"),
PatternRule("c.operator", r"!(?!=)|\+=|-=|\*=|/=|//=|%=|&=\|\^=|>>=|<<=|\*\*="),
PatternRule('c.operator', r"\+|<>|<<|<=|<|-|>>|>=|>|\*\*|&&|&|\*|\|\||\||/|\^|==|//|~|!=|%"),
RegionRule('c.macrocomment', '#if +(?:0|NULL|FALSE)', Grammar, '#endif'),
PatternMatchRule('', r"(')(.)(')", 'c.char.start', 'c.char.data', 'c.char.end'),
PatternMatchRule('', r"(')(\\.)(')", 'c.char.start', 'c.char.escaped', 'c.char.end'),
PatternMatchRule('', r"(')(\\[0-7]{3})(')", 'c.char.start', 'c.char.octal', 'c.char.end'),
#PatternRule('c.char', r"'.'|'\\.'|'\\[0-7]{3}'"),
PatternMatchRule('x', r'(# *include)( +)(.+)(\n|$)',
'c.macro.start', 'spaces', 'c.header', 'c.macro.end'),
PatternRule('c.identifier', word),
OverridePatternRule('c.comment', r'/\* *@@:(?P<token>[.a-zA-Z0-9_]+):(?P<mode>[.a-zA-Z0-9_]+) *\*/$'),
OverridePatternRule('c.comment', r'// *@@:(?P<token>[.a-zA-Z0-9_]+):(?P<mode>[.a-zA-Z0-9_]+) *$'),
]
MacroGrammar.rules.extend(CGrammar.rules)
class CTabber2(tab.StackTabber2):
open_tokens = {'delimiter': {'{': '}', '(': ')', '[': ']'}}
close_tokens = {'delimiter': {'}': '{', ')': '(', ']': '['}}
control_tokens = {'c.builtin': {'if': 1, 'else': 1, 'while': 1, 'do': 1, 'for': 1}}
end_at_eof = False
end_at_tokens = {'delimiter': {';': 1}}
nocontinue_tokens = {'delimiter': {';': 1, ',': 1}}
start_free_tokens = {'c.string.start': 'c.string.end'}
end_free_tokens = {'c.string.end': 'c.string.start'}
start_macro_tokens = {'c.macro.start': 'c.macro.end'}
end_macro_tokens = {'c.macro.end': 'c.macro.start'}
def _is_base(self, y):
if y == 0:
return True
# if there are no tokens we don't really have any info
tokens = self._get_tokens(y)
if not tokens:
return False
# this assumes that people aren't gonna use these macros inside of
# blocks, which is probably ok.
t = tokens[0]
if t.fqmatchs('c.macro.start', ('#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
# types of tokens below
decl = False
for t in tokens:
if decl and self._is_ignored(t):
pass
elif t.isa('c.keyword', 'c.identifier', 'c.type'):
decl = True
elif decl and t.name == 'c.function':
break
else:
decl = False
break
return decl
def _is_indent(self, t):
return t.name == 'spaces'
def _is_ignored(self, t):
return t.fqisa('spaces', 'eol', 'c.comment', 'c.comment.start',
'c.comment.data', 'c.comment.null', 'c.comment.end')
class CCheckSyntax(Exec):
'''Build this C program (using the mode's make cmd)'''
show_success = False
args = []
def _execute(self, w, **vargs):
cmd='C_INCLUDE_PATH=. %s' % w.application.config['c.syntax-cmd']
if w.application.config['c.syntax-rel-dir']:
self._doit(w, w.buffer.path, cmd, cmdname='c-check-syntax',
cmddir=os.path.dirname(w.buffer.path))
else:
self._doit(w, w.buffer.path, cmd, cmdname='c-check-syntax')
class CMake(Exec):
'''Build this C program (using the mode's make cmd)'''
show_success = False
args = []
def _execute(self, w, **vargs):
cmd = w.application.config['c.make-cmd'],
if w.application.config['c.make-rel-dir']:
d = os.path.dirname(w.buffer.path)
self._doit(w, w.buffer.path, cmd, cmdname='c-make', cmddir=d)
else:
self._doit(w, w.buffer.path, cmd, cmdname='c-make')
# white is for delimiters, operators, numbers
default = ('default', 'default')
# magenta is for keywords/builtins
lo_magenta = ('magenta202', 'default')
hi_magenta = ('magenta505', 'default')
# red is for comments
lo_red = ('red300', 'default')
hi_red = ('red511', 'default')
# orange are for arrays and hashes
hi_orange = ('yellow531', 'default')
lo_orange = ('yellow520', 'default')
# yellow is for scalars and prototypes
hi_yellow = ('yellow551', 'default')
lo_yellow = ('yellow330', 'default')
# green is for strings and characters
lo_green = ('green030', 'default')
hi_green = ('green050', 'default')
# cyan is for quotes, evals, regexes, subs
lo_cyan = ('cyan033', 'default')
hi_cyan = ('cyan155', 'default')
# blue is unused
lo_blue = ('blue113', 'default')
hi_blue = ('blue225', 'default')
class C(Fundamental):
name = 'C'
extensions = ['.c', '.h', '.cpp']
tabbercls = CTabber2
grammar = CGrammar
opentokens = ('delimiter',)
opentags = {'(': ')', '[': ']', '{': '}'}
closetokens = ('delimiter',)
closetags = {')': '(', ']': '[', '}': '{'}
actions = [CCheckSyntax, CMake]
format = "%(flag)s %(bname)s (%(mname)s) %(indent)s %(cursor)s %(perc)s [%(func)s]"
commentc = '//'
colors = {
'c.comment': hi_red,
'c.comment.start': hi_red,
'c.comment.data': hi_red,
'c.comment.end': hi_red,
'c.macrocomment.start': hi_red,
'c.macrocomment.null': hi_red,
'c.macrocomment.end': hi_red,
'c.macro': hi_blue,
'c.macro.start': hi_blue,
'c.macro.name': hi_orange,
'c.macro.null': hi_magenta,
'c.macro.concat': hi_yellow,
'c.macro.quoted': hi_green,
'c.error.start': hi_blue,
'c.error.data': hi_green,
'c.char.start': lo_green,
'c.char.end': lo_green,
'c.char.data': hi_green,
'c.char.escaped': hi_magenta,
'c.char.octal': hi_magenta,
'c.type': hi_cyan,
'c.include': hi_blue,
'c.header': lo_orange,
'c.constant': hi_orange,
}
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,
}
lconfig = {
'ignore_suffix': ['.o'],
}
_bindings = {
'close-paren': (')',),
'close-brace': ('}',),
'close-bracket': (']',),
'c-check-syntax': ('C-c s',),
'c-make': ('C-c C-c',),
}
def get_functions(self): return {}
def get_function_names(self): return []
def get_line_function(self, y): return None
def get_status_names(self):
names = Fundamental.get_status_names(self)
c = self.window.logical_cursor()
names['func'] = self.get_line_function(c.y)
return names
install = C.install