CASTY
Casty is a fork of CAST, a C parser and abstract syntax tree for Ruby.
It does not choke on #-sequences (source map etc.) left in by preprocessor. #line-support may be coming, also C::Preprocessor has more options.
Example
require 'casty'
source = File.read('file.c')
ast = C.parse(source)
ast.entities.each do |declaration|
declaration.declarator.each do |declarator|
puts "#{declarator.name}: declarator.type"
end
end
Or in irb:
irb> ast = C.parse('int main(void) { return 0; }')
=> TranslationUnit
entities:
- FunctionDef
type: Function
type: Int
params: []
name: "main"
def: Block
stmts:
- Return
expr: IntLiteral
val: 0
irb> puts ast
int main(void) {
return 0;
}
=> nil
Nodes
C.parse returns a tree of Node objects. Here's the class hierarchy:
- Node
- TranslationUnit
- Comment
- Declaration
- Declarator
- FunctionDef
- Parameter
- Enumerator
- MemberInit
- Member
- Statement
- Block
- If
- Switch
- While
- For
- Goto
- Continue
- Break
- Return
- ExpressionStatement
- Label
- PlainLabel
- Default
- Case
- Type
- IndirectType
- Pointer
- Array
- Function
- DirectType
- Struct
- Union
- Enum
- CustomType
- PrimitiveType
- Void
- Int
- Float
- Char
- Bool
- Complex
- Imaginary
- IndirectType
- Node
- Expression
- Comma
- Conditional
- Variable
- UnaryExpression
- PostfixExpression
- Index
- Call
- Dot
- Arrow
- PostInc
- PostDec
- PrefixExpression
- Cast
- Address
- Dereference
- Sizeof
- Plus
- Minus
- PreInc
- PreDec
- BitNot
- Not
- PostfixExpression
- BinaryExpression
- Add
- Subtract
- Multiply
- Divide
- Mod
- Equal
- NotEqual
- Less
- More
- LessOrEqual
- MoreOrEqual
- BitAnd
- BitOr
- BitXor
- ShiftLeft
- ShiftRight
- And
- Or
- Expression
- Node
- Expression
- AssignmentExpression
- Assign
- MultiplyAssign
- DivideAssign
- ModAssign
- AddAssign
- SubtractAssign
- ShiftLeftAssign
- ShiftRightAssign
- BitAndAssign
- BitXorAssign
- BitOrAssign
- Literal
- StringLiteral
- CharLiteral
- CompoundLiteral
- IntLiteral
- FloatLiteral
- AssignmentExpression
- NodeList
- NodeArray
- NodeChain
- Expression
The highlighted ones are abstract.
The last 2 (NodeLists) represent lists of Nodes. They quack like
standard ruby Arrays. NodeChain is a doubly linked list;
NodeArray is an array.
Node Methods
-
parent: return the parent in the tree (aNodeor nil). -
pos,pos=: the position in the source file (aNode::Pos). -
to_s: return the code for the tree (aString). -
inspect: return a pretty string for inspection, makes irb fun. -
match?(str),=~(str): return true iff str parses as aNodeequal to this one. -
detach: remove this node from the tree (parent becomes nil) and return it. -
detached?,attached?: return true if parent is nil or non-nil respectively. -
replace_with(node): replace this node with node in the tree. -
swap_with(node): exchange this node with node in their trees. -
insert_prev(*nodes),insert_next(*nodes): insert nodes before this node in the parent list. Parent must be aNodeList! Useful for adding statements before a node in a block, for example. -
Foo?: (whereFoois a module name) returnself.is_a?(Foo). This is a convienience for a common need. Example:\# print all global variables ast.entities.each do |node| node.Declaration? or next node.declarators.each do |decl| unless decl.type.Function? puts "#{decl.name}: #{decl.type}" end end end
The =~ method lets you do:
if declarator.type =~ 'const int *'
puts "Ooh, a const int pointer!"
end
This is not the same as declarator.type.to_s == 'const int *';
that'd require you to guess how to_s formats its strings (most
notably, the whitespace).
Fields and Children
The big table down below lists the fields of each Node. A field is
an attribute which:
- is used in equality checks (
==andeql?). - are copied recursively by
dupandclone.
Fields listed as children form the tree structure. They only have a
Node or nil value, and are yielded/returned/affected by the
traversal methods:
next,prev: return the next/prev sibling.list_next,list_prev: likenext/prev, but also requires the parent to beNodeList. I'll be honest; I don't remember why I added these methods. They may well suddenly disappear.each,reverse_each: Yield all (non-nil) children.NodeincludesEnumerable, so, you know.depth_first,reverse_depth_first: Walk the tree in that order, yielding two args (event, node) at each node. event is:downon the way down,:upon the way up. If the block throws:prune, it won't descend any further.preorder,reverse_preorder,postorder,reverse_postorder: Walk the tree depth first, yielding nodes in the given order. For the preorders, if the block throws:prune, it won't descend any further.node_after(child),node_before(child): return the node before/after child (same aschild.next).remove_node(child): remove child from this node (same aschild.detach).replace_node(child, new_child): replace child with yeah you guessed it (same aschild.replace_with(newchild)).
Note: don't modify the tree during traversal!
Other notes about the table:
- Field names that end in '?' are always true-or-false.
- If no default is listed:
- it is false if the field name ends in a '?'
- it is a
NodeArrayif it is aNodeList. - it is
nilotherwise.