Example #1
0
File: uclang.go Project: mewmew/uc
// checkFile performs a static semantic analysis check on the given file.
func compileFile(path string, output io.Writer, goccLexer bool) error {
	// Lexical analysis
	// Syntactic analysis
	// Semantic analysis
	// Intermediate representation generation

	// Create lexer for the input.
	buf, err := ioutilx.ReadFile(path)
	if err != nil {
		return errutil.Err(err)
	}
	if path == "-" {
		path = "<stdin>"
	}

	fmt.Fprintf(os.Stderr, "Compiling %q\n", path)

	var s parser.Scanner
	if goccLexer {
		s = goccscanner.NewFromBytes(buf)
	} else {
		s = handscanner.NewFromBytes(buf)
	}

	// Parse input.
	p := parser.NewParser()
	f, err := p.Parse(s)
	if err != nil {
		if err, ok := err.(*goccerrors.Error); ok {
			// Unwrap Gocc error.
			return parser.NewError(err)
		}
		return errutil.Err(err)
	}
	file := f.(*ast.File)
	input := string(buf)
	src := semerrors.NewSource(path, input)
	info, err := sem.Check(file)
	if err != nil {
		if err, ok := err.(*errutil.ErrInfo); ok {
			// Unwrap errutil error.
			if err, ok := err.Err.(*semerrors.Error); ok {
				// Unwrap semantic analysis error, and add input source information.
				err.Src = src
				return err
			}
		}
		return errutil.Err(err)
	}

	// Generate LLVM IR module based on the syntax tree of the given file.
	module := irgen.Gen(file, info)
	if _, err := fmt.Fprint(output, module); err != nil {
		return errutil.Err(err)
	}

	return nil
}
Example #2
0
func TestGen(t *testing.T) {
	golden := []struct {
		path string
		want string
	}{
		// Identifier usage.
		{
			path: "../testdata/extra/irgen/int_ident_use.c",
			want: "../testdata/extra/irgen/int_ident_use.ll",
		},
		{
			path: "../testdata/extra/irgen/int_ident_def.c",
			want: "../testdata/extra/irgen/int_ident_def.ll",
		},
		{
			path: "../testdata/extra/irgen/array_ident_use.c",
			want: "../testdata/extra/irgen/array_ident_use.ll",
		},
		// NOTE: Not part of grammar for uC.
		//{
		//	path: "../testdata/extra/irgen/array_ident_def.c",
		//	want: "../testdata/extra/irgen/array_ident_def.ll",
		//},
		{
			path: "../testdata/extra/irgen/index_expr_use.c",
			want: "../testdata/extra/irgen/index_expr_use.ll",
		},
		{
			path: "../testdata/extra/irgen/index_expr_def.c",
			want: "../testdata/extra/irgen/index_expr_def.ll",
		},
		{
			path: "../testdata/extra/irgen/global_index_expr_use.c",
			want: "../testdata/extra/irgen/global_index_expr_use.ll",
		},
		{
			path: "../testdata/extra/irgen/global_index_expr_def.c",
			want: "../testdata/extra/irgen/global_index_expr_def.ll",
		},

		// Global variable declarations.
		{
			path: "../testdata/extra/irgen/global_def.c",
			want: "../testdata/extra/irgen/global_def.ll",
		},
		{
			path: "../testdata/extra/irgen/tentative_def.c",
			want: "../testdata/extra/irgen/tentative_def.ll",
		},
		// Return statements.
		{
			path: "../testdata/extra/irgen/void_ret.c",
			want: "../testdata/extra/irgen/void_ret.ll",
		},
		{
			path: "../testdata/extra/irgen/implicit_void_ret.c",
			want: "../testdata/extra/irgen/implicit_void_ret.ll",
		},
		{
			path: "../testdata/extra/irgen/int_ret.c",
			want: "../testdata/extra/irgen/int_ret.ll",
		},
		{
			path: "../testdata/extra/irgen/expr_ret.c",
			want: "../testdata/extra/irgen/expr_ret.ll",
		},
		{
			path: "../testdata/extra/irgen/global_ret.c",
			want: "../testdata/extra/irgen/global_ret.ll",
		},
		// Local variable declarations.
		{
			path: "../testdata/extra/irgen/local_def.c",
			want: "../testdata/extra/irgen/local_def.ll",
		},
		// If and if-else statements.
		{
			path: "../testdata/extra/irgen/if_stmt.c",
			want: "../testdata/extra/irgen/if_stmt.ll",
		},
		{
			path: "../testdata/extra/irgen/if_else_stmt.c",
			want: "../testdata/extra/irgen/if_else_stmt.ll",
		},
		// Parenthesized expressions.
		{
			path: "../testdata/extra/irgen/paren_expr.c",
			want: "../testdata/extra/irgen/paren_expr.ll",
		},
		// Unary expressions.
		{
			path: "../testdata/extra/irgen/unary_expr_sub.c",
			want: "../testdata/extra/irgen/unary_expr_sub.ll",
		},
		{
			path: "../testdata/extra/irgen/unary_expr_not.c",
			want: "../testdata/extra/irgen/unary_expr_not.ll",
		},
		// Binary expressions.
		{
			path: "../testdata/extra/irgen/binary_expr_add.c",
			want: "../testdata/extra/irgen/binary_expr_add.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_sub.c",
			want: "../testdata/extra/irgen/binary_expr_sub.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_mul.c",
			want: "../testdata/extra/irgen/binary_expr_mul.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_div.c",
			want: "../testdata/extra/irgen/binary_expr_div.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_lt.c",
			want: "../testdata/extra/irgen/binary_expr_lt.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_gt.c",
			want: "../testdata/extra/irgen/binary_expr_gt.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_le.c",
			want: "../testdata/extra/irgen/binary_expr_le.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_ge.c",
			want: "../testdata/extra/irgen/binary_expr_ge.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_ne.c",
			want: "../testdata/extra/irgen/binary_expr_ne.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_eq.c",
			want: "../testdata/extra/irgen/binary_expr_eq.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_land.c",
			want: "../testdata/extra/irgen/binary_expr_land.ll",
		},
		{
			path: "../testdata/extra/irgen/binary_expr_assign.c",
			want: "../testdata/extra/irgen/binary_expr_assign.ll",
		},
		{
			path: "../testdata/extra/irgen/while_stmt.c",
			want: "../testdata/extra/irgen/while_stmt.ll",
		},
		// Function parameters.
		{
			path: "../testdata/extra/irgen/func_param.c",
			want: "../testdata/extra/irgen/func_param.ll",
		},
		// Nested variable declarations.
		{
			path: "../testdata/extra/irgen/nested_var_decl.c",
			want: "../testdata/extra/irgen/nested_var_decl.ll",
		},
		// Call expressions.
		{
			path: "../testdata/extra/irgen/call_expr.c",
			want: "../testdata/extra/irgen/call_expr.ll",
		},
		{
			path: "../testdata/extra/irgen/call_expr_cast.c",
			want: "../testdata/extra/irgen/call_expr_cast.ll",
		},
		// NOTE: Correct output. The only difference is that Clang emits all
		// alloca instructions at the beginning of the entry block. Thus, disabled
		// for now.
		//{
		//	path: "../testdata/extra/irgen/call_expr_multi_args.c",
		//	want: "../testdata/extra/irgen/call_expr_multi_args.ll",
		//},
		// NOTE: Correct output. The only difference is that Clang emits all
		// alloca instructions at the beginning of the entry block. Thus, disabled
		// for now.
		//{
		//	path: "../testdata/extra/irgen/call_expr_multi_args_cast.c",
		//	want: "../testdata/extra/irgen/call_expr_multi_args_cast.ll",
		//},
		// Expression statements.
		{
			path: "../testdata/extra/irgen/expr_stmt.c",
			want: "../testdata/extra/irgen/expr_stmt.ll",
		},
		// Index expressions.
		{
			path: "../testdata/extra/irgen/index_expr.c",
			want: "../testdata/extra/irgen/index_expr.ll",
		},
		// Array arguments.
		{
			path: "../testdata/extra/irgen/array_arg.c",
			want: "../testdata/extra/irgen/array_arg.ll",
		},
		// Array parameters.
		// TODO: Re-enable; https://github.com/mewmew/uc/issues/73
		{
			path: "../testdata/extra/irgen/array_param.c",
			want: "../testdata/extra/irgen/array_param.ll",
		},
		// Bug fixes.
		{
			path: "../testdata/extra/irgen/issue_68_nested_if.c",
			want: "../testdata/extra/irgen/issue_68_nested_if.ll",
		},
		{
			path: "../testdata/extra/irgen/issue_68_nested_if_while.c",
			want: "../testdata/extra/irgen/issue_68_nested_if_while.ll",
		},
		// TODO: Re-enable when better support for logical AND expressions have
		// been implemented.
		//{
		//	path: "../testdata/extra/irgen/issue_68_if_land.c",
		//	want: "../testdata/extra/irgen/issue_68_if_land.ll",
		//},
		{
			path: "../testdata/extra/irgen/issue_68_while_land.c",
			want: "../testdata/extra/irgen/issue_68_while_land.ll",
		},
		// NOTE: The test case "issue_70_if_ret.c" no longer panics on SetTerm
		// with a nil basic block. The test case still doesn't pass, but this is
		// simply because Clang allocates a dedicated variable for return values,
		// which differs from how uclang does. Thus, the test case remains
		// disabled for now.
		//{
		//	path: "../testdata/extra/irgen/issue_70_if_ret.c",
		//	want: "../testdata/extra/irgen/issue_70_if_ret.ll",
		//},
		// NOTE: The test case "issue_70_while_ret.c" no longer panics on SetTerm
		// with a nil basic block. The test case still doesn't pass, but this is
		// simply because Clang allocates a dedicated variable for return values,
		// which differs from how uclang does. Thus, the test case remains
		// disabled for now.
		//{
		//	path: "../testdata/extra/irgen/issue_70_while_ret.c",
		//	want: "../testdata/extra/irgen/issue_70_while_ret.ll",
		//},
		{
			path: "../testdata/extra/irgen/issue_69_trunc_arg.c",
			want: "../testdata/extra/irgen/issue_69_trunc_arg.ll",
		},
		// TODO: Re-enable; https://github.com/mewmew/uc/issues/73
		{
			path: "../testdata/extra/irgen/issue_73_pointer_pointer_use.c",
			want: "../testdata/extra/irgen/issue_73_pointer_pointer_use.ll",
		},
		// TODO: Re-enable; https://github.com/mewmew/uc/issues/73
		{
			path: "../testdata/extra/irgen/issue_73_pointer_pointer_ref.c",
			want: "../testdata/extra/irgen/issue_73_pointer_pointer_ref.ll",
		},
		{
			path: "../testdata/extra/irgen/global_array_arg.c",
			want: "../testdata/extra/irgen/global_array_arg.ll",
		},
		{
			path: "../testdata/extra/irgen/global_array_ident_use.c",
			want: "../testdata/extra/irgen/global_array_ident_use.ll",
		},
		// NOTE: Not part of grammar for uC.
		// {
		// 	path: "../testdata/extra/irgen/global_array_ident_def.c",
		// 	want: "../testdata/extra/irgen/global_array_ident_def.ll",
		// },
		{
			path: "../testdata/extra/irgen/global_array_param.c",
			want: "../testdata/extra/irgen/global_array_param.ll",
		},
	}

	for _, g := range golden {
		// Lex input.
		buf, err := ioutil.ReadFile(g.path)
		if err != nil {
			t.Errorf("%q: %v", g.path, err)
			continue
		}
		input := string(buf)
		s := scanner.NewFromString(input)

		// Parse input.
		p := parser.NewParser()
		f, err := p.Parse(s)
		if err != nil {
			t.Errorf("%q: parse error: %v", g.path, err)
			continue
		}
		file := f.(*ast.File)

		// Verify input.
		info, err := sem.Check(file)
		if err != nil {
			t.Errorf("%q: semantic analysis error: %v", g.path, err)
			continue
		}

		// Generate IR.
		// TODO: Remove debug output.
		fmt.Printf("\n=== [ %s ] ====================================\n\n", g.want)
		module := irgen.Gen(file, info)

		// Compare generated module against gold standard.
		buf, err = ioutil.ReadFile(g.want)
		if err != nil {
			t.Errorf("%q: %v", g.path, err)
			continue
		}
		got, want := module.String(), string(buf)
		if got != want {
			t.Errorf("%q: module mismatch; expected `%v`, got `%v`", g.path, want, got)
			// TODO: Remove debug output.
			fmt.Println("### FAIL:", g.path)
			continue
		}
		// TODO: Remove debug output.
		fmt.Println("PASS:", g.path)
	}
}
Example #3
0
// checkFile performs a static semantic analysis check on the given file.
func compileFile(path string, outputPath string, goccLexer bool) error {
	// Lexical analysis
	// Syntactic analysis
	// Semantic analysis
	// Intermediate representation generation

	// Create lexer for the input.
	buf, err := ioutilx.ReadFile(path)
	if err != nil {
		return errutil.Err(err)
	}
	if path == "-" {
		path = "<stdin>"
	}

	fmt.Fprintf(os.Stderr, "Compiling %q\n", path)

	var s parser.Scanner
	if goccLexer {
		s = goccscanner.NewFromBytes(buf)
	} else {
		s = handscanner.NewFromBytes(buf)
	}

	// Parse input.
	p := parser.NewParser()
	f, err := p.Parse(s)
	if err != nil {
		if err, ok := err.(*goccerrors.Error); ok {
			// Unwrap Gocc error.
			return parser.NewError(err)
		}
		return errutil.Err(err)
	}
	file := f.(*ast.File)
	input := string(buf)
	src := semerrors.NewSource(path, input)
	info, err := sem.Check(file)
	if err != nil {
		if err, ok := err.(*errutil.ErrInfo); ok {
			// Unwrap errutil error.
			if err, ok := err.Err.(*semerrors.Error); ok {
				// Unwrap semantic analysis error, and add input source information.
				err.Src = src
				return err
			}
		}
		return errutil.Err(err)
	}

	// Generate LLVM IR module based on the syntax tree of the given file.
	module := irgen.Gen(file, info)

	// Add path to uc lib.
	lib, err := goutil.SrcDir("github.com/mewmew/uc/testdata")
	if err != nil {
		return errutil.Err(err)
	}
	lib = filepath.Join(lib, "uc.ll")

	// Link and create binary through clang
	clang := exec.Command("clang", "-o", outputPath, "-x", "ir", lib, "-")
	clang.Stdin = strings.NewReader(module.String())
	clang.Stderr = os.Stderr
	clang.Stdout = os.Stdout
	if err := clang.Run(); err != nil {
		return errutil.Err(err)
	}
	return nil
}