Browse Source

Initial import

master
maze 1 year ago
parent
commit
e3b9857140
25 changed files with 8507 additions and 0 deletions
  1. +19
    -0
      bytes.go
  2. +257
    -0
      cmd/ebpfasm/main.go
  3. +80
    -0
      constant.go
  4. +190
    -0
      elf.go
  5. +12
    -0
      elf_test.go
  6. +233
    -0
      instruction.go
  7. +150
    -0
      instruction_test.go
  8. +304
    -0
      internal/parser/Assembler.g4
  9. +195
    -0
      internal/parser/assembler_base_listener.go
  10. +124
    -0
      internal/parser/assembler_base_visitor.go
  11. +450
    -0
      internal/parser/assembler_lexer.go
  12. +183
    -0
      internal/parser/assembler_listener.go
  13. +4313
    -0
      internal/parser/assembler_parser.go
  14. +96
    -0
      internal/parser/assembler_visitor.go
  15. +115
    -0
      op.go
  16. +212
    -0
      op_alu.go
  17. +29
    -0
      op_alu_test.go
  18. +144
    -0
      op_call.go
  19. +24
    -0
      op_call_test.go
  20. +190
    -0
      op_jump.go
  21. +83
    -0
      op_jump_test.go
  22. +257
    -0
      op_load.go
  23. +352
    -0
      op_load_test.go
  24. +454
    -0
      program.go
  25. +41
    -0
      register.go

+ 19
- 0
bytes.go View File

@ -0,0 +1,19 @@
package ebpf
// swapUint32 converts a uint16 to network byte order and back.
func swapUint32(n uint32) uint32 {
return (n&0x000000FF)<<24 | (n&0x0000FF00)<<8 |
(n&0x00FF0000)>>8 | (n&0xFF000000)>>24
}
// swapUint64 converts a uint16 to network byte order and back.
func swapUint64(n uint64) uint64 {
return ((n & 0x00000000000000FF) << 56) |
((n & 0x000000000000FF00) << 40) |
((n & 0x0000000000FF0000) << 24) |
((n & 0x00000000FF000000) << 8) |
((n & 0x000000FF00000000) >> 8) |
((n & 0x0000FF0000000000) >> 24) |
((n & 0x00FF000000000000) >> 40) |
((n & 0xFF00000000000000) >> 56)
}

+ 257
- 0
cmd/ebpfasm/main.go View File

@ -0,0 +1,257 @@
package main
import (
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"text/tabwriter"
"maze.io/ebpf"
cli "github.com/urfave/cli/v2"
)
func main() {
app := cli.App{
Name: "bpfasm",
Usage: "Berkeley Packet Filter assembler/disassembler",
Commands: []*cli.Command{
&cli.Command{
Name: "assemble",
Aliases: []string{"a"},
Usage: "Assemble a BPF ASM file",
ArgsUsage: "<input>",
Action: assemble,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "endian",
Aliases: []string{"e"},
Value: "be",
Usage: "Binary byte order (be, le)",
},
&cli.StringFlag{
Name: "format",
Aliases: []string{"f"},
Value: "bin",
Usage: "Output format (bin, asm, c)",
},
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "Output file",
DefaultText: "use stdout",
},
},
},
&cli.Command{
Name: "disassemble",
Aliases: []string{"d"},
Usage: "Disassemble a BPF binary file",
ArgsUsage: "<input>",
Action: disassemble,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "endian",
Aliases: []string{"e"},
Value: "le",
Usage: "Binary byte order (be, le)",
},
&cli.StringFlag{
Name: "format",
Aliases: []string{"f"},
Value: "asm",
Usage: "Output format (asm, c)",
},
&cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "Output file",
DefaultText: "use stdout",
},
},
},
},
}
app.Run(os.Args)
}
func assemble(ctx *cli.Context) error {
if ctx.NArg() != 1 {
return cli.ShowCommandHelp(ctx, "assemble")
}
var asm assembler
switch ctx.String("format") {
case "bin", "binary":
switch ctx.String("endian") {
case "be":
asm = formatBinary(binary.BigEndian)
case "le":
asm = formatBinary(binary.LittleEndian)
default:
fmt.Fprintf(os.Stderr, "invalid byte order %q\n", ctx.String("endian"))
os.Exit(1)
}
case "asm":
asm = formatAssembly
case "c", "C":
asm = formatC
default:
fmt.Fprintf(os.Stderr, "invalid output format %q\n", ctx.String("format"))
os.Exit(1)
}
sourcePath := ctx.Args().First()
sourceBytes, err := ioutil.ReadFile(sourcePath)
if err != nil {
fmt.Fprintf(os.Stderr, "error reading %s: %v\n", sourcePath, err)
os.Exit(1)
}
program, err := ebpf.Assemble(string(sourceBytes))
if err != nil {
fmt.Fprintf(os.Stderr, "error assembling %s: %v\n", sourcePath, err)
os.Exit(1)
}
/*
else if err = program.Verify(); err != nil {
fmt.Fprintf(os.Stderr, "error verifying program: %v\n", err)
os.Exit(1)
}
*/
var output io.WriteCloser
switch outputFile := ctx.String("output"); outputFile {
case "", "-":
if f := ctx.String("format"); f == "bin" || f == "binary" {
fmt.Fprintf(os.Stderr, "writing to %s.bin\n", sourcePath)
if output, err = os.Create(sourcePath + ".bin"); err != nil {
fmt.Fprintf(os.Stderr, "error opening output file: %v\n", err)
os.Exit(1)
}
} else {
output = os.Stdout
}
default:
fmt.Fprintf(os.Stderr, "writing to %s\n", outputFile)
if output, err = os.Create(outputFile); err != nil {
fmt.Fprintf(os.Stderr, "error opening output file: %v\n", err)
os.Exit(1)
}
}
defer output.Close()
return asm(output, program)
}
func disassemble(ctx *cli.Context) error {
if ctx.NArg() != 1 {
return cli.ShowCommandHelp(ctx, "disassemble")
}
var (
sourcePath = ctx.Args().First()
byteOrder binary.ByteOrder
asm assembler
)
switch ctx.String("endian") {
case "be":
byteOrder = binary.BigEndian
case "le":
byteOrder = binary.LittleEndian
default:
fmt.Fprintf(os.Stderr, "invalid byte order %q\n", ctx.String("endian"))
os.Exit(1)
}
switch ctx.String("format") {
case "asm":
asm = formatAssembly
case "c", "C":
asm = formatC
default:
fmt.Fprintf(os.Stderr, "invalid output format %q\n", ctx.String("format"))
os.Exit(1)
}
sourceFile, err := os.Open(sourcePath)
if err != nil {
fmt.Fprintf(os.Stderr, "error opening %s: %v\n", sourcePath, err)
os.Exit(1)
}
defer sourceFile.Close()
program, err := ebpf.DisassembleReaderOrder(sourceFile, byteOrder)
if err != nil {
fmt.Fprintf(os.Stderr, "error disassembling: %v\n", err)
os.Exit(2)
}
var output io.WriteCloser
switch outputFile := ctx.String("output"); outputFile {
case "", "-":
output = os.Stdout
default:
fmt.Fprintf(os.Stderr, "writing to %s\n", outputFile)
if output, err = os.Create(outputFile); err != nil {
fmt.Fprintf(os.Stderr, "error opening output file: %v\n", err)
os.Exit(1)
}
}
defer output.Close()
return asm(output, program)
}
type assembler func(io.Writer, ebpf.Program) error
func formatBinary(byteOrder binary.ByteOrder) assembler {
return func(w io.Writer, program ebpf.Program) error {
raw, err := program.Assemble()
if err != nil {
return err
}
for _, ins := range raw {
if err := binary.Write(w, byteOrder, ins); err != nil {
return err
}
}
return nil
}
}
func formatAssembly(w io.Writer, program ebpf.Program) error {
t := tabwriter.NewWriter(w, 0, 0, 4, ' ', tabwriter.TabIndent)
for _, ins := range program {
if s, ok := ins.(fmt.Stringer); ok {
fmt.Fprintln(t, strings.Replace(s.String(), " ", "\t", -1))
}
}
return t.Flush()
}
func formatC(w io.Writer, program ebpf.Program) error {
raw, err := program.Assemble()
if err != nil {
return err
}
l := len(raw) - 1
for i, ri := range raw {
var comma string
if i < l {
comma = ","
}
if _, err := fmt.Fprintf(w, "{ %#02x, %s, %s, %#04x, %#08x }%s // %s\n",
ri.Op, ri.Dst, ri.Src, ri.Offset, ri.Immediate, comma, ri.Disassemble()); err != nil {
return err
}
}
return nil
}

