compiler_test.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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. input: "-1",
  82. expectedConstants: []interface{}{1},
  83. expectedInstructions: []code.Instructions{
  84. code.Make(code.OpConstant, 0),
  85. code.Make(code.OpMinus),
  86. code.Make(code.OpPop),
  87. },
  88. },
  89. }
  90. runCompilerTests(t, tests)
  91. }
  92. func TestBooleanExpression(t *testing.T) {
  93. tests := []compilerTestCase{
  94. {
  95. input: "true",
  96. expectedConstants: []interface{}{},
  97. expectedInstructions: []code.Instructions{
  98. code.Make(code.OpTrue),
  99. code.Make(code.OpPop),
  100. },
  101. },
  102. {
  103. input: "false",
  104. expectedConstants: []interface{}{},
  105. expectedInstructions: []code.Instructions{
  106. code.Make(code.OpFalse),
  107. code.Make(code.OpPop),
  108. },
  109. },
  110. {
  111. input: "1 > 2",
  112. expectedConstants: []interface{}{1, 2},
  113. expectedInstructions: []code.Instructions{
  114. code.Make(code.OpConstant, 0),
  115. code.Make(code.OpConstant, 1),
  116. code.Make(code.OpGreaterThan),
  117. code.Make(code.OpPop),
  118. },
  119. },
  120. {
  121. input: "1 < 2",
  122. expectedConstants: []interface{}{2, 1},
  123. expectedInstructions: []code.Instructions{
  124. code.Make(code.OpConstant, 0),
  125. code.Make(code.OpConstant, 1),
  126. code.Make(code.OpGreaterThan),
  127. code.Make(code.OpPop),
  128. },
  129. },
  130. {
  131. input: "1 == 2",
  132. expectedConstants: []interface{}{1, 2},
  133. expectedInstructions: []code.Instructions{
  134. code.Make(code.OpConstant, 0),
  135. code.Make(code.OpConstant, 1),
  136. code.Make(code.OpEqual),
  137. code.Make(code.OpPop),
  138. },
  139. },
  140. {
  141. input: "1 != 2",
  142. expectedConstants: []interface{}{1, 2},
  143. expectedInstructions: []code.Instructions{
  144. code.Make(code.OpConstant, 0),
  145. code.Make(code.OpConstant, 1),
  146. code.Make(code.OpNotEqual),
  147. code.Make(code.OpPop),
  148. },
  149. },
  150. {
  151. input: "true == false",
  152. expectedConstants: []interface{}{},
  153. expectedInstructions: []code.Instructions{
  154. code.Make(code.OpTrue),
  155. code.Make(code.OpFalse),
  156. code.Make(code.OpEqual),
  157. code.Make(code.OpPop),
  158. },
  159. },
  160. {
  161. input: "true != false",
  162. expectedConstants: []interface{}{},
  163. expectedInstructions: []code.Instructions{
  164. code.Make(code.OpTrue),
  165. code.Make(code.OpFalse),
  166. code.Make(code.OpNotEqual),
  167. code.Make(code.OpPop),
  168. },
  169. },
  170. {
  171. input: "!true",
  172. expectedConstants: []interface{}{},
  173. expectedInstructions: []code.Instructions{
  174. code.Make(code.OpTrue),
  175. code.Make(code.OpBang),
  176. code.Make(code.OpPop),
  177. },
  178. },
  179. }
  180. runCompilerTests(t, tests)
  181. }
  182. func TestConditionals(t *testing.T) {
  183. tests := []compilerTestCase{
  184. {
  185. input: "if (true) {10} ; 3333;",
  186. expectedConstants: []interface{}{10, 3333},
  187. expectedInstructions: []code.Instructions{
  188. // 0000
  189. code.Make(code.OpTrue),
  190. // 0001
  191. code.Make(code.OpJumpNotTruthy, 10),
  192. // 0004
  193. code.Make(code.OpConstant, 0),
  194. // 0007
  195. code.Make(code.OpJump, 11),
  196. // 0010
  197. code.Make(code.OpNull),
  198. // 0011
  199. code.Make(code.OpPop),
  200. // 0012
  201. code.Make(code.OpConstant, 1),
  202. // 0015
  203. code.Make(code.OpPop),
  204. },
  205. },
  206. {
  207. input: "if(true) {10} else {20}; 3333",
  208. expectedConstants: []interface{}{10, 20, 3333},
  209. expectedInstructions: []code.Instructions{
  210. // 0000
  211. code.Make(code.OpTrue),
  212. // 0001
  213. code.Make(code.OpJumpNotTruthy, 10),
  214. // 0004
  215. code.Make(code.OpConstant, 0),
  216. // 0007
  217. code.Make(code.OpJump, 13),
  218. // 0010
  219. code.Make(code.OpConstant, 1),
  220. // 0013
  221. code.Make(code.OpPop),
  222. // 0014
  223. code.Make(code.OpConstant, 2),
  224. // 0017
  225. code.Make(code.OpPop),
  226. },
  227. },
  228. }
  229. runCompilerTests(t, tests)
  230. }
  231. func TestGlobalLetStatements(t *testing.T) {
  232. tests := []compilerTestCase{
  233. {
  234. input: `let one = 1; let two = 2;`,
  235. expectedConstants: []interface{}{1, 2},
  236. expectedInstructions: []code.Instructions{
  237. code.Make(code.OpConstant, 0),
  238. code.Make(code.OpSetGlobal, 0),
  239. code.Make(code.OpConstant, 1),
  240. code.Make(code.OpSetGlobal, 1),
  241. },
  242. },
  243. {
  244. input: `let one = 1; one;`,
  245. expectedConstants: []interface{}{1},
  246. expectedInstructions: []code.Instructions{
  247. code.Make(code.OpConstant, 0),
  248. code.Make(code.OpSetGlobal, 0),
  249. code.Make(code.OpGetGlobal, 0),
  250. code.Make(code.OpPop),
  251. },
  252. },
  253. }
  254. runCompilerTests(t, tests)
  255. }
  256. func runCompilerTests(t *testing.T, tests []compilerTestCase) {
  257. t.Helper()
  258. for _, tt := range tests {
  259. program := parse(tt.input)
  260. compiler := New()
  261. err := compiler.Compile(program)
  262. if err != nil {
  263. t.Fatalf("compiler error: %s", err)
  264. }
  265. bytecode := compiler.ByteCode()
  266. err = testInstructions(tt.expectedInstructions, bytecode.Instructions)
  267. if err != nil {
  268. t.Fatalf("testInstructions failed: %s", err)
  269. }
  270. err = testConstants(t, tt.expectedConstants, bytecode.Constants)
  271. if err != nil {
  272. t.Fatalf("testConstants failed: %s", err)
  273. }
  274. }
  275. }
  276. func testConstants(
  277. t *testing.T,
  278. expected []interface{},
  279. actual []object.Object,
  280. ) error {
  281. t.Helper()
  282. if len(expected) != len(actual) {
  283. return fmt.Errorf("wrong number of constants. got=%d, want=%d", len(actual), len(expected))
  284. }
  285. for i, constant := range expected {
  286. switch constant := constant.(type) {
  287. case int:
  288. err := testIntegerObject(int64(constant), actual[i])
  289. if err != nil {
  290. return fmt.Errorf("constant %d -- testIntegerObject failed: %s",
  291. i, err)
  292. }
  293. }
  294. }
  295. return nil
  296. }
  297. func testIntegerObject(expected int64, actual object.Object) error {
  298. result, ok := actual.(*object.Integer)
  299. if !ok {
  300. return fmt.Errorf("object is not Integer. got=%T (%+v)", actual, actual)
  301. }
  302. if result.Value != expected {
  303. return fmt.Errorf("object has wrong value. got=%d, want=%d",
  304. result.Value, expected)
  305. }
  306. return nil
  307. }
  308. func testInstructions(
  309. expected []code.Instructions,
  310. actual code.Instructions,
  311. ) error {
  312. concatted := concatInstructions(expected)
  313. if len(actual) != len(concatted) {
  314. return fmt.Errorf("wrong instructions length.\nwant=%q\n got=%q", concatted, actual)
  315. }
  316. for i, ins := range concatted {
  317. if actual[i] != ins {
  318. return fmt.Errorf("wrong instructions at %d.\nwant=%q\n got=%q", i, concatted, actual)
  319. }
  320. }
  321. return nil
  322. }
  323. func concatInstructions(s []code.Instructions) code.Instructions {
  324. out := code.Instructions{}
  325. for _, ins := range s {
  326. out = append(out, ins...)
  327. }
  328. return out
  329. }
  330. func parse(input string) *ast.Program {
  331. l := lexer.New(input)
  332. p := parser.New(l)
  333. return p.ParseProgram()
  334. }