Skip to content

Commit

Permalink
wip type checking
Browse files Browse the repository at this point in the history
  • Loading branch information
developedby committed Aug 6, 2024
1 parent c2e0786 commit 68e6075
Show file tree
Hide file tree
Showing 9 changed files with 1,342 additions and 183 deletions.
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"ints",
"itertools",
"ITRS",
"kindc",
"kwarg",
"kwargs",
"lcons",
Expand Down
205 changes: 34 additions & 171 deletions src/fun/builtins.bend
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
type String:
Nil
Cons { head: u24, ~tail: String }
Cons { head: u24, ~tail: String }

type List(t):
Nil
Cons { head: u24, ~tail: List(t) }
Cons { head: t, tail: List(t) }

def List/length(xs: List(T)) -> (u24, List(T)):
fold xs with len=0, acc=[]:
Expand Down Expand Up @@ -43,7 +43,7 @@ type Map T = (Node (value: T) ~(left: (Map T)) ~(right: (Map T))) | (Leaf)

Map/empty : (Map T) = Map/Leaf

Map/get (map: (Map T)) (key: u24) : ((Map T), T) =
Map/get (map: (Map T)) (key: u24) : (T, (Map T)) =
match map {
Map/Leaf: (*, map)
Map/Node:
Expand Down Expand Up @@ -94,131 +94,6 @@ Map/map (Map/Node value left right) key f =
}


# IO Impl

type IO(T):
Done { magic: (u24, u24), expr: T }
Call { magic: (u24, u24), func: String, argm: Any, cont: T -> IO(T) }

def IO/MAGIC -> (u24, u24):
return (0xD0CA11, 0xFF1FF1)

def IO/wrap(x: T) -> IO(T):
return IO/Done(IO/MAGIC, x)

def IO/bind(a: IO(A), b: A -> IO(B)) -> IO(B):
match a:
case IO/Done:
b = undefer(b)
return b(a.expr)
case IO/Call:
return IO/Call(IO/MAGIC, a.func, a.argm, lambda x: IO/bind(a.cont(x), b))

def call(func: String, argm: Any) -> IO(T):
return IO/Call(IO/MAGIC, func, argm, lambda x: IO/Done(IO/MAGIC, x))

## Time and sleep
# Returns a monotonically increasing nanosecond timestamp as an u48 encoded as a pair of u24s.
IO/get_time : (IO (u24, u24)) =
(IO/Call IO/MAGIC "GET_TIME" * @x (IO/Done IO/MAGIC x))

# Sleeps for the given number of nanoseconds, given by an u48 encoded as a pair of u24s.
IO/nanosleep (hi_lo: (u24, u24)) : (IO None) =
(IO/Call IO/MAGIC "SLEEP" hi_lo @x (IO/Done IO/MAGIC x))

# Sleeps for a given amount of seconds as a float.
def IO/sleep(seconds: f24) -> IO(None):
nanos = seconds * 1_000_000_000.0
lo = to_u24(nanos % 0x1_000_000.0)
hi = to_u24(nanos / 0x1_000_000.0)
return IO/nanosleep((hi, lo))

## File IO

### File IO primitives
IO/FS/open (path: String) (mode: String) : (IO u24) =
(IO/Call IO/MAGIC "OPEN" (path, mode) @x (IO/Done IO/MAGIC x))

IO/FS/close (file: u24) : (IO None) =
(IO/Call IO/MAGIC "CLOSE" file @x (IO/Done IO/MAGIC x))

IO/FS/read (file: u24) (num_bytes: u24) : (IO (List u24)) =
(IO/Call IO/MAGIC "READ" (file, num_bytes) @x (IO/Done IO/MAGIC x))

IO/FS/write (file: u24) (bytes: (List u24)) : (IO None) =
(IO/Call IO/MAGIC "WRITE" (file, bytes) @x (IO/Done IO/MAGIC x))

IO/FS/seek (file: u24) (offset: i24) (mode: u24) : (IO None) =
(IO/Call IO/MAGIC "SEEK" (file, (offset, mode)) @x (IO/Done IO/MAGIC x))

IO/FS/flush (file: u24) : (IO None) =
(IO/Call IO/MAGIC "FLUSH" file @x (IO/Done IO/MAGIC x))

### Always available files
IO/FS/STDIN : u24 = 0
IO/FS/STDOUT : u24 = 1
IO/FS/STDERR : u24 = 2

### Seek modes
# Seek from start of file
IO/FS/SEEK_SET : i24 = +0
# Seek from current position
IO/FS/SEEK_CUR : i24 = +1
# Seek from end of file
IO/FS/SEEK_END : i24 = +2

