<commit_before>/** 
 * Copyright (c) 2005-2006 <NAME>. All rights reserved.
 */

package com.xruby.compiler;

import junit.framework.*;
import java.io.*;

import antlr.RecognitionException;
import antlr.TokenStreamException;

import com.xruby.runtime.lang.*;
import com.xruby.runtime.value.*;
import com.xruby.compiler.codegen.*;

public class RubyCompilerTest extends TestCase {

	public void setUp() {
		RubyRuntime.initBuiltin(new String[] {"my_arg"});
	}
	
	private void compile_run_and_compare_result(String[] program_texts, int[] results) {
		assertEquals("the number of 'results' should match 'program_texts'",
				program_texts.length, results.length);

		for (int i = 0; i < program_texts.length; ++i) {
			RubyCompiler compiler = new RubyCompiler();

			try {
				CompilationResults codes = compiler.compile(new StringReader(program_texts[i]));
				assertTrue(null != codes);
				RubyProgram p = (RubyProgram)codes.getRubyProgram();
				RubyValue v = p.run();
				RubyFixnum r = (RubyFixnum)v;
				assertEquals(results[i], r.intValue());
			} catch (Exception e) {
				assertTrue("Error at " + i + ": " + e.toString(), false);
			}
		}
	}

	private void compile_run_and_catch_exception(String[] program_texts, RubyException[] exceptions) {
		assertEquals("the number of 'exception' should match 'program_texts'",
				program_texts.length, exceptions.length);

		for (int i = 0; i < program_texts.length; ++i) {
			RubyCompiler compiler = new RubyCompiler();

			try {
				CompilationResults codes = compiler.compile(new StringReader(program_texts[i]));
				assertTrue(null != codes);
				RubyProgram p = (RubyProgram)codes.getRubyProgram();
				p.run();
				assertTrue("Error at " + i + ": should throw RubyException", false);
			} catch (RubyException e) {
				if (exceptions[i].getRubyValue().toString() != null) {
					assertEquals("Exception message mismatch at " + i, exceptions[i].getRubyValue().toString(), e.getRubyValue().toString());
				}
				assertEquals("Exception type mismatch at " + i, exceptions[i].getRubyValue().getRubyClass().getName(), e.getRubyValue().getRubyClass().getName());
				continue;
			} catch (Exception e) {
				assertTrue("Error at " + i + ": should throw RubyException, not exception: " + e.toString(), false);
			} catch (VerifyError e) {
				assertTrue("VerifyError at " + i + ": " + e.toString(), false);
			}
		}
	}

	private void compile_run_and_compare_output(String[] program_texts, String[] outputs) {
		assertEquals("the number of 'outputs' should match 'program_texts'",
				program_texts.length, outputs.length);

		for (int i = 0; i < program_texts.length; ++i) {
			RubyCompiler compiler = new RubyCompiler();

			try {
				CompilationResults codes = compiler.compile(new StringReader(program_texts[i]));
				assertTrue(null != codes);
				RubyProgram p = (RubyProgram)codes.getRubyProgram();

				ByteArrayOutputStream output = new ByteArrayOutputStream();
				PrintStream original = System.out;
				System.setOut(new PrintStream(output));

				p.run();
				
				System.setOut(original);

				assertEquals("Failed at " + i, outputs[i], output.toString());
			} catch (RubyException e) {
				assertTrue("RubyException at " + i + ": " + e.toString(), false);
			} catch (RecognitionException e) {
				assertTrue("RecognitionException at " + i + ": " + e.toString(), false);
			} catch (TokenStreamException e) {
				assertTrue("TokenStreamException at " + i + ": " + e.toString(), false);
			} catch (CompilerException e) {
				assertTrue("CompilerException at " + i + ": " + e.toString(), false);
			} catch (InstantiationException e) {
				assertTrue("InstantiationException at " + i + ": " + e.toString(), false);
			} catch (IllegalAccessException e) {
				assertTrue("IllegalAccessException at " + i + ": " + e.toString(), false);
			} catch (VerifyError e) {
				assertTrue("VerifyError at " + i + ": " + e.toString(), false);
			} /*catch (NullPointerException e) {
				assertTrue("NullPointerException at " + i + ": " + e.toString(), false);
			}*/
		}
	}

	public void test_raise() throws RecognitionException, TokenStreamException, CompilerException, InstantiationException, IllegalAccessException {
		String program_texts = "raise 'test'";
		RubyCompiler compiler = new RubyCompiler();
		CompilationResults codes = compiler.compile(new StringReader(program_texts));
		assertTrue(null != codes);
		RubyProgram p = (RubyProgram)codes.getRubyProgram();
		try {
			p.run();
		} catch (RubyException e) {
			RubyValue v = e.getRubyValue();
			assertEquals(v.getRubyClass(), RubyRuntime.RuntimeErrorClass);
			String s = v.toString();
			assertEquals("test", s);
			return;
		}

		assertTrue("You should not reach here!", false);
	}

	public void test_simple_integer() {
		String[] program_texts = { "2", "0", "123456", "0b1100110", "0xFF",
				"070", "+9", "-100"};
		int[] results = { 2, 0, 123456, 102, 255, 56, 9, -100};

		compile_run_and_compare_result(program_texts, results);
	}
	
	public void test_ascii_value() {
		String[] program_texts = { "?a", "?A", "?\\n", "?\\r", "?\\C-a", "?\\M-a", "?\\M-\\C-a"};
		int[] results = {97, 65, 10, 13, 1, 225, 129};

		compile_run_and_compare_result(program_texts, results);
	}
	