+ 80
- 0
constant.go View File

@ -0,0 +1,80 @@
package ebpf
const (
opMaskClass = 0x07
opMaskSize = 0x18
opMaskMode = 0xe0
opMaskSource = 0x08
opMaskOperand = 0xf0
)
// Operand classes.
const (
opClassLoad Opcode = iota // 0x00
opClassLoadX // 0x01
opClassStore // 0x02
opClassStoreX // 0x03
opClassALU // 0x04
opClassJump // 0x05
_ // 0x06 (reserved for future use)
opClassALU64 // 0x07
)
// For load and store instructions:
// +------------+--------+------------+
// | 3 bits | 2 bits | 3 bits |
// | mode | size | insn class |
// +------------+--------+------------+
// (MSB) (LSB)
// Size modifiers.
const (
sizeWord Opcode = iota << 3 // 0x00
sizeHalfWord // 0x08
sizeByte // 0x10
sizeDoubleWord // 0x18
)
// Mode modifiers.
const (
modeImmediate Opcode = iota << 5 // 0x00
modeAbsolute // 0x20
modeIndirect // 0x40
modeMemory // 0x60
_ // 0x80
_ // 0xa0
modeExclusiveAdd // 0xc0
)
// For arithmetic (BPF_ALU/BPF_ALU64) and jump (BPF_JMP) instructions:
// +----------------+----+------------+
// | 4 bits |1 b.| 3 bits |
// | operation code | src| insn class |
// +----------------+----+------------+
// (MSB) (LSB)
// Source modifiers.
const (
aluSourceImmediate Opcode = iota << 3
aluSourceX
jumpSourceImmediate = aluSourceImmediate
jumpSourceX = aluSourceX
)
// Jumps
const (
jumpOp Opcode = iota << 4 // 0x00
jumpOpEqual // 0x10
jumpOpGreater // 0x20
jumpOpGreaterOrEqual // 0x30
jumpOpSet // 0x40
jumpOpNotEqual // 0x50
jumpOpSignedGreater // 0x60
jumpOpSignedGreaterOrEqual // 0x70
jumpOpCall // 0x80
jumpOpExit // 0x90
jumpOpLess // 0xa0
jumpOpLessOrEqual // 0xb0
jumpOpSignedLess // 0xc0
jumpOpSignedLessOrEqual // 0xd0
)

+ 190
- 0
elf.go View File

