Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit bd78a83
Showing
6 changed files
with
244 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
Vigil is a *very safe* programming language, and an entry in the [January 2013 PLT Games competition](http://www.pltgames.com/competition/2013/1). | ||
|
||
Many programming language claim to take testing, contracts and safety seriously, but only Vigil is *truly* vigilant about not allowing code that fails to pass programmatic specifications. | ||
|
||
## Syntax and semantics | ||
|
||
Vigil is very similar to Python with the minor proviso that you must provide a `main()` function which will be automatically called for you. | ||
|
||
Infinitely more important than mere syntax and semantics are its addition of **supreme moral vigilance**. This is similar to contracts, but less legal and more medieval. | ||
|
||
### The `implore` statement to beseech your needs | ||
|
||
Often, a function will require that parameters have certain desireable properties. A function in Vigil can state what it requires by using `implore`: | ||
|
||
``` | ||
def square_root(n): | ||
implore n >= 0 | ||
return math.sqrt(n) | ||
``` | ||
|
||
If a caller fails to provide valid arguments, it is *wrong* and must be punished. | ||
|
||
### The `swear` statement to state what you provide in return | ||
|
||
If a good caller meets its obligations, the onus is thus on you to fulfill your end of the bargain. You can state the oaths that you promise to uphold using `swear`: | ||
|
||
``` | ||
def fib(n): | ||
if n < 2: | ||
result = n | ||
else: | ||
result = fib(n - 1) + fib(n - 2) | ||
# fib() never returns negative number. | ||
swear result >= 0 | ||
return result | ||
``` | ||
|
||
If a function fails to uphold what it has sworn to do, it is *wrong* and must be punished. | ||
|
||
### Unhandled exceptions | ||
|
||
It goes without saying that any function that throws an exception which isn't caught is *wrong* and must be punished. | ||
|
||
## Runtime vigilance | ||
|
||
This is where Vigil sets itself apart from weaker languages that lack the courage of their convictions. When a Vigil program is executed, Vigil itself will monitor all oaths (implorations and swears) that have been made. If an oath is broken, the offending function (the called in the case of `implore` and the callee in the case of `swear`) *will be duly punished.* | ||
|
||
How? | ||
|
||
**Simple: it will be deleted from your source code.** | ||
|
||
The only way to ensure your program meets its requirements to absolutely forbid code that fails to do so. With Vigil, it will do this for you *automatically*. After enough runs, Vigil promises that all remaining code meets its oaths. | ||
|
||
## Usage | ||
|
||
Vigil is a command-line executable. Pass it the path to a file to run: | ||
|
||
``` | ||
$ ./vigil example/hello.vg | ||
``` | ||
|
||
The "example" directory has some to get you started. | ||
|
||
## FAQ | ||
|
||
### Is this serious? | ||
|
||
Eternal moral vigilance is no laughing matter. | ||
|
||
### But isn't a language that deletes code crazy? | ||
|
||
No, wanting to keep code that demonstrably has bugs *according to its own specifications* is crazy. What good could it possibly serve? It is corrupted and must be cleansed from your codebase. | ||
|
||
Vigil will do this for you automatically. | ||
|
||
### Vigil deleted a function. Won't that cause the functions that call it to fail? | ||
|
||
It would seem that those functions appear to be corrupted as well. Run Vigil again and it will take care of that for you. Several invocations may be required to fully excise all bugs from your code. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# This shows what happens when a caller fails to keep its oath. | ||
import math | ||
|
||
def square_root(n): | ||
""" | ||
Calculates the square root of the given number, which must be non-negative. | ||
""" | ||
implore n >= 0 | ||
|
||
return math.sqrt(abs(n)) | ||
|
||
def evil_fn(): | ||
square_root(-1) | ||
|
||
def main(): | ||
evil_fn() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# This shows what happens when a function fails to keep its oath. | ||
|
||
def bad_abs(n): | ||
""" | ||
Calculates the absolute value of the given number. Always returns a | ||
non-negative number. | ||
""" | ||
result = -n # Oops. | ||
|
||
swear result >= 0 | ||
return result | ||
|
||
def innocent_fn(): | ||
print bad_abs(-2) # OK... | ||
print bad_abs(2) # Uh-oh... | ||
|
||
def main(): | ||
innocent_fn() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# This is an innocent program which meets all of its moral obligations. | ||
|
||
def fib(n): | ||
""" | ||
Calculates the nth value in the Fibonacci sequence. | ||
|
||
"n" must be non-negative, and the result will be as well. | ||
""" | ||
implore n >= 0 | ||
if n < 2: | ||
result = n | ||
else: | ||
result = fib(n - 1) + fib(n - 2) | ||
|
||
swear result >= 0 | ||
return result | ||
|
||
def main(): | ||
print "Let's see some Fibonacci numbers!" | ||
for n in xrange(0, 10): | ||
print n, ':', fib(n) | ||
print "I feel happy and innocent!" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# This shows what happens when a function throws an unhandled exception. | ||
|
||
def say_hello(who): | ||
""" | ||
Greets 'who'. | ||
""" | ||
|
||
print "Hi,", who | ||
raise Exception("ERROR!") # Oops. | ||
|
||
def innocent_fn(): | ||
say_hello('Bob') | ||
|
||
def main(): | ||
innocent_fn() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
#!/usr/bin/env python | ||
|
||
import re | ||
import sys | ||
import traceback | ||
|
||
source_path = sys.argv[1] | ||
source_lines = None | ||
offenders = [] | ||
|
||
def punish(line, offense): | ||
# No double jeopardy. | ||
for _, _, offend_line in offenders: | ||
if line == offend_line: return | ||
|
||
# Walk back to find the beginning of the function containing this line. | ||
start = line | ||
while start >= 0: | ||
if re.match('def ', source_lines[start]): | ||
break | ||
start -= 1 | ||
|
||
# Walk forward to find the end of the function. | ||
end = line | ||
while end < len(source_lines): | ||
if re.match('^\s*$', source_lines[end]): | ||
break | ||
end += 1 | ||
|
||
# Duly note those functions which hath caused offence. | ||
offenders.append((source_lines[start].strip(), offense, line)) | ||
|
||
# Clear the lines, but don't delete them. That way later failures will | ||
# have the right line number. | ||
for i in xrange(start, end + 1): | ||
source_lines[i] = '' | ||
|
||
def vigil_implore(ok, expr): | ||
if not ok: | ||
bad_line = traceback.extract_stack()[-3][1] | ||
punish(bad_line, | ||
"Denied the needs of a function which implored '%s'." % expr) | ||
|
||
def vigil_swear(ok, expr): | ||
if not ok: | ||
bad_line = traceback.extract_stack()[-2][1] | ||
punish(bad_line, "Swore '%s' and failed to provide such." % expr) | ||
|
||
def vigil_done(): | ||
# Silent vigil if all is well. | ||
if not offenders: | ||
return | ||
|
||
# Strip out the dirty impure lines. | ||
cleansed = filter(len, source_lines) | ||
with open(source_path, 'w') as f: | ||
f.writelines(cleansed) | ||
|
||
print "" | ||
print "" | ||
print "------------------------------------------------------------------" | ||
print "" | ||
print "The ever vigilant watchers of your code have found malfeasance in:" | ||
print "" | ||
|
||
for fn, offense, _ in offenders: | ||
print fn | ||
print "Crime:", offense | ||
print "" | ||
|
||
print "Each has been dealt an appropriate punishment." | ||
|
||
def vigil_uncaught(): | ||
raise_line = traceback.extract_tb(sys.exc_info()[2])[-1][1] | ||
print "uncaught error from line ", raise_line | ||
punish(raise_line, "Raised '%s' which was not caught." % sys.exc_info()[1]) | ||
|
||
with open(source_path) as f: | ||
source_lines = f.readlines() | ||
|
||
source = "" | ||
for line in source_lines: | ||
line = re.sub(r'(\s*)implore (.*)', r'\1vigil_implore(\2, """\2""")', line) | ||
line = re.sub(r'(\s*)swear (.*)', r'\1vigil_swear(\2, """\2""")', line) | ||
source += line | ||
source += """ | ||
try: | ||
main() | ||
except Exception as ex: | ||
vigil_uncaught() | ||
vigil_done() | ||
""" | ||
exec(source) |
bd78a83
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:) Why isn't vigil written in vigil?
bd78a83
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ex nihilo nihil fit.