	public void test_fixnum_to_f() {
		String[] program_texts = {
				//"print 1.to_f",
				//"print 2.to_f",
				//"print 3.to_f",
				//"print 1.to_f / 3"
		};
		
		String[] outputs = {
				//"1.0",
				//"2.0",
				//"3.0",
				//"0.333333333333333"
		};
		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_big_integer() {
		String[] program_texts = {
				//TODO"print 1234567890.class",
				"print 0x123456789abcdef0",
				"print (1 << 40)",
				"print ((1 << 40) >> 30)",
				"print (0x123456789abcdef0 <=> 0x123456789abcdef0)",
				"print ((1 << 600) <=> ((1 << 600) + 1))",
				"print (0xabcdef0123456789 * 0x123456789abcdef0)"
		};
		String[] outputs = {
				//"Bignum",
				"1311768467463790320",
				"1099511627776",
				"1024",
				"0",
				"-1",
				"16239449295734013608292442272905420400"
		};
		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_BinaryOperatorExpression() {
		String[] program_texts = { "2+2", "0 + 100", "0 + 0", "654321 + 9999",
				"0xFF + 1", "3 - 5", "3 * 5 * 2", "100/2", "4%2", "7%3",
				"1 + 2 + 3 -0 + 100", "6- 5 *2 + 100 - 6", "4.div 2", };
		int[] results = { 4, 100, 0, 664320, 256, -2, 30, 50, 0, 1, 106, 90, 2};

		compile_run_and_compare_result(program_texts, results);
	}

	public void test_BinaryOperatorExpression2() {
		String[] program_texts = {
				"print 2 > 1",
				"print 2 == 1",
				"print 1 >= 0",
				"print 1 <=> 0",
				"print 9 <=> 9",
				"print 1 <=> 2",
				"print 1 <= 2",
		};

		String[] outputs = {
				"true",
				"false",
				"true",
				"1",
				"0",
				"-1",
				"true",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_simple_methodcall() {
		String[] program_texts = { "1.to_i", "0.to_int", "123.to_i.to_int.to_i",  "\"500\".to_i"};
		int[] results = { 1, 0, 123, 500};

		compile_run_and_compare_result(program_texts, results);
	}
	
	public void test_double_quote_string() {
		String[] program_texts = {				
				"print \"\\x63\"",
				"print \"\\142\"",
				"print \"\\142\\102\"",
		};

		String[] outputs = {
				"c",
				"b",
				"bB",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_Method_Proc_arity() {
		String[] program_texts = {
				"def f(a,b,c); end; method(:f).arity",
				
				"Proc.new {||}.arity",
				"Proc.new {|a|}.arity",
				"Proc.new {|a,b|}.arity",
				"Proc.new {|*a|}.arity",
				"Proc.new {|a,*b|}.arity",
		};
		
		int[] results = {
				3,
				0,
				1,
				2,
				-1,
				-2,
		};

		compile_run_and_compare_result(program_texts, results);
	}
	
	public void test_array() {
		String[] program_texts = { "[1, 'xxx', 1.2].length", "[].length",};
		int[] results = { 3, 0,};

		compile_run_and_compare_result(program_texts, results);
	}

	public void test_array_access() {
		String[] program_texts = {
				"[1.8, 24, 55][-1]",
				"[123][0]",
				"[1.8, 24, 35][2]",
				"a = [0, 9];a[1]",
				
				"a = [1,2,3]; a.[](0)",
		};
		int[] results = {
				55,
				123,
				35,
				9,
			
				1,
		};

		compile_run_and_compare_result(program_texts, results);
	}

	public void test_array_assignment() {
		String[] program_texts = {
				"a = [1, 2, 3];  a[0] = 9; print a[0]",
		};

		String[] outputs = {
				"9",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_array_expand() {
		String[] program_texts = {				
				"a = [5,6,[1, 2]]; print a, a.length",
				"a = [5,6,*[1, 2]]; print a, a.length",
				"a = [5,6,*1]; print a, a.length ",
				"a = [5,6,*nil]; print a, a.length",
				"a = [*nil]; print a, '|', a.length, a[0].class",
				"a = [*[1, 2]]; print a, '|', a.length, a[0].class",
		};

		String[] outputs = {
				"56123",
				"56124",
				"5613",
				"563",
				"|1NilClass",
				"12|2Fixnum",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_Array_join() {
		String[] program_texts = {				
				"print [ 'a', 'b', 'c'].join",
				"print [ 'a', 'b', 'c'].join('-')",
		};

		String[] outputs = {
				"abc",
				"a-b-c",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_Array_sort() {
		String[] program_texts = {				
				"a = [ 3, 2, 1]; print a.sort!, a",
				"print [ 4, 6, 5].sort! {|x, y| x <=> y}",
				"print [ 4, 6, 5].sort! {|x, y| 0}",
				"a = [ 3, 2, 1]; print a.sort, a",
		};

		String[] outputs = {
				"123123",
				"456",
				"465",
				"123321",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_Array_misc() {
		String[] program_texts = {
				"a = [1,2,3]; a[1,0] = [1,2,3]; print a, a.size",
				
				"print [ 1, 2, 3 ] * \",\"",
				"print [1, 2].hash == [1, 2].hash",
				"print [ 1, 1, 3, 5 ] & [ 1, 2, 3 ]",
				"print [ 1, 1, 3, 5 ] | [ 1, 2, 3 ]",
				"print [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ]",
				"a = [4,5,6]; a[1,2] = 9; print a",
				"a = [4,5,6]; a[1,0] = 9; print a",
				"a = [nil, 4,5, nil, 6]; a.compact!; print a",
				"a = [nil, 4, 4,6, nil, 6]; a.uniq!; print a",
				"a = [4,5, 6]; a.reverse!; print a",
				"a = [4,5, 6,7]; a.reverse!; print a",
		};

		String[] outputs = {
				"1123236",
				
				"1,2,3",
				"true",
				"13",
				"1352",
				"335",
				"49",
				"4956",
				"456",
				"46",
				"654",
				"7654",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_Array_compare() {
		String[] program_texts = {				
				"print [ 4, 6, 5] <=> [ 4, 6, 5]",
				"print [ 4, 6, 5] <=> [ 4, 6]",
				"print [ 4, 6] <=> [ 4, 6, 5]",
			
		};

		String[] outputs = {
				"0",
				"1",
				"-1",
				
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_UNARY_PLUS_MINUS_METHOD_NAME() {
		String[] program_texts = {
				"print 1.-@",
				"print 2.-@ {}",
				"print -3.to_s",
		};
		
		String[] outputs = {
				"-1",
				"-2",
				"-3",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_print() {
		String[] program_texts = {
				"print \"hello, world!\"",
				"print 2*3*10",
				"print 'x', 'y', 123",
				
				"print",
				"$_ = 'hello';   print;    $_ = nil",
				"$, = '|';    print 1,2,3;   $, = nil",
				"$\\ = '!';    print 1,2,3;   $\\ = nil",
				
				"print [5,6,7]",
				"print [ nil ]",
				"print [ nil ][0];",
		};

		String[] outputs = {
				"hello, world!",
				"60",
				"xy123",
				
				"nil",
				"hello",
				"1|2|3",
				"123!",
				
				"567",
				"",
				"nil",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_method_compile() {
		String program_texts =
			"def f\n" +
			"	print 123\n" +
			"end\n" +
			"f";
		RubyCompiler compiler = new RubyCompiler();

		try {
			CompilationResults codes = compiler.compile(new StringReader(program_texts));
			assertEquals(2, codes.size());
		} catch (Exception e) {
			assertTrue("Error : " + e.toString(), false);
		}
	}

	public void test_duplicate_method_run() {
		String[] program_texts = {
				"def f\n" +
				"	print 123\n" +
				"end\n" +
				"def f\n" +
				"	print 456\n" +
				"end\n" +
				"f",
		};

		String[] outputs = {
				"456",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_method_return() {
		String[] program_texts = {
				"def f\n" +
				" 123\n" +
				"end\n" +
				"print f()",

				"def f1\n" +
				" 456\n" +
				"end\n" +
				"print f1",

				"def f2\n" +
				"	return 'xxx'\n" +
				"end\n" +
				"print f2",
				
				"def r; return; end; a = r(); print a",
				
				"def f\n" +
				" return 1, 2, 3\n" +
				"end\n" +
				"print f()",
				
				"def f(a); a; end; print f('yyy')",
		};

		String[] outputs = {
				"123",
				"456",
				"xxx",
				"nil",
				"123",
				"yyy",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_asterisk_parameter() {
		String[] program_texts = {
				"def my_print(*a)\n" +
				"	print a\n" +
				"end\n" +
				"my_print ':)', 5634 , 888",
				
				"def my_print(a, *b)\n" +
				"	print b, a\n" +
				"end\n" +
				"my_print ':)', 5634 , 888",
				
				"def my_print(*a)\n" +
				"	print a, a\n" +
				"end\n" +
				"my_print ':)', 5634 , 888",
		};

		String[] outputs = {
				":)5634888",
				"5634888:)",
				":)5634888:)5634888",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_method_run() {
		String[] program_texts = {

				"def f\n" +
				"	print 123\n" +
				"end\n" +
				"f",

				"def my_print(a)\n" +
				"	print a\n" +
				"end\n" +
				"my_print ':)'",

				"def my_print(a, b, c)\n" +
				"	print c\n" +
				"	print a\n" +
				"end\n" +
				"my_print ':)', 5634 , 888",

				"def f(a); a = 1; end;  x = 100; f(x); print x",
				"def f(x); x= 5; print x; end; f(4)",
		};

		String[] outputs = {
				"123",
				":)",
				"888:)",
				"100",
				"5",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_class() {
		String[] program_texts = {
				"class C\n" +
				"	def f\n" +
				"		print \"~~~~\"\n" +
				"	end\n" +
				"end\n" +
				"C.new.f",

				"class C1\n" +
				"	def f\n" +
				"		print \"~~~~\"\n" +
				"	end\n" +
				"	def f\n" +
				"		print \"!!!\"\n" +
				"	end\n" +
				"end\n" +
				"C1.new.f",

				"class C2\n" +
				"	def f\n" +
				"		print \"????\"\n" +
				"	end\n" +
				"end\n" +
				"class C2\n" +
				"	def f2\n" +
				"		print \"*****\"\n" +
				"	end\n" +
				"end\n" +
				"C2.new.f2",

				"class C3\n" +
				"	def f1\n" +
				"		print \"????\"\n" +
				"	end\n" +
				"end\n" +
				"class C3\n" +
				"	def f2\n" +
				"		print \"*****\"\n" +
				"	end\n" +
				"end\n" +
				"C3.new.f1",
		};

		String[] outputs = {
				"~~~~",
				"!!!",
				"*****",
				"????",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_class_inheritence() {
		String[] program_texts = {
			"class B\n" +
			"	def say\n" +
			"		print 7654\n" +
			"	end\n" +
			"end\n" +
			"\n" +
			"class S < B\n" +
			"end\n" +
			"\n" +
			"S.new.say",
			
			"def B\n" +
			"	print 999\n" +
			"end\n" +
			"\n" +
			"class B\n" +
			"	def hello\n" +
			"		print 7654\n" +
			"	end\n" +
			"end\n" +
			"\n" +
			"class S < B\n" +
			"end\n" +
			"\n" +
			"S.new.hello\n" +
			"B()",
			
			"def B\n" +
			"	print 999\n" +
			"end\n" +
			"\n" +
			"class B\n" +
			"	def hello\n" +
			"		print 4567\n" +
			"	end\n" +
			"end\n" +
			"\n" +
			"class S < B\n" +
			"end\n" +
			"\n" +
			"S.new.hello\n" +
			"B",    //<-- B should resolved as class B, not method B() 
			
		};
		
		
		
		String[] outputs = {
				"7654",
				"7654999",
				"4567",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_class_inheritence_exception() {
		String[] program_texts = {
				"B = 1\n" +
				"class S < B\n" +
				"end\n" +
				"\n" +
				"S.new.hello",
				
				"class C;end; class C < String;end",
		};
		
		RubyException[] exceptions = {
			new RubyException(RubyRuntime.TypeErrorClass, "superclass must be a Class (Fixnum given)"),
			new RubyException(RubyRuntime.TypeErrorClass, "superclass mismatch for class C"),
		};

		compile_run_and_catch_exception(program_texts, exceptions);
	}

	public void test_dot_class_method() {
		String[] program_texts = {
				"print 1.class\n",
				"print 0.1.class\n",
				"print 'hello'.class\n",
				"print true.class\n",
				"print false.class\n",
				"print nil.class\n",
				"print String.class",
				"print 1.class.class",
				"print Kernel.class",
				"print Comparable.class",//Comparable is defined in builtin.rb
		};

		String[] outputs = {
				"Fixnum",
				"Float",
				"String",
				"TrueClass",
				"FalseClass",
				"NilClass",
				"Class",
				"Class",
				"Module",
				"Module",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_assign() {
		String[] program_texts = {
				"a = b = 12\n" +
				"print a\n",

				//"a = b = 'xxx'\n" +
				//"print b\n",

				"a = 999\n" +
				"print a\n",

				"a = b = 888\n" +
				"print a, b\n",

				"a = 777\n" +
				"a = 666\n" +
				"print a\n",

				"a = 555\n" +
				"b = a\n" +
				"print b\n",

				"a=5; a+=6; print a",
				"a = b = 1 + 2 + 3; print a, b ",
				"a = (b = 1 + 2) + 3; print a, b ",
		};

		String[] outputs = {
				"12",
				//"xxx",
				"999",
				"888888",
				"666",
				"555",
				"11",
				"66",
				"63",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_if() {
		String[] program_texts = {
				"if true\n" +
				"	print 1\n" +
				"else\n" +
				"	print 2\n" +
				"end",

				"if false\n" +
				"	print 1\n" +
				"else\n" +
				"	print 2\n" +
				"end",

				"if true\n" +
				"	print 'xxx'\n" +
				"end",

				"if false then" +
				"	print 1\n" +
				"elsif true\n" +
				"	print 2\n" +
				"else\n" +
				"	print 3\n" +
				"end",
		};

		String[] outputs = {
				"1",
				"2",
				"xxx",
				"2",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_ternaryIfThenElseExpression() {
		String[] program_texts = {
				"print true ? 1: 2",
				"print false ? 1 : 2",
		};

		String[] outputs = {
				"1",
				"2",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_case() {
		String[] program_texts = {
				"print case 1\n" +
				"	when 1\n" +
				"		1234\n" +
				"	else\n" +
				"		2345\n" +
				"end",

				"print case 100\n" +
				"	when 1\n" +
				"		123\n" +
				"	when 2\n" +
				"		234\n" +
				"	when 3\n" +
				"		34\n" +
				"	else\n" +
				"		2245\n" +
				"end",

				"print case 3\n" +
				"	when 1\n" +
				"		123\n" +
				"	when 2\n" +
				"		234\n" +
				"	when 3\n" +
				"		34\n" +
				"	else\n" +
				"		2245\n" +
				"end",
		};

		String[] outputs = {
				"1234",
				"2245",
				"34",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_unless() {
		String[] program_texts = {
				"a =unless false\n" +
				"	111\n" +
				"else\n" +
				"	222\n" +
				"end\n" +
				"\n" +
				"print a",

				"a =unless true then" +
				"	111\n" +
				"else\n" +
				"	222\n" +
				"end\n" +
				"\n" +
				"print a",
				
				"$bad = false; unless $x == $x; $bad = true; end; print $bad"
		};

		String[] outputs = {
				"111",
				"222",
				
				"false",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_ensure() {
		String[] program_texts = {
				"while true\n" +
				"  begin\n" +
				"    break\n" +
				"  ensure\n" +
				"    print \"break\"\n" +
				"  end\n" +
				"end",
				
				"print begin;  end",
				"begin; print 'xxx'; ensure; print 'ensure'; end",
				"begin; ensure; print 'ensure'; end",
				
				"begin\n" +
				"	raise \"!!!!\"\n" +
				"rescue RuntimeError\n" +
				"	print \"xxx\"\n" +
				"ensure\n" +
				"	print \"yyy\"\n" +
				"end",
				
				"begin\n" +
				"rescue NoMethodError\n" +
				"	print \"xxx\"\n" +
				"else\n" +
				"	print \"zzz\"\n" +
				"ensure\n" +
				"	print \"yyy\"\n" +
				"end",
				
				"begin\n" +
				" 1\n" +
				"rescue NoMethodError\n" +
				"	print \"333\"\n" +
				"else\n" +
				"	print \"444\"\n" +
				"ensure\n" +
				"	print \"555\"\n" +
				"end",
				
				"begin\n" +
				"	raise \"!!!\"\n" +
				"rescue RuntimeError\n" +
				"	print \"333\"\n" +
				"else\n" +
				"	print \"444\"\n" +
				"ensure\n" +
				"	print \"555\"\n" +
				"end",
				
				"begin\n" +
				"  begin\n" +
				"    raise \"this must be handled no.5\"\n" +
				"  ensure\n" +
				"    print \"ok\"\n" +
				"  end\n" +
				"rescue\n" +
				"  print \"catch\"\n" +
				"end",
				
				"begin\n" +
				"  begin\n" +
				"    raise \"this must be handled\"\n" +
				"  ensure\n" +
				"    print \"ok\"\n" +
				"  end\n" +
				"rescue\n" +
				"end",
		};
		
		String[] outputs = {
				"break",
				"nil",
				"xxxensure",
				"ensure",
				"xxxyyy",
				"zzzyyy",
				"444555",
				"333555",
				"okcatch",
				"ok",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_stack_depth_consistency_in_begin_end() {
		String[] program_texts = {
				"begin; true; rescue; false; end; begin; true; rescue; false; end",
				"a = [ begin; 1; rescue; 2; end]; print a",
				"print begin; true; rescue; false; end",
		};

		String[] outputs = {
				"",
				"1",
				"true",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_exception() {
		String[] program_texts = {
				
				"def f(a); end; begin; f; rescue; print 55 ; end",
				
				"begin\n" +
				"	raise \"!!!!\"\n" +
				"rescue RuntimeError\n" +
				"	print \"xxx\"\n" +
				"end",
				
				"begin\n" +
				"	raise \"!!!!\"\n" +
				"rescue\n" +
				"	print \"yyy\"\n" +
				"end",	

				"begin\n" +
				"	raise \"!!!!\"\n" +
				"rescue RuntimeError\n" +
				"	print \"aaa\"\n" +
				"end\n" +
				"print 'bbb'",

				"begin\n" +
				"	print 'zzz'\n" +
				"rescue RuntimeError\n" +
				"	print \"ccc\"\n" +
				"end\n" +
				"print 'ddd'",

				"begin\n" +
				"	raise \"!!!!\"\n" +
				"rescue RuntimeError\n" +
				"	print \"111\"\n" +
				"rescue RuntimeError\n" +
				"	print \"222\"\n" +
				"end",

				"begin\n" +
				"	raise \"!!!!\"\n" +
				"	rescue RuntimeError => e\n" +
				"		print e\n" +
				"end",

				"begin\n" +
				"  raise \"test\"\n" +
				"rescue => e\n" +
				"  print e\n" +
				"end",
				
				"begin\n" +
				"  begin\n" +
				"    raise \"this must be handled no.5\"\n" +
				"  end\n" +
				"rescue\n" +
				"  print \"catch\"\n" +
				"end",
				
		};

		String[] outputs = {
				"55",
				"xxx",
				"yyy",
				"aaabbb",
				"zzzddd",
				"111",
				"!!!!",
				"test",
				"catch",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_binding() {
		String[] program_texts = {
				"module TestBinding\n" +
				"  A = 6\n" +
				"  $x = binding\n" +
				"end\n" +
				"\n" +
				"print eval(\"A\", $x)",
				
				"def getBinding(x); lambda {return binding}.call; end\n" +
				"b = getBinding(666)\n" +
				"print eval(\"x\", b)",
				
				"def getBinding(param); return binding; end\n" +
				"b = getBinding(\"hello\")\n" +
				"print eval(\"param\", b) ",
				
				"def getBinding(param); x = 5; return binding; end\n" +
				"b = getBinding(\"hello\")\n" +
				"print eval(\"x\", b) ",
		};

		String[] outputs = {
				"6",
				"666",
				"hello",
				"5",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_eval() {
		String[] program_texts = {
				"TestEval = 5; print eval('TestEval')",
				"a = 1; eval('print a')",
				"print eval('')",
				"eval('print 54321')",	
		};

		String[] outputs = {
				"5",<commit_msg>should not commit broken test<commit_after>/** 
 * Copyright (c) 2005-2006 <NAME>. All rights reserved.
 */

package com.xruby.compiler;

import junit.framework.*;
import java.io.*;

import antlr.RecognitionException;
import antlr.TokenStreamException;

import com.xruby.runtime.lang.*;
import com.xruby.runtime.value.*;
import com.xruby.compiler.codegen.*;

public class RubyCompilerTest extends TestCase {

	public void setUp() {
		RubyRuntime.initBuiltin(new String[] {"my_arg"});
	}
	
	private void compile_run_and_compare_result(String[] program_texts, int[] results) {
		assertEquals("the number of 'results' should match 'program_texts'",
				program_texts.length, results.length);

		for (int i = 0; i < program_texts.length; ++i) {
			RubyCompiler compiler = new RubyCompiler();

			try {
				CompilationResults codes = compiler.compile(new StringReader(program_texts[i]));
				assertTrue(null != codes);
				RubyProgram p = (RubyProgram)codes.getRubyProgram();
				RubyValue v = p.run();
				RubyFixnum r = (RubyFixnum)v;
				assertEquals(results[i], r.intValue());
			} catch (Exception e) {
				assertTrue("Error at " + i + ": " + e.toString(), false);
			}
		}
	}

	private void compile_run_and_catch_exception(String[] program_texts, RubyException[] exceptions) {
		assertEquals("the number of 'exception' should match 'program_texts'",
				program_texts.length, exceptions.length);

		for (int i = 0; i < program_texts.length; ++i) {
			RubyCompiler compiler = new RubyCompiler();

			try {
				CompilationResults codes = compiler.compile(new StringReader(program_texts[i]));
				assertTrue(null != codes);
				RubyProgram p = (RubyProgram)codes.getRubyProgram();
				p.run();
				assertTrue("Error at " + i + ": should throw RubyException", false);
			} catch (RubyException e) {
				if (exceptions[i].getRubyValue().toString() != null) {
					assertEquals("Exception message mismatch at " + i, exceptions[i].getRubyValue().toString(), e.getRubyValue().toString());
				}
				assertEquals("Exception type mismatch at " + i, exceptions[i].getRubyValue().getRubyClass().getName(), e.getRubyValue().getRubyClass().getName());
				continue;
			} catch (Exception e) {
				assertTrue("Error at " + i + ": should throw RubyException, not exception: " + e.toString(), false);
			} catch (VerifyError e) {
				assertTrue("VerifyError at " + i + ": " + e.toString(), false);
			}
		}
	}

	private void compile_run_and_compare_output(String[] program_texts, String[] outputs) {
		assertEquals("the number of 'outputs' should match 'program_texts'",
				program_texts.length, outputs.length);

		for (int i = 0; i < program_texts.length; ++i) {
			RubyCompiler compiler = new RubyCompiler();

			try {
				CompilationResults codes = compiler.compile(new StringReader(program_texts[i]));
				assertTrue(null != codes);
				RubyProgram p = (RubyProgram)codes.getRubyProgram();

				ByteArrayOutputStream output = new ByteArrayOutputStream();
				PrintStream original = System.out;
				System.setOut(new PrintStream(output));

				p.run();
				
				System.setOut(original);

				assertEquals("Failed at " + i, outputs[i], output.toString());
			} catch (RubyException e) {
				assertTrue("RubyException at " + i + ": " + e.toString(), false);
			} catch (RecognitionException e) {
				assertTrue("RecognitionException at " + i + ": " + e.toString(), false);
			} catch (TokenStreamException e) {
				assertTrue("TokenStreamException at " + i + ": " + e.toString(), false);
			} catch (CompilerException e) {
				assertTrue("CompilerException at " + i + ": " + e.toString(), false);
			} catch (InstantiationException e) {
				assertTrue("InstantiationException at " + i + ": " + e.toString(), false);
			} catch (IllegalAccessException e) {
				assertTrue("IllegalAccessException at " + i + ": " + e.toString(), false);
			} catch (VerifyError e) {
				assertTrue("VerifyError at " + i + ": " + e.toString(), false);
			} /*catch (NullPointerException e) {
				assertTrue("NullPointerException at " + i + ": " + e.toString(), false);
			}*/
		}
	}

	public void test_raise() throws RecognitionException, TokenStreamException, CompilerException, InstantiationException, IllegalAccessException {
		String program_texts = "raise 'test'";
		RubyCompiler compiler = new RubyCompiler();
		CompilationResults codes = compiler.compile(new StringReader(program_texts));
		assertTrue(null != codes);
		RubyProgram p = (RubyProgram)codes.getRubyProgram();
		try {
			p.run();
		} catch (RubyException e) {
			RubyValue v = e.getRubyValue();
			assertEquals(v.getRubyClass(), RubyRuntime.RuntimeErrorClass);
			String s = v.toString();
			assertEquals("test", s);
			return;
		}

		assertTrue("You should not reach here!", false);
	}

	public void test_simple_integer() {
		String[] program_texts = { "2", "0", "123456", "0b1100110", "0xFF",
				"070", "+9", "-100"};
		int[] results = { 2, 0, 123456, 102, 255, 56, 9, -100};

		compile_run_and_compare_result(program_texts, results);
	}
	
	public void test_ascii_value() {
		String[] program_texts = { "?a", "?A", "?\\n", "?\\r", "?\\C-a", "?\\M-a", "?\\M-\\C-a"};
		int[] results = {97, 65, 10, 13, 1, 225, 129};

		compile_run_and_compare_result(program_texts, results);
	}
	
	public void test_fixnum_to_f() {
		String[] program_texts = {
				//"print 1.to_f",
				//"print 2.to_f",
				//"print 3.to_f",
				//"print 1.to_f / 3"
		};
		
		String[] outputs = {
				//"1.0",
				//"2.0",
				//"3.0",
				//"0.333333333333333"
		};
		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_big_integer() {
		String[] program_texts = {
				//TODO"print 1234567890.class",
				"print 0x123456789abcdef0",
				"print (1 << 40)",
				"print ((1 << 40) >> 30)",
				"print (0x123456789abcdef0 <=> 0x123456789abcdef0)",
				"print ((1 << 600) <=> ((1 << 600) + 1))",
				"print (0xabcdef0123456789 * 0x123456789abcdef0)"
		};
		String[] outputs = {
				//"Bignum",
				"1311768467463790320",
				"1099511627776",
				"1024",
				"0",
				"-1",
				"16239449295734013608292442272905420400"
		};
		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_BinaryOperatorExpression() {
		String[] program_texts = { "2+2", "0 + 100", "0 + 0", "654321 + 9999",
				"0xFF + 1", "3 - 5", "3 * 5 * 2", "100/2", "4%2", "7%3",
				"1 + 2 + 3 -0 + 100", "6- 5 *2 + 100 - 6", "4.div 2", };
		int[] results = { 4, 100, 0, 664320, 256, -2, 30, 50, 0, 1, 106, 90, 2};

		compile_run_and_compare_result(program_texts, results);
	}

	public void test_BinaryOperatorExpression2() {
		String[] program_texts = {
				"print 2 > 1",
				"print 2 == 1",
				"print 1 >= 0",
				"print 1 <=> 0",
				"print 9 <=> 9",
				"print 1 <=> 2",
				"print 1 <= 2",
		};

		String[] outputs = {
				"true",
				"false",
				"true",
				"1",
				"0",
				"-1",
				"true",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_simple_methodcall() {
		String[] program_texts = { "1.to_i", "0.to_int", "123.to_i.to_int.to_i",  "\"500\".to_i"};
		int[] results = { 1, 0, 123, 500};

		compile_run_and_compare_result(program_texts, results);
	}
	
	public void test_double_quote_string() {
		String[] program_texts = {				
				"print \"\\x63\"",
				"print \"\\142\"",
				"print \"\\142\\102\"",
		};

		String[] outputs = {
				"c",
				"b",
				"bB",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_Method_Proc_arity() {
		String[] program_texts = {
				"def f(a,b,c); end; method(:f).arity",
				
				"Proc.new {||}.arity",
				"Proc.new {|a|}.arity",
				"Proc.new {|a,b|}.arity",
				"Proc.new {|*a|}.arity",
				"Proc.new {|a,*b|}.arity",
		};
		
		int[] results = {
				3,
				0,
				1,
				2,
				-1,
				-2,
		};

		compile_run_and_compare_result(program_texts, results);
	}
	
	public void test_array() {
		String[] program_texts = { "[1, 'xxx', 1.2].length", "[].length",};
		int[] results = { 3, 0,};

		compile_run_and_compare_result(program_texts, results);
	}

	public void test_array_access() {
		String[] program_texts = {
				"[1.8, 24, 55][-1]",
				"[123][0]",
				"[1.8, 24, 35][2]",
				"a = [0, 9];a[1]",
				
				"a = [1,2,3]; a.[](0)",
		};
		int[] results = {
				55,
				123,
				35,
				9,
			
				1,
		};

		compile_run_and_compare_result(program_texts, results);
	}

	public void test_array_assignment() {
		String[] program_texts = {
				"a = [1, 2, 3];  a[0] = 9; print a[0]",
		};

		String[] outputs = {
				"9",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_array_expand() {
		String[] program_texts = {				
				"a = [5,6,[1, 2]]; print a, a.length",
				"a = [5,6,*[1, 2]]; print a, a.length",
				"a = [5,6,*1]; print a, a.length ",
				"a = [5,6,*nil]; print a, a.length",
				"a = [*nil]; print a, '|', a.length, a[0].class",
				"a = [*[1, 2]]; print a, '|', a.length, a[0].class",
		};

		String[] outputs = {
				"56123",
				"56124",
				"5613",
				"563",
				"|1NilClass",
				"12|2Fixnum",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_Array_join() {
		String[] program_texts = {				
				"print [ 'a', 'b', 'c'].join",
				"print [ 'a', 'b', 'c'].join('-')",
		};

		String[] outputs = {
				"abc",
				"a-b-c",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_Array_sort() {
		String[] program_texts = {				
				"a = [ 3, 2, 1]; print a.sort!, a",
				"print [ 4, 6, 5].sort! {|x, y| x <=> y}",
				"print [ 4, 6, 5].sort! {|x, y| 0}",
				"a = [ 3, 2, 1]; print a.sort, a",
		};

		String[] outputs = {
				"123123",
				"456",
				"465",
				"123321",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_Array_misc() {
		String[] program_texts = {
				"a = [1,2,3]; a[1,0] = [1,2,3]; print a, a.size",
				
				"print [ 1, 2, 3 ] * \",\"",
				"print [1, 2].hash == [1, 2].hash",
				"print [ 1, 1, 3, 5 ] & [ 1, 2, 3 ]",
				"print [ 1, 1, 3, 5 ] | [ 1, 2, 3 ]",
				"print [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ]",
				"a = [4,5,6]; a[1,2] = 9; print a",
				"a = [4,5,6]; a[1,0] = 9; print a",
				"a = [nil, 4,5, nil, 6]; a.compact!; print a",
				"a = [nil, 4, 4,6, nil, 6]; a.uniq!; print a",
				"a = [4,5, 6]; a.reverse!; print a",
				"a = [4,5, 6,7]; a.reverse!; print a",
		};

		String[] outputs = {
				"1123236",
				
				"1,2,3",
				"true",
				"13",
				"1352",
				"335",
				"49",
				"4956",
				"456",
				"46",
				"654",
				"7654",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_Array_compare() {
		String[] program_texts = {				
				"print [ 4, 6, 5] <=> [ 4, 6, 5]",
				"print [ 4, 6, 5] <=> [ 4, 6]",
				"print [ 4, 6] <=> [ 4, 6, 5]",
			
		};

		String[] outputs = {
				"0",
				"1",
				"-1",
				
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_UNARY_PLUS_MINUS_METHOD_NAME() {
		String[] program_texts = {
				"print 1.-@",
				"print 2.-@ {}",
				"print -3.to_s",
		};
		
		String[] outputs = {
				"-1",
				"-2",
				"-3",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_print() {
		String[] program_texts = {
				"print \"hello, world!\"",
				"print 2*3*10",
				"print 'x', 'y', 123",
				
				"print",
				"$_ = 'hello';   print;    $_ = nil",
				"$, = '|';    print 1,2,3;   $, = nil",
				"$\\ = '!';    print 1,2,3;   $\\ = nil",
				
				"print [5,6,7]",
				"print [ nil ]",
				"print [ nil ][0];",
		};

		String[] outputs = {
				"hello, world!",
				"60",
				"xy123",
				
				"nil",
				"hello",
				"1|2|3",
				"123!",
				
				"567",
				"",
				"nil",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_method_compile() {
		String program_texts =
			"def f\n" +
			"	print 123\n" +
			"end\n" +
			"f";
		RubyCompiler compiler = new RubyCompiler();

		try {
			CompilationResults codes = compiler.compile(new StringReader(program_texts));
			assertEquals(2, codes.size());
		} catch (Exception e) {
			assertTrue("Error : " + e.toString(), false);
		}
	}

	public void test_duplicate_method_run() {
		String[] program_texts = {
				"def f\n" +
				"	print 123\n" +
				"end\n" +
				"def f\n" +
				"	print 456\n" +
				"end\n" +
				"f",
		};

		String[] outputs = {
				"456",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_method_return() {
		String[] program_texts = {
				"def f\n" +
				" 123\n" +
				"end\n" +
				"print f()",

				"def f1\n" +
				" 456\n" +
				"end\n" +
				"print f1",

				"def f2\n" +
				"	return 'xxx'\n" +
				"end\n" +
				"print f2",
				
				"def r; return; end; a = r(); print a",
				
				"def f\n" +
				" return 1, 2, 3\n" +
				"end\n" +
				"print f()",
				
				"def f(a); a; end; print f('yyy')",
		};

		String[] outputs = {
				"123",
				"456",
				"xxx",
				"nil",
				"123",
				"yyy",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_asterisk_parameter() {
		String[] program_texts = {
				"def my_print(*a)\n" +
				"	print a\n" +
				"end\n" +
				"my_print ':)', 5634 , 888",
				
				"def my_print(a, *b)\n" +
				"	print b, a\n" +
				"end\n" +
				"my_print ':)', 5634 , 888",
				
				"def my_print(*a)\n" +
				"	print a, a\n" +
				"end\n" +
				"my_print ':)', 5634 , 888",
		};

		String[] outputs = {
				":)5634888",
				"5634888:)",
				":)5634888:)5634888",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_method_run() {
		String[] program_texts = {

				"def f\n" +
				"	print 123\n" +
				"end\n" +
				"f",

				"def my_print(a)\n" +
				"	print a\n" +
				"end\n" +
				"my_print ':)'",

				"def my_print(a, b, c)\n" +
				"	print c\n" +
				"	print a\n" +
				"end\n" +
				"my_print ':)', 5634 , 888",

				"def f(a); a = 1; end;  x = 100; f(x); print x",
				"def f(x); x= 5; print x; end; f(4)",
		};

		String[] outputs = {
				"123",
				":)",
				"888:)",
				"100",
				"5",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_class() {
		String[] program_texts = {
				"class C\n" +
				"	def f\n" +
				"		print \"~~~~\"\n" +
				"	end\n" +
				"end\n" +
				"C.new.f",

				"class C1\n" +
				"	def f\n" +
				"		print \"~~~~\"\n" +
				"	end\n" +
				"	def f\n" +
				"		print \"!!!\"\n" +
				"	end\n" +
				"end\n" +
				"C1.new.f",

				"class C2\n" +
				"	def f\n" +
				"		print \"????\"\n" +
				"	end\n" +
				"end\n" +
				"class C2\n" +
				"	def f2\n" +
				"		print \"*****\"\n" +
				"	end\n" +
				"end\n" +
				"C2.new.f2",

				"class C3\n" +
				"	def f1\n" +
				"		print \"????\"\n" +
				"	end\n" +
				"end\n" +
				"class C3\n" +
				"	def f2\n" +
				"		print \"*****\"\n" +
				"	end\n" +
				"end\n" +
				"C3.new.f1",
		};

		String[] outputs = {
				"~~~~",
				"!!!",
				"*****",
				"????",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_class_inheritence() {
		String[] program_texts = {
			"class B\n" +
			"	def say\n" +
			"		print 7654\n" +
			"	end\n" +
			"end\n" +
			"\n" +
			"class S < B\n" +
			"end\n" +
			"\n" +
			"S.new.say",
			
			"def B\n" +
			"	print 999\n" +
			"end\n" +
			"\n" +
			"class B\n" +
			"	def hello\n" +
			"		print 7654\n" +
			"	end\n" +
			"end\n" +
			"\n" +
			"class S < B\n" +
			"end\n" +
			"\n" +
			"S.new.hello\n" +
			"B()",
			
			"def B\n" +
			"	print 999\n" +
			"end\n" +
			"\n" +
			"class B\n" +
			"	def hello\n" +
			"		print 4567\n" +
			"	end\n" +
			"end\n" +
			"\n" +
			"class S < B\n" +
			"end\n" +
			"\n" +
			"S.new.hello\n" +
			"B",    //<-- B should resolved as class B, not method B() 
			
		};
		
		
		
		String[] outputs = {
				"7654",
				"7654999",
				"4567",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_class_inheritence_exception() {
		String[] program_texts = {
				"B = 1\n" +
				"class S < B\n" +
				"end\n" +
				"\n" +
				"S.new.hello",
				
				"class C;end; class C < String;end",
		};
		
		RubyException[] exceptions = {
			new RubyException(RubyRuntime.TypeErrorClass, "superclass must be a Class (Fixnum given)"),
			new RubyException(RubyRuntime.TypeErrorClass, "superclass mismatch for class C"),
		};

		compile_run_and_catch_exception(program_texts, exceptions);
	}

	public void test_dot_class_method() {
		String[] program_texts = {
				"print 1.class\n",
				"print 0.1.class\n",
				"print 'hello'.class\n",
				"print true.class\n",
				"print false.class\n",
				"print nil.class\n",
				"print String.class",
				"print 1.class.class",
				"print Kernel.class",
				"print Comparable.class",//Comparable is defined in builtin.rb
		};

		String[] outputs = {
				"Fixnum",
				"Float",
				"String",
				"TrueClass",
				"FalseClass",
				"NilClass",
				"Class",
				"Class",
				"Module",
				"Module",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_assign() {
		String[] program_texts = {
				"a = b = 12\n" +
				"print a\n",

				//"a = b = 'xxx'\n" +
				//"print b\n",

				"a = 999\n" +
				"print a\n",

				"a = b = 888\n" +
				"print a, b\n",

				"a = 777\n" +
				"a = 666\n" +
				"print a\n",

				"a = 555\n" +
				"b = a\n" +
				"print b\n",

				"a=5; a+=6; print a",
				"a = b = 1 + 2 + 3; print a, b ",
				"a = (b = 1 + 2) + 3; print a, b ",
		};

		String[] outputs = {
				"12",
				//"xxx",
				"999",
				"888888",
				"666",
				"555",
				"11",
				"66",
				"63",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_if() {
		String[] program_texts = {
				"if true\n" +
				"	print 1\n" +
				"else\n" +
				"	print 2\n" +
				"end",

				"if false\n" +
				"	print 1\n" +
				"else\n" +
				"	print 2\n" +
				"end",

				"if true\n" +
				"	print 'xxx'\n" +
				"end",

				"if false then" +
				"	print 1\n" +
				"elsif true\n" +
				"	print 2\n" +
				"else\n" +
				"	print 3\n" +
				"end",
		};

		String[] outputs = {
				"1",
				"2",
				"xxx",
				"2",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_ternaryIfThenElseExpression() {
		String[] program_texts = {
				"print true ? 1: 2",
				"print false ? 1 : 2",
		};

		String[] outputs = {
				"1",
				"2",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_case() {
		String[] program_texts = {
				"print case 1\n" +
				"	when 1\n" +
				"		1234\n" +
				"	else\n" +
				"		2345\n" +
				"end",

				"print case 100\n" +
				"	when 1\n" +
				"		123\n" +
				"	when 2\n" +
				"		234\n" +
				"	when 3\n" +
				"		34\n" +
				"	else\n" +
				"		2245\n" +
				"end",

				"print case 3\n" +
				"	when 1\n" +
				"		123\n" +
				"	when 2\n" +
				"		234\n" +
				"	when 3\n" +
				"		34\n" +
				"	else\n" +
				"		2245\n" +
				"end",
		};

		String[] outputs = {
				"1234",
				"2245",
				"34",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_unless() {
		String[] program_texts = {
				"a =unless false\n" +
				"	111\n" +
				"else\n" +
				"	222\n" +
				"end\n" +
				"\n" +
				"print a",

				"a =unless true then" +
				"	111\n" +
				"else\n" +
				"	222\n" +
				"end\n" +
				"\n" +
				"print a",
				
				"$bad = false; unless $x == $x; $bad = true; end; print $bad"
		};

		String[] outputs = {
				"111",
				"222",
				
				"false",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_ensure() {
		String[] program_texts = {
				"while true\n" +
				"  begin\n" +
				"    break\n" +
				"  ensure\n" +
				"    print \"break\"\n" +
				"  end\n" +
				"end",
				
				"print begin;  end",
				"begin; print 'xxx'; ensure; print 'ensure'; end",
				"begin; ensure; print 'ensure'; end",
				
				"begin\n" +
				"	raise \"!!!!\"\n" +
				"rescue RuntimeError\n" +
				"	print \"xxx\"\n" +
				"ensure\n" +
				"	print \"yyy\"\n" +
				"end",
				
				"begin\n" +
				"rescue NoMethodError\n" +
				"	print \"xxx\"\n" +
				"else\n" +
				"	print \"zzz\"\n" +
				"ensure\n" +
				"	print \"yyy\"\n" +
				"end",
				
				"begin\n" +
				" 1\n" +
				"rescue NoMethodError\n" +
				"	print \"333\"\n" +
				"else\n" +
				"	print \"444\"\n" +
				"ensure\n" +
				"	print \"555\"\n" +
				"end",
				
				"begin\n" +
				"	raise \"!!!\"\n" +
				"rescue RuntimeError\n" +
				"	print \"333\"\n" +
				"else\n" +
				"	print \"444\"\n" +
				"ensure\n" +
				"	print \"555\"\n" +
				"end",
				
				"begin\n" +
				"  begin\n" +
				"    raise \"this must be handled no.5\"\n" +
				"  ensure\n" +
				"    print \"ok\"\n" +
				"  end\n" +
				"rescue\n" +
				"  print \"catch\"\n" +
				"end",
				
				"begin\n" +
				"  begin\n" +
				"    raise \"this must be handled\"\n" +
				"  ensure\n" +
				"    print \"ok\"\n" +
				"  end\n" +
				"rescue\n" +
				"end",
		};
		
		String[] outputs = {
				"break",
				"nil",
				"xxxensure",
				"ensure",
				"xxxyyy",
				"zzzyyy",
				"444555",
				"333555",
				"okcatch",
				"ok",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_stack_depth_consistency_in_begin_end() {
		String[] program_texts = {
				"begin; true; rescue; false; end; begin; true; rescue; false; end",
				"a = [ begin; 1; rescue; 2; end]; print a",
				"print begin; true; rescue; false; end",
		};

		String[] outputs = {
				"",
				"1",
				"true",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_exception() {
		String[] program_texts = {
				
				"def f(a); end; begin; f; rescue; print 55 ; end",
				
				"begin\n" +
				"	raise \"!!!!\"\n" +
				"rescue RuntimeError\n" +
				"	print \"xxx\"\n" +
				"end",
				
				"begin\n" +
				"	raise \"!!!!\"\n" +
				"rescue\n" +
				"	print \"yyy\"\n" +
				"end",	

				"begin\n" +
				"	raise \"!!!!\"\n" +
				"rescue RuntimeError\n" +
				"	print \"aaa\"\n" +
				"end\n" +
				"print 'bbb'",

				"begin\n" +
				"	print 'zzz'\n" +
				"rescue RuntimeError\n" +
				"	print \"ccc\"\n" +
				"end\n" +
				"print 'ddd'",

				"begin\n" +
				"	raise \"!!!!\"\n" +
				"rescue RuntimeError\n" +
				"	print \"111\"\n" +
				"rescue RuntimeError\n" +
				"	print \"222\"\n" +
				"end",

				"begin\n" +
				"	raise \"!!!!\"\n" +
				"	rescue RuntimeError => e\n" +
				"		print e\n" +
				"end",

				"begin\n" +
				"  raise \"test\"\n" +
				"rescue => e\n" +
				"  print e\n" +
				"end",
				
				"begin\n" +
				"  begin\n" +
				"    raise \"this must be handled no.5\"\n" +
				"  end\n" +
				"rescue\n" +
				"  print \"catch\"\n" +
				"end",
				
		};

		String[] outputs = {
				"55",
				"xxx",
				"yyy",
				"aaabbb",
				"zzzddd",
				"111",
				"!!!!",
				"test",
				"catch",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}

	public void test_binding() {
		String[] program_texts = {
				/*TODO
				"module TestBinding\n" +
				"  A = 6\n" +
				"  $x = binding\n" +
				"end\n" +
				"\n" +
				"print eval(\"A\", $x)",
				*/
				
				"def getBinding(x); lambda {return binding}.call; end\n" +
				"b = getBinding(666)\n" +
				"print eval(\"x\", b)",
				
				"def getBinding(param); return binding; end\n" +
				"b = getBinding(\"hello\")\n" +
				"print eval(\"param\", b) ",
				
				"def getBinding(param); x = 5; return binding; end\n" +
				"b = getBinding(\"hello\")\n" +
				"print eval(\"x\", b) ",
		};

		String[] outputs = {
				//"6",
				"666",
				"hello",
				"5",
		};

		compile_run_and_compare_output(program_texts, outputs);
	}
	
	public void test_eval() {
		String[] program_texts = {
				"TestEval = 5; print eval('TestEval')",
				"a = 1; eval('print a')",
				"print eval('')",
				"eval('print 54321')",	
		};

		String[] outputs = {
				"5",
{'fraction_non_alphanumeric': 0.20429202608878602, 'fraction_numerical': 0.0429202608878603, 'mean_word_length': 2.317119129039012, 'pattern_counts': {'":': 30, '<': 59, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2880446', 'n_tokens_mistral': 25514, 'n_tokens_neox': 19123, 'n_words': 3930}