@ -0,0 +1,190 @@
package ebpf
import (
"bytes"
"debug/elf"
"encoding/binary"
"errors"
"fmt"
"log"
)
const (
maxELFSections = 32
)
var (
errELFClass = errors.New("ebpf: invalid ELF class")
errELFByteOrder = errors.New("ebpf: invalid ELF byte order")
errELFVersion = errors.New("ebpf: invalid ELF version")
errELFOSABI = errors.New("ebpf: invalid ELF OS ABI")
errELFRelocatable = errors.New("ebpf: invalid ELF not relocatable")
errTextSection = errors.New("ebpf: text section not found")
)
func loadELF(name string) error {
file, err := elf.Open(name)
if err != nil {
return err
}
if file.Class != elf.ELFCLASS64 {
return errELFClass
}
if file.Data != elf.ELFDATA2LSB {
return errELFByteOrder
}
if file.Version != 1 {
return errELFVersion
}
if file.OSABI != 0 {
return errELFOSABI
}
if file.Type != elf.ET_REL {
return errELFRelocatable
}
if file.Machine != elf.EM_NONE && file.Machine != elf.EM_BPF {
return fmt.Errorf("ebpf: invalid ELF machine, expected BPF, got %d", file.Machine)
}
if l := len(file.Sections); l > maxELFSections {
return fmt.Errorf("ebpf: %d ELF sections exceeds maximum of %d", l, maxELFSections)
}
var textSection *elf.Section
for _, section := range file.Sections {
if section.Type == elf.SHT_PROGBITS && section.Flags == (elf.SHF_ALLOC|elf.SHF_EXECINSTR) {
textSection = section
break
}
}
if textSection == nil {
return errTextSection
}
var textData = make([]byte, textSection.Size)
data, err := textSection.Data()
if err != nil {
return err
}
copy(textData, data)
/* Process each relocation rel */
for _, rel := range file.Sections {
if rel.Type != elf.SHT_REL {
continue
}
log.Printf("rel: %#+v", rel)
if rel.SectionHeader.Link >= uint32(len(file.Sections)) {
return errors.New("ebpf: bad symbol table section index")
}
symbols, _, err := getSymbols(file, file.Sections[rel.SectionHeader.Link])
log.Printf("symbols: %#+v", symbols)
rs, err := parseRelocations(file, rel)
if err != nil {
return err
}
log.Printf("relocations: %#+v", rs)
for _, r := range rs {
var (
_ = r.Info & 0xffffffff // relType
symIndex = r.Info >> 32
)
if symIndex >= uint64(len(symbols)) {
return errors.New("ebpf: bad symbol index")
}
log.Println("relocate to", symbols[symIndex])
}
}
return nil
}
func parseRelocations(file *elf.File, rel *elf.Section) (rs []elf.Rel64, err error) {
var b []byte
if b, err = rel.Data(); err != nil {
return
}
if l := len(b); l%16 != 0 {
return nil, fmt.Errorf("length of relocation section size %d is not a multiple of 16", l)
}
buf := bytes.NewBuffer(b)
for buf.Len() > 0 {
var r elf.Rel64
if err = binary.Read(buf, file.ByteOrder, &r); err != nil {
return
}
rs = append(rs, r)
}
return
}
func getSymbols(file *elf.File, section *elf.Section) ([]elf.Symbol, []byte, error) {
data, err := section.Data()
if err != nil {
return nil, nil, errors.New("cannot load symbol section")
}
symtab := bytes.NewReader(data)
if symtab.Len()%elf.Sym64Size != 0 {
return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")
}
strdata, err := stringTable(file, section.Link)
if err != nil {
return nil, nil, errors.New("cannot load string table section")
}
// The first entry is all zeros.
var skip [elf.Sym64Size]byte
symtab.Read(skip[:])
symbols := make([]elf.Symbol, symtab.Len()/elf.Sym64Size)
i := 0
var sym elf.Sym64
for symtab.Len() > 0 {
binary.Read(symtab, file.ByteOrder, &sym)
str, _ := getString(strdata, int(sym.Name))
symbols[i].Name = str
symbols[i].Info = sym.Info
symbols[i].Other = sym.Other
symbols[i].Section = elf.SectionIndex(sym.Shndx)
symbols[i].Value = sym.Value
symbols[i].Size = sym.Size
i++
}
return symbols, strdata, nil
}
// stringTable reads and returns the string table given by the
// specified link value.
func stringTable(f *elf.File, link uint32) ([]byte, error) {
if link <= 0 || link >= uint32(len(f.Sections)) {
return nil, errors.New("section has invalid string table link")
}
return f.Sections[link].Data()
}
// getString extracts a string from an ELF string table.
func getString(section []byte, start int) (string, bool) {
if start < 0 || start >= len(section) {
return "", false
}
for end := start; end < len(section); end++ {
if section[end] == 0 {
return string(section[start:end]), true
}
}
return "", false
}
func applyRelocations(dst, relocations []byte) error {
return nil
}

+ 12
- 0
elf_test.go View File

@ -0,0 +1,12 @@
package ebpf
import (
"path/filepath"
"testing"
)
func TestLoadELF(t *testing.T) {
if err := loadELF(filepath.Join("testdata", "vfs-4.19.o")); err != nil {
t.Fatal(err)
}
}

+ 233
- 0
instruction.go View File

