Commit 968b85ad77

Andrew Kelley <superjoe30@gmail.com>
2016-01-02 11:38:45
closer to guess number example working
1 parent 724dcdd
doc/langref.md
@@ -34,7 +34,7 @@ Root : many(TopLevelDecl) token(EOF)
 
 TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | StructDecl | VariableDeclaration
 
-VariableDeclaration : (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression))
+VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression))
 
 StructDecl : many(Directive) token(Struct) token(Symbol) token(LBrace) many(StructField) token(RBrace)
 
@@ -150,7 +150,7 @@ ArrayAccessExpression : token(LBracket) Expression token(RBracket)
 
 PrefixOp : token(Not) | token(Dash) | token(Tilde) | (token(Ampersand) option(token(Const)))
 
-PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression
+PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression
 
 StructValueExpression : token(Type) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace)
 
example/guess_number/main.zig
@@ -2,19 +2,33 @@ export executable "guess_number";
 
 use "std.zig";
 
-fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+// TODO don't duplicate these; implement pub const
+const stdout_fileno : isize = 1;
+const stderr_fileno : isize = 2;
+
+pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     print_str("Welcome to the Guess Number Game in Zig.\n");
 
     var seed : u32;
-    ok_or_panic(os_get_random_bytes(&seed, 4));
+    if (os_get_random_bytes(&seed as &u8, 4) != 0) {
+        // TODO full error message
+        fprint_str(stderr_fileno, "unable to get random bytes");
+        return 1;
+    }
+
+    print_str("Seed: ");
+    print_u64(seed);
+    print_str("\n");
+
+    /*
     var rand_state = rand_init(seed);
 
     const answer = rand_int(&rand_state, 0, 100) + 1;
 
-    while true {
+    while (true) {
         const line = readline("\nGuess a number between 1 and 100: ");
 
-        if const guess ?= parse_number(line) {
+        if (const guess ?= parse_number(line)) {
             if (guess > answer) {
                 print_str("Guess lower.\n");
             } else if (guess < answer) {
@@ -27,6 +41,7 @@ fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
             print_str("Invalid number format.\n");
         }
     }
+    */
 
     return 0;
 }
src/analyze.cpp
@@ -38,6 +38,7 @@ static AstNode *first_executing_node(AstNode *node) {
         case NodeTypeCastExpr:
         case NodeTypeNumberLiteral:
         case NodeTypeStringLiteral:
+        case NodeTypeCharLiteral:
         case NodeTypeUnreachable:
         case NodeTypeSymbol:
         case NodeTypePrefixOpExpr:
@@ -588,6 +589,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
         case NodeTypeArrayAccessExpr:
         case NodeTypeNumberLiteral:
         case NodeTypeStringLiteral:
+        case NodeTypeCharLiteral:
         case NodeTypeUnreachable:
         case NodeTypeVoid:
         case NodeTypeBoolLiteral:
@@ -659,6 +661,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) {
         case NodeTypeArrayAccessExpr:
         case NodeTypeNumberLiteral:
         case NodeTypeStringLiteral:
+        case NodeTypeCharLiteral:
         case NodeTypeUnreachable:
         case NodeTypeVoid:
         case NodeTypeBoolLiteral:
@@ -891,6 +894,17 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
         return expected_type;
     }
 
