erl_format.c
来自「OTP是开放电信平台的简称」· C语言 代码 · 共 722 行
C
722 行
/* ``The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the * Erlang Public License along with this software. If not, it can be * retrieved via the world wide web at http://www.erlang.org/. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' * * $Id$ *//* * Function: Provides two primitives: erl_format to build * Erlang terms in an easy way, and erl_match to perform * pattern match similar to what is done in Erlang. * */#include "eidef.h"#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include <ctype.h>#ifdef VRTX#define __READY_EXTENSIONS__#include <errno.h>#endif#include "erl_interface.h"#include "erl_eterm.h"#include "erl_malloc.h"#include "erl_error.h"#include "erl_internal.h"#define ERL_TRUE 1#define ERL_FALSE 0#define ERL_OK 0#define ERL_FORMAT_ERROR -1#define ERL_MAX_ENTRIES 255 /* Max entries in a tuple/list term */#define ERL_MAX_NAME_LENGTH 255 /* Max length of variable names */#define PRINT(t) \{ \ print_term(stderr,t); \ fprintf(stderr,"\n"); \ }typedef struct lvar { ETERM *var; struct lvar *next; } lvar;/* Forward */static ETERM *eformat(char**, va_list*);static int ematch(ETERM*, ETERM*);/* FIXME not thread safe */struct _ef { lvar *chain; /* Chain of local variables */ lvar *idle; /* Idle list of lvar's */} ef;/* Find local variable in term. */static ETERM *find_lvar(char *name){ lvar *tmp=ef.chain; while (tmp != NULL) { if (strcmp(tmp->var->uval.vval.name,name) == 0) return tmp->var->uval.vval.v; tmp = tmp->next; } return (ETERM *) NULL;} /* find_lvar */static void lvar_free(lvar *lv){ lvar *tmp=ef.chain; /* Link in the chain into the idle list */ if (ef.idle == NULL) ef.idle = lv; else { tmp = ef.idle; while (tmp->next != NULL) tmp = tmp->next; tmp->next = lv; } /* Clear out the variable information */ tmp = lv; while (tmp != NULL) { tmp->var = (ETERM *) NULL; tmp = tmp->next; }} /* lvar_free */static lvar *lvar_alloc(void){ lvar *tmp; if ((tmp = ef.idle) == NULL) { tmp = (lvar *) malloc(sizeof(lvar)); /* FIXME check result */ } else { tmp = ef.idle; ef.idle = tmp->next; } return tmp;} /* lvar_alloc */ static void undo_bindings(void){ lvar *tmp=ef.chain; while (tmp != NULL) { erl_free_term(tmp->var->uval.vval.v); tmp->var->uval.vval.v = (ETERM *) NULL; tmp = tmp->next; }} /* undo_bindings */static void release_chain(void){ lvar_free(ef.chain); ef.chain = (lvar *) NULL;} /* release_chain */static void add_lvar(ETERM *t){ lvar *lv; lv = lvar_alloc(); lv->var = t; lv->next = ef.chain; ef.chain = lv;} /* add_lvar */static char *pvariable(char **fmt, char *buf){ char *start=*fmt; char c; int len; while (1) { c = *(*fmt)++; if (isalnum((int) c) || (c == '_')) continue; else break; } (*fmt)--; len = *fmt - start; memcpy(buf, start, len); buf[len] = 0; return buf; } /* pvariable */static char *patom(char **fmt, char *buf){ char *start=*fmt; char c; int len; while (1) { c = *(*fmt)++; if (isalnum((int) c) || (c == '_') || (c == '@')) continue; else break; } (*fmt)--; len = *fmt - start; memcpy(buf, start, len); buf[len] = 0; return buf; } /* patom *//* Check if integer or float */static char *pdigit(char **fmt, char *buf){ char *start=*fmt; char c; int len,dotp=0; while (1) { c = *(*fmt)++; if (isdigit((int) c)) continue; else if (!dotp && (c == '.')) { dotp = 1; continue; } else break; } (*fmt)--; len = *fmt - start; memcpy(buf, start, len); buf[len] = 0; return buf; } /* pdigit */static char *pstring(char **fmt, char *buf){ char *start=++(*fmt); /* skip first quote */ char c; int len; while (1) { c = *(*fmt)++; if (c == '"') { if (*((*fmt)-1) == '\\') continue; else break; } else continue; } len = *fmt - 1 - start; /* skip last quote */ memcpy(buf, start, len); buf[len] = 0; return buf; } /* pstring */static char *pquotedatom(char **fmt, char *buf){ char *start=++(*fmt); /* skip first quote */ char c; int len; while (1) { c = *(*fmt)++; if (c == '\'') { if (*((*fmt)-1) == '\\') continue; else break; } else continue; } len = *fmt - 1 - start; /* skip last quote */ memcpy(buf, start, len); buf[len] = 0; return buf; } /* pquotedatom *//* * The format letters are: * w - Any Erlang term * a - An Atom * s - A String * i - An Integer * f - A Float (double) */static int pformat(char **fmt, va_list *pap, ETERM *v[], int size){ int rc=ERL_OK; /* this next section hacked to remove the va_arg calls */ switch (*(*fmt)++) { case 'w': v[size] = va_arg(*pap, ETERM*); ERL_COUNT(v[size])++; break; case 'a': v[size] = erl_mk_atom(va_arg(*pap, char *)); break; case 's': v[size] = erl_mk_string(va_arg(*pap, char *)); break; case 'i': v[size] = erl_mk_int(va_arg(*pap, int)); break; case 'f': v[size] = erl_mk_float(va_arg(*pap, double)); break; default: rc = ERL_FORMAT_ERROR; break; } return rc;} /* pformat */static int ptuple(char **fmt, va_list *pap, ETERM *v[], int size){ int res=ERL_FORMAT_ERROR; switch (*(*fmt)++) { case '}': res = size; break; case ',': res = ptuple(fmt, pap, v, size); break; case '~': if (pformat(fmt, pap, v, size) == ERL_OK) res = ptuple(fmt, pap, v, ++size); else erl_err_msg("ptuple(1): Wrong format sequence !"); break; case ' ': return ptuple(fmt, pap, v, size); break; default: { (*fmt)--; if ((v[size++] = eformat(fmt, pap)) != (ETERM *) NULL) res = ptuple(fmt, pap, v, size); break; /* if (isupper(**fmt)) { v[size++] = erl_mk_var(pvariable(fmt, wbuf)); res = ptuple(fmt, pap, v, size); } else if ((v[size++] = eformat(fmt, pap)) != (ETERM *) NULL) res = ptuple(fmt, pap, v, size); break; */ } } /* switch */ return res;} /* ptuple */static int plist(char **fmt, va_list *pap, ETERM *v[], int size){ int res=ERL_FORMAT_ERROR; switch (*(*fmt)++) { case ']': res = size; break; case ',': res = plist(fmt, pap, v, size); break; case '~': if (pformat(fmt, pap, v, size) == ERL_OK) res = plist(fmt, pap, v, ++size); else erl_err_msg("plist(1): Wrong format sequence !"); break; case ' ': return plist(fmt, pap, v, size); break; default: { (*fmt)--; if ((v[size++] = eformat(fmt, pap)) != (ETERM *) NULL) res = plist(fmt, pap, v, size); break; /* if (isupper(**fmt)) { v[size++] = erl_mk_var(pvariable(fmt, wbuf)); res = plist(fmt, pap, v, size); } else if ((v[size++] = eformat(fmt, pap)) != (ETERM *) NULL) res = plist(fmt, pap, v, size); break; */ } } /* switch */ return res;} /* plist */static ETERM *eformat(char **fmt, va_list *pap){ int size; ETERM *v[ERL_MAX_ENTRIES],*ep; switch (*(*fmt)++) { case '{': if ((size = ptuple(fmt, pap , v, 0)) != ERL_FORMAT_ERROR) { ep = erl_mk_tuple(v, size); erl_free_array(v, size); return ep; } else return (ETERM *) NULL; break; case '[': if (**fmt == ']') { (*fmt)++; return erl_mk_empty_list(); } else if ((size = plist(fmt, pap , v, 0)) != ERL_FORMAT_ERROR) { ep = erl_mk_list(v, size); erl_free_array(v, size); return ep; } else return (ETERM *) NULL; break; case '$': /* char-value? */ return erl_mk_int((int)(*(*fmt)++)); break; case '~': if (pformat(fmt, pap, v, 0) == ERL_OK) { ep = erl_copy_term(v[0]); erl_free_term(v[0]); return ep; } break; case ' ': return eformat(fmt, pap); break; /* handle negative numbers too... * case '-': * { * ETERM *tmp; * * tmp = eformat(fmt,pap); * if (ERL_IS_INTEGER(tmp)) ERL_INT_VALUE(tmp) = -(ERL_INT_VALUE(tmp)); * return tmp; * } * * * break; */ default: { char wbuf[BUFSIZ]; /* now local to this function for reentrancy */ (*fmt)--; if (islower((int)**fmt)) { /* atom ? */ char *atom=patom(fmt, wbuf); return erl_mk_atom(atom); } else if (isupper((int)**fmt) || (**fmt == '_')) { char *var=pvariable(fmt, wbuf); return erl_mk_var(var); } else if (isdigit((int)**fmt)) { /* integer/float ? */ char *digit=pdigit(fmt, wbuf); if (strchr(digit,(int) '.') == NULL) return erl_mk_int(atoi((const char *) digit)); else return erl_mk_float(atof((const char *) digit)); } else if (**fmt == '"') { /* string ? */ char *string=pstring(fmt, wbuf); return erl_mk_string(string); } else if (**fmt == '\'') { /* quoted atom ? */ char *qatom=pquotedatom(fmt, wbuf); return erl_mk_atom(qatom); } } break; } erl_err_msg("<ERROR> Syntax error in eformat, char was: %c !", **fmt); return (ETERM *) NULL;} /* eformat */ ETERM *erl_format(char *fmt, ... ){ ETERM *res=NULL; va_list ap; va_start(ap, fmt); res = eformat(&fmt, &ap); va_end(ap); return res;} /* erl_format *//* * Perform a pattern match between a pattern p and a term t. * As a side effect bind any unbound variables in p. * Return true or false. */static int ematch(ETERM *p, ETERM *t){ unsigned int type_p; unsigned int type_t; ETERM *tmp; /* two NULLs are equal, one is not... */ if (!p && !t) return ERL_TRUE; if (!p || !t) return ERL_FALSE; /* * ASSERT(p != NULL); * ASSERT(t != NULL); */ type_p = ERL_TYPE(p); type_t = ERL_TYPE(t); if (type_t == ERL_VARIABLE) { if (t->uval.vval.v == NULL) return ERL_FALSE; /* Can't have an unbound variable here ! */ else t = t->uval.vval.v; } if (type_p != ERL_VARIABLE && type_p != type_t) return ERL_FALSE; switch (type_p) { case ERL_ATOM: return p->uval.aval.len == t->uval.aval.len && memcmp(p->uval.aval.a, t->uval.aval.a, p->uval.aval.len) == 0; case ERL_VARIABLE: if (strcmp(p->uval.vval.name, "_") == 0) /* anon. variable */ return ERL_TRUE; else if ((tmp = find_lvar(p->uval.vval.name)) != (ETERM *) NULL) { /* v points to NULL in cases like erl_format("{X,X}") for the second variable */ if (p->uval.vval.v == NULL) p->uval.vval.v = erl_copy_term(tmp); return ematch(p->uval.vval.v, t); } else { /* check if the variable is bound already */ if (p->uval.vval.v != NULL) { if (ematch(p->uval.vval.v, t) == ERL_TRUE ){ add_lvar(p); return ERL_TRUE; } else return ERL_FALSE; } else { p->uval.vval.v = erl_copy_term(t); add_lvar(p); return ERL_TRUE; } } break; case ERL_PID: if ((strcmp(ERL_PID_NODE(p), ERL_PID_NODE(t)) == 0) && (ERL_PID_NUMBER(p) == ERL_PID_NUMBER(t)) && (ERL_PID_SERIAL(p) == ERL_PID_SERIAL(t)) && (ERL_PID_CREATION(p) == ERL_PID_CREATION(t))) return ERL_TRUE; else return ERL_FALSE; break; case ERL_PORT: if ((strcmp(ERL_PORT_NODE(p), ERL_PORT_NODE(t)) == 0) && (ERL_PORT_NUMBER(p) == ERL_PORT_NUMBER(t)) && (ERL_PORT_CREATION(p) == ERL_PORT_CREATION(t))) return ERL_TRUE; else return ERL_FALSE; break; case ERL_REF: { int i, len; if (strcmp(ERL_REF_NODE(p), ERL_REF_NODE(t)) != 0 || ERL_REF_CREATION(p) != ERL_REF_CREATION(t)) return ERL_FALSE; /* FIXME: {len=1, n={42}} and {len=3, n={42, 17, 13}} tests equal. */ len = ERL_REF_LEN(p); if (len > ERL_REF_LEN(t)) len = ERL_REF_LEN(t); for (i = 0; i < len; i++) if (ERL_REF_NUMBERS(p)[i] != ERL_REF_NUMBERS(t)[i]) return ERL_FALSE; return ERL_TRUE; break; } case ERL_EMPTY_LIST: return ERL_TRUE; case ERL_LIST: while (ERL_IS_CONS(p) && ERL_IS_CONS(t)) { if (ematch(p->uval.lval.head, t->uval.lval.head) == ERL_FALSE) return ERL_FALSE; p = p->uval.lval.tail; t = t ->uval.lval.tail; } return ematch(p, t); case ERL_TUPLE: { int i; if (erl_size(p) != erl_size(t)) return ERL_FALSE; else { for(i=0; i<erl_size(p); i++) if (ematch(p->uval.tval.elems[i],t->uval.tval.elems[i]) == ERL_FALSE) return ERL_FALSE; return ERL_TRUE; } } break; case ERL_BINARY: { int i; if ((i = p->uval.bval.size) != t->uval.bval.size) return ERL_FALSE; else return (memcmp(p->uval.bval.b,t->uval.bval.b,i)==0) ? ERL_TRUE : ERL_FALSE; } break; case ERL_INTEGER: return (p->uval.ival.i == t->uval.ival.i) ? ERL_TRUE : ERL_FALSE; break; case ERL_SMALL_BIG: case ERL_U_SMALL_BIG: /* This case can't happend since it is impossible * to create a bignum from the C code. */ return ERL_FALSE; break; case ERL_FLOAT:#if defined(VXWORKS) && CPU == PPC860 { return (erl_fp_compare((unsigned *)&(p->uval.fval.f), (unsigned *)&(t->uval.fval.f)) == 0) ? ERL_TRUE : ERL_FALSE; }#else return (p->uval.fval.f == t->uval.fval.f) ? ERL_TRUE : ERL_FALSE;#endif break; default: return ERL_FALSE; break; } /* erl_err_msg("ematch: Unknown type == %c\n", type_p); */ return ERL_FALSE; } /* ematch */int erl_match(ETERM *p, ETERM *t){ int i; if ((i = ematch(p, t)) == ERL_FALSE) undo_bindings(); release_chain(); return i;} /* erl_match */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?