Commit d697404f64

Andrew Kelley <superjoe30@gmail.com>
2015-12-11 11:55:26
hello world working without libc
1 parent 15ba5bc
doc/langref.md
@@ -78,6 +78,10 @@ AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput)
 
 AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers)
 
+AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) token(Symbol) token(RParen)
+
+AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen)
+
 AsmClobbers: token(Colon) list(token(String), token(Comma))
 
 AssignmentExpression : BoolOrExpression token(Equal) BoolOrExpression | BoolOrExpression
example/hello_world/hello2.zig
@@ -3,6 +3,6 @@ export executable "hello";
 use "std.zig";
 
 export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 {
-    print_str("Hello, world!", 13);
+    print_str("Hello, world!", 13 as isize);
     return 0;
 }
src/analyze.cpp
@@ -536,6 +536,20 @@ static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *i
     return return_type;
 }
 
+static TypeTableEntry *analyze_variable_name(CodeGen *g, BlockContext *context,
+        AstNode *node, Buf *variable_name)
+{
+    LocalVariableTableEntry *local_variable = find_local_variable(context, variable_name);
+    if (local_variable) {
+        return local_variable->type;
+    } else {
+        // TODO: check global variables also
+        add_node_error(g, node,
+                buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
+        return g->builtin_types.entry_invalid;
+    }
+}
+
 static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
@@ -649,6 +663,15 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
             }
         case NodeTypeAsmExpr:
             {
+                for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) {
+                    AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
+                    analyze_variable_name(g, context, node, &asm_output->variable_name);
+                }
+                for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
+                    AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
+                    analyze_expression(g, import, context, nullptr, asm_input->expr);
+                }
+
                 return_type = g->builtin_types.entry_void;
                 break;
             }
@@ -835,22 +858,36 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
 
         case NodeTypeSymbol:
             {
-                Buf *symbol_name = &node->data.symbol;
-                LocalVariableTableEntry *local_variable = find_local_variable(context, symbol_name);
-                if (local_variable) {
-                    return_type = local_variable->type;
-                } else {
-                    // TODO: check global variables also
-                    add_node_error(g, node,
-                            buf_sprintf("use of undeclared identifier '%s'", buf_ptr(symbol_name)));
-                    return_type = g->builtin_types.entry_invalid;
-                }
+                return_type = analyze_variable_name(g, context, node, &node->data.symbol);
                 break;
             }
         case NodeTypeCastExpr:
-            zig_panic("TODO analyze_expression cast expr");
-            break;
+            {
+                TypeTableEntry *wanted_type = resolve_type(g, node->data.cast_expr.type);
+                TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr,
+                        node->data.cast_expr.expr);
+
+                if (wanted_type->id == TypeTableEntryIdInvalid ||
+                    actual_type->id == TypeTableEntryIdInvalid)
+                {
+                    return_type = g->builtin_types.entry_invalid;
+                    break;
+                }
 
+                // special casing this for now, TODO think about casting and do a general solution
+                if (wanted_type == g->builtin_types.entry_isize &&
+                    actual_type->id == TypeTableEntryIdPointer)
+                {
+                    return_type = wanted_type;
+                } else if (wanted_type == g->builtin_types.entry_isize &&
+                           actual_type->id == TypeTableEntryIdInt)
+                {
+                    return_type = wanted_type;
+                } else {
+                    zig_panic("TODO analyze_expression cast expr");
+                }
+                break;
+            }
         case NodeTypePrefixOpExpr:
             switch (node->data.prefix_op_expr.prefix_op) {
                 case PrefixOpBoolNot:
src/codegen.cpp
@@ -232,12 +232,35 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
 static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeCastExpr);
 
-    LLVMValueRef expr = gen_expr(g, node->data.cast_expr.prefix_op_expr);
+    LLVMValueRef expr_val = gen_expr(g, node->data.cast_expr.expr);
 
-    if (!node->data.cast_expr.type)
-        return expr;
+    TypeTableEntry *actual_type = get_expr_type(node->data.cast_expr.expr);
+    TypeTableEntry *wanted_type = get_expr_type(node);
 
