Skip to content

Instantly share code, notes, and snippets.

@nickelpro
Created November 12, 2019 05:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nickelpro/a317daa8913712336ed4e42e0d58071d to your computer and use it in GitHub Desktop.
Save nickelpro/a317daa8913712336ed4e42e0d58071d to your computer and use it in GitHub Desktop.
C code generator in python
# Inspired by https://github.com/cogu/cfile
c_indent_char = '\t'
def set_indent_char(char):
global c_indent_char
c_indent_char = char
class blank:
def __init__(self, num=1):
self.indent = 0 #Irrelevant, kept because it simplifies sequences
self.num = num
def __str__(self):
# Sequences automatically insert one line break for each element, so
# we substract one line break to account for that
return (self.num - 1) * '\n'
# line and its subclasses can be used as container classes for sequences, which
# can span multiple lines. When used on its own though it's a single line
class line:
def __init__(self, elem, indent=0):
self.elem = elem
self._indent = 0
self.indent = indent
@property
def indent(self):
return self._indent
@indent.setter
def indent(self, val):
if hasattr(self.elem, 'indent'):
self.elem.indent = val
self._indent = val
def __str__(self):
return c_indent_char * self.indent + str(self.elem)
class statement(line):
def __str__(self):
return super().__str__() + ';'
class returnval(line):
def __str__(self):
return c_indent_char * self.indent + f'return {self.elem};'
class typedef(line):
def __init__(self, elem, name, indent=0):
super().__init__(elem, indent)
self.name = name
def __str__(self):
return c_indent_char * self.indent + f'typedef {self.elem} {self.name};'
class linecomment(line):
def __str__(self):
return c_indent_char * self.indent + f'// {self.elem}'
class include(line):
def __init__(self, path, sys=False, indent=0):
super().__init__(
f'#include <{path}>' if sys else f'#include "{path}"', indent
)
class preprocessor(line):
directive = ''
def __init__(self, val, indent=0):
super().__init__(f'#{self.directive} {val}', indent)
class define(preprocessor):
directive = 'define'
class indef(preprocessor):
directive = 'ifndef'
class endif(line):
def __init__(self, indent=0):
super().__init__('#endif', indent)
from collections.abc import MutableSequence
# Group of elements at the same indentation level
class sequence(MutableSequence):
def __init__(self, elems=None, indent=0):
self.elems = [] if elems is None else elems
self._indent = 0
self.indent = indent
def __getitem__(self, key):
return self.elems[key]
def __setitem__(self, key, item):
if(isinstance(item, str)):
item = line(item)
item.indent = self.indent
self.elems[key] = item
def __delitem__(self, key):
del self.elems[key]
def __len__(self):
return len(self.elems)
def insert(self, key, item):
if(isinstance(item, str)):
item = line(item)
item.indent = self.indent
self.elems.insert(key, item)
@property
def indent(self):
return self._indent
@indent.setter
def indent(self, val):
for elem in self.elems:
elem.indent = val
self._indent = val
def __str__(self):
return '\n'.join([str(elem) for elem in self.elems])
#Like sequence, but joins on space instead of newline
class linesequence(sequence):
def __setitem__(self, key, item):
if(isinstance(item, str)):
item = line(item)
item.indent = self.indent if(isinstance(item, sequence)) else 0
self.elems[key] = item
def insert(self, key, item):
if(isinstance(item, str)):
item = line(item)
item.indent = self.indent if(isinstance(item, sequence)) else 0
self.elems.insert(key, item)
@property
def indent(self):
return self._indent
@indent.setter
def indent(self, val):
for elem in self.elems:
elem.indent = self.indent if(isinstance(elem, sequence)) else 0
self._indent = val
def __str__(self):
i = c_indent_char * self.indent
return i + ' '.join([str(elem) for elem in self.elems])
# Common for block comments and block scope items
class _block(sequence):
def __init__(self, elems=None, inner_indent=1, indent=0):
self._inner_indent = inner_indent
super().__init__(elems, indent)
@property
def inner_indent(self):
return self._inner_indent
@inner_indent.setter
def inner_indent(self, val):
for elem in self.elems:
elem.indent = self._indent + val
self._inner_indent = val
@property
def indent(self):
return self._indent
@indent.setter
def indent(self, val):
for elem in self.elems:
elem.indent = val + self._inner_indent
self._indent = val
# Curly bracket {} grouped elements, optionally at different indentation level
# Does not indent first line, that's expected to be done by a wrapping class
# such as line, statement, or typedef
class block(_block):
def __str__(self):
i = self.indent * c_indent_char
return f'{{\n{super().__str__()}\n{i}}}'
# Similar to block but with block comment /* */ delimiters instead of {}
# Doesn't need to be wrapped in anything to get indentation correct
class blockcomment(_block):
def __str__(self):
i = self.indent * c_indent_char
return f'{i}/*\n{super().__str__()}\n{i}*/'
class blocktype(block):
keyword = ''
def __init__(self, name=None, elems=None, inner_indent=1, indent=0):
super().__init__(indent=indent, inner_indent=inner_indent, elems=elems)
self.name = name
def __str__(self):
if self.name:
return f'{self.keyword} {self.name} {super().__str__()}'
return f'{self.keyword} {super().__str__()}'
class struct(blocktype):
keyword = 'struct'
class union(blocktype):
keyword = 'union'
class conditional(block):
keyword = ''
def __init__(self, condition, elems=None, inner_indent=1, indent=0):
super().__init__(indent=indent, inner_indent=inner_indent, elems=elems)
self.condition = condition
def __str__(self):
return f'{self.keyword}({self.condition}) {super().__str__()}'
class ifcond(conditional):
keyword = 'if'
class elifcond(conditional):
keyword = 'else if'
class elsecond(conditional):
keyword = 'else'
class variable:
def __init__(self, name, typename, array=0):
self.name = name
self.typename = typename
self.array = array
@property
def decl(self):
return variabledecl(self.name, self.typename, self.array)
def __str__(self):
return str(self.name)
class variabledecl(variable):
def __str__(self):
if self.array:
return f'{self.typename} {self.name}[{self.array}]'
return f'{self.typename} {self.name}'
class operator:
op = ''
def __init__(self, lvalue, rvalue):
self.lvalue = lvalue
self.rvalue = rvalue
def __str__(self):
return f'{self.lvalue} {self.op} {self.rvalue}'
class assign(operator):
op = '='
class addeq(operator):
op = '+='
class subeq(operator):
op = '-='
class eqeq(operator):
op = '=='
class lth(operator):
op = '<'
class ltheq(operator):
op = '<='
class gth(operator):
op = '>'
class gtheq(operator):
op = '>='
class wrap:
def __init__(self, val, invert=False):
self.val = val
self.invert = False
def __str__(self):
if self.invert:
return f'!({self.val})'
return f'({self.val})'
class fcall(MutableSequence):
def __init__(self, name, typename, args=None):
self.name = name
self.typename = typename
self.args = [] if args is None else list(args)
def __getitem__(self, key):
return self.args[key]
def __setitem__(self, key, item):
self.args[key] = item
def __delitem__(self, key):
del self.args[key]
def __len__(self):
return len(self.args)
def insert(self, key, item):
self.args.insert(key, item)
@property
def decl(self):
return fdecl(name, typename, [a.decl for a in self.args])
def __str__(self):
a = ','.join([str(arg) for arg in self.args])
return f'{self.name}({a})'
class fdecl(fcall):
def __str__(self):
a = ','.join([str(arg) for arg in self.args])
return f'{self.typename} {self.name}({a})'
class _file(sequence):
def __init__(self, path, elems=None):
self.path = path
super().__init__(elems)
class cfile(_file):
pass
import os
class hfile(_file):
def __init__(self, path, elems=None, guard=None):
super().__init__(path, elems)
if guard is None:
bn = os.path.basename(path)
self.guard = f'{os.path.splitext(bn)[0].upper()}_H'
else:
self.guard = guard
def __str__(self):
t = sequence([indef(self.guard), define(self.guard), blank(2)])
t.extend(self)
t.append(endif())
return str(t)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment