arabelatso/python-to-dafny-translator
Translate Python programs into equivalent Dafny code, preserving program semantics and ensuring the generated code is well-typed, executable, and verifiable. Use when the user asks to convert Python code to Dafny, port Python programs to Dafny, add formal verification to Python code, or create Dafny versions of Python algorithms with specifications.
npx skills add https://github.com/ArabelaTso/Skills-4-SE --skill python-to-dafny-translator
Transform Python programs into equivalent Dafny code that preserves the original semantics while adding formal specifications and leveraging Dafny's verification capabilities.
Understand the Python program structure and semantics:
Plan the Dafny equivalent:
int for integers (arbitrary precision)nat for non-negative integersbool for booleansstring for stringsreal for floating-point (when needed)seq<T> for listsset<T> for setsmap<K, V> for dictionariesarray<T> for mutable arraysrequires)ensures)modifies, reads)Follow these translation principles:
Pattern: Pure function
# Python
def add(a, b):
return a + b
// Dafny
function add(a: int, b: int): int
{
a + b
}
Pattern: Function with specifications
# Python
def max_value(a, b):
"""Returns the maximum of a and b"""
return a if a > b else b
// Dafny
function max(a: int, b: int): int
ensures max(a, b) >= a && max(a, b) >= b
ensures max(a, b) == a || max(a, b) == b
{
if a > b then a else b
}
Pattern: Method with side effects
# Python
def increment_counter(counter):
counter[0] += 1
// Dafny
method incrementCounter(counter: array<int>)
requires counter.Length > 0
modifies counter
ensures counter[0] == old(counter[0]) + 1
{
counter[0] := counter[0] + 1;
}
Pattern: If-else
# Python
def abs_value(x):
if x < 0:
return -x
else:
return x
// Dafny
function abs(x: int): int
ensures abs(x) >= 0
{
if x < 0 then -x else x
}
Pattern: For loop → While loop with invariants
# Python
def sum_array(arr):
total = 0
for i in range(len(arr)):
total += arr[i]
return total
// Dafny
method sumArray(arr: array<int>) returns (total: int)
{
total := 0;
var i := 0;
while i < arr.Length
invariant 0 <= i <= arr.Length
invariant total == sum(arr[..i])
{
total := total + arr[i];
i := i + 1;
}
}
function sum(s: seq<int>): int
{
if |s| == 0 then 0 else s[0] + sum(s[1..])
}
Pattern: Lists → Sequences
# Python
lst = [1, 2, 3, 4, 5]
first = lst[0]
length = len(lst)
// Dafny
var lst: seq<int> := [1, 2, 3, 4, 5];
var first: int := lst[0];
var length: int := |lst|;
Pattern: Dictionaries → Maps
# Python
d = {"a": 1, "b": 2}
value = d["a"]
// Dafny
var d: map<string, int> := map["a" := 1, "b" := 2];
var value: int := d["a"];
Pattern: Simple class
# Python
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, dx, dy):
self.x += dx
self.y += dy
// Dafny
class Point {
var x: int
var y: int
constructor(x: int, y: int)
ensures this.x == x && this.y == y
{
this.x := x;
this.y := y;
}
method move(dx: int, dy: int)
modifies this
ensures x == old(x) + dx
ensures y == old(y) + dy
{
x := x + dx;
y := y + dy;
}
}
Enhance the Dafny code with formal specifications:
requires):requires n >= 0 for factorialensures):ensures result >= 0 for absolute valueinvariant 0 <= i <= n for loop countermodifies: What can be modified?reads: What can be read?Ensure the translated code is correct:
dafny verify program.dfy
dafny run program.dfy
Improve the translated code:
// Computes the factorial of n
function factorial(n: nat): nat
For detailed patterns, see translation_patterns.md.
| Python | Dafny |
|--------|-------|
| x = 10 | var x: int := 10; |
| def f(x): | function f(x: int): int or method f(x: int) |
| return x | x (function) or return x; (method) |
| if x > 0: | if x > 0 then or if x > 0 { |
| for i in range(n): | while i < n with invariants |
| [1, 2, 3] | [1, 2, 3] (seq) |
| {1, 2, 3} | {1, 2, 3} (set) |
| {"a": 1} | map["a" := 1] |
| len(lst) | \|lst\| |
| lst[i] | lst[i] |
| class C: | class C { |
| None | Use Option<T> datatype |
Python Input:
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
Dafny Output:
function factorial(n: nat): nat
ensures factorial(n) >= 1
{
if n <= 1 then 1
else n * factorial(n - 1)
}
Python Input:
def find_max(arr):
if len(arr) == 0:
return None
max_val = arr[0]
for i in range(1, len(arr)):
if arr[i] > max_val:
max_val = arr[i]
return max_val
Dafny Output:
method findMax(arr: array<int>) returns (maxVal: int)
requires arr.Length > 0
ensures forall k :: 0 <= k < arr.Length ==> maxVal >= arr[k]
ensures exists k :: 0 <= k < arr.Length && maxVal == arr[k]
{
maxVal := arr[0];
var i := 1;
while i < arr.Length
invariant 1 <= i <= arr.Length
invariant forall k :: 0 <= k < i ==> maxVal >= arr[k]
invariant exists k :: 0 <= k < i && maxVal == arr[k]
{
if arr[i] > maxVal {
maxVal := arr[i];
}
i := i + 1;
}
}
Python Input:
class BankAccount:
def __init__(self, balance):
self.balance = balance
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
return True
return False
Dafny Output:
class BankAccount {
var balance: int
constructor(balance: int)
requires balance >= 0
ensures this.balance == balance
{
this.balance := balance;
}
method deposit(amount: int)
requires amount >= 0
modifies this
ensures balance == old(balance) + amount
{
balance := balance + amount;
}
method withdraw(amount: int) returns (success: bool)
requires amount >= 0
modifies this
ensures success ==> balance == old(balance) - amount
ensures !success ==> balance == old(balance)
ensures success <==> amount <= old(balance)
{
if amount <= balance {
balance := balance - amount;
return true;
}
return false;
}
}
Python Input:
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
Dafny Output:
method binarySearch(arr: array<int>, target: int) returns (index: int)
requires forall i, j :: 0 <= i < j < arr.Length ==> arr[i] <= arr[j]
ensures 0 <= index < arr.Length ==> arr[index] == target
ensures index == -1 ==> forall k :: 0 <= k < arr.Length ==> arr[k] != target
{
var left := 0;
var right := arr.Length - 1;
while left <= right
invariant 0 <= left <= arr.Length
invariant -1 <= right < arr.Length
invariant forall k :: 0 <= k < left ==> arr[k] < target
invariant forall k :: right < k < arr.Length ==> arr[k] > target
{
var mid := (left + right) / 2;
if arr[mid] == target {
return mid;
} else if arr[mid] < target {
left := mid + 1;
} else {
right := mid - 1;
}
}
return -1;
}
nat for non-negative values, seq for immutable listsNone, Dafny uses Option<T> datatypeTake arabelatso/python-to-dafny-translator from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.