Exemple #1
0
// 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
}
Exemple #2
0
// checkFile performs a static semantic analysis check on the given file.
func checkFile(path string, goccLexer bool) error {
	// Lexical analysis
	// Syntactic analysis (skip function bodies)
	// Top-level declarations; used for forward-declarations.
	// Syntactic analysis (including function bodies)

	// NOTE: "For each method body, we rewind the lexer to the point where the
	// method body began and parse the method body."
	//
	// ref: https://blogs.msdn.microsoft.com/ericlippert/2010/02/04/how-many-passes/

	// Semantic analysis

	// 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, "Checking %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)
	if _, err := sem.Check(file); 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)
	}

	return nil
}
Exemple #3
0
func TestCheckValid(t *testing.T) {
	var golden = []struct {
		path string
	}{
		{path: "../testdata/quiet/semantic/s01.c"},
		{path: "../testdata/quiet/semantic/s03.c"},
		{path: "../testdata/quiet/semantic/s04.c"},
		{path: "../testdata/quiet/semantic/s05.c"},
		{path: "../testdata/quiet/semantic/s06.c"},
		{path: "../testdata/extra/semantic/missing-return-main.c"},
		{path: "../testdata/extra/semantic/tentative-var-def.c"},
		{path: "../testdata/extra/semantic/variable-sized-array-arg.c"},
		{path: "../testdata/extra/semantic/nested-function-def.c"},
	}

	errors.UseColor = false

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

		p := parser.NewParser()
		file, err := p.Parse(s)
		if err != nil {
			t.Error(err)
			continue
		}
		f := file.(*ast.File)

		if _, err := sem.Check(f); err != nil {
			if e, ok := err.(*errutil.ErrInfo); ok {
				// Unwrap errutil error.
				err = e.Err
				if e, ok := err.(*errors.Error); ok {
					// Unwrap semantic error.
					e.Src = src
				}
			}
			t.Errorf("%q: unexpected error: `%v`", g.path, err.Error())
		}
	}
}
Exemple #4
0
// parseFile parses the given file and pretty-prints its abstract syntax tree to
// standard output, optionally using the Gocc generated lexer.
func parseFile(path string, goccLexer bool) error {
	// Create lexer for the input.
	buf, err := ioutilx.ReadFile(path)
	if err != nil {
		return errutil.Err(err)
	}
	if path == "-" {
		fmt.Fprintln(os.Stderr, "Parsing from standard input")
	} else {
		fmt.Fprintf(os.Stderr, "Parsing %q\n", path)
	}
	var s parser.Scanner
	if goccLexer {
		s = goccscanner.NewFromBytes(buf)
	} else {
		s = handscanner.NewFromBytes(buf)
	}

	// Parse input.
	p := parser.NewParser()
	file, err := p.Parse(s)
	if err != nil {
		if err, ok := err.(*errors.Error); ok {
			// Unwrap Gocc error.
			return parser.NewError(err)
		}
		return errutil.Err(err)
	}
	f := file.(*ast.File)
	for _, decl := range f.Decls {
		fmt.Println("=== [ Top-level declaration ] ===")
		fmt.Println()
		fmt.Printf("decl type: %T\n", decl)
		fmt.Println()
		fmt.Println("decl:", decl)
		fmt.Println()
		pretty.Print(decl)
		fmt.Println()
		spew.Print(decl)
		fmt.Println()
		fmt.Println()
	}

	return nil
}
Exemple #5
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)
	}
}
Exemple #6
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
}
Exemple #7
0
func TestParser(t *testing.T) {
	var golden = []struct {
		path string
		want *ast.File
	}{
		{
			path: "../../testdata/quiet/lexer/l05.c",
			want: &ast.File{
				Decls: []ast.Decl{
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 0,
								Name:    "int",
							},
							Lparen: 8,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 9,
										Name:    "void",
									},
								},
							},
							Rparen: 13,
						},
						FuncName: &ast.Ident{
							NamePos: 4,
							Name:    "main",
						},
						Body: &ast.BlockStmt{
							Lbrace: 15,
							Items: []ast.BlockItem{
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 19,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 23,
										Name:    "i",
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.BasicLit{
											ValPos: 28,
											Kind:   token.IntLit,
											Val:    "1",
										},
										OpPos: 29,
										Op:    token.Ne,
										Y: &ast.UnaryExpr{
											OpPos: 31,
											Op:    token.Not,
											X: &ast.BasicLit{
												ValPos: 32,
												Kind:   token.IntLit,
												Val:    "3",
											},
										},
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.BasicLit{
											ValPos: 37,
											Kind:   token.IntLit,
											Val:    "4",
										},
										OpPos: 38,
										Op:    token.Land,
										Y: &ast.ParenExpr{
											Lparen: 40,
											X: &ast.BasicLit{
												ValPos: 41,
												Kind:   token.IntLit,
												Val:    "6",
											},
											Rparen: 42,
										},
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.BinaryExpr{
											X: &ast.BasicLit{
												ValPos: 47,
												Kind:   token.IntLit,
												Val:    "7",
											},
											OpPos: 48,
											Op:    token.Mul,
											Y: &ast.BasicLit{
												ValPos: 50,
												Kind:   token.IntLit,
												Val:    "8",
											},
										},
										OpPos: 51,
										Op:    token.Add,
										Y: &ast.BasicLit{
											ValPos: 52,
											Kind:   token.IntLit,
											Val:    "10",
										},
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.ParenExpr{
											Lparen: 58,
											X: &ast.BinaryExpr{
												X: &ast.BasicLit{
													ValPos: 59,
													Kind:   token.IntLit,
													Val:    "11",
												},
												OpPos: 61,
												Op:    token.Sub,
												Y: &ast.BasicLit{
													ValPos: 62,
													Kind:   token.IntLit,
													Val:    "12",
												},
											},
											Rparen: 64,
										},
										OpPos: 65,
										Op:    token.Add,
										Y: &ast.ParenExpr{
											Lparen: 66,
											X: &ast.BinaryExpr{
												X: &ast.BasicLit{
													ValPos: 67,
													Kind:   token.IntLit,
													Val:    "12",
												},
												OpPos: 69,
												Op:    token.Div,
												Y: &ast.BasicLit{
													ValPos: 70,
													Kind:   token.IntLit,
													Val:    "16",
												},
											},
											Rparen: 72,
										},
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.BinaryExpr{
											X: &ast.BasicLit{
												ValPos: 77,
												Kind:   token.IntLit,
												Val:    "17",
											},
											OpPos: 79,
											Op:    token.Le,
											Y: &ast.BasicLit{
												ValPos: 81,
												Kind:   token.IntLit,
												Val:    "18",
											},
										},
										OpPos: 84,
										Op:    token.Lt,
										Y: &ast.UnaryExpr{
											OpPos: 85,
											Op:    token.Sub,
											X: &ast.BasicLit{
												ValPos: 86,
												Kind:   token.IntLit,
												Val:    "20",
											},
										},
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.Ident{
											NamePos: 92,
											Name:    "i",
										},
										OpPos: 93,
										Op:    token.Assign,
										Y: &ast.BinaryExpr{
											X: &ast.BasicLit{
												ValPos: 94,
												Kind:   token.IntLit,
												Val:    "21",
											},
											OpPos: 96,
											Op:    token.Eq,
											Y: &ast.BasicLit{
												ValPos: 98,
												Kind:   token.IntLit,
												Val:    "22",
											},
										},
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.BinaryExpr{
											X: &ast.BasicLit{
												ValPos: 104,
												Kind:   token.IntLit,
												Val:    "25",
											},
											OpPos: 107,
											Op:    token.Ge,
											Y: &ast.BasicLit{
												ValPos: 109,
												Kind:   token.IntLit,
												Val:    "27",
											},
										},
										OpPos: 111,
										Op:    token.Gt,
										Y: &ast.BasicLit{
											ValPos: 112,
											Kind:   token.IntLit,
											Val:    "28",
										},
									},
								},
							},
							Rbrace: 116,
						},
					},
				},
			},
		},

		{
			path: "../../testdata/quiet/parser/p01.c",
			want: &ast.File{
				Decls: []ast.Decl{
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 0,
								Name:    "int",
							},
							Lparen: 8,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 9,
										Name:    "void",
									},
								},
							},
							Rparen: 13,
						},
						FuncName: &ast.Ident{
							NamePos: 4,
							Name:    "main",
						},
						Body: &ast.BlockStmt{
							Lbrace: 15,
							Items: []ast.BlockItem{
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 19,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 23,
										Name:    "x",
									},
								},
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 28,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 32,
										Name:    "y",
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.Ident{
											NamePos: 37,
											Name:    "x",
										},
										OpPos: 39,
										Op:    token.Assign,
										Y: &ast.BasicLit{
											ValPos: 41,
											Kind:   token.IntLit,
											Val:    "42",
										},
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.Ident{
											NamePos: 47,
											Name:    "x",
										},
										OpPos: 48,
										Op:    token.Assign,
										Y: &ast.BinaryExpr{
											X: &ast.Ident{
												NamePos: 49,
												Name:    "y",
											},
											OpPos: 50,
											Op:    token.Assign,
											Y: &ast.BasicLit{
												ValPos: 51,
												Kind:   token.IntLit,
												Val:    "4711",
											},
										},
									},
								},
							},
							Rbrace: 57,
						},
					},
				},
			},
		},

		{
			path: "../../testdata/quiet/parser/p02.c",
			want: &ast.File{
				Decls: []ast.Decl{
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 1,
								Name:    "int",
							},
							Lparen: 9,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 10,
										Name:    "void",
									},
								},
							},
							Rparen: 14,
						},
						FuncName: &ast.Ident{
							NamePos: 5,
							Name:    "main",
						},
						Body: &ast.BlockStmt{
							Lbrace: 16,
							Items: []ast.BlockItem{
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 20,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 24,
										Name:    "x",
									},
								},
								&ast.EmptyStmt{
									Semicolon: 29,
								},
								&ast.WhileStmt{
									While: 33,
									Cond: &ast.BinaryExpr{
										X: &ast.Ident{
											NamePos: 40,
											Name:    "x",
										},
										OpPos: 41,
										Op:    token.Lt,
										Y: &ast.BasicLit{
											ValPos: 42,
											Kind:   token.IntLit,
											Val:    "10",
										},
									},
									Body: &ast.ExprStmt{
										X: &ast.BinaryExpr{
											X: &ast.Ident{
												NamePos: 46,
												Name:    "x",
											},
											OpPos: 48,
											Op:    token.Assign,
											Y: &ast.BinaryExpr{
												X: &ast.Ident{
													NamePos: 50,
													Name:    "x",
												},
												OpPos: 52,
												Op:    token.Add,
												Y: &ast.BasicLit{
													ValPos: 54,
													Kind:   token.IntLit,
													Val:    "3",
												},
											},
										},
									},
								},
								&ast.IfStmt{
									If: 60,
									Cond: &ast.BasicLit{
										ValPos: 64,
										Kind:   token.IntLit,
										Val:    "1",
									},
									Body: &ast.ExprStmt{
										X: &ast.BinaryExpr{
											X: &ast.Ident{
												NamePos: 67,
												Name:    "x",
											},
											OpPos: 69,
											Op:    token.Assign,
											Y: &ast.BinaryExpr{
												X: &ast.Ident{
													NamePos: 71,
													Name:    "x",
												},
												OpPos: 73,
												Op:    token.Add,
												Y: &ast.BasicLit{
													ValPos: 75,
													Kind:   token.IntLit,
													Val:    "3",
												},
											},
										},
									},
								},
							},
							Rbrace: 78,
						},
					},
				},
			},
		},

		{
			path: "../../testdata/quiet/parser/p03.c",
			want: &ast.File{
				Decls: []ast.Decl{
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 1,
								Name:    "int",
							},
							Lparen: 9,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 10,
										Name:    "void",
									},
								},
							},
							Rparen: 14,
						},
						FuncName: &ast.Ident{
							NamePos: 5,
							Name:    "main",
						},
						Body: &ast.BlockStmt{
							Lbrace: 16,
							Items: []ast.BlockItem{
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 20,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 24,
										Name:    "x",
									},
								},
								&ast.IfStmt{
									If: 29,
									Cond: &ast.BinaryExpr{
										X: &ast.BasicLit{
											ValPos: 33,
											Kind:   token.IntLit,
											Val:    "1",
										},
										OpPos: 34,
										Op:    token.Lt,
										Y: &ast.BasicLit{
											ValPos: 35,
											Kind:   token.IntLit,
											Val:    "2",
										},
									},
									Body: &ast.ExprStmt{
										X: &ast.BinaryExpr{
											X: &ast.Ident{
												NamePos: 38,
												Name:    "x",
											},
											OpPos: 40,
											Op:    token.Assign,
											Y: &ast.BasicLit{
												ValPos: 42,
												Kind:   token.IntLit,
												Val:    "1",
											},
										},
									},
									Else: &ast.ExprStmt{
										X: &ast.BinaryExpr{
											X: &ast.Ident{
												NamePos: 53,
												Name:    "x",
											},
											OpPos: 55,
											Op:    token.Assign,
											Y: &ast.BasicLit{
												ValPos: 57,
												Kind:   token.IntLit,
												Val:    "2",
											},
										},
									},
								},
							},
							Rbrace: 60,
						},
					},
				},
			},
		},

		{
			path: "../../testdata/quiet/parser/p04.c",
			want: &ast.File{
				Decls: []ast.Decl{
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 0,
								Name:    "int",
							},
							Lparen: 8,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 9,
										Name:    "void",
									},
								},
							},
							Rparen: 13,
						},
						FuncName: &ast.Ident{
							NamePos: 4,
							Name:    "main",
						},
						Body: &ast.BlockStmt{
							Lbrace: 15,
							Items: []ast.BlockItem{
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 19,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 23,
										Name:    "x",
									},
								},
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 28,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 32,
										Name:    "y",
									},
								},
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 37,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 41,
										Name:    "z",
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.BinaryExpr{
											X: &ast.BinaryExpr{
												X: &ast.Ident{
													NamePos: 49,
													Name:    "x",
												},
												OpPos: 50,
												Op:    token.Sub,
												Y: &ast.Ident{
													NamePos: 51,
													Name:    "y",
												},
											},
											OpPos: 52,
											Op:    token.Sub,
											Y: &ast.Ident{
												NamePos: 53,
												Name:    "z",
											},
										},
										OpPos: 54,
										Op:    token.Sub,
										Y: &ast.BasicLit{
											ValPos: 55,
											Kind:   token.IntLit,
											Val:    "42",
										},
									},
								},
								&ast.ExprStmt{
									X: &ast.BinaryExpr{
										X: &ast.BinaryExpr{
											X: &ast.BinaryExpr{
												X: &ast.BinaryExpr{
													X: &ast.UnaryExpr{
														OpPos: 90,
														Op:    token.Not,
														X: &ast.Ident{
															NamePos: 91,
															Name:    "x",
														},
													},
													OpPos: 93,
													Op:    token.Mul,
													Y: &ast.Ident{
														NamePos: 95,
														Name:    "y",
													},
												},
												OpPos: 97,
												Op:    token.Add,
												Y: &ast.Ident{
													NamePos: 99,
													Name:    "z",
												},
											},
											OpPos: 101,
											Op:    token.Lt,
											Y: &ast.Ident{
												NamePos: 103,
												Name:    "x",
											},
										},
										OpPos: 105,
										Op:    token.Ne,
										Y: &ast.BinaryExpr{
											X: &ast.BasicLit{
												ValPos: 108,
												Kind:   token.IntLit,
												Val:    "42",
											},
											OpPos: 111,
											Op:    token.Lt,
											Y: &ast.BinaryExpr{
												X: &ast.Ident{
													NamePos: 113,
													Name:    "x",
												},
												OpPos: 115,
												Op:    token.Add,
												Y: &ast.BinaryExpr{
													X: &ast.Ident{
														NamePos: 117,
														Name:    "y",
													},
													OpPos: 119,
													Op:    token.Mul,
													Y: &ast.UnaryExpr{
														OpPos: 121,
														Op:    token.Not,
														X: &ast.Ident{
															NamePos: 122,
															Name:    "x",
														},
													},
												},
											},
										},
									},
								},
							},
							Rbrace: 164,
						},
					},
				},
			},
		},

		{
			path: "../../testdata/quiet/parser/p05.c",
			want: &ast.File{
				Decls: []ast.Decl{
					&ast.VarDecl{
						VarType: &ast.ArrayType{
							Elem: &ast.Ident{
								NamePos: 0,
								Name:    "int",
							},
							Lbracket: 5,
							Len:      10,
							Rbracket: 8,
						},
						VarName: &ast.Ident{
							NamePos: 4,
							Name:    "c",
						},
					},
					&ast.VarDecl{
						VarType: &ast.ArrayType{
							Elem: &ast.Ident{
								NamePos: 11,
								Name:    "char",
							},
							Lbracket: 17,
							Len:      10,
							Rbracket: 20,
						},
						VarName: &ast.Ident{
							NamePos: 16,
							Name:    "d",
						},
					},
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 24,
								Name:    "void",
							},
							Lparen: 30,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.ArrayType{
										Elem: &ast.Ident{
											NamePos: 31,
											Name:    "int",
										},
										Lbracket: 36,
										Rbracket: 37,
									},
									VarName: &ast.Ident{
										NamePos: 35,
										Name:    "h",
									},
								},
								{
									VarType: &ast.ArrayType{
										Elem: &ast.Ident{
											NamePos: 40,
											Name:    "char",
										},
										Lbracket: 46,
										Rbracket: 47,
									},
									VarName: &ast.Ident{
										NamePos: 45,
										Name:    "i",
									},
								},
							},
							Rparen: 48,
						},
						FuncName: &ast.Ident{
							NamePos: 29,
							Name:    "f",
						},
						Body: &ast.BlockStmt{
							Lbrace: 50,
							Rbrace: 52,
						},
					},
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 56,
								Name:    "int",
							},
							Lparen: 64,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 65,
										Name:    "void",
									},
								},
							},
							Rparen: 69,
						},
						FuncName: &ast.Ident{
							NamePos: 60,
							Name:    "main",
						},
						Body: &ast.BlockStmt{
							Lbrace: 71,
							Items: []ast.BlockItem{
								&ast.EmptyStmt{
									Semicolon: 75,
								},
							},
							Rbrace: 77,
						},
					},
				},
			},
		},

		{
			path: "../../testdata/quiet/parser/p06.c",
			want: &ast.File{
				Decls: []ast.Decl{
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 0,
								Name:    "void",
							},
							Lparen: 6,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 7,
										Name:    "void",
									},
								},
							},
							Rparen: 11,
						},
						FuncName: &ast.Ident{
							NamePos: 5,
							Name:    "f",
						},
						Body: &ast.BlockStmt{
							Lbrace: 13,
							Items: []ast.BlockItem{
								&ast.ReturnStmt{
									Return: 17,
								},
							},
							Rbrace: 25,
						},
					},
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 28,
								Name:    "int",
							},
							Lparen: 33,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 34,
										Name:    "void",
									},
								},
							},
							Rparen: 38,
						},
						FuncName: &ast.Ident{
							NamePos: 32,
							Name:    "g",
						},
						Body: &ast.BlockStmt{
							Lbrace: 40,
							Items: []ast.BlockItem{
								&ast.ReturnStmt{
									Return: 44,
									Result: &ast.BasicLit{
										ValPos: 51,
										Kind:   token.IntLit,
										Val:    "42",
									},
								},
							},
							Rbrace: 55,
						},
					},
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 58,
								Name:    "int",
							},
							Lparen: 66,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 67,
										Name:    "void",
									},
								},
							},
							Rparen: 71,
						},
						FuncName: &ast.Ident{
							NamePos: 62,
							Name:    "main",
						},
						Body: &ast.BlockStmt{
							Lbrace: 72,
							Items: []ast.BlockItem{
								&ast.ExprStmt{
									X: &ast.CallExpr{
										Name: &ast.Ident{
											NamePos: 76,
											Name:    "f",
										},
										Lparen: 77,
										Rparen: 78,
									},
								},
								&ast.ExprStmt{
									X: &ast.CallExpr{
										Name: &ast.Ident{
											NamePos: 83,
											Name:    "g",
										},
										Lparen: 84,
										Rparen: 85,
									},
								},
							},
							Rbrace: 88,
						},
					},
				},
			},
		},

		{
			path: "../../testdata/quiet/parser/p07.c",
			want: &ast.File{
				Decls: []ast.Decl{
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 0,
								Name:    "int",
							},
							Lparen: 8,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 9,
										Name:    "void",
									},
								},
							},
							Rparen: 13,
						},
						FuncName: &ast.Ident{
							NamePos: 4,
							Name:    "main",
						},
						Body: &ast.BlockStmt{
							Lbrace: 14,
							Items: []ast.BlockItem{
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 18,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 22,
										Name:    "x",
									},
								},
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 27,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 31,
										Name:    "y",
									},
								},
								&ast.IfStmt{
									If: 37,
									Cond: &ast.Ident{
										NamePos: 40,
										Name:    "x",
									},
									Body: &ast.WhileStmt{
										While: 43,
										Cond: &ast.Ident{
											NamePos: 50,
											Name:    "y",
										},
										Body: &ast.ExprStmt{
											X: &ast.BinaryExpr{
												X: &ast.Ident{
													NamePos: 53,
													Name:    "x",
												},
												OpPos: 54,
												Op:    token.Assign,
												Y: &ast.BasicLit{
													ValPos: 55,
													Kind:   token.IntLit,
													Val:    "42",
												},
											},
										},
									},
								},
								&ast.WhileStmt{
									While: 64,
									Cond: &ast.Ident{
										NamePos: 70,
										Name:    "x",
									},
									Body: &ast.IfStmt{
										If: 73,
										Cond: &ast.Ident{
											NamePos: 76,
											Name:    "y",
										},
										Body: &ast.ExprStmt{
											X: &ast.BinaryExpr{
												X: &ast.Ident{
													NamePos: 79,
													Name:    "x",
												},
												OpPos: 80,
												Op:    token.Assign,
												Y: &ast.BasicLit{
													ValPos: 81,
													Kind:   token.IntLit,
													Val:    "42",
												},
											},
										},
									},
								},
							},
							Rbrace: 85,
						},
					},
				},
			},
		},

		{
			path: "../../testdata/quiet/parser/p08.c",
			want: &ast.File{
				Decls: []ast.Decl{
					&ast.FuncDecl{
						FuncType: &ast.FuncType{
							Result: &ast.Ident{
								NamePos: 70,
								Name:    "int",
							},
							Lparen: 78,
							Params: []*ast.VarDecl{
								{
									VarType: &ast.Ident{
										NamePos: 79,
										Name:    "void",
									},
								},
							},
							Rparen: 83,
						},
						FuncName: &ast.Ident{
							NamePos: 74,
							Name:    "main",
						},
						Body: &ast.BlockStmt{
							Lbrace: 84,
							Items: []ast.BlockItem{
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 88,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 92,
										Name:    "x",
									},
								},
								&ast.VarDecl{
									VarType: &ast.Ident{
										NamePos: 97,
										Name:    "int",
									},
									VarName: &ast.Ident{
										NamePos: 101,
										Name:    "y",
									},
								},
								&ast.IfStmt{
									If: 107,
									Cond: &ast.Ident{
										NamePos: 110,
										Name:    "x",
									},
									Body: &ast.IfStmt{
										If: 118,
										Cond: &ast.Ident{
											NamePos: 122,
											Name:    "y",
										},
										Body: &ast.ExprStmt{
											X: &ast.BinaryExpr{
												X: &ast.Ident{
													NamePos: 125,
													Name:    "x",
												},
												OpPos: 127,
												Op:    token.Assign,
												Y: &ast.BasicLit{
													ValPos: 129,
													Kind:   token.IntLit,
													Val:    "4711",
												},
											},
										},
										Else: &ast.ExprStmt{
											X: &ast.BinaryExpr{
												X: &ast.Ident{
													NamePos: 144,
													Name:    "x",
												},
												OpPos: 145,
												Op:    token.Assign,
												Y: &ast.BasicLit{
													ValPos: 146,
													Kind:   token.IntLit,
													Val:    "42",
												},
											},
										},
									},
								},
							},
							Rbrace: 150,
						},
					},
				},
			},
		},
	}

	for _, g := range golden {
		log.Println("path:", g.path)
		s, err := scanner.Open(g.path)
		if err != nil {
			t.Error(err)
			continue
		}
		p := parser.NewParser()
		file, err := p.Parse(s)
		if err != nil {
			t.Error(err)
			continue
		}
		got := file.(*ast.File)
		if !reflect.DeepEqual(got, g.want) {
			t.Errorf("%q: AST mismatch; expected %#v, got %#v", g.path, g.want, got)
			fmt.Println(pretty.Diff(g.want, got))
		}
	}
}
Exemple #8
0
func TestParserError(t *testing.T) {
	var golden = []struct {
		path string
		want string
	}{
		{
			path: "../../testdata/incorrect/parser/pe01.c",
			want: `102: unexpected ")", expected ["!" "(" "-" "char_lit" "ident" "int_lit"]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe02.c",
			want: `112: unexpected "}", expected ["!=" "&&" "*" "+" "-" "/" ";" "<" "<=" "=" "==" ">" ">="]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe03.c",
			want: `129: unexpected "}", expected ["!" "(" "-" ";" "char_lit" "ident" "if" "int_lit" "return" "while" "{"]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe04.c",
			want: `111: unexpected "a", expected ["!=" "&&" "(" "*" "+" "-" "/" ";" "<" "<=" "=" "==" ">" ">=" "["]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe05.c",
			want: `71: unexpected "else", expected ["ident"]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe06.c",
			want: `73: unexpected "b", expected ["(" ";" "["]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe07.c",
			want: `72: unexpected ",", expected ["(" ";" "["]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe08.c",
			want: `86: unexpected "42", expected [";" "{"]`,
		},
		{
			// TODO: The ';' at offset 80 in pe09.c shuold probably be a '{', as
			// indicated by the comment "// '}' missing "
			//
			// Update this test case if the test file is fixed.
			path: "../../testdata/incorrect/parser/pe09.c",
			want: `87: unexpected ";", expected ["$" "ident" "typedef"]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe10.c",
			want: `135: unexpected ")", expected ["!" "(" "-" "char_lit" "ident" "int_lit"]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe11.c",
			want: `70: unexpected "(", expected ["ident"]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe12.c",
			want: `77: unexpected "{", expected ["(" ";" "["]`,
		},
		{
			path: "../../testdata/incorrect/parser/pe13.c",
			// Note, empty parameter list is explicitly allowed by the parser,
			// and act the same as a void parameter, as per design decisions.
			//
			// References.
			//    https://github.com/mewmew/uc/issues/46
			//    https://github.com/mewmew/uc/issues/50
			//    https://github.com/mewmew/uc/issues/33
			want: "",
		},
		{
			path: "../../testdata/incorrect/parser/pe14.c",
			// Note, nested procedures is explicitly allowed by the parser, as the
			// validation is postponed to the semantic analysis checker.
			//
			// References.
			//    https://github.com/mewmew/uc/issues/38
			//    https://github.com/mewmew/uc/issues/43
			want: "",
		},
	}

	for _, g := range golden {
		log.Println("path:", g.path)
		s, err := scanner.Open(g.path)
		if err != nil {
			t.Error(err)
			continue
		}
		p := parser.NewParser()
		_, err = p.Parse(s)
		got := ""
		if err != nil {
			if e, ok := err.(*errors.Error); ok {
				// Unwrap Gocc error.
				err = parser.NewError(e)
			}
			got = err.Error()
		}
		if got != g.want {
			t.Errorf("%q: error mismatch; expected `%v`, got `%v`", g.path, g.want, got)
		}
	}
}
Exemple #9
0
func TestCheckError(t *testing.T) {
	var golden = []struct {
		path string
		want string
	}{
		{
			path: "../testdata/quiet/semantic/s02.c",
			want: `(../testdata/quiet/semantic/s02.c:3) error: missing return at end of non-void function "foo"
  ; }
    ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se01.c",
			want: `(../testdata/incorrect/semantic/se01.c:5) error: undeclared identifier "b"
 a = a + b; // Variable 'b' not defined
         ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se02.c",
			want: `(../testdata/incorrect/semantic/se02.c:5) error: undeclared identifier "foo"
  a = foo(a); // Function 'foo' not defined
      ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se03.c",
			want: `(../testdata/incorrect/semantic/se03.c:3) error: undeclared identifier "output"
  output(0); // Procedure 'output' not defined
  ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se04.c",
			want: `(../testdata/incorrect/semantic/se04.c:5) error: redefinition of "a" with type "char" instead of "int"
char a;  // Redeclaration of 'a'
     ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se05.c",
			want: `(../testdata/incorrect/semantic/se05.c:5) error: redefinition of "a" with type "void(void)" instead of "int"
void a(void) {  // Attempt to redefine variable 'a'
     ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se06.c",
			want: `(../testdata/incorrect/semantic/se06.c:7) error: redefinition of "a"
int a(int i) {   // Redeclaration of 'a'
    ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se07.c",
			want: `(../testdata/incorrect/semantic/se07.c:4) error: returning "int" from a function with incompatible result type "void"
  return 2 * n; // Attempt to return value from procedure
         ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se08.c",
			want: `(../testdata/incorrect/semantic/se08.c:4) error: returning "void" from a function with incompatible result type "int"
  return;  // Void return from function
  ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se09.c",
			want: `(../testdata/incorrect/semantic/se09.c:6) error: returning "char[1]" from a function with incompatible result type "int"
  else return x;    // Return from function with erroneous type
              ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se10.c",
			want: `(../testdata/incorrect/semantic/se10.c:6) error: invalid operation: n[2] (type "int" does not support indexing)
  n[2]; // Index an integer
   ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se11.c",
			want: `(../testdata/incorrect/semantic/se11.c:4) error: cannot assign to "a" of type "int(void)"
  a = 1; // 'a' is not an lval
    ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se12.c",
			want: `(../testdata/incorrect/semantic/se12.c:6) error: cannot call non-function "a" of type "int"
  a(2); // 'a' is not a function
   ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se13.c",
			want: `(../testdata/incorrect/semantic/se13.c:8) error: invalid operands to binary expression: 1 + foo(0) ("int" and "void")
  1 + foo(0); // 'foo' does not return a value
    ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se14.c",
			want: `(../testdata/incorrect/semantic/se14.c:12) error: cannot call non-function "f" of type "int"
  f(n);  // 'f' refers only to the local variable
   ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se15.c",
			want: `(../testdata/incorrect/semantic/se15.c:8) error: calling "q" with too few arguments; expected 3, got 2
  1 + q(1, 3); // Too few arguments to function 'q'
       ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se16.c",
			want: `(../testdata/incorrect/semantic/se16.c:9) error: calling "d" with too many arguments; expected 2, got 3
  d(1, 2, 3); // Too many arguments to function 'd'
   ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se17.c",
			want: `(../testdata/incorrect/semantic/se17.c:6) error: invalid operation: hello + 1 (type mismatch between "char[5]" and "int")
  hello+1; //  Attempt to use char array in arithmetic. (legal in C)
       ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se18.c",
			want: `(../testdata/incorrect/semantic/se18.c:6) error: cannot assign to "a" of type "char[10]"
  a = 42;   // assign int to array of char
    ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se19.c",
			want: `(../testdata/incorrect/semantic/se19.c:5) error: invalid operation: a == 42 (type mismatch between "char[10]" and "int")
  if (a==42) ;
       ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se20.c",
			want: `(../testdata/incorrect/semantic/se20.c:7) error: cannot assign to "a" of type "int[10]"
  a=b;
   ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se21.c",
			want: `(../testdata/incorrect/semantic/se21.c:5) error: returning "char[10]" from a function with incompatible result type "int"
    return bv;  //  Return from function with erroneous type
           ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se22.c",
			want: `(../testdata/incorrect/semantic/se22.c:6) error: invalid operation: a + 1 (type mismatch between "char[10]" and "int")
  a+1; // Attempt to apply arithmetic to array reference
   ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se23.c",
			want: `(../testdata/incorrect/semantic/se23.c:6) error: invalid operation: b[0] (type "int" does not support indexing)
  return b[0]; //not an array!
          ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se24.c",
			want: `(../testdata/incorrect/semantic/se24.c:6) error: cannot assign to "b" of type "int[10]"
  b = a;  // b cannot be assigned
    ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se25.c",
			want: `(../testdata/incorrect/semantic/se25.c:4) error: cannot assign to "(1 + 2)" of type "int"
  (1 + 2) = 3; //No assignment here!
          ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se26.c",
			want: `(../testdata/incorrect/semantic/se26.c:9) error: calling "f" with incompatible argument type "char[10]" to parameter of type "int[]"
  f(a);
    ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se27.c",
			want: `(../testdata/incorrect/semantic/se27.c:4) error: returning "int" from a function with incompatible result type "void"
  if (1<2) return 2 * n; // Attempt to return value from procedure
                  ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se28.c",
			want: `(../testdata/incorrect/semantic/se28.c:5) error: returning "int" from a function with incompatible result type "void"
  else  return 2 * n; // Attempt to return value from procedure
               ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se29.c",
			want: `(../testdata/incorrect/semantic/se29.c:4) error: redefinition of "n" with type "char" instead of "int"
  char n;
       ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se30.c",
			want: `(../testdata/incorrect/semantic/se30.c:6) error: cannot assign to "a" (type mismatch between "int" and "int[10]")
  a=b;
   ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se31.c",
			want: `(../testdata/incorrect/semantic/se31.c:5) error: redefinition of "a" with type "void(void)" instead of "int"
void a(void);   // Attempt to redefine  'a' as extern
     ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se32.c",
			want: `(../testdata/incorrect/semantic/se32.c:6) error: invalid operands to binary expression: 1 + foo(0) ("int" and "void")
  1 + foo(0); // 'foo' does not return a value
    ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se33.c",
			want: `(../testdata/incorrect/semantic/se33.c:6) error: calling "q" with too few arguments; expected 3, got 2
  1 + q(1, 3); // Too few arguments to function 'q'
       ^`,
		},
		{
			path: "../testdata/incorrect/semantic/se34.c",
			want: `(../testdata/incorrect/semantic/se34.c:6) error: calling "d" with too many arguments; expected 2, got 3
  d(1, 2, 3); // Too many arguments to function 'd'
   ^`,
		},

		// Extra test cases.
		{
			path: "../testdata/extra/semantic/extra-void-arg.c",
			want: `(../testdata/extra/semantic/extra-void-arg.c:4) error: "void" must be the only parameter
void f(int a, void) {
      ^`,
		},
		{
			path: "../testdata/extra/semantic/incompatible-arg-type.c",
			want: `(../testdata/extra/semantic/incompatible-arg-type.c:10) error: calling "a" with incompatible argument type "int" to parameter of type "int[]"
 return a(b);
          ^`,
		},
		{
			path: "../testdata/extra/semantic/index-array.c",
			want: `(../testdata/extra/semantic/index-array.c:7) error: invalid array index; expected integer, got "int[20]"
 x[y];
   ^`,
		},
		{
			path: "../testdata/extra/semantic/local-var-redef.c",
			want: `(../testdata/extra/semantic/local-var-redef.c:6) error: redefinition of "x"
 int x;
     ^`,
		},
		{
			path: "../testdata/extra/semantic/missing-return.c",
			want: `(../testdata/extra/semantic/missing-return.c:10) error: missing return at end of non-void function "f"
}
^`,
		},
		{
			path: "../testdata/extra/semantic/param-redef.c",
			want: `(../testdata/extra/semantic/param-redef.c:5) error: redefinition of "x"
 int x;
     ^`,
		},
		{
			path: "../testdata/extra/semantic/unnamed-arg.c",
			want: `(../testdata/extra/semantic/unnamed-arg.c:4) error: parameter name obmitted
void f(int) {
       ^`,
		},
		{
			path: "../testdata/extra/semantic/variable-sized-array.c",
			want: `(../testdata/extra/semantic/variable-sized-array.c:5) error: array size or initializer missing for "y"
 char y[];
      ^`,
		},
		{
			path: "../testdata/extra/semantic/void-array.c",
			want: `(../testdata/extra/semantic/void-array.c:5) error: invalid element type "void" of array "x"
 void x[10];
      ^`,
		},
		{
			path: "../testdata/extra/semantic/void-array-arg.c",
			want: `(../testdata/extra/semantic/void-array-arg.c:4) error: invalid element type "void" of array "x"
void f(void x[]) {
            ^`,
		},
		{
			path: "../testdata/extra/semantic/void-param.c",
			want: `(../testdata/extra/semantic/void-param.c:4) error: "x" has invalid type "void"
void f(void x) {
            ^`,
		},
		{
			path: "../testdata/extra/semantic/void-params.c",
			want: `(../testdata/extra/semantic/void-params.c:4) error: "void" must be the only parameter
void f(void, void) {
      ^`,
		},
		{
			path: "../testdata/extra/semantic/void-var.c",
			want: `(../testdata/extra/semantic/void-var.c:5) error: "x" has invalid type "void"
 void x;
      ^`,
		},
	}

	errors.UseColor = false

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

		p := parser.NewParser()
		file, err := p.Parse(s)
		if err != nil {
			t.Error(err)
			continue
		}
		f := file.(*ast.File)

		got := ""
		if _, err := sem.Check(f); err != nil {
			if e, ok := err.(*errutil.ErrInfo); ok {
				// Unwrap errutil error.
				err = e.Err
				if e, ok := err.(*errors.Error); ok {
					// Unwrap semantic error.
					e.Src = src
				}
			}
			got = err.Error()
		}
		if got != g.want {
			t.Errorf("%q: error mismatch; expected `%v`, got `%v`", g.path, g.want, got)
		}
	}
}