+    // implicit non-const to const
+    if (expected_type->id == TypeTableEntryIdPointer &&
+        actual_type->id == TypeTableEntryIdPointer &&
+        expected_type->data.pointer.is_const &&
+        !actual_type->data.pointer.is_const)
+    {
+        return resolve_type_compatibility(g, context, node,
+                expected_type->data.pointer.child_type,
+                actual_type->data.pointer.child_type);
+    }
+
     add_node_error(g, node,
         buf_sprintf("expected type '%s', got '%s'",
             buf_ptr(&expected_type->name),
@@ -1013,6 +1027,9 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
         Buf *name = &node->data.field_access_expr.field_name;
         if (buf_eql_str(name, "len")) {
             return_type = g->builtin_types.entry_usize;
+        } else if (buf_eql_str(name, "ptr")) {
+            // TODO determine whether the pointer should be const
+            return_type = get_pointer_to_type(g, struct_type->data.array.child_type, false);
         } else {
             add_node_error(g, node,
                 buf_sprintf("no member named '%s' in '%s'", buf_ptr(name),
@@ -1160,6 +1177,11 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
         codegen_num_lit->resolved_type = wanted_type;
         cast_node->op = CastOpNothing;
         return wanted_type;
+    } else if (actual_type->id == TypeTableEntryIdPointer &&
+               wanted_type->id == TypeTableEntryIdPointer)
+    {
+        cast_node->op = CastOpPointerReinterpret;
+        return wanted_type;
     } else {
         add_node_error(g, node,
             buf_sprintf("invalid cast from type '%s' to '%s'",
@@ -1286,6 +1308,9 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
             }
         case BinOpTypeAdd:
         case BinOpTypeSub:
+        case BinOpTypeMult:
+        case BinOpTypeDiv:
+        case BinOpTypeMod:
             {
                 AstNode *op1 = node->data.bin_op_expr.op1;
                 AstNode *op2 = node->data.bin_op_expr.op2;
@@ -1294,15 +1319,6 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
 
                 return resolve_peer_type_compatibility(g, context, node, op1, op2, lhs_type, rhs_type);
             }
-        case BinOpTypeMult:
-        case BinOpTypeDiv:
-        case BinOpTypeMod:
-            {
-                // TODO: don't require i32
-                analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
-                analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
-                return g->builtin_types.entry_i32;
-            }
         case BinOpTypeInvalid:
             zig_unreachable();
     }
@@ -1746,6 +1762,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                 return_type = get_array_type(g, g->builtin_types.entry_u8, buf_len(&node->data.string_literal.buf));
             }
             break;
+        case NodeTypeCharLiteral:
+            return_type = g->builtin_types.entry_u8;
+            break;
         case NodeTypeUnreachable:
             return_type = g->builtin_types.entry_unreachable;
             break;
@@ -1959,6 +1978,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
         case NodeTypeArrayAccessExpr:
         case NodeTypeNumberLiteral:
         case NodeTypeStringLiteral:
+        case NodeTypeCharLiteral:
         case NodeTypeUnreachable:
         case NodeTypeVoid:
         case NodeTypeBoolLiteral:
src/analyze.hpp
@@ -277,6 +277,7 @@ enum CastOp {
     CastOpIntWidenOrShorten,
     CastOpArrayToString,
     CastOpMaybeWrap,
+    CastOpPointerReinterpret,
 };
 
 struct CastNode {
src/codegen.cpp
@@ -390,6 +390,8 @@ static LLVMValueRef gen_bare_cast(CodeGen *g, AstNode *node, LLVMValueRef expr_v
             }
         case CastOpPtrToInt:
             return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
+        case CastOpPointerReinterpret:
+            return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, "");
         case CastOpIntWidenOrShorten:
             if (actual_type->size_in_bits == wanted_type->size_in_bits) {
                 return expr_val;
@@ -1236,6 +1238,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
                 LLVMValueRef ptr_val = LLVMBuildInBoundsGEP(g->builder, str_val, indices, 2, "");
                 return ptr_val;
             }
+        case NodeTypeCharLiteral:
+            return LLVMConstInt(LLVMInt8Type(), node->data.char_literal.value, false);
         case NodeTypeSymbol:
             {
                 VariableTableEntry *variable = find_variable(
src/parser.cpp
@@ -102,6 +102,8 @@ const char *node_type_str(NodeType node_type) {
             return "NumberLiteral";
         case NodeTypeStringLiteral:
             return "StringLiteral";
+        case NodeTypeCharLiteral:
+            return "CharLiteral";
         case NodeTypeUnreachable:
             return "Unreachable";
         case NodeTypeSymbol:
@@ -313,6 +315,11 @@ void ast_print(AstNode *node, int indent) {
                         buf_ptr(&node->data.string_literal.buf));
                 break;
             }
+        case NodeTypeCharLiteral:
+            {
+                fprintf(stderr, "%s '%c'\n", node_type_str(node->type), node->data.char_literal.value);
+                break;
+            }
         case NodeTypeUnreachable:
             fprintf(stderr, "Unreachable\n");
             break;
@@ -575,6 +582,55 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) {
     }
 }
 
+static uint8_t parse_char_literal(ParseContext *pc, Token *token) {
+    // skip the single quotes at beginning and end
+    // convert escape sequences
+    bool escape = false;
+    int return_count = 0;
+    uint8_t return_value;
+    for (int i = token->start_pos + 1; i < token->end_pos - 1; i += 1) {
+        uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i);
+        if (escape) {
+            switch (c) {
+                case '\\':
+                    return_value = '\\';
+                    return_count += 1;
+                    break;
+                case 'r':
+                    return_value = '\r';
+                    return_count += 1;
+                    break;
+                case 'n':
+                    return_value = '\n';
+                    return_count += 1;
+                    break;
+                case 't':
+                    return_value = '\t';
+                    return_count += 1;
+                    break;
+                case '\'':
+                    return_value = '\'';
+                    return_count += 1;
+                    break;
+                default:
+                    ast_error(pc, token, "invalid escape character");
+            }
+            escape = false;
+        } else if (c == '\\') {
+            escape = true;
+        } else {
+            return_value = c;
+            return_count += 1;
+        }
+    }
+    if (return_count == 0) {
+        ast_error(pc, token, "character literal too short");
+    } else if (return_count > 1) {
+        ast_error(pc, token, "character literal too long");
+    }
+    return return_count;
+}
+
 static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool *out_c_str,
         ZigList<SrcPos> *offset_map)
 {
@@ -620,6 +676,9 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool
                         buf_append_char(buf, '"');
                         if (offset_map) offset_map->append(pos);
                         break;
+                    default:
+                        ast_error(pc, token, "invalid escape character");
+                        break;
                 }
                 escape = false;
             } else if (c == '\\') {
@@ -1136,7 +1195,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) {
 }
 
 /*
-PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression
+PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression
 */
 static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
@@ -1151,6 +1210,11 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         parse_string_literal(pc, token, &node->data.string_literal.buf, &node->data.string_literal.c, nullptr);
         *token_index += 1;
         return node;
+    } else if (token->id == TokenIdCharLiteral) {
+        AstNode *node = ast_create_node(pc, NodeTypeCharLiteral, token);
+        node->data.char_literal.value = parse_char_literal(pc, token);
+        *token_index += 1;
+        return node;
     } else if (token->id == TokenIdKeywordUnreachable) {
         AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token);
         *token_index += 1;
