Skip to content

Commit

Permalink
some cleanup - rewrite tee
Browse files Browse the repository at this point in the history
  • Loading branch information
wbbradley committed Jun 22, 2018
1 parent bc91d4f commit cea39da
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 140 deletions.
54 changes: 35 additions & 19 deletions apps/tee.zion
@@ -1,23 +1,39 @@
# A basic implementation of tee
module tee

get sys
get file

fn main()
if len(sys.args) == 2
newline := "\n"
f := open(sys.args[1], "w")
state := f.state
match state
is file.Open
for line in readlines()
print(line)
write(f, line)
write(f, newline)
close(f)
else
print("Failed to open file " + sys.args[1])
sys.exit(-1)
else
for line in readlines()
print(line)
get argparse {parse, Parser, Some, Option}

fn main() int {
var options [str: Option]
options["-a"] = Option("append", false /*has_arg*/, false)
parser := Parser("tee", options, Some(0, 1))

with args := parse(parser, sys.args[1:len(sys.args)]) {
newline := "\n"
if len(args.positionals) != 0 {
# There was a filename given, so check whether we need to append or create.
with f := open(args.positionals[0], "-a" in args.options ? "a" : "w") {
# Now pipe to both the file and stdout.
for line in readlines() {
print(line)
write(f, line)
write(f, newline)
}
} else {
print("Failed to open file " + sys.args[1])
return 1
}
} else {
# There was no filename given, so just pipe it through
for line in readlines() {
print(line)
}
}
} else parse_error {
print("Command-line parse failed: " + parse_error.reason)
print(parse_error.usage())
}
return 0
}
77 changes: 59 additions & 18 deletions lib/argparse.zion
@@ -1,53 +1,94 @@
module argparse

get resource {MaybeResource, Failed, Acquired}

type Arity is {
# None means no positional parameters are allowed
None
# Some means that some range of positional parameters are allowed. Use 0 to indicate no
# constraint on either the min or max.
Some(min uint, max uint)
}

type Parser has {
description str
options [str: Option]
expect_extras bool
positionals Arity
}

type Option is { Option(name str, has_arg bool, required bool) }
type Option has {
name str
has_arg bool
required bool
}

type ParseError has {
reason str
usage fn () str
}
type Arguments has {
options [str: str]
positionals [str]
}