-    zig_panic("TODO cast expression");
+    // this asserts are here only because no other casting codegen is supported currently
+    assert(wanted_type == g->builtin_types.entry_isize);
+
+    if (wanted_type->id == TypeTableEntryIdPointer) {
+        return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, "");
+    } else if (wanted_type->id == TypeTableEntryIdInt) {
+        if (actual_type->size_in_bits == wanted_type->size_in_bits) {
+            if (actual_type->id == TypeTableEntryIdPointer) {
+                return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
+            } else {
+                zig_panic("TODO gen_cast_expr");
+            }
+        } else if (actual_type->size_in_bits < wanted_type->size_in_bits) {
+            if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) {
+                return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, "");
+            } else {
+                zig_panic("TODO gen_cast_expr sign mismatch");
+            }
+        } else {
+            zig_panic("TODO gen_cast_expr");
+        }
+    } else {
+        zig_panic("TODO gen_cast_expr");
+    }
 }
 
 static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
@@ -610,16 +633,37 @@ static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *i
     return return_value;
 }
 
+static int find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) {
+    const char *ptr = buf_ptr(&node->data.asm_expr.asm_template) + tok->start + 2;
+    int len = tok->end - tok->start - 2;
+    int result = 0;
+    for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) {
+        AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
+        if (buf_eql_mem(&asm_output->asm_symbolic_name, ptr, len)) {
+            return result;
+        }
+    }
+    for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) {
+        AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
+        if (buf_eql_mem(&asm_input->asm_symbolic_name, ptr, len)) {
+            return result;
+        }
+    }
+    return -1;
+}
+
 static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeAsmExpr);
 
-    Buf *src_template = &node->data.asm_expr.asm_template;
+    AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
+
+    Buf *src_template = &asm_expr->asm_template;
 
     Buf llvm_template = BUF_INIT;
     buf_resize(&llvm_template, 0);
 
-    for (int token_i = 0; token_i < node->data.asm_expr.token_list.length; token_i += 1) {
-        AsmToken *asm_token = &node->data.asm_expr.token_list.at(token_i);
+    for (int token_i = 0; token_i < asm_expr->token_list.length; token_i += 1) {
+        AsmToken *asm_token = &asm_expr->token_list.at(token_i);
         switch (asm_token->id) {
             case AsmTokenIdTemplate:
                 for (int offset = asm_token->start; offset < asm_token->end; offset += 1) {
@@ -634,15 +678,69 @@ static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) {
             case AsmTokenIdPercent:
                 buf_append_char(&llvm_template, '%');
                 break;
+            case AsmTokenIdVar:
+                int index = find_asm_index(g, node, asm_token);
+                assert(index >= 0);
+                buf_appendf(&llvm_template, "$%d", index);
+                break;
+        }
+    }
+
+    Buf constraint_buf = BUF_INIT;
+    buf_resize(&constraint_buf, 0);
+    int total_constraint_count = asm_expr->output_list.length +
+                                 asm_expr->input_list.length +
+                                 asm_expr->clobber_list.length;
+    int input_and_output_count = asm_expr->output_list.length +
+                                 asm_expr->input_list.length;
+    int total_index = 0;
+    LLVMTypeRef *param_types = allocate<LLVMTypeRef>(input_and_output_count);
+    LLVMValueRef *param_values = allocate<LLVMValueRef>(input_and_output_count);
+    for (int i = 0; i < asm_expr->output_list.length; i += 1, total_index += 1) {
+        AsmOutput *asm_output = asm_expr->output_list.at(i);
+        if (buf_eql_str(&asm_output->constraint, "=m")) {
+            buf_append_str(&constraint_buf, "=*m");
+        } else {
+            zig_panic("TODO unable to handle anything other than '=m' for outputs");
+        }
+        if (total_index + 1 < total_constraint_count) {
+            buf_append_char(&constraint_buf, ',');
+        }
+
+        LocalVariableTableEntry *variable = find_local_variable(
+                node->codegen_node->expr_node.block_context,
+                &asm_output->variable_name);
+        assert(variable);
+        param_types[total_index] = LLVMTypeOf(variable->value_ref);
+        param_values[total_index] = variable->value_ref;
+    }
+    for (int i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1) {
+        AsmInput *asm_input = asm_expr->input_list.at(i);
+        buf_append_buf(&constraint_buf, &asm_input->constraint);
+        if (total_index + 1 < total_constraint_count) {
+            buf_append_char(&constraint_buf, ',');
+        }
+
+        TypeTableEntry *expr_type = get_expr_type(asm_input->expr);
+        param_types[total_index] = expr_type->type_ref;
+        param_values[total_index] = gen_expr(g, asm_input->expr);
+    }
+    for (int i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) {
+        Buf *clobber_buf = asm_expr->clobber_list.at(i);
+        buf_appendf(&constraint_buf, "~{%s}", buf_ptr(clobber_buf));
+        if (total_index + 1 < total_constraint_count) {
+            buf_append_char(&constraint_buf, ',');
         }
     }
 
-    LLVMTypeRef function_type = LLVMFunctionType(LLVMVoidType(), nullptr, 0, false);
+    LLVMTypeRef function_type = LLVMFunctionType(LLVMVoidType(), param_types, input_and_output_count, false);
 
-    LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template), "", true, false);
+    bool is_volatile = asm_expr->is_volatile || (asm_expr->output_list.length == 0);
+    LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template),
+            buf_ptr(&constraint_buf), is_volatile, false);
 
     add_debug_source_node(g, node);
