c_parser.py 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. from sympy.external import import_module
  2. import os
  3. cin = import_module('clang.cindex', import_kwargs = {'fromlist': ['cindex']})
  4. """
  5. This module contains all the necessary Classes and Function used to Parse C and
  6. C++ code into SymPy expression
  7. The module serves as a backend for SymPyExpression to parse C code
  8. It is also dependent on Clang's AST and SymPy's Codegen AST.
  9. The module only supports the features currently supported by the Clang and
  10. codegen AST which will be updated as the development of codegen AST and this
  11. module progresses.
  12. You might find unexpected bugs and exceptions while using the module, feel free
  13. to report them to the SymPy Issue Tracker
  14. Features Supported
  15. ==================
  16. - Variable Declarations (integers and reals)
  17. - Assignment (using integer & floating literal and function calls)
  18. - Function Definitions and Declaration
  19. - Function Calls
  20. - Compound statements, Return statements
  21. Notes
  22. =====
  23. The module is dependent on an external dependency which needs to be installed
  24. to use the features of this module.
  25. Clang: The C and C++ compiler which is used to extract an AST from the provided
  26. C source code.
  27. References
  28. ==========
  29. .. [1] https://github.com/sympy/sympy/issues
  30. .. [2] https://clang.llvm.org/docs/
  31. .. [3] https://clang.llvm.org/docs/IntroductionToTheClangAST.html
  32. """
  33. if cin:
  34. from sympy.codegen.ast import (Variable, Integer, Float,
  35. FunctionPrototype, FunctionDefinition, FunctionCall,
  36. none, Return, Assignment, intc, int8, int16, int64,
  37. uint8, uint16, uint32, uint64, float32, float64, float80,
  38. aug_assign, bool_, While, CodeBlock)
  39. from sympy.codegen.cnodes import (PreDecrement, PostDecrement,
  40. PreIncrement, PostIncrement)
  41. from sympy.core import Add, Mod, Mul, Pow, Rel
  42. from sympy.logic.boolalg import And, as_Boolean, Not, Or
  43. from sympy.core.symbol import Symbol
  44. from sympy.core.sympify import sympify
  45. from sympy.logic.boolalg import (false, true)
  46. import sys
  47. import tempfile
  48. class BaseParser:
  49. """Base Class for the C parser"""
  50. def __init__(self):
  51. """Initializes the Base parser creating a Clang AST index"""
  52. self.index = cin.Index.create()
  53. def diagnostics(self, out):
  54. """Diagostics function for the Clang AST"""
  55. for diag in self.tu.diagnostics:
  56. print('%s %s (line %s, col %s) %s' % (
  57. {
  58. 4: 'FATAL',
  59. 3: 'ERROR',
  60. 2: 'WARNING',
  61. 1: 'NOTE',
  62. 0: 'IGNORED',
  63. }[diag.severity],
  64. diag.location.file,
  65. diag.location.line,
  66. diag.location.column,
  67. diag.spelling
  68. ), file=out)
  69. class CCodeConverter(BaseParser):
  70. """The Code Convereter for Clang AST
  71. The converter object takes the C source code or file as input and
  72. converts them to SymPy Expressions.
  73. """
  74. def __init__(self):
  75. """Initializes the code converter"""
  76. super().__init__()
  77. self._py_nodes = []
  78. self._data_types = {
  79. "void": {
  80. cin.TypeKind.VOID: none
  81. },
  82. "bool": {
  83. cin.TypeKind.BOOL: bool_
  84. },
  85. "int": {
  86. cin.TypeKind.SCHAR: int8,
  87. cin.TypeKind.SHORT: int16,
  88. cin.TypeKind.INT: intc,
  89. cin.TypeKind.LONG: int64,
  90. cin.TypeKind.UCHAR: uint8,
  91. cin.TypeKind.USHORT: uint16,
  92. cin.TypeKind.UINT: uint32,
  93. cin.TypeKind.ULONG: uint64
  94. },
  95. "float": {
  96. cin.TypeKind.FLOAT: float32,
  97. cin.TypeKind.DOUBLE: float64,
  98. cin.TypeKind.LONGDOUBLE: float80
  99. }
  100. }
  101. def parse(self, filenames, flags):
  102. """Function to parse a file with C source code
  103. It takes the filename as an attribute and creates a Clang AST
  104. Translation Unit parsing the file.
  105. Then the transformation function is called on the translation unit,
  106. whose reults are collected into a list which is returned by the
  107. function.
  108. Parameters
  109. ==========
  110. filenames : string
  111. Path to the C file to be parsed
  112. flags: list
  113. Arguments to be passed to Clang while parsing the C code
  114. Returns
  115. =======
  116. py_nodes: list
  117. A list of SymPy AST nodes
  118. """
  119. filename = os.path.abspath(filenames)
  120. self.tu = self.index.parse(
  121. filename,
  122. args=flags,
  123. options=cin.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD
  124. )
  125. for child in self.tu.cursor.get_children():
  126. if child.kind == cin.CursorKind.VAR_DECL:
  127. self._py_nodes.append(self.transform(child))
  128. elif (child.kind == cin.CursorKind.FUNCTION_DECL):
  129. self._py_nodes.append(self.transform(child))
  130. else:
  131. pass
  132. return self._py_nodes
  133. def parse_str(self, source, flags):
  134. """Function to parse a string with C source code
  135. It takes the source code as an attribute, stores it in a temporary
  136. file and creates a Clang AST Translation Unit parsing the file.
  137. Then the transformation function is called on the translation unit,
  138. whose reults are collected into a list which is returned by the
  139. function.
  140. Parameters
  141. ==========
  142. source : string
  143. Path to the C file to be parsed
  144. flags: list
  145. Arguments to be passed to Clang while parsing the C code
  146. Returns
  147. =======
  148. py_nodes: list
  149. A list of SymPy AST nodes
  150. """
  151. file = tempfile.NamedTemporaryFile(mode = 'w+', suffix = '.cpp')
  152. file.write(source)
  153. file.seek(0)
  154. self.tu = self.index.parse(
  155. file.name,
  156. args=flags,
  157. options=cin.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD
  158. )
  159. file.close()
  160. for child in self.tu.cursor.get_children():
  161. if child.kind == cin.CursorKind.VAR_DECL:
  162. self._py_nodes.append(self.transform(child))
  163. elif (child.kind == cin.CursorKind.FUNCTION_DECL):
  164. self._py_nodes.append(self.transform(child))
  165. else:
  166. pass
  167. return self._py_nodes
  168. def transform(self, node):
  169. """Transformation Function for Clang AST nodes
  170. It determines the kind of node and calls the respective
  171. transformation function for that node.
  172. Raises
  173. ======
  174. NotImplementedError : if the transformation for the provided node
  175. is not implemented
  176. """
  177. try:
  178. handler = getattr(self, 'transform_%s' % node.kind.name.lower())
  179. except AttributeError:
  180. print(
  181. "Ignoring node of type %s (%s)" % (
  182. node.kind,
  183. ' '.join(
  184. t.spelling for t in node.get_tokens())
  185. ),
  186. file=sys.stderr
  187. )
  188. handler = None
  189. if handler:
  190. result = handler(node)
  191. return result
  192. def transform_var_decl(self, node):
  193. """Transformation Function for Variable Declaration
  194. Used to create nodes for variable declarations and assignments with
  195. values or function call for the respective nodes in the clang AST
  196. Returns
  197. =======
  198. A variable node as Declaration, with the initial value if given
  199. Raises
  200. ======
  201. NotImplementedError : if called for data types not currently
  202. implemented
  203. Notes
  204. =====
  205. The function currently supports following data types:
  206. Boolean:
  207. bool, _Bool
  208. Integer:
  209. 8-bit: signed char and unsigned char
  210. 16-bit: short, short int, signed short,
  211. signed short int, unsigned short, unsigned short int
  212. 32-bit: int, signed int, unsigned int
  213. 64-bit: long, long int, signed long,
  214. signed long int, unsigned long, unsigned long int
  215. Floating point:
  216. Single Precision: float
  217. Double Precision: double
  218. Extended Precision: long double
  219. """
  220. if node.type.kind in self._data_types["int"]:
  221. type = self._data_types["int"][node.type.kind]
  222. elif node.type.kind in self._data_types["float"]:
  223. type = self._data_types["float"][node.type.kind]
  224. elif node.type.kind in self._data_types["bool"]:
  225. type = self._data_types["bool"][node.type.kind]
  226. else:
  227. raise NotImplementedError("Only bool, int "
  228. "and float are supported")
  229. try:
  230. children = node.get_children()
  231. child = next(children)
  232. #ignoring namespace and type details for the variable
  233. while child.kind == cin.CursorKind.NAMESPACE_REF:
  234. child = next(children)
  235. while child.kind == cin.CursorKind.TYPE_REF:
  236. child = next(children)
  237. val = self.transform(child)
  238. supported_rhs = [
  239. cin.CursorKind.INTEGER_LITERAL,
  240. cin.CursorKind.FLOATING_LITERAL,
  241. cin.CursorKind.UNEXPOSED_EXPR,
  242. cin.CursorKind.BINARY_OPERATOR,
  243. cin.CursorKind.PAREN_EXPR,
  244. cin.CursorKind.UNARY_OPERATOR,
  245. cin.CursorKind.CXX_BOOL_LITERAL_EXPR
  246. ]
  247. if child.kind in supported_rhs:
  248. if isinstance(val, str):
  249. value = Symbol(val)
  250. elif isinstance(val, bool):
  251. if node.type.kind in self._data_types["int"]:
  252. value = Integer(0) if val == False else Integer(1)
  253. elif node.type.kind in self._data_types["float"]:
  254. value = Float(0.0) if val == False else Float(1.0)
  255. elif node.type.kind in self._data_types["bool"]:
  256. value = sympify(val)
  257. elif isinstance(val, (Integer, int, Float, float)):
  258. if node.type.kind in self._data_types["int"]:
  259. value = Integer(val)
  260. elif node.type.kind in self._data_types["float"]:
  261. value = Float(val)
  262. elif node.type.kind in self._data_types["bool"]:
  263. value = sympify(bool(val))
  264. else:
  265. value = val
  266. return Variable(
  267. node.spelling
  268. ).as_Declaration(
  269. type = type,
  270. value = value
  271. )
  272. elif child.kind == cin.CursorKind.CALL_EXPR:
  273. return Variable(
  274. node.spelling
  275. ).as_Declaration(
  276. value = val
  277. )
  278. else:
  279. raise NotImplementedError("Given "
  280. "variable declaration \"{}\" "
  281. "is not possible to parse yet!"
  282. .format(" ".join(
  283. t.spelling for t in node.get_tokens()
  284. )
  285. ))
  286. except StopIteration:
  287. return Variable(
  288. node.spelling
  289. ).as_Declaration(
  290. type = type
  291. )
  292. def transform_function_decl(self, node):
  293. """Transformation Function For Function Declaration
  294. Used to create nodes for function declarations and definitions for
  295. the respective nodes in the clang AST
  296. Returns
  297. =======
  298. function : Codegen AST node
  299. - FunctionPrototype node if function body is not present
  300. - FunctionDefinition node if the function body is present
  301. """
  302. if node.result_type.kind in self._data_types["int"]:
  303. ret_type = self._data_types["int"][node.result_type.kind]
  304. elif node.result_type.kind in self._data_types["float"]:
  305. ret_type = self._data_types["float"][node.result_type.kind]
  306. elif node.result_type.kind in self._data_types["bool"]:
  307. ret_type = self._data_types["bool"][node.result_type.kind]
  308. elif node.result_type.kind in self._data_types["void"]:
  309. ret_type = self._data_types["void"][node.result_type.kind]
  310. else:
  311. raise NotImplementedError("Only void, bool, int "
  312. "and float are supported")
  313. body = []
  314. param = []
  315. try:
  316. children = node.get_children()
  317. child = next(children)
  318. # If the node has any children, the first children will be the
  319. # return type and namespace for the function declaration. These
  320. # nodes can be ignored.
  321. while child.kind == cin.CursorKind.NAMESPACE_REF:
  322. child = next(children)
  323. while child.kind == cin.CursorKind.TYPE_REF:
  324. child = next(children)
  325. # Subsequent nodes will be the parameters for the function.
  326. try:
  327. while True:
  328. decl = self.transform(child)
  329. if (child.kind == cin.CursorKind.PARM_DECL):
  330. param.append(decl)
  331. elif (child.kind == cin.CursorKind.COMPOUND_STMT):
  332. for val in decl:
  333. body.append(val)
  334. else:
  335. body.append(decl)
  336. child = next(children)
  337. except StopIteration:
  338. pass
  339. except StopIteration:
  340. pass
  341. if body == []:
  342. function = FunctionPrototype(
  343. return_type = ret_type,
  344. name = node.spelling,
  345. parameters = param
  346. )
  347. else:
  348. function = FunctionDefinition(
  349. return_type = ret_type,
  350. name = node.spelling,
  351. parameters = param,
  352. body = body
  353. )
  354. return function
  355. def transform_parm_decl(self, node):
  356. """Transformation function for Parameter Declaration
  357. Used to create parameter nodes for the required functions for the
  358. respective nodes in the clang AST
  359. Returns
  360. =======
  361. param : Codegen AST Node
  362. Variable node with the value and type of the variable
  363. Raises
  364. ======
  365. ValueError if multiple children encountered in the parameter node
  366. """
  367. if node.type.kind in self._data_types["int"]:
  368. type = self._data_types["int"][node.type.kind]
  369. elif node.type.kind in self._data_types["float"]:
  370. type = self._data_types["float"][node.type.kind]
  371. elif node.type.kind in self._data_types["bool"]:
  372. type = self._data_types["bool"][node.type.kind]
  373. else:
  374. raise NotImplementedError("Only bool, int "
  375. "and float are supported")
  376. try:
  377. children = node.get_children()
  378. child = next(children)
  379. # Any namespace nodes can be ignored
  380. while child.kind in [cin.CursorKind.NAMESPACE_REF,
  381. cin.CursorKind.TYPE_REF,
  382. cin.CursorKind.TEMPLATE_REF]:
  383. child = next(children)
  384. # If there is a child, it is the default value of the parameter.
  385. lit = self.transform(child)
  386. if node.type.kind in self._data_types["int"]:
  387. val = Integer(lit)
  388. elif node.type.kind in self._data_types["float"]:
  389. val = Float(lit)
  390. elif node.type.kind in self._data_types["bool"]:
  391. val = sympify(bool(lit))
  392. else:
  393. raise NotImplementedError("Only bool, int "
  394. "and float are supported")
  395. param = Variable(
  396. node.spelling
  397. ).as_Declaration(
  398. type = type,
  399. value = val
  400. )
  401. except StopIteration:
  402. param = Variable(
  403. node.spelling
  404. ).as_Declaration(
  405. type = type
  406. )
  407. try:
  408. self.transform(next(children))
  409. raise ValueError("Can't handle multiple children on parameter")
  410. except StopIteration:
  411. pass
  412. return param
  413. def transform_integer_literal(self, node):
  414. """Transformation function for integer literal
  415. Used to get the value and type of the given integer literal.
  416. Returns
  417. =======
  418. val : list
  419. List with two arguments type and Value
  420. type contains the type of the integer
  421. value contains the value stored in the variable
  422. Notes
  423. =====
  424. Only Base Integer type supported for now
  425. """
  426. try:
  427. value = next(node.get_tokens()).spelling
  428. except StopIteration:
  429. # No tokens
  430. value = node.literal
  431. return int(value)
  432. def transform_floating_literal(self, node):
  433. """Transformation function for floating literal
  434. Used to get the value and type of the given floating literal.
  435. Returns
  436. =======
  437. val : list
  438. List with two arguments type and Value
  439. type contains the type of float
  440. value contains the value stored in the variable
  441. Notes
  442. =====
  443. Only Base Float type supported for now
  444. """
  445. try:
  446. value = next(node.get_tokens()).spelling
  447. except (StopIteration, ValueError):
  448. # No tokens
  449. value = node.literal
  450. return float(value)
  451. def transform_string_literal(self, node):
  452. #TODO: No string type in AST
  453. #type =
  454. #try:
  455. # value = next(node.get_tokens()).spelling
  456. #except (StopIteration, ValueError):
  457. # No tokens
  458. # value = node.literal
  459. #val = [type, value]
  460. #return val
  461. pass
  462. def transform_character_literal(self, node):
  463. """Transformation function for character literal
  464. Used to get the value of the given character literal.
  465. Returns
  466. =======
  467. val : int
  468. val contains the ascii value of the character literal
  469. Notes
  470. =====
  471. Only for cases where character is assigned to a integer value,
  472. since character literal is not in SymPy AST
  473. """
  474. try:
  475. value = next(node.get_tokens()).spelling
  476. except (StopIteration, ValueError):
  477. # No tokens
  478. value = node.literal
  479. return ord(str(value[1]))
  480. def transform_cxx_bool_literal_expr(self, node):
  481. """Transformation function for boolean literal
  482. Used to get the value of the given boolean literal.
  483. Returns
  484. =======
  485. value : bool
  486. value contains the boolean value of the variable
  487. """
  488. try:
  489. value = next(node.get_tokens()).spelling
  490. except (StopIteration, ValueError):
  491. value = node.literal
  492. return True if value == 'true' else False
  493. def transform_unexposed_decl(self,node):
  494. """Transformation function for unexposed declarations"""
  495. pass
  496. def transform_unexposed_expr(self, node):
  497. """Transformation function for unexposed expression
  498. Unexposed expressions are used to wrap float, double literals and
  499. expressions
  500. Returns
  501. =======
  502. expr : Codegen AST Node
  503. the result from the wrapped expression
  504. None : NoneType
  505. No childs are found for the node
  506. Raises
  507. ======
  508. ValueError if the expression contains multiple children
  509. """
  510. # Ignore unexposed nodes; pass whatever is the first
  511. # (and should be only) child unaltered.
  512. try:
  513. children = node.get_children()
  514. expr = self.transform(next(children))
  515. except StopIteration:
  516. return None
  517. try:
  518. next(children)
  519. raise ValueError("Unexposed expression has > 1 children.")
  520. except StopIteration:
  521. pass
  522. return expr
  523. def transform_decl_ref_expr(self, node):
  524. """Returns the name of the declaration reference"""
  525. return node.spelling
  526. def transform_call_expr(self, node):
  527. """Transformation function for a call expression
  528. Used to create function call nodes for the function calls present
  529. in the C code
  530. Returns
  531. =======
  532. FunctionCall : Codegen AST Node
  533. FunctionCall node with parameters if any parameters are present
  534. """
  535. param = []
  536. children = node.get_children()
  537. child = next(children)
  538. while child.kind == cin.CursorKind.NAMESPACE_REF:
  539. child = next(children)
  540. while child.kind == cin.CursorKind.TYPE_REF:
  541. child = next(children)
  542. first_child = self.transform(child)
  543. try:
  544. for child in children:
  545. arg = self.transform(child)
  546. if (child.kind == cin.CursorKind.INTEGER_LITERAL):
  547. param.append(Integer(arg))
  548. elif (child.kind == cin.CursorKind.FLOATING_LITERAL):
  549. param.append(Float(arg))
  550. else:
  551. param.append(arg)
  552. return FunctionCall(first_child, param)
  553. except StopIteration:
  554. return FunctionCall(first_child)
  555. def transform_return_stmt(self, node):
  556. """Returns the Return Node for a return statement"""
  557. return Return(next(node.get_children()).spelling)
  558. def transform_compound_stmt(self, node):
  559. """Transformation function for compond statemets
  560. Returns
  561. =======
  562. expr : list
  563. list of Nodes for the expressions present in the statement
  564. None : NoneType
  565. if the compound statement is empty
  566. """
  567. try:
  568. expr = []
  569. children = node.get_children()
  570. for child in children:
  571. expr.append(self.transform(child))
  572. except StopIteration:
  573. return None
  574. return expr
  575. def transform_decl_stmt(self, node):
  576. """Transformation function for declaration statements
  577. These statements are used to wrap different kinds of declararions
  578. like variable or function declaration
  579. The function calls the transformer function for the child of the
  580. given node
  581. Returns
  582. =======
  583. statement : Codegen AST Node
  584. contains the node returned by the children node for the type of
  585. declaration
  586. Raises
  587. ======
  588. ValueError if multiple children present
  589. """
  590. try:
  591. children = node.get_children()
  592. statement = self.transform(next(children))
  593. except StopIteration:
  594. pass
  595. try:
  596. self.transform(next(children))
  597. raise ValueError("Don't know how to handle multiple statements")
  598. except StopIteration:
  599. pass
  600. return statement
  601. def transform_paren_expr(self, node):
  602. """Transformation function for Parenthesized expressions
  603. Returns the result from its children nodes
  604. """
  605. return self.transform(next(node.get_children()))
  606. def transform_compound_assignment_operator(self, node):
  607. """Transformation function for handling shorthand operators
  608. Returns
  609. =======
  610. augmented_assignment_expression: Codegen AST node
  611. shorthand assignment expression represented as Codegen AST
  612. Raises
  613. ======
  614. NotImplementedError
  615. If the shorthand operator for bitwise operators
  616. (~=, ^=, &=, |=, <<=, >>=) is encountered
  617. """
  618. return self.transform_binary_operator(node)
  619. def transform_unary_operator(self, node):
  620. """Transformation function for handling unary operators
  621. Returns
  622. =======
  623. unary_expression: Codegen AST node
  624. simplified unary expression represented as Codegen AST
  625. Raises
  626. ======
  627. NotImplementedError
  628. If dereferencing operator(*), address operator(&) or
  629. bitwise NOT operator(~) is encountered
  630. """
  631. # supported operators list
  632. operators_list = ['+', '-', '++', '--', '!']
  633. tokens = list(node.get_tokens())
  634. # it can be either pre increment/decrement or any other operator from the list
  635. if tokens[0].spelling in operators_list:
  636. child = self.transform(next(node.get_children()))
  637. # (decl_ref) e.g.; int a = ++b; or simply ++b;
  638. if isinstance(child, str):
  639. if tokens[0].spelling == '+':
  640. return Symbol(child)
  641. if tokens[0].spelling == '-':
  642. return Mul(Symbol(child), -1)
  643. if tokens[0].spelling == '++':
  644. return PreIncrement(Symbol(child))
  645. if tokens[0].spelling == '--':
  646. return PreDecrement(Symbol(child))
  647. if tokens[0].spelling == '!':
  648. return Not(Symbol(child))
  649. # e.g.; int a = -1; or int b = -(1 + 2);
  650. else:
  651. if tokens[0].spelling == '+':
  652. return child
  653. if tokens[0].spelling == '-':
  654. return Mul(child, -1)
  655. if tokens[0].spelling == '!':
  656. return Not(sympify(bool(child)))
  657. # it can be either post increment/decrement
  658. # since variable name is obtained in token[0].spelling
  659. elif tokens[1].spelling in ['++', '--']:
  660. child = self.transform(next(node.get_children()))
  661. if tokens[1].spelling == '++':
  662. return PostIncrement(Symbol(child))
  663. if tokens[1].spelling == '--':
  664. return PostDecrement(Symbol(child))
  665. else:
  666. raise NotImplementedError("Dereferencing operator, "
  667. "Address operator and bitwise NOT operator "
  668. "have not been implemented yet!")
  669. def transform_binary_operator(self, node):
  670. """Transformation function for handling binary operators
  671. Returns
  672. =======
  673. binary_expression: Codegen AST node
  674. simplified binary expression represented as Codegen AST
  675. Raises
  676. ======
  677. NotImplementedError
  678. If a bitwise operator or
  679. unary operator(which is a child of any binary
  680. operator in Clang AST) is encountered
  681. """
  682. # get all the tokens of assignment
  683. # and store it in the tokens list
  684. tokens = list(node.get_tokens())
  685. # supported operators list
  686. operators_list = ['+', '-', '*', '/', '%','=',
  687. '>', '>=', '<', '<=', '==', '!=', '&&', '||', '+=', '-=',
  688. '*=', '/=', '%=']
  689. # this stack will contain variable content
  690. # and type of variable in the rhs
  691. combined_variables_stack = []
  692. # this stack will contain operators
  693. # to be processed in the rhs
  694. operators_stack = []
  695. # iterate through every token
  696. for token in tokens:
  697. # token is either '(', ')' or
  698. # any of the supported operators from the operator list
  699. if token.kind == cin.TokenKind.PUNCTUATION:
  700. # push '(' to the operators stack
  701. if token.spelling == '(':
  702. operators_stack.append('(')
  703. elif token.spelling == ')':
  704. # keep adding the expression to the
  705. # combined variables stack unless
  706. # '(' is found
  707. while (operators_stack
  708. and operators_stack[-1] != '('):
  709. if len(combined_variables_stack) < 2:
  710. raise NotImplementedError(
  711. "Unary operators as a part of "
  712. "binary operators is not "
  713. "supported yet!")
  714. rhs = combined_variables_stack.pop()
  715. lhs = combined_variables_stack.pop()
  716. operator = operators_stack.pop()
  717. combined_variables_stack.append(
  718. self.perform_operation(
  719. lhs, rhs, operator))
  720. # pop '('
  721. operators_stack.pop()
  722. # token is an operator (supported)
  723. elif token.spelling in operators_list:
  724. while (operators_stack
  725. and self.priority_of(token.spelling)
  726. <= self.priority_of(
  727. operators_stack[-1])):
  728. if len(combined_variables_stack) < 2:
  729. raise NotImplementedError(
  730. "Unary operators as a part of "
  731. "binary operators is not "
  732. "supported yet!")
  733. rhs = combined_variables_stack.pop()
  734. lhs = combined_variables_stack.pop()
  735. operator = operators_stack.pop()
  736. combined_variables_stack.append(
  737. self.perform_operation(
  738. lhs, rhs, operator))
  739. # push current operator
  740. operators_stack.append(token.spelling)
  741. # token is a bitwise operator
  742. elif token.spelling in ['&', '|', '^', '<<', '>>']:
  743. raise NotImplementedError(
  744. "Bitwise operator has not been "
  745. "implemented yet!")
  746. # token is a shorthand bitwise operator
  747. elif token.spelling in ['&=', '|=', '^=', '<<=',
  748. '>>=']:
  749. raise NotImplementedError(
  750. "Shorthand bitwise operator has not been "
  751. "implemented yet!")
  752. else:
  753. raise NotImplementedError(
  754. "Given token {} is not implemented yet!"
  755. .format(token.spelling))
  756. # token is an identifier(variable)
  757. elif token.kind == cin.TokenKind.IDENTIFIER:
  758. combined_variables_stack.append(
  759. [token.spelling, 'identifier'])
  760. # token is a literal
  761. elif token.kind == cin.TokenKind.LITERAL:
  762. combined_variables_stack.append(
  763. [token.spelling, 'literal'])
  764. # token is a keyword, either true or false
  765. elif (token.kind == cin.TokenKind.KEYWORD
  766. and token.spelling in ['true', 'false']):
  767. combined_variables_stack.append(
  768. [token.spelling, 'boolean'])
  769. else:
  770. raise NotImplementedError(
  771. "Given token {} is not implemented yet!"
  772. .format(token.spelling))
  773. # process remaining operators
  774. while operators_stack:
  775. if len(combined_variables_stack) < 2:
  776. raise NotImplementedError(
  777. "Unary operators as a part of "
  778. "binary operators is not "
  779. "supported yet!")
  780. rhs = combined_variables_stack.pop()
  781. lhs = combined_variables_stack.pop()
  782. operator = operators_stack.pop()
  783. combined_variables_stack.append(
  784. self.perform_operation(lhs, rhs, operator))
  785. return combined_variables_stack[-1][0]
  786. def priority_of(self, op):
  787. """To get the priority of given operator"""
  788. if op in ['=', '+=', '-=', '*=', '/=', '%=']:
  789. return 1
  790. if op in ['&&', '||']:
  791. return 2
  792. if op in ['<', '<=', '>', '>=', '==', '!=']:
  793. return 3
  794. if op in ['+', '-']:
  795. return 4
  796. if op in ['*', '/', '%']:
  797. return 5
  798. return 0
  799. def perform_operation(self, lhs, rhs, op):
  800. """Performs operation supported by the SymPy core
  801. Returns
  802. =======
  803. combined_variable: list
  804. contains variable content and type of variable
  805. """
  806. lhs_value = self.get_expr_for_operand(lhs)
  807. rhs_value = self.get_expr_for_operand(rhs)
  808. if op == '+':
  809. return [Add(lhs_value, rhs_value), 'expr']
  810. if op == '-':
  811. return [Add(lhs_value, -rhs_value), 'expr']
  812. if op == '*':
  813. return [Mul(lhs_value, rhs_value), 'expr']
  814. if op == '/':
  815. return [Mul(lhs_value, Pow(rhs_value, Integer(-1))), 'expr']
  816. if op == '%':
  817. return [Mod(lhs_value, rhs_value), 'expr']
  818. if op in ['<', '<=', '>', '>=', '==', '!=']:
  819. return [Rel(lhs_value, rhs_value, op), 'expr']
  820. if op == '&&':
  821. return [And(as_Boolean(lhs_value), as_Boolean(rhs_value)), 'expr']
  822. if op == '||':
  823. return [Or(as_Boolean(lhs_value), as_Boolean(rhs_value)), 'expr']
  824. if op == '=':
  825. return [Assignment(Variable(lhs_value), rhs_value), 'expr']
  826. if op in ['+=', '-=', '*=', '/=', '%=']:
  827. return [aug_assign(Variable(lhs_value), op[0], rhs_value), 'expr']
  828. def get_expr_for_operand(self, combined_variable):
  829. """Gives out SymPy Codegen AST node
  830. AST node returned is corresponding to
  831. combined variable passed.Combined variable contains
  832. variable content and type of variable
  833. """
  834. if combined_variable[1] == 'identifier':
  835. return Symbol(combined_variable[0])
  836. if combined_variable[1] == 'literal':
  837. if '.' in combined_variable[0]:
  838. return Float(float(combined_variable[0]))
  839. else:
  840. return Integer(int(combined_variable[0]))
  841. if combined_variable[1] == 'expr':
  842. return combined_variable[0]
  843. if combined_variable[1] == 'boolean':
  844. return true if combined_variable[0] == 'true' else false
  845. def transform_null_stmt(self, node):
  846. """Handles Null Statement and returns None"""
  847. return none
  848. def transform_while_stmt(self, node):
  849. """Transformation function for handling while statement
  850. Returns
  851. =======
  852. while statement : Codegen AST Node
  853. contains the while statement node having condition and
  854. statement block
  855. """
  856. children = node.get_children()
  857. condition = self.transform(next(children))
  858. statements = self.transform(next(children))
  859. if isinstance(statements, list):
  860. statement_block = CodeBlock(*statements)
  861. else:
  862. statement_block = CodeBlock(statements)
  863. return While(condition, statement_block)
  864. else:
  865. class CCodeConverter(): # type: ignore
  866. def __init__(self, *args, **kwargs):
  867. raise ImportError("Module not Installed")
  868. def parse_c(source):
  869. """Function for converting a C source code
  870. The function reads the source code present in the given file and parses it
  871. to give out SymPy Expressions
  872. Returns
  873. =======
  874. src : list
  875. List of Python expression strings
  876. """
  877. converter = CCodeConverter()
  878. if os.path.exists(source):
  879. src = converter.parse(source, flags = [])
  880. else:
  881. src = converter.parse_str(source, flags = [])
  882. return src