Skip to content

Commit

Permalink
Dump an AST in dot language
Browse files Browse the repository at this point in the history
Signed-off-by: Leonard Schuetz <leni.schuetz@me.com>
  • Loading branch information
KCreate committed Jan 2, 2017
1 parent e605dc8 commit 8dd3e1b
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/charly.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ require "./charly/interpreter/prelude.cr"
require "./charly/gc_warning.cr"
require "./charly/config.cr"
require "./charly/visitors/DumpVisitor.cr"
require "./charly/visitors/DotDumpVisitor.cr"
require "option_parser"

# :nodoc:
Expand Down Expand Up @@ -104,6 +105,12 @@ module Charly
STDOUT.puts output.to_s.strip
end

if flags.includes? "dotdump"
dot_dump_visitor = DotDumpVisitor.new
user_program.tree.accept dot_dump_visitor, dot_dump_visitor.content
dot_dump_visitor.render STDOUT
end

prelude_scope = PreludeLoader.load(PRELUDE_PATH, arguments, flags)
user_scope = Scope.new(prelude_scope)
visitor = Visitor.new(user_scope, prelude_scope)
Expand Down
137 changes: 137 additions & 0 deletions src/charly/visitors/DotDumpVisitor.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
require "./TreeVisitor.cr"

module Charly::AST

# Dumps "dot-language" of a given ASTNode
class DotDumpVisitor < TreeVisitor
property content
@@next_id = 0

HEADER = <<-HEADER
digraph astgraph {
node [
shape=rectangle,
fontsize=10,
fontname="SF Mono",
width=1.6,
height=.1,
margin=.05
];
ranksep=.3;
splines=ortho;
edge [arrowsize=0]
HEADER

def initialize
@content = IO::Memory.new 0
end

def render(io : IO)
io.puts HEADER
io.puts @content.to_s.indent(2, " ")
io.puts "}"
end

macro visit(type, meta, properties)
def visit(node : {{type}}, io : IO)
id = @@next_id
io.puts "node#{id} [label=\"#{node.class.name.split("::").last}\\n#{{{meta}}}\"];"
@@next_id += 1
io << render_node id, {{properties}}
id
end
end

macro visit(type, properties)
def visit(node : {{type}}, io : IO)
id = @@next_id
io.puts "node#{id} [label=\"#{node.class.name.split("::").last}\"];"
@@next_id += 1
io << render_node id, {{properties}}
id
end
end

macro visit(type)
def visit(node : {{type}}, io : IO)
id = @@next_id
io.puts "node#{id} [label=\"#{node.class.name.split("::").last}\"];"
@@next_id += 1
id
end
end

visit ASTNode

visit PrecalculatedValue, node.value, [] of ASTNode
visit Block, node.children

visit IfStatement, [node.test, node.consequent, node.alternate]
visit GuardStatement, [node.test, node.alternate]
visit UnlessStatement, [node.test, node.consequent, node.alternate]

visit WhileStatement, [node.test, node.consequent]
visit UntilStatement, [node.test, node.consequent]
visit LoopStatement, [node.consequent]

visit UnaryExpression, node.operator, [node.right]
visit BinaryExpression, node.operator, [node.left, node.right]
visit ComparisonExpression, node.operator, [node.left, node.right]

visit And, [node.left, node.right]
visit Or, [node.left, node.right]

visit VariableInitialisation, [node.identifier, node.expression]
visit VariableAssignment, [node.identifier, node.expression]
visit ConstantInitialisation, [node.identifier, node.expression]

visit CallExpression, [node.identifier, node.argumentlist]
visit MemberExpression, [node.identifier, node.member]
visit IndexExpression, [node.identifier, node.argument]

visit ExpressionList, node.children
visit IdentifierList, node.children

visit ReturnStatement, [node.expression]
visit ThrowStatement, [node.expression]

visit TryCatchStatement, [node.try_block, node.exception_name, node.catch_block]

visit IdentifierLiteral, node.name, [] of ASTNode
visit ReferenceIdentifier, [node.identifier]
visit StringLiteral, node.value, [] of ASTNode
visit NumericLiteral, node.value, [] of ASTNode
visit BooleanLiteral, node.value, [] of ASTNode
visit ArrayLiteral, node.children
visit FunctionLiteral, node.name, [node.argumentlist, node.block]
visit ContainerLiteral, [node.block]
visit ClassLiteral, node.name, [node.block, node.parents]
visit PrimitiveClassLiteral, node.name, [node.block]
visit PropertyDeclaration, [node.identifier]
visit StaticDeclaration, [node.node]

private def render_node(parent_id, children)
String.build do |io|
children.each_with_index do |node, index|

unless node.is_a? ASTNode
next
end

id = 0
str = String.build do |str|
id = node.accept self, str
end

str.lines.each do |line|
io << line
io << "\n"
end

io.puts "node#{parent_id} -> node#{id};"
end
end
end
end

end

0 comments on commit 8dd3e1b

Please sign in to comment.