-    return LLVMBuildCall(g->builder, asm_fn, nullptr, 0, "");
+    return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
 }
 
 static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
src/parser.cpp
@@ -245,7 +245,7 @@ void ast_print(AstNode *node, int indent) {
             break;
         case NodeTypeCastExpr:
             fprintf(stderr, "%s\n", node_type_str(node->type));
-            ast_print(node->data.cast_expr.prefix_op_expr, indent + 2);
+            ast_print(node->data.cast_expr.expr, indent + 2);
             if (node->data.cast_expr.type)
                 ast_print(node->data.cast_expr.type, indent + 2);
             break;
@@ -307,6 +307,33 @@ struct ParseContext {
     ErrColor err_color;
 };
 
+__attribute__ ((format (printf, 4, 5)))
+__attribute__ ((noreturn))
+static void ast_asm_error(ParseContext *pc, AstNode *node, int offset, const char *format, ...) {
+    assert(node->type == NodeTypeAsmExpr);
+
+    ErrorMsg *err = allocate<ErrorMsg>(1);
+
+    SrcPos pos = node->data.asm_expr.offset_map.at(offset);
+
+    err->line_start = pos.line;
+    err->column_start = pos.column;
+    err->line_end = -1;
+    err->column_end = -1;
+
+    va_list ap;
+    va_start(ap, format);
+    err->msg = buf_vprintf(format, ap);
+    va_end(ap);
+
+    err->path = pc->owner->path;
+    err->source = pc->owner->source_code;
+    err->line_offsets = pc->owner->line_offsets;
+
+    print_err_msg(err, pc->err_color);
+    exit(EXIT_FAILURE);
+}
+
 __attribute__ ((format (printf, 3, 4)))
 __attribute__ ((noreturn))
 static void ast_error(ParseContext *pc, Token *token, const char *format, ...) {
@@ -372,6 +399,7 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) {
         StateStart,
         StatePercent,
         StateTemplate,
+        StateVar,
     };
 
     ZigList<AsmToken> *tok_list = &node->data.asm_expr.token_list;
@@ -403,8 +431,11 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) {
                 if (c == '%') {
                     cur_tok->end = i;
                     state = StateStart;
+                } else if (c == '[') {
+                    cur_tok->id = AsmTokenIdVar;
+                    state = StateVar;
                 } else {
-                    zig_panic("TODO handle assembly tokenize error");
+                    ast_asm_error(pc, node, i, "expected a '%%' or '['");
                 }
                 break;
             case StateTemplate:
@@ -415,6 +446,19 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) {
                     state = StateStart;
                 }
                 break;
+            case StateVar:
+                if (c == ']') {
+                    cur_tok->end = i;
+                    state = StateStart;
+                } else if ((c >= 'a' && c <= 'z') ||
+                        (c >= '0' && c <= '9') ||
+                        (c == '_'))
+                {
+                    // do nothing
+                } else {
+                    ast_asm_error(pc, node, i, "invalid substitution character: '%c'", c);
+                }
+                break;
         }
     }
 
