1
0
mirror of https://github.com/ubf/ubf.git synced 2026-04-16 17:55:48 +00:00

Align UBF's predefined and builtin types with EEP 8

This commit is contained in:
Joseph Wayne Norton
2012-09-02 22:10:49 +09:00
parent e61261cdc9
commit fe276dbc3f
25 changed files with 385 additions and 296 deletions

View File

@@ -32,24 +32,24 @@
%%% Macros
%%%-------------------------------------------------------------------
%% @doc ubf string helper
-define(S(X),
#'#S'{value=X}).
%% @doc ubf proplist helper
-define(P(X),
#'#P'{value=X}).
%% @doc ubf string helper
-define(S(X),
#'#S'{value=X}).
%%%-------------------------------------------------------------------
%%% Records
%%%-------------------------------------------------------------------
%% @doc ubf string record
-record('#S',
{value="" :: string()}).
%% @doc ubf proplist record
-record('#P',
{value=[] :: [{term(),term()}]}).
%% @doc ubf string record
-record('#S',
{value="" :: string()}).
-endif. % -ifndef(ubf)

View File

@@ -1,6 +1,6 @@
+NAME("irc").
+VSN("ubf1.0").
+VSN("ubf2.0").
+STATE start
logon() => proceed() & active. %% Nick randomly assigned
@@ -18,8 +18,8 @@
EVENT => changeNameEvent(). %% Nick changes name
+ANYSTATE
info() => string();
description() => string();
info() => ubfstring();
description() => ubfstring();
contract() => term().

View File

@@ -1,6 +1,6 @@
+NAME("irc").
+VSN("ubf1.0").
+VSN("ubf2.0").
+TYPES
info() :: info;
@@ -9,10 +9,10 @@ contract() :: contract;
ok() :: ok;
bool() :: true | false;
nick() :: string();
nick() :: ubfstring();
oldnick() :: nick();
newnick() :: nick();
group() :: string();
group() :: ubfstring();
groups() :: [group()];
logon() :: logon;
@@ -21,9 +21,9 @@ listGroups() :: groups;
joinGroup() :: {join, group()};
leaveGroup() :: {leave, group()};
changeNick() :: {nick, nick()};
msg() :: {msg, group(), string()};
msg() :: {msg, group(), ubfstring()};
msgEvent() :: {msg, nick(), group(), string()};
msgEvent() :: {msg, nick(), group(), ubfstring()};
joinEvent() :: {joins, nick(), group()};
leaveEvent() :: {leaves, nick(), group()};
changeNameEvent() :: {changesName, oldnick(), newnick(), group()}.
@@ -44,8 +44,8 @@ changeNameEvent() :: {changesName, oldnick(), newnick(), group()}.
EVENT => changeNameEvent(). %% Nick changes name
+ANYSTATE
info() => string();
description() => string();
info() => ubfstring();
description() => ubfstring();
contract() => term().

View File

@@ -1,6 +1,6 @@
+NAME("irc_types").
+VSN("ubf1.0").
+VSN("ubf2.0").
+TYPES
info() :: info;
@@ -9,10 +9,10 @@ contract() :: contract;
ok() :: ok;
bool() :: true | false;
nick() :: string();
nick() :: ubfstring();
oldnick() :: nick();
newnick() :: nick();
group() :: string();
group() :: ubfstring();
groups() :: [group()];
logon() :: logon;
@@ -21,9 +21,9 @@ listGroups() :: groups;
joinGroup() :: {join, group()};
leaveGroup() :: {leave, group()};
changeNick() :: {nick, nick()};
msg() :: {msg, group(), string()};
msg() :: {msg, group(), ubfstring()};
msgEvent() :: {msg, nick(), group(), string()};
msgEvent() :: {msg, nick(), group(), ubfstring()};
joinEvent() :: {joins, nick(), group()};
leaveEvent() :: {leaves, nick(), group()};
changeNameEvent() :: {changesName, oldnick(), newnick(), group()}.

View File

@@ -109,23 +109,23 @@ STRING = DQUOTE *(%x20-21 / %x23-7E) DQUOTE
NONEMTPYSTRING = DQUOTE 1*(%x20-21 / %x23-7E) DQUOTE
predefinedtype = ('atom' "(" [atomattrs] ")")
/ ('boolean' "(" [booleanattrs] ")")
predefinedtype = ('any' "(" [anyattrs] ")")
/ ('none' "(" [noneattrs] ")")
/ ('atom' "(" [atomattrs] ")")
/ ('binary' "(" [binaryattrs] ")")
/ ('float' "(" [floatattrs] ")")
/ ('integer' "(" [integerattrs] ")")
/ ('list' "(" [listattrs] ")")
/ ('proplist' "(" [proplistattrs] ")")
/ ('string' "(" [stringattrs] ")")
/ ('term' "(" [termattrs] ")")
/ ('tuple' "(" [tupleattrs] ")")
/ ('none' "(" [noneattrs] ")")
anyattrs = anyattr
/ (anyattr *WSP "," *WSP anyattrs)
noneattrs = *WSP
atomattrs = atomattr
/ (atomattr *WSP "," *WSP atomattrs)
booleanattrs = *WSP
binaryattrs = binaryattr
/ (binaryattr *WSP "," *WSP binaryattrs)
@@ -136,24 +136,11 @@ integerattrs = *WSP
listattrs = listattr
/ (listattr *WSP "," *WSP listattrs)
proplistattrs = proplistattr
/ (proplistattr *WSP "," *WSP proplistattrs)
stringattrs = stringattr
/ (stringattr *WSP "," *WSP stringattrs)
termattrs = termattr
/ (termattr *WSP "," *WSP termattrs)
tupleattrs = tupleattr
/ (tupleattr *WSP "," *WSP tupleattrs)
noneattrs = *WSP
anyattr = 'nonempty' / 'nonundefined'
atomattr = 'ascii' / 'asciiprintable' / 'nonempty' / 'nonundefined'
binaryattr = 'ascii' / 'asciiprintable' / 'nonempty'
listattr = 'nonempty'
proplistattr = 'nonempty'
stringattr = 'ascii' / 'asciiprintable' / 'nonempty'
termattr = 'nonempty' / 'nonundefined'
tupleattr = 'nonempty' / 'nonundefined'

View File

@@ -9,15 +9,16 @@
%% Required (except keepalive/0) callback API for UBF stateless
%% implementations.
-export([info/0, description/0, keepalive/0]).
-export([moduleStart/1, moduleRestart/1]).
-export([handlerStart/1, handlerStop/3, handlerRpc/1, handlerEvent/1]).
-import(ubf_plugin_handler, [sendEvent/2, install_handler/2]).
-compile({parse_transform,contract_parser}).
-add_contract("ubf_bertrpc_plugin").
-add_contract("src/ubf_bertrpc_plugin").
-include("ubf.hrl").
-include("ubf_plugin_stateless.hrl").
-include_lib("ubf/include/ubf.hrl").
-include_lib("ubf/include/ubf_plugin_stateless.hrl").
info() ->
"I am a BERT-RPC server".
@@ -28,6 +29,14 @@ description() ->
keepalive() ->
ok.
%% @doc start module
moduleStart(_Args) ->
unused.
%% @doc restart module
moduleRestart(Args) ->
moduleStart(Args).
%% @doc start handler
handlerStart(_Args) ->
ack = install_handler(self(), fun handlerEvent/1),

View File

@@ -1,9 +1,9 @@
// -*- Doc -*-
// vim: set syntax=asciidoc:
= UBF User's Guide _1st DRAFT_
:Date: 2012/08/02
:Revision: 0.3.7
= UBF User's Guide
:Date: 2012/09/02
:Revision: 1.0.0
:Copyright: 2012 by Joseph Wayne Norton <norton@alum.mit.edu>
:Copyright: 2010-2011 Gemini Mobile Technologies, Inc. All rights reserved.
:Copyright: 2002 Joe Armstrong
@@ -299,14 +299,14 @@ The version of the contract is specified as a double-quoted string.
==== Types: +TYPES.
The UBF(b) type system has user-defined types and predefined types.
User-defined types and predefined types are either primitive types or
complex types.
The UBF(b) type system has user-defined types, builtin types, and
predefined types. All types are either primitive types or complex
types.
The primitive types are Integer, Range, Float, Binary, String, Atom,
and Reference. The complex types are Alternative, Tuple, Record,
Extended Record, and List. User-defined "complex types" are defined
recursively.
Extended Record, and List. Builtin and User-defined "complex types"
are defined recursively.
===== Definition: X() :: T
@@ -402,6 +402,50 @@ A type [X1, X2, ..., Xn] is of type [T] if all of Xi are of type T.
Unbounded, bounded, left unbounded, and right unbounded lists are
supported.
===== Builtin: B()
Builtin types are referenced by the notation:
------
B()
------
The name of the builtin type is 'B' and builtin types are of 2
categories: +Erlang+ and +UBF+. The names used for builtin types are
reserved and cannot be used for user-defined types.
Erlang builtin types are a subset of the types defined by EEP8
<<EEP8>>. EEP8 types are used for authoring Erlang +types+ and
+specs+ definitions.
|============
| nil() | []
| term() | any()
| boolean() | 'false' | 'true'
| byte() | 0..255
| char() | 0..16#10ffff
| non_neg_integer() | 0..
| pos_integer() | 1..
| neg_integer() | ..-1
| number() | integer() | float()
| string() | [char()]
| nonempty_string() | [char()]+
| module() | atom()
| mfa() | {atom(), atom(), byte()}
| node() | atom()
| timeout() | 'infinity' | non_neg_integer()
| no_return() | none()
|
|============
UBF builtin types are types particular to UBF that can be helpful for
encoding and decoding proplists and strings.
|============
| ubfproplist() | {'#P', [{term(), term()}]}
| ubfstring() | {'#S', [byte()]}
|============
===== Predefined: P() _or_ P(A1, A2, ..., An)
Predefined types are referenced by the notation:
@@ -416,57 +460,48 @@ or by the notation:
P(A1, A2, ..., An)
------
The name of the predefined type is 'P'. Using the second notation,
attributes can be specified to make the predefined type less general
and thus more specific when matching objects.
The name of the predefined type is 'P'. The names used for predefined
types are reserved and cannot be used for user-defined types.
Using the second notation, attributes can be specified to make the
predefined type less general and thus more specific when matching
objects.
|============
| | ascii | asciiprintable | nonempty | nonundefined
| any | X | X | O | O
| none | X | X | X | X
| integer | X | X | X | X
| float | X | X | X | X
| binary | O | O | O | X
| string | O | O | O | X
| atom | O | O | O | O
| boolean | X | X | X | X
| tuple | X | X | O | X
| list | X | X | O | X
| proplist | X | X | O | X
| term | X | X | O | O
| none | X | X | X | X
|============
The above table summarizes the set of supported predefined types and
their respective optional attributes.
The "integer", "float", "binary", "string", "atom", "boolean",
"tuple", and "list" predefined types match directly to the
corresponding primitive or complex type.
The "term" predefined type matches any object.
The "proplist" predefined type is a specialized version of the "list"
predefined type that matches the following types:
------
[{term(), term()}]
------
The "any" predefined type matches any object.
The "none" predefined type is a placeholder to describe the return
value of a function call that does not return to the caller.
The "ascii" attribute permits matches with binaries, strings, and
atoms containing only ASCII values <<RFC20>>. Similarly, the
The "integer", "float", "binary", "atom", "boolean", "tuple", and
"list" predefined types match directly to the corresponding primitive
or complex type.
The "ascii" attribute permits matches with binaries and atoms
containing only ASCII values <<RFC20>>. Similarly, the
"asciiprintable" attribute permits matches with only printable ASCII
values.
The "nonempty" attribute permits matches with binaries, strings,
atoms, tuples, lists, proplists, and terms that are of length greater
than zero. The following objects would not be matched with the
"nonempty" attribute:
The "nonempty" attribute permits matches with binaries, atoms, tuples,
lists, and any that are of length greater than zero. The following
objects would not be matched with the "nonempty" attribute:
------
<<"">>
""
''
{}
[]
@@ -995,8 +1030,7 @@ follows:
{\'proto', {\'ubf' | \'ebf' | \'jsf' | \'tbf' | \'ftbf' | atom()}}::
Enable the UBF, EBF, JSF, TBF, FTBF, or an alternative protocol wire
format. Default: \'ubf'.
{\'proto', {\'ubf' | \'ebf' | \'jsf' | \'tbf' | \'ftbf' | atom(), proplist()}}::
{\'proto', {\'ubf' | \'ebf' | \'jsf' | \'tbf' | \'ftbf' | atom(), [atom() | tuple()]}}::
Enable the UBF, EBF, JSF, TBF, FTBF, or an alternative protocol wire
format with options. Default: {\'ubf', []}. Supported options:
\'safe';;
@@ -1015,7 +1049,7 @@ follows:
Set the starting plugin, set after a client first connects to the
server. If not set, client may select the service using the
startSession() API. There is no default setting.
{\'serverhello', string() | \'undefined'}::
{\'serverhello', ubfstring() | \'undefined'}::
Meta contract greeting string, sent when a client first connects to
the server. If \'undefined', server hello is not sent to the
client. Default: "meta_server".
@@ -1063,8 +1097,8 @@ are mandatory for all stateless plugins.
------
%% common callback API
-spec info() -> string().
-spec description() -> string().
-spec info() -> ubfstring().
-spec description() -> ubfstring().
-spec handlerStop(Handler::pid(), Reason::term(), StateData::term()) ->
NewStateData::term().
@@ -1128,10 +1162,10 @@ The above example also introduces three new concepts:
- The "?S(X)" macro definition plus other helpers are located in the
"ubf.hrl" Erlang header file. For Erlang, the implementation of a
UBF string is a two tuple having \'#S' as the first element and a
list of integers as the second element. A similar technique is also
used for the implementation of a UBF proplist (i.e. \'#P' and
"?P(X)).
UBF +ubfstring()+ is a two tuple having \'#S' as the first element
and a list of bytes as the second element. A similar technique is
also used for the implementation of a UBF +ubfproplist()+
(i.e. \'#P' and "?P(X)).
=== Stateful
@@ -1146,8 +1180,8 @@ mandatory for all stateful plugins.
------
%% common callback API
-spec info() -> string().
-spec description() -> string().
-spec info() -> ubfstring().
-spec description() -> ubfstring().
-spec handlerStop(Handler::pid(), Reason::term(), ManagerData::term()) ->
NewManagerData::term().
@@ -1257,8 +1291,37 @@ known) is returned. For TCP/IP transports, the default method is to
connect to the specified host() and TCP ipport(). For the ETF
transport, the alternative method is to connect to server() using the
specified plugins(). The server() is either the process id or process
registered name for an already-started UBF server. See <<Servers>>
for a description of options().
registered name for an already-started UBF server.
The list of supported options() are as follows:
{\'clientport', ipport() | {ipport(), ipport()}}::
Specifies the TCP port to be used by the client. If tuple format, a
port is automatically selected within the specifed range. If
\'undefined', a random port is automatically selected. Default:
\'undefined'.
{\'proto', {\'ubf' | \'ebf' | \'jsf' | \'tbf' | \'ftbf' | atom()}}::
Enable the UBF, EBF, JSF, TBF, FTBF, or an alternative protocol wire
format. Default: \'ubf'.
{\'proto', {\'ubf' | \'ebf' | \'jsf' | \'tbf' | \'ftbf' | atom(), [atom() | tuple()]}}::
Enable the UBF, EBF, JSF, TBF, FTBF, or an alternative protocol wire
format with options. Default: {\'ubf', []}. Supported options:
\'safe';;
Prevents decoding data that may be used to attack the Erlang
system. In the event of receiving unsafe data, decoding fails
with a badarg error.
{\'startplugin', module()}::
Set the starting plugin, set after a client first connects to the
server. If not set, client's caller may select the service using
the startSession() API. Default: \'undefined'.
{\'serverhello', true | 'undefined'}::
Meta contract greeting string, sent to a client when it first connects to
the server. If \'undefined', client does not expect server hello to
be sent by the server. Default: \'true'.
{\'simplerpc', boolean()}::
Set the simple RPC mode. If \'true', client expects only the rpc
reply from the server. If \'false', server returns the rpc reply and next
state to client. Default: \'false'.
The rpc/{2,3} functions make a synchronous call to the server.
@@ -1571,8 +1634,8 @@ created by making a symlink to the lang-en.conf file.
------
diff -r -u 8.6.4-orig/bin/a2x.py 8.6.4/bin/a2x.py
--- 8.6.4-orig/bin/a2x.py 2011-04-24 00:50:26.000000000 +0900
+++ 8.6.4/bin/a2x.py 2011-04-24 00:35:55.000000000 +0900
--- 8.6.4-orig/bin/a2x.py 2011-04-24 00:50:26.000000000 +0900
+++ 8.6.4/bin/a2x.py 2011-04-24 00:35:55.000000000 +0900
@@ -156,7 +156,10 @@
def shell_copy(src, dst):
verbose('copying "%s" to "%s"' % (src,dst))
@@ -1675,6 +1738,9 @@ _Under Construction - To Be Updated_
- [[[ERLANG]]] "A general-purpose programming language and runtime
environment", http://www.erlang.org.
- [[[EEP8]]] "EEP 8: Types and function specifications",
http://www.erlang.org/eeps/eep-0008.html.
- [[[GIT]]] "Fast Version Control System", http://git-scm.com.
- [[[JSFCHARSET]]] "Gemini Mobile Technologies, Inc. charset module",

View File

@@ -14,6 +14,8 @@
{erl_first_files, ["src/contract_lex.erl"
, "src/contract_yecc.erl"
, "src/ubf_utils.erl"
, "src/contract_parser.erl"
, "src/ubf_types_builtin.erl"
]}.
%% Erlang compiler options

View File

@@ -37,7 +37,9 @@
-include("ubf_impl.hrl").
-export([parse_transform/2,
make/0, make_lex/0, make_yecc/0, preDefinedTypes/0, preDefinedTypesWithoutAttrs/0, preDefinedTypesWithAttrs/0,
make/0, make_lex/0, make_yecc/0,
preDefinedTypes/0, preDefinedTypesWithoutAttrs/0, preDefinedTypesWithAttrs/0,
builtInTypes/0, builtInTypesErlang/0, builtInTypesUBF/0,
tags/1, tags/2,
parse_transform_contract/2,
parse_file/1
@@ -237,15 +239,19 @@ tags(P1, Imports) ->
end
end.
preDefinedTypes() -> preDefinedTypesWithoutAttrs() ++ preDefinedTypesWithAttrs().
preDefinedTypes() ->
preDefinedTypesWithoutAttrs() ++ preDefinedTypesWithAttrs().
preDefinedTypesWithoutAttrs() ->
[atom, boolean, binary, float, integer, list, proplist, string, term, tuple, none].
[any, none, atom, binary, float, integer, list, tuple].
preDefinedTypesWithAttrs() ->
[
%% any
{any,[nonempty]}, {any,[nonundefined]}
, {any,[nonempty,nonundefined]}
%% atom
{atom,[ascii]}, {atom,[asciiprintable]}, {atom,[nonempty]}, {atom,[nonundefined]}
, {atom,[ascii]}, {atom,[asciiprintable]}, {atom,[nonempty]}, {atom,[nonundefined]}
, {atom,[ascii,nonempty]}, {atom,[ascii,nonundefined]}, {atom,[asciiprintable,nonempty]}, {atom,[asciiprintable,nonundefined]}
, {atom,[ascii,nonempty,nonundefined]}, {atom,[asciiprintable,nonempty,nonundefined]}
, {atom,[nonempty,nonundefined]}
@@ -254,18 +260,19 @@ preDefinedTypesWithAttrs() ->
, {binary,[ascii,nonempty]}, {binary,[asciiprintable,nonempty]}
%% list
, {list,[nonempty]}
%% proplist
, {proplist,[nonempty]}
%% string
, {string,[ascii]}, {string,[asciiprintable]}, {string,[nonempty]}
, {string,[ascii,nonempty]}, {string,[asciiprintable,nonempty]}
%% term
, {term,[nonempty]}, {term,[nonundefined]}
, {term,[nonempty,nonundefined]}
%% tuple
, {tuple,[nonempty]}
].
builtInTypes() ->
builtInTypesErlang() ++ builtInTypesUBF().
builtInTypesErlang() ->
[nil, term, boolean, byte, char, non_neg_integer, pos_integer, neg_integer, number, string, nonempty_string, module, mfa, node, timeout, no_return].
builtInTypesUBF() ->
[ubfproplist, ubfstring].
pass2(P, Imports) ->
Name = require(one, name, P),
Vsn = require(one, vsn, P),
@@ -273,6 +280,8 @@ pass2(P, Imports) ->
Any = require(zero_or_one, anystate, P),
Trans = require(many, transition, P),
AllImports = if Name =/= "ubf_types_builtin" -> [ubf_types_builtin|Imports]; true -> Imports end,
ImportTypes = lists:flatten(
[
begin
@@ -294,7 +303,7 @@ pass2(P, Imports) ->
[ begin {TDef, TTag} = Mod:contract_type(T), {T, TDef, TTag} end
|| T <- TL ]
end
|| Import <- Imports ]
|| Import <- AllImports ]
),
ImportTypeNames = [ T || {T, _, _} <- ImportTypes ],

View File

@@ -188,11 +188,11 @@ rec_ext(Name, Args) ->
if Types == [] ->
[{atom,Name},
{atom,undefined},
{predef,term}];
{predef,any}];
true ->
[{atom,Name},
eor({atom,undefined}, {tuple, [ {atom,X} || X <- Fields ]}),
{predef,term}|Types]
{predef,any}|Types]
end),
{record_ext, Name, Fields, Defaults, Types1}.

View File

@@ -89,6 +89,8 @@ checkEventIn(Msg, ThisState, Mod) ->
%%----------------------------------------------------------------------
%% Check type attribute
isTypeAttr(any,nonempty) -> true;
isTypeAttr(any,nonundefined) -> true;
isTypeAttr(atom,ascii) -> true;
isTypeAttr(atom,asciiprintable) -> true;
isTypeAttr(atom,nonempty) -> true;
@@ -97,12 +99,6 @@ isTypeAttr(binary,ascii) -> true;
isTypeAttr(binary,asciiprintable) -> true;
isTypeAttr(binary,nonempty) -> true;
isTypeAttr(list,nonempty) -> true;
isTypeAttr(proplist,nonempty) -> true;
isTypeAttr(string,ascii) -> true;
isTypeAttr(string,asciiprintable) -> true;
isTypeAttr(string,nonempty) -> true;
isTypeAttr(term,nonempty) -> true;
isTypeAttr(term,nonundefined) -> true;
isTypeAttr(tuple,nonempty) -> true;
isTypeAttr(_,_) -> false.
@@ -241,13 +237,6 @@ check_term({atom, Y}=_Check, X, _Level, _Mod) ->
true ->
?FAIL({Check,X})
end;
%% boolean
check_term({boolean, Y}=_Check, X, _Level, _Mod) ->
if Y =:= X andalso is_boolean(Y) ->
true;
true ->
?FAIL({Check,X})
end;
%% binary
check_term({binary, Y}=_Check, X, _Level, _Mod) ->
if Y =:= X andalso is_binary(Y) ->
@@ -269,13 +258,6 @@ check_term({integer, Y}=_Check, X, _Level, _Mod) ->
true ->
?FAIL({Check,X})
end;
%% string
check_term({string, {'#S', Y0}=Y}=_Check, X, _Level, _Mod) ->
if Y =:= X andalso is_list(Y0) ->
true;
true ->
?FAIL({Check,X})
end;
%% predef
check_term({predef, Args}=_Check, X, _Level, _Mod) ->
case check_term_predef(Args, X) of
@@ -335,10 +317,12 @@ check_term_range(Min, Max, X) ->
%% check_term_predef
check_term_predef(any, _X) ->
true;
check_term_predef(none, _X) ->
true;
check_term_predef(atom, X) ->
is_atom(X);
check_term_predef(boolean, X) ->
is_boolean(X);
check_term_predef(binary, X) ->
is_binary(X);
check_term_predef(float, X) ->
@@ -347,54 +331,19 @@ check_term_predef(integer, X) ->
is_integer(X);
check_term_predef(list, X) ->
is_list(X);
check_term_predef(proplist, X) ->
case X of
{'#P', Y} when is_list(Y) ->
is_proplist(Y);
_ ->
false
end;
check_term_predef(string, X) ->
case X of
{'#S', Y} when is_list(Y) ->
is_string(Y);
_ ->
false
end;
check_term_predef(term, _X) ->
true;
check_term_predef(tuple, X) ->
is_tuple(X);
check_term_predef(none, _X) ->
true;
check_term_predef({any,Attrs}, X) ->
check_term_attrlist(any,Attrs,X);
check_term_predef({atom,Attrs}, X) ->
is_atom(X) andalso check_term_attrlist(atom,Attrs,X);
check_term_predef({boolean,Attrs}, X) ->
is_boolean(X) andalso check_term_attrlist(boolean,Attrs,X);
check_term_predef({binary,Attrs}, X) ->
is_binary(X) andalso check_term_attrlist(binary,Attrs,X);
check_term_predef({list,Attrs}, X) ->
is_list(X) andalso check_term_attrlist(list,Attrs,X);
check_term_predef({proplist,Attrs}, X) ->
case X of
{'#P', Y} when is_list(Y) ->
is_proplist(Y) andalso check_term_attrlist(proplist,Attrs,X);
_ ->
false
end;
check_term_predef({string,Attrs}, X) ->
case X of
{'#S', Y} when is_list(Y) ->
is_string(Y) andalso check_term_attrlist(string,Attrs,X);
_ ->
false
end;
check_term_predef({term,Attrs}, X) ->
check_term_attrlist(term,Attrs,X);
check_term_predef({tuple,Attrs}, X) ->
is_tuple(X) andalso check_term_attrlist(tuple,Attrs,X).
%% check_term_attrlist
check_term_attrlist(Type, Attrs, Val) ->
[] == [ {Type,Attr,Val} || Attr <- Attrs, not check_term_attr(Type,Attr,Val) ].
@@ -412,21 +361,6 @@ check_term_attr(Type,nonundefined,Val) ->
check_term_attr(_,_,_) ->
false.
%% is_string
is_string([H|T]) when is_integer(H), H < 256, H > -1 ->
is_string(T);
is_string([]) -> true;
is_string(_) -> false.
%% is_proplist
is_proplist([P|T]) when tuple_size(P) =:= 2 ->
is_proplist(T);
is_proplist([]) -> true;
is_proplist(_) -> false.
%% is_ascii
is_ascii(A) when is_atom(A) ->
is_ascii(atom_to_list(A));

View File

@@ -109,9 +109,6 @@ type1(_Gen,{range,Lo,Hi}) ->
%% atom
type1(_Gen,{atom,Value}) when is_atom(Value) ->
Value;
%% boolean
type1(_Gen,{boolean,Value}) when is_boolean(Value) ->
Value;
%% binary
type1(_Gen,{binary,Value}) when is_binary(Value) ->
Value;
@@ -125,10 +122,13 @@ type1(_Gen,{integer,Value}) when is_integer(Value) ->
type1(_Gen,{string,Value}) when is_list(Value) ->
Value;
%% predef
type1(_Gen,{predef,any}) ->
qc_gen:qc_term();
type1(_Gen,{predef,none}) ->
%% not supported
exit(fatal);
type1(_Gen,{predef,atom}) ->
qc_gen:qc_atom();
type1(_Gen,{predef,boolean}) ->
bool();
type1(_Gen,{predef,integer}) ->
oneof([int(),largeint()]);
type1(_Gen,{predef,float}) ->
@@ -137,30 +137,17 @@ type1(_Gen,{predef,binary}) ->
qc_gen:qc_binary();
type1(_Gen,{predef,list}) ->
qc_gen:qc_list();
type1(_Gen,{predef,proplist}) ->
?P(qc_gen:qc_proplist());
type1(_Gen,{predef,string}) ->
?S(qc_gen:qc_string());
type1(_Gen,{predef,tuple}) ->
qc_gen:qc_tuple();
type1(_Gen,{predef,term}) ->
qc_gen:qc_term();
type1(_Gen,{predef,none}) ->
%% not supported
exit(fatal);
%% predef with attributes
type1(_Gen,{predef,{any,Attrs}}) ->
qc_gen:qc_term(Attrs);
type1(_Gen,{predef,{atom,Attrs}}) ->
qc_gen:qc_atom(Attrs);
type1(_Gen,{predef,{binary,Attrs}}) ->
qc_gen:qc_binary(Attrs);
type1(_Gen,{predef,{list,Attrs}}) ->
qc_gen:qc_list(Attrs);
type1(_Gen,{predef,{proplist,Attrs}}) ->
?P(qc_gen:qc_proplist(Attrs));
type1(_Gen,{predef,{string,Attrs}}) ->
?S(qc_gen:qc_string(Attrs));
type1(_Gen,{predef,{term,Attrs}}) ->
qc_gen:qc_term(Attrs);
type1(_Gen,{predef,{tuple,Attrs}}) ->
qc_gen:qc_tuple(Attrs);
%% abnf

View File

@@ -32,6 +32,7 @@
{modules, [contract_parser
, contract_lex
, contract_yecc
, ubf_types_builtin
, ubf_client
, ubf_server
, ubf_plugin_handler

View File

@@ -234,7 +234,7 @@ ubf_client(Parent, Plugins, Server, Options, Timeout)
when is_list(Plugins) andalso length(Plugins) > 0 andalso is_pid(Server) andalso is_list(Options) ->
process_flag(trap_exit, true),
Driver = ubf_server:start_term_listener(Server, Plugins, Options),
ServerHello = proplists:get_value(serverhello, Options, defined),
ServerHello = proplists:get_value(serverhello, Options, true),
SimpleRPC = proplists:get_value(simplerpc, Options, false),
if ServerHello =/= undefined ->
%% wait for a startup message

View File

@@ -25,7 +25,7 @@
+NAME("meta_server").
+VSN("ubf1.0").
+VSN("ubf2.0").
+TYPES
@@ -33,11 +33,11 @@ info() :: info;
description() :: description;
services() :: services;
contract() :: contract;
serviceList() :: [string()] "Names of the offered services";
serviceList() :: [ubfstring()] "Names of the offered services";
help() :: help;
restartService() :: {restartService, string(), term()};
startSession() :: {startSession, string(), term()};
restartService() :: {restartService, ubfstring(), term()};
startSession() :: {startSession, ubfstring(), term()};
ok() :: {ok, term()};
error() :: {error, term()};
ok_or_error() :: ok() | error().

View File

@@ -104,7 +104,7 @@ start(Name, Plugins, Port) ->
%% - +{proto, {ubf | ebf | atom()}}+ Enable the UBF, EBF, or
%% an alternative protocol wire format.
%% Default: ubf.
%% - +{proto, {ubf | ebf | atom(), proplist()}}+ Enable the UBF,
%% - +{proto, {ubf | ebf | atom(), [atom() | tuple()]}}+ Enable the UBF,
%% EBF, or an alternative protocol wire format with options.
%% Default: +{ubf, []}+.
%%
@@ -125,7 +125,7 @@ start(Name, Plugins, Port) ->
%% client first connects to the server. If not set, client may
%% select the service using the startSession() API. There is
%% no default setting.
%% - +{serverhello, string() | undefined}+ Meta contract greeting
%% - +{serverhello, ubfstring() | undefined}+ Meta contract greeting
%% string, sent when a client first connects to the server. If
%% undefined, server hello is not sent to the client.
%% Default: "meta_server".

124
src/ubf_types_builtin.con Normal file
View File

@@ -0,0 +1,124 @@
%%% -*- mode: erlang -*-
%%%
+NAME("ubf_types_builtin").
+VSN("ubf2.0").
%%
%% Excerpt from http://www.erlang.org/eeps/eep-0008.html
%%
%% For convenience, the following types are also built-in. They can be
%% thought as predefined aliases for the type unions also shown in the
%% table. (Some type unions below slightly abuse the syntax of types.)
%%
%% ========================== =====================================
%% Built-in type Stands for
%% ========================== =====================================
%% ``term()`` ``any()``
%% ``boolean()`` ``'false' | 'true'``
%% ``byte()`` ``0..255``
%% ``char()`` ``0..16#10ffff``
%% ``non_neg_integer()`` ``0..``
%% ``pos_integer()`` ``1..``
%% ``neg_integer()`` ``..-1``
%% ``number()`` ``integer() | float()``
%% ``list()`` ``[any()]``
%% ``maybe_improper_list()`` ``maybe_improper_list(any(), any())``
%% ``maybe_improper_list(T)`` ``maybe_improper_list(T, any())``
%% ``string()`` ``[char()]``
%% ``nonempty_string()`` ``[char(),...]``
%% ``iolist()`` ``maybe_improper_list(``
%% ``char() | binary() |``
%% ``iolist(), binary() | [])``
%% ``module()`` ``atom()``
%% ``mfa()`` ``{atom(),atom(),byte()}``
%% ``node()`` ``atom()``
%% ``timeout()`` ``'infinity' | non_neg_integer()``
%% ``no_return()`` ``none()``
%% ========================== =====================================
%%
%% Users are not allowed to define types with the same names as the
%% predefined or built-in ones. This is checked by the compiler and
%% its violation results in a compilation error. (For bootstrapping
%% purposes, it can also result to just a warning if this involves a
%% built-in type which has just been introduced.)
%%
%% *NOTE*: The following built-in list types also exist, but they are
%% expected to be rarely used. Hence, they have long names:
%%
%% ::
%%
%% nonempty_maybe_improper_list(Type) :: nonempty_maybe_improper_list(Type, any())
%% nonempty_maybe_improper_list() :: nonempty_maybe_improper_list(any())
%%
%% where the following two types
%%
%% ::
%%
%% nonempty_improper_list(Type1, Type2)
%% nonempty_maybe_improper_list(Type1, Type2)
%%
%% define the set of Erlang terms one would expect.
%%
%%
%% Supported eep8 predefined types:
%%
%% via ubf predefined types
%% - any()
%% - none()
%% - list()
%%
%% via ubf contract
%% - nil()
%% - term()
%% - byte()
%% - char()
%% - non_neg_integer()
%% - pos_integer()
%% - neg_integer()
%% - number()
%% - string()
%% - nonempty_string()
%% - module()
%% - mfa()
%% - node()
%% - timeout()
%% - no_return()
%%
%% not implemented
%% - iolist()
%% - maybe_improper_list()
%% - maybe_improper_list(T)
%% - nonempty_maybe_improper_list()
%% - nonempty_maybe_improper_list(T)
%%
%%%%%%
+TYPES
%% Erlang built-in types
nil() :: [];
term() :: any();
boolean() :: 'false' | 'true';
byte() :: 0..255;
char() :: 0..16#10ffff;
non_neg_integer() :: 0..;
pos_integer() :: 1..;
neg_integer() :: ..-1;
number() :: integer() | float();
string() :: [char()];
nonempty_string() :: [char()]+;
module() :: atom();
mfa() :: {atom(), atom(), byte()};
node() :: atom();
timeout() :: 'infinity' | non_neg_integer();
no_return() :: none();
%% UBF built-in types
ubfproplist() :: {'#P', [{term(), term()}]};
ubfstring() :: {'#S', [byte()]}.

12
src/ubf_types_builtin.erl Normal file
View File

@@ -0,0 +1,12 @@
%%%
%%% @doc UBF TYPES builtin types
%%%
%%% @end
%%%
-module(ubf_types_builtin).
%% NOTE the following three lines
-compile({parse_transform,contract_parser}).
-add_contract("src/ubf_types_builtin").

View File

@@ -77,12 +77,15 @@ ubf_contract2(C) ->
, "% false\n%\t\tfalse"
, "% undefined\n%\t\tundefined"
, "%"
, "% any()\n%\t\tany"
, "% any()?\n%\t\tany | undefined"
, "%"
, "% none()\n%\t\t /* no result is returned */"
, "% none()?\n%\t\t /* no result is returned */ | undefined"
, "%"
, "% atom()\n%\t\tatom"
, "% atom()?\n%\t\tatom | undefined"
, "%"
, "% boolean()\n%\t\tboolean"
, "% boolean()?\n%\t\tboolean | undefined"
, "%"
, "% binary()\n%\t\tbinary"
, "% binary()?\n%\t\tbinary | undefined"
, "%"
@@ -95,25 +98,16 @@ ubf_contract2(C) ->
, "% list()\n%\t\tlist"
, "% list()?\n%\t\tlist | undefined"
, "%"
, "% proplist()\n%\t\t{'#P',proplist}"
, "% proplist()?\n%\t\t{'#P',proplist} | undefined"
, "%"
, "% string()\n%\t\t{'#S',string}"
, "% string()?\n%\t\t{'#S',string} | undefined"
, "%"
, "% term()\n%\t\tterm"
, "% term()?\n%\t\tterm | undefined"
, "%"
, "% tuple()\n%\t\ttuple"
, "% tuple()?\n%\t\ttuple | undefined"
, "%"
, "% none()\n%\t\t /* no result is returned */"
, "% none()?\n%\t\t /* no result is returned */ | undefined"
, "%"
, "%% --------------------"
, "%% type attributes"
, "%%"
, "%"
, "% any(AnyAttrs)\n%\t\tany"
, "% any(AnyAttrs)?\n%\t\tany | undefined"
, "%"
, "% atom(AtomAttrs)\n%\t\tatom"
, "% atom(AtomAttrs)?\n%\t\tatom | undefined"
, "%"
@@ -123,17 +117,13 @@ ubf_contract2(C) ->
, "% list(ListAttrs)\n%\t\tlist"
, "% list(ListAttrs)?\n%\t\tlist | undefined"
, "%"
, "% proplist(PropListAttrs)\n%\t\t{'#P',proplist}"
, "% proplist(PropListAttrs)?\n%\t\t{'#P',proplist} | undefined"
, "%"
, "% string(StringAttrs)\n%\t\t{'#S',string}"
, "% string(StringAttrs)?\n%\t\t{'#S',string} | undefined"
, "%"
, "% tuple(TupleAttrs)\n%\t\ttuple"
, "% tuple(TupleAttrs)?\n%\t\ttuple | undefined"
, "%"
, "% term(TermAttrs)\n%\t\tterm"
, "% term(TermAttrs)?\n%\t\tterm | undefined"
, "%"
, "% AnyAttrs"
, "% \t nonempty"
, "% \t nonundefined"
, "%"
, "% AtomAttrs"
, "% \t ascii | asciiprintable"
@@ -147,17 +137,6 @@ ubf_contract2(C) ->
, "% ListAttrs"
, "% \t nonempty"
, "%"
, "% PropListAttrs"
, "% \t nonempty"
, "%"
, "% StringAttrs"
, "% \t ascii | asciiprintable"
, "% \t nonempty"
, "%"
, "% TermAttrs"
, "% \t nonempty"
, "% \t nonundefined"
, "%"
, "% TupleAttrs"
, "% \t nonempty"
, "%"
@@ -366,9 +345,6 @@ typeref(_Style,{range,Lo,Hi},_C) ->
%% atom
typeref(_Style,{atom,Value},_C) ->
io_lib:format("~p", [Value]);
%% boolean
typeref(_Style,{boolean,Value},_C) ->
io_lib:format("~p", [Value]);
%% binary
typeref(_Style,{binary,Value},_C) ->
io_lib:format("~p", [Value]);
@@ -382,10 +358,12 @@ typeref(_Style,{integer,Value},_C) ->
typeref(_Style,{string,Value},_C) ->
io_lib:format("~p", [Value]);
%% predef
typeref(_Style,{predef,any},_C) ->
"any()";
typeref(_Style,{predef,none},_C) ->
"none()";
typeref(_Style,{predef,atom},_C) ->
"atom()";
typeref(_Style,{predef,boolean},_C) ->
"boolean()";
typeref(_Style,{predef,binary},_C) ->
"binary()";
typeref(_Style,{predef,float},_C) ->
@@ -394,31 +372,17 @@ typeref(_Style,{predef,integer},_C) ->
"integer()";
typeref(_Style,{predef,list},_C) ->
"list()";
typeref(_Style,{predef,proplist},_C) ->
"proplist()";
typeref(_Style,{predef,string},_C) ->
"string()";
typeref(_Style,{predef,term},_C) ->
"term()";
typeref(_Style,{predef,tuple},_C) ->
"tuple()";
typeref(_Style,{predef,none},_C) ->
"none()";
%% predef with attributes
typeref(_Style,{predef,{any,Attrs}},_C) ->
io_lib:format("any(~s)", [join([ atom_to_list(Attr) || Attr <- Attrs ], ",")]);
typeref(_Style,{predef,{atom,Attrs}},_C) ->
io_lib:format("atom(~s)", [join([ atom_to_list(Attr) || Attr <- Attrs ], ",")]);
typeref(_Style,{predef,{boolean,Attrs}},_C) ->
io_lib:format("boolean(~s)", [join([ atom_to_list(Attr) || Attr <- Attrs ], ",")]);
typeref(_Style,{predef,{binary,Attrs}},_C) ->
io_lib:format("binary(~s)", [join([ atom_to_list(Attr) || Attr <- Attrs ], ",")]);
typeref(_Style,{predef,{list,Attrs}},_C) ->
io_lib:format("list(~s)", [join([ atom_to_list(Attr) || Attr <- Attrs ], ",")]);
typeref(_Style,{predef,{proplist,Attrs}},_C) ->
io_lib:format("proplist(~s)", [join([ atom_to_list(Attr) || Attr <- Attrs ], ",")]);
typeref(_Style,{predef,{string,Attrs}},_C) ->
io_lib:format("string(~s)", [join([ atom_to_list(Attr) || Attr <- Attrs ], ",")]);
typeref(_Style,{predef,{term,Attrs}},_C) ->
io_lib:format("term(~s)", [join([ atom_to_list(Attr) || Attr <- Attrs ], ",")]);
typeref(_Style,{predef,{tuple,Attrs}},_C) ->
io_lib:format("tuple(~s)", [join([ atom_to_list(Attr) || Attr <- Attrs ], ",")]);
%% otherwise

View File

@@ -25,7 +25,7 @@
+NAME("test").
+VSN("ubf1.0").
+VSN("ubf2.0").
%%%%%
%%% states

View File

@@ -25,7 +25,7 @@
+NAME("test").
+VSN("ubf1.0").
+VSN("ubf2.0").
%%%%%

View File

@@ -25,7 +25,7 @@
+NAME("types").
+VSN("ubf1.0").
+VSN("ubf2.0").
%%%%%%
+TYPES
@@ -33,11 +33,9 @@
%%%%%
%%% well-known primitives
bool() :: true | false;
expires() :: {integer(), integer(), integer()}; %% erlang-style timestamp
opaque() :: binary();
time_t() :: integer();
timeout() :: infinity | integer(); %% milliseconds
timeout_or_expires() :: timeout() | expires();
ts() :: integer();
utf8() :: binary();
@@ -84,10 +82,10 @@ ubf_keepalive_res() :: ok;
%%%%%
%%% meta methods
ubf_info_req() :: info;
ubf_info_res() :: string();
ubf_info_res() :: ubfstring();
ubf_description_req() :: description;
ubf_description_res() :: string();
ubf_description_res() :: ubfstring();
ubf_contract_req() :: contract;
ubf_contract_res() :: term().

View File

@@ -25,7 +25,7 @@
+NAME("file_server").
+VSN("ubf1.0").
+VSN("ubf2.0").
+TYPES
@@ -33,7 +33,7 @@ info() :: info;
description() :: description;
contract() :: contract;
file() :: string();
file() :: ubfstring();
ls() :: ls;
files() :: {files, [file()]};
getFile() :: {get, file()};

View File

@@ -25,7 +25,7 @@
+NAME("irc").
+VSN("ubf1.0").
+VSN("ubf2.0").
+TYPES
info() :: info;
@@ -34,10 +34,10 @@ contract() :: contract;
ok() :: ok;
bool() :: true | false;
nick() :: string();
nick() :: ubfstring();
oldnick() :: nick();
newnick() :: nick();
group() :: string();
group() :: ubfstring();
groups() :: [group()];
logon() :: logon;

View File

@@ -25,7 +25,7 @@
+NAME("test").
+VSN("ubf1.0").
+VSN("ubf2.0").
+TYPES
@@ -33,27 +33,25 @@ info() :: info;
description() :: description;
services() :: services;
contract() :: contract;
serviceList() :: [string()];
serviceList() :: [ubfstring()];
dummy() :: {dummy, dummyrecord()};
dummyrecord() :: #dummyrecord{foo::integer(), bar::binary()};
dummy1a() :: atom(ascii,nonempty,nonundefined);
dummy1b() :: atom(asciiprintable,nonempty,nonundefined);
dummy2a() :: string(ascii,nonempty);
dummy2b() :: string(asciiprintable,nonempty);
dummy3a() :: binary(ascii,nonempty);
dummy3b() :: binary(asciiprintable,nonempty);
dummy4() :: term(nonempty,nonundefined);
dummy4() :: any(nonempty,nonundefined);
dummy5() :: #dummy5{hoge1::1, hoge2::two, hoge3::3.0, hoge4::<<"4">>};
password() :: string();
file() :: string();
password() :: ubfstring();
file() :: ubfstring();
noSuchFile() :: {error, noSuchFile};
doubleInt() :: [integer()];
upCase() :: string();
upCase() :: ubfstring();
logon() :: {logon, password()};
ls() :: ls;
files() :: {files, [string()]} "request a list of file";
files() :: {files, [ubfstring()]} "request a list of file";
callback() :: {callback, none()};
getFile() :: {get, file()};
okFile() :: {ok, binary()};
@@ -63,7 +61,7 @@ ack() :: ack;
error() :: error;
ok() :: ok;
intList() :: [integer()];
propList() :: proplist();
propList() :: ubfproplist();
testAmbiguities() :: testAmbiguities;
foo() :: #foo{attribute1::term()};
callbackOnItsWay() :: callbackOnItsWay.
@@ -83,7 +81,7 @@ callbackOnItsWay() :: callbackOnItsWay.
EVENT <= callback().
+STATE funny
string() => upCase() & funny;
ubfstring() => upCase() & funny;
intList() => doubleInt() & funny;
propList() => propList() & funny;
stop() => ack() & start.
@@ -92,14 +90,12 @@ callbackOnItsWay() :: callbackOnItsWay.
dummy() => dummy();
dummy1a() => dummy1a();
dummy1b() => dummy1b();
dummy2a() => dummy2a();
dummy2b() => dummy2b();
dummy3a() => dummy3a();
dummy3b() => dummy3b();
dummy4() => dummy4();
dummy5() => dummy5();
foo() => term();
info() => string();
description() => string();
info() => ubfstring();
description() => ubfstring();
services() => serviceList();
contract() => term().