Index: uspace/app/sbi/Makefile
===================================================================
--- uspace/app/sbi/Makefile	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/Makefile	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -28,5 +28,6 @@
 
 USPACE_PREFIX = ../..
-EXTRA_CFLAGS = -D__HELENOS__
+LIBS = $(LIB_PREFIX)/clui/libclui.a
+EXTRA_CFLAGS = -D__HELENOS__ -I$(LIB_PREFIX)/clui
 
 BINARY = sbi
Index: uspace/app/sbi/src/ancr.c
===================================================================
--- uspace/app/sbi/src/ancr.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/ancr.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -67,4 +67,7 @@
  * Note that currently we expect there to be exactly one module in the
  * whole program.
+ *
+ * @param prog		Program being processed.
+ * @param module	Module to process.
  */
 void ancr_module_process(stree_program_t *prog, stree_module_t *module)
@@ -79,6 +82,4 @@
 		modm = list_node_data(node, stree_modm_t *);
 		assert(modm->mc == mc_csi); /* XXX */
-/*		printf("ancr_csi_process() on '%s'\n",
-		    strtab_get_str(modm->u.csi->name->sid));*/
 		ancr_csi_dfs(prog, modm->u.csi);
 
@@ -92,4 +93,7 @@
  * process all CSI nodes by calling ancr_csi_process() on them.
  * (Which causes that and possibly some other nodes to be processed).
+ *
+ * @param prog		Program being processed.
+ * @param csi		CSI node to visit.
  */
 static void ancr_csi_dfs(stree_program_t *prog, stree_csi_t *csi)
@@ -116,4 +120,7 @@
  * Fist processes the pre-required nodes (outer CSI and base CSIs),
  * then processes @a node. This is the core 'outward-and-baseward' walk.
+ *
+ * @param prog		Program being processed.
+ * @param node		CSI node to process.
  */
 static void ancr_csi_process(stree_program_t *prog, stree_csi_t *node)
@@ -171,4 +178,7 @@
  * We have detected a loop in CSI ancestry. Traverse it (by following the
  * nodes in ws_active state and print it.
+ *
+ * @param prog		Program.
+ * @param node		CSI node participating in an ancestry cycle.
  */
 static void ancr_csi_print_cycle(stree_program_t *prog, stree_csi_t *node)
Index: uspace/app/sbi/src/builtin.c
===================================================================
--- uspace/app/sbi/src/builtin.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/builtin.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -27,5 +27,13 @@
  */
 
-/** @file Builtin symbol binding. */
+/** @file Builtin symbol binding.
+ *
+ * 'Builtin' symbols are implemented outside of the language itself.
+ * Here we refer to entities residing within the interpreted universe
+ * as 'internal', while anything implemented outside this universe
+ * as 'external'. This module facilitates declaration of builtin
+ * symbols and the binding of these symbols to their external
+ * implementation.
+ */
 
 #include <stdio.h>
@@ -54,4 +62,6 @@
  *
  * Declares symbols that will be hooked to builtin interpreter procedures.
+ *
+ * @param program	Program in which to declare builtin symbols.
  */
 void builtin_declare(stree_program_t *program)
@@ -86,5 +96,12 @@
 }
 
-/** Get grandfather class. */
+/** Get grandfather class.
+ *
+ * Grandfather class is the class from which all other classes are
+ * (directly or indirectly) derived.
+ *
+ * @param builtin	Builtin context (corresponsds to program).
+ * @return		Grandfather class (CSI).
+ */
 stree_csi_t *builtin_get_gf_class(builtin_t *builtin)
 {
@@ -95,4 +112,8 @@
 }
 
+/** Allocate new builtin context object.
+ *
+ * @return	Builtin context object.
+ */
 static builtin_t *builtin_new(void)
 {
@@ -126,5 +147,12 @@
 }
 
-/** Simplifed search for a global symbol. */
+/** Simplifed search for a global symbol.
+ *
+ * The specified symbol must exist.
+ *
+ * @param bi		Builtin context object.
+ * @param sym_name	Name of symbol to find.
+ * @return		Symbol.
+ */
 stree_symbol_t *builtin_find_lvl0(builtin_t *bi, const char *sym_name)
 {
@@ -136,9 +164,18 @@
 	ident->sid = strtab_get_sid(sym_name);
 	sym = symbol_lookup_in_csi(bi->program, NULL, ident);
+	assert(sym != NULL);
 
 	return sym;
 }
 
-/** Simplifed search for a level 1 symbol. */
+/** Simplifed search for a level 1 symbol.
+ *
+ * The specified symbol must exist.
+ *
+ * @param bi		Builtin context object.
+ * @param csi_name	CSI in which to look for symbol.
+ * @param sym_name	Name of symbol to find.
+ * @return		Symbol.
+ */
 stree_symbol_t *builtin_find_lvl1(builtin_t *bi, const char *csi_name,
     const char *sym_name)
@@ -154,4 +191,5 @@
 	ident->sid = strtab_get_sid(csi_name);
 	csi_sym = symbol_lookup_in_csi(bi->program, NULL, ident);
+	assert(csi_sym != NULL);
 	csi = symbol_to_csi(csi_sym);
 	assert(csi != NULL);
@@ -159,8 +197,19 @@
 	ident->sid = strtab_get_sid(sym_name);
 	mbr_sym = symbol_lookup_in_csi(bi->program, csi, ident);
+	assert(mbr_sym != NULL);
 
 	return mbr_sym;
 }
 
+/** Bind level 1 member function to external implementation.
+ *
+ * Binds a member function (of a global class) to external implementation.
+ * The specified CSI and member function must exist.
+ *
+ * @param bi		Builtin context object.
+ * @param csi_name	CSI which contains the function.
+ * @param sym_name	Function name.
+ * @param bproc		Pointer to C function implementation.
+ */
 void builtin_fun_bind(builtin_t *bi, const char *csi_name,
     const char *sym_name, builtin_proc_t bproc)
@@ -177,4 +226,11 @@
 }
 
+/** Execute a builtin procedure.
+ *
+ * Executes a procedure that has an external implementation.
+ *
+ * @param run		Runner object.
+ * @param proc		Procedure that has an external implementation.
+ */
 void builtin_run_proc(run_t *run, stree_proc_t *proc)
 {
@@ -201,5 +257,13 @@
 }
 
-/** Get pointer to member var of current object. */
+/** Get pointer to member var of current object.
+ *
+ * Returns the var node that corresponds to a member of the currently
+ * active object with the given name. This member must exist.
+ *
+ * @param run		Runner object.
+ * @param mbr_name	Name of member to find.
+ * @return		Var node of the member.
+ */
 rdata_var_t *builtin_get_self_mbr_var(run_t *run, const char *mbr_name)
 {
@@ -220,5 +284,13 @@
 }
 
-/** Declare a builtin function in @a csi. */
+/** Declare a builtin function in @a csi.
+ *
+ * Declare a builtin function member of CSI @a csi. Deprecated in favor
+ * of builtin_code_snippet().
+ *
+ * @param csi		CSI in which to declare function.
+ * @param name		Name of member function to declare.
+ * @return		Symbol of newly declared function.
+ */
 stree_symbol_t *builtin_declare_fun(stree_csi_t *csi, const char *name)
 {
@@ -251,5 +323,13 @@
 }
 
