compiler_test.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package compiler
  2. import (
  3. "fmt"
  4. "github/runnignwater/monkey/ast"
  5. "github/runnignwater/monkey/code"
  6. "github/runnignwater/monkey/lexer"
  7. "github/runnignwater/monkey/object"
  8. "github/runnignwater/monkey/parser"
  9. "testing"
  10. )
  11. type compilerTestCase struct {
  12. input string
  13. expectedConstants []interface{}
  14. expectedInstructions []code.Instructions
  15. }
  16. func TestIntegerArithmetic(t *testing.T) {
  17. tests := []compilerTestCase{
  18. {
  19. input: "1 + 2",
  20. expectedConstants: []interface{}{1, 2},
  21. expectedInstructions: []code.Instructions{
  22. code.Make(code.OpConstant, 0),
  23. code.Make(code.OpConstant, 1),
  24. code.Make(code.OpAdd),
  25. },
  26. },
  27. }
  28. runCompilerTests(t, tests)
  29. }
  30. func runCompilerTests(t *testing.T, tests []compilerTestCase) {
  31. t.Helper()
  32. for _, tt := range tests {
  33. program := parse(tt.input)
  34. compiler := New()
  35. err := compiler.Compile(program)
  36. if err != nil {
  37. t.Fatalf("compiler error: %s", err)
  38. }
  39. bytecode := compiler.ByteCode()
  40. err = testInstructions(tt.expectedInstructions, bytecode.Instructions)
  41. if err != nil {
  42. t.Fatalf("testInstructions failed: %s", err)
  43. }
  44. err = testConstants(t, tt.expectedConstants, bytecode.Constants)
  45. if err != nil {
  46. t.Fatalf("testConstants failed: %s", err)
  47. }
  48. }
  49. }
  50. func testConstants(
  51. t *testing.T,
  52. expected []interface{},
  53. actual []object.Object,
  54. ) error {
  55. if len(expected) != len(actual) {
  56. return fmt.Errorf("wrong number of constants. got=%d, want=%d", len(actual), len(expected))
  57. }
  58. for i, constant := range expected {
  59. switch constant := constant.(type) {
  60. case int:
  61. err := testIntegerObject(int64(constant), actual[i])
  62. if err != nil {
  63. return fmt.Errorf("constant %d -- testIntegerObject failed: %s",
  64. i, err)
  65. }
  66. }
  67. }
  68. return nil
  69. }
  70. func testIntegerObject(expected int64, actual object.Object) error {
  71. result, ok := actual.(*object.Integer)
  72. if !ok {
  73. return fmt.Errorf("object is not Integer. got=%T (%+v)", actual, actual)
  74. }
  75. if result.Value != expected {
  76. return fmt.Errorf("object has wrong value. got=%d, want=%d",
  77. result.Value, expected)
  78. }
  79. return nil
  80. }
  81. func testInstructions(
  82. expected []code.Instructions,
  83. actual code.Instructions,
  84. ) error {
  85. concatted := concatInstructions(expected)
  86. if len(actual) != len(concatted) {
  87. return fmt.Errorf("wrong instructions length.\nwant=%q\ngot=%q", concatted, actual)
  88. }
  89. for i, ins := range concatted {
  90. if actual[i] != ins {
  91. return fmt.Errorf("wrong instructions at %d.\nwant=%q\ngot=%q", i, concatted, actual)
  92. }
  93. }
  94. return nil
  95. }
  96. func concatInstructions(s []code.Instructions) code.Instructions {
  97. out := code.Instructions{}
  98. for _, ins := range s {
  99. out = append(out, ins...)
  100. }
  101. return out
  102. }
  103. func parse(input string) *ast.Program {
  104. l := lexer.New(input)
  105. p := parser.New(l)
  106. return p.ParseProgram()
  107. }