140 lines
2.5 KiB
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
|
|
}
|