Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #322

Merged
merged 41 commits into from
Feb 10, 2025
Merged

Dev #322

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
b63559a
New: `make run-tests` is like `make tests`, but runs only if sources …
andreas-zeller Feb 7, 2025
5932a75
Fix: handle concatenation of byte strings
andreas-zeller Feb 7, 2025
448f5d6
New: in comparisons, evaluate byte strings and integers as such
andreas-zeller Feb 7, 2025
bc9b518
Doc update
andreas-zeller Feb 7, 2025
1bf8996
New: warn if types are incompatible
andreas-zeller Feb 7, 2025
dfd8aed
New: check grammar for bit length
andreas-zeller Feb 7, 2025
a22699b
Fix: compute integer value correctly
andreas-zeller Feb 7, 2025
c8474ee
New: int() on bits
andreas-zeller Feb 7, 2025
a38b6d0
Implemented additional operators
andreas-zeller Feb 7, 2025
de73f71
Minor fixes
andreas-zeller Feb 7, 2025
d5a46c8
Refined bit counting
andreas-zeller Feb 7, 2025
dca93a1
Refined `make tests`
andreas-zeller Feb 7, 2025
2756e9a
Avoid bit warning
andreas-zeller Feb 7, 2025
a6ea6d2
Adjusted to new syntax and semantics
andreas-zeller Feb 7, 2025
c781354
Allow running without stdlib
andreas-zeller Feb 7, 2025
88e836f
Improved diagnostic capabilities
andreas-zeller Feb 8, 2025
bc8c3bf
Simplified clean()
andreas-zeller Feb 8, 2025
a78de08
Fix: in fix_population(), be sure to handle bits and binary strings p…
andreas-zeller Feb 8, 2025
9c9b286
Make mypy happy
andreas-zeller Feb 8, 2025
41ea9fe
Added a few tests for slices
andreas-zeller Feb 9, 2025
b0d6064
Added a few tests for slices
andreas-zeller Feb 9, 2025
1face2e
Moved slices to their own (now failing) tests
andreas-zeller Feb 9, 2025
2abc81f
Added test for simple parenthesis
andreas-zeller Feb 9, 2025
ec17d8f
New: in Fandango shell, KeyboardInterrupt now returns to shell
andreas-zeller Feb 9, 2025
2320af6
Improved diagnostic capabilities
andreas-zeller Feb 9, 2025
f3fa9bb
Fix: stronger conditions for fix_individual()
andreas-zeller Feb 9, 2025
2abfaa3
New: better checks for type (in)compatibility (#291)
andreas-zeller Feb 9, 2025
532901a
Simplified slicing tests as semantics are still unclear
andreas-zeller Feb 9, 2025
2aaab8d
Clarified why constraints that return `None` are ignored (#290)
andreas-zeller Feb 9, 2025
0306d5d
Fix: compatibility with cached versions
andreas-zeller Feb 9, 2025
bba3725
check_type_compatibility(): improved diagnostics
andreas-zeller Feb 9, 2025
340a6f9
New: No longer ignore `None` results during constraint evaluation (#290)
andreas-zeller Feb 9, 2025
49b904a
Addressed #77
andreas-zeller Feb 9, 2025
77a5db1
Reformatted
andreas-zeller Feb 9, 2025
1a23703
Fix: bad quotes in error message
andreas-zeller Feb 9, 2025
d115d2f
Fix for Python 3.11
andreas-zeller Feb 9, 2025
e8dbeda
Merge branch 'main' into andreas
smythi93 Feb 10, 2025
59dc6c3
Merge pull request #317 from fandango-fuzzer/andreas
joszamama Feb 10, 2025
979c831
fix: faster experiment runs
joszamama Feb 10, 2025
6fa533b
Merge branch 'main' into dev
joszamama Feb 10, 2025
cbc9ab1
Merge branch 'dev' of https://github.com/fandango-fuzzer/fandango int…
joszamama Feb 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,6 @@ instance/
# Sphinx documentation
docs/_build/

# Own test results
tests/test-marker.txt

# PyBuilder
.pybuilder/
target/
Expand Down Expand Up @@ -176,3 +173,8 @@ gen/*
*.o
utils/lextab.py
utils/yacctab.py

# Test result markers
tests/test-marker.txt
evaluation/test-evaluation.txt
evaluation/experiments/test-experiments.txt
49 changes: 38 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,45 @@ clean-docs:

## Tests
TESTS = tests
TEST_SOURCES = $(wildcard $(TESTS)/*.py $(TESTS)/resources/*)
TEST_SOURCES = $(wildcard $(TESTS)/*.py $(TESTS)/resources/* $(TESTS)/docs/*.fan)
TEST_MARKER = $(TESTS)/test-marker.txt

.PHONY: test tests
test tests:
$(PYTEST)
.PHONY: test tests run-tests
test tests $(TEST_MARKER): $(PYTHON_SOURCES) $(TEST_SOURCES)
$(PYTEST)
echo 'Success' > $(TEST_MARKER)

run-tests: $(TEST_MARKER)

## Evaluation
EVALUATION = evaluation
EVALUATION_SOURCES = $(wildcard $(EVALUATION)/*.py $(EVALUATION)/*/*.py $(EVALUATION)/*/*/*.py $(EVALUATION)/*/*/*.fan $(EVALUATION)/*/*/*.txt)
EVALUATION_MARKER = $(EVALUATION)/test-evaluation.txt

# python -m evaluation.vs_isla.run_evaluation
.PHONY: evaluation evaluate
evaluation evaluate $(EVALUATION_MARKER): $(PYTHON_SOURCES) $(EVALUATION_SOURCES)
$(PYTHON) -m evaluation.vs_isla.run_evaluation 1
echo 'Success' > $(EVALUATION_MARKER)

run-evaluation: $(EVALUATION_MARKER)

## Experiments
EXPERIMENTS = $(EVALUATION)/experiments
EXPERIMENTS_SOURCES = $(wildcard $(EXPERIMENTS)/*/*.py $(EXPERIMENTS)/*/*.fan)
EXPERIMENTS_MARKER = $(EXPERIMENTS)/test-experiments.txt

.PHONY: experiment experiments
experiment experiments $(EXPERIMENTS_MARKER): $(PYTHON_SOURCES) $(EXPERIMENTS_SOURCES)
$(PYTHON) -m evaluation.experiments.run_experiments
echo 'Success' > $(EXPERIMENTS_MARKER)

run-experiments: $(EXPERIMENTS_MARKER)

## All
.PHONY: run-all
run-all: $(TEST_MARKER) $(EVALUATION_MARKER) $(EXPERIMENTS_MARKER)
@echo 'All tests passed.'

## Installation
.PHONY: install install-test install-tests
Expand All @@ -185,10 +219,3 @@ install-test install-tests:
uninstall:
$(PIP) uninstall fandango-fuzzer -y

# python -m evaluation.vs_isla.run_evaluation
.PHONY: evaluation evaluate experiment experiments
evaluate evaluation:
$(PYTHON) -m evaluation.vs_isla.run_evaluation 1

experiment experiments:
$(PYTHON) -m evaluation.experiments.run_experiments
44 changes: 16 additions & 28 deletions docs/Bits.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,64 +62,52 @@ assert _exit_code == 0
The combination of `--format=bits` and `--start-symbol` is particularly useful to debug bit fields.
```

Internally, Fandango treats the individual flags as if they were strings - that is, `"\x00"` for zero bits and `"\x01"` for nonzero bits.
Hence, we can also apply _constraints_ to the individual flags:
Internally, Fandango treats individual flags as integers, too.
Hence, we can also apply _constraints_ to the individual flags.
For instance, we can profit from the fact that Python treats `0` as False and `1` as True:

```shell
$ fandango fuzz --format=bits -f bits.fan -n 10 -c '<italic> == "\x01" and <bold> == "\x00"'
$ fandango fuzz --format=bits -f bits.fan -n 10 -c '<italic> and <bold>'
```

```{code-cell}
:tags: ["remove-input"]
!fandango fuzz --format=bits -f bits.fan -n 10 -c '<italic> == "\x01" and <bold> == "\x00"' --validate
!fandango fuzz --format=bits -f bits.fan -n 10 -c '<italic> and <bold>' --validate
assert _exit_code == 0
```

This alternative, using `chr()` to generate a single byte out of the specified integer might be more readable:
Fandango strictly follows a "left-to-right" order - that is, the order in which bits and bytes are specified in the grammar, the most significant bit is stored first.

```shell
$ fandango fuzz --format=bits -f bits.fan -n 1 -c '<italic> == chr(1) and <bold> == chr(0)'
```

```{code-cell}
:tags: ["remove-input"]
!fandango fuzz --format=bits -f bits.fan -n 1 -c '<italic> == chr(1) and <bold> == chr(0)' --validate
assert _exit_code == 0
```

We can also easily set the value of the entire `format_flag` field using a constraint:
Hence, we can also easily set the value of the entire `brightness` field using a constraint:

```shell
$ fandango fuzz --format=bits -f bits.fan -n 1 -c '<format_flag> == chr(0b11110000)'
$ fandango fuzz --format=bits -f bits.fan -n 1 -c '<brightness> == 0b1111'
```

```{code-cell}
:tags: ["remove-input"]
!fandango fuzz --format=bits -f bits.fan -n 1 -c '<format_flag> == chr(0b11110000)' --validate
!fandango fuzz --format=bits -f bits.fan -n 1 -c '<brightness> == 0b1111' --validate
assert _exit_code == 0
```

Since Fandango strictly follows a "left-to-right" order - that is, the order in which bits and bytes are specified in the grammar, the most significant bit is stored first.
Thus, the order of bits in the `chr()` argument is identical to the order of bits in the produced output.

```{note}
Fandango always strictly follows a "left-to-right" order - that is, the order in which bits and bytes are specified in the grammar.
```

To convert a bit into a numerical value, applying the Python `ord()` function comes in handy.
Note that its argument (the symbol) must be converted into a string first:
Of course, we can also give the number in decimal format:

```shell
$ fandango fuzz --format=bits -f bits.fan -n 1 -c 'ord(str(<brightness>)) > 10'
$ fandango fuzz --format=bits -f bits.fan -n 1 -c '<brightness> == 15'
```

% TODO: This does not work with <format_flag> :-(
```{code-cell}
:tags: ["remove-input"]
!fandango fuzz --format=bits -f bits.fan -n 10 -c 'ord(str(<brightness>)) > 10' --validate
!fandango fuzz --format=bits -f bits.fan -n 10 -c '<brightness> == 15' --validate --population-size=20
assert _exit_code == 0
```

Note how the last four bits (the `<brightness>` field) always represent a number greater than ten.
Note how the last four bits (the `<brightness>` field) are always set to `1111` - the number 15.

```{warning}
When implementing a format, be sure to follow its conventions regarding
Expand All @@ -140,7 +128,7 @@ $ echo -n '\xf0' | fandango parse -f bits.fan -o - --format=bits

```{code-cell}
:tags: ["remove-input"]
!echo -n '\xf0' | fandango parse -f bits.fan -o - --format=bits
!echo -n '\xf0' | fandango parse -f bits.fan -o - --format=bits --validate
assert _exit_code == 0
```

Expand Down Expand Up @@ -174,7 +162,7 @@ $ echo -n '\xf0' | fandango parse -f bits.fan -o - --format=grammar

```{code-cell}
:tags: ["remove-input"]
!echo -n '\xf0' | fandango parse -f bits.fan -o - --format=grammar
!echo -n '\xf0' | fandango parse -f bits.fan -o - --format=grammar --validate
assert _exit_code == 0
```

Expand Down
4 changes: 2 additions & 2 deletions docs/gif89a.fan
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

include('gif.fan')

where <GifHeader>..<Signature> == "GIF"
where <GifHeader>..<Version> == "89a"
where <GifHeader>..<Signature> == b"GIF"
where <GifHeader>..<Version> == b"89a"

<char> ::= <byte>
<unsigned_short> ::= <byte> <byte>
Expand Down
23 changes: 13 additions & 10 deletions evaluation/experiments/pixels/pixels.fan
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<start> ::= <img> ;
<img> ::= <width> <height> <pixels> ;
<width> ::= <uint16> ;
<height> ::= <uint16> ;
<pixels> ::= <rgb>* ;
<uint16> ::= <byte> <byte> ;
<rgb> ::= <byte> <byte> <byte> ;
<byte> ::= <bit> <bit> <bit> <bit> <bit> <bit> <bit> <bit> ;
<bit> ::= "0" | "1" ;
<start> ::= <img>
<img> ::= <width> <height> <pixels>
<width> ::= <uint16>
<height> ::= <uint16>
<pixels> ::= <rgb>*
<uint16> ::= <byte> <byte>
<rgb> ::= <byte> <byte> <byte>
<byte> ::= br"[\x00-\xff]"

int(<pixels>) == int(<width>) * int(<height>) * 3;
def uint16(tree):
b = tree.to_bytes()
return b[1] * 256 + b[0]

where len(<pixels>) == uint16(<width>) * uint16(<height>) * 3
2 changes: 1 addition & 1 deletion language/FandangoLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ INTEGER: DECIMAL_INTEGER | OCT_INTEGER | HEX_INTEGER | BIN_INTEGER;

PYTHON_START: '<py>' {self.python_start()};
PYTHON_END : '</py>' {self.python_end()};
NONTERMINAL: '<' ID_CONTINUE+ '>';
NONTERMINAL: '<' NAME '>';

// python keywords
AND : 'and';
Expand Down
15 changes: 12 additions & 3 deletions src/fandango/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,9 @@ def get_parser(in_command_line=True):
)
file_parser.add_argument(
"--format",
choices=["string", "bits", "tree", "repr", "grammar", "none"],
choices=["string", "bits", "tree", "grammar", "value", "repr", "none"],
default="string",
help="produce output(s) as string (default), as a bit string, as a derivation tree, in internal representation, as a grammar, or none",
help="produce output(s) as string (default), as a bit string, as a derivation tree, as a grammar, as a Python value, in internal representation, or none",
)
file_parser.add_argument(
"--file-mode",
Expand Down Expand Up @@ -682,6 +682,8 @@ def convert(s: str) -> str | bytes:
return convert(tree.to_bits())
if args.format == "grammar":
return convert(tree.to_grammar())
if args.format == "value":
return convert(tree.to_value())
if args.format == "none":
return convert("")

Expand Down Expand Up @@ -939,7 +941,12 @@ def fuzz_command(args):

# Ensure that every generated file can be parsed
# and returns the same string as the original
temp_dir = tempfile.TemporaryDirectory(delete=False)
try:
temp_dir = tempfile.TemporaryDirectory(delete=False)
except TypeError:
# Python 3.11 does not know the `delete` argument
temp_dir = tempfile.TemporaryDirectory()

args.directory = temp_dir.name
args.format = "string"
output_population(population, args, file_mode=file_mode, output_on_stdout=False)
Expand Down Expand Up @@ -1281,6 +1288,8 @@ def _complete(text, state):
last_status = run(command, args)
except SystemExit:
pass
except KeyboardInterrupt:
pass

return last_status

Expand Down
Loading