-/** Add one formal parameter to function. */
+/** Add one formal parameter to function.
+ *
+ * Used to incrementally construct formal parameter list of a builtin
+ * function. Deprecated in favor of builtin_code_snippet(). Does not
+ * support type checking.
+ *
+ * @param fun_sym	Symbol of function to add parameters to.
+ * @param name		Name of parameter to add.
+ */
 void builtin_fun_add_arg(stree_symbol_t *fun_sym, const char *name)
 {
Index: uspace/app/sbi/src/debug.h
===================================================================
--- uspace/app/sbi/src/debug.h	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/debug.h	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -33,4 +33,10 @@
 //#define DEBUG_PARSE_TRACE
 
+/**
+ *Uncomment this to get extra verbose messages from parser's lexing
+ * primitives.
+ */
+//#define DEBUG_LPARSE_TRACE
+
 /** Uncomment this to get verbose debugging messagges during typing. */
 //#define DEBUG_TYPE_TRACE
Index: uspace/app/sbi/src/imode.c
===================================================================
--- uspace/app/sbi/src/imode.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/imode.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -27,12 +27,24 @@
  */
 
-/** @file Interactive mode. */
+/** @file Interactive mode.
+ *
+ * In interactive mode the user types in statements. As soon as the outermost
+ * statement is complete (terminated with ';' or 'end'), the interpreter
+ * executes it. Otherwise it prompts the user until the entire statement
+ * is read in.
+ *
+ * The user interface depends on the OS. In HelenOS we use the CLUI library
+ * which gives us rich line editing capabilities.
+ */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include "ancr.h"
+#include "assert.h"
 #include "builtin.h"
+#include "intmap.h"
 #include "list.h"
 #include "mytypes.h"
+#include "rdata.h"
 #include "stree.h"
 #include "strtab.h"
@@ -45,4 +57,8 @@
 #include "imode.h"
 
+/** Run in interactive mode.
+ *
+ * Repeatedly read in statements from the user and execute them.
+ */
 void imode_run(void)
 {
@@ -56,4 +72,8 @@
 	stree_symbol_t *fun_sym;
 	stype_t stype;
+	stype_block_vr_t *block_vr;
+	list_node_t *bvr_n;
+	rdata_item_t *rexpr;
+	rdata_item_t *rexpr_vi;
 
 	run_t run;
@@ -72,6 +92,39 @@
 	ancr_module_process(program, program->module);
 
+	/* Construct typing context. */
+	stype.program = program;
+	stype.proc_vr = stype_proc_vr_new();
+	list_init(&stype.proc_vr->block_vr);
+	stype.current_csi = NULL;
+	proc = stree_proc_new();
+
+	fun = stree_fun_new();
+	fun_sym = stree_symbol_new(sc_fun);
+	fun_sym->u.fun = fun;
+	fun->name = stree_ident_new();
+	fun->name->sid = strtab_get_sid("$imode");
+
+	stype.proc_vr->proc = proc;
+	fun->symbol = fun_sym;
+	proc->outer_symbol = fun_sym;
+
+	/* Create block visit record. */
+	block_vr = stype_block_vr_new();
+	intmap_init(&block_vr->vdecls);
+
+	/* Add block visit record to the stack. */
+	list_append(&stype.proc_vr->block_vr, block_vr);
+
+	/* Construct run context. */
+	run.thread_ar = run_thread_ar_new();
+	list_init(&run.thread_ar->proc_ar);
+	run_proc_ar_create(&run, NULL, proc, &proc_ar);
+	list_append(&run.thread_ar->proc_ar, proc_ar);
+
 	quit_im = b_false;
 	while (quit_im != b_true) {
+		parse.error = b_false;
+		stype.error = b_false;
+
 		input_new_interactive(&input);
 
@@ -85,34 +138,34 @@
 		stat = parse_stat(&parse);
 
-		/* Construct typing context. */
-		stype.program = program;
-		stype.proc_vr = stype_proc_vr_new();
-		stype.current_csi = NULL;
-		proc = stree_proc_new();
-
-		fun = stree_fun_new();
-		fun_sym = stree_symbol_new(sc_fun);
-		fun_sym->u.fun = fun;
-		fun->name = stree_ident_new();
-		fun->name->sid = strtab_get_sid("$imode");
-
-		stype.proc_vr->proc = proc;
-		fun->symbol = fun_sym;
-		proc->outer_symbol = fun_sym;
-
-		/* Construct run context. */
-		run.thread_ar = run_thread_ar_new();
-		list_init(&run.thread_ar->proc_ar);
-		run_proc_ar_create(&run, NULL, proc, &proc_ar);
-		list_append(&run.thread_ar->proc_ar, proc_ar);
+		if (parse.error != b_false)
+			continue;
 
 		/* Type statement. */
-		stype_stat(&stype, stat);
+		stype_stat(&stype, stat, b_true);
+
+		if (stype.error != b_false)
+			continue;
 
 		/* Run statement. */
 		run_init(&run);
 		run.program = program;
-		run_stat(&run, stat);
+		run_stat(&run, stat, &rexpr);
+
+		if (rexpr != NULL) {
+			/* Convert expression result to value item. */
+			run_cvt_value_item(&run, rexpr, &rexpr_vi);
+			assert(rexpr_vi->ic == ic_value);
+
+			/* Print result. */
+			printf("Result: ");
+			rdata_value_print(rexpr_vi->u.value);
+			printf("\n");
+		}
 	}
+
+	/* Remove block visit record from the stack, */
+	bvr_n = list_last(&stype.proc_vr->block_vr);
+	assert(list_node_data(bvr_n, stype_block_vr_t *) == block_vr);
+	list_remove(&stype.proc_vr->block_vr, bvr_n);
 
 	printf("\nBye!\n");
Index: uspace/app/sbi/src/input.c
===================================================================
--- uspace/app/sbi/src/input.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/input.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -29,5 +29,7 @@
 /** @file Input module
  *
- * Reads source code from a file.
+ * Reads source code. Currently input can be read from a file (standard
+ * case), from a string literal (when parsing builtin code) or interactively
+ * from the user.
  */
 
@@ -35,8 +37,10 @@
 #include <stdlib.h>
 #include "mytypes.h"
+#include "os/os.h"
 #include "strtab.h"
 
 #include "input.h"
 
+/** Size of input buffer. XXX This limits the maximum line length. */
 #define INPUT_BUFFER_SIZE 256
 
@@ -45,5 +49,12 @@
 static void input_init_string(input_t *input, const char *str);
 
-/** Create new input object for reading from file. */
+/** Create new input object for reading from file.
+ *
+ * @param input		Place to store pointer to new input object.
+ * @param fname		Name of file to read from.
+ *
+ * @return		EOK on success, ENOMEM when allocation fails,
+ *			ENOENT when opening file fails.
+ */
 int input_new_file(input_t **input, char *fname)
 {
@@ -55,5 +66,9 @@
 }
 
-/** Create new input object for reading from interactive input. */
+/** Create new input object for reading from interactive input.
+ *
+ * @param input		Place to store pointer to new input object.
+ * @return		EOK on success, ENOMEM when allocation fails.
+ */
 int input_new_interactive(input_t **input)
 {
@@ -66,5 +81,10 @@
 }
 
-/** Create new input object for reading from string. */
+/** Create new input object for reading from string.
+ *
+ * @param input		Place to store pointer to new input object.
+ * @param str		String literal from which to read input.
+ * @return		EOK on success, ENOMEM when allocation fails.
+ */
 int input_new_string(input_t **input, const char *str)
 {
@@ -77,5 +97,11 @@
 }
 
-/** Initialize input object for reading from file. */
+/** Initialize input object for reading from file.
+ *
+ * @param input		Input object.
+ * @param fname		Name of file to read from.
+ *
+ * @return		EOK on success, ENOENT when opening file fails.
+*/
 static int input_init_file(input_t *input, char *fname)
 {
@@ -92,4 +118,5 @@
 	}
 
+	input->str = NULL;
 	input->line_no = 0;
 	input->fin = f;
@@ -97,5 +124,8 @@
 }
 
-/** Initialize input object for reading from interactive input. */
+/** Initialize input object for reading from interactive input.
+ *
+ * @param input		Input object.
+ */
 static void input_init_interactive(input_t *input)
 {
@@ -106,9 +136,14 @@
 	}
 
+	input->str = NULL;
 	input->line_no = 0;
 	input->fin = NULL;
 }
 
-/** Initialize input object for reading from string. */
+/** Initialize input object for reading from string.
+ *
+ * @param input		Input object.
+ * @param str		String literal from which to read input.
+ */
 static void input_init_string(input_t *input, const char *str)
 {
@@ -124,9 +159,21 @@
 }
 
-/** Get next line of input. */
+/** Get next line of input.
+ *
+ * The pointer stored in @a line is owned by @a input and is valid until the
+ * next call to input_get_line(). The caller is not to free it. The returned
+ * line is terminated with '\n' if another line is coming (potentially empty).
+ * An empty line ("") signals end of input.
+ *
+ * @param input		Input object.
+ * @param line		Place to store pointer to next line.
+ *
+ * @return		EOK on success, EIO on failure.
+ */
 int input_get_line(input_t *input, char **line)
 {
 	const char *sp;
 	char *dp;
+	char *line_p;
 	size_t cnt;
 
@@ -166,11 +213,9 @@
 			printf("...  ");
 
-		if (fgets(input->buffer, INPUT_BUFFER_SIZE, stdin) == NULL)
-			input->buffer[0] = '\0';
-
-		if (ferror(stdin))
+		fflush(stdout);
+		if (os_input_line(&line_p) != EOK)
 			return EIO;
 
-		*line = input->buffer;
+		*line = line_p;
 	}
 
@@ -179,5 +224,10 @@
 }
 
