Class: Riml::AST_Rewriter
- Inherits:
-
Object
- Object
- Riml::AST_Rewriter
- Includes:
- Constants
- Defined in:
- lib/ast_rewriter.rb
Direct Known Subclasses
CallToExplicitCall, ClassDefinitionToFunctions, ClassDefinitionToFunctions::DefNodeToPrivateFunction, ClassDefinitionToFunctions::ExtendObjectWithMethods, ClassDefinitionToFunctions::InitializeSuperToObjectExtension, ClassDefinitionToFunctions::InsertInitializeMethod, ClassDefinitionToFunctions::PrivateFunctionCallToPassObjExplicitly, ClassDefinitionToFunctions::RegisterPrivateFunctions, ClassDefinitionToFunctions::SelfToDictName, ClassDefinitionToFunctions::SelfToObjArgumentInPrivateFunction, ClassDefinitionToFunctions::SuperToSuperclassFunction, DefaultParamToIfNode, DeserializeVarAssignment, ObjectInstantiationToCall, RegisterDefinedClasses, StrictEqualsComparisonOperator, TopLevelDefMethodToDef, VarEqualsComparisonOperator
Defined Under Namespace
Classes: CallToExplicitCall, ClassDefinitionToFunctions, DefaultParamToIfNode, DeserializeVarAssignment, ObjectInstantiationToCall, RegisterDefinedClasses, StrictEqualsComparisonOperator, TopLevelDefMethodToDef, VarEqualsComparisonOperator
Constant Summary
Constants included from Constants
Constants::BUILTIN_COMMANDS, Constants::BUILTIN_FUNCTIONS, Constants::COMPARISON_OPERATORS, Constants::DEFINE_KEYWORDS, Constants::END_KEYWORDS, Constants::IGNORECASE_CAPABLE_OPERATORS, Constants::KEYWORDS, Constants::REGISTERS, Constants::RIML_COMMANDS, Constants::RIML_END_KEYWORDS, Constants::RIML_KEYWORDS, Constants::SPECIAL_VARIABLE_PREFIXES, Constants::SPLAT_LITERAL, Constants::VIML_COMMANDS, Constants::VIML_END_KEYWORDS, Constants::VIML_KEYWORDS
Instance Attribute Summary collapse
-
#ast ⇒ Object
Returns the value of attribute ast.
-
#classes ⇒ Object
readonly
Returns the value of attribute classes.
-
#rewritten_included_and_sourced_files ⇒ Object
readonly
Returns the value of attribute rewritten_included_and_sourced_files.
Instance Method Summary collapse
-
#add_SID_function! ⇒ Object
:h <SID>.
-
#add_SID_function?(filename) ⇒ Boolean
Add SID function if this is the main file and it has defined classes, or if it included other files and any one of those other files defined classes.
- #do_establish_parents(node) ⇒ Object
- #do_rewrite_on_match(node) ⇒ Object
- #establish_parents(node) ⇒ Object (also: #reestablish_parents)
-
#initialize(ast = nil, classes = nil) ⇒ AST_Rewriter
constructor
A new instance of AST_Rewriter.
- #recursive? ⇒ Boolean
- #rewrite(filename = nil, included = false) ⇒ Object
-
#rewrite_included_and_sourced_files!(filename) ⇒ Object
We need to rewrite the included/sourced files before anything else.
- #rewrite_on_match(node = ast) ⇒ Object
Constructor Details
#initialize(ast = nil, classes = nil) ⇒ AST_Rewriter
Returns a new instance of AST_Rewriter.
12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/ast_rewriter.rb', line 12 def initialize(ast = nil, classes = nil) @ast = ast @classes = classes || ClassMap.new # Keeps track of filenames with their rewritten ASTs, to prevent rewriting # the same AST more than once. @rewritten_included_and_sourced_files = {} # Keeps track of which filenames included/sourced which. # ex: { nil => ["main.riml"], "main.riml" => ["lib1.riml", "lib2.riml"], # "lib1.riml" => [], "lib2.riml" => [] } @included_and_sourced_file_refs = Hash.new { |h, k| h[k] = [] } end |
Instance Attribute Details
#ast ⇒ Object
Returns the value of attribute ast.
9 10 11 |
# File 'lib/ast_rewriter.rb', line 9 def ast @ast end |
#classes ⇒ Object (readonly)
Returns the value of attribute classes.
10 11 12 |
# File 'lib/ast_rewriter.rb', line 10 def classes @classes end |
#rewritten_included_and_sourced_files ⇒ Object (readonly)
Returns the value of attribute rewritten_included_and_sourced_files.
10 11 12 |
# File 'lib/ast_rewriter.rb', line 10 def rewritten_included_and_sourced_files @rewritten_included_and_sourced_files end |
Instance Method Details
#add_SID_function! ⇒ Object
:h <SID>
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/ast_rewriter.rb', line 126 def add_SID_function! fchild = ast.nodes.first return false if DefNode === fchild && fchild.name == 'SID' && fchild.scope_modifier == 's:' fn = DefNode.new('!', nil, 's:', 'SID', [], nil, Nodes.new([ ReturnNode.new(CallNode.new(nil, 'matchstr', [ CallNode.new(nil, 'expand', [StringNode.new('<sfile>', :s)]), StringNode.new('<SNR>\zs\d\+\ze_SID$', :s) ] )) ]) ) fn.parent = ast.nodes establish_parents(fn) ast.nodes.unshift fn end |
#add_SID_function?(filename) ⇒ Boolean
Add SID function if this is the main file and it has defined classes, or if it included other files and any one of those other files defined classes.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/ast_rewriter.rb', line 109 def add_SID_function?(filename) return true if ast.children.grep(ClassDefinitionNode).any? included_files = @included_and_sourced_file_refs[filename] while included_files.any? incs = [] included_files.each do |included_file| if (ast = rewritten_included_and_sourced_files[included_file]) return true if ast.children.grep(ClassDefinitionNode).any? end incs.concat @included_and_sourced_file_refs[included_file] end included_files = incs end false end |
#do_establish_parents(node) ⇒ Object
56 57 58 59 60 |
# File 'lib/ast_rewriter.rb', line 56 def do_establish_parents(node) node.children.each do |child| child.parent_node = node if child.respond_to?(:parent_node=) end if node.respond_to?(:children) end |
#do_rewrite_on_match(node) ⇒ Object
66 67 68 |
# File 'lib/ast_rewriter.rb', line 66 def do_rewrite_on_match(node) replace node if match?(node) end |
#establish_parents(node) ⇒ Object Also known as: reestablish_parents
51 52 53 |
# File 'lib/ast_rewriter.rb', line 51 def establish_parents(node) Walker.walk_node(node, method(:do_establish_parents)) end |
#recursive? ⇒ Boolean
103 104 105 |
# File 'lib/ast_rewriter.rb', line 103 def recursive? true end |
#rewrite(filename = nil, included = false) ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/ast_rewriter.rb', line 24 def rewrite(filename = nil, included = false) if filename && (rewritten_ast = rewritten_included_and_sourced_files[filename]) return rewritten_ast end establish_parents(ast) class_registry = RegisterDefinedClasses.new(ast, classes) class_registry.rewrite_on_match rewrite_included_and_sourced_files!(filename) if filename && !included && add_SID_function?(filename) add_SID_function! end rewriters = [ StrictEqualsComparisonOperator.new(ast, classes), VarEqualsComparisonOperator.new(ast, classes), ClassDefinitionToFunctions.new(ast, classes), ObjectInstantiationToCall.new(ast, classes), CallToExplicitCall.new(ast, classes), DefaultParamToIfNode.new(ast, classes), DeserializeVarAssignment.new(ast, classes), TopLevelDefMethodToDef.new(ast, classes) ] rewriters.each do |rewriter| rewriter.rewrite_on_match end ast end |
#rewrite_included_and_sourced_files!(filename) ⇒ Object
We need to rewrite the included/sourced files before anything else. This is in order to keep track of any classes defined in the included and sourced files (and files included/sourced in those, etc…). We keep a cache of rewritten asts because the included/sourced files are parsed more than once. They’re parsed first in this step, plus whenever the compiler visits a ‘riml_include’/‘riml_source’ node in order to compile it on the spot.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/ast_rewriter.rb', line 76 def rewrite_included_and_sourced_files!(filename) old_ast = ast ast.children.each do |node| next unless RimlCommandNode === node action = node.name == 'riml_include' ? 'include' : 'source' node.each_existing_file! do |file, fullpath| if filename && @included_and_sourced_file_refs[file].include?(filename) msg = "#{filename.inspect} can't #{action} #{file.inspect}, as " \ " #{file.inspect} already included/sourced #{filename.inspect}" # IncludeFileLoop/SourceFileLoop raise Riml.const_get("#{action.capitalize}FileLoop"), msg elsif filename == file raise UserArgumentError, "#{file.inspect} can't include itself" end @included_and_sourced_file_refs[filename] << file riml_src = File.read(fullpath) # recursively parse included files with this ast_rewriter in order # to pick up any classes that are defined there rewritten_ast = Parser.new.parse(riml_src, self, file, action == 'include') rewritten_included_and_sourced_files[file] = rewritten_ast end end ensure self.ast = old_ast end |