c_parser.py 37 KB

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