@@ -1733,7 +1797,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m
 }
 
 /*
-VariableDeclaration : (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression))
+VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression))
 */
 static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *var_or_const_tok = &pc->tokens->at(*token_index);
src/parser.hpp
@@ -35,6 +35,7 @@ enum NodeType {
     NodeTypeCastExpr,
     NodeTypeNumberLiteral,
     NodeTypeStringLiteral,
+    NodeTypeCharLiteral,
     NodeTypeUnreachable,
     NodeTypeSymbol,
     NodeTypePrefixOpExpr,
@@ -289,6 +290,10 @@ struct AstNodeStringLiteral {
     bool c;
 };
 
+struct AstNodeCharLiteral {
+    uint8_t value;
+};
+
 enum NumLit {
     NumLitF32,
     NumLitF64,
@@ -359,6 +364,7 @@ struct AstNode {
         AstNodeStructDecl struct_decl;
         AstNodeStructField struct_field;
         AstNodeStringLiteral string_literal;
+        AstNodeCharLiteral char_literal;
         AstNodeNumberLiteral number_literal;
         AstNodeStructValueExpr struct_val_expr;
         AstNodeStructValueField struct_val_field;
src/tokenizer.cpp
@@ -103,6 +103,7 @@ enum TokenizeState {
     TokenizeStateFloatExponentUnsigned, // "123.456e", "123e", "0x123p"
     TokenizeStateFloatExponentNumber, // "123.456e-", "123.456e5", "123.456e5e-5"
     TokenizeStateString,
+    TokenizeStateCharLiteral,
     TokenizeStateSawStar,
     TokenizeStateSawSlash,
     TokenizeStateSawPercent,
@@ -307,6 +308,10 @@ void tokenize(Buf *buf, Tokenization *out) {
                         begin_token(&t, TokenIdStringLiteral);
                         t.state = TokenizeStateString;
                         break;
+                    case '\'':
+                        begin_token(&t, TokenIdCharLiteral);
+                        t.state = TokenizeStateCharLiteral;
+                        break;
                     case '(':
                         begin_token(&t, TokenIdLParen);
                         end_token(&t);
@@ -773,6 +778,16 @@ void tokenize(Buf *buf, Tokenization *out) {
                         break;
                 }
                 break;
+            case TokenizeStateCharLiteral:
+                switch (c) {
+                    case '\'':
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        break;
+                }
+                break;
             case TokenizeStateZero:
                 switch (c) {
                     case 'b':
@@ -912,6 +927,9 @@ void tokenize(Buf *buf, Tokenization *out) {
         case TokenizeStateString:
             tokenize_error(&t, "unterminated string");
             break;
+        case TokenizeStateCharLiteral:
+            tokenize_error(&t, "unterminated character literal");
+            break;
         case TokenizeStateSymbol:
         case TokenizeStateSymbolFirst:
         case TokenizeStateZero:
@@ -993,6 +1011,7 @@ static const char * token_name(Token *token) {
         case TokenIdLBracket: return "LBracket";
         case TokenIdRBracket: return "RBracket";
         case TokenIdStringLiteral: return "StringLiteral";
+        case TokenIdCharLiteral: return "CharLiteral";
         case TokenIdSemicolon: return "Semicolon";
         case TokenIdNumberLiteral: return "NumberLiteral";
         case TokenIdPlus: return "Plus";
src/tokenizer.hpp
@@ -44,6 +44,7 @@ enum TokenId {
     TokenIdLBracket,
     TokenIdRBracket,
     TokenIdStringLiteral,
+    TokenIdCharLiteral,
     TokenIdSemicolon,
     TokenIdNumberLiteral,
     TokenIdPlus,
std/errno.zig
@@ -0,0 +1,146 @@
+pub const EPERM = 1; // Operation not permitted
+pub const ENOENT = 2; // No such file or directory
+pub const ESRCH = 3; // No such process
+pub const EINTR = 4; // Interrupted system call
+pub const EIO = 5; // I/O error
+pub const ENXIO = 6; // No such device or address
+pub const E2BIG = 7; // Arg list too long
+pub const ENOEXEC = 8; // Exec format error
+pub const EBADF = 9; // Bad file number
+pub const ECHILD = 10; // No child processes
+pub const EAGAIN = 11; // Try again
+pub const ENOMEM = 12; // Out of memory
+pub const EACCES = 13; // Permission denied
+pub const EFAULT = 14; // Bad address
+pub const ENOTBLK = 15; // Block device required
+pub const EBUSY = 16; // Device or resource busy
+pub const EEXIST = 17; // File exists
+pub const EXDEV = 18; // Cross-device link
+pub const ENODEV = 19; // No such device
+pub const ENOTDIR = 20; // Not a directory
+pub const EISDIR = 21; // Is a directory
+pub const EINVAL = 22; // Invalid argument
+pub const ENFILE = 23; // File table overflow
+pub const EMFILE = 24; // Too many open files
+pub const ENOTTY = 25; // Not a typewriter
+pub const ETXTBSY = 26; // Text file busy
+pub const EFBIG = 27; // File too large
+pub const ENOSPC = 28; // No space left on device
+pub const ESPIPE = 29; // Illegal seek
+pub const EROFS = 30; // Read-only file system
+pub const EMLINK = 31; // Too many links
+pub const EPIPE = 32; // Broken pipe
+pub const EDOM = 33; // Math argument out of domain of func
+pub const ERANGE = 34; // Math result not representable
+pub const EDEADLK = 35; // Resource deadlock would occur
+pub const ENAMETOOLONG = 36; // File name too long
+pub const ENOLCK = 37; // No record locks available
+pub const ENOSYS = 38; // Function not implemented
+pub const ENOTEMPTY = 39; // Directory not empty
+pub const ELOOP = 40; // Too many symbolic links encountered
+pub const EWOULDBLOCK = EAGAIN; // Operation would block
+pub const ENOMSG = 42; // No message of desired type
+pub const EIDRM = 43; // Identifier removed
+pub const ECHRNG = 44; // Channel number out of range
+pub const EL2NSYNC = 45; // Level 2 not synchronized
+pub const EL3HLT = 46; // Level 3 halted
+pub const EL3RST = 47; // Level 3 reset
+pub const ELNRNG = 48; // Link number out of range
+pub const EUNATCH = 49; // Protocol driver not attached
+pub const ENOCSI = 50; // No CSI structure available
+pub const EL2HLT = 51; // Level 2 halted
+pub const EBADE = 52; // Invalid exchange
+pub const EBADR = 53; // Invalid request descriptor
+pub const EXFULL = 54; // Exchange full
+pub const ENOANO = 55; // No anode
+pub const EBADRQC = 56; // Invalid request code
+pub const EBADSLT = 57; // Invalid slot
+
+pub const EBFONT = 59; // Bad font file format
+pub const ENOSTR = 60; // Device not a stream
+pub const ENODATA = 61; // No data available
+pub const ETIME = 62; // Timer expired
+pub const ENOSR = 63; // Out of streams resources
+pub const ENONET = 64; // Machine is not on the network
+pub const ENOPKG = 65; // Package not installed
+pub const EREMOTE = 66; // Object is remote
+pub const ENOLINK = 67; // Link has been severed
+pub const EADV = 68; // Advertise error
+pub const ESRMNT = 69; // Srmount error
+pub const ECOMM = 70; // Communication error on send
+pub const EPROTO = 71; // Protocol error
+pub const EMULTIHOP = 72; // Multihop attempted
+pub const EDOTDOT = 73; // RFS specific error
+pub const EBADMSG = 74; // Not a data message
+pub const EOVERFLOW = 75; // Value too large for defined data type
+pub const ENOTUNIQ = 76; // Name not unique on network
+pub const EBADFD = 77; // File descriptor in bad state
+pub const EREMCHG = 78; // Remote address changed
+pub const ELIBACC = 79; // Can not access a needed shared library
+pub const ELIBBAD = 80; // Accessing a corrupted shared library
+pub const ELIBSCN = 81; // .lib section in a.out corrupted
+pub const ELIBMAX = 82; // Attempting to link in too many shared libraries
+pub const ELIBEXEC = 83; // Cannot exec a shared library directly
+pub const EILSEQ = 84; // Illegal byte sequence
+pub const ERESTART = 85; // Interrupted system call should be restarted
+pub const ESTRPIPE = 86; // Streams pipe error
+pub const EUSERS = 87; // Too many users
+pub const ENOTSOCK = 88; // Socket operation on non-socket
+pub const EDESTADDRREQ = 89; // Destination address required
+pub const EMSGSIZE = 90; // Message too long
+pub const EPROTOTYPE = 91; // Protocol wrong type for socket
+pub const ENOPROTOOPT = 92; // Protocol not available
+pub const EPROTONOSUPPORT = 93; // Protocol not supported
+pub const ESOCKTNOSUPPORT = 94; // Socket type not supported
+pub const EOPNOTSUPP = 95; // Operation not supported on transport endpoint
+pub const EPFNOSUPPORT = 96; // Protocol family not supported
+pub const EAFNOSUPPORT = 97; // Address family not supported by protocol
+pub const EADDRINUSE = 98; // Address already in use
+pub const EADDRNOTAVAIL = 99; // Cannot assign requested address
+pub const ENETDOWN = 100; // Network is down
+pub const ENETUNREACH = 101; // Network is unreachable
+pub const ENETRESET = 102; // Network dropped connection because of reset
+pub const ECONNABORTED = 103; // Software caused connection abort
+pub const ECONNRESET = 104; // Connection reset by peer
+pub const ENOBUFS = 105; // No buffer space available
+pub const EISCONN = 106; // Transport endpoint is already connected
+pub const ENOTCONN = 107; // Transport endpoint is not connected
+pub const ESHUTDOWN = 108; // Cannot send after transport endpoint shutdown
+pub const ETOOMANYREFS = 109; // Too many references: cannot splice
+pub const ETIMEDOUT = 110; // Connection timed out
+pub const ECONNREFUSED = 111; // Connection refused
+pub const EHOSTDOWN = 112; // Host is down
+pub const EHOSTUNREACH = 113; // No route to host
+pub const EALREADY = 114; // Operation already in progress
+pub const EINPROGRESS = 115; // Operation now in progress
+pub const ESTALE = 116; // Stale NFS file handle
+pub const EUCLEAN = 117; // Structure needs cleaning
+pub const ENOTNAM = 118; // Not a XENIX named type file
+pub const ENAVAIL = 119; // No XENIX semaphores available
+pub const EISNAM = 120; // Is a named type file
+pub const EREMOTEIO = 121; // Remote I/O error
+pub const EDQUOT = 122; // Quota exceeded
+
+pub const ENOMEDIUM = 123; // No medium found
+pub const EMEDIUMTYPE = 124; // Wrong medium type
+
+// nameserver query return codes
+pub const ENSROK = 0; // DNS server returned answer with no data
+pub const ENSRNODATA = 160; // DNS server returned answer with no data
+pub const ENSRFORMERR = 161; // DNS server claims query was misformatted
+pub const ENSRSERVFAIL = 162; // DNS server returned general failure
+pub const ENSRNOTFOUND = 163; // Domain name not found
+pub const ENSRNOTIMP = 164; // DNS server does not implement requested operation
+pub const ENSRREFUSED = 165; // DNS server refused query
+pub const ENSRBADQUERY = 166; // Misformatted DNS query
+pub const ENSRBADNAME = 167; // Misformatted domain name
+pub const ENSRBADFAMILY = 168; // Unsupported address family
+pub const ENSRBADRESP = 169; // Misformatted DNS reply
+pub const ENSRCONNREFUSED = 170; // Could not contact DNS servers
+pub const ENSRTIMEOUT = 171; // Timeout while contacting DNS servers
+pub const ENSROF = 172; // End of file
+pub const ENSRFILE = 173; // Error reading file
+pub const ENSRNOMEM = 174; // Out of memory
+pub const ENSRDESTRUCTION = 175; // Application terminated lookup
+pub const ENSRQUERYDOMAINTOOLONG = 176; // Domain name is too long
+pub const ENSRCNAMELOOP = 177; // Domain name is too long
std/std.zig
@@ -1,6 +1,9 @@
 const SYS_write : isize = 1;
 const SYS_exit : isize = 60;
+const SYS_getrandom : isize = 278;
+
 const stdout_fileno : isize = 1;
+const stderr_fileno : isize = 2;
 
 fn syscall1(number: isize, arg1: isize) -> isize {
     asm volatile ("syscall"
@@ -16,6 +19,12 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize {
         : "rcx", "r11")
 }
 
+/*
+pub fn getrandom(buf: &u8, count: usize, flags: u32) -> isize {
+    return syscall3(SYS_getrandom, buf as isize, count as isize, flags as isize);
+}
+*/
+
 pub fn write(fd: isize, buf: &const u8, count: usize) -> isize {
     return syscall3(SYS_write, fd, buf as isize, count as isize);
 }
@@ -25,8 +34,62 @@ pub fn exit(status: i32) -> unreachable {
     unreachable;
 }
 
+/*
+fn digit_to_char(digit: u64) -> u8 { '0' + (digit as u8) }
+
+const max_u64_base10_digits: usize = 20;
+
+fn buf_print_u64(out_buf: &u8, x: u64) -> usize {
+    // TODO use max_u64_base10_digits instead of hardcoding 20
+    var buf: [u8; 20];
+    var a = x;
+    var index = max_u64_base10_digits;
+
+    while (true) {
+        const digit = a % 10;
+        index -= 1;
+        buf[index] = digit_to_char(digit);
+        a /= 10;
+        if (a == 0)
+            break;
+    }
+
+    const len = max_u64_base10_digits - index;
+
+    // TODO memcpy intrinsic
+    var i: usize = 0;
+    while (i < len) {
+        out_buf[i] = buf[index + i];
+        i += 1;
+    }
+
+    return len;
+}
+
+// TODO handle buffering and flushing (mutex protected)
+// TODO error handling
+pub fn print_u64(x: u64) -> isize {
+    // TODO use max_u64_base10_digits instead of hardcoding 20
+    var buf: [u8; 20];
+    const len = buf_print_u64(buf.ptr, x);
+    return write(stdout_fileno, buf.ptr, len);
+}
+*/
+
+
+// TODO error handling
+// TODO handle buffering and flushing (mutex protected)
+pub fn print_str(str: string) -> isize { fprint_str(stdout_fileno, str) }
+
+// TODO error handling
+// TODO handle buffering and flushing (mutex protected)
+pub fn fprint_str(fd: isize, str: string) -> isize {
+    return write(fd, str.ptr, str.len);
+}
+
+/*
 // TODO error handling
-// TODO handle buffering and flushing
-pub fn print_str(str : string) -> isize {
-    return write(stdout_fileno, str.ptr, str.len);
+pub fn os_get_random_bytes(buf: &u8, count: usize) -> isize {
+    return getrandom(buf, count, 0);
 }
+*/