sexpir 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/sexpir +5 -0
- data/lib/sexpir/ast.rb +98 -0
- data/lib/sexpir/ast_sexp.rb +144 -0
- data/lib/sexpir/checker.rb +14 -0
- data/lib/sexpir/code.rb +46 -0
- data/lib/sexpir/code_generator.rb +8 -0
- data/lib/sexpir/compiler.rb +39 -0
- data/lib/sexpir/graph.rb +116 -0
- data/lib/sexpir/log.rb +22 -0
- data/lib/sexpir/parser.rb +274 -0
- data/lib/sexpir/printer.rb +10 -0
- data/lib/sexpir/ruby_rtl_generator.rb +169 -0
- data/lib/sexpir/runner.rb +64 -0
- data/lib/sexpir/transformer.rb +9 -0
- data/lib/sexpir/version.rb +3 -0
- data/lib/sexpir/visitor.rb +76 -0
- data/lib/sexpir.rb +4 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 682317cfd3be1aa8cbdefa505279d36be704c833b4f9cfc1165dbfe6f17777c0
|
4
|
+
data.tar.gz: 739265b361bf8d05fa41c827d1db2b2a858306832343189eba1c5f368110000f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 60bff8d21a153e2c9f919b07d24647b55233f67170789ce247625f4f8e4024d5756dd85b200840a121df202441d1003505b9e31cda1003755d52e11fe6a03433
|
7
|
+
data.tar.gz: 0d501270fe47eaa5a1d46d71686ce1e0aba1dc19921fa579cedd6b029ea98faaf64102e2149bf6b8418c64abacfc6462eeedfa5eadbb16cc40ea294888dedd16
|
data/bin/sexpir
ADDED
data/lib/sexpir/ast.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
module Sexpir
|
2
|
+
class Ast
|
3
|
+
attr_accessor :node
|
4
|
+
def accept(visitor, arg=nil)
|
5
|
+
name = self.class.name.split(/::/).last
|
6
|
+
visitor.send("visit#{name}".to_sym, self ,arg) # Metaprograming !
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Circuit < Ast
|
11
|
+
attr_accessor :name,:signals,:inputs,:outputs,:body
|
12
|
+
def initialize
|
13
|
+
@signals=[]
|
14
|
+
@inputs,@outputs=[],[]
|
15
|
+
@body=Body.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Signal < Ast
|
20
|
+
attr_accessor :name,:type
|
21
|
+
end
|
22
|
+
|
23
|
+
class Io < Signal
|
24
|
+
end
|
25
|
+
|
26
|
+
class Port < Io
|
27
|
+
attr_accessor :component_name,:name
|
28
|
+
end
|
29
|
+
|
30
|
+
class Input < Io
|
31
|
+
end
|
32
|
+
|
33
|
+
class Output < Io
|
34
|
+
end
|
35
|
+
|
36
|
+
# statements
|
37
|
+
class Body < Ast
|
38
|
+
attr_accessor :stmts
|
39
|
+
def initialize
|
40
|
+
@stmts=[]
|
41
|
+
end
|
42
|
+
|
43
|
+
def << e
|
44
|
+
@stmts << e
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Combinatorial < Ast
|
49
|
+
attr_accessor :label,:body
|
50
|
+
end
|
51
|
+
|
52
|
+
class Sequential < Ast
|
53
|
+
attr_accessor :label,:body
|
54
|
+
end
|
55
|
+
|
56
|
+
class Assign < Ast
|
57
|
+
attr_accessor :lhs,:rhs
|
58
|
+
end
|
59
|
+
|
60
|
+
class If < Ast
|
61
|
+
attr_accessor :cond,:then,:elsifs,:else
|
62
|
+
def initialize
|
63
|
+
@elsifs=[]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
#===============================
|
67
|
+
class Component < Ast
|
68
|
+
attr_accessor :name,:type
|
69
|
+
end
|
70
|
+
|
71
|
+
class Connect < Ast
|
72
|
+
attr_accessor :source,:sink
|
73
|
+
end
|
74
|
+
#================================
|
75
|
+
class Expression < Ast
|
76
|
+
end
|
77
|
+
|
78
|
+
class Binary < Expression
|
79
|
+
attr_accessor :lhs,:op,:rhs
|
80
|
+
end
|
81
|
+
|
82
|
+
class Term < Expression
|
83
|
+
end
|
84
|
+
|
85
|
+
class Var < Term
|
86
|
+
attr_accessor :name
|
87
|
+
def initialize name
|
88
|
+
@name=name
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Const < Expression
|
93
|
+
attr_accessor :value
|
94
|
+
def initialize value
|
95
|
+
@value=value
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require_relative 'code'
|
2
|
+
|
3
|
+
class Symbol
|
4
|
+
def sexp
|
5
|
+
self
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Sexpir
|
10
|
+
|
11
|
+
class Circuit < Ast
|
12
|
+
def sexp
|
13
|
+
code=Code.new
|
14
|
+
code << "(circuit #{name}"
|
15
|
+
code.indent=2
|
16
|
+
signals.each{|sig| code << sig.sexp}
|
17
|
+
inputs.each{|input | code << input.sexp}
|
18
|
+
outputs.each{|output| code << output.sexp}
|
19
|
+
code.newline
|
20
|
+
code << body.sexp
|
21
|
+
code.indent=0
|
22
|
+
code << ")"
|
23
|
+
code.finalize
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Signal < Ast
|
28
|
+
def sexp
|
29
|
+
"(signal #{name} #{type})"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Port < Io
|
34
|
+
def sexp
|
35
|
+
"(port #{component_name} #{name})"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Input < Io
|
40
|
+
def sexp
|
41
|
+
"(input #{name} #{type})"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Output < Io
|
46
|
+
def sexp
|
47
|
+
"(output #{name} #{type})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Body < Ast
|
52
|
+
def sexp
|
53
|
+
code=Code.new
|
54
|
+
stmts.each{|stmt| code << stmt.sexp}
|
55
|
+
code
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Combinatorial < Ast
|
60
|
+
def sexp
|
61
|
+
code=Code.new
|
62
|
+
label_s=label ? label : "nil"
|
63
|
+
code << "(combinatorial #{label_s}"
|
64
|
+
code.indent=2
|
65
|
+
code << body.sexp
|
66
|
+
code.indent=0
|
67
|
+
code << ")"
|
68
|
+
code
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Sequential < Ast
|
73
|
+
def sexp
|
74
|
+
code=Code.new
|
75
|
+
label_s=label ? label : "nil"
|
76
|
+
code << "(sequential #{label_s}"
|
77
|
+
code.indent=2
|
78
|
+
code << body.sexp
|
79
|
+
code.indent=0
|
80
|
+
code << ")"
|
81
|
+
code
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Assign < Ast
|
86
|
+
def sexp
|
87
|
+
"(assign #{lhs.sexp} #{rhs.sexp})"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class If < Ast
|
92
|
+
def sexp
|
93
|
+
code=Code.new
|
94
|
+
code << "(if #{cond.sexp}"
|
95
|
+
code.indent=2
|
96
|
+
code << "(then"
|
97
|
+
code.indent=4
|
98
|
+
code << self.then.sexp
|
99
|
+
code.indent=2
|
100
|
+
code << ")"
|
101
|
+
if self.else
|
102
|
+
code << "(else"
|
103
|
+
code.indent=4
|
104
|
+
code << self.else.sexp
|
105
|
+
code.indent=2
|
106
|
+
code << ")"
|
107
|
+
end
|
108
|
+
code.indent=0
|
109
|
+
code << ")"
|
110
|
+
code
|
111
|
+
end
|
112
|
+
end
|
113
|
+
#===============================
|
114
|
+
class Component < Ast
|
115
|
+
def sexp
|
116
|
+
"(component #{name} #{type})"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Connect < Ast
|
121
|
+
def sexp
|
122
|
+
"(connect #{source.sexp} #{sink.sexp})"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
#================================
|
127
|
+
class Binary < Expression
|
128
|
+
def sexp
|
129
|
+
"(#{op} #{lhs.sexp} #{rhs.sexp})"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Var < Term
|
134
|
+
def sexp
|
135
|
+
name
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class Const < Expression
|
140
|
+
def sexp
|
141
|
+
value
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/lib/sexpir/code.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
class Code
|
2
|
+
|
3
|
+
attr_accessor :indent,:lines
|
4
|
+
|
5
|
+
def initialize str=nil
|
6
|
+
@lines=[]
|
7
|
+
(@lines << str) if str
|
8
|
+
@indent=0
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(thing)
|
12
|
+
if (code=thing).is_a? Code
|
13
|
+
code.lines.each do |line|
|
14
|
+
@lines << " "*@indent+line.to_s
|
15
|
+
end
|
16
|
+
elsif thing.is_a? Array
|
17
|
+
thing.each do |kode|
|
18
|
+
@lines << kode
|
19
|
+
end
|
20
|
+
elsif thing.nil?
|
21
|
+
else
|
22
|
+
@lines << " "*@indent+thing.to_s
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def finalize
|
27
|
+
return @lines.join("\n") if @lines.any?
|
28
|
+
""
|
29
|
+
end
|
30
|
+
|
31
|
+
def newline
|
32
|
+
@lines << " "
|
33
|
+
end
|
34
|
+
|
35
|
+
def save_as filename,verbose=true,sep="\n"
|
36
|
+
str=self.finalize
|
37
|
+
File.open(filename,'w'){|f| f.puts(str)}
|
38
|
+
return filename
|
39
|
+
end
|
40
|
+
|
41
|
+
def size
|
42
|
+
@lines.size
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'sxp'
|
2
|
+
|
3
|
+
require_relative 'version'
|
4
|
+
require_relative 'ast'
|
5
|
+
require_relative 'parser'
|
6
|
+
require_relative 'printer'
|
7
|
+
require_relative 'checker'
|
8
|
+
require_relative 'transformer'
|
9
|
+
require_relative 'ruby_rtl_generator'
|
10
|
+
|
11
|
+
module Sexpir
|
12
|
+
|
13
|
+
class Compiler
|
14
|
+
|
15
|
+
include Log
|
16
|
+
attr_accessor :options
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
end
|
20
|
+
|
21
|
+
def header
|
22
|
+
log "Sexpir compiler - version #{VERSION}"
|
23
|
+
#log "author : jean-christophe.le_lann@ensta-bretagne.fr"
|
24
|
+
end
|
25
|
+
|
26
|
+
def compile sexpfile
|
27
|
+
header
|
28
|
+
circuit=Parser.new.parse(sexpfile)
|
29
|
+
Printer.new.print(circuit)
|
30
|
+
Checker.new.check(circuit)
|
31
|
+
RubyRTLGenerator.new.generate(circuit)
|
32
|
+
end
|
33
|
+
|
34
|
+
def close
|
35
|
+
log "[+] closing : log is #{$log.inspect}"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/sexpir/graph.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require_relative "code"
|
2
|
+
|
3
|
+
module GRAPH
|
4
|
+
|
5
|
+
class Graph
|
6
|
+
|
7
|
+
attr_accessor :name,:nodes
|
8
|
+
def initialize name
|
9
|
+
@name=name
|
10
|
+
@nodes=[]
|
11
|
+
end
|
12
|
+
|
13
|
+
def << node
|
14
|
+
@nodes << node
|
15
|
+
end
|
16
|
+
|
17
|
+
def size
|
18
|
+
@nodes.size
|
19
|
+
end
|
20
|
+
|
21
|
+
def include? node
|
22
|
+
@nodes.include? node
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect source,sink,infos={}
|
26
|
+
raise "source nil" unless source
|
27
|
+
raise "sink nil" unless sink
|
28
|
+
@nodes << source unless @nodes.include? source
|
29
|
+
@nodes << sink unless @nodes.include? sink
|
30
|
+
source.to(sink,infos)
|
31
|
+
end
|
32
|
+
|
33
|
+
def connected? source,sink
|
34
|
+
source.succ_edges.select{|edge| edge.sink==sink}.any?
|
35
|
+
end
|
36
|
+
|
37
|
+
def unconnect source,sink
|
38
|
+
source.succ_edges.delete_if{|edge| edge.sink==sink}
|
39
|
+
sink.pred_edges.delete_if{|edge| edge.source==source}
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete node
|
43
|
+
node.preds.each{|pred| unconnect(pred,node)}
|
44
|
+
node.succs.each{|succ| unconnect(node,succ)}
|
45
|
+
@nodes.delete(node)
|
46
|
+
end
|
47
|
+
|
48
|
+
def each_node &block
|
49
|
+
@nodes.each(&block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def each_edge &block
|
53
|
+
edges.each(&block)
|
54
|
+
end
|
55
|
+
|
56
|
+
def edges
|
57
|
+
@nodes.collect{|n| n.succ_edges}.flatten
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_dot
|
61
|
+
dot_code=Code.new
|
62
|
+
dot_code << "digraph #{name} {"
|
63
|
+
dot_code.indent=2
|
64
|
+
|
65
|
+
each_node do |node|
|
66
|
+
dot_code << "#{node.object_id};"
|
67
|
+
end
|
68
|
+
each_edge do |edge|
|
69
|
+
label=edge.infos.to_s
|
70
|
+
dot_code << "#{edge.object_id} -> #{edge.object_id} [label=\"#{label}\"];"
|
71
|
+
end
|
72
|
+
dot_code.indent=0
|
73
|
+
dot_code << "}"
|
74
|
+
return dot_code
|
75
|
+
end
|
76
|
+
|
77
|
+
def save_as dotfilename
|
78
|
+
dot_code||=to_dot()
|
79
|
+
dot_code.save_as dotfilename
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Node
|
84
|
+
attr_accessor :infos
|
85
|
+
attr_accessor :succ_edges,:pred_edges
|
86
|
+
|
87
|
+
def initialize infos={}
|
88
|
+
@infos=infos
|
89
|
+
@succ_edges=[]
|
90
|
+
@pred_edges=[]
|
91
|
+
end
|
92
|
+
|
93
|
+
def to node,infos={}
|
94
|
+
e=Edge.new(self,node,infos)
|
95
|
+
@succ_edges << e
|
96
|
+
node.pred_edges << e
|
97
|
+
end
|
98
|
+
|
99
|
+
def succs
|
100
|
+
@succ_edges.collect{|edge| edge.sink}
|
101
|
+
end
|
102
|
+
|
103
|
+
def preds
|
104
|
+
@pred_edges.collect{|edge| edge.source}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Edge
|
109
|
+
attr_accessor :name,:source,:sink,:infos
|
110
|
+
def initialize source,sink,infos={}
|
111
|
+
@infos=infos
|
112
|
+
@source=source
|
113
|
+
@sink=sink
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/lib/sexpir/log.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Log
|
2
|
+
|
3
|
+
def indent v,str
|
4
|
+
@indent=v
|
5
|
+
puts " "*@indent+str.to_s
|
6
|
+
end
|
7
|
+
|
8
|
+
def log msg
|
9
|
+
puts msg
|
10
|
+
#log_dbg msg
|
11
|
+
end
|
12
|
+
|
13
|
+
def log_dbg msg
|
14
|
+
puts msg
|
15
|
+
#$log.puts msg
|
16
|
+
end
|
17
|
+
|
18
|
+
def step
|
19
|
+
log "press any key"
|
20
|
+
key=$stdin.gets
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
require 'sxp'
|
2
|
+
require_relative 'ast'
|
3
|
+
require_relative 'log'
|
4
|
+
|
5
|
+
module Sexpir
|
6
|
+
|
7
|
+
class Parser
|
8
|
+
|
9
|
+
include Log
|
10
|
+
|
11
|
+
attr_accessor :options
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@type_table={}
|
15
|
+
@var_table ={}
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse sexpfile
|
19
|
+
log "[+] parsing '#{sexpfile}'"
|
20
|
+
sexp=SXP.read IO.read(sexpfile)
|
21
|
+
ast=objectify(sexp)
|
22
|
+
end
|
23
|
+
|
24
|
+
def objectify sexp
|
25
|
+
circ=Circuit.new
|
26
|
+
sexp.shift
|
27
|
+
circ.name=sexp.shift
|
28
|
+
while sexp.any?
|
29
|
+
s=sexp.shift
|
30
|
+
case s.first
|
31
|
+
when :signal
|
32
|
+
circ.signals << parse_signal(s)
|
33
|
+
when :input
|
34
|
+
circ.inputs << parse_input(s)
|
35
|
+
when :output
|
36
|
+
circ.outputs << parse_output(s)
|
37
|
+
when :assign
|
38
|
+
circ.body << parse_assign(s)
|
39
|
+
when :component
|
40
|
+
circ.body << parse_component(s)
|
41
|
+
when :connect
|
42
|
+
circ.body << parse_connect(s)
|
43
|
+
when :comb,:combinatorial
|
44
|
+
circ.body << parse_comb(s)
|
45
|
+
when :seq,:sync,:sequential
|
46
|
+
circ.body << parse_seq(s)
|
47
|
+
else
|
48
|
+
raise "unknown car #{s}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
circ
|
52
|
+
end
|
53
|
+
|
54
|
+
def expect sexp,klass,value=nil
|
55
|
+
unless (kind=sexp.shift).is_a? klass
|
56
|
+
log "ERROR : expecting a #{klass}. Got a #{kind}."
|
57
|
+
raise "Syntax error"
|
58
|
+
end
|
59
|
+
if value
|
60
|
+
unless value==kind
|
61
|
+
log "ERROR : expecting value '#{value}'. Got '#{kind}'"
|
62
|
+
raise "Syntax error"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
return kind
|
66
|
+
end
|
67
|
+
|
68
|
+
#======== IO & Signals ===========
|
69
|
+
def parse_signal sexp
|
70
|
+
sig=Signal.new
|
71
|
+
sexp.shift
|
72
|
+
sig.name=sexp.shift
|
73
|
+
sig.type=sexp.shift
|
74
|
+
sig
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_input sexp
|
78
|
+
input=Input.new
|
79
|
+
sexp.shift
|
80
|
+
input.name=sexp.shift
|
81
|
+
input.type=sexp.shift
|
82
|
+
input
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_output sexp
|
86
|
+
output=Output.new
|
87
|
+
sexp.shift
|
88
|
+
output.name=sexp.shift
|
89
|
+
output.type=sexp.shift
|
90
|
+
output
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
# ===== components stuff =====
|
96
|
+
def parse_component sexp
|
97
|
+
comp=Component.new
|
98
|
+
sexp.shift
|
99
|
+
comp.name=sexp.shift
|
100
|
+
comp.type=sexp.shift
|
101
|
+
comp
|
102
|
+
end
|
103
|
+
|
104
|
+
def parse_connect sexp
|
105
|
+
con=Connect.new
|
106
|
+
sexp.shift
|
107
|
+
con.source=parse_port(sexp.shift)
|
108
|
+
con.sink =parse_port(sexp.shift)
|
109
|
+
con
|
110
|
+
end
|
111
|
+
|
112
|
+
def parse_port sexp
|
113
|
+
case sexp
|
114
|
+
when Array
|
115
|
+
port=Port.new
|
116
|
+
sexp.shift
|
117
|
+
port.component_name=sexp.shift
|
118
|
+
port.name=sexp.shift
|
119
|
+
return port
|
120
|
+
else
|
121
|
+
return sexp
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# statements
|
126
|
+
def parse_statement sexp
|
127
|
+
case sexp.first
|
128
|
+
when :if
|
129
|
+
parse_if(sexp)
|
130
|
+
when :assign
|
131
|
+
parse_assign(sexp)
|
132
|
+
else
|
133
|
+
raise "unknow statement starting with : #{sexp.first}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def parse_comb sexp
|
138
|
+
comb=Combinatorial.new
|
139
|
+
sexp.shift
|
140
|
+
label=sexp.shift
|
141
|
+
comb.body=parse_body(sexp)
|
142
|
+
comb
|
143
|
+
end
|
144
|
+
|
145
|
+
def parse_body sexp
|
146
|
+
body=Body.new
|
147
|
+
while sexp.any?
|
148
|
+
body << parse_statement(sexp.shift)
|
149
|
+
end
|
150
|
+
body
|
151
|
+
end
|
152
|
+
|
153
|
+
def parse_if sexp
|
154
|
+
if_=If.new
|
155
|
+
sexp.shift
|
156
|
+
if_.cond = parse_expression(sexp.shift)
|
157
|
+
while sexp.any?
|
158
|
+
case sexp.first.first
|
159
|
+
when :then
|
160
|
+
if_.then = parse_then(sexp.shift)
|
161
|
+
when :elsif
|
162
|
+
if_.elsifs << parse_elsif(sexp.shift)
|
163
|
+
when :else
|
164
|
+
if_.else = parse_else(sexp.shift)
|
165
|
+
else
|
166
|
+
raise "error parsing 'if' : #{sexp.first.first}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
if_
|
170
|
+
end
|
171
|
+
|
172
|
+
def parse_then sexp
|
173
|
+
sexp.shift
|
174
|
+
body=parse_body(sexp)
|
175
|
+
body
|
176
|
+
end
|
177
|
+
|
178
|
+
def parse_elsif sexp
|
179
|
+
sexp.shift
|
180
|
+
body=parse_body(sexp)
|
181
|
+
body
|
182
|
+
end
|
183
|
+
|
184
|
+
def parse_else sexp
|
185
|
+
sexp.shift
|
186
|
+
body=parse_body(sexp)
|
187
|
+
body
|
188
|
+
end
|
189
|
+
|
190
|
+
def parse_assign sexp
|
191
|
+
assign=Assign.new
|
192
|
+
sexp.shift
|
193
|
+
assign.lhs=parse_expression(sexp.shift)
|
194
|
+
assign.rhs=parse_expression(sexp.shift)
|
195
|
+
assign
|
196
|
+
end
|
197
|
+
|
198
|
+
# expressions
|
199
|
+
|
200
|
+
def parse_expression sexp
|
201
|
+
case sexp
|
202
|
+
when Array
|
203
|
+
case sexp.size
|
204
|
+
when 1
|
205
|
+
case sexp
|
206
|
+
when Symbol
|
207
|
+
return Var.new(sexp)
|
208
|
+
when Integer
|
209
|
+
return Const.new(sexp)
|
210
|
+
else
|
211
|
+
raise "unknown expression '#{sexp}'"
|
212
|
+
end
|
213
|
+
when 2
|
214
|
+
ret=parse_unary(sexp)
|
215
|
+
when 3
|
216
|
+
ret=parse_binary(sexp)
|
217
|
+
else
|
218
|
+
raise "unknown expression '#{sexp}'(size = '#{sexp.size}')"
|
219
|
+
end
|
220
|
+
when Symbol
|
221
|
+
ret=Var.new(sexp)
|
222
|
+
when Integer
|
223
|
+
ret=Const.new(sexp)
|
224
|
+
else
|
225
|
+
raise "unknown expression '#{sexp}'"
|
226
|
+
end
|
227
|
+
ret
|
228
|
+
end
|
229
|
+
|
230
|
+
OP_TRANSLATE={
|
231
|
+
"gt" => ">",
|
232
|
+
"lt" => "<",
|
233
|
+
"eq" => "=",
|
234
|
+
"neq"=> "/=",
|
235
|
+
"gte"=> ">=",
|
236
|
+
"lte"=> "<=",
|
237
|
+
}
|
238
|
+
|
239
|
+
NORMALIZED_OP={
|
240
|
+
"*" => "mul",
|
241
|
+
"+" => "add",
|
242
|
+
"-" => "sub",
|
243
|
+
"/" => "div",
|
244
|
+
"=" => "eq",
|
245
|
+
">=" => "gte",
|
246
|
+
"<=" => "lte",
|
247
|
+
"/=" => "neq"
|
248
|
+
}
|
249
|
+
|
250
|
+
def parse_binary sexp
|
251
|
+
case sexp.first
|
252
|
+
when :mread
|
253
|
+
ret=MRead.new
|
254
|
+
sexp.shift
|
255
|
+
ret.mem = Mem.new(sexp.shift) # memory name
|
256
|
+
ret.addr= parse_expression(sexp.shift)
|
257
|
+
else
|
258
|
+
ret=Binary.new
|
259
|
+
ret.op=sexp.shift
|
260
|
+
#ret.op=OP_TRANSLATE[ret.op.to_s]||ret.op.to_s
|
261
|
+
ret.op=NORMALIZED_OP[ret.op.to_s] || ret.op.to_s
|
262
|
+
ret.lhs=parse_expression(sexp.shift)
|
263
|
+
ret.rhs=parse_expression(sexp.shift)
|
264
|
+
end
|
265
|
+
ret
|
266
|
+
end
|
267
|
+
|
268
|
+
def parse_unary sexp
|
269
|
+
ret=Unary.new
|
270
|
+
ret.op=sexp.shift
|
271
|
+
ret
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require_relative 'log'
|
2
|
+
class Symbol
|
3
|
+
def accept visitor,args=nil
|
4
|
+
self
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Sexpir
|
9
|
+
class RubyRTLGenerator < Visitor
|
10
|
+
include Log
|
11
|
+
def generate circuit
|
12
|
+
log "[+] generating RubyRTL '#{circuit.name}'"
|
13
|
+
code=circuit.accept(self)
|
14
|
+
puts code.finalize
|
15
|
+
end
|
16
|
+
|
17
|
+
def visitCircuit circuit,args=nil
|
18
|
+
code=Code.new
|
19
|
+
code << "require 'ruby_rtl'"
|
20
|
+
code << "class #{circuit.name.capitalize} < Circuit"
|
21
|
+
code.indent=2
|
22
|
+
circuit.inputs.each{|input| code << input.accept(self)}
|
23
|
+
circuit.outputs.each{|output| code << output.accept(self)}
|
24
|
+
circuit.signals.each{|signal| code << signal.accept(self)}
|
25
|
+
code.newline
|
26
|
+
code << circuit.body.accept(self)
|
27
|
+
code.indent=0
|
28
|
+
code << "end"
|
29
|
+
code
|
30
|
+
end
|
31
|
+
|
32
|
+
def gen_type(type)
|
33
|
+
case num=type
|
34
|
+
when Integer
|
35
|
+
case num
|
36
|
+
when 1
|
37
|
+
return "bit"
|
38
|
+
else
|
39
|
+
return "bv#{num}"
|
40
|
+
end
|
41
|
+
else
|
42
|
+
return type
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def visitSignal sig,args=nil
|
47
|
+
sig.name
|
48
|
+
type=gen_type(sig.type)
|
49
|
+
"wire :#{sig.name} => :#{type}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def visitIo io,args=nil
|
53
|
+
io.name
|
54
|
+
io.type
|
55
|
+
end
|
56
|
+
|
57
|
+
def visitPort port,args=nil
|
58
|
+
comp=port.component_name
|
59
|
+
name=port.name
|
60
|
+
"#{comp}.#{name}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def visitInput input,args=nil
|
64
|
+
input.name
|
65
|
+
input.type
|
66
|
+
"input :#{input.name} => :#{input.type}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def visitOutput output,args=nil
|
70
|
+
output.name
|
71
|
+
output.type
|
72
|
+
"output :#{output.name} => :#{output.type}"
|
73
|
+
end
|
74
|
+
|
75
|
+
# === statements ===
|
76
|
+
def visitBody body,args=nil
|
77
|
+
code=Code.new
|
78
|
+
body.stmts.each{|stmt| code << stmt.accept(self)}
|
79
|
+
code
|
80
|
+
end
|
81
|
+
|
82
|
+
def visitCombinatorial comb,args=nil
|
83
|
+
code=Code.new
|
84
|
+
code << "combinatorial(#{comb.label}){"
|
85
|
+
code.indent=2
|
86
|
+
code << comb.body.accept(self)
|
87
|
+
code.indent=0
|
88
|
+
code << "}"
|
89
|
+
code
|
90
|
+
end
|
91
|
+
def visitSequential seq,args=nil
|
92
|
+
code=Code.new
|
93
|
+
code << "sequential(#{seq.label}){"
|
94
|
+
code.indent=2
|
95
|
+
code << seq.body.accept(self)
|
96
|
+
code.indent=0
|
97
|
+
code << "}"
|
98
|
+
code
|
99
|
+
end
|
100
|
+
|
101
|
+
def visitAssign assign,args=nil
|
102
|
+
lhs=assign.lhs.accept(self)
|
103
|
+
rhs=assign.rhs.accept(self)
|
104
|
+
"assign(#{lhs} <= #{rhs})"
|
105
|
+
end
|
106
|
+
|
107
|
+
def visitIf if_,args=nil
|
108
|
+
cond=if_.cond.accept(self)
|
109
|
+
code=Code.new
|
110
|
+
code << "if (#{cond}"
|
111
|
+
code.indent=2
|
112
|
+
code << if_.then.accept(self)
|
113
|
+
if if_.else
|
114
|
+
code.indent=0
|
115
|
+
code << "else"
|
116
|
+
code.indent=2
|
117
|
+
code << if_.else.accept(self)
|
118
|
+
code.indent=0
|
119
|
+
end
|
120
|
+
code.indent=0
|
121
|
+
code << "end"
|
122
|
+
code
|
123
|
+
end
|
124
|
+
|
125
|
+
# === component stuff ====
|
126
|
+
def visitComponent component,args=nil
|
127
|
+
name=component.name
|
128
|
+
type=component.type
|
129
|
+
"component :#{name} => #{type.capitalize}"
|
130
|
+
end
|
131
|
+
|
132
|
+
def visitConnect connect,args=nil
|
133
|
+
source=connect.source.accept(self)
|
134
|
+
sink=connect.sink.accept(self)
|
135
|
+
"connect #{source} => #{sink}"
|
136
|
+
end
|
137
|
+
|
138
|
+
#===========
|
139
|
+
def visitExpression expr,args=nil
|
140
|
+
end
|
141
|
+
|
142
|
+
SEXPIR_TO_RUBY_OP={
|
143
|
+
"and" => "&",
|
144
|
+
"or" => "|",
|
145
|
+
"xor" => "^",
|
146
|
+
"not" => "!",
|
147
|
+
}
|
148
|
+
def visitBinary binary,args=nil
|
149
|
+
lhs=binary.lhs.accept(self)
|
150
|
+
rhs=binary.rhs.accept(self)
|
151
|
+
op=SEXPIR_TO_RUBY_OP[binary.op] || binary.op
|
152
|
+
"(#{lhs} #{op} #{rhs})"
|
153
|
+
end
|
154
|
+
|
155
|
+
def visitTerm term,args=nil
|
156
|
+
term
|
157
|
+
end
|
158
|
+
|
159
|
+
def visitVar var,args=nil
|
160
|
+
var.name
|
161
|
+
end
|
162
|
+
|
163
|
+
def visitConst const,args=nil
|
164
|
+
const.value
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
require_relative "compiler"
|
4
|
+
|
5
|
+
module Sexpir
|
6
|
+
|
7
|
+
class Runner
|
8
|
+
|
9
|
+
def self.run *arguments
|
10
|
+
new.run(arguments)
|
11
|
+
end
|
12
|
+
|
13
|
+
def run arguments
|
14
|
+
compiler=Compiler.new
|
15
|
+
compiler.options = args = parse_options(arguments)
|
16
|
+
$options=compiler.options
|
17
|
+
|
18
|
+
if filename=args[:filename]
|
19
|
+
compiler.compile filename
|
20
|
+
else
|
21
|
+
puts "need an sexpir file : sexpir <file.sexp>"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def parse_options(arguments)
|
27
|
+
|
28
|
+
size=arguments.size
|
29
|
+
|
30
|
+
parser = OptionParser.new
|
31
|
+
|
32
|
+
options = {}
|
33
|
+
|
34
|
+
parser.on("-h", "--help", "Show help message") do
|
35
|
+
puts parser
|
36
|
+
exit(true)
|
37
|
+
end
|
38
|
+
|
39
|
+
parser.on("-v", "--version", "Show version number") do
|
40
|
+
puts VERSION
|
41
|
+
exit(true)
|
42
|
+
end
|
43
|
+
|
44
|
+
parser.on("--verbose", "verbose mode") do
|
45
|
+
options[:verbose]=true
|
46
|
+
$VERBOSE=true
|
47
|
+
end
|
48
|
+
|
49
|
+
parser.parse!(arguments)
|
50
|
+
|
51
|
+
options[:filename]=arguments.shift
|
52
|
+
|
53
|
+
if arguments.any?
|
54
|
+
puts "WARNING : superfluous arguments : #{arguments}"
|
55
|
+
end
|
56
|
+
|
57
|
+
if size==0
|
58
|
+
puts parser
|
59
|
+
end
|
60
|
+
|
61
|
+
options
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Sexpir
|
2
|
+
|
3
|
+
class Visitor
|
4
|
+
|
5
|
+
def visitCircuit circuit,args=nil
|
6
|
+
circuit.name
|
7
|
+
circuit.inputs.each{|input| input.accept(self)}
|
8
|
+
circuit.outputs.each{|output| output.accept(self)}
|
9
|
+
circuit.signals.each{|signal| signal.accept(self)}
|
10
|
+
circuit.body.accept(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def visitSignal sig,args=nil
|
14
|
+
signal.name
|
15
|
+
signal.type
|
16
|
+
end
|
17
|
+
|
18
|
+
def visitIo io,args=nil
|
19
|
+
io.name
|
20
|
+
io.type
|
21
|
+
end
|
22
|
+
|
23
|
+
def visitPort port,args=nil
|
24
|
+
port.name
|
25
|
+
port.type
|
26
|
+
end
|
27
|
+
|
28
|
+
def visitInput input,args=nil
|
29
|
+
input.name
|
30
|
+
input.type
|
31
|
+
end
|
32
|
+
|
33
|
+
def visitOutput output,args=nil
|
34
|
+
output.name
|
35
|
+
output.type
|
36
|
+
end
|
37
|
+
|
38
|
+
def visitBody body,args=nil
|
39
|
+
body.stmts.each{|stmt| stmt.accept(self)}
|
40
|
+
end
|
41
|
+
|
42
|
+
def visitAssign assign,args=nil
|
43
|
+
assign.lhs.accept(self)
|
44
|
+
assign.rhs.accept(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
def visitComponent component,args=nil
|
48
|
+
name=component.name
|
49
|
+
type=component.type
|
50
|
+
"component #{name} => #{type}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def visitConnect connect,args=nil
|
54
|
+
connect.source
|
55
|
+
connect.sink
|
56
|
+
end
|
57
|
+
|
58
|
+
#===========
|
59
|
+
def visitExpression expr,args=nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def visitBinary binary,args=nil
|
63
|
+
binary.lhs.accept(self)
|
64
|
+
binary.rhs.accept(self)
|
65
|
+
end
|
66
|
+
|
67
|
+
def visitTerm term,args=nil
|
68
|
+
term
|
69
|
+
end
|
70
|
+
|
71
|
+
def visitVar var,args=nil
|
72
|
+
var.name
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
data/lib/sexpir.rb
ADDED
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sexpir
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jean-Christophe Le Lann
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: distribution
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.7.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.7.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: colorize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.8.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.8.1
|
41
|
+
description: sexpir is a sexp-based interchange format for RTL design
|
42
|
+
email: lelannje@ensta-bretagne.fr
|
43
|
+
executables:
|
44
|
+
- sexpir
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- bin/sexpir
|
49
|
+
- lib/sexpir.rb
|
50
|
+
- lib/sexpir/ast.rb
|
51
|
+
- lib/sexpir/ast_sexp.rb
|
52
|
+
- lib/sexpir/checker.rb
|
53
|
+
- lib/sexpir/code.rb
|
54
|
+
- lib/sexpir/code_generator.rb
|
55
|
+
- lib/sexpir/compiler.rb
|
56
|
+
- lib/sexpir/graph.rb
|
57
|
+
- lib/sexpir/log.rb
|
58
|
+
- lib/sexpir/parser.rb
|
59
|
+
- lib/sexpir/printer.rb
|
60
|
+
- lib/sexpir/ruby_rtl_generator.rb
|
61
|
+
- lib/sexpir/runner.rb
|
62
|
+
- lib/sexpir/transformer.rb
|
63
|
+
- lib/sexpir/version.rb
|
64
|
+
- lib/sexpir/visitor.rb
|
65
|
+
homepage: https://github.com/JC-LL/sexpir
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message: Thanks for installing ! Homepage :https://github.com/JC-LL/sexpir
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.0.0
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubygems_version: 3.0.6
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: interchange format for RTL designs
|
88
|
+
test_files: []
|