mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-23 03:32:51 -06:00
A simple type checker in the C runtime. Dependent types are not supported yet but HOAS is implemented. The API is accessible from Python as well
This commit is contained in:
@@ -97,6 +97,7 @@ libpgf_la_SOURCES = \
|
|||||||
pgf/reader.h \
|
pgf/reader.h \
|
||||||
pgf/reader.c \
|
pgf/reader.c \
|
||||||
pgf/linearizer.c \
|
pgf/linearizer.c \
|
||||||
|
pgf/typechecker.c \
|
||||||
pgf/reasoner.c \
|
pgf/reasoner.c \
|
||||||
pgf/printer.c \
|
pgf/printer.c \
|
||||||
pgf/graphviz.c \
|
pgf/graphviz.c \
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ typedef GuString PgfToken;
|
|||||||
|
|
||||||
extern GU_DECLARE_TYPE(PgfExn, abstract);
|
extern GU_DECLARE_TYPE(PgfExn, abstract);
|
||||||
extern GU_DECLARE_TYPE(PgfParseError, abstract);
|
extern GU_DECLARE_TYPE(PgfParseError, abstract);
|
||||||
|
extern GU_DECLARE_TYPE(PgfTypeError, abstract);
|
||||||
|
|
||||||
/// @name PGF Grammar objects
|
/// @name PGF Grammar objects
|
||||||
/// @{
|
/// @{
|
||||||
@@ -171,4 +172,16 @@ pgf_complete(PgfConcr* concr, PgfCId cat, GuString string,
|
|||||||
void
|
void
|
||||||
pgf_print(PgfPGF* pgf, GuOut* out, GuExn* err);
|
pgf_print(PgfPGF* pgf, GuOut* out, GuExn* err);
|
||||||
|
|
||||||
|
void
|
||||||
|
pgf_check_expr(PgfPGF* gr, PgfExpr* pe, PgfType* ty,
|
||||||
|
GuExn* exn, GuPool* pool);
|
||||||
|
|
||||||
|
PgfType*
|
||||||
|
pgf_infer_expr(PgfPGF* gr, PgfExpr* pe,
|
||||||
|
GuExn* exn, GuPool* pool);
|
||||||
|
|
||||||
|
void
|
||||||
|
pgf_check_type(PgfPGF* gr, PgfType** ty,
|
||||||
|
GuExn* exn, GuPool* pool);
|
||||||
|
|
||||||
#endif // PGF_H_
|
#endif // PGF_H_
|
||||||
|
|||||||
581
src/runtime/c/pgf/typechecker.c
Normal file
581
src/runtime/c/pgf/typechecker.c
Normal file
@@ -0,0 +1,581 @@
|
|||||||
|
#include "pgf.h"
|
||||||
|
#include "data.h"
|
||||||
|
#include "gu/mem.h"
|
||||||
|
#include "gu/string.h"
|
||||||
|
|
||||||
|
typedef struct PgfCFHypos PgfCFHypos;
|
||||||
|
struct PgfCFHypos {
|
||||||
|
PgfType* ty;
|
||||||
|
PgfCFHypos* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct PgfContext PgfContext;
|
||||||
|
struct PgfContext {
|
||||||
|
PgfCId var;
|
||||||
|
PgfType* ty;
|
||||||
|
PgfContext* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PgfCFHypos* hypos;
|
||||||
|
PgfCId cat;
|
||||||
|
} PgfCFType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PgfAbstr* abstr;
|
||||||
|
GuExn* exn;
|
||||||
|
GuPool* pool;
|
||||||
|
GuPool* tmp_pool;
|
||||||
|
} PgfTypeChecker;
|
||||||
|
|
||||||
|
static PgfCFType null_cf_type = { .hypos = NULL, .cat = NULL };
|
||||||
|
|
||||||
|
static PgfCFType
|
||||||
|
pgf_ty2cfty(PgfTypeChecker* checker, PgfType* ty)
|
||||||
|
{
|
||||||
|
PgfCFHypos* hypos = NULL;
|
||||||
|
size_t n_hypos = gu_seq_length(ty->hypos);
|
||||||
|
while (n_hypos-- > 0) {
|
||||||
|
PgfHypo* hypo = gu_seq_index(ty->hypos, PgfHypo, n_hypos);
|
||||||
|
|
||||||
|
PgfCFHypos* new_hypos = gu_new(PgfCFHypos, checker->tmp_pool);
|
||||||
|
new_hypos->ty = hypo->type;
|
||||||
|
new_hypos->next = hypos;
|
||||||
|
hypos = new_hypos;
|
||||||
|
}
|
||||||
|
return ((PgfCFType) { .hypos = hypos, .cat = ty->cid });
|
||||||
|
}
|
||||||
|
|
||||||
|
static PgfType*
|
||||||
|
pgf_cfty2ty(PgfTypeChecker* checker, PgfCFType cf_ty)
|
||||||
|
{
|
||||||
|
PgfCFHypos* hypos;
|
||||||
|
|
||||||
|
size_t n_hypos = 0;
|
||||||
|
hypos = cf_ty.hypos;
|
||||||
|
while (hypos != NULL) {
|
||||||
|
n_hypos++;
|
||||||
|
hypos = hypos->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
PgfType* ty = gu_new(PgfType, checker->pool);
|
||||||
|
ty->hypos = gu_new_seq(PgfHypo, n_hypos, checker->pool);
|
||||||
|
ty->cid = cf_ty.cat;
|
||||||
|
ty->n_exprs = 0;
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
hypos = cf_ty.hypos;
|
||||||
|
while (hypos != NULL) {
|
||||||
|
PgfHypo* hypo = gu_seq_index(ty->hypos, PgfHypo, i++);
|
||||||
|
hypo->bind_type = PGF_BIND_TYPE_EXPLICIT;
|
||||||
|
hypo->cid = "_";
|
||||||
|
hypo->type = pgf_cfty2ty(checker, pgf_ty2cfty(checker, hypos->ty));
|
||||||
|
hypos = hypos->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
GU_DEFINE_TYPE(PgfTypeError, abstract, _);
|
||||||
|
|
||||||
|
static PgfPrintContext*
|
||||||
|
pgf_tc_mk_print_context(PgfTypeChecker* checker, PgfContext* ctxt)
|
||||||
|
{
|
||||||
|
PgfPrintContext* pctxt = NULL;
|
||||||
|
PgfPrintContext** pprev = &pctxt;
|
||||||
|
|
||||||
|
while (ctxt != NULL) {
|
||||||
|
PgfPrintContext* new_pctxt =
|
||||||
|
gu_new(PgfPrintContext, checker->tmp_pool);
|
||||||
|
new_pctxt->name = ctxt->var;
|
||||||
|
new_pctxt->next = NULL;
|
||||||
|
|
||||||
|
*pprev = new_pctxt;
|
||||||
|
pprev = &new_pctxt->next;
|
||||||
|
|
||||||
|
ctxt = ctxt->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pctxt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgf_tc_err_cannot_infer(PgfTypeChecker* checker, PgfContext* ctxt, PgfExpr e)
|
||||||
|
{
|
||||||
|
GuStringBuf* sbuf = gu_string_buf(checker->tmp_pool);
|
||||||
|
GuOut* out = gu_string_buf_out(sbuf);
|
||||||
|
GuExn* err = gu_exn(NULL, type, checker->tmp_pool);
|
||||||
|
|
||||||
|
gu_puts("Cannot infer the type of expression ", out, err);
|
||||||
|
pgf_print_expr(e, pgf_tc_mk_print_context(checker, ctxt), 0, out, err);
|
||||||
|
|
||||||
|
GuExnData* exn = gu_raise(checker->exn, PgfTypeError);
|
||||||
|
exn->data = (void*)
|
||||||
|
gu_string_buf_freeze(sbuf, exn->pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgf_tc_err_exp_fun_type_1(PgfTypeChecker* checker, PgfContext* ctxt,
|
||||||
|
PgfExpr e, PgfType* ty)
|
||||||
|
{
|
||||||
|
GuStringBuf* sbuf = gu_string_buf(checker->tmp_pool);
|
||||||
|
GuOut* out = gu_string_buf_out(sbuf);
|
||||||
|
GuExn* err = gu_exn(NULL, type, checker->tmp_pool);
|
||||||
|
|
||||||
|
PgfPrintContext* pctxt = pgf_tc_mk_print_context(checker, ctxt);
|
||||||
|
gu_puts("The expression ", out, err);
|
||||||
|
pgf_print_expr(e, pctxt, 0, out, err);
|
||||||
|
gu_puts(" is of function type but ", out, err);
|
||||||
|
pgf_print_type(ty, pctxt, 0, out, err);
|
||||||
|
gu_puts(" is expected", out, err);
|
||||||
|
|
||||||
|
GuExnData* exn = gu_raise(checker->exn, PgfTypeError);
|
||||||
|
exn->data = (void*)
|
||||||
|
gu_string_buf_freeze(sbuf, exn->pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgf_tc_err_exp_fun_type_2(PgfTypeChecker* checker, PgfContext* ctxt,
|
||||||
|
PgfExpr e, PgfType* ty)
|
||||||
|
{
|
||||||
|
GuStringBuf* sbuf = gu_string_buf(checker->tmp_pool);
|
||||||
|
GuOut* out = gu_string_buf_out(sbuf);
|
||||||
|
GuExn* err = gu_exn(NULL, type, checker->tmp_pool);
|
||||||
|
|
||||||
|
PgfPrintContext* pctxt = pgf_tc_mk_print_context(checker, ctxt);
|
||||||
|
gu_puts("A function type is expected for the expression ", out, err);
|
||||||
|
pgf_print_expr(e, pctxt, 0, out, err);
|
||||||
|
gu_puts(" instead of type ", out, err);
|
||||||
|
pgf_print_type(ty, pctxt, 0, out, err);
|
||||||
|
|
||||||
|
GuExnData* exn = gu_raise(checker->exn, PgfTypeError);
|
||||||
|
exn->data = (void*)
|
||||||
|
gu_string_buf_freeze(sbuf, exn->pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgf_tc_err_n_args(PgfTypeChecker* checker,
|
||||||
|
PgfAbsCat* abs_cat, size_t n_args)
|
||||||
|
{
|
||||||
|
GuExnData* exn = gu_raise(checker->exn, PgfTypeError);
|
||||||
|
exn->data = (void*)
|
||||||
|
gu_format_string(exn->pool,
|
||||||
|
"The category %s has %d indices but %d are given",
|
||||||
|
abs_cat->name,
|
||||||
|
gu_seq_length(abs_cat->context),
|
||||||
|
n_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgf_tc_err_type_mismatch(PgfTypeChecker* checker,
|
||||||
|
PgfContext* ctxt,
|
||||||
|
PgfExpr e, PgfCFType ty1, PgfCFType ty2)
|
||||||
|
{
|
||||||
|
GuStringBuf* sbuf = gu_string_buf(checker->tmp_pool);
|
||||||
|
GuOut* out = gu_string_buf_out(sbuf);
|
||||||
|
GuExn* err = gu_exn(NULL, type, checker->tmp_pool);
|
||||||
|
|
||||||
|
PgfPrintContext* pctxt = pgf_tc_mk_print_context(checker, ctxt);
|
||||||
|
gu_puts("The expected type of the expression ", out, err);
|
||||||
|
pgf_print_expr(e, pctxt, 0, out, err);
|
||||||
|
gu_puts(" is ", out, err);
|
||||||
|
pgf_print_type(pgf_cfty2ty(checker, ty1), pctxt, 0, out, err);
|
||||||
|
gu_puts(" but ", out, err);
|
||||||
|
pgf_print_type(pgf_cfty2ty(checker, ty2), pctxt, 0, out, err);
|
||||||
|
gu_puts(" is infered", out, err);
|
||||||
|
|
||||||
|
GuExnData* exn = gu_raise(checker->exn, PgfTypeError);
|
||||||
|
exn->data = (void*)
|
||||||
|
gu_string_buf_freeze(sbuf, exn->pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
pgf_unify_types(PgfTypeChecker* checker, PgfCFType ty1, PgfCFType ty2)
|
||||||
|
{
|
||||||
|
if (strcmp(ty1.cat, ty2.cat) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (ty1.hypos != NULL && ty2.hypos != NULL) {
|
||||||
|
if (!pgf_unify_types(checker,
|
||||||
|
pgf_ty2cfty(checker, ty1.hypos->ty),
|
||||||
|
pgf_ty2cfty(checker, ty2.hypos->ty)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ty1.hypos = ty1.hypos->next;
|
||||||
|
ty2.hypos = ty2.hypos->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ty1.hypos != NULL || ty2.hypos != NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PgfCFType
|
||||||
|
pgf_inf_expr(PgfTypeChecker* checker, PgfContext* ctxt, PgfExpr* pe);
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgf_tc_expr(PgfTypeChecker* checker,
|
||||||
|
PgfContext* ctxt, PgfExpr* pe, PgfCFType ty)
|
||||||
|
{
|
||||||
|
GuVariantInfo i = gu_variant_open(*pe);
|
||||||
|
switch (i.tag) {
|
||||||
|
case PGF_EXPR_ABS: {
|
||||||
|
PgfExprAbs* eabs = i.data;
|
||||||
|
|
||||||
|
if (ty.hypos == NULL) {
|
||||||
|
pgf_tc_err_exp_fun_type_1(checker, ctxt, *pe, pgf_cfty2ty(checker, ty));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PgfContext* new_ctxt = gu_new(PgfContext, checker->tmp_pool);
|
||||||
|
new_ctxt->var = eabs->id;
|
||||||
|
new_ctxt->ty = ty.hypos->ty;
|
||||||
|
new_ctxt->next = ctxt;
|
||||||
|
|
||||||
|
PgfExprAbs* new_eabs =
|
||||||
|
gu_new_variant(PGF_EXPR_ABS,
|
||||||
|
PgfExprAbs,
|
||||||
|
pe, checker->pool);
|
||||||
|
new_eabs->bind_type = eabs->bind_type;
|
||||||
|
new_eabs->id = gu_string_copy(eabs->id, checker->pool);
|
||||||
|
new_eabs->body = eabs->body;
|
||||||
|
|
||||||
|
PgfCFType new_ty = { .hypos = ty.hypos->next, .cat = ty.cat };
|
||||||
|
pgf_tc_expr(checker, new_ctxt, &new_eabs->body, new_ty);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PGF_EXPR_LIT:
|
||||||
|
case PGF_EXPR_APP:
|
||||||
|
case PGF_EXPR_FUN:
|
||||||
|
case PGF_EXPR_VAR:
|
||||||
|
case PGF_EXPR_TYPED: {
|
||||||
|
PgfCFType inf_ty =
|
||||||
|
pgf_inf_expr(checker, ctxt, pe);
|
||||||
|
if (!gu_ok(checker->exn))
|
||||||
|
return;
|
||||||
|
if (!pgf_unify_types(checker, ty, inf_ty)) {
|
||||||
|
pgf_tc_err_type_mismatch(checker, ctxt, *pe, ty, inf_ty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PGF_EXPR_META: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PGF_EXPR_IMPL_ARG: {
|
||||||
|
PgfExprImplArg* eimpl = i.data;
|
||||||
|
|
||||||
|
PgfExprImplArg* new_eimpl =
|
||||||
|
gu_new_variant(PGF_EXPR_IMPL_ARG,
|
||||||
|
PgfExprImplArg,
|
||||||
|
pe, checker->pool);
|
||||||
|
new_eimpl->expr = eimpl->expr;
|
||||||
|
pgf_tc_expr(checker, ctxt, &new_eimpl->expr, ty);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
gu_impossible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgf_tc_type(PgfTypeChecker* checker, PgfType** pty)
|
||||||
|
{
|
||||||
|
PgfType* ty = *pty;
|
||||||
|
|
||||||
|
PgfAbsCat* abs_cat =
|
||||||
|
gu_map_get(checker->abstr->cats, ty->cid, PgfAbsCat*);
|
||||||
|
if (abs_cat == NULL) {
|
||||||
|
GuExnData* exn = gu_raise(checker->exn, PgfExn);
|
||||||
|
exn->data = (void*)
|
||||||
|
gu_format_string(exn->pool,
|
||||||
|
"Unknown category \"%s\"", ty->cid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PgfContext* ctxt = NULL;
|
||||||
|
|
||||||
|
size_t n_hypos = gu_seq_length(ty->hypos);
|
||||||
|
PgfHypos* new_hypos = gu_new_seq(PgfHypo, n_hypos, checker->pool);
|
||||||
|
for (size_t i = 0; i < n_hypos; i++) {
|
||||||
|
PgfHypo* hypo = gu_seq_index(ty->hypos, PgfHypo, i);
|
||||||
|
PgfHypo* new_hypo = gu_seq_index(new_hypos, PgfHypo, i);
|
||||||
|
|
||||||
|
new_hypo->bind_type = hypo->bind_type;
|
||||||
|
new_hypo->cid = gu_string_copy(hypo->cid, checker->pool);
|
||||||
|
new_hypo->type = hypo->type;
|
||||||
|
|
||||||
|
pgf_tc_type(checker, &new_hypo->type);
|
||||||
|
if (!gu_ok(checker->exn))
|
||||||
|
return;
|
||||||
|
|
||||||
|
PgfContext* new_ctxt = gu_new(PgfContext, checker->tmp_pool);
|
||||||
|
new_ctxt->var = hypo->cid;
|
||||||
|
new_ctxt->ty = hypo->type;
|
||||||
|
new_ctxt->next = ctxt;
|
||||||
|
ctxt = new_ctxt;
|
||||||
|
}
|
||||||
|
|
||||||
|
PgfType *new_ty =
|
||||||
|
gu_new_flex(checker->pool, PgfType, exprs, ty->n_exprs);
|
||||||
|
new_ty->hypos = new_hypos;
|
||||||
|
new_ty->cid = gu_string_copy(ty->cid, checker->pool);
|
||||||
|
new_ty->n_exprs = ty->n_exprs;
|
||||||
|
|
||||||
|
if (gu_seq_length(abs_cat->context) != ty->n_exprs) {
|
||||||
|
pgf_tc_err_n_args(checker, abs_cat, ty->n_exprs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ty->n_exprs; i++) {
|
||||||
|
PgfHypo* hypo = gu_seq_index(abs_cat->context, PgfHypo, i);
|
||||||
|
|
||||||
|
new_ty->exprs[i] = ty->exprs[i];
|
||||||
|
pgf_tc_expr(checker, ctxt, &new_ty->exprs[i], pgf_ty2cfty(checker, hypo->type));
|
||||||
|
if (!gu_ok(checker->exn))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pty = new_ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PgfCFType
|
||||||
|
pgf_inf_expr(PgfTypeChecker* checker, PgfContext* ctxt, PgfExpr* pe)
|
||||||
|
{
|
||||||
|
GuVariantInfo i = gu_variant_open(*pe);
|
||||||
|
switch (i.tag) {
|
||||||
|
case PGF_EXPR_ABS: {
|
||||||
|
pgf_tc_err_cannot_infer(checker, ctxt, *pe);
|
||||||
|
return null_cf_type;
|
||||||
|
}
|
||||||
|
case PGF_EXPR_APP: {
|
||||||
|
PgfExprApp* eapp = i.data;
|
||||||
|
|
||||||
|
PgfExprApp* new_eapp =
|
||||||
|
gu_new_variant(PGF_EXPR_APP,
|
||||||
|
PgfExprApp,
|
||||||
|
pe, checker->pool);
|
||||||
|
new_eapp->fun = eapp->fun;
|
||||||
|
new_eapp->arg = eapp->arg;
|
||||||
|
|
||||||
|
PgfCFType fun_ty =
|
||||||
|
pgf_inf_expr(checker, ctxt, &new_eapp->fun);
|
||||||
|
if (!gu_ok(checker->exn))
|
||||||
|
return null_cf_type;
|
||||||
|
if (fun_ty.hypos == NULL) {
|
||||||
|
pgf_tc_err_exp_fun_type_2(checker,
|
||||||
|
ctxt,
|
||||||
|
eapp->fun, pgf_cfty2ty(checker, fun_ty));
|
||||||
|
return null_cf_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pgf_tc_expr(checker, ctxt, &new_eapp->arg, pgf_ty2cfty(checker, fun_ty.hypos->ty));
|
||||||
|
return ((PgfCFType) { .hypos = fun_ty.hypos->next,
|
||||||
|
.cat = fun_ty.cat
|
||||||
|
});
|
||||||
|
}
|
||||||
|
case PGF_EXPR_LIT: {
|
||||||
|
PgfExprLit* elit = i.data;
|
||||||
|
|
||||||
|
PgfExprLit* new_elit =
|
||||||
|
gu_new_variant(PGF_EXPR_LIT,
|
||||||
|
PgfExprLit,
|
||||||
|
pe, checker->pool);
|
||||||
|
|
||||||
|
GuVariantInfo i = gu_variant_open(elit->lit);
|
||||||
|
switch (i.tag) {
|
||||||
|
case PGF_LITERAL_STR: {
|
||||||
|
PgfLiteralStr* lstr = i.data;
|
||||||
|
|
||||||
|
PgfLiteralStr* new_lstr =
|
||||||
|
gu_new_flex_variant(PGF_LITERAL_STR,
|
||||||
|
PgfLiteralStr,
|
||||||
|
val, strlen(lstr->val)+1,
|
||||||
|
&new_elit->lit, checker->pool);
|
||||||
|
strcpy(new_lstr->val, lstr->val);
|
||||||
|
|
||||||
|
return ((PgfCFType) { .hypos = NULL, .cat = "String" });
|
||||||
|
}
|
||||||
|
case PGF_LITERAL_INT: {
|
||||||
|
PgfLiteralInt* lint = i.data;
|
||||||
|
|
||||||
|
PgfLiteralInt* new_lint =
|
||||||
|
gu_new_variant(PGF_LITERAL_INT,
|
||||||
|
PgfLiteralInt,
|
||||||
|
&new_elit->lit, checker->pool);
|
||||||
|
new_lint->val = lint->val;
|
||||||
|
|
||||||
|
return ((PgfCFType) { .hypos = NULL, .cat = "Int" });
|
||||||
|
}
|
||||||
|
case PGF_LITERAL_FLT: {
|
||||||
|
PgfLiteralFlt* lflt = i.data;
|
||||||
|
|
||||||
|
PgfLiteralFlt* new_lflt =
|
||||||
|
gu_new_variant(PGF_LITERAL_FLT,
|
||||||
|
PgfLiteralFlt,
|
||||||
|
&new_elit->lit, checker->pool);
|
||||||
|
new_lflt->val = lflt->val;
|
||||||
|
|
||||||
|
return ((PgfCFType) { .hypos = NULL, .cat = "Float" });
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
gu_impossible();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PGF_EXPR_META: {
|
||||||
|
pgf_tc_err_cannot_infer(checker, ctxt, *pe);
|
||||||
|
return null_cf_type;
|
||||||
|
}
|
||||||
|
case PGF_EXPR_FUN: {
|
||||||
|
PgfExprFun* efun = i.data;
|
||||||
|
|
||||||
|
PgfType* ty = NULL;
|
||||||
|
|
||||||
|
int var = 0;
|
||||||
|
PgfContext* var_ctxt = ctxt;
|
||||||
|
while (var_ctxt != NULL) {
|
||||||
|
if (strcmp(var_ctxt->var, efun->fun) == 0) {
|
||||||
|
PgfExprVar* new_evar =
|
||||||
|
gu_new_variant(PGF_EXPR_VAR,
|
||||||
|
PgfExprVar,
|
||||||
|
pe, checker->pool);
|
||||||
|
new_evar->var = var;
|
||||||
|
ty = var_ctxt->ty;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var++;
|
||||||
|
var_ctxt = var_ctxt->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ty == NULL) {
|
||||||
|
PgfAbsFun* abs_fun =
|
||||||
|
gu_map_get(checker->abstr->funs, efun->fun, PgfAbsFun*);
|
||||||
|
if (abs_fun == NULL) {
|
||||||
|
GuExnData* exn = gu_raise(checker->exn, PgfExn);
|
||||||
|
exn->data = (void*)
|
||||||
|
gu_format_string(exn->pool,
|
||||||
|
"Unknown function \"%s\"", efun->fun);
|
||||||
|
return null_cf_type;
|
||||||
|
} else {
|
||||||
|
PgfExprFun *new_efun =
|
||||||
|
gu_new_flex_variant(PGF_EXPR_FUN,
|
||||||
|
PgfExprFun,
|
||||||
|
fun, strlen(efun->fun)+1,
|
||||||
|
pe, checker->pool);
|
||||||
|
strcpy(new_efun->fun, efun->fun);
|
||||||
|
ty = abs_fun->type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pgf_ty2cfty(checker, ty);
|
||||||
|
}
|
||||||
|
case PGF_EXPR_VAR: {
|
||||||
|
PgfExprVar* evar = i.data;
|
||||||
|
|
||||||
|
int var = evar->var;
|
||||||
|
PgfContext* var_ctxt = ctxt;
|
||||||
|
assert(var_ctxt != NULL);
|
||||||
|
while (var > 0) {
|
||||||
|
var--;
|
||||||
|
var_ctxt = var_ctxt->next;
|
||||||
|
assert(var_ctxt != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pgf_ty2cfty(checker, var_ctxt->ty);
|
||||||
|
}
|
||||||
|
case PGF_EXPR_TYPED: {
|
||||||
|
PgfExprTyped* etyped = i.data;
|
||||||
|
|
||||||
|
PgfExprTyped* new_etyped =
|
||||||
|
gu_new_variant(PGF_EXPR_TYPED,
|
||||||
|
PgfExprTyped,
|
||||||
|
pe, checker->pool);
|
||||||
|
new_etyped->expr = etyped->expr;
|
||||||
|
new_etyped->type = etyped->type;
|
||||||
|
|
||||||
|
pgf_tc_type(checker, &new_etyped->type);
|
||||||
|
if (!gu_ok(checker->exn))
|
||||||
|
return null_cf_type;
|
||||||
|
|
||||||
|
PgfCFType cf_ty = pgf_ty2cfty(checker, new_etyped->type);
|
||||||
|
|
||||||
|
pgf_tc_expr(checker, ctxt, &new_etyped->expr, cf_ty);
|
||||||
|
return cf_ty;
|
||||||
|
}
|
||||||
|
case PGF_EXPR_IMPL_ARG: {
|
||||||
|
PgfExprImplArg* eimpl = i.data;
|
||||||
|
|
||||||
|
PgfExprImplArg* new_eimpl =
|
||||||
|
gu_new_variant(PGF_EXPR_IMPL_ARG,
|
||||||
|
PgfExprImplArg,
|
||||||
|
pe, checker->pool);
|
||||||
|
new_eimpl->expr = eimpl->expr;
|
||||||
|
return pgf_inf_expr(checker, ctxt, &new_eimpl->expr);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
gu_impossible();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null_cf_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pgf_check_expr(PgfPGF* gr, PgfExpr* pe, PgfType* ty,
|
||||||
|
GuExn* exn, GuPool* pool)
|
||||||
|
{
|
||||||
|
GuPool* tmp_pool = gu_new_pool();
|
||||||
|
|
||||||
|
PgfTypeChecker* checker = gu_new(PgfTypeChecker, tmp_pool);
|
||||||
|
checker->abstr = &gr->abstract;
|
||||||
|
checker->exn = exn;
|
||||||
|
checker->pool = pool;
|
||||||
|
checker->tmp_pool = tmp_pool;
|
||||||
|
|
||||||
|
pgf_tc_expr(checker, NULL, pe, pgf_ty2cfty(checker, ty));
|
||||||
|
|
||||||
|
gu_pool_free(tmp_pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
PgfType*
|
||||||
|
pgf_infer_expr(PgfPGF* gr, PgfExpr* pe,
|
||||||
|
GuExn* exn, GuPool* pool)
|
||||||
|
{
|
||||||
|
GuPool* tmp_pool = gu_new_pool();
|
||||||
|
|
||||||
|
PgfTypeChecker* checker = gu_new(PgfTypeChecker, tmp_pool);
|
||||||
|
checker->abstr = &gr->abstract;
|
||||||
|
checker->exn = exn;
|
||||||
|
checker->pool = pool;
|
||||||
|
checker->tmp_pool = tmp_pool;
|
||||||
|
|
||||||
|
PgfCFType cf_ty = pgf_inf_expr(checker, NULL, pe);
|
||||||
|
|
||||||
|
PgfType* ty = NULL;
|
||||||
|
if (gu_ok(exn)) {
|
||||||
|
ty = pgf_cfty2ty(checker, cf_ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
gu_pool_free(tmp_pool);
|
||||||
|
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pgf_check_type(PgfPGF* gr, PgfType** pty,
|
||||||
|
GuExn* exn, GuPool* pool)
|
||||||
|
{
|
||||||
|
GuPool* tmp_pool = gu_new_pool();
|
||||||
|
|
||||||
|
PgfTypeChecker* checker = gu_new(PgfTypeChecker, tmp_pool);
|
||||||
|
checker->abstr = &gr->abstract;
|
||||||
|
checker->exn = exn;
|
||||||
|
checker->pool = pool;
|
||||||
|
checker->tmp_pool = tmp_pool;
|
||||||
|
|
||||||
|
pgf_tc_type(checker, pty);
|
||||||
|
|
||||||
|
gu_pool_free(tmp_pool);
|
||||||
|
}
|
||||||
@@ -12,6 +12,8 @@ static PyObject* PGFError;
|
|||||||
|
|
||||||
static PyObject* ParseError;
|
static PyObject* ParseError;
|
||||||
|
|
||||||
|
static PyObject* TypeError;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
GuPool* pool;
|
GuPool* pool;
|
||||||
@@ -2036,6 +2038,143 @@ PGF_compute(PGFObject* self, PyObject *args)
|
|||||||
return py_expr;
|
return py_expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ExprObject*
|
||||||
|
PGF_checkExpr(PGFObject* self, PyObject *args)
|
||||||
|
{
|
||||||
|
ExprObject* py_expr = NULL;
|
||||||
|
TypeObject* py_type = NULL;
|
||||||
|
if (!PyArg_ParseTuple(args, "O!O!", &pgf_ExprType, &py_expr, &pgf_TypeType, &py_type))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ExprObject* new_pyexpr = (ExprObject*) pgf_ExprType.tp_alloc(&pgf_ExprType, 0);
|
||||||
|
if (new_pyexpr == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
new_pyexpr->pool = gu_new_pool();
|
||||||
|
new_pyexpr->expr = py_expr->expr;
|
||||||
|
new_pyexpr->master = NULL;
|
||||||
|
|
||||||
|
GuPool* tmp_pool = gu_local_pool();
|
||||||
|
GuExn* exn = gu_new_exn(NULL, gu_kind(type), tmp_pool);
|
||||||
|
|
||||||
|
pgf_check_expr(self->pgf, &new_pyexpr->expr, py_type->type,
|
||||||
|
exn, new_pyexpr->pool);
|
||||||
|
if (!gu_ok(exn)) {
|
||||||
|
if (gu_exn_caught(exn) == gu_type(PgfExn)) {
|
||||||
|
GuString msg = (GuString) gu_exn_caught_data(exn);
|
||||||
|
PyErr_SetString(PGFError, msg);
|
||||||
|
} else if (gu_exn_caught(exn) == gu_type(PgfTypeError)) {
|
||||||
|
GuString msg = (GuString) gu_exn_caught_data(exn);
|
||||||
|
PyErr_SetString(TypeError, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(new_pyexpr);
|
||||||
|
gu_pool_free(tmp_pool);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gu_pool_free(tmp_pool);
|
||||||
|
|
||||||
|
return new_pyexpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
PGF_inferExpr(PGFObject* self, PyObject *args)
|
||||||
|
{
|
||||||
|
ExprObject* py_expr = NULL;
|
||||||
|
if (!PyArg_ParseTuple(args, "O!", &pgf_ExprType, &py_expr))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ExprObject* new_pyexpr = (ExprObject*) pgf_ExprType.tp_alloc(&pgf_ExprType, 0);
|
||||||
|
if (new_pyexpr == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
new_pyexpr->pool = gu_new_pool();
|
||||||
|
new_pyexpr->expr = py_expr->expr;
|
||||||
|
new_pyexpr->master = NULL;
|
||||||
|
|
||||||
|
TypeObject* new_pytype = (TypeObject*) pgf_TypeType.tp_alloc(&pgf_TypeType, 0);
|
||||||
|
if (new_pytype == NULL) {
|
||||||
|
Py_DECREF(new_pyexpr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_pytype->pool = NULL;
|
||||||
|
new_pytype->type = NULL;
|
||||||
|
new_pytype->master = (PyObject*) new_pyexpr;
|
||||||
|
Py_INCREF(new_pyexpr);
|
||||||
|
|
||||||
|
GuPool* tmp_pool = gu_local_pool();
|
||||||
|
GuExn* exn = gu_new_exn(NULL, gu_kind(type), tmp_pool);
|
||||||
|
|
||||||
|
new_pytype->type =
|
||||||
|
pgf_infer_expr(self->pgf, &new_pyexpr->expr,
|
||||||
|
exn, new_pyexpr->pool);
|
||||||
|
if (!gu_ok(exn)) {
|
||||||
|
if (gu_exn_caught(exn) == gu_type(PgfExn)) {
|
||||||
|
GuString msg = (GuString) gu_exn_caught_data(exn);
|
||||||
|
PyErr_SetString(PGFError, msg);
|
||||||
|
} else if (gu_exn_caught(exn) == gu_type(PgfTypeError)) {
|
||||||
|
GuString msg = (GuString) gu_exn_caught_data(exn);
|
||||||
|
PyErr_SetString(TypeError, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(new_pyexpr);
|
||||||
|
Py_DECREF(new_pytype);
|
||||||
|
gu_pool_free(tmp_pool);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gu_pool_free(tmp_pool);
|
||||||
|
|
||||||
|
PyObject* res =
|
||||||
|
Py_BuildValue("OO", new_pyexpr, new_pytype);
|
||||||
|
|
||||||
|
Py_DECREF(new_pyexpr);
|
||||||
|
Py_DECREF(new_pytype);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeObject*
|
||||||
|
PGF_checkType(PGFObject* self, PyObject *args)
|
||||||
|
{
|
||||||
|
TypeObject* py_type = NULL;
|
||||||
|
if (!PyArg_ParseTuple(args, "O!", &pgf_TypeType, &py_type))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
TypeObject* new_pytype = (TypeObject*) pgf_TypeType.tp_alloc(&pgf_TypeType, 0);
|
||||||
|
if (new_pytype == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_pytype->pool = gu_new_pool();
|
||||||
|
new_pytype->type = py_type->type;
|
||||||
|
new_pytype->master = NULL;
|
||||||
|
|
||||||
|
GuPool* tmp_pool = gu_local_pool();
|
||||||
|
GuExn* exn = gu_new_exn(NULL, gu_kind(type), tmp_pool);
|
||||||
|
|
||||||
|
pgf_check_type(self->pgf, &new_pytype->type,
|
||||||
|
exn, new_pytype->pool);
|
||||||
|
if (!gu_ok(exn)) {
|
||||||
|
if (gu_exn_caught(exn) == gu_type(PgfExn)) {
|
||||||
|
GuString msg = (GuString) gu_exn_caught_data(exn);
|
||||||
|
PyErr_SetString(PGFError, msg);
|
||||||
|
} else if (gu_exn_caught(exn) == gu_type(PgfTypeError)) {
|
||||||
|
GuString msg = (GuString) gu_exn_caught_data(exn);
|
||||||
|
PyErr_SetString(TypeError, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
gu_pool_free(tmp_pool);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gu_pool_free(tmp_pool);
|
||||||
|
|
||||||
|
return new_pytype;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
PGF_graphvizAbstractTree(PGFObject* self, PyObject *args) {
|
PGF_graphvizAbstractTree(PGFObject* self, PyObject *args) {
|
||||||
ExprObject* pyexpr;
|
ExprObject* pyexpr;
|
||||||
@@ -2101,6 +2240,15 @@ static PyMethodDef PGF_methods[] = {
|
|||||||
{"compute", (PyCFunction)PGF_compute, METH_VARARGS,
|
{"compute", (PyCFunction)PGF_compute, METH_VARARGS,
|
||||||
"Computes the normal form of an abstract syntax tree"
|
"Computes the normal form of an abstract syntax tree"
|
||||||
},
|
},
|
||||||
|
{"checkExpr", (PyCFunction)PGF_checkExpr, METH_VARARGS,
|
||||||
|
"Type checks an abstract syntax expression and returns the updated expression"
|
||||||
|
},
|
||||||
|
{"inferExpr", (PyCFunction)PGF_inferExpr, METH_VARARGS,
|
||||||
|
"Type checks an abstract syntax expression and returns the updated expression"
|
||||||
|
},
|
||||||
|
{"checkType", (PyCFunction)PGF_checkType, METH_VARARGS,
|
||||||
|
"Type checks an abstract syntax type and returns the updated type"
|
||||||
|
},
|
||||||
{"graphvizAbstractTree", (PyCFunction)PGF_graphvizAbstractTree, METH_VARARGS,
|
{"graphvizAbstractTree", (PyCFunction)PGF_graphvizAbstractTree, METH_VARARGS,
|
||||||
"Renders an abstract syntax tree in a Graphviz format"
|
"Renders an abstract syntax tree in a Graphviz format"
|
||||||
},
|
},
|
||||||
@@ -2289,6 +2437,10 @@ initpgf(void)
|
|||||||
PyModule_AddObject(m, "ParseError", ParseError);
|
PyModule_AddObject(m, "ParseError", ParseError);
|
||||||
Py_INCREF(ParseError);
|
Py_INCREF(ParseError);
|
||||||
|
|
||||||
|
TypeError = PyErr_NewException("pgf.TypeError", NULL, NULL);
|
||||||
|
PyModule_AddObject(m, "TypeError", TypeError);
|
||||||
|
Py_INCREF(TypeError);
|
||||||
|
|
||||||
PyModule_AddObject(m, "Expr", (PyObject *) &pgf_ExprType);
|
PyModule_AddObject(m, "Expr", (PyObject *) &pgf_ExprType);
|
||||||
Py_INCREF(&pgf_ExprType);
|
Py_INCREF(&pgf_ExprType);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ LOCAL_PATH := $(call my-dir)
|
|||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
jni_c_files := jpgf.c
|
jni_c_files := jpgf.c
|
||||||
pgf_c_files := data.c expr.c graphviz.c linearizer.c literals.c parser.c parseval.c pgf.c printer.c reader.c reasoner.c jit.c
|
pgf_c_files := data.c expr.c graphviz.c linearizer.c literals.c parser.c parseval.c pgf.c printer.c reader.c reasoner.c jit.c typechecker.c
|
||||||
gu_c_files := assert.c choice.c exn.c fun.c in.c map.c out.c str.c type.c utf8.c \
|
gu_c_files := assert.c choice.c exn.c fun.c in.c map.c out.c str.c type.c utf8.c \
|
||||||
bits.c defs.c enum.c file.c hash.c mem.c prime.c seq.c string.c ucs.c variant.c
|
bits.c defs.c enum.c file.c hash.c mem.c prime.c seq.c string.c ucs.c variant.c
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user