@@ -422,7 +466,8 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) {
         case StateStart:
             break;
         case StatePercent:
-            zig_panic("TODO handle assembly tokenize error eof");
+        case StateVar:
+            ast_asm_error(pc, node, buf_len(asm_template), "unexpected end of assembly template");
             break;
         case StateTemplate:
             cur_tok->end = buf_len(asm_template);
@@ -430,40 +475,59 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) {
     }
 }
 
-static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf) {
+static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, ZigList<SrcPos> *offset_map) {
     // skip the double quotes at beginning and end
     // convert escape sequences
 
     buf_resize(buf, 0);
     bool escape = false;
-    for (int i = token->start_pos + 1; i < token->end_pos - 1; i += 1) {
+    bool first = true;
+    SrcPos pos = {token->start_line, token->start_column};
+    for (int i = token->start_pos; i < token->end_pos - 1; i += 1) {
         uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i);
-        if (escape) {
-            switch (c) {
-                case '\\':
-                    buf_append_char(buf, '\\');
-                    break;
-                case 'r':
-                    buf_append_char(buf, '\r');
-                    break;
-                case 'n':
-                    buf_append_char(buf, '\n');
-                    break;
-                case 't':
-                    buf_append_char(buf, '\t');
-                    break;
-                case '"':
-                    buf_append_char(buf, '"');
-                    break;
+        if (first) {
+            first = false;
+        } else {
+            if (escape) {
+                switch (c) {
+                    case '\\':
+                        buf_append_char(buf, '\\');
+                        if (offset_map) offset_map->append(pos);
+                        break;
+                    case 'r':
+                        buf_append_char(buf, '\r');
+                        if (offset_map) offset_map->append(pos);
+                        break;
+                    case 'n':
+                        buf_append_char(buf, '\n');
+                        if (offset_map) offset_map->append(pos);
+                        break;
+                    case 't':
+                        buf_append_char(buf, '\t');
+                        if (offset_map) offset_map->append(pos);
+                        break;
+                    case '"':
+                        buf_append_char(buf, '"');
+                        if (offset_map) offset_map->append(pos);
+                        break;
+                }
+                escape = false;
+            } else if (c == '\\') {
+                escape = true;
+            } else {
+                buf_append_char(buf, c);
+                if (offset_map) offset_map->append(pos);
             }
-            escape = false;
-        } else if (c == '\\') {
-            escape = true;
+        }
+        if (c == '\n') {
+            pos.line += 1;
+            pos.column = 0;
         } else {
-            buf_append_char(buf, c);
+            pos.column += 1;
         }
     }
     assert(!escape);
+    if (offset_map) offset_map->append(pos);
 }
 
 __attribute__ ((noreturn))
@@ -505,7 +569,7 @@ static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_
     token_index += 1;
     ast_expect_token(pc, param_str, TokenIdStringLiteral);
 
-    parse_string_literal(pc, param_str, &node->data.directive.param);
+    parse_string_literal(pc, param_str, &node->data.directive.param, nullptr);
 
     Token *r_paren = &pc->tokens->at(token_index);
     token_index += 1;
@@ -718,7 +782,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         return node;
     } else if (token->id == TokenIdStringLiteral) {
         AstNode *node = ast_create_node(pc, NodeTypeStringLiteral, token);
-        parse_string_literal(pc, token, &node->data.string);
+        parse_string_literal(pc, token, &node->data.string, nullptr);
         *token_index += 1;
         return node;
     } else if (token->id == TokenIdKeywordUnreachable) {
@@ -861,7 +925,7 @@ static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bo
     *token_index += 1;
 
     AstNode *node = ast_create_node(pc, NodeTypeCastExpr, as_kw);
-    node->data.cast_expr.prefix_op_expr = prefix_op_expr;
+    node->data.cast_expr.expr = prefix_op_expr;
 
     node->data.cast_expr.type = ast_parse_type(pc, *token_index, token_index);
 
@@ -1333,6 +1397,141 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand
     return node;
 }
 
+static Token *ast_eat_token(ParseContext *pc, int *token_index, TokenId token_id) {
+    Token *token = &pc->tokens->at(*token_index);
+    ast_expect_token(pc, token, token_id);
+    *token_index += 1;
+    return token;
+}
+
+
+/*
+AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen)
+*/
+static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode *node) {
+    ast_eat_token(pc, token_index, TokenIdLBracket);
+    Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol);
+    ast_eat_token(pc, token_index, TokenIdRBracket);
+
+    Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral);
+
+    ast_eat_token(pc, token_index, TokenIdLParen);
+    AstNode *expr_node = ast_parse_expression(pc, token_index, true);
+    ast_eat_token(pc, token_index, TokenIdRParen);
+
+    AsmInput *asm_input = allocate<AsmInput>(1);
+    ast_buf_from_token(pc, alias, &asm_input->asm_symbolic_name);
+    parse_string_literal(pc, constraint, &asm_input->constraint, nullptr);
+    asm_input->expr = expr_node;
+    node->data.asm_expr.input_list.append(asm_input);
+}
+
+/*
+AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) token(Symbol) token(RParen)
+*/
+static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) {
+    ast_eat_token(pc, token_index, TokenIdLBracket);
+    Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol);
+    ast_eat_token(pc, token_index, TokenIdRBracket);
+
+    Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral);
+
+    ast_eat_token(pc, token_index, TokenIdLParen);
+    Token *out_symbol = ast_eat_token(pc, token_index, TokenIdSymbol);
+    ast_eat_token(pc, token_index, TokenIdRParen);
+
+    AsmOutput *asm_output = allocate<AsmOutput>(1);
+    ast_buf_from_token(pc, alias, &asm_output->asm_symbolic_name);
+    parse_string_literal(pc, constraint, &asm_output->constraint, nullptr);
+    ast_buf_from_token(pc, out_symbol, &asm_output->variable_name);
+    node->data.asm_expr.output_list.append(asm_output);
+}
+
+/*
+AsmClobbers: token(Colon) list(token(String), token(Comma))
+*/
+static void ast_parse_asm_clobbers(ParseContext *pc, int *token_index, AstNode *node) {
+    Token *colon_tok = &pc->tokens->at(*token_index);
+
+    if (colon_tok->id != TokenIdColon)
+        return;
+
+    *token_index += 1;
+
+    for (;;) {
+        Token *string_tok = &pc->tokens->at(*token_index);
+        ast_expect_token(pc, string_tok, TokenIdStringLiteral);
+        *token_index += 1;
+
+        Buf *clobber_buf = buf_alloc();
+        parse_string_literal(pc, string_tok, clobber_buf, nullptr);
+        node->data.asm_expr.clobber_list.append(clobber_buf);
+
+        Token *comma = &pc->tokens->at(*token_index);
+
+        if (comma->id == TokenIdComma) {
+            *token_index += 1;
+            continue;
+        } else {
+            break;
+        }
+    }
+}
+
+/*
+AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers)
+*/
+static void ast_parse_asm_input(ParseContext *pc, int *token_index, AstNode *node) {
+    Token *colon_tok = &pc->tokens->at(*token_index);
+
+    if (colon_tok->id != TokenIdColon)
+        return;
+
+    *token_index += 1;
+
+    for (;;) {
+        ast_parse_asm_input_item(pc, token_index, node);
+
+        Token *comma = &pc->tokens->at(*token_index);
+
+        if (comma->id == TokenIdComma) {
+            *token_index += 1;
+            continue;
+        } else {
+            break;
+        }
+    }
+
+    ast_parse_asm_clobbers(pc, token_index, node);
+}
+
+/*
+AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput)
+*/
+static void ast_parse_asm_output(ParseContext *pc, int *token_index, AstNode *node) {
+    Token *colon_tok = &pc->tokens->at(*token_index);
+
+    if (colon_tok->id != TokenIdColon)
+        return;
+
+    *token_index += 1;
+
+    for (;;) {
+        ast_parse_asm_output_item(pc, token_index, node);
+
+        Token *comma = &pc->tokens->at(*token_index);
+
+        if (comma->id == TokenIdComma) {
+            *token_index += 1;
+            continue;
+        } else {
+            break;
+        }
+    }
+
+    ast_parse_asm_input(pc, token_index, node);
+}
+
 /*
 AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen)
 */
