This repository has been archived on 2021-03-01. You can view files and clone it, but cannot push or open issues or pull requests.
moulinette/c.py

624 lines
22 KiB
Python
Executable File

#!/usr/bin/python
import re
import os
from report import perr
from common import checkTrailingWhitespace
from common import check80cols
def validateString(path, content, start, lign):
current = start + 1
contentSize = len(content)
while current < contentSize and (content[current] != '"' or content[current - 1] == '\\'):
if content[current] == '\n':
perr(path, "Wrap in string", lign)
current += 1
return (current - start)
def validatePreprocDirective(path, content, start, start_lign, states):
lign = start_lign
current = start
contentSize = len(content)
cOnLine = 0
eolAlign = -1 #Alignement de la fin de ligne
state = 0
directive = "" #The name of the directive
restol = "" #The rest of the line
ident = 0;
while current < contentSize and (content[current] != '\n' or content[current - 1] == '\\'):
current += 1
if content[current] == '\n' and content[current - 1] == '\\':
lign += 1
cOnLine = -1
if content[current - 1] == '\t':
perr(path, "Tabulation are baned!", lign)
if cOnLine % 8 == 0:
cOnLine += 8 + 1
else:
cOnLine += cOnLine % 8 + 1
elif content[current] != '\n':
cOnLine += 1
#Check indentation
if state == 0:
if content[current] != ' ' and content[current] != '\t':
state += 1
ident = current - start - 1
#Wrap
if content[current] == '\\':
if eolAlign <= 0:
eolAlign = cOnLine
elif cOnLine != eolAlign:
perr(path, "Bad endline preprocessor alignement (expected: {0}, real: {1})".format(eolAlign, cOnLine), lign)
#Spaces state
if state == 2 or state == 4 or state == 6 or state == 8:
if content[current] != ' ' and content[current] != '\t':
state += 1
#Searching the directive name
if state == 1:
if content[current] != ' ' and content[current] != '\t':
directive += content[current]
else:
state += 1
#Validate Macro Name
if state == 3:
if content[current] != ' ' and content[current] != '\t' and content[current] != '(':
restol += content[current]
else:
state += 1
if re.match("^[A-Z0-9_]$", restol) is None:
perr(path, "Preprocessor macro name not fully capitalized or contain invalid chars ({0})".format(restol), lign)
restol = ""
#Validate Macro Var
if state == 5:
if content[current] != ' ' and content[current] != '\t' and content[current] != ',':
restol += content[current]
else:
if content[current] != ')':
state += 1
if restol != "" and re.match("^[A-Z][a-z0-9_]$", restol) is None:
perr(path, "Preprocessor macro variable name not fully capitalized or contains invalid chars ({0})".format(restol), lign)
restol = ""
if directive.find("endif") == 0:
states['preproc-ident'] -= 1
if ident != states['preproc-ident']:
perr(path, "Bad preprocessor indentation (expected: {0}, real: {1})".format(states['preproc-ident'], (ident)), lign)
if directive.find("if") == 0:
states['preproc-ident'] += 1
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
return eaten
def validateType(path, content, current, lign, states):
content += " " #Add a sentinel
contentSize = len(content)
state = 0
while current + 1 < contentSize:
if content[current] == '*' and content[current - 1] != ' ':
perr(path, "No space before *", lign)
elif content[current] == '=' and content[current - 1] != ' ':
perr(path, "No space before =", lign)
elif content[current] == '=' and content[current + 1] != ' ':
perr(path, "No space after =", lign)
elif content[current] == '=':
state = 1
elif state == 1:
if content[current] == '*' and content[current + 1] != ' ':
perr(path, "No space after *", lign)
elif content[current] == '+' and content[current + 1] != ' ':
perr(path, "No space after +", lign)
elif content[current] == '+' and content[current - 1] != ' ':
perr(path, "No space before +", lign)
elif content[current] == '-' and content[current + 1] != ' ':
perr(path, "No space before -", lign)
elif content[current] == '-' and content[current - 1] != ' ':
perr(path, "No space before -", lign)
elif content[current] == '/' and content[current + 1] != ' ':
perr(path, "No space before /", lign)
elif content[current] == '/' and content[current - 1] != ' ':
perr(path, "No space before /", lign)
elif content[current] == '%' and content[current + 1] != ' ':
perr(path, "No space before %", lign)
elif content[current] == '%' and content[current - 1] != ' ':
perr(path, "No space before %", lign)
elif content[current] == '(' and content[current - 1] != ' ':
perr(path, "Warning: it seems there is a function calling with a declaration", lign)
current += 1
def validateStruct(path, content, start, start_lign, states):
lign = start_lign
current = start
contentSize = len(content)
cOnLine = 0
varAlign = -1
state = 0
lastBlank = 0
typeName = ""
strEc = ""
while current < contentSize and content[current] != '}':
if content[current] == ' ' and content[current + 1] != ';':
typeName += strEc + ' '
strEc = ""
else:
strEc += content[current]
#Search the struct begining
if state == 0:
if content[current] == '{':
state = 1
cOnLine = 0
strEc = ""
typeName = ""
if state == 1:
if content[current] == ' ' or content[current] == '\t':
lastBlank = cOnLine
elif content[current] == '\n' and lastBlank != 0:
#Check type
validateType(path, typeName.strip().rstrip(), 0, start_lign, states)
strEc = ""
typeName = ""
#Check variable alignment
if varAlign == -1:
varAlign = lastBlank
elif lastBlank != varAlign:
perr(path, "Bad variable alignement in a struct (expected: {0}, real: {1})".format(varAlign, lastBlank), lign)
elif content[current] == ',':
perr(path, "Two variable declarations aren't allowed on the same line", lign)
elif content[current] == ';' and content[current - 1] == ' ':
perr(path, "There are a space before ;", lign)
if content[current] == '\n':
lign += 1
cOnLine = -1
lastBlank = 0
current += 1
if content[current - 1] == '\t':
if cOnLine % 8 == 0:
cOnLine += 9
else:
cOnLine += cOnLine % 8 + 1
else:
cOnLine += 1
while current < contentSize and content[current] != ';':
cOnLine += 1
current += 1
if content[current] == ';' and content[current - 1] == ' ':
perr(path, "There is a space before ;", lign)
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
return eaten
def validateUnion(path, content, start, start_lign, states):
lign = start_lign
current = start
contentSize = len(content)
cOnLine = 0
varAlign = -1
state = 0
lastBlank = 0
typeName = ""
strEc = ""
while current < contentSize and content[current] != '}':
if content[current] == ' ' and content[current + 1] != ';':
typeName += strEc + ' '
strEc = ""
else:
strEc += content[current]
#Search the union begining
if state == 0:
if content[current] == '{':
state = 1
cOnLine = 0
strEc = ""
typeName = ""
if state == 1:
if content[current] == ' ' or content[current] == '\t':
lastBlank = cOnLine
elif content[current] == '\n' and lastBlank != 0:
#Check type
validateType(path, typeName.strip().rstrip(), 0, start_lign, states)
strEc = ""
typeName = ""
#Check variable alignment
if varAlign == -1:
varAlign = lastBlank
elif lastBlank != varAlign:
perr(path, "Bad variable alignement in an union (expected: {0}, real: {1})".format(varAlign, lastBlank), lign)
elif content[current] == ',':
perr(path, "Two variable declarations aren't allowed on the same line", lign)
elif content[current] == ';' and content[current - 1] == ' ':
perr(path, "There is a space before ;", lign)
if content[current] == '\n':
lign += 1
cOnLine = -1
lastBlank = 0
current += 1
if content[current - 1] == '\t':
if cOnLine % 8 == 0:
cOnLine += 9
else:
cOnLine += cOnLine % 8 + 1
else:
cOnLine += 1
while current < contentSize and content[current] != ';':
cOnLine += 1
current += 1
if content[current] == ';' and content[current - 1] == ' ':
perr(path, "There is a space before ;", lign)
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
return eaten
def validateEnum(path, content, start, start_lign, states):
lign = start_lign
current = start
contentSize = len(content)
cOnLine = 0
varAlign = -1
state = 0
while current < contentSize and content[current] != '}':
#Search the enum begining
if state == 0:
if content[current] == '{':
state = 1
cOnLine = 0
if state == 1:
if content[current] == '=' and content[current + 1] != ' ':
perr(path, "Missing space after =", lign)
if content[current] == '=' and content[current - 1] != ' ':
perr(path, "Missing space before =", lign)
if content[current] == ',' and content[current + 1] != '\n':
perr(path, "Two statements on the same line", lign)
if content[current] == '\n':
lign += 1
cOnLine = -1
if content[current - 1] == '\t':
if cOnLine % 8 == 0:
cOnLine += 9
else:
cOnLine += cOnLine % 8 + 1
else:
cOnLine += 1
current += 1
while current < contentSize and content[current] != ';':
cOnLine += 1
current += 1
if content[current] == ';' and content[current - 1] == ' ':
perr(path, "There is a space before ;", lign)
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
return eaten
def validateTypedef(path, content, start, start_lign, states):
lign = start_lign
current = start
contentSize = len(content)
cOnLine = 0
newname = "" #The name of the typedef
restol = "" #The rest of the line
state = 0
while current < contentSize and (content[current] != ';' or state > 0):
current += 1
if content[current] == '\n':
lign += 1
cOnLine = -1
if content[current - 1] == '\t':
if cOnLine % 8 == 0:
cOnLine += 9
else:
cOnLine += cOnLine % 8 + 1
else:
cOnLine += 1
if content[current] == '{':
state += 1
elif content[current] == '}':
state -= 1
if content[current] == ' ' and content[current + 1] != ';':
restol += newname + ' '
newname = ""
else:
newname += content[current]
if content[current - 1] == ' ' and content[current] == ';':
perr(path, "There is a space before ;", lign)
restol = restol.strip().rstrip();
if restol.find("struct") == 0:
validateStruct(path, restol + ";", 0, start_lign, states)
if newname[0] != 's' or newname[1] != '_':
perr(path, "Bad typedef name for a struct (expected: s_; real: {0}{1})".format(newname[0], newname[1]), lign)
elif restol.find("union") == 0:
validateUnion(path, restol + ";", 0, start_lign, states)
if newname[0] != 'u' or newname[1] != '_':
perr(path, "Bad typedef name for an union (expected: u_; real: {0}{1})".format(newname[0], newname[1]), lign)
elif restol.find("enum") == 0:
validateEnum(path, restol + ";", 0, start_lign, states)
if newname[0] != 'e' or newname[1] != '_':
perr(path, "Bad typedef name for an enum (expected: e_; real: {0}{1})".format(newname[0], newname[1]), lign)
else:
validateType(path, restol, 0, start_lign, states)
if newname[0] != 't' or newname[1] != '_':
perr(path, "Bad typedef name (expected: t_; real: {0}{1})".format(newname[0], newname[1]), lign)
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
return eaten
def validateFor(path, content, start, start_lign, states):
lign = start_lign
current = start
contentSize = len(content)
cOnLine = 0
stack = -1
while current < contentSize and (content[current] != ')' or stack >= 0):
current += 1
if content[current] == '\n':
lign += 1
cOnLine = -1
if content[current - 1] == '\t':
if cOnLine % 8 == 0:
cOnLine += 9
else:
cOnLine += cOnLine % 8 + 1
elif content[current] != '\n':
cOnLine += 1
if content[current] == ';':
if content[current - 1] == ' ' and content[current - 2] != ';':
perr(path, "There is a space before ;", lign)
if content[current + 1] != ' ':
perr(path, "There is no space after ;", lign)
if content[current] == '(':
stack += 1
elif content[current] == ')':
stack -= 1
if content[current] == ')':
if content[current - 1] == ' ' and content[current - 2] != ';':
perr(path, "There is a space before )", lign)
if content[current - 1] == ';':
perr(path, "There is no space before ), expected one in this case", lign)
eaten = { 'chars': current - start + 1, 'lines': lign, 'col': cOnLine }
return eaten
def validateComment(path, content, start, start_lign, states):
lign = start_lign
current = start
contentSize = len(content)
while current + 1 < contentSize and content[current] != '*' and content[current + 1] != '/':
current += 1
eaten = { 'chars': current - start - 1, 'lines': lign }
return eaten
def validateFunction(path, content, start, start_lign, states, prototype):
lign = start_lign
nbLign = 0
current = start
contentSize = len(content)
cOnLine = 0
stack = 0
while current < contentSize and (content[current] != '}' or stack >= 0):
current += 1
if content[current] == '\n':
if cOnLine > 0:
nbLign += 1
lign += 1
cOnLine = -1
if content[current - 1] == '\t':
if cOnLine % 8 == 0:
cOnLine += 9
else:
cOnLine += cOnLine % 8 + 1
elif content[current] != '\n':
cOnLine += 1
#String
if content[current] == '"':
eatenChar = validateString(path, content, current, lign)
current += eatenChar
cOnLine += eatenChar
if content[current - 2] == 'f' and content[current - 1] == 'o' and content[current] == 'r':
eaten = validateFor(path, content, current, lign, states)
cOnLine = eaten['col'];
current += eaten['chars'];
lign = eaten['lines'];
if content[current - 1] == ';' and content[current] != '\n':
perr(path, "There is a ; without new line after", lign)
if content[current] == '{':
stack += 1
elif content[current] == '}':
stack -= 1
#Skip lines containing comments in the count
if content[current] == "/" == content[current - 1]:
nbLign -= 1
elif content[current] == "/" and content[current - 1] == "*":
eaten = validateComment(path, content, current, lign, states)
current += eaten['chars']
lign += eaten['lines']
if nbLign > 25:
perr(path, "To many line in this function: {0}".format(nbLign), start_lign)
#else:
# perr(path, "This function is a {0} lines function, good :)".format(nbLign), start_lign)
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
return eaten
def validateVarOrProto(path, content, start, start_lign, states):
return 1
def validateFunOrGlobal(path, content, start, start_lign, states):
lign = start_lign
current = start
contentSize = len(content)
cOnLine = 0
restol = "" #The rest of the line
while current < contentSize and content[current] != ';' and content[current] != '{':
current += 1
if content[current] == '\n':
lign += 1
cOnLine = -1
if content[current - 1] == '\t':
if cOnLine % 8 == 0:
cOnLine += 9
else:
cOnLine += cOnLine % 8 + 1
elif content[current] != '\n':
cOnLine += 1
if content[current] != '\n':
restol += content[current]
if content[current] == '{':
eaten = validateFunction(path, content, current, lign, states, restol)
current += eaten['chars']
cOnLine = eaten['col']
lign = eaten['lines']
else:
eaten = validateVarOrProto(path, content, current, lign, states)
eaten = { 'chars': current - start, 'lines': lign, 'col': cOnLine }
return eaten
def validateExternalWord(path, content, start, start_lign, states):
lign = start_lign
current = start
contentSize = len(content)
cOnLine = 0
directive = "" #The name of the directive
while current < contentSize and content[current] != '\n':
#Search keyword
if content[current] != ' ' and content[current] != '\t':
directive += content[current]
else:
if directive == "typedef":
eaten = validateTypedef(path, content, current, lign, states)
elif directive == "struct":
eaten = validateStruct(path, content, current, lign, states)
elif directive == "enum":
eaten = validateEnum(path, content, current, lign, states)
elif directive == "union":
eaten = validateUnion(path, content, current, lign, states)
else:
eaten = validateFunOrGlobal(path, content, current, lign, states)
current += eaten['chars']
cOnLine = eaten['col']
lign = eaten['lines']
current += 1
if content[current - 1] == '\t':
if cOnLine % 8 == 0:
cOnLine += 9
else:
cOnLine += cOnLine % 8 + 1
else:
cOnLine += 1
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
return eaten
def checkFile(path, opt):
numLign = 1
with open(path, "r") as fp:
content = fp.read()
current = 0;
cOnLine = 0
last_nblank = 0
contentSize = len(content)
states = {
'preproc-ident': 0 #Niveau d'identation dans le preprocesseur
}
while current < contentSize:
if content[current] == '\n':
numLign += 1
last_nblank = 0
cOnLine = -1
#Preprocessor line
if last_nblank == cOnLine and content[current] == '#':
if cOnLine != 0:
perr(path, "Preprocessor directive mark not on the first column", numLign)
eaten = validatePreprocDirective(path, content, current, numLign, states)
current += eaten['chars']
cOnLine = eaten['col']
numLign = eaten['lines']
#String
elif content[current] == '"':
print "str"
eatenChar = validateString(path, content, current, numLign)
current += eatenChar
cOnLine += eatenChar
#Comments
elif content[current] == '*' and content[current - 1] == '/':
eaten = validateComment(path, content, current, numLign, states)
current += eaten['chars']
cOnLine = 0
numLign = eaten['lines']
#Skip empty lines
elif content[current] != '\n':
eaten = validateExternalWord(path, content, current, numLign, states)
current += eaten['chars']
cOnLine = eaten['col']
numLign = eaten['lines']
if last_nblank == cOnLine and content[current] == ' ' or content[current] == '\t':
last_nblank += 1
current += 1
cOnLine += 1
#print("{1} contient {0} lignes".format(numLign, os.path.basename(path)))
import sys
if __name__ == "__main__":
i = 1
while i < len(sys.argv):
checkFile(sys.argv[i], dict())
i += 1