atom_p syntax_set(atom_p args, atom_p env) {
	if ( !(args->array.len == 2 && args->array.ptr[0]->type == T_SYM) )
		return error_atom("set!: need exactly 2 args, first has to be a symbol");
	
	hash_set(env, args->array.ptr[0]->sym, eval(args->array.ptr[1], env));
	return nil_atom;
}
Example #2
0
static void
zmqdrv_error_code(zmq_drv_t *drv, int err)
{
    ErlDrvTermData spec[] =
        {ERL_DRV_ATOM, am_error,
         ERL_DRV_ATOM, error_atom(err),
         ERL_DRV_TUPLE,  2};
    driver_send_term(drv->port, driver_caller(drv->port), spec, sizeof(spec)/sizeof(spec[0]));
}
atom_p syntax_if(atom_p args, atom_p env) {
	if (args->array.len != 3)
		return error_atom("if: needs exactly 3 args");
	
	atom_p cond = eval(args->array.ptr[0], env);
	if (cond->type == T_FALSE || cond->type == T_NIL)
		return eval(args->array.ptr[2], env);
	else
		return eval(args->array.ptr[1], env);
}
atom_p syntax_lambda(atom_p args, atom_p env) {
	if ( !(args->array.len == 2 && args->array.ptr[0]->type == T_ARRAY && args->array.ptr[1]->type == T_ARRAY) )
		return error_atom("lambda: needs exactly 2 args, both have to be arrays");
	
	atom_p atom = gc_alloc(sizeof(atom_t));
	atom->type = T_LAMBDA;
	atom->lambda.args = args->array.ptr[0];
	atom->lambda.body = args->array.ptr[1];
	atom->lambda.env = env;
	
	return atom;
}
Example #5
0
static void
zmqdrv_socket_error(zmq_drv_t *drv, ErlDrvTermData pid, uint32_t idx, int err) {
    // Return {zmq, Socket::integer(), {error, Reason::atom()}}
    ErlDrvTermData spec[] =
        {ERL_DRV_ATOM,  am_zmq,
            ERL_DRV_UINT,  idx,
            ERL_DRV_ATOM,  am_error,
            ERL_DRV_ATOM,  error_atom(err),
            ERL_DRV_TUPLE, 2,
            ERL_DRV_TUPLE, 3};
    driver_send_term(drv->port, pid, spec, sizeof(spec)/sizeof(spec[0]));
}
Example #6
0
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
static void
reply_error(ErlDrvPort port, ErlDrvTermData pid, int err)
{
    // Return {zmq, Socket::integer(), {error, Reason::atom()}}
    ErlDrvTermData spec[] = {
        ERL_DRV_ATOM,   am_zmq_drv,
        ERL_DRV_ATOM,   am_error,
        ERL_DRV_ATOM,   error_atom(err),
        ERL_DRV_TUPLE,  2,
        ERL_DRV_TUPLE,  2
    };
    driver_send_term(port, pid, spec, sizeof(spec)/sizeof(spec[0]));
}
Example #7
0
// send {Ref, {error,Reason}}
int dthread_port_send_error(dthread_t* thr, dthread_t* source, 
			    ErlDrvTermData target,
			    ErlDrvTermData ref, int error)
{
    dterm_t t;
    dterm_mark_t m,e;
    int r;

    dterm_init(&t);
    dterm_tuple_begin(&t, &m); {
	dterm_int(&t, ref);
	dterm_tuple_begin(&t, &e); {
	    dterm_atom(&t, am_error);
	    dterm_atom(&t, error_atom(error));
	}
	dterm_tuple_end(&t,&e);
    }
    dterm_tuple_end(&t,&m);
	
    r = dthread_port_send_term(thr, source, target,
			       dterm_data(&t), dterm_used_size(&t));
    dterm_finish(&t);
    return r;    
}
Example #8
0
static void
zmqdrv_ready_input(ErlDrvData handle, ErlDrvEvent event)
{
    zmq_drv_t *drv = (zmq_drv_t *)handle;

    // Get 0MQ sockets managed by application thread's signaler
    // identified by "event" fd.
    zmq_fd_sockets_map_t::iterator it = drv->zmq_fd_sockets.find((long)event);

    zmqdrv_fprintf("input ready on [idx=%ld]\r\n", (long)event);

    assert(it != drv->zmq_fd_sockets.end());

    zmq_sock_set_t::iterator si = it->second.begin();

    assert(si != it->second.end());

    for (; si != it->second.end(); ++si) {
        zmq_socket_t   s     = (*si)->socket;
        uint32_t       idx   = (*si)->idx;
        ErlDrvTermData owner = (*si)->owner;
        int            rc    = 0;
        uint32_t       events;
        size_t         events_size = sizeof(events);

        zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size);

        while (((*si)->active_mode || (*si)->in_caller) && (events & ZMQ_POLLIN)) {
            msg_t msg;

            rc = zmq_recv(s, &msg, ZMQ_NOBLOCK);

            ErlDrvTermData pid = (*si)->active_mode ? owner : (*si)->in_caller;

            if (rc == -1) {
                if (zmq_errno() != EAGAIN) {
                    ErlDrvTermData spec[] =
                        {ERL_DRV_ATOM,  am_zmq,
                         ERL_DRV_PORT,  driver_mk_port(drv->port),
                         ERL_DRV_UINT,  idx,
                         ERL_DRV_TUPLE, 2,
                         ERL_DRV_ATOM,  am_error,
                         ERL_DRV_ATOM,  error_atom(zmq_errno()),
                         ERL_DRV_TUPLE, 2,
                         ERL_DRV_TUPLE, 3};
                    driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0]));
                    (*si)->in_caller = 0;
                }
                break;
            }

            if ((*si)->active_mode == 1) {

                // Send message {zmq, Socket, binary()} to the owner pid
                ErlDrvTermData spec[] =
                    {ERL_DRV_ATOM,  am_zmq,
                     ERL_DRV_PORT,  driver_mk_port(drv->port),
                     ERL_DRV_UINT,  idx,
                     ERL_DRV_TUPLE, 2,
                     ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg),
                     ERL_DRV_TUPLE, 3};
                driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0]));

            } else if ((*si)->active_mode == 2) {

                // Send message {zmq, Socket, [binary()]} to the owner pid
                // where binary() is each message part
                std::vector<ErlDrvTermData> specv;
                specv.reserve(100);
                std::vector<msg_t*> parts;
                specv.push_back(ERL_DRV_ATOM);
                specv.push_back(am_zmq);
                specv.push_back(ERL_DRV_PORT);
                specv.push_back(driver_mk_port(drv->port));
                specv.push_back(ERL_DRV_UINT);
                specv.push_back(idx);
                specv.push_back(ERL_DRV_TUPLE);
                specv.push_back(2);
                specv.push_back(ERL_DRV_BUF2BINARY);
                specv.push_back((ErlDrvTermData)zmq_msg_data(&msg));
                specv.push_back(zmq_msg_size(&msg));
                int64_t more;
                size_t more_size = sizeof(more);
                int next_count = 0;
                while (true) {
                    zmq_getsockopt(s, ZMQ_RCVMORE, &more, &more_size);
                    if (!more)
                        break;
                    next_count += 1;
                    msg_t *next_part = new msg_t;
                    parts.push_back(next_part);
                    rc = zmq_recv(s, next_part, ZMQ_NOBLOCK);
                    assert (!rc); // FIXME
                    specv.push_back(ERL_DRV_BUF2BINARY);
                    specv.push_back((ErlDrvTermData)zmq_msg_data(next_part));
                    specv.push_back(zmq_msg_size(next_part));
                }
                specv.push_back(ERL_DRV_NIL);
                specv.push_back(ERL_DRV_LIST);
                specv.push_back(next_count + 2);
                specv.push_back(ERL_DRV_TUPLE);
                specv.push_back(3);
                driver_send_term(drv->port, owner, specv.data(), specv.size());
                for (uint i = 0; i < parts.size(); i++) {
                    delete parts[i];
                }

            } else {
                // Return result {ok, binary()} to the waiting caller's pid
                ErlDrvTermData spec[] = 
                    {ERL_DRV_ATOM,   am_zok,
                     ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg),
                     ERL_DRV_TUPLE, 2};
                driver_send_term(drv->port, pid, spec, sizeof(spec)/sizeof(spec[0]));
                (*si)->in_caller = 0;
            }

            // FIXME: add error handling
            zmqdrv_fprintf("received %ld byte message relayed to pid %ld\r\n", zmq_msg_size(&msg), pid);
            zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size);
        }
    
        zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size);

        if ((*si)->out_caller != 0 && (events & ZMQ_POLLOUT)) {
            // There was a pending unwritten message on this socket.
            // Try to write it.  If the write succeeds/fails clear the ZMQ_POLLOUT
            // flag and notify the waiting caller of completion of operation.
            rc = zmq_send(s, &(*si)->out_msg, (*si)->out_flags | ZMQ_NOBLOCK);

            zmqdrv_fprintf("resending message %p (size=%ld) on socket %p (ret=%d)\r\n", 
                zmq_msg_data(&(*si)->out_msg), zmq_msg_size(&(*si)->out_msg), s, rc);

            if (rc == 0) {
                zmq_msg_close(&(*si)->out_msg);
                // Unblock the waiting caller's pid by returning result
                zmqdrv_ok(drv, (*si)->out_caller);
                (*si)->out_caller = 0;
            } else if (zmq_errno() != EAGAIN) {
                // Unblock the waiting caller's pid by returning result
                zmq_msg_close(&(*si)->out_msg);
                zmqdrv_socket_error(drv, (*si)->out_caller, idx, zmq_errno());
                (*si)->out_caller = 0;
            }
        }

        zmqdrv_fprintf("--> socket %p events=%d\r\n", s, events);
    }
}
atom_p builtin_not(atom_p args, atom_p env) {
	if (args->array.len != 1)
		return error_atom("! needs one arg");
	
	return (args->array.ptr[0]->type == T_FALSE || args->array.ptr[0]->type == T_NIL) ? true_atom : false_atom;
}
atom_p builtin_gt(atom_p args, atom_p env) {
	if (args->array.len != 2 || args->array.ptr[0]->type != T_NUM || args->array.ptr[1]->type != T_NUM)
		return error_atom("> needs 2 number args");
	
	return (args->array.ptr[0]->num > args->array.ptr[1]->num) ? true_atom : false_atom;
}
atom_p syntax_quote(atom_p args, atom_p env) {
	if (args->array.len == 1)
		return args->array.ptr[0];
	return error_atom("quote: only supports one arg");
}
/**

nil, true, false, num, str, obj, error → self
sym → env lookup
array → func call
	eval func slot
	func slot == error
		return error
	func slot == builtin
		eval all args
		return error if args contain error
		call builtin with args
		return result
	func slot == syntax
		return error if args contain error
		call builtin with unevaled args
		return result
env → error

**/
atom_p eval(atom_p atom, atom_p env) {
	switch(atom->type) {
		case T_NIL: case T_TRUE: case T_FALSE:
		case T_NUM: case T_STR: case T_OBJ:
		case T_ERROR:
			return atom;
		
		case T_SYM:
			for(atom_p e = env; e != NULL; e = e->obj.parent) {
				atom_p value = hash_get(e, atom->sym);
				if (value)
					return value;
			}
			return nil_atom;
		
		case T_ARRAY:
			{
				if (atom->array.len == 0)
					return error_atom("eval: array needs at least one elment to be evaled");
				
				atom_p func = eval(atom->array.ptr[0], env);
				if (func->type == T_ERROR)
					return func;
				if ( !(func->type == T_BUILTIN || func->type == T_SYNTAX || func->type == T_LAMBDA) )
					return error_atom("eval: only builtin, syntax and lambda atoms are allowed in the function slot");
				
				atom_p args = gc_alloc(sizeof(atom_t));
				args->type = T_ARRAY;
				
				if (func->type == T_BUILTIN) {
					args->array.len = atom->array.len - 1;
					args->array.ptr = gc_alloc(args->array.len * sizeof(atom_p));
					
					for(size_t i = 1; i < atom->array.len; i++) {
						atom_p evaled_arg = eval(atom->array.ptr[i], env);
						if (evaled_arg->type == T_ERROR)
							return evaled_arg;
						args->array.ptr[i - 1] = evaled_arg;
					}
					
					return func->builtin(args, env);
				} else if (func->type == T_SYNTAX) {
					args->array.len = atom->array.len - 1;
					args->array.ptr = atom->array.ptr + 1;
					
					for(size_t i = 0; i < args->array.len; i++) {
						if (args->array.ptr[i]->type == T_ERROR)
							return args->array.ptr[i];
					}
					
					return func->syntax(args, env);
				} else if (func->type == T_LAMBDA) {
					if ( func->lambda.args->array.len != atom->array.len - 1 )
						return error_atom("eval: a lambda needs to be called with exactly as many args as defined");
					
					atom_p lambda_env = gc_alloc_zeroed(sizeof(atom_t));
					lambda_env->type = T_ENV;
					lambda_env->obj.parent = func->lambda.env;
					
					for(size_t i = 1; i < atom->array.len; i++) {
						atom_p arg_name = func->lambda.args->array.ptr[i - 1];
						if (arg_name->type != T_SYM)
							return error_atom("eval: in the definition of an lambda an argument has to be a symbol");
						atom_p evaled_arg = eval(atom->array.ptr[i], env);
						if (evaled_arg->type == T_ERROR)
							return evaled_arg;
						hash_set(lambda_env, arg_name->sym, evaled_arg);
					}
					
					return eval(func->lambda.body, lambda_env);
				}
			}
			return error_atom("eval: unknown atom in function slot");
		
		default:
			return error_atom("eval: got unknown atom");
	}
}
atom_p read(FILE* input) {
	fscanf(input, " ");
	if (feof(input))
		return NULL;
	
	char* str = NULL;
	if ( fscanf(input, "\"%m[^\"]\"", &str) == 1 ) {
		atom_p atom = gc_alloc(sizeof(atom_t));
		atom->type = T_STR;
		atom->str = gc_alloc(strlen(str) + 1);
		strcpy(atom->str, str);
		free(str);
		return atom;
	}
	
	int c = getc(input);
	if ( isdigit(c) ) {
		double value = c - '0';
		while ( (c = getc(input)), c >= '0' && c <= '9' ) {
			value = value * 10;
			value += c - '0';
		}
		
		if (c == '.') {
			double fraction = 0.1;
			while ( (c = getc(input)), c >= '0' && c <= '9' ) {
				value += (c - '0') * fraction;
				fraction /= 10;
			}
		}
		
		ungetc(c, input);
		
		atom_p atom = gc_alloc(sizeof(atom_t));
		atom->type = T_NUM;
		atom->num = value;
		return atom;
	} else if (c == '(') {
		atom_p atom = gc_alloc(sizeof(atom_t));
		atom->type = T_ARRAY;
		atom->array.len = 0;
		atom->array.ptr = NULL;
		
		int c = 0;
		while (true) {
			fscanf(input, " ");
			if ( (c = getc(input)) == ')' )
				return atom;
			ungetc(c, input);
			
			atom_p item = read(input);
			if (item == NULL)
				return error_atom("unexpected EOF while reading array elements");
			
			atom->array.ptr = gc_realloc(atom->array.ptr, atom->array.len * sizeof(atom->array.ptr[0]), (atom->array.len + 1) * sizeof(atom->array.ptr[0]));
			atom->array.len += 1;
			atom->array.ptr[atom->array.len - 1] = item;
		}
	} else if (c == '{') {
		atom_p atom = gc_alloc_zeroed(sizeof(atom_t));
		atom->type = T_OBJ;
		
		char c = 0;
		fscanf(input, " %c", &c);
		if (c == '}')
			return atom;
		ungetc(c, input);
		
		do {
			char* key = NULL;
			if ( fscanf(input, " %m[^:{}() \f\n\r\t\v]", &key) != 1 )
				return error_atom("expected object property name after '{' or ','");
			
			fscanf(input, " %c", &c);
			if (c != ':')
				return error_atom("expected ':' after object property name");
			
			atom_p value = read(input);
			hash_set(atom, key, value);
		} while( fscanf(input, " %c", &c) == 1 && c == ',' );
		
		if (c != '}')
			return error_atom("expected '}' or ',' after object property value");
		return atom;
	} else {
		ungetc(c, input);
	}
	
	if ( fscanf(input, " %m[^:{}() \f\n\r\t\v]", &str) == 1 ) {
		atom_p atom = NULL;
		
		if ( strcmp(str, "nil") == 0 ) {
			atom = nil_atom;
			free(str);
		} else if ( strcmp(str, "true") == 0 ) {
			atom = true_atom;
			free(str);
		} else if ( strcmp(str, "false") == 0 ) {
			atom = false_atom;
			free(str);
		} else {
			atom = gc_alloc_uncollected(sizeof(atom_t));
			atom->type = T_SYM;
			atom->sym = gc_alloc(strlen(str) + 1);
			strcpy(atom->sym, str);
			free(str);
		}
		
		return atom;
	}
	
	fscanf(input, "%*[^\n]");
	return error_atom("syntax error, ignoring rest of line");
}
Example #14
0
static void
zmqdrv_ready_input(ErlDrvData handle, ErlDrvEvent event)
{
    zmq_drv_t *drv = (zmq_drv_t *)handle;

    // Get 0MQ sockets managed by application thread's signaler
    // identified by "event" fd.
    zmq_fd_sockets_map_t::iterator it = drv->zmq_fd_sockets.find((long)event);

    zmqdrv_fprintf("input ready on [idx=%ld]\r\n", (long)event);

    assert(it != drv->zmq_fd_sockets.end());

    zmq_sock_set_t::iterator si = it->second.begin();

    assert(si != it->second.end());

    for (; si != it->second.end(); ++si) {
        zmq_socket_t   s     = (*si)->socket;
        uint32_t       idx   = (*si)->idx;
        ErlDrvTermData owner = (*si)->owner;
        int            rc    = 0;
        uint32_t       events;
        size_t         events_size = sizeof(events);

        zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size);

        while (((*si)->active_mode || (*si)->in_caller) && (events & ZMQ_POLLIN)) {
            msg_t msg;

            rc = zmq_recv(s, &msg, ZMQ_NOBLOCK);

            ErlDrvTermData pid = (*si)->active_mode ? owner : (*si)->in_caller;

            if (rc == -1) {
                if (zmq_errno() != EAGAIN) {
                    ErlDrvTermData spec[] =
                        {ERL_DRV_ATOM,  am_zmq,
                         ERL_DRV_UINT,  idx,
                         ERL_DRV_ATOM,  error_atom(zmq_errno()),
                         ERL_DRV_TUPLE, 2,
                         ERL_DRV_TUPLE, 3};
                    driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0]));
                    (*si)->in_caller = 0;
                }
                break;
            }

            if ((*si)->active_mode) {
                // Send message {zmq, Socket, binary()} to the owner pid
                ErlDrvTermData spec[] =
                    {ERL_DRV_ATOM,  am_zmq,
                     ERL_DRV_UINT,  idx,
                     ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg),
                     ERL_DRV_TUPLE, 3};
                driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0]));
            } else {
                // Return result {ok, binary()} to the waiting caller's pid
                ErlDrvTermData spec[] = 
                    {ERL_DRV_ATOM,   am_zok,
                     ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg),
                     ERL_DRV_TUPLE, 2};
                driver_send_term(drv->port, pid, spec, sizeof(spec)/sizeof(spec[0]));
                (*si)->in_caller = 0;
            }

            // FIXME: add error handling
            zmqdrv_fprintf("received %ld byte message relayed to pid %ld\r\n", zmq_msg_size(&msg), pid);
            zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size);
        }
    
        zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size);

        if ((*si)->out_caller != 0 && (events & ZMQ_POLLOUT)) {
            // There was a pending unwritten message on this socket.
            // Try to write it.  If the write succeeds/fails clear the ZMQ_POLLOUT
            // flag and notify the waiting caller of completion of operation.
            rc = zmq_send(s, &(*si)->out_msg, (*si)->out_flags | ZMQ_NOBLOCK);

            zmqdrv_fprintf("resending message %p (size=%ld) on socket %p (ret=%d)\r\n", 
                zmq_msg_data(&(*si)->out_msg), zmq_msg_size(&(*si)->out_msg), s, rc);

            if (rc == 0) {
                zmq_msg_close(&(*si)->out_msg);
                // Unblock the waiting caller's pid by returning result
                zmqdrv_ok(drv, (*si)->out_caller);
                (*si)->out_caller = 0;
            } else if (zmq_errno() != EAGAIN) {
                // Unblock the waiting caller's pid by returning result
                zmq_msg_close(&(*si)->out_msg);
                zmqdrv_socket_error(drv, (*si)->out_caller, idx, zmq_errno());
                (*si)->out_caller = 0;
            }
        }

        zmqdrv_fprintf("--> socket %p events=%d\r\n", s, events);
    }
}