@@ -1366,9 +1565,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand
     ast_expect_token(pc, template_tok, TokenIdStringLiteral);
     *token_index += 1;
 
-    parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template);
+    parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template,
+            &node->data.asm_expr.offset_map);
     parse_asm_template(pc, node);
 
+    ast_parse_asm_output(pc, token_index, node);
+
     Token *rparen_tok = &pc->tokens->at(*token_index);
     ast_expect_token(pc, rparen_tok, TokenIdRParen);
     *token_index += 1;
@@ -1675,7 +1877,7 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b
     *token_index += 1;
     ast_expect_token(pc, export_name, TokenIdStringLiteral);
 
-    parse_string_literal(pc, export_name, &node->data.root_export_decl.name);
+    parse_string_literal(pc, export_name, &node->data.root_export_decl.name, nullptr);
 
     Token *semicolon = &pc->tokens->at(*token_index);
     *token_index += 1;
@@ -1705,7 +1907,7 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory
 
     AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw);
 
-    parse_string_literal(pc, use_name, &node->data.use.path);
+    parse_string_literal(pc, use_name, &node->data.use.path, nullptr);
 
     node->data.use.directives = pc->directive_list;
     pc->directive_list = nullptr;
src/parser.hpp
@@ -169,8 +169,7 @@ struct AstNodeRootExportDecl {
 };
 
 struct AstNodeCastExpr {
-    AstNode *prefix_op_expr;
-    // if type is non-null, do cast, otherwise nothing
+    AstNode *expr;
     AstNode *type;
 };
 
