Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: whitequark/furnace
base: f1272df9c3
...
head fork: whitequark/furnace
compare: 8dd5d41692
  • 2 commits
  • 18 files changed
  • 0 commit comments
  • 1 contributor
View
9 lib/furnace.rb
@@ -2,17 +2,22 @@
# static analyzers--any programs which read, manipulate or transform
# other programs.
#
-# Currently it provides four independent modules, grouped by the main
-# data structure being used:
+# Currently it provides four loosely coupled modules, each operating
+# upon a single kind of entity:
#
# * Abstract syntax trees: {AST}
# * Parametric types: {Type}
# * Static single assignment representation: {SSA}
# * Transformations: {Transform}
#
+# Additionally, a custom pretty printing module {AwesomePrinter} is
+# provided which has built-in knowledge of {Type}s.
+#
module Furnace
require "furnace/version"
+ require "furnace/awesome_printer"
+
require "furnace/ast"
require "furnace/type"
require "furnace/ssa"
View
121 lib/furnace/awesome_printer.rb
@@ -0,0 +1,121 @@
+require 'ansi'
+
+module Furnace
+ class AwesomePrinter
+ @colorize = true
+
+ class << self
+ attr_accessor :colorize
+ end
+
+ def initialize(colorize=self.class.colorize,
+ annotator=Type::Variable::Annotator.new)
+ @colorize = colorize
+ @annotator = annotator
+
+ @buffer = ""
+ @need_space = false
+
+ yield self if block_given?
+ end
+
+ def to_s
+ @buffer
+ end
+
+ alias to_str to_s
+
+ def ==(other)
+ to_s == other
+ end
+
+ def =~(other)
+ to_s =~ other
+ end
+
+ def append(what)
+ @need_space = false
+ @buffer << what.to_s
+
+ self
+ end
+
+ def text(what)
+ ensure_space { append what }
+ end
+
+ def newline
+ append "\n"
+ end
+
+ def nest(what, &block)
+ if what
+ if what.respond_to? :awesome_print
+ what.awesome_print(self)
+ else
+ text what.to_s
+ end
+ end
+
+ self
+ end
+
+ def name(what)
+ text "%#{what}"
+ end
+
+ def type(what)
+ text with_ansi(:green) { what }
+ end
+
+ def type_variable(what)
+ text with_ansi(:bright, :magenta) { "~#{@annotator.annotate(what)}" }
+ end
+
+ def keyword(what)
+ text with_ansi(:bright, :white) { what }
+ end
+
+ def collection(left='', separator='', right='', what, &block)
+ return self if what.empty?
+
+ ensure_space do
+ append left
+
+ what.each.with_index do |element, index|
+ if index > 0
+ append separator
+ end
+
+ if block_given?
+ yield element
+ else
+ nest element
+ end
+ end
+
+ append right
+ end
+ end
+
+ protected
+
+ def with_ansi(*colors)
+ if @colorize
+ ANSI::Code.ansi(yield.to_s, *colors)
+ else
+ yield
+ end
+ end
+
+ def ensure_space
+ append " " if @need_space
+
+ yield
+
+ @need_space = true
+
+ self
+ end
+ end
+end
View
1  lib/furnace/ssa.rb
@@ -21,7 +21,6 @@ def self.opcode_to_class_name(opcode)
end
end
- require_relative 'ssa/pretty_printer'
require_relative 'ssa/event_stream'
require_relative 'ssa/types/basic_block'
View
6 lib/furnace/ssa/argument.rb
@@ -15,9 +15,9 @@ def replace_type_with(type, replacement)
self.type = self.type.replace_type_with(type, replacement)
end
- def pretty_print(p=SSA::PrettyPrinter.new)
- @type.pretty_print(p)
- p.name name
+ def awesome_print(p=AwesomePrinter.new)
+ p.nest(@type).
+ name(name)
end
end
end
View
25 lib/furnace/ssa/basic_block.rb
@@ -127,22 +127,23 @@ def constant?
true
end
- def pretty_print(p=SSA::PrettyPrinter.new)
- p.text @name, ":"
- p.newline
-
- each do |insn|
- p << ' '
- insn.pretty_print(p)
- p.newline
+ def awesome_print(p=AwesomePrinter.new)
+ p.text(@name).
+ append(":").
+ newline
+
+ p.collection(@instructions) do |insn|
+ p.append(' ').
+ nest(insn).
+ newline
end
- p
+ p.newline
end
- def inspect_as_value(p=SSA::PrettyPrinter.new)
- p.keyword 'label'
- p.name name
+ def inspect_as_value(p=AwesomePrinter.new)
+ p.keyword('label').
+ name(name)
end
protected
View
6 lib/furnace/ssa/constant.rb
@@ -30,14 +30,14 @@ def ==(other)
end
end
- def inspect_as_value(p=SSA::PrettyPrinter.new)
- type.pretty_print(p)
+ def inspect_as_value(p=AwesomePrinter.new)
+ type.awesome_print(p)
p.text @value.inspect
p
end
def inspect
- pretty_print
+ awesome_print
end
end
end
View
4 lib/furnace/ssa/event_stream.rb
@@ -133,7 +133,7 @@ def dump_type(type)
when Type::Top, Type::Variable
desc = { kind: "monotype",
- name: type.pretty_print(printer).to_s }
+ name: type.awesome_print(printer).to_s }
else
raise "Cannot dump type #{type}:#{type.class}"
@@ -166,7 +166,7 @@ def object_kind(object)
protected
def printer
- SSA::PrettyPrinter.new(false, @annotator)
+ AwesomePrinter.new(false, @annotator)
end
end
end
View
30 lib/furnace/ssa/function.rb
@@ -110,6 +110,10 @@ def each(&proc)
alias each_basic_block each
+ def size
+ @basic_blocks.count
+ end
+
def include?(name)
@basic_blocks.any? { |n| n.name == name }
end
@@ -180,24 +184,18 @@ def to_value
SSA::Constant.new(self.class.to_type, @name)
end
- def pretty_print(p=SSA::PrettyPrinter.new)
- p.keyword 'function'
- @return_type.pretty_print(p)
- p.text @name, '('
- p.objects @arguments
- p.text ') {'
- p.newline
-
- each do |basic_block|
- basic_block.pretty_print(p)
- p.newline
- end
-
- p.text "}"
- p.newline
+ def awesome_print(p=AwesomePrinter.new)
+ p.keyword('function').
+ nest(@return_type).
+ text(@name).
+ collection('(', ', ', ') {', @arguments).
+ newline.
+ collection(@basic_blocks).
+ append('}').
+ newline
end
- alias inspect pretty_print
+ alias inspect awesome_print
def instrument
yield @instrumentation if @instrumentation
View
28 lib/furnace/ssa/instruction.rb
@@ -58,18 +58,14 @@ def terminator?
false
end
- def pretty_print(p=SSA::PrettyPrinter.new)
+ def awesome_print(p=AwesomePrinter.new)
unless type == Type::Bottom.new
- type.pretty_print(p)
- p.name name
- p.text '='
+ p.nest(type).
+ name(name).
+ text('=')
end
- if valid?
- p.keyword opcode
- else
- p.keyword_invalid opcode
- end
+ p.keyword(opcode)
pretty_parameters(p)
pretty_operands(p)
@@ -77,22 +73,24 @@ def pretty_print(p=SSA::PrettyPrinter.new)
p
end
- def inspect_as_value(p=SSA::PrettyPrinter.new)
+ def inspect_as_value(p=AwesomePrinter.new)
if type == Type::Bottom.new
- type.pretty_print(p)
+ p.nest(type)
else
super
end
end
- def pretty_parameters(p=SSA::PrettyPrinter.new)
+ def pretty_parameters(p=AwesomePrinter.new)
end
- def pretty_operands(p=SSA::PrettyPrinter.new)
+ def pretty_operands(p=AwesomePrinter.new)
if @operands
- p.values @operands
+ p.collection('', ', ', '', @operands) do |operand|
+ operand.inspect_as_value(p)
+ end
else
- p.text '<DETACHED>'
+ p.text('<DETACHED>')
end
end
View
4 lib/furnace/ssa/instructions/phi.rb
@@ -35,7 +35,7 @@ def pretty_operands(p)
p.text '=>'
value.inspect_as_value p
- p << ',' if index < @operands.count - 1
+ p.append ',' if index < @operands.count - 1
end
end
@@ -60,4 +60,4 @@ def replace_uses_of_operands(use, new_use)
end
end
end
-end
+end
View
6 lib/furnace/ssa/named_value.rb
@@ -14,12 +14,12 @@ def name=(name)
@name = @function.make_name(name)
end
- def inspect_as_value(p=SSA::PrettyPrinter.new)
- p.name name
+ def inspect_as_value(p=AwesomePrinter.new)
+ p.name(@name)
end
def inspect
- pretty_print
+ awesome_print
end
end
end
View
123 lib/furnace/ssa/pretty_printer.rb
@@ -1,123 +0,0 @@
-require 'ansi'
-
-module Furnace
- class SSA::PrettyPrinter
- @colorize = true
-
- class << self
- attr_accessor :colorize
- end
-
- def initialize(colorize=self.class.colorize,
- annotator=Type::Variable::Annotator.new)
- @colorize = colorize
- @annotator = annotator
- @buffer = ""
- @need_space = false
-
- yield self if block_given?
- end
-
- def to_s
- @buffer
- end
-
- alias to_str to_s
-
- def ==(other)
- to_s == other
- end
-
- def =~(other)
- to_s =~ other
- end
-
- def <<(what)
- @buffer << what
-
- self
- end
-
- def text(*what)
- what = what.map(&:to_s)
- return if what.all?(&:empty?)
-
- ensure_space do
- self << what.join
- end
- end
-
- def newline
- @need_space = false
-
- self << "\n"
- end
-
- def name(what)
- text '%', what
- end
-
- def type(what)
- text with_ansi(:green) { what.to_s }
- end
-
- def type_var(what)
- text with_ansi(:bright, :magenta) { '~' + @annotator.annotate(what) }
- end
-
- def keyword(what)
- text with_ansi(:bright, :white) { what.to_s }
- end
-
- def keyword_invalid(what)
- if @colorize
- text with_ansi(:bright, :red) { what.to_s }
- else
- text "!#{what}".to_s
- end
- end
-
- def objects(objects, separator=",", printer=:pretty_print)
- objects.each_with_index do |object, index|
- if block_given?
- yield object
- else
- object.send(printer, self)
- end
-
- self << separator if index < objects.count - 1
- end
-
- self
- end
-
- def values(values, separator=",")
- objects(values, separator, :inspect_as_value)
- end
-
- protected
-
- def with_ansi(*colors)
- string = yield
-
- if @colorize
- ANSI::Code.ansi(yield, *colors)
- else
- yield
- end
- end
-
- def ensure_space(need_space_after=true)
- if @need_space
- self << " "
- @need_space = false
- end
-
- yield
-
- @need_space = need_space_after
-
- self
- end
- end
-end
View
13 lib/furnace/ssa/user.rb
@@ -41,17 +41,6 @@ def replace_uses_of(value, new_value)
self
end
- def valid?(*args)
- verify!(*args)
- true
- rescue TypeError
- false
- end
-
- def verify!
- # do nothing
- end
-
protected
def update_use_lists
@@ -88,4 +77,4 @@ def replace_uses_of_operands(value, new_value)
def instrument_update
end
end
-end
+end
View
4 lib/furnace/ssa/value.rb
@@ -51,11 +51,11 @@ def ==(other)
equal?(other.to_value)
end
- def pretty_print(p=SSA::PrettyPrinter.new)
+ def awesome_print(p=AwesomePrinter.new)
inspect_as_value(p)
end
- def inspect_as_value(p=SSA::PrettyPrinter.new)
+ def inspect_as_value(p=AwesomePrinter.new)
p.text inspect
end
end
View
4 lib/furnace/type/top.rb
@@ -65,8 +65,8 @@ def to_s
'top'
end
- def pretty_print(p=SSA::PrettyPrinter.new)
- p.type self.to_s
+ def awesome_print(p=AwesomePrinter.new)
+ p.type(self)
end
end
end
View
2  lib/furnace/type/variable.rb
@@ -33,7 +33,7 @@ def specialize(other)
{ self => other }
end
- def pretty_print(p=SSA::PrettyPrinter.new)
+ def awesome_print(p=AwesomePrinter.new)
p.type_var self
end
end
View
148 test/test_awesome_printer.rb
@@ -0,0 +1,148 @@
+require_relative 'helper'
+
+describe AwesomePrinter do
+ AwesomePrinter.colorize = false
+
+ it 'outputs chunks' do
+ AwesomePrinter.new do |p|
+ p.text 'foo'
+ end.should == 'foo'
+
+ AwesomePrinter.new do |p|
+ Integer.to_type.awesome_print(p)
+ end.should == '^Integer'
+
+ AwesomePrinter.new do |p|
+ p.keyword 'bar'
+ end.should == 'bar'
+ end
+
+ it 'supports matching by =~' do
+ AwesomePrinter.new do |p|
+ p.text 'foo'
+ end.should =~ /foo/
+ end
+
+ it 'ensures space between chunks' do
+ AwesomePrinter.new do |p|
+ p.text 'foo'
+ p.keyword 'doh'
+ p.text 'bar'
+ end.should == 'foo doh bar'
+ end
+
+ it 'adds no space before and after #append' do
+ AwesomePrinter.new do |p|
+ p.text 'foo'
+ p.append 'bar'
+ p.text 'baz'
+ end.should == 'foobarbaz'
+ end
+
+ it 'adds no space after #newline' do
+ AwesomePrinter.new do |p|
+ p.text 'foo'
+ p.newline
+ p.text 'bar'
+ end.should == "foo\nbar"
+ end
+
+ it 'converts objects to chunks with to_s' do
+ AwesomePrinter.new do |p|
+ p.text :foo
+ p.text 1
+ p.keyword :bar
+ end.should == 'foo 1 bar'
+ end
+
+ it 'when nesting, delegates to #awesome_print and then #to_s' do
+ with_awesome_print = Object.new.tap do |o|
+ def o.awesome_print(p)
+ p.text "awesome print"
+ end
+ end
+
+ with_inspect = Object.new.tap do |o|
+ def o.to_s
+ "awesome to_s"
+ end
+ end
+
+ AwesomePrinter.new do |p|
+ p.nest with_awesome_print
+ p.nest with_inspect
+ end.should == 'awesome print awesome to_s'
+ end
+
+ it 'prints %names' do
+ AwesomePrinter.new do |p|
+ p.name 'foo'
+ end.should == '%foo'
+ end
+
+ it 'prints type variables' do
+ a, b = 2.times.map { Type::Variable.new }
+
+ AwesomePrinter.new do |p|
+ p.type_variable a
+ p.type_variable b
+ p.type_variable a
+ end.should == '~a ~b ~a'
+ end
+
+ it 'prints type variables with passed Annotator' do
+ a, b = 2.times.map { Type::Variable.new }
+
+ ann = Type::Variable::Annotator.new
+ ann.annotate a
+
+ AwesomePrinter.new(false, ann) do |p|
+ p.type_variable b
+ end.should == '~b'
+ end
+
+ it 'prints collections' do
+ AwesomePrinter.new do |p|
+ p.collection(%w(a b c))
+ end.should == 'abc'
+
+ AwesomePrinter.new do |p|
+ p.collection('<', '; ', '>', %w(a b c))
+ end.should == '<a; b; c>'
+ end
+
+ it 'ensures space before collections' do
+ AwesomePrinter.new do |p|
+ p.text 'foo'
+ p.collection(%w(a b c))
+ end.should == 'foo abc'
+ end
+
+ it 'prints collections with custom iterator' do
+ AwesomePrinter.new do |p|
+ p.collection(%w(abc def)) do |e|
+ p.text e.reverse
+ end
+ end.should == 'cbafed'
+ end
+
+ it 'chains' do
+ AwesomePrinter.new do |p|
+ p.append("foo").should == p
+ p.text("foo").should == p
+ p.newline.should == p
+ p.nest("foo").should == p
+ p.name("foo").should == p
+ p.type("foo").should == p
+ p.type_variable(Type::Variable.new).should == p
+ p.keyword("foo").should == p
+ p.collection(%w(a b)).should == p
+ end
+ end
+
+ it 'adds colors if requested' do
+ AwesomePrinter.new(true) do |p|
+ p.keyword :bar
+ end.should == "\e[1;37mbar\e[0m"
+ end
+end
View
98 test/test_ssa.rb
@@ -1,7 +1,7 @@
require_relative 'helper'
describe SSA do
- SSA::PrettyPrinter.colorize = false
+ AwesomePrinter.colorize = false
class BindingInsn < SSA::Instruction
def type
@@ -83,59 +83,6 @@ def insn_binary(basic_block, left, right)
SSA.opcode_to_class_name(:foo_bar).should == 'FooBarInsn'
end
- describe SSA::PrettyPrinter do
- it 'outputs chunks' do
- SSA::PrettyPrinter.new do |p|
- p.text 'foo'
- end.to_s.should == 'foo'
-
- SSA::PrettyPrinter.new do |p|
- Integer.to_type.pretty_print(p)
- end.to_s.should == '^Integer'
-
- SSA::PrettyPrinter.new do |p|
- p.keyword 'bar'
- end.to_s.should == 'bar'
- end
-
- it 'ensures space between chunks' do
- SSA::PrettyPrinter.new do |p|
- p.text 'foo'
- p.keyword 'doh'
- p.text 'bar'
- end.to_s.should == 'foo doh bar'
- end
-
- it 'adds no space inside #text chunk' do
- SSA::PrettyPrinter.new do |p|
- p.text 'foo', 'bar'
- p.keyword 'squick'
- end.to_s.should == 'foobar squick'
- end
-
- it 'adds no space after newline' do
- SSA::PrettyPrinter.new do |p|
- p.text 'foo'
- p.newline
- p.text 'bar'
- end.to_s.should == "foo\nbar"
- end
-
- it 'converts objects to chunks with to_s' do
- SSA::PrettyPrinter.new do |p|
- p.text :foo
- p.text 1
- p.keyword :bar
- end.to_s.should == 'foo 1 bar'
- end
-
- it 'adds colors if requested' do
- SSA::PrettyPrinter.new(true) do |p|
- p.keyword :bar
- end.to_s.should == "\e[1;37mbar\e[0m"
- end
- end
-
describe SSA::Value do
before do
@val = SSA::Value.new
@@ -158,7 +105,7 @@ def insn_binary(basic_block, left, right)
end
it 'pretty prints' do
- @val.pretty_print.should =~ %r{#<Furnace::SSA::Value}
+ @val.awesome_print.should =~ %r{#<Furnace::SSA::Value}
end
it 'has an use list' do
@@ -196,7 +143,7 @@ def insn_binary(basic_block, left, right)
end
it 'pretty prints' do
- @imm.pretty_print.should == '^Integer 1'
+ @imm.awesome_print.should == '^Integer 1'
end
it 'converts to value' do
@@ -238,7 +185,7 @@ def insn_binary(basic_block, left, right)
end
it 'pretty prints' do
- @val.pretty_print.
+ @val.awesome_print.
should == '^Integer %foo'
end
@@ -371,29 +318,29 @@ def insn_binary(basic_block, left, right)
it 'pretty prints' do
dup = DupInsn.new(@basic_block, [SSA::Constant.new(Integer, 1)])
- dup.pretty_print.should == '^Integer %2 = dup ^Integer 1'
+ dup.awesome_print.should == '^Integer %2 = dup ^Integer 1'
dup.inspect_as_value.should == '%2'
concat = TupleConcatInsn.new(@basic_block,
[SSA::Constant.new(Array, [1]), SSA::Constant.new(Array, [2,3])])
- concat.pretty_print.should == '^Array %3 = tuple_concat ^Array [1], ^Array [2, 3]'
+ concat.awesome_print.should == '^Array %3 = tuple_concat ^Array [1], ^Array [2, 3]'
concat.inspect_as_value.should == '%3'
zero_arity = BindingInsn.new(@basic_block)
- zero_arity.pretty_print.should == '^Binding %4 = binding'
+ zero_arity.awesome_print.should == '^Binding %4 = binding'
zero_arity.inspect_as_value.should == '%4'
zero_all = TestScope::NestedInsn.new(@basic_block)
- zero_all.pretty_print.should == 'nested'
+ zero_all.awesome_print.should == 'nested'
zero_all.inspect_as_value.should == 'bottom'
end
describe SSA::GenericInstruction do
it 'has settable type' do
i = GenericInsn.new(@basic_block, Integer, [])
- i.pretty_print.should =~ /\^Integer %\d+ = generic/
+ i.awesome_print.should =~ /\^Integer %\d+ = generic/
i.type = Binding
- i.pretty_print.should =~ /\^Binding %\d+ = generic/
+ i.awesome_print.should =~ /\^Binding %\d+ = generic/
end
describe SSA::PhiInsn do
@@ -416,7 +363,7 @@ def insn_binary(basic_block, left, right)
it 'pretty prints' do
phi = SSA::PhiInsn.new(@basic_block, Integer,
{ @basic_block => SSA::Constant.new(Integer, 1) })
- phi.pretty_print.should =~
+ phi.awesome_print.should =~
/\^Integer %\d = phi %\d => \^Integer 1/
end
@@ -516,8 +463,8 @@ def i.exits?; true; end
it 'pretty prints' do
@basic_block.append insn_noary(@basic_block)
@basic_block.append insn_noary(@basic_block)
- @basic_block.pretty_print.should ==
- "1:\n ^Binding %2 = binding\n ^Binding %3 = binding\n"
+ @basic_block.awesome_print.should ==
+ "1:\n ^Binding %2 = binding\n ^Binding %3 = binding\n\n"
end
it 'inspects as value' do
@@ -715,8 +662,8 @@ def i.exits?; true; end
@function.add bb2
bb2.append insn_unary(@basic_block, SSA::Constant.new(Integer, 1))
- @function.pretty_print.should == <<-END
-function bottom foo( ^Integer %count, ^Binding %outer ) {
+ @function.awesome_print.should == <<-END
+function bottom foo (^Integer %count, ^Binding %outer) {
1:
^Array %2 = tuple_concat %count, %outer
@@ -794,7 +741,7 @@ def i.exits?; true; end
end
f2.name = f1.name
- f2.pretty_print.to_s.should == f1.pretty_print.to_s
+ f2.awesome_print.to_s.should == f1.awesome_print.to_s
end
end
@@ -948,19 +895,6 @@ class SyntaxSplatInsn < SSA::Instruction
i.operands.should == [@iconst, @iinsn, @fconst]
end
- it 'allows to inquire status' do
- i = SyntaxTypedInsn.new(@basic_block, [ @iconst ])
- i.should.be.valid
- i.foo = @fconst
- i.should.not.be.valid
- end
-
- it 'highlights invalid insns when pretty printing' do
- i = SyntaxTypedInsn.new(@basic_block, [ @iconst ])
- i.foo = @fconst
- i.pretty_print.should =~ /!syntax_typed/
- end
-
it 'does not interfere with def-use tracking' do
i = SyntaxUntypedInsn.new(@basic_block, [ @iconst, @fconst ])
@fconst.should.enumerate :each_use, [ i ]

No commit comments for this range

Something went wrong with that request. Please try again.