CodeTools::AST << {
# A Ruby code generation visitor ToRuby < ::Myco::Object { var lines: [""] # The currently held string of generated ruby code to_s: lines.join("\n") # Add to the current line the given list of Strings # and/or objects that respond to :to_ruby add: |*strings| strings.each |string| { string.is_a?(String) &? lines.last.concat(string.dup) ?? string.to_ruby(self) } # Start a new line, optionally adding a list of Strings # and/or objects that respond to :to_ruby line: |*strings| lines.push(indents.last.dup); add(*strings) # Add the given items to the current line, # using the given separator between items. # If a block is given, it is called on # each iteration with the item instead. # Returns true if any items were written, else false. list: |items, separator="", auto_lines:false, &block| { items.any? && ( items = items.dup last_item = items.pop use_lines = items.any? && auto_lines use_lines && self.push_indent items.each |item| { use_lines && self.line block &? block.call(item) ?? add(item) add(separator) } use_lines && self.line block &? block.call(last_item) ?? add(last_item) use_lines && self.pop_indent use_lines && self.line ) } # Stack of indent levels (as strings to be prefixed) var indents: [""] var indent_amount: 2 pop_indent: indents.pop push_indent: |amount=indent_amount| indents.push(indents.last + " " * amount) # Stack of every AST node in hierarchy that can hold local variables var var_scopes: [] var_scope: var_scopes.last with_nested_var_scope: |node, &block| { var_scopes.push(node) block.call var_scopes.pop } var_scope_declare_local: |name| var_scope.variables[name] = true var_scope_declare_locals: |*names| { names.each |name| { var_scope_declare_local(name) } } var_scope_has_local?: |name| { !!var_scopes.reverse.detect |scope| { scope.variables.has_key?(name) } } # Helper methods # Return false if the given indent might be problematic in generated Ruby # TODO: make comprehensive easy_ident?: |sym| { str = sym.to_s (str =~ Regexp.new("^[a-z_][A-Za-z_]*$")) && (![:if,:unless,:and,:or,:def,:do,:class, :module,:end,:break,:begin,:rescue,:ensure].include?(sym)) } } # Allow any Node to be visited with the to_ruby meme Node << { to_ruby_code: { g = ToRuby.new to_ruby(g) g.to_s } } Self << { to_ruby: |g| g.add("self") } NullLiteral << { to_ruby: |g| g.add("nil") } VoidLiteral << { to_ruby: |g| g.add("::Myco::Void") } TrueLiteral << { to_ruby: |g| g.add("true") } FalseLiteral << { to_ruby: |g| g.add("false") } NumericLiteral << { to_ruby: |g| g.add(self.value.inspect) } StringLiteral << { to_ruby: |g| g.add(self.value.inspect) } SymbolLiteral << { to_ruby: |g| g.add(self.value.inspect) } StringCompose << { to_ruby: |g| { g.add('"') self.body.each_slice(2) |string, to_string| { g.add(string.value.to_s.inspect.slice(Range.new(1, -2))) to_string && (g.add('#{'); g.add(to_string); g.add('}')) } g.add('"') } } SymbolCompose << { to_ruby: |g| { g.add(':"') self.body.each_slice(2) |string, to_string| { g.add(string.value.to_s.inspect.slice(Range.new(1, -2))) to_string && (g.add('#{'); g.add(to_string); g.add('}')) } g.add('"') } } ArrayAssembly << { to_ruby: |g| g.add("["); g.list(self.body, ",", auto_lines:true); g.add("]") } KeywordAssembly << { to_ruby: |g| { g.add("{") g.list(self.body.each_slice(2).to_a, ",", auto_lines:true) |pair| { g.add(pair.first, " => ", pair.last) } g.add("}") } } ConstantAccess << { to_ruby: |g| { name_list = self.names.map(&:to_sym) first_name = name_list.shift self.toplevel &? ( first_name case( :Myco, &{ g.add("::Myco") }, :Ruby, &{ g.add("::Object") }, :Rubinius, &{ g.add("::Rubinius") }, &{ g.add("::Myco::"first_name"") } ) ) ?? ( g.add("::Myco.find_constant("first_name.inspect")") ) name_list.each |name| { g.add("::"name"") } } } ConstantAssignment << { to_ruby: |g| { name_list = self.constant.names.map(&:to_sym) last_name = name_list.pop first_name = name_list.any? && name_list.shift self.constant.toplevel &? ( first_name &? ( first_name case( :Myco, &{ g.add("::Myco") }, :Ruby, &{ g.add("::Object") }, :Rubinius, &{ g.add("::Rubinius") }, &{ g.add("::Myco::"first_name"") } ) ) ?? ( g.add("::Myco") ) ) ?? ( first_name &? ( g.add("::Myco.find_constant("first_name.inspect")") ) ?? ( g.add("::Myco.cscope.for_method_definition") ) ) name_list.each |name| { g.add("::"name"") } g.add("::"last_name" = ") g.add(self.value) } } ConstantDefine << { to_ruby: |g| { g.add(implementation) g.line(".tap { |__c__| __c__.__name__ = ") g.add(self.constant.names.last.inspect) g.add(" }") } } DeclareExtension << { to_ruby: |g| { g.add(self.constant); g.add(".component_eval {") g.add(self.body); g.add("}") } } DeclaredScope << { to_ruby: |g| g.with_nested_var_scope(self) { g.add(self.body) } } Script << { to_ruby: |g| g.add(self.body) } DeclareFile << { to_ruby: |g| g.line; implementation.to_ruby(g); g.line } DeclareObject << { to_ruby: |g| { g.add("::Myco::Component.new("); g.add(self.types); g.add(", ::Myco.cscope.for_method_definition, __FILE__, __LINE__)") g.line(".tap { |__c__| __c__.__last__ = __c__.component_eval {"); g.add(scope_implementation); g.add("}}") self.create && g.add(".instance") } } DeclareString << { to_ruby: |g| g.add(implementation) } DeclareCategory << { to_ruby: |g| { g.add("__category__("self.name.inspect")") g.add(".component_eval {"); g.add(self.body); g.add("}") } } DeclareMeme << { to_ruby: |g| { name = self.decorations.last name.node_type == :symbol &? ( g.add("declare_meme(") g.list([ self.effective_name self.effective_decorations self.effective_body ], ",", auto_lines: true) g.add(")") ) ?? ( g.add(ConstantAssignment.new( line: self.line constant: self.effective_name value: self.effective_body.block.body )) ) } } RequiredParameter << { to_ruby: |g| g.add(""self.name"") } OptionalParameter << { to_ruby: |g| g.add(""self.name"="); g.add(self.value) } RestParameter << { to_ruby: |g| g.add("*"self.name"") } KeywordRequiredParameter << { to_ruby: |g| g.add(""self.name":") } KeywordOptionalParameter << { to_ruby: |g| g.add(""self.name":"); g.add(self.value) } KeywordRestParameter << { to_ruby: |g| g.add("**"self.name"") } BlockParameter << { to_ruby: |g| g.add("&"self.name"") } ParameterAssembly << { to_ruby: |g| g.add("|"); g.list(self.all_params, ", "); g.add("|") } Sequence << { to_ruby: |g| g.add("("); g.list(self.array, "", auto_lines:true); g.add(")") } Invoke << { to_ruby: |g| g.add(implementation) } InvokeMethod << { to_ruby: |g| { list = self.arguments &? self.arguments.body.dup ?? [] self.arguments.block.is_a?(BlockArgument) && # TODO: receive block as part of arguments list list.push(self.arguments.block) g.add(self.receiver) g.easy_ident?(self.name) &? ( g.add("."self.name"") ) ?? ( g.add(".__send__") list.unshift(SymbolLiteral.new(line:self.line, value:self.name)) ) list.any? && ( g.add("(") g.list(list, ",", auto_lines:true) g.add(")") ) self.arguments.block.is_a?(Block) && ( g.add(" {"); g.add(self.arguments.block); g.add("}") ) } } LocalVariableAssignment << { to_ruby: |g| { g.var_scope_declare_local(self.name) g.var_scope.variables[self.name] = true g.add(self.name.to_s); g.add(" = "); g.add(self.value) } } LocalVariableAccessAmbiguous << { to_ruby: |g| { g.var_scope_has_local?(self.name) &? ( g.add(self.name.to_s) ) ?? ( g.add("self") g.easy_ident?(self.name) &? ( g.add("."self.name"") ) ?? ( g.add(".__send__("self.name.inspect")") ) ) } } SplatValue << { to_ruby: |g| { self.value.is_a?(self.class) &? ( self.value.to_ruby(g) ) ?? ( g.add("*") self.value.to_ruby(g) ) } } BlockArgument << { to_ruby: |g| g.add("&"); g.add(self.body) } Block << { to_ruby: |g| { g.with_nested_var_scope(self) { g.var_scope_declare_locals(*self.effective_parameters.names) g.add(" "); g.add(self.effective_parameters); g.add(" "); g.add(self.body) } } } BlockLiteral << { to_ruby: |g| g.add(implementation) } Quest << { to_ruby: |g| { # TODO: make this output more verbose, but more correct, and without duping the questable associated_questable = self.questable.dup associated_questable.receiver = self.receiver g.add("(") g.add("Rubinius::Type.object_respond_to?(") g.add(self.receiver) g.add(", "self.questable.name.inspect").false?") g.add(" ? ::Myco::Void : ") g.add(associated_questable) g.add(")") } } BranchOperator << { to_ruby: |g| { g.add('::Myco.branch_op(') g.add(self.type.inspect); g.add(', ') g.add(self.left); g.add(') {') g.add(self.right); g.add('}') } } PipeOperator << { to_ruby: |g| g.add(implementation) }
}