-/** Get number of the last provided line of input. */
+/** Get number of the last provided line of input.
+ *
+ * @param input		Input module.
+ * @return		Line number of the last provided input line (counting
+ *			from 1 up).
+ */
 int input_get_line_no(input_t *input)
 {
Index: uspace/app/sbi/src/intmap.c
===================================================================
--- uspace/app/sbi/src/intmap.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/intmap.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -29,5 +29,6 @@
 /** @file Integer map.
  *
- * Maps integers to pointers (void *).
+ * Maps integers to pointers (void *). Current implementation is trivial
+ * (linked list of key-value pairs).
  */
 
@@ -40,5 +41,8 @@
 #include "intmap.h"
 
-/** Initialize map. */
+/** Initialize map.
+ *
+ * @param intmap	Map to initialize.
+ */
 void intmap_init(intmap_t *intmap)
 {
@@ -46,5 +50,14 @@
 }
 
-/** Set value corresponding to a key. */
+/** Set value corresponding to a key.
+ *
+ * If there already exists a mapping for @a key in the map, it is
+ * silently replaced. If @a value is @c NULL, the mapping for @a key
+ * is removed from the map.
+ *
+ * @param intmap	Map.
+ * @param key		Key (integer).
+ * @param value		Value (must be a pointer) or @c NULL.
+ */
 void intmap_set(intmap_t *intmap, int key, void *value)
 {
@@ -83,5 +96,12 @@
 }
 
-/** Get value corresponding to a key. */
+/** Get value corresponding to a key.
+ *
+ * @param intmap	Map.
+ * @param key		Key for which to retrieve mapping.
+ *
+ * @return		Value correspoding to @a key or @c NULL if no mapping
+ *			exists.
+ */
 void *intmap_get(intmap_t *intmap, int key)
 {
Index: uspace/app/sbi/src/lex.c
===================================================================
--- uspace/app/sbi/src/lex.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/lex.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -145,5 +145,11 @@
 };
 
-/** Print lclass value. */
+/** Print lclass value.
+ *
+ * Prints lclass (lexical element class) value in human-readable form
+ * (for debugging).
+ *
+ * @param lclass	Lclass value for display.
+ */
 void lclass_print(lclass_t lclass)
 {
@@ -184,5 +190,10 @@
 }
 
-/** Print lexical element. */
+/** Print lexical element.
+ *
+ * Prints lexical element in human-readable form (for debugging).
+ *
+ * @param lem		Lexical element for display.
+ */
 void lem_print(lem_t *lem)
 {
@@ -203,5 +214,10 @@
 }
 
-/** Print lem coordinates. */
+/** Print lem coordinates.
+ *
+ * Print the coordinates (line number, column number) of a lexical element.
+ *
+ * @param lem		Lexical element for coordinate printing.
+ */
 void lem_print_coords(lem_t *lem)
 {
@@ -209,5 +225,9 @@
 }
 
-/** Initialize lexer instance. */
+/** Initialize lexer instance.
+ *
+ * @param lex		Lexer object to initialize.
+ * @param input		Input to associate with lexer.
+ */
 void lex_init(lex_t *lex, struct input *input)
 {
@@ -229,5 +249,7 @@
 /** Advance to next lexical element.
  *
- * The new element be read in lazily then it is actually accessed.
+ * The new element is read in lazily then it is actually accessed.
+ *
+ * @param lex		Lexer object.
  */
 void lex_next(lex_t *lex)
@@ -243,4 +265,6 @@
  *
  * The returned pointer is invalidated by next call to lex_next()
+ *
+ * @param lex		Lexer object.
  */
 lem_t *lex_get_current(lex_t *lex)
@@ -250,5 +274,8 @@
 }
 
-/** Read in the current lexical element (unless already read in). */
+/** Read in the current lexical element (unless already read in).
+ *
+ * @param lex		Lexer object.
+ */
 static void lex_touch(lex_t *lex)
 {
@@ -267,5 +294,12 @@
 /** Try reading next lexical element.
  *
- * @return @c b_true on success or @c b_false if it needs restarting.
+ * Attemps to read the next lexical element. In some cases (such as a comment)
+ * this function will need to give it another try and returns @c b_false
+ * in such case.
+ *
+ * @param lex		Lexer object.
+ * @return		@c b_true on success or @c b_false if it needs
+ *			restarting. On success the lem is stored to
+ *			the current lem in @a lex.
  */
 static bool_t lex_read_try(lex_t *lex)
@@ -369,5 +403,11 @@
 }
 
-/** Lex a word (identifier or keyword). */
+/** Lex a word (identifier or keyword).
+ *
+ * Read in a word. This may later turn out to be a keyword or a regular
+ * identifier. It is stored in the current lem in @a lex.
+ *
+ * @param lex		Lexer object.
+ */
 static void lex_word(lex_t *lex)
 {
@@ -409,5 +449,10 @@
 }
 
-/** Lex a numeric literal. */
+/** Lex a numeric literal.
+ *
+ * Reads in a numeric literal and stores it in the current lem in @a lex.
+ *
+ * @param lex		Lexer object.
+ */
 static void lex_number(lex_t *lex)
 {
@@ -429,5 +474,10 @@
 }
 
-/** Lex a string literal. */
+/** Lex a string literal.
+ *
+ * Reads in a string literal and stores it in the current lem in @a lex.
+ *
+ * @param lex		Lexer object.
+ */
 static void lex_string(lex_t *lex)
 {
@@ -461,5 +511,10 @@
 }
 
-/** Lex a single-line comment. */
+/** Lex a single-line comment.
+ *
+ * This does not produce any lem. The comment is just skipped.
+ *
+ * @param lex		Lexer object.
+ */
 static void lex_skip_comment(lex_t *lex)
 {
@@ -475,5 +530,10 @@
 }
 
-/** Skip whitespace characters. */
+/** Skip whitespace characters.
+ *
+ * This does not produce any lem. The whitespace is just skipped.
+ *
+ * @param lex		Lexer object.
+ */
 static void lex_skip_ws(lex_t *lex)
 {
@@ -485,6 +545,8 @@
 	while (b_true) {
 		while (*bp == ' ' || *bp == '\t') {
-			if (*bp == '\t')
+			if (*bp == '\t') {
+				/* XXX This is too simplifed. */
 				lex->col_adj += (TAB_WIDTH - 1);
+			}
 			++bp;
 		}
@@ -507,5 +569,9 @@
 }
 
-/** Determine if character can start a word. */
+/** Determine if character can start a word.
+ *
+ * @param c 	Character.
+ * @return	@c b_true if @a c can start a word, @c b_false otherwise.
+ */
 static bool_t is_wstart(char c)
 {
@@ -514,5 +580,10 @@
 }
 
-/** Determine if character can continue a word. */
+/** Determine if character can continue a word.
+ *
+ * @param c 	Character.
+ * @return	@c b_true if @a c can start continue word, @c b_false
+ *		otherwise.
+ */
 static bool_t is_wcont(char c)
 {
@@ -520,4 +591,9 @@
 }
 
+/** Determine if character is a numeric digit.
+ *
+ * @param c 	Character.
+ * @return	@c b_true if @a c is a numeric digit, @c b_false otherwise.
+ */
 static bool_t is_digit(char c)
 {
@@ -525,4 +601,9 @@
 }
 
+/** Determine numeric value of digit character.
+ *
+ * @param c 	Character, must be a valid decimal digit.
+ * @return	Value of the digit (0-9).
+ */
 static int digit_value(char c)
 {
Index: uspace/app/sbi/src/list.c
===================================================================
--- uspace/app/sbi/src/list.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/list.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -47,5 +47,8 @@
 static bool_t list_node_present(list_t *list, list_node_t *node);
 
-/** Initialize list. */
+/** Initialize list.
+ *
+ * @param list	List to initialize.
+ */
 void list_init(list_t *list)
 {
@@ -54,5 +57,11 @@
 }
 
-/** Append data to list. */
+/** Append data to list.
+ *
+ * Create a new list node and append it at the end of the list.
+ *
+ * @param list	Linked list.
+ * @param data	Data for the new node.
+ */
 void list_append(list_t *list, void *data)
 {
@@ -63,5 +72,11 @@
 }
 
-/** Prepend data to list. */
+/** Prepend data to list.
+ *
+ * Create a new list node and prepend it at the beginning of the list.
+ *
+ * @param list	Linked list.
+ * @param data	Data for the new node.
+ */
 void list_prepend(list_t *list, void *data)
 {
@@ -72,5 +87,13 @@
 }
 
-/** Remove data from list. */
+/** Remove data from list.
+ *
+ * Removes the given node from a list and destroys it. Any data the node might
+ * have is ignored. If asserts are on, we check wheter node is really present
+ * in the list the caller is requesting us to remove it from.
+ *
+ * @param list	Linked list.
+ * @param node	List node to remove.
+ */
 void list_remove(list_t *list, list_node_t *node)
 {
@@ -82,5 +105,9 @@
 }
 
-/** Return first list node or NULL if list is empty. */
+/** Return first list node or NULL if list is empty.
+ *
+ * @param list	Linked list.
+ * @return	First node of the list or @c NULL if the list is empty.
+ */
 list_node_t *list_first(list_t *list)
 {
@@ -93,5 +120,9 @@
 }
 
-/** Return last list node or NULL if list is empty. */
+/** Return last list node or NULL if list is empty.
+ *
+ * @param list	Linked list.
+ * @return	Last node of the list or @c NULL if the list is empty.
+ */
 list_node_t *list_last(list_t *list)
 {
@@ -104,5 +135,10 @@
 }
 
-/** Return next list node or NULL if this was the last one. */
+/** Return next list node or NULL if this was the last one.
+ *
+ * @param list	Linked list.
+ * @param node	Node whose successor we are interested in.
+ * @return	Following list node or @c NULL if @a node is last.
+ */
 list_node_t *list_next(list_t *list, list_node_t *node)
 {
@@ -114,5 +150,10 @@
 }
 
-/** Return next list node or NULL if this was the last one. */
+/** Return previous list node or NULL if this was the last one.
+ *
+ * @param list	Linked list.
+ * @param node	Node whose predecessor we are interested in.
+ * @return	Preceding list node or @c NULL if @a node is last.
+ */
 list_node_t *list_prev(list_t *list, list_node_t *node)
 {
@@ -124,5 +165,9 @@
 }
 
-/** Return b_true if list is empty. */
+/** Return b_true if list is empty.
+ *
+ * @param list	Linked list.
+ * @return	@c b_true if list is empty, @c b_false otherwise.
+ */
 bool_t list_is_empty(list_t *list)
 {
@@ -130,5 +175,11 @@
 }
 
-/** Change node data. */
+/** Change node data.
+ *
+ * Change the data associated with a node.
+ *
+ * @param node	List node.
+ * @param data	New data for node.
+ */
 void list_node_setdata(list_node_t *node, void *data)
 {
@@ -136,5 +187,9 @@
 }
 
-/** Create new node. */
+/** Create new node.
+ *
+ * @param data	Initial data for node.
+ * @return	New list node.
+ */
 static list_node_t *list_node_new(void *data)
 {
@@ -154,5 +209,8 @@
 }
 
-/** Delete node. */
+/** Delete node.
+ *
+ * @param node	List node. Must not take part in any list.
+ */
 static void list_node_delete(list_node_t *node)
 {
@@ -163,6 +221,14 @@
 }
 
-/** Insert node between two other nodes. */
-static void list_node_insert_between(list_node_t *n, list_node_t *a, list_node_t *b)
+/** Insert node between two other nodes.
+ *
+ * Inserts @a n between neighboring nodes @a a and @a b.
+ *
+ * @param n	Node to insert.
+ * @param a	Node to precede @a n.
+ * @param b	Node to follow @a n.
+ */
+static void list_node_insert_between(list_node_t *n, list_node_t *a,
+    list_node_t *b)
 {
 	assert(n->prev == NULL);
@@ -177,5 +243,10 @@
 }
 
-/** Unlink node. */
+/** Unlink node.
+ *
+ * Unlink node from the list it is currently in.
+ * 
+ * @param n	Node to unlink from its current list.
+ */
 static void list_node_unlink(list_node_t *n)
 {
@@ -197,5 +268,10 @@
 }
 
-/** Check whether @a node is in list @a list. */
+/** Check whether @a node is in list @a list.
+ *
+ * @param list	Linked list.
+ * @param node	Node.
+ * @return	@c b_true if @a node is part of @a list, @c b_false otherwise.
+ */
 static bool_t list_node_present(list_t *list, list_node_t *node)
 {
Index: uspace/app/sbi/src/main.c
===================================================================
--- uspace/app/sbi/src/main.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/main.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -27,5 +27,10 @@
  */
 
-/** @file Main module. */
+/** @file Main module.
+ *
+ * Main entry point for SBI, the Sysel Bootstrap Interpreter.
+ * When run without parameters, the interpreter will enter interactive
+ * mode.
+ */
 
 #include <stdio.h>
@@ -45,4 +50,8 @@
 void syntax_print(void);
 
+/** Main entry point.
+ *
+ * @return	Zero on success, non-zero on error.
+ */
 int main(int argc, char *argv[])
 {
@@ -85,4 +94,7 @@
 	parse_module(&parse);
 
+	if (parse.error)
+		return 1;
+
 	/* Resolve ancestry. */
 	ancr_module_process(program, parse.cur_mod);
@@ -90,5 +102,9 @@
 	/* Type program. */
 	stype.program = program;
+	stype.error = b_false;
 	stype_module(&stype, program->module);
+
+	if (stype.error)
+		return 1;
 
 	/* Run program. */
@@ -99,4 +115,5 @@
 }
 
+/** Print command-line syntax help. */
 void syntax_print(void)
 {
Index: uspace/app/sbi/src/os/helenos.c
===================================================================
--- uspace/app/sbi/src/os/helenos.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/os/helenos.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -34,4 +34,5 @@
 #include <str.h>
 #include <task.h>
+#include <tinput.h>
 
 #include "os.h"
@@ -40,4 +41,6 @@
  * Using HelenOS-specific string API.
  */
+
+static tinput_t *tinput = NULL;
 
 /** Concatenate two strings. */
@@ -98,4 +101,26 @@
 }
 
+/** Read one line of input from the user. */
+int os_input_line(char **ptr)
+{
+	char *line;
+
+	if (tinput == NULL) {
+		tinput = tinput_new();
+		if (tinput == NULL)
+			return EIO;
+	}
+
+	line = tinput_read(tinput);
+	if (line == NULL)
+		return EIO;
+
+	/* XXX Input module needs trailing newline to keep going. */
+	*ptr = os_str_acat(line, "\n");
+	free(line);
+
+	return EOK;
+}
+
 /** Simple command execution. */
 int os_exec(char *const cmd[])
Index: uspace/app/sbi/src/os/os.h
===================================================================
--- uspace/app/sbi/src/os/os.h	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/os/os.h	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -34,4 +34,5 @@
 char *os_str_dup(const char *str);
 int os_str_get_char(const char *str, int index, int *out_char);
+int os_input_line(char **ptr);
 int os_exec(char *const cmd[]);
 
Index: uspace/app/sbi/src/os/posix.c
===================================================================
--- uspace/app/sbi/src/os/posix.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/os/posix.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -92,4 +92,22 @@
 }
 
+#define OS_INPUT_BUFFER_SIZE 256
+static char os_input_buffer[OS_INPUT_BUFFER_SIZE];
+
+/** Read one line of input from the user. */
+int os_input_line(char **ptr)
+{
+	if (fgets(os_input_buffer, OS_INPUT_BUFFER_SIZE, stdin) == NULL)
+		os_input_buffer[0] = '\0';
+
+	if (ferror(stdin)) {
+		*ptr = NULL;
+		return EIO;
+	}
+
+	*ptr = strdup(os_input_buffer);
+	return EOK;
+}
+
 /** Simple command execution. */
 int os_exec(char *const cmd[])
Index: uspace/app/sbi/src/p_expr.c
===================================================================
--- uspace/app/sbi/src/p_expr.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/p_expr.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -31,4 +31,5 @@
 #include <assert.h>
 #include <stdlib.h>
+#include "debug.h"
 #include "lex.h"
 #include "list.h"
@@ -57,11 +58,31 @@
 static stree_expr_t *parse_self_ref(parse_t *parse);
 
-/** Parse expression. */
+static stree_expr_t *parse_recovery_expr(parse_t *parse);
+
+/** Parse expression.
+ *
+ * Input is read from the input object associated with @a parse. If any
+ * error occurs, parse->error will @c b_true when this function
+ * returns. parse->error_bailout will be @c b_true if the error has not
+ * been recovered yet. Similar holds for other parsing functions in this
+ * module.
+ *
+ * @param parse		Parser object.
+ */
 stree_expr_t *parse_expr(parse_t *parse)
 {
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse expression.\n");
+#endif
+	if (parse_is_error(parse))
+		return parse_recovery_expr(parse);
+
 	return parse_assign(parse);
 }
 
-/** Parse assignment expression. */
+/** Parse assignment expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_assign(parse_t *parse)
 {
@@ -93,5 +114,8 @@
 }
 
-/** Parse comparative expression. */
+/** Parse comparative expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_comparative(parse_t *parse)
 {
@@ -105,4 +129,7 @@
 	    lcur_lc(parse) == lc_lt || lcur_lc(parse) == lc_gt ||
 	    lcur_lc(parse) == lc_lt_equal || lcur_lc(parse) == lc_gt_equal) {
+
+		if (parse_is_error(parse))
+			break;
 
 		switch (lcur_lc(parse)) {
@@ -131,5 +158,8 @@
 }
 
-/** Parse additive expression. */
+/** Parse additive expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_additive(parse_t *parse)
 {
@@ -139,4 +169,7 @@
 	a = parse_prefix(parse);
 	while (lcur_lc(parse) == lc_plus) {
+		if (parse_is_error(parse))
+			break;
+
 		lskip(parse);
 		b = parse_prefix(parse);
@@ -154,5 +187,8 @@
 }
 
-/** Parse prefix expression. */
+/** Parse prefix expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_prefix(parse_t *parse)
 {
@@ -162,5 +198,7 @@
 	case lc_plus:
 		printf("Unimplemented: Unary plus.\n");
-		exit(1);
+		a = parse_recovery_expr(parse);
+		parse_note_error(parse);
+		break;
 	case lc_new:
 		a = parse_prefix_new(parse);
@@ -174,5 +212,8 @@
 }
 
-/** Parse @c new operator. */
+/** Parse @c new operator.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_prefix_new(parse_t *parse)
 {
@@ -198,5 +239,8 @@
 }
 
-/** Parse postfix expression. */
+/** Parse postfix expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_postfix(parse_t *parse)
 {
@@ -208,4 +252,7 @@
 	while (lcur_lc(parse) == lc_period || lcur_lc(parse) == lc_lparen ||
 	    lcur_lc(parse) == lc_lsbr || lcur_lc(parse) == lc_as) {
+
+		if (parse_is_error(parse))
+			break;
 
 		switch (lcur_lc(parse)) {
@@ -232,5 +279,8 @@
 }
 
-/** Parse member access expression */
+/** Parse member access expression
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_pf_access(parse_t *parse, stree_expr_t *a)
 {
@@ -252,5 +302,8 @@
 }
 
-/** Parse function call expression. */
+/** Parse function call expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_pf_call(parse_t *parse, stree_expr_t *a)
 {
@@ -268,5 +321,5 @@
 
 	if (lcur_lc(parse) != lc_rparen) {
-		while (b_true) {
+		while (!parse_is_error(parse)) {
 			arg = parse_expr(parse);
 			list_append(&call->args, arg);
@@ -286,5 +339,8 @@
 }
 
-/** Parse index expression. */
+/** Parse index expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_pf_index(parse_t *parse, stree_expr_t *a)
 {
@@ -302,5 +358,5 @@
 
 	if (lcur_lc(parse) != lc_rsbr) {
-		while (b_true) {
+		while (!parse_is_error(parse)) {
 			arg = parse_expr(parse);
 			list_append(&index->args, arg);
@@ -320,5 +376,8 @@
 }
 
-/** Parse @c as operator. */
+/** Parse @c as operator.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_pf_as(parse_t *parse, stree_expr_t *a)
 {
@@ -339,5 +398,8 @@
 }
 
-/** Parse primitive expression. */
+/** Parse primitive expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_primitive(parse_t *parse)
 {
@@ -362,11 +424,14 @@
 	default:
 		lunexpected_error(parse);
-		exit(1);
-	}
-
-	return expr;
-}
-
-/** Parse name reference. */
+		expr = parse_recovery_expr(parse);
+	}
+
+	return expr;
+}
+
+/** Parse name reference.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_nameref(parse_t *parse)
 {
@@ -382,5 +447,8 @@
 }
 
-/** Parse integer literal. */
+/** Parse integer literal.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_lit_int(parse_t *parse)
 {
@@ -401,5 +469,8 @@
 }
 
-/** Parse reference literal (@c nil). */
+/** Parse reference literal (@c nil).
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_lit_ref(parse_t *parse)
 {
@@ -417,5 +488,8 @@
 }
 
-/** Parse string literal. */
+/** Parse string literal.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_lit_string(parse_t *parse)
 {
@@ -436,5 +510,8 @@
 }
 
-/** Parse @c self keyword. */
+/** Parse @c self keyword.
+ *
+ * @param parse		Parser object.
+ */
 static stree_expr_t *parse_self_ref(parse_t *parse)
 {
@@ -451,2 +528,21 @@
 	return expr;
 }
+
+/** Construct a special recovery expression.
+ *
+ * @param parse		Parser object.
+ */
+static stree_expr_t *parse_recovery_expr(parse_t *parse)
+{
+	stree_literal_t *literal;
+	stree_expr_t *expr;
+
+	(void) parse;
+
+	literal = stree_literal_new(ltc_ref);
+
+	expr = stree_expr_new(ec_literal);
+	expr->u.literal = literal;
+
+	return expr;
+}
Index: uspace/app/sbi/src/p_type.c
===================================================================
--- uspace/app/sbi/src/p_type.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/p_type.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -1,3 +1,3 @@
-/*                              YI
+/*
  * Copyright (c) 2010 Jiri Svoboda
  * All rights reserved.
@@ -31,4 +31,5 @@
 #include <assert.h>
 #include <stdlib.h>
+#include "debug.h"
 #include "lex.h"
 #include "list.h"
@@ -48,11 +49,31 @@
 static stree_tnameref_t *parse_tnameref(parse_t *parse);
 
-/** Parse type expression. */
+static stree_texpr_t *parse_recovery_texpr(parse_t *parse);
+
+/** Parse type expression.
+ *
+ * Input is read from the input object associated with @a parse. If any
+ * error occurs, parse->error will @c b_true when this function
+ * returns. parse->error_bailout will be @c b_true if the error has not
+ * been recovered yet. Similar holds for other parsing functions in this
+ * module.
+ *
+ * @param parse		Parser object.
+ */
 stree_texpr_t *parse_texpr(parse_t *parse)
 {
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse type expression.\n");
+#endif
+	if (parse_is_error(parse))
+		return parse_recovery_texpr(parse);
+
 	return parse_tapply(parse);
 }
 
-/** Parse type application expression. */
+/** Parse type application expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_texpr_t *parse_tapply(parse_t *parse)
 {
@@ -62,4 +83,8 @@
 	a = parse_tpostfix(parse);
 	while (lcur_lc(parse) == lc_slash) {
+
+		if (parse_is_error(parse))
+			break;
+
 		lskip(parse);
 		b = parse_tpostfix(parse);
@@ -77,5 +102,8 @@
 }
 
-/** Parse postfix type expression. */
+/** Parse postfix type expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_texpr_t *parse_tpostfix(parse_t *parse)
 {
@@ -86,4 +114,7 @@
 
 	while (lcur_lc(parse) == lc_period || lcur_lc(parse) == lc_lsbr) {
+
+		if (parse_is_error(parse))
+			break;
 
 		switch (lcur_lc(parse)) {
@@ -96,5 +127,6 @@
 		default:
 			lunexpected_error(parse);
-			exit(1);
+			tmp = parse_recovery_texpr(parse);
+			break;
 		}
 
@@ -105,5 +137,9 @@
 }
 
-/** Parse access type expression. */
+/** Parse access type expression.
+ *
+ * @param parse		Parser object.
+ * @param a		Base expression.
+ */
 static stree_texpr_t *parse_pf_taccess(parse_t *parse, stree_texpr_t *a)
 {
@@ -125,5 +161,9 @@
 }
 
-/** Parse index type expression. */
+/** Parse index type expression.
+ *
+ * @param parse		Parser object.
+ * @param a		Base expression.
+ */
 static stree_texpr_t *parse_pf_tindex(parse_t *parse, stree_texpr_t *a)
 {
@@ -142,4 +182,6 @@
 	if (lcur_lc(parse) != lc_rsbr && lcur_lc(parse) != lc_comma) {
 		while (b_true) {
+			if (parse_is_error(parse))
+				break;
 			expr = parse_expr(parse);
 			tindex->n_args += 1;
@@ -154,4 +196,6 @@
 		tindex->n_args = 1;
 		while (lcur_lc(parse) == lc_comma) {
+			if (parse_is_error(parse))
+				break;
 			lskip(parse);
 			tindex->n_args += 1;
@@ -167,5 +211,8 @@
 }
 
-/** Parse primitive type expression. */
+/** Parse primitive type expression.
+ *
+ * @param parse		Parser object.
+ */
 static stree_texpr_t *parse_tprimitive(parse_t *parse)
 {
@@ -185,5 +232,6 @@
 	default:
 		lunexpected_error(parse);
-		exit(1);
+		texpr = parse_recovery_texpr(parse);
+		break;
 	}
 
@@ -191,5 +239,8 @@
 }
 
-/** Parse type literal. */
+/** Parse type literal.
+ *
+ * @param parse		Parser object.
+ */
 static stree_tliteral_t *parse_tliteral(parse_t *parse)
 {
@@ -217,5 +268,8 @@
 }
 
-/** Parse type identifier. */
+/** Parse type identifier.
+ *
+ * @param parse		Parser object.
+ */
 static stree_tnameref_t *parse_tnameref(parse_t *parse)
 {
@@ -227,2 +281,21 @@
 	return tnameref;
 }
+
+/** Construct a special type expression fore recovery.
+ *
+ * @param parse		Parser object.
+ */
+static stree_texpr_t *parse_recovery_texpr(parse_t *parse)
+{
+	stree_tliteral_t *tliteral;
+	stree_texpr_t *texpr;
+
+	(void) parse;
+
+	tliteral = stree_tliteral_new(tlc_int);
+
+	texpr = stree_texpr_new(tc_tliteral);
+	texpr->u.tliteral = tliteral;
+
+	return texpr;
+}
Index: uspace/app/sbi/src/parse.c
===================================================================
--- uspace/app/sbi/src/parse.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/parse.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -84,4 +84,8 @@
 	parse->cur_mod = parse->program->module;
 	parse->lex = lex;
+
+	parse->error = b_false;
+	parse->error_bailout = b_false;
+
 	lex_next(parse->lex);
 }
@@ -93,5 +97,5 @@
 	stree_modm_t *modm;
 
-	while (lcur_lc(parse) != lc_eof) {
+	while (lcur_lc(parse) != lc_eof && !parse_is_error(parse)) {
 		switch (lcur_lc(parse)) {
 		case lc_class:
@@ -155,5 +159,5 @@
 
 	/* Parse class, struct or interface members. */
-	while (lcur_lc(parse) != lc_end) {
+	while (lcur_lc(parse) != lc_end && !parse_is_error(parse)) {
 		csimbr = parse_csimbr(parse, csi);
 		list_append(&csi->members, csimbr);
@@ -235,5 +239,5 @@
 
 		/* Parse formal parameters. */
-		while (b_true) {
+		while (!parse_is_error(parse)) {
 			arg = parse_proc_arg(parse);
 
@@ -264,5 +268,5 @@
 
 	/* Parse attributes. */
-	while (lcur_lc(parse) == lc_comma) {
+	while (lcur_lc(parse) == lc_comma && !parse_is_error(parse)) {
 		lskip(parse);
 		attr = parse_symbol_attr(parse);
@@ -281,5 +285,5 @@
 			symbol_print_fqn(symbol);
 			printf("' has no body.\n");
-			exit(1);
+			parse_note_error(parse);
 		}
 		fun->proc->body = NULL;
@@ -345,5 +349,5 @@
 
 		/* Parse formal parameters. */
-		while (b_true) {
+		while (!parse_is_error(parse)) {
 			arg = parse_proc_arg(parse);
 			if (stree_arg_has_attr(arg, aac_packed)) {
@@ -370,5 +374,5 @@
 	lmatch(parse, lc_is);
 
-	while (lcur_lc(parse) != lc_end) {
+	while (lcur_lc(parse) != lc_end && !parse_is_error(parse)) {
 		switch (lcur_lc(parse)) {
 		case lc_get:
@@ -377,5 +381,8 @@
 			if (prop->getter != NULL) {
 				printf("Error: Duplicate getter.\n");
-				exit(1);
+				(void) parse_block(parse); /* XXX Free */
+				lmatch(parse, lc_end);
+				parse_note_error(parse);
+				break;
 			}
 
@@ -395,5 +402,7 @@
 			if (prop->setter != NULL) {
 				printf("Error: Duplicate setter.\n");
-				exit(1);
+				(void) parse_block(parse); /* XXX Free */
+				lmatch(parse, lc_end);
+				parse_note_error(parse);
 			}
 
@@ -424,5 +433,5 @@
 		lem_print(lcur(parse));
 		printf("'.\n");
-		exit(1);
+		parse_note_error(parse);
 	}
 
@@ -447,5 +456,5 @@
 
 	/* Parse attributes. */
-	while (lcur_lc(parse) == lc_comma) {
+	while (lcur_lc(parse) == lc_comma && !parse_is_error(parse)) {
 		lskip(parse);
 		attr = parse_arg_attr(parse);
@@ -468,5 +477,5 @@
 		lem_print(lcur(parse));
 		printf("'.\n");
-		exit(1);
+		parse_note_error(parse);
 	}
 
@@ -486,5 +495,11 @@
 	list_init(&block->stats);
 
-	while (terminates_block(lcur_lc(parse)) != b_true) {
+	/* Avoid peeking if there is an error condition. */
+	if (parse_is_error(parse))
+		return block;
+
+	while (terminates_block(lcur_lc(parse)) != b_true &&
+	    !parse_is_error(parse)) {
+
 		stat = parse_stat(parse);
 		list_append(&block->stats, stat);
@@ -508,4 +523,7 @@
 	stree_exps_t *exp_s;
 
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse statement.\n");
+#endif
 	switch (lcur_lc(parse)) {
 	case lc_var:
@@ -590,4 +608,7 @@
 	stree_if_t *if_s;
 
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse 'if' statement.\n");
+#endif
 	if_s = stree_if_new();
 
@@ -613,4 +634,7 @@
 	stree_while_t *while_s;
 
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse 'while' statement.\n");
+#endif
 	while_s = stree_while_new();
 
@@ -629,4 +653,7 @@
 	stree_for_t *for_s;
 
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse 'for' statement.\n");
+#endif
 	for_s = stree_for_new();
 
@@ -649,4 +676,7 @@
 	stree_raise_t *raise_s;
 
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse 'raise' statement.\n");
+#endif
 	raise_s = stree_raise_new();
 	lmatch(parse, lc_raise);
@@ -662,4 +692,7 @@
 	stree_return_t *return_s;
 
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse 'return' statement.\n");
+#endif
 	return_s = stree_return_new();
 
@@ -677,4 +710,7 @@
 	stree_except_t *except_c;
 
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse WEF statement.\n");
+#endif
 	wef_s = stree_wef_new();
 	list_init(&wef_s->except_clauses);
@@ -692,5 +728,5 @@
 	wef_s->with_block = parse_block(parse);
 
-	while (lcur_lc(parse) == lc_except) {
+	while (lcur_lc(parse) == lc_except && !parse_is_error(parse)) {
 		except_c = parse_except(parse);
 		list_append(&wef_s->except_clauses, except_c);
@@ -716,4 +752,7 @@
 	stree_exps_t *exps;
 
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse expression statement.\n");
+#endif
 	expr = parse_expr(parse);
 	lmatch(parse, lc_scolon);
@@ -730,4 +769,7 @@
 	stree_except_t *except_c;
 
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse 'except' statement.\n");
+#endif
 	except_c = stree_except_new();
 
@@ -748,4 +790,7 @@
 	stree_ident_t *ident;
 
+#ifdef DEBUG_PARSE_TRACE
+	printf("Parse identifier.\n");
+#endif
 	lcheck(parse, lc_ident);
 	ident = stree_ident_new();
@@ -756,7 +801,41 @@
 }
 
+/** Signal a parse error, start bailing out from parser. */
+void parse_raise_error(parse_t *parse)
+{
+	parse->error = b_true;
+	parse->error_bailout = b_true;
+}
+
+/** Note a parse error that has been immediately recovered. */
+void parse_note_error(parse_t *parse)
+{
+	parse->error = b_true;
+}
+
+/** Check if we are currently bailing out of parser due to a parse error. */
+bool_t parse_is_error(parse_t *parse)
+{
+	return parse->error_bailout;
+}
+
+/** Recover from parse error bailout.
+ *
+ * Still remember that there was an error, but stop bailing out.
+ */
+void parse_recover_error(parse_t *parse)
+{
+	assert(parse->error == b_true);
+	assert(parse->error_bailout == b_true);
+
+	parse->error_bailout = b_false;
+}
+
 /** Return current lem. */
 lem_t *lcur(parse_t *parse)
 {
+#ifdef DEBUG_LPARSE_TRACE
+	printf("lcur()\n");
+#endif
 	return lex_get_current(parse->lex);
 }
@@ -767,4 +846,15 @@
 	lem_t *lem;
 
+	/*
+	 * This allows us to skip error checking in many places. If there is an
+	 * active error, lcur_lc() returns lc_invalid without reading input.
+	 *
+	 * Without this measure we would have to check for error all the time
+	 * or risk requiring extra input from the user (in interactive mode)
+	 * before actually bailing out from the parser.
+	 */
+	if (parse_is_error(parse))
+		return lc_invalid;
+
 	lem = lcur(parse);
 	return lem->lclass;
@@ -774,4 +864,7 @@
 void lskip(parse_t *parse)
 {
+#ifdef DEBUG_LPARSE_TRACE
+	printf("lskip()\n");
+#endif
 	lex_next(parse->lex);
 }
@@ -780,4 +873,9 @@
 void lcheck(parse_t *parse, lclass_t lc)
 {
+#ifdef DEBUG_LPARSE_TRACE
+	printf("lcheck(");
+	lclass_print(lc);
+	printf(")\n");
+#endif
 	if (lcur(parse)->lclass != lc) {
 		lem_print_coords(lcur(parse));
@@ -785,5 +883,5 @@
 		printf("', got '"); lem_print(lcur(parse));
 		printf("'.\n");
-		exit(1);
+		parse_raise_error(parse);
 	}
 }
@@ -792,4 +890,20 @@
 void lmatch(parse_t *parse, lclass_t lc)
 {
+#ifdef DEBUG_LPARSE_TRACE
+	printf("lmatch(");
+	lclass_print(lc);
+	printf(")\n");
+#endif
+	/*
+	 * This allows us to skip error checking in many places. If there is an
+	 * active error, lmatch() does nothing (similar to parse_block(), etc.
+	 *
+	 * Without this measure we would have to check for error all the time
+	 * or risk requiring extra input from the user (in interactive mode)
+	 * before actually bailing out from the parser.
+	 */
+	if (parse_is_error(parse))
+		return;
+
 	lcheck(parse, lc);
 	lskip(parse);
@@ -803,5 +917,5 @@
 	lem_print(lcur(parse));
 	printf("'.\n");
-	exit(1);
+	parse_raise_error(parse);
 }
 
Index: uspace/app/sbi/src/parse.h
===================================================================
--- uspace/app/sbi/src/parse.h	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/parse.h	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -38,4 +38,9 @@
 stree_ident_t *parse_ident(parse_t *parse);
 
+void parse_raise_error(parse_t *parse);
+void parse_note_error(parse_t *parse);
+bool_t parse_is_error(parse_t *parse);
+void parse_recover_error(parse_t *parse);
+
 /*
  * Parsing primitives
Index: uspace/app/sbi/src/parse_t.h
===================================================================
--- uspace/app/sbi/src/parse_t.h	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/parse_t.h	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -40,4 +40,10 @@
 	/** Module currently being parsed */
 	struct stree_module *cur_mod;
+
+	/** @c b_true if an error occured. */
+	bool_t error;
+
+	/** @c b_true if bailing out due to an error. */
+	bool_t error_bailout;
 } parse_t;
 
Index: uspace/app/sbi/src/rdata.c
===================================================================
--- uspace/app/sbi/src/rdata.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/rdata.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -48,5 +48,4 @@
 
 static void rdata_address_print(rdata_address_t *address);
-static void rdata_value_print(rdata_value_t *value);
 static void rdata_var_print(rdata_var_t *var);
 
@@ -450,5 +449,5 @@
 }
 
-static void rdata_value_print(rdata_value_t *value)
+void rdata_value_print(rdata_value_t *value)
 {
 	rdata_var_print(value->var);
Index: uspace/app/sbi/src/rdata.h
===================================================================
--- uspace/app/sbi/src/rdata.h	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/rdata.h	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -56,4 +56,5 @@
 
 void rdata_item_print(rdata_item_t *item);
+void rdata_value_print(rdata_value_t *value);
 
 #endif
Index: uspace/app/sbi/src/run.c
===================================================================
--- uspace/app/sbi/src/run.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/run.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -48,5 +48,5 @@
 
 static void run_block(run_t *run, stree_block_t *block);
-static void run_exps(run_t *run, stree_exps_t *exps);
+static void run_exps(run_t *run, stree_exps_t *exps, rdata_item_t **res);
 static void run_vdecl(run_t *run, stree_vdecl_t *vdecl);
 static void run_if(run_t *run, stree_if_t *if_s);
@@ -196,5 +196,5 @@
 	while (node != NULL) {
 		stat = list_node_data(node, stree_stat_t *);
-		run_stat(run, stat);
+		run_stat(run, stat, NULL);
 
 		if (run->thread_ar->bo_mode != bm_none)
@@ -214,14 +214,26 @@
 }
 
-/** Run statement. */
-void run_stat(run_t *run, stree_stat_t *stat)
+/** Run statement.
+ *
+ * Executes a statement. If @a res is not NULL and the statement is an
+ * expression statement with a value, the value item will be stored to
+ * @a res.
+ *
+ * @param run	Runner object.
+ * @param stat	Statement to run.
+ * @param res	Place to store exps result or NULL if not interested.
+ */
+void run_stat(run_t *run, stree_stat_t *stat, rdata_item_t **res)
 {
 #ifdef DEBUG_RUN_TRACE
 	printf("Executing one statement %p.\n", stat);
 #endif
+
+	if (res != NULL)
+		*res = NULL;
 
 	switch (stat->sc) {
 	case st_exps:
-		run_exps(run, stat->u.exp_s);
+		run_exps(run, stat->u.exp_s, res);
 		break;
 	case st_vdecl:
@@ -251,6 +263,14 @@
 }
 
-/** Run expression statement. */
-static void run_exps(run_t *run, stree_exps_t *exps)
+/** Run expression statement.
+ *
+ * Executes an expression statement. If @a res is not NULL then the value
+ * of the expression (or NULL if it has no value) will be stored to @a res.
+ *
+ * @param run	Runner object.
+ * @param exps	Expression statement to run.
+ * @param res	Place to store exps result or NULL if not interested.
+ */
+static void run_exps(run_t *run, stree_exps_t *exps, rdata_item_t **res)
 {
 	rdata_item_t *rexpr;
@@ -261,7 +281,6 @@
 	run_expr(run, exps->expr, &rexpr);
 
-	if (rexpr != NULL) {
-		printf("Warning: Expression value ignored.\n");
-	}
+	if (res != NULL)
+		*res = rexpr;
 }
 
@@ -504,4 +523,21 @@
 	return tdata_is_csi_derived_from_ti(payload_o->class_sym->u.csi,
 	    etype);
+}
+
+/** Raise an irrecoverable run-time error, start bailing out.
+ *
+ * Raises an error that cannot be handled by the user program.
+ */
+void run_raise_error(run_t *run)
+{
+	run->thread_ar->bo_mode = bm_error;
+	run->thread_ar->error = b_true;
+}
+
+/** Construct a special recovery item. */
+rdata_item_t *run_recovery_item(run_t *run)
+{
+	(void) run;
+	return NULL;
 }
 
@@ -1136,5 +1172,7 @@
 	if (addr_var->vref == NULL) {
 		printf("Error: Accessing null reference.\n");
-		exit(1);
+		run_raise_error(run);
+		*ritem = run_recovery_item(run);
+		return;
 	}
 
@@ -1144,5 +1182,4 @@
 	*ritem = item;
 }
-
 
 run_thread_ar_t *run_thread_ar_new(void)
Index: uspace/app/sbi/src/run.h
===================================================================
--- uspace/app/sbi/src/run.h	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/run.h	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -35,7 +35,10 @@
 void run_program(run_t *run, stree_program_t *prog);
 void run_proc(run_t *run, run_proc_ar_t *proc_ar, rdata_item_t **res);
-void run_stat(run_t *run, stree_stat_t *stat);
+void run_stat(run_t *run, stree_stat_t *stat, rdata_item_t **res);
 
 void run_print_fun_bt(run_t *run);
+
+void run_raise_error(run_t *run);
+rdata_item_t *run_recovery_item(run_t *run);
 
 rdata_var_t *run_local_vars_lookup(run_t *run, sid_t name);
Index: uspace/app/sbi/src/run_expr.c
===================================================================
--- uspace/app/sbi/src/run_expr.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/run_expr.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -210,4 +210,7 @@
 	sym = symbol_lookup_in_csi(run->program, csi, nameref->name);
 
+	/* Existence should have been verified in type checking phase. */
+	assert(sym != NULL);
+
 	switch (sym->sc) {
 	case sc_csi:
@@ -857,4 +860,9 @@
 	run_dereference(run, arg, &darg);
 
+	if (run->thread_ar->bo_mode != bm_none) {
+		*res = run_recovery_item(run);
+		return;
+	}
+
 	/* Try again. */
 	run_access_item(run, access, darg, res);
@@ -1021,8 +1029,12 @@
 	run_expr(run, call->fun, &rfun);
 
+	if (run->thread_ar->bo_mode != bm_none) {
+		*res = run_recovery_item(run);
+		return;
+	}
+
 	if (rfun->ic != ic_value || rfun->u.value->var->vc != vc_deleg) {
 		printf("Unimplemented: Call expression of this type.\n");
-		*res = NULL;
-		return;
+		exit(1);
 	}
 
@@ -1182,5 +1194,7 @@
 			printf("Error: Array index (value: %d) is out of range.\n",
 			    arg_val);
-			exit(1);
+			run_raise_error(run);
+			*res = run_recovery_item(run);
+			return;
 		}
 
Index: uspace/app/sbi/src/run_t.h
===================================================================
--- uspace/app/sbi/src/run_t.h	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/run_t.h	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -80,5 +80,8 @@
 
 	/** Exception */
-	bm_exc
+	bm_exc,
+
+	/** Unrecoverable runtime error */
+	bm_error
 } run_bailout_mode_t;
 
@@ -96,4 +99,7 @@
 	/** Exception payload */
 	struct rdata_value *exc_payload;
+
+	/** @c b_true if a run-time error occured. */
+	bool_t error;
 } run_thread_ar_t;
 
Index: uspace/app/sbi/src/run_texpr.c
===================================================================
--- uspace/app/sbi/src/run_texpr.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/run_texpr.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -94,4 +94,8 @@
 
 	sym = symbol_lookup_in_csi(prog, base_csi, taccess->member_name);
+
+	/* Existence should have been verified in type checking phase. */
+	assert(sym != NULL);
+
 	if (sym->sc != sc_csi) {
 		printf("Error: Symbol '");
@@ -188,4 +192,7 @@
 	sym = symbol_lookup_in_csi(prog, ctx, tnameref->name);
 
+	/* Existence should have been verified in type-checking phase. */
+	assert(sym);
+
 	if (sym->sc != sc_csi) {
 		printf("Error: Symbol '");
Index: uspace/app/sbi/src/stype.c
===================================================================
--- uspace/app/sbi/src/stype.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/stype.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -61,5 +61,5 @@
 static void stype_raise(stype_t *stype, stree_raise_t *raise_s);
 static void stype_return(stype_t *stype, stree_return_t *return_s);
-static void stype_exps(stype_t *stype, stree_exps_t *exp_s);
+static void stype_exps(stype_t *stype, stree_exps_t *exp_s, bool_t want_value);
 static void stype_wef(stype_t *stype, stree_wef_t *wef_s);
 
@@ -164,5 +164,5 @@
 		if (titem->tic != tic_tarray) {
 			printf("Error: Packed argument is not an array.\n");
-			exit(1);
+			stype_note_error(stype);
 		}
 	}
@@ -240,5 +240,5 @@
 	while (stat_n != NULL) {
 		stat = list_node_data(stat_n, stree_stat_t *);
-		stype_stat(stype, stat);
+		stype_stat(stype, stat, b_false);
 
 		stat_n = list_next(&block->stats, stat_n);
@@ -251,6 +251,15 @@
 }
 
-/** Type statement */
-void stype_stat(stype_t *stype, stree_stat_t *stat)
+/** Type statement
+ *
+ * Types a statement. If @a want_value is @c b_true, then warning about
+ * ignored expression value will be supressed for this statement (but not
+ * for nested statemens). This is used in interactive mode.
+ *
+ * @param stype		Static typer object.
+ * @param stat		Statement to type.
+ * @param want_value	@c b_true to allow ignoring expression value.
+ */
+void stype_stat(stype_t *stype, stree_stat_t *stat, bool_t want_value)
 {
 #ifdef DEBUG_TYPE_TRACE
@@ -264,5 +273,5 @@
 	case st_raise: stype_raise(stype, stat->u.raise_s); break;
 	case st_return: stype_return(stype, stat->u.return_s); break;
-	case st_exps: stype_exps(stype, stat->u.exp_s); break;
+	case st_exps: stype_exps(stype, stat->u.exp_s, want_value); break;
 	case st_wef: stype_wef(stype, stat->u.wef_s); break;
 	}
@@ -285,5 +294,5 @@
 		printf("Error: Duplicate variable declaration '%s'.\n",
 		    strtab_get_str(vdecl_s->name->sid));
-		exit(1);
+		stype_note_error(stype);
 	}
 
@@ -387,5 +396,5 @@
 			printf("Error: Return statement in "
 			    "setter.\n");
-			exit(1);
+			stype_note_error(stype);
 		}
 
@@ -406,5 +415,5 @@
 
 /** Type expression statement */
-static void stype_exps(stype_t *stype, stree_exps_t *exp_s)
+static void stype_exps(stype_t *stype, stree_exps_t *exp_s, bool_t want_value)
 {
 #ifdef DEBUG_TYPE_TRACE
@@ -413,5 +422,5 @@
 	stype_expr(stype, exp_s->expr);
 
-	if (exp_s->expr->titem != NULL)
+	if (want_value == b_false && exp_s->expr->titem != NULL)
 		printf("Warning: Expression value ignored.\n");
 }
@@ -468,10 +477,12 @@
 	if (dest == NULL) {
 		printf("Error: Conversion destination is not valid.\n");
-		exit(1);
+		stype_note_error(stype);
+		return expr;
 	}
 
 	if (src == NULL) {
 		printf("Error: Conversion source is not valid.\n");
-		exit(1);
+		stype_note_error(stype);
+		return expr;
 	}
 
@@ -519,5 +530,5 @@
 		tdata_item_print(dest);
 		printf("'.\n");
-		exit(1);
+		stype_note_error(stype);
 	}
 
@@ -531,6 +542,6 @@
 	printf(".\n");
 
-	/* XXX We should rather return a bogus expression of type @a dest */
-	exit(1);
+	stype_note_error(stype);
+	return expr;
 }
 
@@ -659,4 +670,26 @@
 }
 
+/** Note a static typing error that has been immediately recovered. */
+void stype_note_error(stype_t *stype)
+{
+	stype->error = b_true;
+}
+
+/** Construct a special type item for recovery. */
+tdata_item_t *stype_recovery_titem(stype_t *stype)
+{
+	tdata_item_t *titem;
+	tdata_primitive_t *tprimitive;
+
+	(void) stype;
+
+	titem = tdata_item_new(tic_tprimitive);
+	tprimitive = tdata_primitive_new(tpc_int);
+
+	titem->u.tprimitive = tprimitive;
+
+	return titem;
+}
+
 /** Get current block visit record. */
 stype_block_vr_t *stype_get_current_block_vr(stype_t *stype)
Index: uspace/app/sbi/src/stype.h
===================================================================
--- uspace/app/sbi/src/stype.h	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/stype.h	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -33,5 +33,8 @@
 
 void stype_module(stype_t *stype, stree_module_t *module);
-void stype_stat(stype_t *stype, stree_stat_t *stat);
+void stype_stat(stype_t *stype, stree_stat_t *stat, bool_t want_value);
+
+void stype_note_error(stype_t *stype);
+tdata_item_t *stype_recovery_titem(stype_t *stype);
 
 stree_expr_t *stype_convert(stype_t *stype, stree_expr_t *expr,
Index: uspace/app/sbi/src/stype_expr.c
===================================================================
--- uspace/app/sbi/src/stype_expr.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/stype_expr.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -182,4 +182,20 @@
 	    nameref->name);
 
+	if (sym == NULL) {
+		/* Not found. */
+		if (stype->current_csi != NULL) {
+			printf("Error: Symbol '%s' not found in '",
+			    strtab_get_str(nameref->name->sid));
+			symbol_print_fqn(csi_to_symbol(stype->current_csi));
+			printf("'.\n");
+		} else {
+			printf("Error: Symbol '%s' not found.\n",
+			    strtab_get_str(nameref->name->sid));
+		}
+		stype_note_error(stype);
+		*rtitem = stype_recovery_titem(stype);
+		return;
+	}
+
 	switch (sym->sc) {
 	case sc_var:
@@ -273,13 +289,18 @@
 
 	if (binop->arg1->titem == NULL) {
-		/* XXX Make this an error when ready. */
 		printf("Error First binary operand has no value.\n");
-		exit(1);
+		stype_note_error(stype);
+		if (binop->arg2->titem != NULL)
+			*rtitem = binop->arg2->titem;
+		else
+			*rtitem = stype_recovery_titem(stype);
+		return;
 	}
 
 	if (binop->arg2->titem == NULL) {
-		/* XXX Make this an error when ready. */
 		printf("Error: Second binary operand has no value.\n");
-		exit(1);
+		stype_note_error(stype);
+		*rtitem = binop->arg1->titem;
+		return;
 	}
 
@@ -292,5 +313,7 @@
 		tdata_item_print(binop->arg2->titem);
 		printf("').\n");
-		exit(1);
+		stype_note_error(stype);
+		*rtitem = binop->arg1->titem;
+		return;
 	}
 
@@ -311,5 +334,7 @@
 		tdata_item_print(titem);
 		printf("').\n");
-		exit(1);
+		stype_note_error(stype);
+		*rtitem = titem;
+		break;
 	}
 
@@ -334,10 +359,12 @@
 	case tpc_nil:
 		printf("Unimplemented; Binary operation on nil.\n");
-		exit(1);
+		stype_note_error(stype);
+		rtpc = tpc_nil;
+		break;
 	case tpc_string:
 		if (binop->bc != bo_plus) {
 			printf("Unimplemented: Binary operation(%d) "
 			    "on strings.\n", binop->bc);
-			exit(1);
+			stype_note_error(stype);
 		}
 		rtpc = tpc_string;
@@ -345,5 +372,6 @@
 	case tpc_resource:
 		printf("Error: Cannot apply operator to resource type.\n");
-		exit(1);
+		stype_note_error(stype);
+		rtpc = tpc_resource;
 	}
 
@@ -425,5 +453,7 @@
 	if (arg_ti == NULL) {
 		printf("Error: Argument of access has no value.\n");
-		exit(1);
+		stype_note_error(stype);
+		*rtitem = stype_recovery_titem(stype);
+		return;
 	}
 
@@ -443,5 +473,6 @@
 	case tic_tfun:
 		printf("Error: Using '.' operator on a function.\n");
-		exit(1);
+		stype_note_error(stype);
+		*rtitem = stype_recovery_titem(stype);
 		break;
 	}
@@ -459,5 +490,6 @@
 	tdata_item_print(arg_ti);
 	printf("'.\n");
-	exit(1);
+	stype_note_error(stype);
+	*rtitem = stype_recovery_titem(stype);
 }
 
@@ -488,5 +520,6 @@
 		printf("' has no member named '%s'.\n",
 		    strtab_get_str(access->member_name->sid));
-		exit(1);
+		*rtitem = stype_recovery_titem(stype);
+		return;
 	}
 
@@ -500,5 +533,7 @@
 		printf("Error: Accessing object member which is nested "
 		    "CSI.\n");
-		exit(1);
+		stype_note_error(stype);
+		*rtitem = stype_recovery_titem(stype);
+		break;
 	case sc_fun:
 		fun = symbol_to_fun(member_sym);
@@ -536,5 +571,6 @@
 	tdata_item_print(arg_ti);
 	printf("'.\n");
-	exit(1);
+	stype_note_error(stype);
+	*rtitem = stype_recovery_titem(stype);
 }
 
@@ -550,5 +586,6 @@
 	tdata_item_print(arg_ti);
 	printf("'.\n");
-	exit(1);
+	stype_note_error(stype);
+	*rtitem = stype_recovery_titem(stype);
 }
 
@@ -639,5 +676,5 @@
 		symbol_print_fqn(fun_to_symbol(fun));
 		printf("'.\n");
-		exit(1);
+		stype_note_error(stype);
 	}
 
@@ -646,5 +683,5 @@
 		symbol_print_fqn(fun_to_symbol(fun));
 		printf("'.\n");
-		exit(1);
+		stype_note_error(stype);
 	}
 
@@ -696,5 +733,6 @@
 	case tic_tfun:
 		printf("Error: Indexing a function.\n");
-		exit(1);
+		stype_note_error(stype);
+		*rtitem = stype_recovery_titem(stype);
 		break;
 	}
@@ -724,5 +762,6 @@
 	tdata_item_print(base_ti);
 	printf("'.\n");
-	exit(1);
+	stype_note_error(stype);
+	*rtitem = stype_recovery_titem(stype);
 }
 
@@ -756,5 +795,7 @@
 		tdata_item_print(base_ti);
 		printf("' which does not have an indexer.\n");
-		exit(1);
+		stype_note_error(stype);
+		*rtitem = stype_recovery_titem(stype);
+		return;
 	}
 
