diff options
Diffstat (limited to 'src/runtime/c/pgf/expr.c')
| -rw-r--r-- | src/runtime/c/pgf/expr.c | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/src/runtime/c/pgf/expr.c b/src/runtime/c/pgf/expr.c new file mode 100644 index 000000000..cd5d69928 --- /dev/null +++ b/src/runtime/c/pgf/expr.c @@ -0,0 +1,334 @@ +#include "expr.h" +#include <gu/intern.h> +#include <gu/assert.h> +#include <ctype.h> + + +PgfExpr +pgf_expr_unwrap(PgfExpr expr) +{ + while (true) { + GuVariantInfo i = gu_variant_open(expr); + switch (i.tag) { + case PGF_EXPR_IMPL_ARG: { + PgfExprImplArg* eimpl = i.data; + expr = eimpl->expr; + break; + } + case PGF_EXPR_TYPED: { + PgfExprTyped* etyped = i.data; + expr = etyped->expr; + break; + } + default: + return expr; + } + } +} + +int +pgf_expr_arity(PgfExpr expr) +{ + int n = 0; + while (true) { + PgfExpr e = pgf_expr_unwrap(expr); + GuVariantInfo i = gu_variant_open(e); + switch (i.tag) { + case PGF_EXPR_APP: { + PgfExprApp* app = i.data; + expr = app->fun; + n = n + 1; + break; + } + case PGF_EXPR_FUN: + return n; + default: + return -1; + } + } +} + +PgfApplication* +pgf_expr_unapply(PgfExpr expr, GuPool* pool) +{ + int arity = pgf_expr_arity(expr); + if (arity < 0) { + return NULL; + } + PgfApplication* appl = gu_new_flex(pool, PgfApplication, args, arity); + appl->n_args = arity; + for (int n = arity - 1; n >= 0; n--) { + PgfExpr e = pgf_expr_unwrap(expr); + gu_assert(gu_variant_tag(e) == PGF_EXPR_APP); + PgfExprApp* app = gu_variant_data(e); + appl->args[n] = app->arg; + expr = app->fun; + } + PgfExpr e = pgf_expr_unwrap(expr); + gu_assert(gu_variant_tag(e) == PGF_EXPR_FUN); + PgfExprFun* fun = gu_variant_data(e); + appl->fun = fun->fun; + return appl; +} + +GU_DEFINE_TYPE(PgfBindType, enum, + GU_ENUM_C(PgfBindType, PGF_BIND_TYPE_EXPLICIT), + GU_ENUM_C(PgfBindType, PGF_BIND_TYPE_IMPLICIT)); + +GU_DEFINE_TYPE(PgfLiteral, GuVariant, + GU_CONSTRUCTOR_S(PGF_LITERAL_STR, PgfLiteralStr, + GU_MEMBER(PgfLiteralStr, val, GuString)), + GU_CONSTRUCTOR_S(PGF_LITERAL_INT, PgfLiteralInt, + GU_MEMBER(PgfLiteralInt, val, int)), + GU_CONSTRUCTOR_S(PGF_LITERAL_FLT, PgfLiteralFlt, + GU_MEMBER(PgfLiteralFlt, val, double))); + +GU_DECLARE_TYPE(PgfType, struct); + +GU_DEFINE_TYPE(PgfHypo, struct, + GU_MEMBER(PgfHypo, bindtype, PgfBindType), + GU_MEMBER(PgfHypo, cid, PgfCId), + GU_MEMBER_P(PgfHypo, type, PgfType)); + +GU_DEFINE_TYPE(PgfHypos, GuSeq, gu_type(PgfHypo)); + +GU_DEFINE_TYPE(PgfType, struct, + GU_MEMBER(PgfType, hypos, PgfHypos), + GU_MEMBER(PgfType, cid, PgfCId), + GU_MEMBER(PgfType, n_exprs, GuLength), + GU_FLEX_MEMBER(PgfType, exprs, PgfExpr)); + +GU_DEFINE_TYPE( + PgfExpr, GuVariant, + GU_CONSTRUCTOR_S( + PGF_EXPR_ABS, PgfExprAbs, + GU_MEMBER(PgfExprAbs, bind_type, PgfBindType), + GU_MEMBER(PgfExprAbs, id, GuStr), + GU_MEMBER(PgfExprAbs, body, PgfExpr)), + GU_CONSTRUCTOR_S( + PGF_EXPR_APP, PgfExprApp, + GU_MEMBER(PgfExprApp, fun, PgfExpr), + GU_MEMBER(PgfExprApp, arg, PgfExpr)), + GU_CONSTRUCTOR_S( + PGF_EXPR_LIT, PgfExprLit, + GU_MEMBER(PgfExprLit, lit, PgfLiteral)), + GU_CONSTRUCTOR_S( + PGF_EXPR_META, PgfExprMeta, + GU_MEMBER(PgfExprMeta, id, int)), + GU_CONSTRUCTOR_S( + PGF_EXPR_FUN, PgfExprFun, + GU_MEMBER(PgfExprFun, fun, GuStr)), + GU_CONSTRUCTOR_S( + PGF_EXPR_VAR, PgfExprVar, + GU_MEMBER(PgfExprVar, var, int)), + GU_CONSTRUCTOR_S( + PGF_EXPR_TYPED, PgfExprTyped, + GU_MEMBER(PgfExprTyped, expr, PgfExpr), + GU_MEMBER_P(PgfExprTyped, type, PgfType)), + GU_CONSTRUCTOR_S( + PGF_EXPR_IMPL_ARG, PgfExprImplArg, + GU_MEMBER(PgfExprImplArg, expr, PgfExpr))); + + +typedef struct PgfExprParser PgfExprParser; + +struct PgfExprParser { + GuReader* rdr; + GuIntern* intern; + GuExn* err; + GuPool* expr_pool; + const char* lookahead; + int next_char; +}; + + +static const char pgf_expr_lpar[] = "("; +static const char pgf_expr_rpar[] = ")"; +static const char pgf_expr_semic[] = ";"; + +static char +pgf_expr_parser_next(PgfExprParser* parser) +{ + if (parser->next_char >= 0) { + char ret = (char) parser->next_char; + parser->next_char = -1; + return ret; + } + return gu_getc(parser->rdr, parser->err); +} + +static const char* +pgf_expr_parser_lookahead(PgfExprParser* parser) +{ + if (parser->lookahead != NULL) { + return parser->lookahead; + } + const char* str = NULL; + char c; + do { + c = pgf_expr_parser_next(parser); + if (!gu_ok(parser->err)) { + return NULL; + } + } while (isspace(c)); + switch (c) { + case '(': + str = pgf_expr_lpar; + break; + case ')': + str = pgf_expr_rpar; + break; + case ';': + str = pgf_expr_semic; + break; + default: + if (isalpha(c)) { + GuPool* tmp_pool = gu_new_pool(); + GuCharBuf* chars = gu_new_buf(char, tmp_pool); + while (isalnum(c) || c == '_') { + gu_buf_push(chars, char, c); + c = pgf_expr_parser_next(parser); + if (!gu_ok(parser->err)) { + return NULL; + } + } + parser->next_char = (unsigned char) c; + char* tmp_str = gu_chars_str(gu_buf_seq(chars), + tmp_pool); + str = gu_intern_str(parser->intern, tmp_str); + gu_pool_free(tmp_pool); + } + } + parser->lookahead = str; + return str; +} + +static bool +pgf_expr_parser_token_is_id(const char* str) +{ + if (str == NULL || !str[0]) { + return false; + } + char c = str[0]; + return (isalpha(c) || c == '_'); +} + +static void +pgf_expr_parser_consume(PgfExprParser* parser) +{ + pgf_expr_parser_lookahead(parser); + parser->lookahead = NULL; +} + +static PgfExpr +pgf_expr_parser_expr(PgfExprParser* parser); + +static PgfExpr +pgf_expr_parser_term(PgfExprParser* parser) +{ + const char* la = pgf_expr_parser_lookahead(parser); + + if (la == pgf_expr_lpar) { + pgf_expr_parser_consume(parser); + PgfExpr expr = pgf_expr_parser_expr(parser); + la = pgf_expr_parser_lookahead(parser); + if (la == pgf_expr_rpar) { + pgf_expr_parser_consume(parser); + return expr; + } + } else if (pgf_expr_parser_token_is_id(la)) { + pgf_expr_parser_consume(parser); + GuString s = gu_str_string(la, parser->expr_pool); + return gu_new_variant_i(parser->expr_pool, + PGF_EXPR_FUN, + PgfExprFun, + s); + } + return gu_null_variant; +} + +static PgfExpr +pgf_expr_parser_expr(PgfExprParser* parser) +{ + PgfExpr expr = pgf_expr_parser_term(parser); + if (gu_variant_is_null(expr)) + { + return expr; + } + while (true) { + PgfExpr arg = pgf_expr_parser_term(parser); + if (gu_variant_is_null(arg)) { + return expr; + } + expr = gu_new_variant_i(parser->expr_pool, + PGF_EXPR_APP, + PgfExprApp, + expr, arg); + } +} + + + +PgfExpr +pgf_read_expr(GuReader* rdr, GuPool* pool, GuExn* err) +{ + GuPool* tmp_pool = gu_new_pool(); + PgfExprParser* parser = gu_new(PgfExprParser, tmp_pool); + parser->rdr = rdr; + parser->intern = gu_new_intern(pool, tmp_pool); + parser->expr_pool = pool; + parser->err = err; + parser->lookahead = NULL; + parser->next_char = -1; + PgfExpr expr = pgf_expr_parser_expr(parser); + const char* la = pgf_expr_parser_lookahead(parser); + if (la == pgf_expr_semic) { + pgf_expr_parser_consume(parser); + } else { + expr = gu_null_variant; + } + gu_pool_free(tmp_pool); + return expr; +} + +static void +pgf_expr_print_with_paren(PgfExpr expr, bool need_paren, + GuWriter* wtr, GuExn* err) +{ + GuVariantInfo ei = gu_variant_open(expr); + switch (ei.tag) { + case PGF_EXPR_FUN: { + PgfExprFun* fun = ei.data; + gu_string_write(fun->fun, wtr, err); + break; + } + case PGF_EXPR_APP: { + PgfExprApp* app = ei.data; + if (need_paren) { + gu_puts("(", wtr, err); + } + pgf_expr_print_with_paren(app->fun, false, wtr, err); + gu_puts(" ", wtr, err); + pgf_expr_print_with_paren(app->arg, true, wtr, err); + if (need_paren) { + gu_puts(")", wtr, err); + } + break; + } + case PGF_EXPR_ABS: + case PGF_EXPR_LIT: + case PGF_EXPR_META: + case PGF_EXPR_VAR: + case PGF_EXPR_TYPED: + case PGF_EXPR_IMPL_ARG: + gu_impossible(); + break; + default: + gu_impossible(); + } +} + +void +pgf_expr_print(PgfExpr expr, GuWriter* wtr, GuExn* err) { + pgf_expr_print_with_paren(expr, false, wtr, err); +} |
