2. Install dependencies from `requirements.txt` using `pip`: `pip install -r requirements.txt`.
3. That's all!
-## Integration tests
-
-To run the unit tests, run `python integration_tests.py`. You can test just the output of the examples or just the memory usage of the tests by running
-`python integration_tests.py OutputTests` or `python integration_tests.py MemoryLeakTests` respectively.
-
# Running
-Example Fur programs are in the `examples/` folder. The main compiler (`main.py`) compiles Fur
+Example Fur programs are in the `examples/` folder. The main compiler (`main.py compile`) compiles Fur
programs to C. An example of usage:
~/fur$ python main.py examples/01_hello.fur
~/fur$ ./a.out
Hello, world~/fur$
+You can also run the programs through an interpreter (`main.py interpret`):
+
+ ~/fur$ python main.py examples/01_hello.fur
+ Hello, world$
+
+The final way to invoke the main program is `main.py ir`. This outputs an intermediate "assembly" for the bytecode representation of the program:
+
+ ~/fur$ python main.py ir examples/01_hello.fur
+ __main__:
+ push_string "Hello, world"
+ push sym(print)
+ call 1
+ drop
+ end nil
+
+## Integration tests
+
+Integration tests are divided into three categories:
+
+* Compiler output tests: test that compiled Fur programs give expected output. Run with `python integration_tests.py CompilerOutputTests`.
+* Interpreter output tests: test that interpreted Fur programs give expected output. Run with `python integration_tests.py InterpreterOutputTests`.
+* Memory lead tests: test that compiled Fur programs don't leak memory (requires Valgrind). Run with `python integration_tests.py MemoryLeakTests`.
+
+Calling `python integration_tests.py` with no arguments runs all the integration tests.
+
## Disclaimers
Fur is GPL 3 and will only ever target GPL compilers. Fur supports closures, integer math, boolean
# Go to the directory of the current file so we know where we are in the filesystem
os.chdir(os.path.dirname(os.path.abspath(__file__)))
-class OutputTests(unittest.TestCase):
+class InterpreterOutputTests(unittest.TestCase):
pass
-def add_example_output_test(filename):
+def add_example_interpreter_output_test(filename):
+ def test(self):
+ try:
+ p = subprocess.Popen(
+ 'python main.py interpret {}'.format(
+ os.path.join('examples', filename),
+ ),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+
+ actual_stdout, actual_stderr = p.communicate()
+
+ expected_stdout_path = os.path.join('examples', filename + '.stdout.txt')
+
+ if os.path.isfile(expected_stdout_path):
+ with open(expected_stdout_path, 'rb') as f:
+ expected_stdout = f.read()
+ else:
+ expected_stdout = b''
+
+ expected_stderr_path = os.path.join('examples', filename + '.stderr.txt')
+
+ if os.path.isfile(expected_stderr_path):
+ with open(expected_stderr_path, 'rb') as f:
+ expected_stderr = f.read()
+ else:
+ expected_stderr = b''
+
+ self.assertEqual(expected_stderr, actual_stderr)
+
+ # We don't clean up the C file in the finally clause because it can be useful to have in case of errors
+ os.remove(os.path.join('examples', filename + '.c'))
+
+ finally:
+ try:
+ os.remove('a.out')
+ except OSError:
+ pass
+
+ setattr(InterpreterOutputTests, 'test_' + filename, test)
+
+class CompilerOutputTests(unittest.TestCase):
+ pass
+
+def add_example_compiler_output_test(filename):
def test(self):
compile_fur_to_c_result = subprocess.call([
'python',
'main.py',
+ 'compile',
os.path.join('examples', filename),
])
except OSError:
pass
- setattr(OutputTests, 'test_' + filename, test)
+ setattr(CompilerOutputTests, 'test_' + filename, test)
class MemoryLeakTests(unittest.TestCase):
pass
)
for filename in filenames:
- add_example_output_test(filename)
+ add_example_compiler_output_test(filename)
+ add_example_interpreter_output_test(filename)
add_example_memory_leak_test(filename)
unittest.main()
import parsing
import tokenization
-source_path = sys.argv[1]
+command = sys.argv[1]
+
+source_path = sys.argv[2]
with open(source_path, 'r') as f:
source = f.read()
crossplatform_ir = crossplatform_ir_generation.generate(converted)
optimized = optimization.optimize(crossplatform_ir)
-outputted = crossplatform_ir_generation.output(optimized)
-print(outputted)
-generated = c_generation.generate(optimized)
+if command == 'compile':
+ generated = c_generation.generate(optimized)
+
+ assert source_path.endswith('.fur')
+ destination_path = source_path + '.c'
+
+ with open(destination_path, 'w') as f:
+ f.write(generated)
-assert source_path.endswith('.fur')
-destination_path = source_path + '.c'
+elif command == 'ir':
+ outputted = crossplatform_ir_generation.output(optimized)
+ print(outputted)
-with open(destination_path, 'w') as f:
- f.write(generated)
+elif command == 'interpret':
+ interpreter.interpret(optimized)