| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- package compiler
- import (
- "fmt"
- "github/runnignwater/monkey/ast"
- "github/runnignwater/monkey/code"
- "github/runnignwater/monkey/lexer"
- "github/runnignwater/monkey/object"
- "github/runnignwater/monkey/parser"
- "testing"
- )
- type compilerTestCase struct {
- input string
- expectedConstants []interface{}
- expectedInstructions []code.Instructions
- }
- func TestIntegerArithmetic(t *testing.T) {
- tests := []compilerTestCase{
- {
- input: "1 + 2",
- expectedConstants: []interface{}{1, 2},
- expectedInstructions: []code.Instructions{
- code.Make(code.OpConstant, 0),
- code.Make(code.OpConstant, 1),
- },
- },
- }
- runCompilerTests(t, tests)
- }
- func runCompilerTests(t *testing.T, tests []compilerTestCase) {
- t.Helper()
- for _, tt := range tests {
- program := parse(tt.input)
- compiler := New()
- err := compiler.Compile(program)
- if err != nil {
- t.Fatalf("compiler error: %s", err)
- }
- bytecode := compiler.ByteCode()
- err = testInstructions(tt.expectedInstructions, bytecode.Instructions)
- if err != nil {
- t.Fatalf("testInstructions failed: %s", err)
- }
- err = testConstants(t, tt.expectedConstants, bytecode.Constants)
- if err != nil {
- t.Fatalf("testConstants failed: %s", err)
- }
- }
- }
- func testConstants(
- t *testing.T,
- expected []interface{},
- actual []object.Object,
- ) error {
- if len(expected) != len(actual) {
- return fmt.Errorf("wrong number of constants. got=%d, want=%d", len(actual), len(expected))
- }
- for i, constant := range expected {
- switch constant := constant.(type) {
- case int:
- err := testIntegerObject(int64(constant), actual[i])
- if err != nil {
- return fmt.Errorf("constant %d -- testIntegerObject failed: %s",
- i, err)
- }
- }
- }
- return nil
- }
- func testIntegerObject(expected int64, actual object.Object) error {
- result, ok := actual.(*object.Integer)
- if !ok {
- return fmt.Errorf("object is not Integer. got=%T (%+v)", actual, actual)
- }
- if result.Value != expected {
- return fmt.Errorf("object has wrong value. got=%d, want=%d",
- result.Value, expected)
- }
- return nil
- }
- func testInstructions(
- expected []code.Instructions,
- actual code.Instructions,
- ) error {
- concatted := concatInstructions(expected)
- if len(actual) != len(concatted) {
- return fmt.Errorf("wrong instructions length.\nwant=%q\ngot=%q", concatted, actual)
- }
- for i, ins := range concatted {
- if actual[i] != ins {
- return fmt.Errorf("wrong instructions at %d.\nwant=%q\ngot=%q", i, concatted, actual)
- }
- }
- return nil
- }
- func concatInstructions(s []code.Instructions) code.Instructions {
- out := code.Instructions{}
- for _, ins := range s {
- out = append(out, ins...)
- }
- return out
- }
- func parse(input string) *ast.Program {
- l := lexer.New(input)
- p := parser.New(l)
- return p.ParseProgram()
- }
|