return res;
}
+struct stmt *stmt_vardecl(struct vardecl *vardecl)
+{
+ struct stmt *res = safe_malloc(sizeof(struct stmt));
+ res->type = svardecl;
+ res->data.svardecl = vardecl;
+ return res;
+}
+
struct stmt *stmt_while(struct expr *pred, struct list *body)
{
struct stmt *res = safe_malloc(sizeof(struct stmt));
res->data.ebool = b;
return res;
}
-int fromHex(char c)
-{
- if (c >= '0' && c <= '9')
- return c-'0';
- if (c >= 'a' && c <= 'f')
- return c-'a'+10;
- if (c >= 'A' && c <= 'F')
- return c-'A'+10;
- return -1;
-}
-struct expr *expr_char(const char *c)
+struct expr *expr_char(char *c)
{
struct expr *res = safe_malloc(sizeof(struct expr));
res->type = echar;
- //regular char
- if (c[0] == '\'' && c[2] == '\'')
- res->data.echar = c[1];
- //escape
- else if (c[0] == '\'' && c[1] == '\\' && c[3] == '\'')
- switch(c[2]) {
- case '0': res->data.echar = '\0'; break;
- case '\'': res->data.echar = '\''; break;
- case '\\': res->data.echar = '\\'; break;
- case 'a': res->data.echar = '\a'; break;
- case 'b': res->data.echar = '\b'; break;
- case 't': res->data.echar = '\t'; break;
- case 'v': res->data.echar = '\v'; break;
- case 'f': res->data.echar = '\f'; break;
- case 'r': res->data.echar = '\r'; break;
- }
- //hex escape
- else if (c[0] == '\'' && c[1] == '\\' && c[2] == 'x' && c[5] == '\'')
- res->data.echar = (fromHex(c[3])<<4)+fromHex(c[4]);
- else
- die("malformed character: %s\n", c);
+ res->data.echar = unescape_char(c)[0];
return res;
}
-struct expr *expr_funcall(char *ident, struct list *args)
+static void set_fields(enum fieldspec **farray, int *n, struct list *fields)
+{
+ void **els = list_to_array(fields, n, true);
+ *farray = (enum fieldspec *)safe_malloc(*n*sizeof(enum fieldspec));
+ for (int i = 0; i<*n; i++) {
+ char *t = els[i];
+ if (strcmp(t, "fst") == 0)
+ (*farray)[i] = fst;
+ else if (strcmp(t, "snd") == 0)
+ (*farray)[i] = snd;
+ else if (strcmp(t, "hd") == 0)
+ (*farray)[i] = hd;
+ else if (strcmp(t, "tl") == 0)
+ (*farray)[i] = tl;
+ free(t);
+ }
+ free(els);
+}
+
+
+struct expr *expr_funcall(char *ident, struct list *args, struct list *fields)
{
struct expr *res = safe_malloc(sizeof(struct expr));
res->type = efuncall;
res->data.efuncall.ident = ident;
res->data.efuncall.args = (struct expr **)
list_to_array(args, &res->data.efuncall.nargs, true);
+ set_fields(&res->data.efuncall.fields,
+ &res->data.efuncall.nfields, fields);
return res;
}
struct expr *res = safe_malloc(sizeof(struct expr));
res->type = eident;
res->data.eident.ident = ident;
-
- void **els = list_to_array(fields, &res->data.eident.nfields, true);
- res->data.eident.fields = (enum fieldspec *)safe_malloc(
- res->data.eident.nfields*sizeof(enum fieldspec));
- for (int i = 0; i<res->data.eident.nfields; i++) {
- char *t = els[i];
- if (strcmp(t, "fst") == 0)
- res->data.eident.fields[i] = fst;
- else if (strcmp(t, "snd") == 0)
- res->data.eident.fields[i] = snd;
- else if (strcmp(t, "hd") == 0)
- res->data.eident.fields[i] = hd;
- else if (strcmp(t, "tl") == 0)
- res->data.eident.fields[i] = tl;
- free(t);
- }
- free(els);
+ set_fields(&res->data.eident.fields, &res->data.eident.nfields, fields);
return res;
}
{
struct expr *res = safe_malloc(sizeof(struct expr));
res->type = estring;
- res->data.estring = safe_strdup(str+1);
- res->data.estring[strlen(res->data.estring)-1] = '\0';
- //TODO escapes
+ res->data.estring.nchar = 0;
+ res->data.estring.chars = safe_malloc(strlen(str)+1);
+ char *p = res->data.estring.chars;
+ while(*str != '\0') {
+ str = unescape_char(str);
+ *p++ = *str++;
+ res->data.estring.nchar++;
+ }
+ *p = '\0';
return res;
}
return res;
}
-
-const char *cescapes[] = {
- [0] = "0", [1] = "x01", [2] = "x02", [3] = "x03",
- [4] = "x04", [5] = "x05", [6] = "x06", [7] = "a", [8] = "b",
- [9] = "t", [10] = "n", [11] = "v", [12] = "f", [13] = "r",
- [14] = "x0E", [15] = "x0F", [16] = "x10", [17] = "x11",
- [18] = "x12", [19] = "x13", [20] = "x14", [21] = "x15",
- [22] = "x16", [23] = "x17", [24] = "x18", [25] = "x19",
- [26] = "x1A", [27] = "x1B", [28] = "x1C", [29] = "x1D",
- [30] = "x1E", [31] = "x1F",
- ['\\'] = "\\", ['\''] = "'",
- [127] = "x7F"
-};
-
void ast_print(struct ast *ast, FILE *out)
{
if (ast == NULL)
expr_print(stmt->data.sexpr, out);
safe_fprintf(out, ";\n");
break;
+ case svardecl:
+ vardecl_print(stmt->data.svardecl, indent, out);
+ break;
case swhile:
pindent(indent, out);
safe_fprintf(out, "while (");
{
if (expr == NULL)
return;
+ char buf[] = "\\xff";
switch(expr->type) {
case ebinop:
safe_fprintf(out, "(");
safe_fprintf(out, "%s", expr->data.ebool ? "true" : "false");
break;
case echar:
- if (expr->data.echar < 0) {
- safe_fprintf(out, "'?'");
- } else if (expr->data.echar < ' ' || expr->data.echar == 127
- || expr->data.echar == '\\'
- || expr->data.echar == '\'') {
- safe_fprintf(out, "'\\%s'",
- cescapes[(int)expr->data.echar]);
- } else {
- safe_fprintf(out, "'%c'", expr->data.echar);
- }
+ safe_fprintf(out, "'%s'",
+ escape_char(expr->data.echar, buf, false));
break;
case efuncall:
safe_fprintf(out, "%s(", expr->data.efuncall.ident);
safe_fprintf(out, ", ");
}
safe_fprintf(out, ")");
+ for (int i = 0; i<expr->data.efuncall.nfields; i++)
+ fprintf(out, ".%s",
+ fieldspec_str[expr->data.efuncall.fields[i]]);
break;
case eint:
safe_fprintf(out, "%d", expr->data.eint);
safe_fprintf(out, ")");
break;
case estring:
- safe_fprintf(out, "\"%s\"", expr->data.estring);
+ safe_fprintf(out, "\"");
+ for (int i = 0; i<expr->data.estring.nchar; i++)
+ safe_fprintf(out, "%s", escape_char(
+ expr->data.estring.chars[i], buf, true));
+ safe_fprintf(out, "\"");
break;
case eunop:
safe_fprintf(out, "(%s", unop_str[expr->data.eunop.op]);
free(stmt->data.sassign.ident);
for (int i = 0; i<stmt->data.sassign.nfield; i++)
free(stmt->data.sassign.fields[i]);
+ free(stmt->data.sassign.fields);
expr_free(stmt->data.sassign.expr);
break;
case sif:
stmt_free(stmt->data.swhile.body[i]);
free(stmt->data.swhile.body);
break;
+ case svardecl:
+ vardecl_free(stmt->data.svardecl);
+ break;
default:
die("Unsupported stmt node\n");
}
free(expr->data.efuncall.ident);
for (int i = 0; i<expr->data.efuncall.nargs; i++)
expr_free(expr->data.efuncall.args[i]);
+ free(expr->data.efuncall.fields);
free(expr->data.efuncall.args);
break;
case eint:
expr_free(expr->data.etuple.right);
break;
case estring:
- free(expr->data.estring);
+ free(expr->data.estring.chars);
break;
case eunop:
expr_free(expr->data.eunop.l);
: /* empty */ { $$ = NULL; }
| funtype ftype { $$ = list_cons($2, $1); }
;
-ftype
+/* don't allow vardecls to be fully polymorph, this complicates parsing a lot */
+type
: BOPEN ftype COMMA ftype BCLOSE { $$ = type_tuple($2, $4); }
| SOPEN ftype SCLOSE { $$ = type_list($2); }
| TBOOL { $$ = type_basic(btbool); }
| TCHAR { $$ = type_basic(btchar); }
| TINT { $$ = type_basic(btint); }
| TVOID { $$ = type_basic(btvoid); }
- | IDENT { $$ = type_var($1); }
-type
- : BOPEN type COMMA type BCLOSE { $$ = type_tuple($2, $4); }
- | SOPEN type SCLOSE { $$ = type_list($2); }
+ ;
+ftype
+ : BOPEN ftype COMMA ftype BCLOSE { $$ = type_tuple($2, $4); }
+ | SOPEN ftype SCLOSE { $$ = type_list($2); }
| TBOOL { $$ = type_basic(btbool); }
| TCHAR { $$ = type_basic(btchar); }
| TINT { $$ = type_basic(btint); }
| TVOID { $$ = type_basic(btvoid); }
+ | IDENT { $$ = type_var($1); }
;
args
: /* empty */ { $$ = NULL; }
| IDENT field ASSIGN expr SEMICOLON { $$ = stmt_assign($1, $2, $4); }
| RETURN expr SEMICOLON { $$ = stmt_return($2); }
| RETURN SEMICOLON { $$ = stmt_return(NULL); }
+ | vardecl { $$ = stmt_vardecl($1); }
| expr SEMICOLON { $$ = stmt_expr($1); }
;
expr
| expr POWER expr { $$ = expr_binop($1, power, $3); }
| MINUS expr %prec TIMES { $$ = expr_unop(negate, $2); }
| INVERSE expr %prec TIMES { $$ = expr_unop(inverse, $2); }
- | IDENT BOPEN fargs BCLOSE { $$ = expr_funcall($1, $3); }
+ | IDENT BOPEN fargs BCLOSE field { $$ = expr_funcall($1, $3, $5); }
| BOPEN expr COMMA expr BCLOSE { $$ = expr_tuple($2, $4); }
| BOPEN expr BCLOSE { $$ = $2; }
| INTEGER
return i;
}
+int fromHex(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c-'0';
+ if (c >= 'a' && c <= 'f')
+ return c-'a'+10;
+ if (c >= 'A' && c <= 'F')
+ return c-'A'+10;
+ return -1;
+}
+
+char *escape_char(char c, char *buf, bool str)
+{
+ buf = buf == NULL ? safe_malloc(10) : buf;
+ switch (c) {
+ case '\0': strcpy(buf, "\\0"); break;
+ case '\a': strcpy(buf, "\\a"); break;
+ case '\b': strcpy(buf, "\\b"); break;
+ case '\t': strcpy(buf, "\\t"); break;
+ case '\n': strcpy(buf, "\\n"); break;
+ case '\v': strcpy(buf, "\\v"); break;
+ case '\f': strcpy(buf, "\\f"); break;
+ case '\r': strcpy(buf, "\\r"); break;
+ case '\'': strcpy(buf, str ? "'" : "\\'"); break;
+ case '"': strcpy(buf, str ? "\\\"" : "\""); break;
+ default:
+ if (c >= ' ' && c < 127) {
+ sprintf(buf, "%c", c);
+ } else {
+ sprintf(buf, "\\x%02x", (unsigned char)c);
+ }
+ break;
+ }
+ return buf;
+}
+
+char *unescape_char(char *c)
+{
+ //escape
+ if (c[0] == '\\') {
+ switch (c[1]) {
+ case '0': c[1] = '\0'; break;
+ case '\'': c[1] = '\''; break;
+ case '\\': c[1] = '\\'; break;
+ case '"': c[1] = '"'; break;
+ case 'a': c[1] = '\a'; break;
+ case 'b': c[1] = '\b'; break;
+ case 't': c[1] = '\t'; break;
+ case 'v': c[1] = '\v'; break;
+ case 'f': c[1] = '\f'; break;
+ case 'r': c[1] = '\r'; break;
+ case 'x': c[3] = (fromHex(c[2])<<4)+fromHex(c[3]); c+=2; break;
+ }
+ c++;
+ }
+ return c;
+}
+
+char *trimquotes(char *c)
+{
+ char *r = c+1;
+ r[strlen(r)-1] = '\0';
+ return r;
+}
+
void pdie(const char *msg)
{
perror(msg);