PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/mirah/ast/intrinsics.rb

https://github.com/jackowayed/mirah
Ruby | 470 lines | 397 code | 53 blank | 20 comment | 25 complexity | aa62744b6d5c3b26f3df190e79e5783b MD5 | raw file
  1. require 'fileutils'
  2. module Duby::AST
  3. class Unquote < Node
  4. child :value
  5. def infer(typer)
  6. raise "Unquote used outside of macro"
  7. end
  8. def _dump(depth)
  9. vals = Unquote.__extracted
  10. index = vals.size
  11. # Make sure the scope is saved
  12. if Scoped === value
  13. value.scope
  14. scoped_value = value
  15. else
  16. scoped_value = ScopedBody.new(value.parent, value.position) {[value]}
  17. scoped_value.static_scope = scoped_value.scope.static_scope
  18. end
  19. vals << self.value
  20. Marshal.dump([position, index])
  21. end
  22. def self._load(str)
  23. if str =~ /^\d+$/
  24. # This just returns the exact node passed in.
  25. index = str.to_i
  26. Unquote.__injected[index].dup
  27. else
  28. position, index = Marshal.load(str)
  29. holder = UnquotedValue.new(nil, position)
  30. holder << Unquote.__injected[index].dup
  31. holder
  32. end
  33. end
  34. def self.__extracted
  35. Thread.current[:'Duby::AST::Unqote.extracted']
  36. end
  37. def self.__extracted=(value)
  38. Thread.current[:'Duby::AST::Unqote.extracted'] = value
  39. end
  40. def self.__injected
  41. Thread.current[:'Duby::AST::Unqote.injected']
  42. end
  43. def self.__injected=(value)
  44. Thread.current[:'Duby::AST::Unqote.injected'] = value
  45. end
  46. def self.extract_values
  47. values = self.__extracted = []
  48. begin
  49. yield
  50. return values
  51. ensure
  52. self.__extracted = nil
  53. end
  54. end
  55. def self.inject_values(values)
  56. self.__injected = values
  57. begin
  58. yield
  59. ensure
  60. self.__injected = nil
  61. end
  62. end
  63. end
  64. class UnquotedValue < Node
  65. java_import 'java.lang.Character'
  66. child :value
  67. def name
  68. case value
  69. when Duby::AST::String
  70. value.literal
  71. when ::String
  72. value
  73. when Named
  74. value.name
  75. else
  76. raise "Bad unquote value #{value}"
  77. end
  78. end
  79. def node
  80. case value
  81. when Node
  82. value
  83. when ::String
  84. c = value[0]
  85. if c == ?@
  86. return Field.new(nil, position, value[1, value.length])
  87. elsif Character.isUpperCase(c)
  88. return Constant.new(nil, position, value)
  89. else
  90. return Local.new(nil, position, value)
  91. end
  92. else
  93. raise "Bad unquote value"
  94. end
  95. end
  96. def f_arg
  97. case value
  98. when Arguments, Argument
  99. value
  100. when Named
  101. RequiredArgument.new(nil, position, value.name)
  102. when ::String
  103. RequiredArgument.new(nil, position, value)
  104. else
  105. raise "Bad unquote value"
  106. end
  107. end
  108. end
  109. class UnquoteAssign < Node
  110. child :name
  111. child :value
  112. def infer(typer)
  113. raise "UnquoteAssign used outside of macro"
  114. end
  115. def _dump(depth)
  116. vals = Unquote.__extracted
  117. index = vals.size
  118. vals << self.name
  119. Marshal.dump([position, index, self.value])
  120. end
  121. def self._load(str)
  122. position, index, value = Marshal.load(str)
  123. holder = UnquotedValueAssign.new(nil, position)
  124. holder << Unquote.__injected[index].dup
  125. holder << value
  126. holder
  127. end
  128. end
  129. class UnquotedValueAssign < UnquotedValue
  130. child :name_node
  131. child :value
  132. def name
  133. raise "Bad unquote value #{value}"
  134. end
  135. def node
  136. klass = LocalAssignment
  137. if Field === name_node
  138. name = name_node.name
  139. klass = FieldAssignment
  140. # TODO support AttrAssign
  141. elsif Named === name_node
  142. name = name_node.name
  143. elsif String === name_node
  144. name = name_node.literal
  145. elsif ::String === name_node
  146. name = name
  147. else
  148. raise "Bad unquote value"
  149. end
  150. if name[0] == ?@
  151. name = name[1, name.length]
  152. klass = FieldAssignment
  153. end
  154. n = klass.new(nil, position, name)
  155. n << value
  156. n.validate_children
  157. return n
  158. end
  159. def f_arg
  160. raise "Bad unquote value"
  161. end
  162. end
  163. class MacroDefinition < Node
  164. include Named
  165. include Scoped
  166. child :arguments
  167. child :body
  168. attr_accessor :proxy
  169. def self.new(*args, &block)
  170. real_node = super
  171. real_node.proxy = NodeProxy.new(real_node)
  172. end
  173. def initialize(parent, line_number, name, &block)
  174. super(parent, line_number, &block)
  175. @name = name
  176. end
  177. def infer(typer)
  178. resolve_if(typer) do
  179. self_type = scope.static_scope.self_type
  180. extension_name = "%s$%s" % [self_type.name,
  181. typer.transformer.tmp("Extension%s")]
  182. klass = build_and_load_extension(self_type,
  183. extension_name,
  184. typer.transformer.state)
  185. # restore the self type since we're sharing a type factory
  186. typer.known_types['self'] = self_type
  187. arg_types = argument_types
  188. macro = self_type.add_compiled_macro(klass, name, arg_types)
  189. if arguments[-1].kind_of?(BlockArgument) && arguments[-1].optional?
  190. arg_types.pop
  191. self_type.add_method(name, arg_types, macro)
  192. end
  193. proxy.__inline__(Noop.new(parent, position))
  194. proxy.infer(typer)
  195. end
  196. end
  197. def argument_types
  198. arguments.map do |arg|
  199. if arg.kind_of?(BlockArgument)
  200. TypeReference::BlockType
  201. else
  202. # TODO support typed args. Also there should be a way
  203. # to accept any AST node.
  204. Duby::JVM::Types::Object
  205. end
  206. end
  207. end
  208. def signature
  209. args = argument_types
  210. if args.size > 0 && args[-1].block?
  211. args[-1] = BiteScript::ASM::Type.getObjectType('duby.lang.compiler.Block')
  212. end
  213. [nil] + args
  214. end
  215. def build_and_load_extension(parent, name, state)
  216. transformer = Duby::Transform::Transformer.new(state)
  217. transformer.filename = name.gsub(".", "/")
  218. orig_factory = Duby::AST.type_factory
  219. new_factory = orig_factory.dup
  220. Duby::AST.type_factory = new_factory
  221. ast = build_ast(name, parent, transformer)
  222. puts ast.inspect if state.verbose
  223. classes = compile_ast(name, ast, transformer)
  224. loader = DubyClassLoader.new(
  225. JRuby.runtime.jruby_class_loader, classes)
  226. klass = loader.loadClass(name, true)
  227. annotate(parent, name)
  228. Duby::AST.type_factory = orig_factory
  229. klass
  230. end
  231. def annotate(type, class_name)
  232. node = type.unmeta.node
  233. if node
  234. extension = node.annotation('duby.anno.Extensions')
  235. extension ||= begin
  236. node.annotations << Annotation.new(
  237. nil, nil, BiteScript::ASM::Type.getObjectType('duby/anno/Extensions'))
  238. node.annotations[-1].runtime = false
  239. node.annotations[-1]
  240. end
  241. extension['macros'] ||= []
  242. macro = Annotation.new(nil, nil,
  243. BiteScript::ASM::Type.getObjectType('duby/anno/Macro'))
  244. macro['name'] = name
  245. macro['signature'] = BiteScript::Signature.signature(*signature)
  246. macro['class'] = class_name
  247. extension['macros'] << macro
  248. # TODO deal with optional blocks.
  249. else
  250. puts "Warning: No ClassDefinition for #{type.name}. Macros can't be loaded from disk."
  251. end
  252. end
  253. def compile_ast(name, ast, transformer)
  254. typer = Duby::Typer::JVM.new(transformer)
  255. typer.infer(ast)
  256. typer.resolve(true)
  257. compiler = Duby::Compiler::JVM.new
  258. ast.compile(compiler, false)
  259. class_map = {}
  260. compiler.generate do |outfile, builder|
  261. outfile = "#{transformer.destination}#{outfile}"
  262. FileUtils.mkdir_p(File.dirname(outfile))
  263. File.open(outfile, 'wb') do |f|
  264. bytes = builder.generate
  265. name = builder.class_name.gsub(/\//, '.')
  266. class_map[name] = bytes
  267. f.write(bytes)
  268. end
  269. end
  270. class_map
  271. end
  272. def build_ast(name, parent, transformer)
  273. # TODO should use a new type factory too.
  274. ast = Duby::AST.parse_ruby("begin;end")
  275. ast = transformer.transform(ast, nil)
  276. # Start building the extension class
  277. extension = transformer.define_class(position, name)
  278. #extension.superclass = Duby::AST.type(nil, 'duby.lang.compiler.Macro')
  279. extension.implements(Duby::AST.type(nil, 'duby.lang.compiler.Macro'))
  280. extension.static_scope.import('duby.lang.compiler.Node', 'Node')
  281. # The constructor just saves the state
  282. extension.define_constructor(
  283. position,
  284. ['mirah', Duby::AST.type(nil, 'duby.lang.compiler.Compiler')],
  285. ['call', Duby::AST.type(nil, 'duby.lang.compiler.Call')]) do |c|
  286. transformer.eval("@mirah = mirah;@call = call", '-', c, 'mirah', 'call')
  287. end
  288. node_type = Duby::AST.type(nil, 'duby.lang.compiler.Node')
  289. # expand() parses the arguments out of call and then passes them off to
  290. # _expand
  291. expand = extension.define_method(
  292. position, 'expand', node_type)
  293. args = []
  294. arguments.each_with_index do |arg, i|
  295. # TODO optional args
  296. args << if arg.kind_of?(BlockArgument)
  297. "@call.block"
  298. else
  299. "Node(args.get(#{i}))"
  300. end
  301. end
  302. expand.body = transformer.eval(<<-end)
  303. args = @call.arguments
  304. _expand(#{args.join(', ')})
  305. end
  306. actual_args = arguments.map do |arg|
  307. type = if arg.kind_of?(BlockArgument)
  308. Duby::AST.type(nil, 'duby.lang.compiler.Block')
  309. else
  310. node_type
  311. end
  312. [arg.name, type, arg.position]
  313. end
  314. m = extension.define_method(position, '_expand', node_type, *actual_args)
  315. m.body = self.body
  316. ast.body = extension
  317. ast
  318. end
  319. end
  320. defmacro('defmacro') do |duby, fcall, parent|
  321. macro = fcall.parameters[0]
  322. block_arg = nil
  323. args = macro.parameters if macro.respond_to?(:parameters)
  324. body = if macro.respond_to?(:block) && macro.block
  325. macro.block
  326. else
  327. fcall.block
  328. end
  329. body = body.body if body
  330. MacroDefinition.new(parent, fcall.position, macro.name) do |mdef|
  331. # TODO optional args?
  332. args = if args
  333. args.map do |arg|
  334. case arg
  335. when LocalAssignment
  336. OptionalArgument.new(mdef, arg.position, arg.name) do |optarg|
  337. # TODO check that they actually passed nil as the value
  338. Null.new(parent, arg.value_node.position)
  339. end
  340. when FunctionalCall
  341. RequiredArgument.new(mdef, arg.position, arg.name)
  342. when BlockPass
  343. farg = BlockArgument.new(mdef, arg.position, arg.value.name)
  344. farg.optional = true if LocalAssignment === arg.value
  345. farg
  346. else
  347. raise "Unsupported argument #{arg}"
  348. end
  349. end
  350. else
  351. []
  352. end
  353. body.parent = mdef if body
  354. [args, body]
  355. end
  356. end
  357. defmacro('macro') do |transformer, fcall, parent|
  358. # Alternate macro syntax.
  359. # macro def foo(...);...;end
  360. # This one supports special names like []=,
  361. # but you can't use optional blocks.
  362. method = fcall.parameters[0]
  363. macro = MacroDefinition.new(parent, fcall.position, method.name)
  364. macro.arguments = method.arguments.args || []
  365. macro.body = method.body
  366. macro
  367. end
  368. defmacro('abstract') do |transformer, fcall, parent|
  369. class_or_method = fcall.parameters[0]
  370. class_or_method.abstract = true
  371. class_or_method
  372. end
  373. defmacro('puts') do |transformer, fcall, parent|
  374. Call.new(parent, fcall.position, "println") do |x|
  375. args = fcall.parameters
  376. args.each do |arg|
  377. arg.parent = x
  378. end
  379. [
  380. Call.new(x, fcall.position, "out") do |y|
  381. [
  382. Constant.new(y, fcall.position, "System"),
  383. []
  384. ]
  385. end,
  386. args,
  387. nil
  388. ]
  389. end
  390. end
  391. defmacro('print') do |transformer, fcall, parent|
  392. Call.new(parent, fcall.position, "print") do |x|
  393. args = fcall.parameters
  394. args.each do |arg|
  395. arg.parent = x
  396. end
  397. [
  398. Call.new(x, fcall.position, "out") do |y|
  399. [
  400. Constant.new(y, fcall.position, "System"),
  401. []
  402. ]
  403. end,
  404. args,
  405. nil
  406. ]
  407. end
  408. end
  409. class InlineCode
  410. def initialize(&block)
  411. @block = block
  412. end
  413. def inline(transformer, call)
  414. @block.call(transformer, call)
  415. end
  416. end
  417. end