Bladeren bron

Add nice format for runtime errors (#12)

* Enhance runtime errors with file and line

* update test files
pull/14/head
mtardy 4 jaren geleden
committed by GitHub
bovenliggende
commit
b51fe9f606
Geen bekende sleutel gevonden voor deze handtekening in de database GPG sleutel-ID: 4AEE18F83AFDEB23
5 gewijzigde bestanden met toevoegingen van 139 en 93 verwijderingen
  1. +43
    -7
      object.c
  2. +86
    -77
      parse.leg
  3. +4
    -1
      test-proto.txt
  4. +6
    -0
      test-runtime-error.txt
  5. +0
    -8
      test3.txt

+ 43
- 7
object.c Bestand weergeven

@ -414,10 +414,10 @@ oop map_append(oop map, oop value)
bool isHidden(oop obj) {
if (is(Symbol, obj)) {
char *s = get(obj, Symbol, name);
size_t l = strlen(s);
// maybe 'l > 5' because of ____?
return (l > 4 && s[0] == '_' && s[1] == '_' && s[l-2] == '_' && s[l-1] == '_');
char *s = get(obj, Symbol, name);
size_t l = strlen(s);
// maybe 'l > 5' because of ____?
return (l > 4 && s[0] == '_' && s[1] == '_' && s[l-2] == '_' && s[l-1] == '_');
}
return false;
}
@ -439,7 +439,7 @@ oop map_allKeys(oop map)
assert(is(Map, map));
oop keys = makeMap();
for (size_t i = 0; i < get(map, Map, size); i++) {
map_append(keys, get(map, Map, elements)[i].key);
map_append(keys, get(map, Map, elements)[i].key);
}
return keys;
}
@ -486,6 +486,42 @@ void map_print(oop map, int ident)
return;
}
char *toString(oop ast)
{
int length;
assert(ast);
switch (getType(ast)) {
case Undefined:
return "null";
case Integer:
//TODO
length = snprintf(NULL, 0, "%d", getInteger(ast));
printf("length is %i\n", length);
printf("%i", getInteger(ast));
return "null";
case String:
return get(ast, String, value);
case Symbol:
return get(ast, Symbol, name);
case Function:
// TODO
if (get(ast, Function, primitive) == NULL) {
printf("Function:");
} else {
printf("Primitive:");
}
print(get(ast, Function, name));
printf("(");
print(get(ast, Function, param));
printf(")");
case Map:
// TODO
map_print(ast, 0);
}
assert(0);
}
void print(oop ast)
{
assert(ast);
@ -509,9 +545,9 @@ void print(oop ast)
printf("Primitive:");
}
print(get(ast, Function, name));
printf("(");
printf("(");
print(get(ast, Function, param));
printf(")");
printf(")");
return;
case Map:
map_print(ast, 0);

+ 86
- 77
parse.leg Bestand weergeven