### File utilities
# Reads an entire file, returning a list of bytes.
def IO/FS/read_file(path: String) -> IO(List(u24)):
with IO:
fd <- IO/FS/open(path, "r")
bytes <- IO/FS/read_to_end(fd)
* <- IO/FS/close(fd)
return wrap(bytes)

# Reads the remaining contents of a file, returning a list of read bytes.
def IO/FS/read_to_end(fd: u24) -> IO(List(u24)):
return IO/FS/read_to_end.read_chunks(fd, [])

def IO/FS/read_to_end.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(List(u24)):
with IO:
# Read file in 1MB chunks
chunk <- IO/FS/read(fd, 1048576)
match chunk:
case List/Nil:
return wrap(List/flatten(chunks))
case List/Cons:
return IO/FS/read_to_end.read_chunks(fd, List/Cons(chunk, chunks))

# Reads a single line from a file, returning a list of bytes.
def IO/FS/read_line(fd: u24) -> IO(List(u24)):
return IO/FS/read_line.read_chunks(fd, [])

def IO/FS/read_line.read_chunks(fd: u24, chunks: List(List(u24))) -> IO(List(u24)):
with IO:
# Read line in 1kB chunks
chunk <- IO/FS/read(fd, 1024)
match res = Bytes/split_once(chunk, '\n'):
case Result/Ok:
(line, rest) = res.val
(length, *) = List/length(rest)
* <- IO/FS/seek(fd, to_i24(length) * -1, IO/FS/SEEK_CUR)
chunks = List/Cons(line, chunks)
bytes = List/flatten(chunks)
return wrap(bytes)
case Result/Err:
line = res.val
chunks = List/Cons(line, chunks)
return IO/FS/read_line.read_chunks(fd, chunks)

# Writes a list of bytes to a file given by a path.
def IO/FS/write_file(path: String, bytes: List(u24)) -> IO(None):
with IO:
f <- IO/FS/open(path, "w")
* <- IO/FS/write(f, bytes)
* <- IO/FS/close(f)
return wrap(bytes)

Bytes/split_once (xs: (List u24)) (cond: u24) : (Result ((List u24), (List u24)) (List u24)) =
(Bytes/split_once.go xs cond @x x)

Expand All @@ -231,48 +106,20 @@ Bytes/split_once.go (List/Cons x xs) cond acc =
(Bytes/split_once.go xs cond @y (acc (List/Cons x y)))
}

### Standard input and output utilities

# Prints a string to stdout, encoding it with utf-8.
IO/print (text: String) : (IO None) =
(IO/FS/write IO/FS/STDOUT (String/encode_utf8 text))

# Read characters from stdin until a newline is found.
# Returns the read input decoded as utf-8.
IO/input : (IO String) =
(IO/input.go @x x)

def IO/input.go(acc: List(u24) -> List(u24)) -> IO(String):
# TODO: This is slow and inefficient, should be done in hvm using fgets.
with IO:
byte <- IO/FS/read(IO/FS/STDIN, 1)
match byte:
case List/Nil:
# Nothing read, try again (to simulate blocking a read)
return IO/input.go(acc)
case List/Cons:
if byte.head == '\n':
bytes = acc(List/Nil)
text = Bytes/decode_utf8(bytes)
return wrap(text)
else:
acc = lambda x: acc(List/Cons(byte.head, x))
return IO/input.go(acc)

# Lazy thunks
# We can defer the evaluation of a function by wrapping it in a thunk
# Ex: @x (x @arg1 @arg2 @arg3 (f arg1 arg2 arg3) arg1 arg2 arg3)
# This is only evaluated when we call it with 'undefer' (undefer my_thunk)
# We can build a defered call directly or by by using defer and defer_arg
# The example above can be written as:
# (defer_arg (defer_arg (defer_arg (defer @arg1 @arg2 @arg3 (f arg1 arg2 arg3)) arg1) arg2) arg3)
defer val =
defer (val: a) : (a -> b) -> b =
@x (x val)

defer_arg defered arg =
defer_arg (defered: a -> b -> c) (arg: b) : (a -> c) -> c =
@x (defered x arg)

undefer defered =
undefer (defered: (a -> a) -> b) : b =
(defered @x x)


Expand All @@ -289,21 +136,37 @@ hvm log -> (f24 -> f24 -> f24):
hvm atan2 -> (f24 -> f24 -> f24):
($([&] $(y ret)) (y ret))

# to_f24(x: native number) -> f24
# Casts any native number to an f24.
hvm to_f24 -> (Number(t) -> f24):
($([f24] ret) ret)
# f24_to_u24(x: f24) -> u24
# Casts a f24 number to a u24.
hvm f24_to_u24 -> (f24 -> u24):
($([u24] ret) ret)

