Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
 
Ruby Macros
 
 
“ print 'hello'” :(print 'hello')‏ proc{ print 'hello' }
RedParse.new(“print 'hello'”).parse :(print 'hello')‏ RedParse::CallNode[nil, "print", [RedParse::StringNode[&qu...
str=:(“hello”)‏ :(print ^str)‏
macro simple(a,b)  :(^a+^b)  end def simple_user p simple(1,2)‏ end
if $Debug macro assert(cond)‏ if RedParse::OpNode===cond and /A[=!]=/===cond.op left,op,right=*cond :(fail 'expected '+^l...
def test_assert a=1  b=2 assert a  #ok assert a!=b  #ok assert(a==b) #oops, fails. msg="expected a(==1) to be == b(==...
Syntax trees are represented by trees of nested Nodes. All  Nodes descend from Array, and their subnodes can be  addressed...
VarNameToken<RubyLexer::Token  #represents variables and constants (ident: String)‏ Node<Array  #abstract ancestor of all ...
|||+MatchNode  #=~ expressions |||+NotMatchNode  #!~ expressions |+LiteralNode  #literal symbols, integers ||  (val: Numer...
|+AssigneeList  #abstract, comma-delimited list of assignables |||  (Array[LValue*])‏ ||+NestedAssign  #nested lhs, in par...
|+MethodNode  #def..end ||  (receiver:Value|nil, name:String, ||  params:Array[VarNameToken*,AssignNode*,UnaryStarNode?,Un...
Drawbacks: <ul><li>Pre-processing is very, very slow (because of RedParse)‏ </li></ul><ul><li>Macro calls must be in some ...
Upcoming SlideShare
Loading in …5
×

Rubymacros

530 views

Published on

Lisp-like macros for ruby!

Published in: Technology, Education
  • Be the first to comment

Rubymacros

  1. 2. Ruby Macros
  2. 5. “ print 'hello'” :(print 'hello')‏ proc{ print 'hello' }
  3. 6. RedParse.new(“print 'hello'”).parse :(print 'hello')‏ RedParse::CallNode[nil, &quot;print&quot;, [RedParse::StringNode[&quot;hello&quot;, {:@line=>1, :@close=>&quot;'&quot;, :@open=>&quot;'&quot;, :@char=>&quot;&quot;&quot;}]], nil, nil, {:@line=>1, :@not_real_parens=>true, :@offset=>0, :@lvalue=>nil}]
  4. 7. str=:(“hello”)‏ :(print ^str)‏
  5. 8. macro simple(a,b) :(^a+^b) end def simple_user p simple(1,2)‏ end
  6. 9. if $Debug macro assert(cond)‏ if RedParse::OpNode===cond and /A[=!]=/===cond.op left,op,right=*cond :(fail 'expected '+^left.unparse({})+&quot;(==#{^left}) to be &quot;+ ^op+&quot; &quot;+^right.unparse({})+&quot;(==#{^right})&quot; unless ^cond) else :(fail &quot;expected #{:(^^cond)}, but was not true&quot; unless ^cond)‏ end end else macro assert(cond)‏ end end
  7. 10. def test_assert a=1 b=2 assert a #ok assert a!=b #ok assert(a==b) #oops, fails. msg=&quot;expected a(==1) to be == b(==2)&quot; assert(nil) #oops, fails. msg=&quot;expected nil, but was not true&quot; #ok, that message didn't make a lot of sense... end
  8. 11. Syntax trees are represented by trees of nested Nodes. All Nodes descend from Array, and their subnodes can be addressed by numeric index, just like normal Arrays. However, many subnodes want to have names as well, thus most (but not all) array slots within the various Node classes have names. The general rule is that Node slots may contain a Node, a VarNameToken, a plain Array, a String, or nil. However, many cases are more specific than that.
  9. 12. VarNameToken<RubyLexer::Token #represents variables and constants (ident: String)‏ Node<Array #abstract ancestor of all nodes (except VarNameToken)‏ +RescueNode #a rescue clause in a def of begin statement | (exceptions: Array[Value*], varname: VarNameToken|nil, action: Value)‏ +WhenNode #a when clause in a case statement | (when: Value|Array[Value+] then: Value|nil )‏ +ElsifNode #an elsif clause in an if statement | (elsif: Value, then: Value|nil)‏ +ValueNode #abstract, a node which has a value (an expression)‏ |+ListOpNode #abstract, ancestor for nodes which are lists of ||| #things separated by some op ||+SequenceNode #a sequence of statements ||| (Array[Value*])‏ ||+ConstantNode #a constant expression of the form A::B::C or the like || #first expression can be anything || (Array[String|Value|nil,String+])‏ |+RawOpNode #ancestor of all operators (but not . :: ; , ?..:)‏ ||| (left: Value, op: String, right: Value)‏ ||+OpNode #ancestor of some operators |||+RangeNode #a range literal node |||+KeywordOpNode #abstract, ancestor of keyword operators ||||+LogicalNode #and or && || expressions ||||+WhileOpNode #while as an operator ||||+UntilOpNode #until as an operator ||||+IfOpNode #if as an operator ||||+UnlessOpNode #unless as an operator |||+NotEqualNode #!= expressions
  10. 13. |||+MatchNode #=~ expressions |||+NotMatchNode #!~ expressions |+LiteralNode #literal symbols, integers || (val: Numeric|Symbol|StringNode)‏ |+StringNode #literal strings ||| (Array[(String|Value)+])‏ ||+HereDocNode #here documents |+StringCatNode #adjacent strings are catenated (&quot;foo&quot; &quot;bar&quot; == &quot;foobar&quot;)‏ || (Array[StringNode+])‏ |+NopNode #an expression with no tokens at all in it || (no attributes)‏ |+VarLikeNode #nil,false,true,__FILE__,__LINE__,self || (name: String)‏ |+UnOpNode #unary operators || (op: String, val: Value)‏ ||+UnaryStarNode #unary star (splat)‏ |||+DanglingStarNode #unary star with no argument ||||| (no attributes)‏ ||||+DanglingCommaNode #comma with no rhs || (no attributes)‏ |+ParenedNode #ugly, parenthesized expressions and begin..end || (body: Value) -OR- (parentheses)‏ || (body: Value|nil, rescues: Array[RescueNode*], || else: Value|nil, ensure: Value|nil) (begin...end and rescue as operator)‏ |+AssignNode #assignment (including eg +=)‏ || (left:AssigneeList|LValue, op:String ,right:Array[Value*]|Value)‏
  11. 14. |+AssigneeList #abstract, comma-delimited list of assignables ||| (Array[LValue*])‏ ||+NestedAssign #nested lhs, in parentheses ||+MultiAssign #regular top-level lhs ||+BlockParams #block formal parameter list |+CallSiteNode #abstract, method calls ||| (receiver: Value|nil, name: String, ||| params: nil|Array[Value+,UnaryStarNode?,UnAmpNode?], ||| block_params: BlockParams, block: Value)‏ ||+CallNode #normal method calls ||+KWCallNode #keywords that look (more or less) like methods || #(BEGIN END yield return break continue next)‏ |+ArrayLiteralNode #[..] || (Array[Value*])‏ |+IfNode #if..end and unless..end || (if: Value, then: Value|nil, elsifs: Array[ElsifNode+]|Nil, else: Value|nil)‏ |+LoopNode #while..end and until..end || (while: Value, do: Value:nil)‏ |+CaseNode #case..end || (case: Value|nil, whens: Array[WhenNode*], else: Value|nil)‏ |+ForNode #for..end || (for: LValue, in: Value, do: Value|nil)‏ |+HashLiteralNode #{..} || (Array[Value*]) (size must be even)‏ |+TernaryNode # ? .. : || (if: Value, then: Value, else: Value)‏
  12. 15. |+MethodNode #def..end || (receiver:Value|nil, name:String, || params:Array[VarNameToken*,AssignNode*,UnaryStarNode?,UnAmpNode?]|nil, || body: Value|nil, rescues: Array[RescueNode+]|nil, else: Value|nil, ensure: Value|nil)‏ |+AliasNode #alias foo bar || (to: String|VarNameToken|StringNode, from: String|VarNameToken|StringNode)‏ |+UndefNode #undef foo || (Array[String|StringNode+])‏ |+NamespaceNode #abstract ||+ModuleNode #module..end ||| (name: VarNameToken|ConstantNode, body: Value|nil)‏ ||+ClassNode #class..end ||| (name: VarNameToken|ConstantNode, parent: Value|nil, body: Value|nil)‏ ||+MetaClassNode #class<<x..end || (val: Value, body: Value|nil)‏ |+BracketsGetNode #a[b] | (receiver: Value, params: Array[Value+,UnaryStarNode?]|nil)‏ | ErrorNode #mixed in to nodes with a syntax error +MisparsedNode #mismatched braces or begin..end or the like
  13. 16. Drawbacks: <ul><li>Pre-processing is very, very slow (because of RedParse)‏ </li></ul><ul><li>Macro calls must be in some sort of method... straight out macros at the top level won't work </li></ul><ul><li>Macro blocks and receivers aren't supported </li></ul><ul><li>Some ruby syntax won't work in files using macros </li></ul><ul><li>Files using macros must be loaded via Macro.require... not normal require </li></ul><ul><li>RedParse Node tree format will be changing </li></ul><ul><li>Macros cannot be scoped </li></ul>

×