compiler_test.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. code.Make(code.OpPop),
  26. },
  27. },
  28. {
  29. input: "1;2",
  30. expectedConstants: []interface{}{1, 2},
  31. expectedInstructions: []code.Instructions{
  32. code.Make(code.OpConstant, 0),
  33. code.Make(code.OpPop),
  34. code.Make(code.OpConstant, 1),
  35. code.Make(code.OpPop),
  36. },
  37. },
  38. {
  39. input: "1 - 2",
  40. expectedConstants: []interface{}{1, 2},
  41. expectedInstructions: []code.Instructions{
  42. code.Make(code.OpConstant, 0),
  43. code.Make(code.OpConstant, 1),
  44. code.Make(code.OpSub),
  45. code.Make(code.OpPop),
  46. },
  47. },
  48. {
  49. input: "1 * 2",
  50. expectedConstants: []interface{}{1, 2},
  51. expectedInstructions: []code.Instructions{
  52. code.Make(code.OpConstant, 0),
  53. code.Make(code.OpConstant, 1),
  54. code.Make(code.OpMul),
  55. code.Make(code.OpPop),
  56. },
  57. },
  58. {
  59. input: "2 / 1",
  60. expectedConstants: []interface{}{2, 1},
  61. expectedInstructions: []code.Instructions{
  62. code.Make(code.OpConstant, 0),
  63. code.Make(code.OpConstant, 1),
  64. code.Make(code.OpDiv),
  65. code.Make(code.OpPop),
  66. },
  67. },
  68. {
  69. input: "5 * (2 + 10)",
  70. expectedConstants: []interface{}{5, 2, 10},
  71. expectedInstructions: []code.Instructions{
  72. code.Make(code.OpConstant, 0),
  73. code.Make(code.OpConstant, 1),
  74. code.Make(code.OpConstant, 2),
  75. code.Make(code.OpAdd),
  76. code.Make(code.OpMul),
  77. code.Make(code.OpPop),
  78. },
  79. },
  80. }
  81. runCompilerTests(t, tests)
  82. }
  83. func runCompilerTests(t *testing.T, tests []compilerTestCase) {
  84. t.Helper()
  85. for _, tt := range tests {
  86. program := parse(tt.input)
  87. compiler := New()
  88. err := compiler.Compile(program)
  89. if err != nil {
  90. t.Fatalf("compiler error: %s", err)
  91. }
  92. bytecode := compiler.ByteCode()
  93. err = testInstructions(tt.expectedInstructions, bytecode.Instructions)
  94. if err != nil {
  95. t.Fatalf("testInstructions failed: %s", err)
  96. }
  97. err = testConstants(t, tt.expectedConstants, bytecode.Constants)
  98. if err != nil {
  99. t.Fatalf("testConstants failed: %s", err)
  100. }
  101. }
  102. }
  103. func testConstants(
  104. t *testing.T,
  105. expected []interface{},
  106. actual []object.Object,
  107. ) error {
  108. t.Helper()
  109. if len(expected) != len(actual) {
  110. return fmt.Errorf("wrong number of constants. got=%d, want=%d", len(actual), len(expected))
  111. }
  112. for i, constant := range expected {
  113. switch constant := constant.(type) {
  114. case int:
  115. err := testIntegerObject(int64(constant), actual[i])
  116. if err != nil {
  117. return fmt.Errorf("constant %d -- testIntegerObject failed: %s",
  118. i, err)
  119. }
  120. }
  121. }
  122. return nil
  123. }
  124. func testIntegerObject(expected int64, actual object.Object) error {
  125. result, ok := actual.(*object.Integer)
  126. if !ok {
  127. return fmt.Errorf("object is not Integer. got=%T (%+v)", actual, actual)
  128. }
  129. if result.Value != expected {
  130. return fmt.Errorf("object has wrong value. got=%d, want=%d",
  131. result.Value, expected)
  132. }
  133. return nil
  134. }
  135. func testInstructions(
  136. expected []code.Instructions,
  137. actual code.Instructions,
  138. ) error {
  139. concatted := concatInstructions(expected)
  140. if len(actual) != len(concatted) {
  141. return fmt.Errorf("wrong instructions length.\nwant=%q\ngot=%q", concatted, actual)
  142. }
  143. for i, ins := range concatted {
  144. if actual[i] != ins {
  145. return fmt.Errorf("wrong instructions at %d.\nwant=%q\ngot=%q", i, concatted, actual)
  146. }
  147. }
  148. return nil
  149. }
  150. func concatInstructions(s []code.Instructions) code.Instructions {
  151. out := code.Instructions{}
  152. for _, ins := range s {
  153. out = append(out, ins...)
  154. }
  155. return out
  156. }
  157. func parse(input string) *ast.Program {
  158. l := lexer.New(input)
  159. p := parser.New(l)
  160. return p.ParseProgram()
  161. }