Class: Macro
- Defined in:
- lib/macro/form.rb,
lib/macro.rb,
lib/rubymacros/version.rb
Overview
require “macro”
Defined Under Namespace
Modules: DisableMacros, Names Classes: CallSiteNode, ClassNode, FormEscapeNode, FormNode, IncludedSourceNode, JustNilNode, MacroNode, MetaClassNode, MethodNode, ModuleNode, Node, OneLineParenedNode
Constant Summary collapse
- ListInNode =
Node helper module
RedParse::ListInNode
- PostponedMethods =
all 3 of these are giant memory leaks
[]
- GLOBALS =
{}
- QuotedStore =
[]
- UNCOPYABLE =
Symbol|Numeric|true|false|nil|
Module|Proc|IO|Method|UnboundMethod|Thread|Continuation
- FormParameterNode =
FormEscapeNode
- VERSION =
"0.1.6"
- Macro_ParserMixin =
old name
::RedParse::MacroMixin
- RedParseWithMacros =
old name
::RedParse::WithMacros
Class Method Summary collapse
-
.copy(obj, seen = {}) ⇒ Object
TODO: dead code (only used by the dead else block above).
- .delete(name, context = ::Object) ⇒ Object
- .delete_all!(*contexts) ⇒ Object
-
.eval(code, binding = nil, file = "(eval)", line = 1) ⇒ Object
like Kernel#eval, but allows macros (and forms) as well.
-
.expand(tree, macros = Macro::GLOBALS, session = {}, filename = nil) ⇒ Object
remember macro definitions and expand macros within a parsetree.
- .list(*contexts) ⇒ Object
-
.load(filename, wrap = false) ⇒ Object
like Kernel#load, but allows macros (and forms) as well.
-
.parse(code, file = "(eval)", line = 1, lvars = []) ⇒ Object
A helper for Macro.eval which returns a RedParse tree for the given code string.
-
.postpone(node, session) ⇒ Object
Create a node to postpone the macro (or method) definition until it is actually executed.
-
.quote(obj) ⇒ Object
Return a quoted node for the given scalar.
-
.require(filename) ⇒ Object
like Kernel#require, but allows macros (and forms) as well.
Class Method Details
.copy(obj, seen = {}) ⇒ Object
TODO: dead code (only used by the dead else block above).
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/macro.rb', line 191 def Macro.copy obj,seen={} result=seen[obj.__id__] return result if result result= case obj when Symbol,Numeric,true,false,nil; return obj when String; seen[obj.__id__]=obj.dup when Array seen[obj.__id__]=dup=obj.dup dup.map!{|x| copy x,seen} when Hash result={} seen[obj.__id__]=result obj.each_pair{|k,v| result[copy( k )]=copy v,seen } result when Module,Proc,IO,Method, UnboundMethod,Thread,Continuation return obj else obj.dup end obj.instance_variables.each{|iv| result.instance_variable_set iv, copy(obj.instance_variable_get(iv),seen) } return result end |
.delete(name, context = ::Object) ⇒ Object
85 86 87 88 89 90 91 92 |
# File 'lib/macro.rb', line 85 def Macro.delete(name,context=::Object) Thread.current[:Macro_being_undefined]="macro_"+name class<<context; remove_method(Thread.current[:Macro_being_undefined]); end if context==::Object Macro::GLOBALS.delete name.to_sym end Thread.current[:Macro_being_undefined]=nil end |
.delete_all!(*contexts) ⇒ Object
105 106 107 108 109 110 |
# File 'lib/macro.rb', line 105 def Macro.delete_all!(*contexts) contexts=[::Object] if contexts.empty? contexts.each{|ctx| Macro.list(ctx).each{|mac| Macro.delete mac,ctx } } end |
.eval(code, binding = nil, file = "(eval)", line = 1) ⇒ Object
like Kernel#eval, but allows macros (and forms) as well. beware: default for second argument is currently broken. best practice is to pass an explicit binding (see Kernel#binding) for now.
code
-
a string of code to evaluate
binding
-
the binding in which to evaluate the code
file
-
the name of the file this code came from
line
-
the line number this code came from
122 123 124 125 126 127 128 129 |
# File 'lib/macro.rb', line 122 def Macro.eval(code,binding=nil,file="(eval)",line=1) #binding should default to Binding.of_caller, but byellgch lvars=binding ? ::Kernel.eval("local_variables()",binding) : [] code=Macro.parse(code,file,line,lvars) unless Node===code tree=Macro.(code,file) tree.eval binding,file,line end |
.expand(tree, macros = Macro::GLOBALS, session = {}, filename = nil) ⇒ Object
remember macro definitions and expand macros within a parsetree. the first argument must be a parse tree in RedParse format. the optional second argument is a hash of macros to be pre-loaded. (the keys of the hash are symbols and the values are Methods for the corresponding macro method. typically, callers won’t need to use any but the first argument; just define macros in the source text.) on returning, the second arg is updated with the macro definitions seen during expansion.
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/macro.rb', line 382 def Macro. tree,macros=Macro::GLOBALS,session={},filename=nil if String===macros filename=macros macros=Macro::GLOBALS end if String===session filename=session session={} end session[:@modpath]||=[] session[:filename]||=filename filename||="(eval)" case tree when String,IO; tree=parse(tree,filename) end fail unless macros.__id__==Macro::GLOBALS.__id__ #for now tree.walk{|parent,i,subi,node| is_node=Node===node if is_node and node.respond_to? :macro_expand newnode,recurse=node.(macros,session) #implementations of macro_expand follow, but to summarize: #look for macro definitions, save them and remove them from the tree (MacroNode) #look for macro invocations, and expand them (CallSiteNode) #disable macro definitions within classes and modules(for now) (ClassNode and ModuleNode #postpone macro expansion (and definition) in forms until they are evaled (Form) #(or returned from a macro) #but not in form parameters #postpone macro expansion in method defs til method def'n is executed #otherwise, disable other macro expansion for now. #postpone macro definitions until the definition is executed. if newnode return newnode unless parent #replacement at top level if subi target,index=parent[i],subi else target,index=parent,i end if JustNilNode===newnode and target.class==::Array || case target when UndefNode,AssigneeList,SequenceNode; true end target.delete_at index else target[index]=newnode end fail if recurse end else recurse=is_node end recurse } return tree end |
.list(*contexts) ⇒ Object
94 95 96 97 |
# File 'lib/macro.rb', line 94 def Macro.list(*contexts) contexts=[::Object] if contexts.empty? contexts.map{|ctx| ctx.singleton_methods.grep(/\Amacro_/) }.flatten.map{|ctx| ctx.to_s.gsub!(/\Amacro_/,'') } end |
.load(filename, wrap = false) ⇒ Object
like Kernel#load, but allows macros (and forms) as well.
filename
-
the name of the file to load
wrap
-
whether to wrap the loaded file in an anonymous module
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/macro.rb', line 69 def Macro.load(filename,wrap=false) [''].concat($:).each{|pre| pre+="/" unless %r{(\A|/)\Z}===pre if File.exist? finally=pre+filename tree=File.open(finally){|code| #code="::Module.new do\n#{code}\nend\n" if wrap Macro.(parse(code,finally),filename) } tree.load filename,wrap return true end } raise LoadError, "no such file to load -- "+filename end |
.parse(code, file = "(eval)", line = 1, lvars = []) ⇒ Object
A helper for Macro.eval which returns a RedParse tree for the given code string.
code
-
a string of code to evaluate
binding
-
the binding in which to evaluate the code
file
-
the name of the file this code came from
line
-
the line number this code came from
lvars
-
a list of local variables (empty unless called
recursively)
141 142 143 144 145 146 147 148 149 150 |
# File 'lib/macro.rb', line 141 def Macro.parse(code,file="(eval)",line=1,lvars=[]) if Binding===file or Array===file lvars=file file="(eval)" end if Binding===lvars lvars=eval "local_variables", lvars end ::RedParse::WithMacros.new(code,file,line,lvars).parse end |
.postpone(node, session) ⇒ Object
Create a node to postpone the macro (or method) definition until it is actually executed. For example, in the following code:
if foo
macro bar
...
end
else
macro bar
...
end
end
without postponing macro definition, the latter macro would always override the former.
node
-
the RedParse node for the entire method or macro defintion that is being postponed
session
-
the context in which this macro is being processed
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/macro.rb', line 240 def Macro.postpone node,session return node #disable postponement =begin was filename=session[:filename] unless session[:@modpath_unsure] modpath=ConstantNode[nil,*session[:@modpath]] modpath.push "Object" unless modpath.size>1 if session[:@namespace_type]==ModuleNode node=ModuleNode[modpath,node,[],nil,nil] #:(module ^modpath; ^node; end) else node=ClassNode[modpath,nil,node,[],nil,nil] #:(class ^modpath; ^node; end) end end evalname=modpath ? "load" : "eval" PostponedMethods << node #unexpanded=:(::Macro::PostponedMethods[^(PostponedMethods.size-1)].deep_copy) #expanded=:(::Macro.expand(^unexpanded,Macro::GLOBALS,{:@expand_in_defs=>true},^filename)) #return :( ^expanded.^evalname(^filename) ) unexpanded=CallNode[CallNode[ConstantNode[nil,"Macro", "PostponedMethods"], "[]",[LiteralNode[PostponedMethods.size-1]],nil,nil],"deep_copy",nil,nil,nil] expanded= CallNode[ConstantNode[nil,"Macro"],"expand", [unexpanded, ConstantNode[nil,"Macro","GLOBALS"], HashLiteralNode[LiteralNode[:@expand_in_defs], VarLikeNode["true"]], Macro.quote(filename)], nil,nil] return CallNode[expanded,evalname,[Macro.quote( filename )],nil,nil] =end end |
.quote(obj) ⇒ Object
Return a quoted node for the given scalar
obj
-
any object or node
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/macro.rb', line 159 def Macro.quote obj #result= case obj when Symbol,Numeric; LiteralNode[obj] when true,false,nil; VarLikeNode[obj.inspect] when String obj=obj.gsub(/['\\]/){|ch| '\\'+ch } StringNode[obj,{:@open=>"'", :@close=>"'"}] when Reg::Formula Reg::Deferred.defang! obj when Reg::Reg obj # TODO: The following is dead code and should be removed else #result=:(::Macro::QuotedStore[^QuotedStore.size]) result=CallNode[ConstantNode[nil,"Macro","QuotedStore"],"[]", [LiteralNode[QuotedStore.size]], nil,nil] QuotedStore << obj #register obj in quoted store UNCOPYABLE===result or #result=:(::Macro.copy ^result) result= CallNode[ConstantNode[nil,"Macro"],"copy", [result], nil,nil] result end end |
.require(filename) ⇒ Object
like Kernel#require, but allows macros (and forms) as well. c extensions (.dll,.so,etc) cannot be loaded via this method.
filename
-
the name of the feature to require
57 58 59 60 61 62 |
# File 'lib/macro.rb', line 57 def Macro.require(filename) filename+='.rb' unless filename[/\.rb\Z/] return if $".include? filename $" << filename load filename end |