interpreterbase.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. # Copyright 2016-2017 The Meson development team
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. # Unless required by applicable law or agreed to in writing, software
  7. # distributed under the License is distributed on an "AS IS" BASIS,
  8. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. # See the License for the specific language governing permissions and
  10. # limitations under the License.
  11. # This class contains the basic functionality needed to run any interpreter
  12. # or an interpreter-based tool.
  13. from . import mparser, mesonlib, mlog
  14. from . import environment, dependencies
  15. import os, copy, re
  16. from functools import wraps
  17. # Decorators for method calls.
  18. def check_stringlist(a, msg='Arguments must be strings.'):
  19. if not isinstance(a, list):
  20. mlog.debug('Not a list:', str(a))
  21. raise InvalidArguments('Argument not a list.')
  22. if not all(isinstance(s, str) for s in a):
  23. mlog.debug('Element not a string:', str(a))
  24. raise InvalidArguments(msg)
  25. def noPosargs(f):
  26. @wraps(f)
  27. def wrapped(self, node, args, kwargs):
  28. if args:
  29. raise InvalidArguments('Function does not take positional arguments.')
  30. return f(self, node, args, kwargs)
  31. return wrapped
  32. def noKwargs(f):
  33. @wraps(f)
  34. def wrapped(self, node, args, kwargs):
  35. if kwargs:
  36. raise InvalidArguments('Function does not take keyword arguments.')
  37. return f(self, node, args, kwargs)
  38. return wrapped
  39. def stringArgs(f):
  40. @wraps(f)
  41. def wrapped(self, node, args, kwargs):
  42. assert(isinstance(args, list))
  43. check_stringlist(args)
  44. return f(self, node, args, kwargs)
  45. return wrapped
  46. class InterpreterException(mesonlib.MesonException):
  47. pass
  48. class InvalidCode(InterpreterException):
  49. pass
  50. class InvalidArguments(InterpreterException):
  51. pass
  52. class InterpreterObject:
  53. def __init__(self):
  54. self.methods = {}
  55. def method_call(self, method_name, args, kwargs):
  56. if method_name in self.methods:
  57. return self.methods[method_name](args, kwargs)
  58. raise InvalidCode('Unknown method "%s" in object.' % method_name)
  59. class MutableInterpreterObject(InterpreterObject):
  60. def __init__(self):
  61. super().__init__()
  62. class InterpreterBase:
  63. def __init__(self, source_root, subdir):
  64. self.source_root = source_root
  65. self.funcs = {}
  66. self.builtin = {}
  67. self.subdir = subdir
  68. self.variables = {}
  69. self.argument_depth = 0
  70. def load_root_meson_file(self):
  71. mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename)
  72. if not os.path.isfile(mesonfile):
  73. raise InvalidArguments('Missing Meson file in %s' % mesonfile)
  74. with open(mesonfile, encoding='utf8') as mf:
  75. code = mf.read()
  76. if code.isspace():
  77. raise InvalidCode('Builder file is empty.')
  78. assert(isinstance(code, str))
  79. try:
  80. self.ast = mparser.Parser(code, self.subdir).parse()
  81. except mesonlib.MesonException as me:
  82. me.file = environment.build_filename
  83. raise me
  84. def parse_project(self):
  85. """
  86. Parses project() and initializes languages, compilers etc. Do this
  87. early because we need this before we parse the rest of the AST.
  88. """
  89. self.evaluate_codeblock(self.ast, end=1)
  90. def sanity_check_ast(self):
  91. if not isinstance(self.ast, mparser.CodeBlockNode):
  92. raise InvalidCode('AST is of invalid type. Possibly a bug in the parser.')
  93. if not self.ast.lines:
  94. raise InvalidCode('No statements in code.')
  95. first = self.ast.lines[0]
  96. if not isinstance(first, mparser.FunctionNode) or first.func_name != 'project':
  97. raise InvalidCode('First statement must be a call to project')
  98. def run(self):
  99. # Evaluate everything after the first line, which is project() because
  100. # we already parsed that in self.parse_project()
  101. self.evaluate_codeblock(self.ast, start=1)
  102. def evaluate_codeblock(self, node, start=0, end=None):
  103. if node is None:
  104. return
  105. if not isinstance(node, mparser.CodeBlockNode):
  106. e = InvalidCode('Tried to execute a non-codeblock. Possibly a bug in the parser.')
  107. e.lineno = node.lineno
  108. e.colno = node.colno
  109. raise e
  110. statements = node.lines[start:end]
  111. i = 0
  112. while i < len(statements):
  113. cur = statements[i]
  114. try:
  115. self.evaluate_statement(cur)
  116. except Exception as e:
  117. if not(hasattr(e, 'lineno')):
  118. e.lineno = cur.lineno
  119. e.colno = cur.colno
  120. e.file = os.path.join(self.subdir, 'meson.build')
  121. raise e
  122. i += 1 # In THE FUTURE jump over blocks and stuff.
  123. def evaluate_statement(self, cur):
  124. if isinstance(cur, mparser.FunctionNode):
  125. return self.function_call(cur)
  126. elif isinstance(cur, mparser.AssignmentNode):
  127. return self.assignment(cur)
  128. elif isinstance(cur, mparser.MethodNode):
  129. return self.method_call(cur)
  130. elif isinstance(cur, mparser.StringNode):
  131. return cur.value
  132. elif isinstance(cur, mparser.BooleanNode):
  133. return cur.value
  134. elif isinstance(cur, mparser.IfClauseNode):
  135. return self.evaluate_if(cur)
  136. elif isinstance(cur, mparser.IdNode):
  137. return self.get_variable(cur.value)
  138. elif isinstance(cur, mparser.ComparisonNode):
  139. return self.evaluate_comparison(cur)
  140. elif isinstance(cur, mparser.ArrayNode):
  141. return self.evaluate_arraystatement(cur)
  142. elif isinstance(cur, mparser.NumberNode):
  143. return cur.value
  144. elif isinstance(cur, mparser.AndNode):
  145. return self.evaluate_andstatement(cur)
  146. elif isinstance(cur, mparser.OrNode):
  147. return self.evaluate_orstatement(cur)
  148. elif isinstance(cur, mparser.NotNode):
  149. return self.evaluate_notstatement(cur)
  150. elif isinstance(cur, mparser.UMinusNode):
  151. return self.evaluate_uminusstatement(cur)
  152. elif isinstance(cur, mparser.ArithmeticNode):
  153. return self.evaluate_arithmeticstatement(cur)
  154. elif isinstance(cur, mparser.ForeachClauseNode):
  155. return self.evaluate_foreach(cur)
  156. elif isinstance(cur, mparser.PlusAssignmentNode):
  157. return self.evaluate_plusassign(cur)
  158. elif isinstance(cur, mparser.IndexNode):
  159. return self.evaluate_indexing(cur)
  160. elif isinstance(cur, mparser.TernaryNode):
  161. return self.evaluate_ternary(cur)
  162. elif self.is_elementary_type(cur):
  163. return cur
  164. else:
  165. raise InvalidCode("Unknown statement.")
  166. def evaluate_arraystatement(self, cur):
  167. (arguments, kwargs) = self.reduce_arguments(cur.args)
  168. if len(kwargs) > 0:
  169. raise InvalidCode('Keyword arguments are invalid in array construction.')
  170. return arguments
  171. def evaluate_notstatement(self, cur):
  172. v = self.evaluate_statement(cur.value)
  173. if not isinstance(v, bool):
  174. raise InterpreterException('Argument to "not" is not a boolean.')
  175. return not v
  176. def evaluate_if(self, node):
  177. assert(isinstance(node, mparser.IfClauseNode))
  178. for i in node.ifs:
  179. result = self.evaluate_statement(i.condition)
  180. if not(isinstance(result, bool)):
  181. raise InvalidCode('If clause {!r} does not evaluate to true or false.'.format(result))
  182. if result:
  183. self.evaluate_codeblock(i.block)
  184. return
  185. if not isinstance(node.elseblock, mparser.EmptyNode):
  186. self.evaluate_codeblock(node.elseblock)
  187. def evaluate_comparison(self, node):
  188. val1 = self.evaluate_statement(node.left)
  189. val2 = self.evaluate_statement(node.right)
  190. if node.ctype == '==':
  191. return val1 == val2
  192. elif node.ctype == '!=':
  193. return val1 != val2
  194. elif not isinstance(val1, type(val2)):
  195. raise InterpreterException(
  196. 'Values of different types ({}, {}) cannot be compared using {}.'.format(type(val1).__name__,
  197. type(val2).__name__,
  198. node.ctype))
  199. elif not self.is_elementary_type(val1):
  200. raise InterpreterException('{} can only be compared for equality.'.format(node.left.value))
  201. elif not self.is_elementary_type(val2):
  202. raise InterpreterException('{} can only be compared for equality.'.format(node.right.value))
  203. elif node.ctype == '<':
  204. return val1 < val2
  205. elif node.ctype == '<=':
  206. return val1 <= val2
  207. elif node.ctype == '>':
  208. return val1 > val2
  209. elif node.ctype == '>=':
  210. return val1 >= val2
  211. else:
  212. raise InvalidCode('You broke my compare eval.')
  213. def evaluate_andstatement(self, cur):
  214. l = self.evaluate_statement(cur.left)
  215. if not isinstance(l, bool):
  216. raise InterpreterException('First argument to "and" is not a boolean.')
  217. if not l:
  218. return False
  219. r = self.evaluate_statement(cur.right)
  220. if not isinstance(r, bool):
  221. raise InterpreterException('Second argument to "and" is not a boolean.')
  222. return r
  223. def evaluate_orstatement(self, cur):
  224. l = self.evaluate_statement(cur.left)
  225. if not isinstance(l, bool):
  226. raise InterpreterException('First argument to "or" is not a boolean.')
  227. if l:
  228. return True
  229. r = self.evaluate_statement(cur.right)
  230. if not isinstance(r, bool):
  231. raise InterpreterException('Second argument to "or" is not a boolean.')
  232. return r
  233. def evaluate_uminusstatement(self, cur):
  234. v = self.evaluate_statement(cur.value)
  235. if not isinstance(v, int):
  236. raise InterpreterException('Argument to negation is not an integer.')
  237. return -v
  238. def evaluate_arithmeticstatement(self, cur):
  239. l = self.evaluate_statement(cur.left)
  240. r = self.evaluate_statement(cur.right)
  241. if cur.operation == 'add':
  242. try:
  243. return l + r
  244. except Exception as e:
  245. raise InvalidCode('Invalid use of addition: ' + str(e))
  246. elif cur.operation == 'sub':
  247. if not isinstance(l, int) or not isinstance(r, int):
  248. raise InvalidCode('Subtraction works only with integers.')
  249. return l - r
  250. elif cur.operation == 'mul':
  251. if not isinstance(l, int) or not isinstance(r, int):
  252. raise InvalidCode('Multiplication works only with integers.')
  253. return l * r
  254. elif cur.operation == 'div':
  255. if not isinstance(l, int) or not isinstance(r, int):
  256. raise InvalidCode('Division works only with integers.')
  257. return l // r
  258. elif cur.operation == 'mod':
  259. if not isinstance(l, int) or not isinstance(r, int):
  260. raise InvalidCode('Modulo works only with integers.')
  261. return l % r
  262. else:
  263. raise InvalidCode('You broke me.')
  264. def evaluate_ternary(self, node):
  265. assert(isinstance(node, mparser.TernaryNode))
  266. result = self.evaluate_statement(node.condition)
  267. if not isinstance(result, bool):
  268. raise InterpreterException('Ternary condition is not boolean.')
  269. if result:
  270. return self.evaluate_statement(node.trueblock)
  271. else:
  272. return self.evaluate_statement(node.falseblock)
  273. def evaluate_foreach(self, node):
  274. assert(isinstance(node, mparser.ForeachClauseNode))
  275. varname = node.varname.value
  276. items = self.evaluate_statement(node.items)
  277. if not isinstance(items, list):
  278. raise InvalidArguments('Items of foreach loop is not an array')
  279. for item in items:
  280. self.set_variable(varname, item)
  281. self.evaluate_codeblock(node.block)
  282. def evaluate_plusassign(self, node):
  283. assert(isinstance(node, mparser.PlusAssignmentNode))
  284. varname = node.var_name
  285. addition = self.evaluate_statement(node.value)
  286. # Remember that all variables are immutable. We must always create a
  287. # full new variable and then assign it.
  288. old_variable = self.get_variable(varname)
  289. if isinstance(old_variable, str):
  290. if not isinstance(addition, str):
  291. raise InvalidArguments('The += operator requires a string on the right hand side if the variable on the left is a string')
  292. new_value = old_variable + addition
  293. elif isinstance(old_variable, int):
  294. if not isinstance(addition, int):
  295. raise InvalidArguments('The += operator requires an int on the right hand side if the variable on the left is an int')
  296. new_value = old_variable + addition
  297. elif not isinstance(old_variable, list):
  298. raise InvalidArguments('The += operator currently only works with arrays, strings or ints ')
  299. # Add other data types here.
  300. else:
  301. if isinstance(addition, list):
  302. new_value = old_variable + addition
  303. else:
  304. new_value = old_variable + [addition]
  305. self.set_variable(varname, new_value)
  306. def evaluate_indexing(self, node):
  307. assert(isinstance(node, mparser.IndexNode))
  308. iobject = self.evaluate_statement(node.iobject)
  309. if not isinstance(iobject, list):
  310. raise InterpreterException('Tried to index a non-array object.')
  311. index = self.evaluate_statement(node.index)
  312. if not isinstance(index, int):
  313. raise InterpreterException('Index value is not an integer.')
  314. if index < -len(iobject) or index >= len(iobject):
  315. raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject)))
  316. return iobject[index]
  317. def function_call(self, node):
  318. func_name = node.func_name
  319. (posargs, kwargs) = self.reduce_arguments(node.args)
  320. if func_name in self.funcs:
  321. return self.funcs[func_name](node, self.flatten(posargs), kwargs)
  322. else:
  323. self.unknown_function_called(func_name)
  324. def method_call(self, node):
  325. invokable = node.source_object
  326. if isinstance(invokable, mparser.IdNode):
  327. object_name = invokable.value
  328. obj = self.get_variable(object_name)
  329. else:
  330. obj = self.evaluate_statement(invokable)
  331. method_name = node.name
  332. args = node.args
  333. if isinstance(obj, str):
  334. return self.string_method_call(obj, method_name, args)
  335. if isinstance(obj, bool):
  336. return self.bool_method_call(obj, method_name, args)
  337. if isinstance(obj, int):
  338. return self.int_method_call(obj, method_name, args)
  339. if isinstance(obj, list):
  340. return self.array_method_call(obj, method_name, self.reduce_arguments(args)[0])
  341. if isinstance(obj, mesonlib.File):
  342. raise InvalidArguments('File object "%s" is not callable.' % obj)
  343. if not isinstance(obj, InterpreterObject):
  344. raise InvalidArguments('Variable "%s" is not callable.' % object_name)
  345. (args, kwargs) = self.reduce_arguments(args)
  346. if method_name == 'extract_objects':
  347. self.validate_extraction(obj.held_object)
  348. return obj.method_call(method_name, self.flatten(args), kwargs)
  349. def bool_method_call(self, obj, method_name, args):
  350. (posargs, _) = self.reduce_arguments(args)
  351. if method_name == 'to_string':
  352. if not posargs:
  353. if obj:
  354. return 'true'
  355. else:
  356. return 'false'
  357. elif len(posargs) == 2 and isinstance(posargs[0], str) and isinstance(posargs[1], str):
  358. if obj:
  359. return posargs[0]
  360. else:
  361. return posargs[1]
  362. else:
  363. raise InterpreterException('bool.to_string() must have either no arguments or exactly two string arguments that signify what values to return for true and false.')
  364. elif method_name == 'to_int':
  365. if obj:
  366. return 1
  367. else:
  368. return 0
  369. else:
  370. raise InterpreterException('Unknown method "%s" for a boolean.' % method_name)
  371. def int_method_call(self, obj, method_name, args):
  372. (posargs, _) = self.reduce_arguments(args)
  373. if method_name == 'is_even':
  374. if not posargs:
  375. return obj % 2 == 0
  376. else:
  377. raise InterpreterException('int.is_even() must have no arguments.')
  378. elif method_name == 'is_odd':
  379. if not posargs:
  380. return obj % 2 != 0
  381. else:
  382. raise InterpreterException('int.is_odd() must have no arguments.')
  383. else:
  384. raise InterpreterException('Unknown method "%s" for an integer.' % method_name)
  385. def string_method_call(self, obj, method_name, args):
  386. (posargs, _) = self.reduce_arguments(args)
  387. if method_name == 'strip':
  388. return obj.strip()
  389. elif method_name == 'format':
  390. return self.format_string(obj, args)
  391. elif method_name == 'to_upper':
  392. return obj.upper()
  393. elif method_name == 'to_lower':
  394. return obj.lower()
  395. elif method_name == 'underscorify':
  396. return re.sub(r'[^a-zA-Z0-9]', '_', obj)
  397. elif method_name == 'split':
  398. if len(posargs) > 1:
  399. raise InterpreterException('Split() must have at most one argument.')
  400. elif len(posargs) == 1:
  401. s = posargs[0]
  402. if not isinstance(s, str):
  403. raise InterpreterException('Split() argument must be a string')
  404. return obj.split(s)
  405. else:
  406. return obj.split()
  407. elif method_name == 'startswith' or method_name == 'contains' or method_name == 'endswith':
  408. s = posargs[0]
  409. if not isinstance(s, str):
  410. raise InterpreterException('Argument must be a string.')
  411. if method_name == 'startswith':
  412. return obj.startswith(s)
  413. elif method_name == 'contains':
  414. return obj.find(s) >= 0
  415. return obj.endswith(s)
  416. elif method_name == 'to_int':
  417. try:
  418. return int(obj)
  419. except Exception:
  420. raise InterpreterException('String {!r} cannot be converted to int'.format(obj))
  421. elif method_name == 'join':
  422. if len(posargs) != 1:
  423. raise InterpreterException('Join() takes exactly one argument.')
  424. strlist = posargs[0]
  425. check_stringlist(strlist)
  426. return obj.join(strlist)
  427. elif method_name == 'version_compare':
  428. if len(posargs) != 1:
  429. raise InterpreterException('Version_compare() takes exactly one argument.')
  430. cmpr = posargs[0]
  431. if not isinstance(cmpr, str):
  432. raise InterpreterException('Version_compare() argument must be a string.')
  433. return mesonlib.version_compare(obj, cmpr)
  434. raise InterpreterException('Unknown method "%s" for a string.' % method_name)
  435. def unknown_function_called(self, func_name):
  436. raise InvalidCode('Unknown function "%s".' % func_name)
  437. def array_method_call(self, obj, method_name, args):
  438. if method_name == 'contains':
  439. return self.check_contains(obj, args)
  440. elif method_name == 'length':
  441. return len(obj)
  442. elif method_name == 'get':
  443. index = args[0]
  444. fallback = None
  445. if len(args) == 2:
  446. fallback = args[1]
  447. elif len(args) > 2:
  448. m = 'Array method \'get()\' only takes two arguments: the ' \
  449. 'index and an optional fallback value if the index is ' \
  450. 'out of range.'
  451. raise InvalidArguments(m)
  452. if not isinstance(index, int):
  453. raise InvalidArguments('Array index must be a number.')
  454. if index < -len(obj) or index >= len(obj):
  455. if fallback is None:
  456. m = 'Array index {!r} is out of bounds for array of size {!r}.'
  457. raise InvalidArguments(m.format(index, len(obj)))
  458. return fallback
  459. return obj[index]
  460. m = 'Arrays do not have a method called {!r}.'
  461. raise InterpreterException(m.format(method_name))
  462. def reduce_arguments(self, args):
  463. assert(isinstance(args, mparser.ArgumentNode))
  464. if args.incorrect_order():
  465. raise InvalidArguments('All keyword arguments must be after positional arguments.')
  466. self.argument_depth += 1
  467. reduced_pos = [self.evaluate_statement(arg) for arg in args.arguments]
  468. reduced_kw = {}
  469. for key in args.kwargs.keys():
  470. if not isinstance(key, str):
  471. raise InvalidArguments('Keyword argument name is not a string.')
  472. a = args.kwargs[key]
  473. reduced_kw[key] = self.evaluate_statement(a)
  474. self.argument_depth -= 1
  475. return reduced_pos, reduced_kw
  476. def flatten(self, args):
  477. if isinstance(args, mparser.StringNode):
  478. return args.value
  479. if isinstance(args, (int, str, mesonlib.File, InterpreterObject)):
  480. return args
  481. result = []
  482. for a in args:
  483. if isinstance(a, list):
  484. rest = self.flatten(a)
  485. result = result + rest
  486. elif isinstance(a, mparser.StringNode):
  487. result.append(a.value)
  488. else:
  489. result.append(a)
  490. return result
  491. def assignment(self, node):
  492. assert(isinstance(node, mparser.AssignmentNode))
  493. if self.argument_depth != 0:
  494. raise InvalidArguments('''Tried to assign values inside an argument list.
  495. To specify a keyword argument, use : instead of =.''')
  496. var_name = node.var_name
  497. if not isinstance(var_name, str):
  498. raise InvalidArguments('Tried to assign value to a non-variable.')
  499. value = self.evaluate_statement(node.value)
  500. if not self.is_assignable(value):
  501. raise InvalidCode('Tried to assign an invalid value to variable.')
  502. # For mutable objects we need to make a copy on assignment
  503. if isinstance(value, MutableInterpreterObject):
  504. value = copy.deepcopy(value)
  505. self.set_variable(var_name, value)
  506. return None
  507. def set_variable(self, varname, variable):
  508. if variable is None:
  509. raise InvalidCode('Can not assign None to variable.')
  510. if not isinstance(varname, str):
  511. raise InvalidCode('First argument to set_variable must be a string.')
  512. if not self.is_assignable(variable):
  513. raise InvalidCode('Assigned value not of assignable type.')
  514. if re.match('[_a-zA-Z][_0-9a-zA-Z]*$', varname) is None:
  515. raise InvalidCode('Invalid variable name: ' + varname)
  516. if varname in self.builtin:
  517. raise InvalidCode('Tried to overwrite internal variable "%s"' % varname)
  518. self.variables[varname] = variable
  519. def get_variable(self, varname):
  520. if varname in self.builtin:
  521. return self.builtin[varname]
  522. if varname in self.variables:
  523. return self.variables[varname]
  524. raise InvalidCode('Unknown variable "%s".' % varname)
  525. def is_assignable(self, value):
  526. return isinstance(value, (InterpreterObject, dependencies.Dependency,
  527. str, int, list, mesonlib.File))
  528. def func_build_target(self, node, args, kwargs):
  529. if 'target_type' not in kwargs:
  530. raise InterpreterException('Missing target_type keyword argument')
  531. target_type = kwargs.pop('target_type')
  532. if target_type == 'executable':
  533. return self.func_executable(node, args, kwargs)
  534. elif target_type == 'shared_library':
  535. return self.func_shared_lib(node, args, kwargs)
  536. elif target_type == 'static_library':
  537. return self.func_static_lib(node, args, kwargs)
  538. elif target_type == 'library':
  539. return self.func_library(node, args, kwargs)
  540. elif target_type == 'jar':
  541. return self.func_jar(node, args, kwargs)
  542. else:
  543. raise InterpreterException('Unknown target_type.')
  544. def func_set_variable(self, node, args, kwargs):
  545. if len(args) != 2:
  546. raise InvalidCode('Set_variable takes two arguments.')
  547. varname = args[0]
  548. value = args[1]
  549. self.set_variable(varname, value)
  550. # @noKwargs
  551. def func_get_variable(self, node, args, kwargs):
  552. if len(args) < 1 or len(args) > 2:
  553. raise InvalidCode('Get_variable takes one or two arguments.')
  554. varname = args[0]
  555. if not isinstance(varname, str):
  556. raise InterpreterException('First argument must be a string.')
  557. try:
  558. return self.variables[varname]
  559. except KeyError:
  560. pass
  561. if len(args) == 2:
  562. return args[1]
  563. raise InterpreterException('Tried to get unknown variable "%s".' % varname)
  564. @stringArgs
  565. @noKwargs
  566. def func_is_variable(self, node, args, kwargs):
  567. if len(args) != 1:
  568. raise InvalidCode('Is_variable takes two arguments.')
  569. varname = args[0]
  570. return varname in self.variables
  571. def is_elementary_type(self, v):
  572. return isinstance(v, (int, float, str, bool, list))