@@ -791,5 +832,5 @@
 
 			printf("Error: Array index is not an integer.\n");
-			exit(1);
+			stype_note_error(stype);
 		}
 
@@ -800,5 +841,5 @@
 		printf("Error: Using %d indices with array of rank %d.\n",
 		    arg_count, base_ti->u.tarray->rank);
-		exit(1);
+		stype_note_error(stype);
 	}
 
@@ -817,5 +858,6 @@
 	tdata_item_print(base_ti);
 	printf("'.\n");
-	exit(1);
+	stype_note_error(stype);
+	*rtitem = stype_recovery_titem(stype);
 }
 
@@ -857,5 +899,5 @@
 		tdata_item_print(as_op->arg->titem);
 		printf("'.\n");
-		exit(1);
+		stype_note_error(stype);
 	}
 
Index: uspace/app/sbi/src/stype_t.h
===================================================================
--- uspace/app/sbi/src/stype_t.h	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/stype_t.h	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -70,4 +70,7 @@
 	/** Procedure VR for the current procedure. */
 	stype_proc_vr_t *proc_vr;
+
+	/** @c b_true if an error occured. */
+	bool_t error;
 } stype_t;
 
Index: uspace/app/sbi/src/symbol.c
===================================================================
--- uspace/app/sbi/src/symbol.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/symbol.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -75,5 +75,12 @@
 }
 
