go_study/fabric-main/vendor/github.com/mmcloughlin/addchain/acc/translate.go

140 lines
2.5 KiB
Go

package acc
import (
"fmt"
"github.com/mmcloughlin/addchain/acc/ast"
"github.com/mmcloughlin/addchain/acc/ir"
"github.com/mmcloughlin/addchain/internal/errutil"
)
// Translate converts an abstract syntax tree to an intermediate representation.
func Translate(c *ast.Chain) (*ir.Program, error) {
s := newstate()
for _, stmt := range c.Statements {
if err := s.statement(stmt); err != nil {
return nil, err
}
}
return s.prog, nil
}
type state struct {
prog *ir.Program
n int
variable map[ast.Identifier]*ir.Operand
}
func newstate() *state {
return &state{
prog: &ir.Program{},
n: 1,
variable: map[ast.Identifier]*ir.Operand{},
}
}
func (s *state) statement(stmt ast.Statement) error {
out, err := s.expr(stmt.Expr)
if err != nil {
return err
}
if err := s.define(stmt.Name, out); err != nil {
return err
}
return nil
}
func (s *state) expr(expr ast.Expr) (*ir.Operand, error) {
switch e := expr.(type) {
case ast.Operand:
return &ir.Operand{Index: int(e)}, nil
case ast.Identifier:
return s.lookup(e)
case ast.Add:
return s.add(e)
case ast.Double:
return s.double(e)
case ast.Shift:
return s.shift(e)
default:
return nil, errutil.UnexpectedType(e)
}
}
func (s *state) add(a ast.Add) (*ir.Operand, error) {
x, err := s.expr(a.X)
if err != nil {
return nil, err
}
y, err := s.expr(a.Y)
if err != nil {
return nil, err
}
if x.Index > y.Index {
x, y = y, x
}
out := ir.Index(s.n)
inst := &ir.Instruction{
Output: out,
Op: ir.Add{X: x, Y: y},
}
s.prog.AddInstruction(inst)
s.n++
return out, nil
}
func (s *state) double(d ast.Double) (*ir.Operand, error) {
x, err := s.expr(d.X)
if err != nil {
return nil, err
}
out := ir.Index(s.n)
inst := &ir.Instruction{
Output: out,
Op: ir.Double{X: x},
}
s.prog.AddInstruction(inst)
s.n++
return out, nil
}
func (s *state) shift(sh ast.Shift) (*ir.Operand, error) {
x, err := s.expr(sh.X)
if err != nil {
return nil, err
}
s.n += int(sh.S)
out := ir.Index(s.n - 1)
inst := &ir.Instruction{
Output: out,
Op: ir.Shift{X: x, S: sh.S},
}
s.prog.AddInstruction(inst)
return out, nil
}
func (s *state) define(name ast.Identifier, op *ir.Operand) error {
if _, found := s.variable[name]; found {
return fmt.Errorf("cannot redefine %q", name)
}
op.Identifier = string(name)
s.variable[name] = op
return nil
}
func (s state) lookup(name ast.Identifier) (*ir.Operand, error) {
operand, ok := s.variable[name]
if !ok {
return nil, fmt.Errorf("variable %q undefined", name)
}
return operand, nil
}