@ -0,0 +1,233 @@
package ebpf
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
type Instruction interface {
// Assemble assembles the Instruction into a RawInstruction.
Assemble() (RawInstruction, error)
// String returns the assembly.
String() string
}
// A RawInstruction is a raw BPF virtual machine instruction.
type RawInstruction struct {
// Operation to execute.
Op Opcode
Dst, Src Register
Offset uint16
Immediate uint32
}
// Assemble implements the Instruction Assemble method.
func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil }
// Disassemble parses ri into an Instruction and returns it. If ri is
// not recognized by this package, ri itself is returned.
func (ri RawInstruction) Disassemble() Instruction {
// log.Printf("op class %#02x", ri.Op&opMaskClass)
switch class := ri.Op & opMaskClass; class {
case opClassLoad, opClassLoadX:
var size uint8
switch ri.Op & opMaskSize {
case sizeWord:
size = 4
case sizeDoubleWord:
size = 8
case sizeHalfWord:
size = 2
case sizeByte:
size = 1
default:
return ri
}
switch ri.Op & opMaskMode {
case modeImmediate: // 0x00
if class == opClassLoad {
if size != 4 {
return ri
}
return LoadConstant{Dst: ri.Dst, Value: ri.Immediate}
}
case modeAbsolute: // 0x20
if class == opClassLoad {
return LoadAbsolute{
Dst: ri.Dst,
Src: ri.Src,
Value: ri.Immediate,
Size: size,
}
}
case modeIndirect: // 0x40
if class == opClassLoad {
return LoadIndirect{
Dst: ri.Dst,
Src: ri.Src,
Value: ri.Immediate,
Size: size,
}
}
case modeMemory: // 0x60
if class == opClassLoadX {
return LoadMemory{
Dst: ri.Dst,
Src: ri.Src,
Offset: ri.Offset,
Size: size,
}
}
case modeExclusiveAdd: // 0xc0
}
case opClassStore, opClassStoreX:
var size uint8
switch ri.Op & opMaskSize {
case sizeWord:
size = 4
case sizeDoubleWord:
size = 8
case sizeHalfWord:
size = 2
case sizeByte:
size = 1
default:
return ri
}
switch ri.Op & opMaskMode {
case modeMemory: // 0x60
if class == opClassStore {
return Store{
Dst: ri.Dst,
Offset: ri.Offset,
Value: ri.Immediate,
Size: size,
}
}
return StoreMemory{
Dst: ri.Dst,
Src: ri.Src,
Offset: ri.Offset,
Size: size,
}
}
case opClassALU:
switch op := ALUOp(ri.Op & opMaskOperand); op {
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor, ALUOpMove, ALUOpArithmicShiftRight:
switch ri.Op & opMaskSource {
case aluSourceImmediate:
return ALUOpConstant{
Op: op,
Dst: ri.Dst,
Value: ri.Immediate,
}
case aluSourceX:
return ALUOpRegister{
Op: op,
Dst: ri.Dst,
Src: ri.Src,
}
}
case aluOpNegate:
return Negate{ri.Dst}
case aluOpByteSwap:
var byteOrder binary.ByteOrder = binary.LittleEndian
if ri.Op&opMaskSource == aluSourceX {
byteOrder = binary.BigEndian
}
return ByteSwap{
Dst: ri.Dst,
ByteOrder: byteOrder,
Size: uint8(ri.Immediate),
}
}
case opClassALU64:
switch op := ALUOp(ri.Op & opMaskOperand); op {
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor, ALUOpMove, ALUOpArithmicShiftRight:
switch ri.Op & opMaskSource {
case aluSourceImmediate:
return ALU64OpConstant{
Op: op,
Dst: ri.Dst,
Value: ri.Immediate,
}
case aluSourceX:
return ALU64OpRegister{
Op: op,
Dst: ri.Dst,
Src: ri.Src,
}
}
case aluOpNegate:
return Negate64{ri.Dst}
}
case opClassJump:
switch op := ri.Op & 0xf0; op {
case jumpOp:
return Jump{ri.Offset}
case jumpOpEqual, jumpOpGreater, jumpOpGreaterOrEqual, jumpOpSet, jumpOpNotEqual,
jumpOpSignedGreater, jumpOpSignedGreaterOrEqual, jumpOpLess, jumpOpLessOrEqual,
jumpOpSignedLess, jumpOpSignedLessOrEqual:
cond := jumpOpToTest(op)
if ri.Op&0x08 == jumpSourceImmediate {
return JumpIf{cond, ri.Dst, ri.Immediate, ri.Offset}
}
return JumpIfX{cond, ri.Dst, ri.Src, ri.Offset}
case jumpOpExit:
return Exit{}
case jumpOpCall:
return Builtin(ri.Immediate)
}
}
return ri
}
func (ri RawInstruction) String() string {
return fmt.Sprintf("# invalid opcode = %#02x, class = %#02x", ri.Op, ri.Op&opMaskClass)
}
// Disassemble a slice of bytes containing an eBPF program.
func Disassemble(data []byte) (Program, error) {
if l := len(data); l%8 != 0 {
return nil, fmt.Errorf("ebpf: raw data size %d does not align with 64-bit boundary", l)
}
return DisassembleReader(bytes.NewBuffer(data))
}
// DisassembleReader disassembles an eBPF program from reader using the default byte order.
func DisassembleReader(r io.Reader) (Program, error) {
return DisassembleReaderOrder(r, binary.LittleEndian)
}
// DisassembleReaderOrder is like DisassembleReader with a custom byte order.
func DisassembleReaderOrder(r io.Reader, byteOrder binary.ByteOrder) (Program, error) {
var out Program
for {
var load struct {
Op Opcode
DstSrc uint8
Offset uint16
Immediate uint32
}
if err := binary.Read(r, byteOrder, &load); err == io.EOF {
break
} else if err != nil {
return nil, err
}
out = append(out, RawInstruction{
Op: load.Op,
Dst: Register(load.DstSrc & 0x0f),
Src: Register(load.DstSrc >> 4),
Offset: load.Offset,
Immediate: load.Immediate,
}.Disassemble())
}
return out, nil
}

+ 150
- 0
instruction_test.go View File

