
import re
import os

line_re = re.compile(r'\s*//\s*#(include|define|if|ifdef|elif|ifndef|endif|else)(?:\s+(.*)\s*$|($))')


def eval_conditional(cond, symbols):
	assert cond.strip() == cond

	if not cond:
		return True

	negate = False
	if cond[0] == '!':
		negate = True
		cond = cond[1:]

	val = cond in symbols
	return not val if negate else val


def run(text, base_dir, debug_filename, symbols = set()):
	"""Rudimentary resolver for the following preprocessor commands:

		// #include <some-file>
		    (no check for cyclic includes!)

		// #ifdef | #if <symbol>
		//   <contents>
		// [ #elif
		//   <alt-contents> ]*
		// [ #else
		//   <alt-contents> ]
		// #endif 

	"""
	out = []
	stack = []

	lines = text.split('\n')
	l_iter = iter(zip(range(1, len(lines)+1),lines))
	push_line = None

	nline = -1

	def error(msg):
		raise Exception(msg + '   @   ' + debug_filename + ':' + str(nline))
	
	while True:
		try:
			nline, line = push_line or next(l_iter)
			push_line = None
		except StopIteration:
			break

		match = line_re.match(line)
		if match:

			skip_branch = False
			
			cmd = match.group(1)
			if cmd == 'include':
				name = match.group(2).strip('<>"\'')
				fpath = os.path.join(base_dir, name)

				print 'handling js #include: ' + fpath
				with open( fpath, 'rt' ) as inp:
					out.append(run(inp.read(), os.path.split(fpath)[1], name, symbols))

			elif cmd in ['if', 'ifdef', 'ifndef']:
				val = eval_conditional(match.group(2), symbols)
				print('eval: ' + match.group(2) + ' as ' + str(val))
				if cmd == 'ifndef':
					val = not val

				skip_branch = not val
				stack.append(val)

			elif cmd in ['else', 'elif']:
				if not stack:
					error('syntax error, unexpected ' + cmd)
				# has been handled before?
				if stack[-1]:
					skip_branch = True
				elif cmd != 'elif' or eval_conditional(match.group(2), symbols):
					stack[-1] = True
				else:
					skip_branch = True
			elif cmd == 'endif':
				if not stack:
					error('syntax error, unexpected endif')
					continue

				stack.pop()

			else:
				error('define/ifdef/endif/else currently ignored')

			if skip_branch:
				# skip everything up to the next elif/else/endif at the same nesting level
				nesting = 1
				while True:
					try:
						nline, line = next(l_iter)
						match = line_re.match(line)
						if match:
							cmd = match.group(1)
							if cmd in ['if', 'ifdef']:
								nesting += 1
							elif cmd == 'endif':
								nesting -= 1

							if cmd in ['else', 'elif', 'endif'] and nesting == 0:
								push_line = nline, line
								break

					except StopIteration:
						error('syntax error, unexpected EOF')
						return
		else:
			out.append(line)

	return '\n'.join(out)

@@ -1462,16 +1462,60 @@
 ymbols)%0A
+%09%09%09%09if cmd == 'ifndef':%0A%09%09%09%09%09val = not val%0A%0A
 %09%09%09%09prin
@@ -1526,16 +1526,28 @@
 val: ' +
+ cmd + ' ' +
  match.g
@@ -1578,51 +1578,8 @@
 al))
-%0A%09%09%09%09if cmd == 'ifndef':%0A%09%09%09%09%09val = not val
 %0A%0A%09%09
@@ -2323,24 +2323,44 @@
 %09%09if match:%0A
+%09%09%09%09%09%09%09done = False%0A
 %09%09%09%09%09%09%09cmd =
@@ -2478,16 +2478,62 @@
 ing -= 1
+%0A%09%09%09%09%09%09%09%09if nesting == 0:%0A%09%09%09%09%09%09%09%09%09done = True
 %0A%0A%09%09%09%09%09%09
@@ -2562,17 +2562,8 @@
 lif'
-, 'endif'
 %5D an
@@ -2567,33 +2567,78 @@
  and nesting == 
-0
+1:%0A%09%09%09%09%09%09%09%09done = True%0A%09%09%09%09%09%09%09%09%0A%09%09%09%09%09%09%09if done
 :%0A%09%09%09%09%09%09%09%09push_l

build system: fix bug causing #ifndef .. #else .. #endif constructs to evaluate to nothing (i.e. neither branch taken)