@ -73,7 +73,7 @@ oop globals= 0;
DO_PROTOS() _DO(__proto__) _DO(__name__) _DO(__default__) _DO(__arguments__) \
_DO(name) _DO(body) _DO(param) _DO(key) _DO(value) _DO(condition) _DO(consequent) _DO(alternate) \
_DO(lhs) _DO(rhs) _DO(scope) _DO(args) _DO(expression) _DO(labels) _DO(statements) _DO(initialise) \
_DO(update) _DO(this) _DO(fixed) _DO(operator) _DO(map) _DO(func)
_DO(update) _DO(this) _DO(fixed) _DO(operator) _DO(map) _DO(func) _DO(__line__) _DO(__file__)
#define _DO(NAME) oop NAME##_symbol;
DO_SYMBOLS()
@ -85,6 +85,43 @@ DO_PROTOS()
int opt_v = 0;
typedef struct input_t
{
oop name;
FILE *file;
struct input_t *next;
int lineNumber;
} input_t;
input_t *inputStack= NULL;
void inputStackPush(char *name) {
FILE *file = stdin;
if (NULL != name) {
file= fopen(name, "rb");
if (NULL == file) {
perror(name);
exit(1);
}
} else {
name= "<stdin>";
}
input_t *input = memcheck(malloc(sizeof(input_t)));
input->name= makeString(name);
input->lineNumber= 1;
input->file= file;
input->next= inputStack;
inputStack= input;
return;
}
input_t *inputStackPop(void) {
assert(inputStack);
input_t *first= inputStack;
inputStack= first->next;
return first;
}
int isFalse(oop obj)
{
return obj == null || (isInteger(obj) && (0 == getInteger(obj)));
@ -99,6 +136,9 @@ oop newObject(oop proto)
{
oop map = makeMap();
map_set(map, __proto___symbol, proto);
// set context (file and line) for runtime error msg
map_set(map, __line___symbol, makeInteger(inputStack->lineNumber));
map_set(map, __file___symbol, inputStack->name);
return map;
}
@ -506,43 +546,6 @@ oop newContinue(void)
oop fold(oop ast);
typedef struct input_t
{
char *name;
FILE *file;
struct input_t *next;
int lineNumber;
} input_t;
input_t *inputStack= NULL;
void inputStackPush(char *name) {
FILE *file = stdin;
if (NULL != name) {
file= fopen(name, "rb");
if (NULL == file) {
perror(name);
exit(1);
}
} else {
name= "<stdin>";
}
input_t *input = memcheck(malloc(sizeof(input_t)));
input->name= memcheck(strdup(name));
input->lineNumber= 1;
input->file= file;
input->next= inputStack;
inputStack= input;
return;
}
input_t *inputStackPop(void) {
assert(inputStack);
input_t *first= inputStack;
inputStack= first->next;
return first;
}
#define YY_INPUT(buf, result, max_size) \
{ \
int yyc= getc(inputStack->file); \
@ -555,9 +558,9 @@ YYSTYPE yylval;
int errorLine= 1;
void error(char *text)
void syntaxError(char *text)
{
fprintf(stderr, "\nSyntax error in %s near line %i:\n%s\n", inputStack->name, errorLine, text);
fprintf(stderr, "\nSyntax error in %s near line %i:\n%s\n", get(inputStack->name, String, value), errorLine, text);
exit(1);
}
@ -576,7 +579,7 @@ start = - ( IMPORT s:STRING { yylval = null; in
)
error = { errorLine= inputStack->lineNumber }
eol* < (!eol .)* eol* (!eol .)* > { error(yytext) }
eol* < (!eol .)* eol* (!eol .)* > { syntaxError(yytext) }
stmt = e:exp SEMICOLON* { $$ = e }
| s:block { $$ = s }
@ -678,8 +681,8 @@ shift = l:sum
)* { $$ = l }
sum = l:prod
( PLUS r:prod { l = newBinary(Add_proto, l, r) }
| MINUS r:prod { l = newBinary(Sub_proto, l, r) }
( PLUS r:prod { l = newBinary(Add_proto, l, r) }
| MINUS r:prod { l = newBinary(Sub_proto, l, r) }
)* { $$ = l }
prod = l:prefix
@ -884,19 +887,26 @@ oop clone(oop obj)
return obj;
}
oop addOperation(oop lhs, oop rhs)
void runtimeError(oop ast, char *msg)
{
char *fileName= get(map_get(ast, __file___symbol), String, value);
fprintf(stderr, "\nRuntime error in %s near line %i:\nError: %s\n", fileName, getInteger(map_get(ast, __line___symbol)), msg);
exit(1);
}
oop addOperation(oop ast, oop lhs, oop rhs)
{
if (getType(lhs) == Integer && getType(rhs) == Integer) {
return makeInteger(getInteger(lhs) + getInteger(rhs));
} else if (getType(lhs) == String && getType(rhs) == String) {
return string_concat(lhs, rhs);
} else {
fprintf(stderr, "\naddition between two incompatible types\n");
exit(1);
runtimeError(ast, "addition between two incompatible types");
assert(0); // to prevent: control may reach end of non-void function
}
}
oop mulOperation(oop lhs, oop rhs)
oop mulOperation(oop ast, oop lhs, oop rhs)
{
if (getType(lhs) == Integer && getType(rhs) == Integer) {
return makeInteger(getInteger(lhs) * getInteger(rhs));
@ -905,8 +915,8 @@ oop mulOperation(oop lhs, oop rhs)
} else if (getType(lhs) == Integer && getType(rhs) == String) {
return string_mul(rhs, lhs);
} else {
fprintf(stderr, "\nmultiplication between two incompatible types\n");
exit(1);
runtimeError(ast, "multiplication between two incompatible types");
assert(0);
}
}
@ -997,13 +1007,13 @@ oop fold(oop ast)
return null;
}
oop applyOperator(oop op, oop lhs, oop rhs)
oop applyOperator(oop ast, oop op, oop lhs, oop rhs)
{
if (null != op) { assert(is(Symbol, op));
switch (get(op, Symbol, prototype)) {
case t_Add: return addOperation(lhs, rhs);
case t_Add: return addOperation(ast, lhs, rhs);
case t_Sub: return makeInteger(getInteger(lhs) - getInteger(rhs));
case t_Mul: return makeInteger(getInteger(lhs) * getInteger(rhs));
case t_Mul: return mulOperation(ast, lhs, rhs);
case t_Div: return makeInteger(getInteger(lhs) / getInteger(rhs));
case t_Mod: return makeInteger(getInteger(lhs) % getInteger(rhs));
case t_Bitor: return makeInteger(getInteger(lhs) | getInteger(rhs));
@ -1067,8 +1077,7 @@ oop eval(oop scope, oop ast)
return expandUnquotes(scope, obj);
}
case t_Unquote: {
fprintf(stderr, "\n@ outside of `\n");
exit(1);
runtimeError(ast, "@ outside of `");
}
case t_Declaration: {
oop lhs = map_get(ast, lhs_symbol);
@ -1226,7 +1235,7 @@ oop eval(oop scope, oop ast)
oop lhs = map_get(ast, lhs_symbol);
oop op = map_get(ast, operator_symbol);
oop rhs = eval(scope, map_get(ast, rhs_symbol));
if (null != op) rhs= applyOperator(op, getVariable(scope, lhs), rhs);
if (null != op) rhs= applyOperator(ast, op, getVariable(scope, lhs), rhs);
setVariable(scope, lhs, rhs);
if (is(Function, rhs) && null == get(rhs, Function, name)) {
set(rhs, Function, name, lhs);
@ -1251,6 +1260,8 @@ oop eval(oop scope, oop ast)
case t_Call: {
oop func = eval(scope, map_get(ast, func_symbol));
if (!is(Function, func)) {
// TODO better error printing
// Please search for ISSUE1 in parse.leg
printf("cannot call ");
println(func);
exit(1);
@ -1281,12 +1292,10 @@ oop eval(oop scope, oop ast)
return result;
}
case j_break: {
fprintf(stderr, "\nbreak outside of a loop\n");
exit(1);
runtimeError(ast, "break outside of a loop");
}
case j_continue: {
fprintf(stderr, "\ncontinue outside of a loop\n");
exit(1);
runtimeError(ast, "continue outside of a loop");
}
case j_throw: {
oop res= jbs->result;
@ -1338,12 +1347,10 @@ oop eval(oop scope, oop ast)
return result;
}
case j_break: {
fprintf(stderr, "\nbreak outside of a loop\n");
exit(1);
runtimeError(ast, "break outside of a loop");
}
case j_continue: {
fprintf(stderr, "\ncontinue outside of a loop\n");
exit(1);
runtimeError(ast, "continue outside of a loop");
}
case j_throw: {
oop res= jbs->result;
@ -1402,7 +1409,7 @@ oop eval(oop scope, oop ast)
oop key = map_get(ast, key_symbol);
oop op = map_get(ast, operator_symbol);
oop value = eval(scope, map_get(ast, value_symbol));
if (null != op) value= applyOperator(op, getMember(map, key), value);
if (null != op) value= applyOperator(ast, op, getMember(map, key), value);
if (is(Function, value) && null == get(value, Function, name)) {
set(value, Function, name, key);
}
@ -1414,16 +1421,13 @@ oop eval(oop scope, oop ast)
switch (getType(map)) {
case String:
if (getInteger(key) >= get(map, String, size)) {
fprintf(stderr, "\nGetIndex out of range on String\n");
exit(1);
runtimeError(ast, "GetIndex out of range on String");
}
return makeInteger(unescape(get(map, String, value))[getInteger(key)]);
case Map:
return map_get(map, key);
default:
// should it returns null instead?
fprintf(stderr, "\nGetIndex on non Map or String\n");
exit(1);
runtimeError(ast, "GetIndex on non Map or String");
}
}
case t_SetIndex: {
@ -1434,18 +1438,15 @@ oop eval(oop scope, oop ast)
switch (getType(map)) {
case String:
if (getInteger(key) >= get(map, String, size)) {
fprintf(stderr, "\nSetIndex out of range on String\n");
exit(1);
runtimeError(ast, "SetIndex out of range on String");
}
get(map, String, value)[getInteger(key)] = getInteger(value);
return value;
case Map:
if (null != op) value= applyOperator(op, map_get(map, key), value);
if (null != op) value= applyOperator(ast, op, map_get(map, key), value);
return map_set(map, key, value);
default:
// should it returns null instead?
fprintf(stderr, "\nSetIndex on non Map or String\n");
exit(1);
runtimeError(ast, "SetIndex on non Map or String");
}
}
@ -1489,14 +1490,14 @@ oop eval(oop scope, oop ast)
case t_Add: {
oop lhs = eval(scope, map_get(ast, lhs_symbol));
oop rhs = eval(scope, map_get(ast, rhs_symbol));
return addOperation(lhs, rhs);
return addOperation(ast, lhs, rhs);
}
BINARY(Sub, - );
// BINARY(Mul, * );
case t_Mul: {
oop lhs = eval(scope, map_get(ast, lhs_symbol));
oop rhs = eval(scope, map_get(ast, rhs_symbol));
return mulOperation(lhs, rhs);
return mulOperation(ast, lhs, rhs);
}
BINARY(Div, / );
BINARY(Mod, % );
@ -1648,6 +1649,14 @@ oop prim_length(oop params)
return null;
}
// TODO
// ISSUE1: how can I handle the "cannot invoke " with nice printing the func name and its args
// it would be really nice to have a toString() function (that would be directly used in print())
// but it seems that it's really complicated to write a toString() for integer, function and maps...
// please see the draft line 490 in object.c
// ISSUE2: how can I tackle the 2 fprintf(stderr, "\nbreak/continue oustide of a loop\n") in prim_invoke and prim_apply
// in this situation it seems that I don't have access to any AST
oop prim_invoke(oop params)
{
oop this= null; if (map_hasIntegerKey(params, 0)) this= get(params, Map, elements)[0].value;

+ 4
- 1
test-proto.txt Bestand weergeven

@ -1,4 +1,7 @@
fun println() { apply(print, __arguments__); print("\n"); }
fun println() {
apply(print, __arguments__);
print("\n");
}
var Object = { __name__: #"Object" };

+ 6
- 0
test-runtime-error.txt Bestand weergeven

@ -0,0 +1,6 @@
2;
12;
(3*null);
a = {}
a.piz = fun f() {continue;}
`invoke(a, (a.piz), {})

+ 0
- 8
test3.txt Bestand weergeven

@ -1,8 +0,0 @@
a = {}
a.b = fun () {
switch (1) {
case 1:
throw { name: "EvalError", message: "oops" }
}
}
invoke(a, a.b, {})

Laden…
Annuleren
Opslaan