@ -0,0 +1,150 @@
package ebpf
import (
"encoding/hex"
"reflect"
"testing"
)
func TestDisassemble(t *testing.T) {
tests := []struct {
Name string
Data []byte
Want []string
}{
{
Name: "alu",
Data: mustUnhex("040100000200000004090000ffffffff0c210000000000001c210000000000002c210000000000003c210000000000004c210000000000005c210000000000006c210000000000007c2100000000000084010000000000009c21000000000000ac21000000000000bc21000000000000cc21000000000000d401000010000000d401000020000000d401000040000000dc01000010000000dc01000020000000dc01000040000000"),
Want: []string{
"add32 r1, 2",
"add32 r9, 4294967295",
"add32 r1, r2",
"sub32 r1, r2",
"mul32 r1, r2",
"div32 r1, r2",
"or32 r1, r2",
"and32 r1, r2",
"lsh32 r1, r2",
"rsh32 r1, r2",
"neg32 r1",
"mod32 r1, r2",
"xor32 r1, r2",
"mov32 r1, r2",
"arsh32 r1, r2",
"le16 r1",
"le32 r1",
"le64 r1",
"be16 r1",
"be32 r1",
"be64 r1",
},
},
{
Name: "alu-bit",
Want: []string{
"mov32 r0, 0",
"mov32 r1, 1",
"mov32 r2, 2",
"mov32 r3, 3",
"mov32 r4, 4",
"mov32 r5, 5",
"mov32 r6, 6",
"mov32 r7, 7",
"mov32 r8, 8",
"or32 r0, r5",
"or32 r0, 0xa0",
"and32 r0, 0xa3",
"mov32 r9, 0x91",
"and32 r0, r9",
"lsh32 r0, 22",
"lsh32 r0, r8",
"rsh32 r0, 19",
"rsh32 r0, r7",
"xor32 r0, 3",
"xor32 r0, r2",
"exit",
},
},
{
Name: "ja",
Data: mustUnhex("b7000000010000000500010000000000b7000000020000009500000000000000"),
Want: []string{
"mov r0, 1",
"ja +1",
"mov r0, 2",
"exit",
},
},
}
for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
ins, err := Disassemble(test.Data)
if err != nil {
t.Fatal(err)
}
if l := len(ins); l != len(test.Want) {
t.Errorf("expected %d instructions, got %d", len(test.Want), l)
}
for i, a := range ins {
if i < len(test.Want) {
if v := a.String(); v != test.Want[i] {
t.Errorf("unexpected assembly in line %d:\ntest: %q\nwant: %q", i+1, test.Want[i], v)
}
} else {
t.Errorf("unexpected %q", a)
}
}
})
}
}
func mustUnhex(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}
type testInstruction struct {
Test Instruction
WantRaw RawInstruction
WantError string
WantString string
}
func (test testInstruction) Run(t *testing.T) {
t.Helper()
r, err := test.Test.Assemble()
if err != nil {
if test.WantError == "" {
t.Fatal(err)
return
} else if v := err.Error(); v != test.WantError {
t.Errorf("unexpected error:\nwant: %v\ntest: %v", test.WantError, err)
}
t.Log(err)
return
}
if !reflect.DeepEqual(r, test.WantRaw) {
t.Errorf("unexpected result:\nwant: %#+v\ntest: %#+v", test.WantRaw, r)
}
if v := test.Test.String(); v != test.WantString {
t.Errorf("unexpected result:\nwant: %q\ntest: %q", test.WantString, v)
} else {
t.Log(v)
}
}
type testInstructionSuite []testInstruction
func (suite testInstructionSuite) Run(t *testing.T) {
for _, test := range suite {
t.Run(test.WantString, func(it *testing.T) {
test.Run(it)
})
}
}

+ 304
- 0
internal/parser/Assembler.g4 View File

@ -0,0 +1,304 @@
grammar Assembler;
program
: (labelDefinition | labelDefinition? instruction)+ EOF
;
labelDefinition
: IDENTIFIER ':'
;
label : IDENTIFIER ;
comment
: COMMENT
;
instruction
: aluOperation
| alu64Operation
| byteSwapOperation
| jumpOperation
| jumpConditionalOperation
| callOperation
| exitOperation
| loadOperation
| storeOperation
;
aluOperation
: aluOperator register ',' (register | immediate)
| aluOperator register
;
aluOperator
: ADD32
| SUB32
| MUL32
| DIV32
| MOD32
| AND32
| OR32
| NEG32
| XOR32
| MOV32
| LSH32
| RSH32
| ARSH32
;
ADD32 : A D D '32' ;
SUB32 : S U B '32' ;
MUL32 : M U L '32' ;
DIV32 : D I V '32' ;
MOD32 : M O D '32' ;
AND32 : A N D '32' ;
OR32 : O R '32' ;
NEG32 : N E G '32' ;
XOR32 : X O R '32' ;
MOV32 : M O V '32' ;
LSH32 : L S H '32' ;
RSH32 : R S H '32' ;
ARSH32 : A R S H '32' ;
alu64Operation
: alu64Operator register ',' (register | immediate)
| alu64Operator register
;
alu64Operator
: ADD
| SUB
| MUL
| DIV
| MOD
| AND
| OR
| NEG
| XOR
| MOV
| LSH
| RSH
| ARSH
| LE
| BE
;
ADD : A D D ;
SUB : S U B ;
MUL : M U L ;
DIV : D I V ;
MOD : M O D ;
AND : A N D ;
OR : O R ;
NEG : N E G ;
XOR : X O R ;
MOV : M O V ;
LSH : L S H ;
RSH : R S H ;
ARSH : A R S H ;
byteSwapOperation
: byteSwapOperator register
;
byteSwapOperator
: LE
| BE
;
LE : L E '16'
| L E '32'
| L E '64'
;
BE : B E '16'
| B E '32'
| B E '64'
;
jumpOperation
: jumpOperator '+' number
;
jumpOperator
: JA
;
jumpConditionalOperation
: jumpConditionalOperator register ',' ( register | immediate ) ',' '+' offset
;
jumpConditionalOperator
: JEQ
| JGT
| JGE
| JLT
| JLE
| JSET
| JNE
| JSGT
| JSGE
| JSLT
| JSLE
;
callOperation
: CALL literalNumber
;
exitOperation
: EXIT
;
JA : J A ;
JEQ : J E Q ;
JGT : J G T ;
JGE : J G E ;
JLT : J L T ;
JLE : J L E ;
JSET : J S E T ;
JNE : J N E ;
JSGT : J S G T ;
JSGE : J S G E ;
JSLT : J S L T ;
JSLE : J S L E ;
CALL : C A L L ;
EXIT : E X I T ;
loadOperation
: LDDW register ',' literalNumber
| loadAbsoluteOperator register ',' register ',' immediate
| loadOperator register ',' '[' register '+' offset ']'
;
loadAbsoluteOperator
: LDABSW
| LDABSH
| LDABSB
| LDABSDW
;
loadOperator
: LDXW
| LDXH
| LDXB
| LDXDW
;
LDDW : L D D W ;
LDABSW : L D A B S W ;
LDABSH : L D A B S H ;
LDABSB : L D A B S B ;
LDABSDW : L D A B S D W ;
LDXW : L D X W ;
LDXH : L D X H ;
LDXB : L D X B ;
LDXDW : L D X D W ;
storeOperation
: storeOperator '[' register '+' offset ']' ',' immediate
| storeXOperator '[' register '+' offset ']' ',' register
;
storeOperator
: STW
| STH
| STB
| STDW
;
storeXOperator
: STXW
| STXH
| STXB
| STXDW
;
STW : S T W;
STH : S T H;
STB : S T B;
STDW : S T D W;
STXW : S T X W;
STXH : S T X H;
STXB : S T X B;
STXDW : S T X D W;
number
: NUMBER
;
literalNumber
: ('#' number)
;
offset
: NUMBER
;
immediate
: NUMBER
;
register
: REGISTER
| RAX
| RFP
;
extension
: IDENTIFIER
;
NUMBER
: '0b' [01]+
| '0x' [0-9a-fA-F]+
| '-'? [0-9]+
;
COMMENT
: ';' ~ [\r\n]* -> skip
;
IDENTIFIER
: [._a-zA-Z]+
;
WHITESPACE
: [ \t\r\n]+ -> skip
;
REGISTER
: R [0-9]
| R '10'
;
RAX : R A X ;
RFP : R F P ;
fragment A : ( 'a' | 'A' );
fragment B : ( 'b' | 'B' );
fragment C : ( 'c' | 'C' );
fragment D : ( 'd' | 'D' );
fragment E : ( 'e' | 'E' );
fragment F : ( 'f' | 'F' );
fragment G : ( 'g' | 'G' );
fragment H : ( 'h' | 'H' );
fragment I : ( 'i' | 'I' );
fragment J : ( 'j' | 'J' );
fragment K : ( 'k' | 'K' );
fragment L : ( 'l' | 'L' );
fragment M : ( 'm' | 'M' );
fragment N : ( 'n' | 'N' );
fragment O : ( 'o' | 'O' );
fragment P : ( 'p' | 'P' );
fragment Q : ( 'q' | 'Q' );
fragment R : ( 'r' | 'R' );
fragment S : ( 's' | 'S' );
fragment T : ( 't' | 'T' );
fragment U : ( 'u' | 'U' );
fragment V : ( 'v' | 'V' );
fragment W : ( 'w' | 'W' );
fragment X : ( 'x' | 'X' );
fragment Y : ( 'y' | 'Y' );
fragment Z : ( 'z' | 'Z' );