-/** Lookup symbol reference in CSI. */
+/** Lookup symbol reference in CSI.
+ *
+ * @param prog	Program to look in.
+ * @param scope CSI in @a prog which is the base for references.
+ * @param name	Identifier of the symbol.
+ *
+ * @return	Symbol or @c NULL if symbol not found.
+ */
 stree_symbol_t *symbol_lookup_in_csi(stree_program_t *prog, stree_csi_t *scope,
 	stree_ident_t *name)
@@ -92,9 +99,4 @@
 	if (symbol == NULL)
 		symbol = symbol_search_global(prog, name);
-
-	if (symbol == NULL) {
-		printf("Error: Symbol '%s' not found.\n", strtab_get_str(name->sid));
-		exit(1);
-	}
 
 	return symbol;
Index: uspace/app/sbi/src/tdata.c
===================================================================
--- uspace/app/sbi/src/tdata.c	(revision 5da468ef646d75eceb4aa88033bece23060612fb)
+++ uspace/app/sbi/src/tdata.c	(revision 1ebc1a62b0e759c001488fc0e221be6347082167)
@@ -177,5 +177,5 @@
 
 	printf("[");
-	for (i = 0; i < tarray->rank; ++i)
+	for (i = 0; i < tarray->rank - 1; ++i)
 		printf(",");
 	printf("]");