fn parse(parser Parser, args [str]) resource.MaybeResource [str: str] ParseError {
fn parse(parser Parser, args [str]) MaybeResource Arguments ParseError {
var iarg = 0
var results [str: str]
let results [str: str]
let arguments = Arguments(results, [] as [str])

while iarg < len(args) {
match parser.options[args[iarg]] {
Just(Option(name, has_arg, _)) {
if has_arg {
arg := args[iarg]
match parser.options[arg] {
Just(option) {
if option.has_arg {
if iarg + 1 >= len(args) {
return resource.Failed(ParseError("Option " + args[iarg] + " expects an arg but none was provided", make_usage(parser)))
return Failed(ParseError("Option " + arg + " expects an arg but none was provided", make_usage(parser)))
} else {
results[name] = args[iarg + 1]
arguments.options[option.name] = args[iarg + 1]
iarg += 2
continue
}
} else {
print("found argument " + arg)
arguments.options[arg] = "1"
}
}
Nothing {
return resource.Failed(ParseError("Unexpected parameter " + args[iarg] + " encountered", make_usage(parser)))
match parser.positionals {
None {
return Failed(ParseError("Unexpected parameter " + arg + " encountered", make_usage(parser)))
}
Some(_, max) {
if max != 0 and len(arguments.positionals) == max {
return Failed(ParseError("Too many positional arguments given: " + arg, make_usage(parser)))
} else {
append(arguments.positionals, arg)
}
}
}
}
}

iarg += 1
}

for option in parser.options {
match option.value {
Option(name, _, required) {
if required and name not in results {
return resource.Failed(ParseError("Missing option " + name + " in command line parameters", make_usage(parser)))
}
}
for pair in parser.options {
option := pair.value
if option.required and option.name not in results {
return Failed(ParseError("Missing option " + option.name + " in command line parameters", make_usage(parser)))
}
}

return resource.Acquired(results, fn () {})
min := match(parser.positionals) {
None => 0
Some(min, _) => min
}

if min != 0 and len(arguments.positionals) < min {
return Failed(ParseError("Not enough positional parameters", make_usage(parser)))
}

return Acquired(arguments, fn () {})
}

fn make_usage(parser Parser) fn () str {
Expand Down
1 change: 1 addition & 0 deletions lib/posix.zion
Expand Up @@ -50,6 +50,7 @@ link fn qsort(base *void, nel size_t, width size_t, _ fn _(lhs *void, rhs *void)
link fn raise(signal posix.int) posix.int
link fn read(fileds posix.int, buf *void, nbytes size_t) ssize_t
link fn realloc(ptr *void, cb size_t) *?void
link fn sleep(seconds uint) uint
link fn strcmp(x *char, y *char) posix.int
link fn strdup(s *char) *?char
link fn strlen(s *char) size_t
Expand Down
6 changes: 5 additions & 1 deletion lib/std.zion
Expand Up @@ -65,7 +65,11 @@ fn breakpoint() {
fn rand(max int) int {
var r = 0 as uint
posix.arc4random_buf(&r, sizeof(uint))
return r % max
if r < 0 {
return -r % max
} else {
return r % max
}
}

type IntRange has {
Expand Down
124 changes: 124 additions & 0 deletions play/conway.zion
@@ -0,0 +1,124 @@
# An implementation of Conway's Game of Life.
# Stolen shamelessly from https://golang.org/ in order to
# see how hard it would be to port to Zion.
module conway

# Field represents a two-dimensional field of cells.
type Field has {
s [[bool]]
w int
h int
}

# NewField returns an empty field of the specified width and height.
fn NewField(w int, h int) Field {
# TODO: make array allocation better. It was painful
# to have to remove Go's `make` construct, and to
# manually initialize this 2-d array.
let s [[bool]]
reserve(s, h)
for y in range(h) {
let row [bool]
reserve(row, w)
for x in range(w) {
append(row, false)
}
append(s, row)
}
return Field(s, w, h)
}

# Set sets the state of the specified cell to the given value.
fn Set(f Field, x int, y int, b bool) {
f.s[y][x] = b
}

# Alive reports whether the specified cell is alive.
# If the x or y coordinates are outside the field boundaries they are wrapped
# toroidally. For instance, an x value of -1 is treated as width-1.
fn Alive(f Field, x int, y int) bool {
x += f.w
# TODO: implement mod_assignment_t::render
x = x % f.w
y += f.h
y = y % f.h
return f.s[y][x]
}

# Next returns the state of the specified cell at the next time step.
fn Next(f Field, x int, y int) bool {
# Count the adjacent cells that are alive.
var alive = 0
for i in range(-1, 2) {
for j in range(-1, 2) {
if (j != 0 or i != 0) and Alive(f, x+i, y+j) {
alive += 1
}
}
}
# Return next state according to the game rules:
# exactly 3 neighbors: on,
# exactly 2 neighbors: maintain current state,
# otherwise: off.
return alive == 3 or alive == 2 and Alive(f, x, y)
}

# Life stores the state of a round of Conway's Game of Life.
type Life has {
var a Field
var b Field
w int
h int
}

# NewLife returns a new Life game state with a random initial state.
fn NewLife(w int, h int) Life {
a := NewField(w, h)
for i in range(w * h / 4) {
Set(a, rand(w), rand(h), true)
}
return Life(a, NewField(w, h), w, h)
}

# Step advances the game by one instant, recomputing and updating all cells.
fn Step(l Life) {
# Update the state of the next field (b) from the current field (a).
for y in range(l.h) {
for x in range(l.w) {
Set(l.b, x, y, Next(l.a, x, y))
}
}
# Swap fields a and b.
c := l.a
l.a = l.b
l.b = c
}

# String returns the game board as a str.
[global]
fn str(l Life) str {
# TODO: make a better string builder type thing
var buf str
for y in range(l.h) {
for x in range(l.w) {
if Alive(l.a, x, y) {
buf += "*"
} else {
buf += " "
}
}
buf += "\n"
}
return buf
}

fn main() {
l := NewLife(40, 15)
for i in range(300) {
Step(l)
print("\x0c")
posix.fflush(stdout)
print(l)
posix.sleep(1)
}
}
10 changes: 0 additions & 10 deletions play/csv.zion

This file was deleted.

34 changes: 34 additions & 0 deletions play/fib_closure.zion
@@ -0,0 +1,34 @@
module fib_closure

type Ref T has {
var val T
}

# fib returns a function that returns
# successive Fibonacci numbers.
fn fib() fn() int {
# Consider that in the Golang version of this, it's much more succinct, however that is because
# Go's closure's capture by reference. This may require some consideration as to whether it is
# permissible to allow capture by reference in Zion. Given that it can be simulated by creating
# a user-defined reference type as follows, I'm not certain it's worth introducing more implicit
# impurity.
a := Ref(0)
b := Ref(1)
return fn() int {
return match (b.val, a.val + b.val) {
(next_a, next_b) {
a.val = next_a
b.val = next_b
a.val
}
}
}
}

fn main() {
f := fib()
# Function calls are evaluated left-to-right.
print([f(), f(), f(), f(), f()])
}


9 changes: 9 additions & 0 deletions src/utils.cpp
Expand Up @@ -428,6 +428,15 @@ std::string unescape_json_quotes(const char *str, size_t len) {
case 't':
res.push_back((char)'\t');
continue;
case 'x':
if (i + 3 <= str_end) {
++i;
int val = hexval(*i++);
val <<= 4;
val |= hexval(*i);
res.push_back(val);
}
continue;
case 'u':
assert(std::distance(i, str_end) >= 5);
uint16_t ch = 0;
Expand Down

0 comments on commit cea39da

Please sign in to comment.