+ 195
- 0
internal/parser/assembler_base_listener.go View File

@ -0,0 +1,195 @@
// Code generated from internal/parser/Assembler.g4 by ANTLR 4.7.2. DO NOT EDIT.
package parser // Assembler
import "github.com/antlr/antlr4/runtime/Go/antlr"
// BaseAssemblerListener is a complete listener for a parse tree produced by AssemblerParser.
type BaseAssemblerListener struct{}
var _ AssemblerListener = &BaseAssemblerListener{}
// VisitTerminal is called when a terminal node is visited.
func (s *BaseAssemblerListener) VisitTerminal(node antlr.TerminalNode) {}
// VisitErrorNode is called when an error node is visited.
func (s *BaseAssemblerListener) VisitErrorNode(node antlr.ErrorNode) {}
// EnterEveryRule is called when any rule is entered.
func (s *BaseAssemblerListener) EnterEveryRule(ctx antlr.ParserRuleContext) {}
// ExitEveryRule is called when any rule is exited.
func (s *BaseAssemblerListener) ExitEveryRule(ctx antlr.ParserRuleContext) {}
// EnterProgram is called when production program is entered.
func (s *BaseAssemblerListener) EnterProgram(ctx *ProgramContext) {}
// ExitProgram is called when production program is exited.
func (s *BaseAssemblerListener) ExitProgram(ctx *ProgramContext) {}
// EnterLabelDefinition is called when production labelDefinition is entered.
func (s *BaseAssemblerListener) EnterLabelDefinition(ctx *LabelDefinitionContext) {}
// ExitLabelDefinition is called when production labelDefinition is exited.
func (s *BaseAssemblerListener) ExitLabelDefinition(ctx *LabelDefinitionContext) {}
// EnterLabel is called when production label is entered.
func (s *BaseAssemblerListener) EnterLabel(ctx *LabelContext) {}
// ExitLabel is called when production label is exited.
func (s *BaseAssemblerListener) ExitLabel(ctx *LabelContext) {}
// EnterComment is called when production comment is entered.
func (s *BaseAssemblerListener) EnterComment(ctx *CommentContext) {}
// ExitComment is called when production comment is exited.
func (s *BaseAssemblerListener) ExitComment(ctx *CommentContext) {}
// EnterInstruction is called when production instruction is entered.
func (s *BaseAssemblerListener) EnterInstruction(ctx *InstructionContext) {}
// ExitInstruction is called when production instruction is exited.
func (s *BaseAssemblerListener) ExitInstruction(ctx *InstructionContext) {}
// EnterAlu64Operation is called when production alu64Operation is entered.
func (s *BaseAssemblerListener) EnterAlu64Operation(ctx *Alu64OperationContext) {}
// ExitAlu64Operation is called when production alu64Operation is exited.
func (s *BaseAssemblerListener) ExitAlu64Operation(ctx *Alu64OperationContext) {}
// EnterAlu64Operator is called when production alu64Operator is entered.
func (s *BaseAssemblerListener) EnterAlu64Operator(ctx *Alu64OperatorContext) {}
// ExitAlu64Operator is called when production alu64Operator is exited.
func (s *BaseAssemblerListener) ExitAlu64Operator(ctx *Alu64OperatorContext) {}
// EnterAluOperation is called when production aluOperation is entered.
func (s *BaseAssemblerListener) EnterAluOperation(ctx *AluOperationContext) {}
// ExitAluOperation is called when production aluOperation is exited.
func (s *BaseAssemblerListener) ExitAluOperation(ctx *AluOperationContext) {}
// EnterAluOperator is called when production aluOperator is entered.
func (s *BaseAssemblerListener) EnterAluOperator(ctx *AluOperatorContext) {}
// ExitAluOperator is called when production aluOperator is exited.
func (s *BaseAssemblerListener) ExitAluOperator(ctx *AluOperatorContext) {}
// EnterByteSwapOperation is called when production byteSwapOperation is entered.
func (s *BaseAssemblerListener) EnterByteSwapOperation(ctx *ByteSwapOperationContext) {}
// ExitByteSwapOperation is called when production byteSwapOperation is exited.
func (s *BaseAssemblerListener) ExitByteSwapOperation(ctx *ByteSwapOperationContext) {}
// EnterByteSwapOperator is called when production byteSwapOperator is entered.
func (s *BaseAssemblerListener) EnterByteSwapOperator(ctx *ByteSwapOperatorContext) {}
// ExitByteSwapOperator is called when production byteSwapOperator is exited.
func (s *BaseAssemblerListener) ExitByteSwapOperator(ctx *ByteSwapOperatorContext) {}
// EnterJumpOperation is called when production jumpOperation is entered.
func (s *BaseAssemblerListener) EnterJumpOperation(ctx *JumpOperationContext) {}
// ExitJumpOperation is called when production jumpOperation is exited.
func (s *BaseAssemblerListener) ExitJumpOperation(ctx *JumpOperationContext) {}
// EnterJumpOperator is called when production jumpOperator is entered.
func (s *BaseAssemblerListener) EnterJumpOperator(ctx *JumpOperatorContext) {}
// ExitJumpOperator is called when production jumpOperator is exited.
func (s *BaseAssemblerListener) ExitJumpOperator(ctx *JumpOperatorContext) {}
// EnterJumpConditionalOperation is called when production jumpConditionalOperation is entered.
func (s *BaseAssemblerListener) EnterJumpConditionalOperation(ctx *JumpConditionalOperationContext) {}
// ExitJumpConditionalOperation is called when production jumpConditionalOperation is exited.
func (s *BaseAssemblerListener) ExitJumpConditionalOperation(ctx *JumpConditionalOperationContext) {}
// EnterJumpConditionalOperator is called when production jumpConditionalOperator is entered.
func (s *BaseAssemblerListener) EnterJumpConditionalOperator(ctx *JumpConditionalOperatorContext) {}
// ExitJumpConditionalOperator is called when production jumpConditionalOperator is exited.
func (s *BaseAssemblerListener) ExitJumpConditionalOperator(ctx *JumpConditionalOperatorContext) {}
// EnterCallOperation is called when production callOperation is entered.
func (s *BaseAssemblerListener) EnterCallOperation(ctx *CallOperationContext) {}
// ExitCallOperation is called when production callOperation is exited.
func (s *BaseAssemblerListener) ExitCallOperation(ctx *CallOperationContext) {}
// EnterExitOperation is called when production exitOperation is entered.
func (s *BaseAssemblerListener) EnterExitOperation(ctx *ExitOperationContext) {}
// ExitExitOperation is called when production exitOperation is exited.
func (s *BaseAssemblerListener) ExitExitOperation(ctx *ExitOperationContext) {}
// EnterLoadOperation is called when production loadOperation is entered.
func (s *BaseAssemblerListener) EnterLoadOperation(ctx *LoadOperationContext) {}
// ExitLoadOperation is called when production loadOperation is exited.
func (s *BaseAssemblerListener) ExitLoadOperation(ctx *LoadOperationContext) {}
// EnterLoadAbsoluteOperator is called when production loadAbsoluteOperator is entered.
func (s *BaseAssemblerListener) EnterLoadAbsoluteOperator(ctx *LoadAbsoluteOperatorContext) {}
// ExitLoadAbsoluteOperator is called when production loadAbsoluteOperator is exited.
func (s *BaseAssemblerListener) ExitLoadAbsoluteOperator(ctx *LoadAbsoluteOperatorContext) {}
// EnterLoadOperator is called when production loadOperator is entered.
func (s *BaseAssemblerListener) EnterLoadOperator(ctx *LoadOperatorContext) {}
// ExitLoadOperator is called when production loadOperator is exited.
func (s *BaseAssemblerListener) ExitLoadOperator(ctx *LoadOperatorContext) {}
// EnterStoreOperation is called when production storeOperation is entered.
func (s *BaseAssemblerListener) EnterStoreOperation(ctx *StoreOperationContext) {}
// ExitStoreOperation is called when production storeOperation is exited.
func (s *BaseAssemblerListener) ExitStoreOperation(ctx *StoreOperationContext) {}
// EnterStoreOperator is called when production storeOperator is entered.
func (s *BaseAssemblerListener) EnterStoreOperator(ctx *StoreOperatorContext) {}
// ExitStoreOperator is called when production storeOperator is exited.
func (s *BaseAssemblerListener) ExitStoreOperator(ctx *StoreOperatorContext) {}
// EnterStoreXOperator is called when production storeXOperator is entered.
func (s *BaseAssemblerListener) EnterStoreXOperator(ctx *StoreXOperatorContext) {}
// ExitStoreXOperator is called when production storeXOperator is exited.
func (s *BaseAssemblerListener) ExitStoreXOperator(ctx *StoreXOperatorContext) {}
// EnterNumber is called when production number is entered.
func (s *BaseAssemblerListener) EnterNumber(ctx *NumberContext) {}
// ExitNumber is called when production number is exited.
func (s *BaseAssemblerListener) ExitNumber(ctx *NumberContext) {}
// EnterLiteralNumber is called when production literalNumber is entered.
func (s *BaseAssemblerListener) EnterLiteralNumber(ctx *LiteralNumberContext) {}
// ExitLiteralNumber is called when production literalNumber is exited.
func (s *BaseAssemblerListener) ExitLiteralNumber(ctx *LiteralNumberContext) {}
// EnterOffset is called when production offset is entered.
func (s *BaseAssemblerListener) EnterOffset(ctx *OffsetContext) {}
// ExitOffset is called when production offset is exited.
func (s *BaseAssemblerListener) ExitOffset(ctx *OffsetContext) {}
// EnterImmediate is called when production immediate is entered.
func (s *BaseAssemblerListener) EnterImmediate(ctx *ImmediateContext) {}
// ExitImmediate is called when production immediate is exited.
func (s *BaseAssemblerListener) ExitImmediate(ctx *ImmediateContext) {}
// EnterRegister is called when production register is entered.
func (s *BaseAssemblerListener) EnterRegister(ctx *RegisterContext) {}
// ExitRegister is called when production register is exited.
func (s *BaseAssemblerListener) ExitRegister(ctx *RegisterContext) {}
// EnterExtension is called when production extension is entered.
func (s *BaseAssemblerListener) EnterExtension(ctx *ExtensionContext) {}
// ExitExtension is called when production extension is exited.
func (s *BaseAssemblerListener) ExitExtension(ctx *ExtensionContext) {}

