from pyparsing import (
Literal,
Word,
Group,
Forward,
alphas,
alphanums,
Regex,
ParseException,
CaselessKeyword,
Suppress,
delimitedList,
infixNotation,
)
import math
import operator
import pyparsing as pp
import re
from apiconventions import APIConventions as APIConventions
conventions = APIConventions()
def markupPassthrough(name):
return name
def leafMarkupAsciidoc(name):
return conventions.formatVersionOrExtension(name)
def leafMarkupC(name):
(apivariant, major, minor) = apiVersionNameMatch(name)
if apivariant is not None:
return name
else:
return f'ext.{name}'
opMarkupAsciidocMap = { '+' : 'and', ',' : 'or' }
def opMarkupAsciidoc(op):
return opMarkupAsciidocMap[op]
opMarkupCMap = { '+' : '&&', ',' : '||' }
def opMarkupC(op):
return opMarkupCMap[op]
exprStack = []
def push_first(toks):
exprStack.append(toks[0])
dependencyIdent = Word(alphanums + '_')
dependencyExpr = pp.infixNotation(dependencyIdent,
[ (pp.oneOf(', +'), 2, pp.opAssoc.LEFT), ])
_bnf = None
def dependencyBNF():
global _bnf
if _bnf is None:
and_, or_ = map(Literal, '+,')
lpar, rpar = map(Suppress, '()')
boolop = and_ | or_
expr = Forward()
expr_list = delimitedList(Group(expr))
atom = (
boolop[...]
+ (
(dependencyIdent).setParseAction(push_first)
| Group(lpar + expr + rpar)
)
)
expr <<= atom + (boolop + atom).setParseAction(push_first)[...]
_bnf = expr
return _bnf
_opn = {
'+': operator.and_,
',': operator.or_,
}
def evaluateStack(stack, isSupported):
op, num_args = stack.pop(), 0
if isinstance(op, tuple):
op, num_args = op
if op in '+,':
op2 = evaluateStack(stack, isSupported)
op1 = evaluateStack(stack, isSupported)
return _opn[op](op1, op2)
elif op[0].isalpha():
return isSupported(op)
else:
raise Exception(f'invalid op: {op}')
def evaluateDependency(dependency, isSupported):
global exprStack
exprStack = []
results = dependencyBNF().parseString(dependency, parseAll=True)
val = evaluateStack(exprStack[:], isSupported)
return val
def evalDependencyLanguage(stack, leafMarkup, opMarkup, parenthesize, root):
op, num_args = stack.pop(), 0
if isinstance(op, tuple):
op, num_args = op
if op in '+,':
rhs = evalDependencyLanguage(stack, leafMarkup, opMarkup, parenthesize, root = False)
opname = opMarkup(op)
lhs = evalDependencyLanguage(stack, leafMarkup, opMarkup, parenthesize, root = False)
if parenthesize and not root:
return f'({lhs} {opname} {rhs})'
else:
return f'{lhs} {opname} {rhs}'
elif op[0].isalpha():
return leafMarkup(op)
else:
raise Exception(f'invalid op: {op}')
def dependencyLanguage(dependency, leafMarkup, opMarkup, parenthesize):
global exprStack
exprStack = []
results = dependencyBNF().parseString(dependency, parseAll=True)
return evalDependencyLanguage(exprStack, leafMarkup, opMarkup, parenthesize, root = True)
def dependencyLanguageComment(dependency):
return dependencyLanguage(dependency, leafMarkup = markupPassthrough, opMarkup = opMarkupAsciidoc, parenthesize = True)
def dependencyLanguageSpecMacros(dependency):
return dependencyLanguage(dependency, leafMarkup = leafMarkupAsciidoc, opMarkup = opMarkupAsciidoc, parenthesize = False)
def dependencyLanguageC(dependency):
return dependencyLanguage(dependency, leafMarkup = leafMarkupC, opMarkup = opMarkupC, parenthesize = True)
def evalDependencyNames(stack):
op, num_args = stack.pop(), 0
if isinstance(op, tuple):
op, num_args = op
if op in '+,':
return evalDependencyNames(stack) | evalDependencyNames(stack)
elif op[0].isalpha():
return { op }
else:
raise Exception(f'invalid op: {op}')
def dependencyNames(dependency):
global exprStack
exprStack = []
results = dependencyBNF().parseString(dependency, parseAll=True)
return evalDependencyNames(exprStack)
def markupTraverse(expr, level = 0, root = True):
if level > 0:
prefix = '{nbsp}{nbsp}' * level * 2 + ' '
else:
prefix = ''
str = ''
for elem in expr:
if isinstance(elem, pp.ParseResults):
if not root:
nextlevel = level + 1
else:
nextlevel = level
str = str + markupTraverse(elem, level = nextlevel, root = False)
elif elem in ('+', ','):
str = str + f'{prefix}{opMarkupAsciidoc(elem)} +\n'
else:
str = str + f'{prefix}{leafMarkupAsciidoc(elem)} +\n'
return str
def dependencyMarkup(dependency):
parsed = dependencyExpr.parseString(dependency)
return markupTraverse(parsed)
if __name__ == "__main__":
for str in [ 'VK_VERSION_1_0', 'cl_khr_extension_name', 'XR_VERSION_3_2', 'CL_VERSION_1_0' ]:
print(f'{str} -> {conventions.formatVersionOrExtension(str)}')
import sys
sys.exit(0)
termdict = {
'VK_VERSION_1_1' : True,
'false' : False,
'true' : True,
}
termSupported = lambda name: name in termdict and termdict[name]
def test(dependency, expected):
val = False
try:
val = evaluateDependency(dependency, termSupported)
except ParseException as pe:
print(dependency, f'failed parse: {dependency}')
except Exception as e:
print(dependency, f'failed eval: {dependency}')
if val == expected:
True
else:
print(f'{dependency} ERROR: {val} != {expected}')
test('false,false+false', False)
test('false,false+true', False)
test('false,true+false', False)
test('false,true+true', True)
test('true,false+false', False)
test('true,false+true', True)
test('true,true+false', False)
test('true,true+true', True)
test('false,(false+false)', False)
test('false,(false+true)', False)
test('false,(true+false)', False)
test('false,(true+true)', True)
test('true,(false+false)', True)
test('true,(false+true)', True)
test('true,(true+false)', True)
test('true,(true+true)', True)
test('false+false,false', False)
test('false+false,true', True)
test('false+true,false', False)
test('false+true,true', True)
test('true+false,false', False)
test('true+false,true', True)
test('true+true,false', True)
test('true+true,true', True)
test('false+(false,false)', False)
test('false+(false,true)', False)
test('false+(true,false)', False)
test('false+(true,true)', False)
test('true+(false,false)', False)
test('true+(false,true)', True)
test('true+(true,false)', True)
test('true+(true,true)', True)
for dependency in [
'true+false',
'true+(true+false),(false,true)',
'VK_VERSION_1_0+VK_KHR_display',
]:
print(f'expr = {dependency}\n{dependencyMarkup(dependency)}')
print(f' spec language = {dependencyLanguageSpecMacros(dependency)}')
print(f' comment language = {dependencyLanguageComment(dependency)}')
print(f' C language = {dependencyLanguageC(dependency)}')
print(f' names = {dependencyNames(dependency)}')
print(f' value = {evaluateDependency(dependency, termSupported)}')