@@ -205,10 +204,31 @@ struct AstNodeGoto {
     Buf name;
 };
 
+struct AsmOutput {
+    Buf asm_symbolic_name;
+    Buf constraint;
+    Buf variable_name;
+};
+
+struct AsmInput {
+    Buf asm_symbolic_name;
+    Buf constraint;
+    AstNode *expr;
+};
+
+struct SrcPos {
+    int line;
+    int column;
+};
+
 struct AstNodeAsmExpr {
     bool is_volatile;
     Buf asm_template;
+    ZigList<SrcPos> offset_map;
     ZigList<AsmToken> token_list;
+    ZigList<AsmOutput*> output_list;
+    ZigList<AsmInput*> input_list;
+    ZigList<Buf*> clobber_list;
 };
 
 struct AstNode {
@@ -250,6 +270,7 @@ struct AstNode {
 enum AsmTokenId {
     AsmTokenIdTemplate,
     AsmTokenIdPercent,
+    AsmTokenIdVar,
 };
 
 struct AsmToken {
std/std.zig
@@ -1,13 +1,13 @@
 fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize {
     let mut result : isize;
-    asm volatile (
-        "mov %[number], %%rax\n"
-        "mov %[arg1], %%rdi\n"
-        "mov %[arg2], %%rsi\n"
-        "mov %[arg3], %%rdx\n"
-        "syscall\n"
-        "mov %%rax, %[ret]\n"
-        : [ret] "=r" (result)
+    asm volatile ("
+        mov %[number], %%rax
+        mov %[arg1], %%rdi
+        mov %[arg2], %%rsi
+        mov %[arg3], %%rdx
+        syscall
+        mov %%rax, %[ret]"
+        : [ret] "=m" (result)
         : [number] "r" (number), [arg1] "r" (arg1), [arg2] "r" (arg2), [arg3] "r" (arg3)
         : "rcx", "r11", "rax", "rdi", "rsi", "rdx");
     return result;
@@ -16,8 +16,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize {
 // TODO error handling
 // TODO zig strings instead of C strings
 // TODO handle buffering and flushing
-pub print_str(str : *const u8, len: isize) {
+// TODO non-i32 integer literals so we can remove the casts
+pub fn print_str(str : *const u8, len: isize) {
     let SYS_write = 1;
     let stdout_fileno = 1;
-    syscall3(SYS_write, stdout_fileno, str as isize, str_len);
+    syscall3(SYS_write as isize, stdout_fileno as isize, str as isize, len);
 }
test/run_tests.cpp
@@ -398,6 +398,18 @@ loop_2_end:
 }
     )SOURCE", "OK\n");
 
+
+    add_simple_case("hello world without libc", R"SOURCE(
+use "std.zig";
+
+export fn main(argc : isize, argv : *mut *mut u8, env : *mut *mut u8) -> i32 {
+    print_str("Hello, world!\n", 14 as isize);
+    return 0;
+}
+    )SOURCE", "Hello, world!\n");
+
+
+
 }
 
 static void add_compile_failure_test_cases(void) {