|
|
Subject: Re: Fix for A=<<1>> - msg#00239
List: lang.erlang.general
You are of course right, but:
Sometimes I and many others do not use spaces to separate the symbols.
The best example I can think of is:
Rec#rec{length=0,state=init,count=0,buf=<<>>}
where I do _not_ want spaces.
Size comparision of binaries I think is so uncommon that the programmer
that thinks "A =< <<1>>" does not write "A=<<<1>>". It is to ambiguous
to the eye. Especially size comparisions of binaries must be _very_
uncommon. The programmer that skips as many spaces between tokens as
possible is begging for trouble.
Equality comparisions of binaries I think is also rather uncommon, but
note that "A==<<1>>" is scanned correctly(tm) and "A==<1>>" gives an
error since scanning of '==' is eager. Remains typing "A=<<<1>>" when
meaning "A==<<1>>", but that is a double fault.
About the code comments: more precisely, which (kind of) comments need
to be fixed?
About including the state in the fun: I guess more/6 could be change
into something like:
more(Cs, Pos, State, Fun) ->
{more,{Cs,Pos,State,Fun}}.
And tokens/3 into:
tokens([], Chars, Pos) ->
tokens({[],Pos,io,
fun (Cs,P,State)->
scan(Cs, [], [] Pos, State})
end, Chars, Pos);
tokens({Cs,Pos,eof,_Fun}, eof, _) ->
{done,{eof,Pos},Cs};
tokens({Cs,Pos,_State,Fun}, eof, _) ->
Fun(Cs++eof, eof);
tokens({Cs,Pos,State,Fun}, Chars, _) ->
Fun(Cs++Chars, State).
note that Cs, Pos, State and Fun are still needed in tokens/3. The fun
can only contain Stack, Tokens and Pos.
Then the calls to more/6 would have to change into:
scan(">"=Cs, Stack, Toks, Pos, State) ->
more(Cs, Pos, State, fun (C, S) ->
scan(C, Stack, Toks, Pos, S)
end);
And these calls are rather many so it will clutter the code compared to:
scan(">"=Cs, Stack, Toks, Pos, State) ->
more(Cs, Stack, Toks, Pos, State, fun scan/5);
By passing two (as far as I can see they are only 2: Stack and Tokens)
unneseccary arguments to more/6 I can use the "fun scan/5" notation
which improves readability.
Or did you have a smarter change in mind?
/ Raimo
Robert Virding wrote:
The problems are at different levels:
1. There is a fundamental difference in scanning atoms/strings and =<<<<<.
You have basically introduced infinite look-ahead (at least to the end of
the file) to try and determine what the first token actually is, for
everything else there is one character look-ahead. (As is the grammar which
is one LALR1)
2. There are at least three different "one key-press typos" which can give
"A=<<1>>":
A==<<1>>
A=<<<1>>
A= <<1>>
Two tests and a match. How can you so so certain what I meant as to choose
one? Introducing a match when I really meant a test will introduce a
fundamental semantic change to the code, both as to return value and to the
error case. Also if I had mistyped the variable name then this *really*
changes the meaning of the code. And it does it automagically, completely
silently and in a way which can make it extremely difficult for the
programmer to find!
3. Nothing should try to correct its input, especially not something as
fundamental as the scanner. Especially when the "correction" is not
unambiguous. Just because you now don't think the chances of meaning
something else is slim doesn't mean other people think the same way. Or that
you might not think so in the future. :-)
4. I personally always (or almost always) use spaces to separate my symbols
so I don't really see what the problem is.
My basic premise is that you can not add an "improver" which works silently
and is only sometimes correct. Will you take the responsibility when this
generates an serious, invisible error in someones code?
The trouble with writing code at this level is that you always have to try
and handle the case when users do things which you had assumed was so stupid
or strange,even though it is legal, that no one would do it, in this case
mean "A=<<<1>>" when they wrote "A=<<1>>". I remember that a relatively
early version of the JAM compiler could not handle the case when you did a
send as an argument to a function to get the message in as an argument,
(foo(X ! <big evaluation>, ...)). The premise was that send is used for side
effects not return values. Of course someone did just this and it crashed.
Robert
P.S. Liked the new code. You need to fix the comments. Can't the fun be
generated each suspend and include the state? I think it would make things
easier.
Was this page helpful?
Thread at a glance:
Previous Message by Date:
click to view message preview
Fw: erl_interface
Heil, Serge Aleynikov
Got the code :
=============== COMM_DRV.CC=========================
#include <vector>
using namespace std;
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#ifndef WIN32
#include <termios.h>
#endif
#include <unistd.h>
extern "C"{
#include "erl_eterm.h"
#include "erl_format.h"
#include "erl_marshal.h"
#include "erl_fix_alloc.h"
#include "erl_malloc.h"
#include "erl_rport.h"
#include "erl_connect.h"
#include "erl_start.h"
#include "erl_resolve.h"
extern void erl_init_nothreads(void *x, long y);
#define erl_init(x,y) erl_init_nothreads(x,y)
}
#include <ei.h>
#pragma pack(1)
#include <erl_driver.h>
#include "comm_drv.h"
typedef ETERM * PETERM;
static void hex_dump(char * buff,int len);
HPORT openport(char * name,int speed)
{
if(!name)
{
#ifdef WIN32
return INVALID_HANDLE_VALUE;
#else
return -1;
#endif
}
#ifdef WIN32 //WIN32 platform
COMMTIMEOUTS t_outs;
COMMCONFIG cfg;
DWORD size;
HANDLE porthandle=CreateFile(name,GENERIC_READ | GENERIC_WRITE,NULL,NULL,
OPEN_EXISTING,NULL,NULL);
if(porthandle==INVALID_HANDLE_VALUE ||
!GetCommConfig(porthandle,&cfg,&size))
{
return INVALID_HANDLE_VALUE;
}
t_outs.ReadIntervalTimeout=1;
t_outs.ReadTotalTimeoutMultiplier=1;
t_outs.ReadTotalTimeoutConstant=1;
t_outs.WriteTotalTimeoutMultiplier=1;
t_outs.WriteTotalTimeoutConstant=1;
cfg.dcb.BaudRate=speed;
cfg.dcb.Parity=NOPARITY;
cfg.dcb.StopBits=ONESTOPBIT;
cfg.dcb.ByteSize=8;
if(!SetCommConfig(porthandle,&cfg,cfg.dwSize) ||
!SetCommTimeouts(porthandle,&t_outs))
{
return INVALID_HANDLE_VALUE;
}
return porthandle;
#else //UNIX (posix) platform
int ispeed;
int porthandle;
switch(speed)
{
case 115200:
ispeed=B115200;
break;
case 57600:
ispeed=B57600;
break;
case 38400:
ispeed=B38400;
break;
case 19200:
ispeed=B19200;
break;
case 9600:
ispeed=B9600;
break;
case 4800:
ispeed=B4800;
break;
case 2400:
ispeed=B2400;
break;
case 1200:
ispeed=B1200;
break;
case 600:
ispeed=B600;
break;
case 300:
ispeed=B300;
break;
default:
return -1;
}
termios Settings;
porthandle=::open(name,O_RDWR | O_NONBLOCK | O_NOCTTY);
if(porthandle == -1)
{
return -1;
}
tcgetattr(porthandle, &Settings);
cfmakeraw(&Settings);
Settings.c_cflag = (CS8 | CREAD | CLOCAL | ispeed);
Settings.c_lflag =0;
//Settings.c_iflag = (IGNCR | IGNBRK | IGNPAR);
Settings.c_iflag = (IGNBRK | IGNPAR);
Settings.c_iflag &= ~INPCK;
tcflush(porthandle,TCIOFLUSH);
if(tcsetattr(porthandle, TCSANOW, &Settings) ==-1)
{
return -1;
}
return porthandle;
#endif
};
void closeport(HPORT porthandle)
{
#ifdef WIN32
if(porthandle!=INVALID_HANDLE_VALUE)CloseHandle(porthandle);
#else
if(porthandle != -1)close(porthandle);
#endif
}
bool writeport(HPORT port,char * buff,int size)
{
int rc;
if(size == 0)return true;
#ifdef WIN32
WriteFile(port,buff,size,(DWORD *)&rc,NULL);
#else
rc=::write(port,buff,size);
#endif
//printf("\noutwrite:%u",rc);
if(rc==size)return true;
if(rc == 0)return false;
if(rc < 0)
{
if(errno == EAGAIN || errno == EINTR)return false;
return false;
}
};
void commstep(HPORT port,ErlDrvPort dport)
{
char buff[512];
int rc;
#ifdef WIN32
if(port ==INVALID_HANDLE_VALUE)return;
bool rc2=ReadFile(port,buff,sizeof(buff),(DWORD *)&rc,NULL);
#else
if(port == -1)return -1;
rc=::read(port,buff,sizeof(buff));
#endif
if(rc == 0)return;
if(rc < 0)
{
if(errno == EAGAIN || errno == EINTR)return;
return;
}
int n=2;
ETERM ** termlist=(ETERM **)new (ETERM *)[n];
ETERM * term_int=erl_mk_int(COMM_READ);
ETERM * term_bin=erl_mk_binary(buff,rc);
termlist[0]=term_int;
termlist[1]=term_bin;
ETERM * term2=erl_mk_list(termlist,n);
int outsize=erl_term_len(term2);
unsigned char * outbuff=new unsigned char[outsize];
erl_encode(term2,outbuff);
driver_output(dport,(char *)outbuff,outsize);
erl_free_term(term2);
erl_free_term(term_int);
erl_free_term(term_bin);
delete[] termlist;
};
#pragma pack()
static void hex_dump(char * buff,int len)
{
for(int i1=0;i1 < len;i1++)
{
char c1=buff[i1];
printf("%2x(%c)",c1,(isprint(c1))?c1:' ');
}
printf("\n");
};
void send_int(ErlDrvPort port,int arg)
{
ETERM * term=erl_mk_int(arg);
int size=erl_term_len(term);
unsigned char * buff=new unsigned char[size];
erl_encode(term,buff);
driver_output(port,(char *)buff,size);
delete[] buff;
erl_free_term(term);
};
typedef struct {
ErlDrvPort port;
HPORT porthandle;
} comm_data;
static ErlDrvData comm_drv_start(ErlDrvPort port, char *buff)
{
erl_init(NULL,0);
comm_data* d = (comm_data*)driver_alloc(sizeof(comm_data));
d->port = port;
return (ErlDrvData)d;
}
static void comm_drv_stop(ErlDrvData handle)
{
driver_free((char*)handle);
}
static void comm_drv_output(ErlDrvData handle, char *buff, int bufflen)
{
ETERM * term1;
ETERM * tport;
ETERM * tspd;
char * portname;
int spd;
ETERM * tbuff;
ETERM * tsize;
char * buff2;
int size2;
unsigned char * outbuff;
int outsize;
comm_data* d = (comm_data*)handle;
char fn = buff[0], arg = buff[1], res;
switch(fn)
{
case COMM_OPEN:
term1=erl_decode((unsigned char *)buff+1);
((unsigned char *)term1));
tport=erl_element(1,term1);
tspd=erl_element(2,term1);
portname=erl_iolist_to_string(tport);
spd=ERL_INT_VALUE(tspd);
d->porthandle=openport(portname,spd);
erl_free(portname);
erl_free_term(tspd);
erl_free_term(tport);
erl_free_term(term1);
res=(d->porthandle > 0)?1:0;
driver_output(d->port, &res, 1);
break;
case COMM_CLOSE:
closeport(d->porthandle);
res=1;
driver_output(d->port, &res, 1);
break;
case COMM_STEP:
commstep(d->porthandle,d->port);
break;
case COMM_WRITE:
tbuff=erl_decode((unsigned char *)buff+1);
buff2=(char *)ERL_BIN_PTR(tbuff);
size2=ERL_BIN_SIZE(tbuff);
res=(writeport(d->porthandle,buff2,size2))?1:0;
erl_free_term(tbuff);
driver_output(d->port, &res, 1);
break;
default:
res=0;
driver_output(d->port, &res, 1);
break;
};
}
ErlDrvEntry comm_driver_entry = {
NULL, /* F_PTR init, N/A */
comm_drv_start, /* L_PTR start, called when port is opened */
comm_drv_stop, /* F_PTR stop, called when port is closed */
comm_drv_output, /* F_PTR output, called when erlang has sent */
NULL, /* F_PTR ready_input, called when input
descriptor ready */
NULL, /* F_PTR ready_output, called when output
descriptor ready */
"comm_drv", /* char *driver_name, the argument to open_port
*/
NULL, /* F_PTR finish, called when unloaded */
NULL, /* F_PTR control, port_command callback */
NULL, /* F_PTR timeout, reserved */
NULL /* F_PTR outputv, reserved */
};
extern "C"{
DRIVER_INIT(comm_drv) /* must match name in driver_entry */
{
return &comm_driver_entry;
}
}
==================COMM_DRV.H====================
#ifndef __erlint_h__
#define __erlint_h__
#include <erl_driver.h>
#define COMM_OPEN 1
#define COMM_CLOSE 2
#define COMM_STEP 3
#define COMM_READ 4
#define COMM_WRITE 5
#define COMM_DATA 6
#define COMM_ERROR 7
#ifdef WIN32
#include <windows.h>
typedef HANDLE HPORT;
#else
typedef int HPORT;
#endif
HPORT openport(char * name,int speed);
void closeport(HPORT port);
bool writeport(HPORT port,char * buff,int size);
void commstep(HPORT port,ErlDrvPort dport);
#endif
=================COMM.HRL====================
-define(COMM_OPEN,1).
-define(COMM_CLOSE,2).
-define(COMM_STEP,3).
-define(COMM_READ,4).
-define(COMM_WRITE,5).
==================COMM.ERL=======================
-module(comm).
-export([start/2,stop/1]).
-export([close/1,open/3,write/2]).
-export([encode/1,init/3,loop/2]).
-compile({inline,30}).
-include("comm.hrl").
call_port(Pid,Msg) ->
Pid ! {call, self(), Msg},
receive
{Pid, Result} ->
Result
end.
encode({comm_open,Name,Speed}) -> [?COMM_OPEN,term_to_binary({Name,Speed})];
encode({comm_close}) -> [?COMM_CLOSE];
encode({comm_step}) -> [?COMM_STEP];
encode({comm_write,Bin}) -> [?COMM_WRITE,term_to_binary(Bin)].
loop(Port,Client) ->
receive
% command interface to COMM_DRV
{call, Caller, Msg} ->
Port ! {self(), {command, encode(Msg)}},
receive
{Port, {data, Data}} ->
Caller ! {self(), Data}
end,
loop(Port,Client);
% periodic poll serial driver
{comm_step,Delay} ->
Port ! {self(), {command, [?COMM_STEP]}},
timer:send_after(Delay,self(),{comm_step,Delay}),
loop(Port,Client);
% data from serial driver
{Port,{data,List}}->
Bin1=list_to_binary(List),
case binary_to_term(Bin1) of
[?COMM_READ,Bin2]->
case catch Client ! {?COMM_READ,Bin2} of
{'EXIT',Reason}->
io:fwrite("~nCLIENT ~w invalid.~w",[Client,Reason]);
_->ok
end,
io:fwrite("~nREAD:~w",[Bin2]);
X ->
io:fwrite("~nREAD?:~w",[X])
end,
loop(Port,Client);
{comm_read,Bin} ->
io:fwrite("~nREAD:~w",[Bin]),
loop(Port,Client);
% stop the driver
stop ->
Port ! {self(), close},
receive
{Port, closed} ->
exit(normal)
end;
% abort in the C++ code
{'EXIT', Port, Reason} ->
io:fwrite("~nEXIT:~w ~n", [Reason]),
exit(port_terminated);
X->
io:fwrite("~nLOOP1:~w",[X]),
loop(Port,Client)
end.
init(SharedLib,Client,Period) ->
Port = open_port({spawn, SharedLib}, []),
timer:send_after(Period,self(),{comm_step,Period}),
loop(Port,Client).
% load and started serial driver with pid Client and integer Period
% Client - pid of receiver of data
% Period - millisecond
start(Client,Period) ->
case erl_ddll:load_driver("./","comm_drv") of
ok -> ok;
{error, already_loaded} -> ok;
{error,A} ->exit({error,A,"comm_drv"})
end,
Pid=spawn(?MODULE, init, ["comm_drv",Client,Period]).
%close serial port
close(Pid) ->
call_port(Pid,{comm_close}).
% open serial port with Name and Speed
% Name - string name of serial port
% Speed - integer speed in baud
open(Pid,Name,Speed) ->
call_port(Pid,{comm_open,Name,Speed}).
write(Pid,Bin) ->
io:fwrite("~nERL:~w",[Bin]),
call_port(Pid,{comm_write,Bin}).
% stop and unload serial driver
stop(Pid) ->
Pid ! {call, self(), {comm_close}},
Pid ! stop,
erl_ddll:unload_driver("comm_drv").
loop2() ->
receive
Data ->
io:fwrite("~n~w",[Data]),
loop2()
end.
start2() ->
Pid=spawn(?MODULE,loop2,[]).
> I wonder if someone could share erl_interface-based examples with me.
> I've been trying to write some code that passes either a tuple/list, or
> an integer from C to Erlang, and I am having a problem that in case of
> integers (see iterate() and finalize() below) Erlangs gets a response
> correctly, but in case of tuple/list (see initialize()), looks like
> Erlang is waiting for something in addition to what the port replies. I
> can see in debugger that the Port sends everything normally, and then
> blocks on the read() function as expected.
>
> Serge
Next Message by Date:
click to view message preview
Re: Web applications unification
On Wed, 28 May 2003, Mikael Karlsson wrote:
>I guess a good starting point for such a framework would be
>that one should implement the generic behaviour API then,
>so that a supervisor can start the application or by using
>application:start.
If you have a runnable application, you need to write a
start/2 function and define it in an .app file. In most
cases, such a start function would call
supervisor:start_link(), and while this is definitely
recommended, it is no requirement.
There are other ways to start an erlang node, e.g. using the
-s flag and then calling application:start() (which assumes
the existence of an .app file), or simply spawning
processes. For a robust framework, we should definitely go
with the OTP way of doing things, since it works well, and
comes with both support and documentation.
>If you provide a library with no processes, like xmerl,
>then there is of course no need for this.
Correct.
>Then you have a choice of using builder and making .app,
>.rel, .script and .boot files or using application:start in
>your own code?
If your applications do not have a start function, using
builder will still give you the benefit of a start script,
always picking the latest version of each application (if
that's what you want, otherwise, a specific version of some
app) and making sure they're in the path, and semi-automatic
configuration of environment variables.
/Uffe
--
Ulf Wiger, Senior Specialist,
/ / / Architecture & Design of Carrier-Class Software
/ / / Strategic Product & System Management
/ / / Ericsson AB, Connectivity and Control Nodes
Previous Message by Thread:
click to view message preview
Re: Fix for A=<<1>>
The problems are at different levels:
1. There is a fundamental difference in scanning atoms/strings and =<<<<<.
You have basically introduced infinite look-ahead (at least to the end of
the file) to try and determine what the first token actually is, for
everything else there is one character look-ahead. (As is the grammar which
is one LALR1)
2. There are at least three different "one key-press typos" which can give
"A=<<1>>":
A==<<1>>
A=<<<1>>
A= <<1>>
Two tests and a match. How can you so so certain what I meant as to choose
one? Introducing a match when I really meant a test will introduce a
fundamental semantic change to the code, both as to return value and to the
error case. Also if I had mistyped the variable name then this *really*
changes the meaning of the code. And it does it automagically, completely
silently and in a way which can make it extremely difficult for the
programmer to find!
3. Nothing should try to correct its input, especially not something as
fundamental as the scanner. Especially when the "correction" is not
unambiguous. Just because you now don't think the chances of meaning
something else is slim doesn't mean other people think the same way. Or that
you might not think so in the future. :-)
4. I personally always (or almost always) use spaces to separate my symbols
so I don't really see what the problem is.
My basic premise is that you can not add an "improver" which works silently
and is only sometimes correct. Will you take the responsibility when this
generates an serious, invisible error in someones code?
The trouble with writing code at this level is that you always have to try
and handle the case when users do things which you had assumed was so stupid
or strange,even though it is legal, that no one would do it, in this case
mean "A=<<<1>>" when they wrote "A=<<1>>". I remember that a relatively
early version of the JAM compiler could not handle the case when you did a
send as an argument to a function to get the message in as an argument,
(foo(X ! <big evaluation>, ...)). The premise was that send is used for side
effects not return values. Of course someone did just this and it crashed.
Robert
P.S. Liked the new code. You need to fix the comments. Can't the fun be
generated each suspend and include the state? I think it would make things
easier.
Next Message by Thread:
click to view message preview
Re: Fix for A=<<1>>
OK, your argument about the typo "A=<<<1>>" when meaning "A=<<1>>" i.e
"A = << 1 >>" accidentally becoming a valid syntax "A =< << 1 >>" has
convinced us to remove this change from erl_scan (at least for R9C).
The debate will probably respawn (reflame) after the R9C release. It is
easier to add later than to remove. The final word is certainly not said.
/ Raimo Niskanen, Erlang/OTP, Ericsson AB
Robert Virding wrote:
The problems are at different levels:
1. There is a fundamental difference in scanning atoms/strings and =<<<<<.
You have basically introduced infinite look-ahead (at least to the end of
the file) to try and determine what the first token actually is, for
everything else there is one character look-ahead. (As is the grammar which
is one LALR1)
2. There are at least three different "one key-press typos" which can give
"A=<<1>>":
A==<<1>>
A=<<<1>>
A= <<1>>
Two tests and a match. How can you so so certain what I meant as to choose
one? Introducing a match when I really meant a test will introduce a
fundamental semantic change to the code, both as to return value and to the
error case. Also if I had mistyped the variable name then this *really*
changes the meaning of the code. And it does it automagically, completely
silently and in a way which can make it extremely difficult for the
programmer to find!
3. Nothing should try to correct its input, especially not something as
fundamental as the scanner. Especially when the "correction" is not
unambiguous. Just because you now don't think the chances of meaning
something else is slim doesn't mean other people think the same way. Or that
you might not think so in the future. :-)
4. I personally always (or almost always) use spaces to separate my symbols
so I don't really see what the problem is.
My basic premise is that you can not add an "improver" which works silently
and is only sometimes correct. Will you take the responsibility when this
generates an serious, invisible error in someones code?
The trouble with writing code at this level is that you always have to try
and handle the case when users do things which you had assumed was so stupid
or strange,even though it is legal, that no one would do it, in this case
mean "A=<<<1>>" when they wrote "A=<<1>>". I remember that a relatively
early version of the JAM compiler could not handle the case when you did a
send as an argument to a function to get the message in as an argument,
(foo(X ! <big evaluation>, ...)). The premise was that send is used for side
effects not return values. Of course someone did just this and it crashed.
Robert
P.S. Liked the new code. You need to fix the comments. Can't the fun be
generated each suspend and include the state? I think it would make things
easier.
|
|