+ 124
- 0
internal/parser/assembler_base_visitor.go View File

@ -0,0 +1,124 @@
// Code generated from internal/parser/Assembler.g4 by ANTLR 4.7.2. DO NOT EDIT.
package parser // Assembler
import "github.com/antlr/antlr4/runtime/Go/antlr"
type BaseAssemblerVisitor struct {
*antlr.BaseParseTreeVisitor
}
func (v *BaseAssemblerVisitor) VisitProgram(ctx *ProgramContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitLabelDefinition(ctx *LabelDefinitionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitLabel(ctx *LabelContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitComment(ctx *CommentContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitInstruction(ctx *InstructionContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitAlu64Operation(ctx *Alu64OperationContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitAlu64Operator(ctx *Alu64OperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitAluOperation(ctx *AluOperationContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitAluOperator(ctx *AluOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitByteSwapOperation(ctx *ByteSwapOperationContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitByteSwapOperator(ctx *ByteSwapOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitJumpOperation(ctx *JumpOperationContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitJumpOperator(ctx *JumpOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitJumpConditionalOperation(ctx *JumpConditionalOperationContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitJumpConditionalOperator(ctx *JumpConditionalOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitCallOperation(ctx *CallOperationContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitExitOperation(ctx *ExitOperationContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitLoadOperation(ctx *LoadOperationContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitLoadAbsoluteOperator(ctx *LoadAbsoluteOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitLoadOperator(ctx *LoadOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitStoreOperation(ctx *StoreOperationContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitStoreOperator(ctx *StoreOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitStoreXOperator(ctx *StoreXOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitNumber(ctx *NumberContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitLiteralNumber(ctx *LiteralNumberContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitOffset(ctx *OffsetContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitImmediate(ctx *ImmediateContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitRegister(ctx *RegisterContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseAssemblerVisitor) VisitExtension(ctx *ExtensionContext) interface{} {
return v.VisitChildren(ctx)
}

+ 450
- 0
internal/parser/assembler_lexer.go View File

@ -0,0 +1,450 @@
// Code generated from internal/parser/Assembler.g4 by ANTLR 4.7.2. DO NOT EDIT.
package parser
import (
"fmt"
"unicode"
"github.com/antlr/antlr4/runtime/Go/antlr"
)
// Suppress unused import error
var _ = fmt.Printf
var _ = unicode.IsLetter
var serializedLexerAtn = []uint16{
3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 2, 74, 646,
8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7,
9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12,
4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4,
18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23,
9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9,
28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33,
4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4,
39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44,
9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9,
49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54,
4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4,
60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65,
9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9,
70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75,
4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4,
81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86,
9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9,
91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96,
4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 3, 2, 3, 2, 3, 3, 3, 3, 3, 4,
3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 9,
3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11,
3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3,
14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17,
3, 17, 3, 17, 3, 18, 3, 18, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, 3,
20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21, 3, 21,
3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 23, 3, 23, 3,
23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25,
3, 25, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 3, 27, 3,
27, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 29,
3, 29, 3, 29, 3, 29, 3, 29, 3, 29, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 3,
30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 32,
3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 34, 3,
34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34,
3, 34, 3, 34, 3, 34, 5, 34, 357, 10, 34, 3, 35, 3, 35, 3, 35, 3, 35, 3,
35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35,
5, 35, 374, 10, 35, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3,
38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40,
3, 40, 3, 41, 3, 41, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 3, 42, 3, 42, 3,
43, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3, 45, 3, 45,
3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 47, 3, 47, 3,
47, 3, 47, 3, 47, 3, 48, 3, 48, 3, 48, 3, 48, 3, 48, 3, 49, 3, 49, 3, 49,
3, 49, 3, 49, 3, 50, 3, 50, 3, 50, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 3,
51, 3, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52,
3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 54, 3, 54, 3, 54, 3,
54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 55, 3, 55, 3, 55, 3, 55, 3, 55, 3, 56,
3, 56, 3, 56, 3, 56, 3, 56, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 3, 58, 3,
58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 59, 3, 59, 3, 59, 3, 59, 3, 60, 3, 60,
3, 60, 3, 60, 3, 61, 3, 61, 3, 61, 3, 61, 3, 62, 3, 62, 3, 62, 3, 62, 3,
62, 3, 63, 3, 63, 3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 64, 3, 64,
3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3,
66, 3, 67, 3, 67, 3, 67, 3, 67, 6, 67, 535, 10, 67, 13, 67, 14, 67, 536,
3, 67, 3, 67, 3, 67, 3, 67, 6, 67, 543, 10, 67, 13, 67, 14, 67, 544, 3,
67, 5, 67, 548, 10, 67, 3, 67, 6, 67, 551, 10, 67, 13, 67, 14, 67, 552,
5, 67, 555, 10, 67, 3, 68, 3, 68, 7, 68, 559, 10, 68, 12, 68, 14, 68, 562,
11, 68, 3, 68, 3, 68, 3, 69, 6, 69, 567, 10, 69, 13, 69, 14, 69, 568, 3,
70, 6, 70, 572, 10, 70, 13, 70, 14, 70, 573, 3, 70, 3, 70, 3, 71, 3, 71,
3, 71, 3, 71, 3, 71, 3, 71, 3, 71, 5, 71, 585, 10, 71, 3, 72, 3, 72, 3,
72, 3, 72, 3, 73, 3, 73, 3, 73, 3, 73, 3, 74, 3, 74, 3, 75, 3, 75, 3, 76,
3, 76, 3, 77, 3, 77, 3, 78, 3, 78, 3, 79, 3, 79, 3, 80, 3, 80, 3, 81, 3,