Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but 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
9 lib/furnace.rb
View
@@ -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"
121 lib/furnace/awesome_printer.rb
View
@@ -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
1  lib/furnace/ssa.rb
View
@@ -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'
6 lib/furnace/ssa/argument.rb
View
@@ -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
25 lib/furnace/ssa/basic_block.rb
View
@@ -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
6 lib/furnace/ssa/constant.rb
View
@@ -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
4 lib/furnace/ssa/event_stream.rb
View
@@ -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
30 lib/furnace/ssa/function.rb
View
@@ -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
28 lib/furnace/ssa/instruction.rb
View
@@ -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
4 lib/furnace/ssa/instructions/phi.rb
View
@@ -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
6 lib/furnace/ssa/named_value.rb
View
@@ -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
123 lib/furnace/ssa/pretty_printer.rb
View
@@ -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
13 lib/furnace/ssa/user.rb
View
@@ -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
4 lib/furnace/ssa/value.rb
View
@@ -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
4 lib/furnace/type/top.rb
View
@@ -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
2  lib/furnace/type/variable.rb
View
@@ -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
148 test/test_awesome_printer.rb
View
@@ -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
98 test/test_ssa.rb
View
@@ -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.