Initial commit
This commit is contained in:
commit
5dbf58b460
28
compiler/CodeGenVisitor.cpp
Normal file
28
compiler/CodeGenVisitor.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "CodeGenVisitor.h"
|
||||
|
||||
antlrcpp::Any CodeGenVisitor::visitProg(ifccParser::ProgContext *ctx)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
std::cout<< ".globl _main\n" ;
|
||||
std::cout<< " _main: \n" ;
|
||||
#else
|
||||
std::cout<< ".globl main\n" ;
|
||||
std::cout<< " main: \n" ;
|
||||
#endif
|
||||
|
||||
this->visit( ctx->return_stmt() );
|
||||
|
||||
std::cout << " ret\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
antlrcpp::Any CodeGenVisitor::visitReturn_stmt(ifccParser::Return_stmtContext *ctx)
|
||||
{
|
||||
int retval = stoi(ctx->CONST()->getText());
|
||||
|
||||
std::cout << " movl $"<<retval<<", %eax\n" ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
13
compiler/CodeGenVisitor.h
Normal file
13
compiler/CodeGenVisitor.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "antlr4-runtime.h"
|
||||
#include "generated/ifccBaseVisitor.h"
|
||||
|
||||
|
||||
class CodeGenVisitor : public ifccBaseVisitor {
|
||||
public:
|
||||
virtual antlrcpp::Any visitProg(ifccParser::ProgContext *ctx) override ;
|
||||
virtual antlrcpp::Any visitReturn_stmt(ifccParser::Return_stmtContext *ctx) override;
|
||||
};
|
||||
|
||||
71
compiler/Makefile
Normal file
71
compiler/Makefile
Normal file
@ -0,0 +1,71 @@
|
||||
# config.mk contains the paths to antlr4 etc.
|
||||
# Each student should have a config.mk corresponding to her system.
|
||||
# Examples are ubuntu.mk, DI.mk, fedora.mk
|
||||
# Then config.mk should be in the .gitignore of your project
|
||||
include config.mk
|
||||
|
||||
CC=g++
|
||||
CCFLAGS=-g -c -std=c++17 -I$(ANTLRINC) -Wno-attributes # -Wno-defaulted-function-deleted -Wno-unknown-warning-option
|
||||
LDFLAGS=-g
|
||||
|
||||
default: all
|
||||
all: ifcc
|
||||
|
||||
##########################################
|
||||
# link together all pieces of our compiler
|
||||
OBJECTS=build/ifccBaseVisitor.o \
|
||||
build/ifccLexer.o \
|
||||
build/ifccVisitor.o \
|
||||
build/ifccParser.o \
|
||||
build/main.o \
|
||||
build/CodeGenVisitor.o
|
||||
|
||||
ifcc: $(OBJECTS)
|
||||
@mkdir -p build
|
||||
$(CC) $(LDFLAGS) build/*.o $(ANTLRLIB) -o ifcc
|
||||
|
||||
##########################################
|
||||
# compile our hand-writen C++ code: main(), CodeGenVisitor, etc.
|
||||
build/%.o: %.cpp generated/ifccParser.cpp
|
||||
@mkdir -p build
|
||||
$(CC) $(CCFLAGS) -MMD -o $@ $<
|
||||
|
||||
##########################################
|
||||
# compile all the antlr-generated C++
|
||||
build/%.o: generated/%.cpp
|
||||
@mkdir -p build
|
||||
$(CC) $(CCFLAGS) -MMD -o $@ $<
|
||||
|
||||
# automagic dependency management: `gcc -MMD` generates all the .d files for us
|
||||
-include build/*.d
|
||||
build/%.d:
|
||||
|
||||
##########################################
|
||||
# generate the C++ implementation of our Lexer/Parser/Visitor
|
||||
generated/ifccLexer.cpp: generated/ifccParser.cpp
|
||||
generated/ifccVisitor.cpp: generated/ifccParser.cpp
|
||||
generated/ifccBaseVisitor.cpp: generated/ifccParser.cpp
|
||||
generated/ifccParser.cpp: ifcc.g4
|
||||
@mkdir -p generated
|
||||
java -jar $(ANTLRJAR) -visitor -no-listener -Dlanguage=Cpp -o generated ifcc.g4
|
||||
|
||||
# prevent automatic cleanup of "intermediate" files like ifccLexer.cpp etc
|
||||
.PRECIOUS: generated/ifcc%.cpp
|
||||
|
||||
##########################################
|
||||
# view the parse tree in a graphical window
|
||||
|
||||
# Usage: `make gui FILE=path/to/your/file.c`
|
||||
FILE ?= ../tests/testfiles/1_return42.c
|
||||
|
||||
gui:
|
||||
@mkdir -p generated build
|
||||
java -jar $(ANTLRJAR) -Dlanguage=Java -o generated ifcc.g4
|
||||
javac -cp $(ANTLRJAR) -d build generated/*.java
|
||||
java -cp $(ANTLRJAR):build org.antlr.v4.gui.TestRig ifcc axiom -gui $(FILE)
|
||||
|
||||
##########################################
|
||||
# delete all machine-generated files
|
||||
clean:
|
||||
rm -rf build generated
|
||||
rm -f ifcc
|
||||
4
compiler/config-IF501.mk
Normal file
4
compiler/config-IF501.mk
Normal file
@ -0,0 +1,4 @@
|
||||
# these values work with the "install-antlr.sh" script provided for the PLD
|
||||
ANTLRJAR=../antlr/jar/antlr-4.9.2-complete.jar
|
||||
ANTLRINC=../antlr/include
|
||||
ANTLRLIB=../antlr/lib/libantlr4-runtime.a
|
||||
3
compiler/config-wsl-2025.mk
Normal file
3
compiler/config-wsl-2025.mk
Normal file
@ -0,0 +1,3 @@
|
||||
ANTLRJAR=/home/$(USER)/antlr4-install/antlr-4.13.2-complete.jar
|
||||
ANTLRINC=/usr/local/include/antlr4-runtime/
|
||||
ANTLRLIB=/usr/local/lib/libantlr4-runtime.a
|
||||
3
compiler/config.mk
Normal file
3
compiler/config.mk
Normal file
@ -0,0 +1,3 @@
|
||||
ANTLRJAR=/home/$(USER)/antlr4-install/antlr-4.13.2-complete.jar
|
||||
ANTLRINC=/usr/local/include/antlr4-runtime/
|
||||
ANTLRLIB=/usr/local/lib/libantlr4-runtime.a
|
||||
13
compiler/ifcc.g4
Normal file
13
compiler/ifcc.g4
Normal file
@ -0,0 +1,13 @@
|
||||
grammar ifcc;
|
||||
|
||||
axiom : prog EOF ;
|
||||
|
||||
prog : 'int' 'main' '(' ')' '{' return_stmt '}' ;
|
||||
|
||||
return_stmt: RETURN CONST ';' ;
|
||||
|
||||
RETURN : 'return' ;
|
||||
CONST : [0-9]+ ;
|
||||
COMMENT : '/*' .*? '*/' -> skip ;
|
||||
DIRECTIVE : '#' .*? '\n' -> skip ;
|
||||
WS : [ \t\r\n] -> channel(HIDDEN);
|
||||
56
compiler/main.cpp
Normal file
56
compiler/main.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "antlr4-runtime.h"
|
||||
#include "generated/ifccLexer.h"
|
||||
#include "generated/ifccParser.h"
|
||||
#include "generated/ifccBaseVisitor.h"
|
||||
|
||||
#include "CodeGenVisitor.h"
|
||||
|
||||
using namespace antlr4;
|
||||
using namespace std;
|
||||
|
||||
int main(int argn, const char **argv)
|
||||
{
|
||||
stringstream in;
|
||||
if (argn==2)
|
||||
{
|
||||
ifstream lecture(argv[1]);
|
||||
if( !lecture.good() )
|
||||
{
|
||||
cerr<<"error: cannot read file: " << argv[1] << endl ;
|
||||
exit(1);
|
||||
}
|
||||
in << lecture.rdbuf();
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "usage: ifcc path/to/file.c" << endl ;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ANTLRInputStream input(in.str());
|
||||
|
||||
ifccLexer lexer(&input);
|
||||
CommonTokenStream tokens(&lexer);
|
||||
|
||||
tokens.fill();
|
||||
|
||||
ifccParser parser(&tokens);
|
||||
tree::ParseTree* tree = parser.axiom();
|
||||
|
||||
if(parser.getNumberOfSyntaxErrors() != 0)
|
||||
{
|
||||
cerr << "error: syntax error during parsing" << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
CodeGenVisitor v;
|
||||
v.visit(tree);
|
||||
|
||||
return 0;
|
||||
}
|
||||
333
ifcc-test.py
Executable file
333
ifcc-test.py
Executable file
@ -0,0 +1,333 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# In "multiple-files mode" (by default), this script runs both GCC and
|
||||
# IFCC on each test-case provided and compares the results.
|
||||
#
|
||||
# In "single-file mode", we mimic the CLI behaviour of GCC i.e. we
|
||||
# interpret the '-o', '-S', and '-c' options.
|
||||
#
|
||||
# Run "python3 ifcc-test.py --help" for more info.
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
def run_command(string, logfile=None, toscreen=False):
|
||||
""" execute `string` as a shell command. Maybe write stdout+stderr to `logfile` and/or to the toscreen.
|
||||
return the exit status"""
|
||||
|
||||
if args.debug:
|
||||
print("ifcc-test.py: "+string)
|
||||
|
||||
process=subprocess.Popen(string,shell=True,
|
||||
stderr=subprocess.STDOUT,stdout=subprocess.PIPE,
|
||||
text=True,bufsize=0)
|
||||
if logfile:
|
||||
logfile=open(logfile,'w')
|
||||
|
||||
while True:
|
||||
output = process.stdout.readline()
|
||||
if len(output) == 0: # only happens when 'process' has terminated
|
||||
break
|
||||
if logfile: logfile.write(output)
|
||||
if toscreen: sys.stdout.write(output)
|
||||
process.wait() # collect child exit status
|
||||
assert process.returncode != None # sanity check (I was using poll() instead of wait() previously, and did see some unsanity)
|
||||
if logfile:
|
||||
logfile.write(f'\nexit status: {process.returncode}\n')
|
||||
return process.returncode
|
||||
|
||||
def dumpfile(name,quiet=False):
|
||||
data=open(name,"rb").read().decode('utf-8',errors='ignore')
|
||||
if not quiet:
|
||||
print(data,end='')
|
||||
return data
|
||||
|
||||
######################################################################################
|
||||
## ARGPARSE step: make sense of our command-line arguments
|
||||
|
||||
# This is where we decide between multiple-files
|
||||
# mode and single-file mode
|
||||
|
||||
import textwrap
|
||||
import shutil
|
||||
width = shutil.get_terminal_size().columns-2
|
||||
twf=lambda text: textwrap.fill(text,width,initial_indent=' '*4,subsequent_indent=' '*6)
|
||||
|
||||
argparser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description = "Testing script for the ifcc compiler. operates in one of two modes:\n\n"
|
||||
+twf("- Multiple-files mode (by default): Compile several programs with both GCC and IFCC, run them, and compare the results.",)+"\n\n"
|
||||
+twf("- Single-file mode (with options -o,-c and/or -S): Compile and/or assemble and/or link a single program."),
|
||||
epilog="examples:\n\n"
|
||||
+twf("python3 ifcc-test.py testfiles")+'\n'
|
||||
+twf("python3 ifcc-test.py path/to/some/dir/*.c path/to/some/other/dir")+'\n'
|
||||
+'\n'
|
||||
+twf("python3 ifcc-test.py -o ./myprog path/to/some/source.c")+'\n'
|
||||
+twf("python3 ifcc-test.py -S -o truc.s truc.c")+'\n'
|
||||
,
|
||||
)
|
||||
|
||||
argparser.add_argument('input',metavar='PATH',nargs='+',help='For each path given:'
|
||||
+' if it\'s a file, use this file;'
|
||||
+' if it\'s a directory, use all *.c files under this subtree')
|
||||
|
||||
argparser.add_argument('-v','--verbose',action="count",default=0,
|
||||
help='increase verbosity level. You can use this option multiple times.')
|
||||
argparser.add_argument('-d','--debug',action="count",default=0,
|
||||
help='increase quantity of debugging messages (only useful to debug the test script itself)')
|
||||
argparser.add_argument('-S',action = "store_true", help='single-file mode: compile from C to assembly, but do not assemble')
|
||||
argparser.add_argument('-c',action = "store_true", help='single-file mode: compile/assemble to machine code, but do not link')
|
||||
argparser.add_argument('-o','--output',metavar = 'OUTPUTNAME', help='single-file mode: write output to that file')
|
||||
|
||||
args=argparser.parse_args()
|
||||
|
||||
if args.debug >=2:
|
||||
print('debug: command-line arguments '+str(args))
|
||||
|
||||
orig_cwd=os.getcwd()
|
||||
if "ifcc-test-output" in orig_cwd:
|
||||
print('error: cannot run ifcc-test.py from within its own output directory')
|
||||
exit(1)
|
||||
|
||||
pld_base_dir=os.path.normpath(os.path.dirname(__file__))
|
||||
if args.debug:
|
||||
print("ifcc-test.py: "+os.path.dirname(__file__))
|
||||
|
||||
# cleanup stale output directory
|
||||
if os.path.isdir(f'{pld_base_dir}/ifcc-test-output'):
|
||||
run_command(f'rm -rf {pld_base_dir}/ifcc-test-output')
|
||||
|
||||
# Ensure that the `ifcc` executable itself is up-to-date
|
||||
makestatus=run_command(f'cd {pld_base_dir}/compiler; make --question ifcc')
|
||||
if makestatus: # updates are needed
|
||||
makestatus=run_command(f'cd {pld_base_dir}/compiler; make ifcc',toscreen=True) # this time we run `make` for real
|
||||
if makestatus: # if `make` failed, we fail too
|
||||
if os.path.exists("ifcc"): # and we remove any out-of-date compiler (to reduce chance of confusion)
|
||||
os.unlink("ifcc")
|
||||
exit(makestatus)
|
||||
|
||||
##########################################
|
||||
## single-file mode aka "let's act just like GCC (almost)"
|
||||
|
||||
if args.S or args.c or args.output:
|
||||
if args.S and args.c:
|
||||
print("error: options -S and -c are not compatible")
|
||||
exit(1)
|
||||
if len(args.input)>1:
|
||||
print("error: this mode only supports a single input file")
|
||||
exit(1)
|
||||
inputfilename=args.input[0]
|
||||
|
||||
if inputfilename[-2:] != ".c":
|
||||
print("error: incorrect filename suffix (should be '.c'): "+inputfilename)
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
open(inputfilename,"r").close()
|
||||
except Exception as e:
|
||||
print("error: "+e.args[1]+": "+inputfilename)
|
||||
exit(1)
|
||||
|
||||
if (args.S or args.c) and not args.output:
|
||||
print("error: option '-o filename' is required in this mode")
|
||||
exit(1)
|
||||
|
||||
if args.S: # produce assembly
|
||||
if args.output[-2:] != ".s":
|
||||
print("error: output file name must end with '.s'")
|
||||
exit(1)
|
||||
ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename} > {args.output}')
|
||||
if ifccstatus: # let's show error messages on screen
|
||||
exit(run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename}',toscreen=True))
|
||||
else:
|
||||
exit(0)
|
||||
|
||||
elif args.c: # produce machine code
|
||||
if args.output[-2:] != ".o":
|
||||
print("error: output file name must end with '.o'")
|
||||
exit(1)
|
||||
asmname=args.output[:-2]+".s"
|
||||
ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename} > {asmname}')
|
||||
if ifccstatus: # let's show error messages on screen
|
||||
exit(run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename}',toscreen=True))
|
||||
exit(run_command(f'gcc -c -o {args.output} {asmname}',toscreen=True))
|
||||
|
||||
else: # produce an executable
|
||||
if args.output[-2:] in [".o",".c",".s"]:
|
||||
print("error: incorrect name for an executable: "+args.output)
|
||||
exit(1)
|
||||
asmname=args.output+".s"
|
||||
ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename} > {asmname}')
|
||||
if ifccstatus:
|
||||
exit(run_command(f'{pld_base_dir}/compiler/ifcc {inputfilename}', toscreen=True))
|
||||
exit(run_command(f'gcc -o {args.output} {asmname}'))
|
||||
|
||||
# we should never end up here
|
||||
print("unexpected error. please report this bug.")
|
||||
exit(1)
|
||||
|
||||
# if we were not in single-file mode, then it means we are in
|
||||
# multiple-files mode.
|
||||
|
||||
######################################################################################
|
||||
## PREPARE step: find and copy all test-cases under ifcc-test-output
|
||||
|
||||
## Process each cli argument as a filename or subtree
|
||||
os.chdir(orig_cwd)
|
||||
inputfilenames=[]
|
||||
for path in args.input:
|
||||
path=os.path.normpath(path) # collapse redundant slashes etc.
|
||||
if os.path.isfile(path):
|
||||
if path[-2:] == '.c':
|
||||
inputfilenames.append(path)
|
||||
else:
|
||||
print("error: incorrect filename suffix (should be '.c'): "+path)
|
||||
exit(1)
|
||||
elif os.path.isdir(path):
|
||||
for dirpath,dirnames,filenames in os.walk(path):
|
||||
inputfilenames+=[dirpath+'/'+name for name in filenames if name[-2:]=='.c']
|
||||
else:
|
||||
print("error: cannot read input path `"+path+"'")
|
||||
sys.exit(1)
|
||||
|
||||
inputfilenames=sorted(inputfilenames)
|
||||
## debug: after treewalk
|
||||
if args.debug:
|
||||
print("debug: list of files after tree walk:"," ".join(inputfilenames))
|
||||
|
||||
## sanity check
|
||||
if len(inputfilenames) == 0:
|
||||
print("error: found no test-case in: "+" ".join(args.input))
|
||||
sys.exit(1)
|
||||
|
||||
## Check that we actually can read these files. Our goal is to
|
||||
## fail as early as possible when the CLI arguments are wrong.
|
||||
for inputfilename in inputfilenames:
|
||||
try:
|
||||
f=open(inputfilename,"r")
|
||||
f.close()
|
||||
except Exception as e:
|
||||
print("error: "+e.args[1]+": "+inputfilename)
|
||||
exit(1)
|
||||
|
||||
## We're going to copy every test-case in its own subdir of ifcc-test-output
|
||||
os.mkdir(pld_base_dir+'/ifcc-test-output')
|
||||
|
||||
jobs=[]
|
||||
|
||||
for inputfilename in inputfilenames:
|
||||
if args.debug>=2:
|
||||
print("debug: PREPARING "+inputfilename)
|
||||
|
||||
if 'ifcc-test-output' in os.path.realpath(inputfilename):
|
||||
print('error: input filename is within output directory: '+inputfilename)
|
||||
exit(1)
|
||||
|
||||
## each test-case gets copied and processed in its own subdirectory:
|
||||
## ../somedir/subdir/file.c becomes ifcc-test-output/--somedir-subdir-file/input.c
|
||||
subdirname=inputfilename[:-2] # remove the '.c' suffix
|
||||
if pld_base_dir in subdirname: # hide "absolute" part of path when not meaningful
|
||||
subdirname=subdirname[len(pld_base_dir):]
|
||||
subdirname=subdirname.replace('..','-') # keep some punctuation to discern "bla.c" from "../bla.c"
|
||||
subdirname=subdirname.replace('./','') # remove meaningless part of name
|
||||
subdirname=subdirname.replace('/','-') # flatten path to single subdir
|
||||
if args.debug>=2:
|
||||
print("debug: subdir="+subdirname)
|
||||
|
||||
os.mkdir(pld_base_dir+'/ifcc-test-output/'+subdirname)
|
||||
shutil.copyfile(inputfilename,pld_base_dir+'/ifcc-test-output/'+subdirname+'/input.c')
|
||||
jobs.append(subdirname)
|
||||
|
||||
## eliminate duplicate paths from the 'jobs' list
|
||||
unique_jobs=[]
|
||||
for j in jobs:
|
||||
for d in unique_jobs:
|
||||
if os.path.samefile(pld_base_dir+'/ifcc-test-output/'+j,pld_base_dir+'/ifcc-test-output/'+d):
|
||||
break # and skip the 'else' branch
|
||||
else:
|
||||
unique_jobs.append(j)
|
||||
jobs=sorted(unique_jobs)
|
||||
|
||||
# debug: after deduplication
|
||||
if args.debug:
|
||||
print("debug: list of test-cases after PREPARE step:"," ".join(jobs))
|
||||
|
||||
######################################################################################
|
||||
## TEST step: actually compile/link/run each test-case with both compilers.
|
||||
##
|
||||
## if both toolchains agree, this test-case is passed.
|
||||
## otherwise, this is a fail.
|
||||
|
||||
all_ok=True
|
||||
|
||||
for jobname in jobs:
|
||||
os.chdir(f'{pld_base_dir}/ifcc-test-output')
|
||||
|
||||
print('TEST-CASE: '+jobname)
|
||||
os.chdir(jobname)
|
||||
|
||||
## Reference compiler = GCC
|
||||
gccstatus=run_command("gcc -S -o asm-gcc.s input.c", "gcc-compile.txt")
|
||||
if gccstatus == 0:
|
||||
# test-case is a valid program. we should run it
|
||||
gccstatus=run_command("gcc -o exe-gcc asm-gcc.s", "gcc-link.txt")
|
||||
if gccstatus == 0: # then both compile and link stage went well
|
||||
exegccstatus=run_command("./exe-gcc", "gcc-execute.txt")
|
||||
if args.verbose >=2:
|
||||
dumpfile("gcc-execute.txt")
|
||||
|
||||
## IFCC compiler
|
||||
ifccstatus=run_command(f'{pld_base_dir}/compiler/ifcc input.c > asm-ifcc.s', 'ifcc-compile.txt')
|
||||
|
||||
if gccstatus != 0 and ifccstatus != 0:
|
||||
## ifcc correctly rejects invalid program -> test-case ok
|
||||
print("TEST OK")
|
||||
continue
|
||||
elif gccstatus != 0 and ifccstatus == 0:
|
||||
## ifcc wrongly accepts invalid program -> error
|
||||
print("TEST FAIL (your compiler accepts an invalid program)")
|
||||
all_ok=False
|
||||
continue
|
||||
elif gccstatus == 0 and ifccstatus != 0:
|
||||
## ifcc wrongly rejects valid program -> error
|
||||
print("TEST FAIL (your compiler rejects a valid program)")
|
||||
all_ok=False
|
||||
if args.verbose:
|
||||
dumpfile("asm-ifcc.s") # stdout of ifcc
|
||||
dumpfile("ifcc-compile.txt") # stderr of ifcc
|
||||
continue
|
||||
else:
|
||||
## ifcc accepts to compile valid program -> let's link it
|
||||
ldstatus=run_command("gcc -o exe-ifcc asm-ifcc.s", "ifcc-link.txt")
|
||||
if ldstatus:
|
||||
print("TEST FAIL (your compiler produces incorrect assembly)")
|
||||
all_ok=False
|
||||
if args.verbose:
|
||||
dumpfile("asm-ifcc.s")
|
||||
dumpfile("ifcc-link.txt")
|
||||
continue
|
||||
|
||||
## both compilers did produce an executable, so now we run both
|
||||
## these executables and compare the results.
|
||||
|
||||
run_command("./exe-ifcc", "ifcc-execute.txt")
|
||||
if open("gcc-execute.txt").read() != open("ifcc-execute.txt").read() :
|
||||
print("TEST FAIL (different results at execution)")
|
||||
all_ok=False
|
||||
|
||||
if args.verbose:
|
||||
print("GCC:")
|
||||
dumpfile("gcc-execute.txt")
|
||||
print("you:")
|
||||
dumpfile("ifcc-execute.txt")
|
||||
continue
|
||||
|
||||
## last but not least
|
||||
print("TEST OK")
|
||||
|
||||
if not (all_ok or args.verbose):
|
||||
print("Some test-cases failed. Run ifcc-test.py with option '--verbose' for more detailed feedback.")
|
||||
3
testfiles/1_return42.c
Normal file
3
testfiles/1_return42.c
Normal file
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return 42;
|
||||
}
|
||||
1
testfiles/2_invalid_program.c
Normal file
1
testfiles/2_invalid_program.c
Normal file
@ -0,0 +1 @@
|
||||
Si ça c'est du C, moi je suis prof de manga.
|
||||
5
testfiles/3_return_var.c
Normal file
5
testfiles/3_return_var.c
Normal file
@ -0,0 +1,5 @@
|
||||
int main() {
|
||||
int x;
|
||||
x=8;
|
||||
return x;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user