# to_u24(x: native number) -> u24
# Casts any native number to a u24.
hvm to_u24 -> (Number(t) -> u24):
# i24_to_u24(x: i24) -> u24
# Casts an i24 number to a u24.
hvm i24_to_u24 -> (i24 -> u24):
($([u24] ret) ret)

# to_i24(x: native number) -> i24
# Casts any native number to an i24.
hvm to_i24 -> (Number(t) -> i24):
# u24_to_i24(x: u24) -> i24
# Casts a u24 number to an i24.
hvm u24_to_i24 -> (u24 -> i24):
($([i24] ret) ret)

# f24_to_i24(x: f24) -> i24
# Casts a f24 number to an i24.
hvm f24_to_i24 -> (f24 -> i24):
($([i24] ret) ret)

# u24_to_f24(x: u24) -> f24
# Casts a u24 number to a f24.
hvm u24_to_f24 -> (u24 -> f24):
($([f24] ret) ret)

# i24_to_f24(x: i24) -> f24
# Casts an i24 number to a f24.
hvm i24_to_f24 -> (i24 -> f24):
($([f24] ret) ret)


# String Encoding and Decoding

Utf8/REPLACEMENT_CHARACTER : u24 = '\u{FFFD}'
Expand Down Expand Up @@ -376,7 +239,7 @@ Utf8/decode_character (List/Cons a (List/Cons b (List/Cons c (List/Cons d rest))
}
}

String/encode_utf8 String : (List u24)
String/encode_utf8 (str: String) : (List u24)
String/encode_utf8 (String/Nil) = (List/Nil)
String/encode_utf8 (String/Cons x xs) =
use Utf8/rune1max = 0b01111111
Expand Down Expand Up @@ -414,7 +277,7 @@ Bytes/decode_ascii (bytes: (List u24)) : String
Bytes/decode_ascii (List/Cons x xs) = (String/Cons x (Bytes/decode_ascii xs))
Bytes/decode_ascii (List/Nil) = (String/Nil)

String/encode_ascii (String: String) : (List u24)
String/encode_ascii (str: String) : (List u24)
String/encode_ascii (String/Cons x xs) = (List/Cons x (String/encode_ascii xs))
String/encode_ascii (String/Nil) = (List/Nil)

Expand Down
1 change: 1 addition & 0 deletions src/fun/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ pub struct AdtCtr {
pub struct CtrField {
pub nam: Name,
pub rec: bool,
pub typ: Type,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
Expand Down
6 changes: 3 additions & 3 deletions src/fun/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ impl<'a> FunParser<'a> {
let ctr_name = Name::new(format!("{type_name}/{ctr_name}"));

let fields = self.list_like(|p| p.parse_type_ctr_field(), "", ")", "", false, 0)?;
let (fields, field_types): (Vec<_>, Vec<_>) = fields.into_iter().unzip();
let field_types = fields.iter().map(|f| f.typ.clone()).collect::<Vec<_>>();
let end_idx = *self.index();
self.check_repeated_ctr_fields(&fields, &ctr_name, ini_idx..end_idx)?;

Expand All @@ -238,7 +238,7 @@ impl<'a> FunParser<'a> {
}
}

fn parse_type_ctr_field(&mut self) -> ParseResult<(CtrField, Type)> {
fn parse_type_ctr_field(&mut self) -> ParseResult<CtrField> {
let rec = self.try_consume("~");

let nam;
Expand All @@ -255,7 +255,7 @@ impl<'a> FunParser<'a> {
nam = self.parse_var_name()?;
typ = Type::Any;
}
Ok((CtrField { nam, rec }, typ))
Ok(CtrField { nam, typ, rec })
}

fn parse_fun_def(&mut self) -> ParseResult<FunDefinition> {
Expand Down
7 changes: 5 additions & 2 deletions src/fun/transform/expand_main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@ impl Term {
Term::Ref { nam } => {
if seen.contains(nam) {
// Don't expand recursive references
} else {
} else if let Some(def) = book.defs.get(nam) {
// Regular function, expand
seen.push(nam.clone());
let mut body = book.defs.get(nam).unwrap().rule().body.clone();
let mut body = def.rule().body.clone();
body.rename_unscoped(globals_count, &mut HashMap::new());
*self = body;
self.expand_ref_return(book, seen, globals_count);
seen.pop().unwrap();
} else {
// Not a regular function, don't expand
}
}
Term::Fan { els, .. } | Term::List { els } => {
Expand Down
1 change: 1 addition & 0 deletions src/fun/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ pub mod resolve_refs;
pub mod resolve_type_ctrs;
pub mod resugar_list;
pub mod resugar_string;
pub mod type_check;
pub mod unique_names;
Loading

0 comments on commit 68e6075

Please sign in to comment.