-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFuzzParser.res
More file actions
130 lines (115 loc) · 3.76 KB
/
Copy pathFuzzParser.res
File metadata and controls
130 lines (115 loc) · 3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// SPDX-License-Identifier: MPL-2.0
// Fuzz target for the Error-Lang parser.
//
// Invariant: the parser must NEVER crash on ANY input. It should always
// return a (program, diagnostics) tuple without throwing an uncaught
// exception.
//
// Strategy: lex first, then feed tokens to Parser.parse(). Uses both
// raw random bytes and structured inputs mixing Error-Lang keywords,
// operators, and identifiers for deeper parser coverage.
//
// Run with:
// deno task res:build && node compiler/fuzz/FuzzParser.res.js
open Types
// Interesting fragments biased toward Error-Lang syntax for deeper
// parser coverage. These form syntactically-plausible token sequences.
let fragments = [
// Keywords
"main", "end", "let", "mutable", "function", "struct",
"if", "elseif", "else", "while", "for", "in",
"break", "continue", "return", "and", "or", "not",
"true", "false", "nil", "gutter", "fn",
// Types
"Int", "Float", "String", "Bool", "Array",
// Built-in functions
"print", "println",
// Operators
"+", "-", "*", "/", "%", "=", "==", "!=",
"<", "<=", ">", ">=", "<<", ">>",
"&", "|", "^", "~", "?", ":", "->",
// Delimiters
"(", ")", "[", "]", "{", "}", ",", ".",
// Literals
"42", "0", "3.14", "1e10",
"0xFF", "0b1010",
"\"hello\"", "\"escape\\n\"", "\"\"\"triple\"\"\"",
// Smart quotes (intentional error case)
"\u201C", "\u201D",
// Comments
"#", "# comment\n",
// Whitespace
" ", "\t", "\n", "\r",
// Identifiers
"foo", "bar_baz", "_private", "myVar1",
// Structured patterns — syntactically plausible Error-Lang
"main\n let x = 42\nend",
"function add(a, b)\n return a + b\nend",
"if true\n println(42)\nend",
"struct Point\n x: Int\n y: Int\nend",
"for i in [1, 2, 3]\n println(i)\nend",
"while true\n break\nend",
// Edge cases
"\\", "\x00",
]
// Simple pseudo-random number generator (LCG)
let seed = ref(Date.now()->Float.toInt->Int.mod(2147483647))
let nextRand = () => {
seed := Int.mod(seed.contents * 1103515245 + 12345, 2147483647)
abs(seed.contents)
}
// Generate a random byte string of up to maxLen bytes
let randomBytes = (maxLen: int): string => {
let len = Int.mod(nextRand(), maxLen + 1)
let buf = ref("")
for _ in 0 to len - 1 {
let byte = Int.mod(nextRand(), 256)
buf := buf.contents ++ String.fromCharCode(byte)
}
buf.contents
}
// Generate a fragment-based random input
let randomFragments = (maxLen: int): string => {
let buf = ref("")
while String.length(buf.contents) < maxLen {
let idx = Int.mod(nextRand(), Array.length(fragments))
switch fragments->Array.get(idx) {
| Some(frag) => buf := buf.contents ++ frag
| None => ()
}
}
buf.contents
}
// Generate a random input (50% random bytes, 50% fragments)
let randomInput = (maxLen: int): string => {
if Int.mod(nextRand(), 2) == 0 {
randomBytes(maxLen)
} else {
randomFragments(maxLen)
}
}
// Run the fuzzer
let iterations = 100_000
let () = {
Console.log(`Error-Lang parser fuzzer: running ${Int.toString(iterations)} iterations`)
for i in 1 to iterations {
let input = randomInput(4096)
// Step 1: Lex the input (the lexer must not throw)
let (tokens, _lexDiags) = Lexer.lex(input, "<fuzz>", 1)
// Step 2: Feed tokens to the parser (must not throw)
let (program, parseDiags) = Parser.parse(tokens, "<fuzz>", 1)
// Walk the program to ensure it is well-formed
program.declarations->Array.forEach(decl => {
let _ = decl
})
// Walk diagnostics similarly
parseDiags->Array.forEach(diag => {
let _ = diag.code
let _ = diag.message
})
if Int.mod(i, 10_000) == 0 {
Console.log(` ... ${Int.toString(i)} iterations complete`)
}
}
Console.log(`Error-Lang parser fuzzer: ${Int.toString(iterations)} iterations passed with no crashes`)
}