Commit a6aa9d6a authored by Kenton Varda's avatar Kenton Varda

Make enum syntax match docs, make enum lists work correctly, add enum tests,…

Make enum syntax match docs, make enum lists work correctly, add enum tests, enforce maximum ordinals on enum values and interface methods, change the maximum field ordinal to 16-bit to match docs (now all ordinal limits match).
parent 9ebc3020
......@@ -51,15 +51,15 @@ struct Car {
}
enum Color {
black = 0;
white = 1;
red = 2;
green = 3;
blue = 4;
cyan = 5;
magenta = 6;
yellow = 7;
silver = 8;
black @0;
white @1;
red @2;
green @3;
blue @4;
cyan @5;
magenta @6;
yellow @7;
silver @8;
}
struct Wheel {
......
......@@ -22,11 +22,11 @@
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
enum Operation {
add = 0;
subtract = 1;
multiply = 2;
divide = 3;
modulus = 4;
add @0;
subtract @1;
multiply @2;
divide @3;
modulus @4;
}
struct Expression {
......
......@@ -30,7 +30,19 @@
namespace capnproto {
namespace internal {
template <typename T> struct IsPrimitive;
template <typename T>
class IsPrimitive {
typedef char no;
typedef long yes;
template <typename U> static no test(typename U::Reader*);
template <typename U> static yes test(...);
public:
static constexpr bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
};
} // namespace internal
template <typename T, bool isPrimitive = internal::IsPrimitive<T>::value>
......@@ -38,41 +50,35 @@ struct List;
namespace internal {
template <typename T> struct IsPrimitive { static constexpr bool value = false; };
template <> struct IsPrimitive<Void> { static constexpr bool value = true; };
template <> struct IsPrimitive<bool> { static constexpr bool value = true; };
template <> struct IsPrimitive<int8_t> { static constexpr bool value = true; };
template <> struct IsPrimitive<int16_t> { static constexpr bool value = true; };
template <> struct IsPrimitive<int32_t> { static constexpr bool value = true; };
template <> struct IsPrimitive<int64_t> { static constexpr bool value = true; };
template <> struct IsPrimitive<uint8_t> { static constexpr bool value = true; };
template <> struct IsPrimitive<uint16_t> { static constexpr bool value = true; };
template <> struct IsPrimitive<uint32_t> { static constexpr bool value = true; };
template <> struct IsPrimitive<uint64_t> { static constexpr bool value = true; };
template <> struct IsPrimitive<float> { static constexpr bool value = true; };
template <> struct IsPrimitive<double> { static constexpr bool value = true; };
template <typename T, bool b> struct IsPrimitive<List<T, b>> {
static constexpr bool value = IsPrimitive<T>::value;
template <size_t size> struct FieldSizeForByteSize;
template <> struct FieldSizeForByteSize<1> { static constexpr FieldSize value = FieldSize::BYTE; };
template <> struct FieldSizeForByteSize<2> { static constexpr FieldSize value = FieldSize::TWO_BYTES; };
template <> struct FieldSizeForByteSize<4> { static constexpr FieldSize value = FieldSize::FOUR_BYTES; };
template <> struct FieldSizeForByteSize<8> { static constexpr FieldSize value = FieldSize::EIGHT_BYTES; };
template <typename T> struct FieldSizeForType {
static constexpr FieldSize value = IsPrimitive<T>::value ?
// Primitive types that aren't special-cased below can be determined from sizeof().
FieldSizeForByteSize<sizeof(T)>::value :
// Non-primitive types that aren't special-cased below are presumed to be structs.
FieldSize::INLINE_COMPOSITE;
};
template <typename T> struct FieldSizeForType { static constexpr FieldSize value = FieldSize::INLINE_COMPOSITE; };
template <> struct FieldSizeForType<Void> { static constexpr FieldSize value = FieldSize::VOID; };
template <> struct FieldSizeForType<bool> { static constexpr FieldSize value = FieldSize::BIT; };
template <> struct FieldSizeForType<int8_t> { static constexpr FieldSize value = FieldSize::BYTE; };
template <> struct FieldSizeForType<int16_t> { static constexpr FieldSize value = FieldSize::TWO_BYTES; };
template <> struct FieldSizeForType<int32_t> { static constexpr FieldSize value = FieldSize::FOUR_BYTES; };
template <> struct FieldSizeForType<int64_t> { static constexpr FieldSize value = FieldSize::EIGHT_BYTES; };
template <> struct FieldSizeForType<uint8_t> { static constexpr FieldSize value = FieldSize::BYTE; };
template <> struct FieldSizeForType<uint16_t> { static constexpr FieldSize value = FieldSize::TWO_BYTES; };
template <> struct FieldSizeForType<uint32_t> { static constexpr FieldSize value = FieldSize::FOUR_BYTES; };
template <> struct FieldSizeForType<uint64_t> { static constexpr FieldSize value = FieldSize::EIGHT_BYTES; };
template <> struct FieldSizeForType<float> { static constexpr FieldSize value = FieldSize::FOUR_BYTES; };
template <> struct FieldSizeForType<double> { static constexpr FieldSize value = FieldSize::EIGHT_BYTES; };
// Void and bool are special.
template <> struct FieldSizeForType<Void> { static constexpr FieldSize value = FieldSize::VOID; };
template <> struct FieldSizeForType<bool> { static constexpr FieldSize value = FieldSize::BIT; };
// Lists and blobs are references, not structs.
template <typename T, bool b> struct FieldSizeForType<List<T, b>> {
static constexpr FieldSize value = FieldSize::REFERENCE;
};
template <> struct FieldSizeForType<Text> {
static constexpr FieldSize value = FieldSize::REFERENCE;
};
template <> struct FieldSizeForType<Data> {
static constexpr FieldSize value = FieldSize::REFERENCE;
};
template <typename T>
class TemporaryPointer {
......
......@@ -65,6 +65,7 @@ void genericInitTestMessage(Builder builder) {
subSubBuilder.setTextField("nested");
subSubBuilder.initStructField().setTextField("really nested");
}
subBuilder.setEnumField(TestEnum::BAZ);
subBuilder.setVoidList({Void::VOID, Void::VOID, Void::VOID});
subBuilder.setBoolList({false, true, false, true, true});
......@@ -81,14 +82,15 @@ void genericInitTestMessage(Builder builder) {
subBuilder.setFloat64List({0, 123456789012345, 1e306, -1e306, 1e-306, -1e-306});
subBuilder.setTextList({"quux", "corge", "grault"});
subBuilder.setDataList({"garply", "waldo", "fred"});
{
auto listBuilder = subBuilder.initStructList(3);
listBuilder[0].setTextField("x structlist 1");
listBuilder[1].setTextField("x structlist 2");
listBuilder[2].setTextField("x structlist 3");
}
subBuilder.setEnumList({TestEnum::QUX, TestEnum::BAR, TestEnum::GRAULT});
}
builder.setEnumField(TestEnum::CORGE);
builder.initVoidList(6);
builder.setBoolList({true, false, false, true});
......@@ -104,13 +106,13 @@ void genericInitTestMessage(Builder builder) {
builder.setFloat64List({7777.75, 1111.125});
builder.setTextList({"plugh", "xyzzy", "thud"});
builder.setDataList({"oops", "exhausted", "rfc3092"});
{
auto listBuilder = builder.initStructList(3);
listBuilder[0].setTextField("structlist 1");
listBuilder[1].setTextField("structlist 2");
listBuilder[2].setTextField("structlist 3");
}
builder.setEnumList({TestEnum::FOO, TestEnum::GARPLY});
}
template <typename T, typename U>
......@@ -174,6 +176,7 @@ void genericCheckTestMessage(Reader reader) {
EXPECT_EQ("nested", subSubReader.getTextField());
EXPECT_EQ("really nested", subSubReader.getStructField().getTextField());
}
EXPECT_EQ(TestEnum::BAZ, subReader.getEnumField());
checkList(subReader.getVoidList(), {Void::VOID, Void::VOID, Void::VOID});
checkList(subReader.getBoolList(), {false, true, false, true, true});
......@@ -190,7 +193,6 @@ void genericCheckTestMessage(Reader reader) {
checkList(subReader.getFloat64List(), {0.0, 123456789012345.0, 1e306, -1e306, 1e-306, -1e-306});
checkList(subReader.getTextList(), {"quux", "corge", "grault"});
checkList(subReader.getDataList(), {"garply", "waldo", "fred"});
{
auto listReader = subReader.getStructList();
ASSERT_EQ(3u, listReader.size());
......@@ -198,7 +200,9 @@ void genericCheckTestMessage(Reader reader) {
EXPECT_EQ("x structlist 2", listReader[1].getTextField());
EXPECT_EQ("x structlist 3", listReader[2].getTextField());
}
checkList(subReader.getEnumList(), {TestEnum::QUX, TestEnum::BAR, TestEnum::GRAULT});
}
EXPECT_EQ(TestEnum::CORGE, reader.getEnumField());
EXPECT_EQ(6u, reader.getVoidList().size());
checkList(reader.getBoolList(), {true, false, false, true});
......@@ -214,7 +218,6 @@ void genericCheckTestMessage(Reader reader) {
checkList(reader.getFloat64List(), {7777.75, 1111.125});
checkList(reader.getTextList(), {"plugh", "xyzzy", "thud"});
checkList(reader.getDataList(), {"oops", "exhausted", "rfc3092"});
{
auto listReader = reader.getStructList();
ASSERT_EQ(3u, listReader.size());
......@@ -222,6 +225,7 @@ void genericCheckTestMessage(Reader reader) {
EXPECT_EQ("structlist 2", listReader[1].getTextField());
EXPECT_EQ("structlist 3", listReader[2].getTextField());
}
checkList(reader.getEnumList(), {TestEnum::FOO, TestEnum::GARPLY});
}
template <typename Reader>
......
......@@ -21,6 +21,17 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
enum TestEnum {
foo @0;
bar @1;
baz @2;
qux @3;
quux @4;
corge @5;
grault @6;
garply @7;
}
struct TestAllTypes {
voidField @0 : Void;
boolField @1 : Bool;
......@@ -37,7 +48,7 @@ struct TestAllTypes {
textField @12 : Text;
dataField @13 : Data;
structField @14 : TestAllTypes;
enumField @15 : Void; # TODO
enumField @15 : TestEnum;
interfaceField @16 : Void; # TODO
voidList @17 : List(Void);
......@@ -55,7 +66,7 @@ struct TestAllTypes {
textList @29 : List(Text);
dataList @30 : List(Data);
structList @31 : List(TestAllTypes);
enumList @32 : Void; # TODO
enumList @32 : List(TestEnum);
interfaceList @33 : Void; # TODO
}
......@@ -92,7 +103,7 @@ struct TestDefaults {
structField = (
textField = "nested",
structField = (textField = "really nested")),
# enumField = TODO
enumField = baz,
# interfaceField can't have a default
voidList = [void, void, void],
......@@ -112,11 +123,11 @@ struct TestDefaults {
structList = [
(textField = "x structlist 1"),
(textField = "x structlist 2"),
(textField = "x structlist 3")]
# enumList = TODO
(textField = "x structlist 3")],
enumList = [qux, bar, grault]
# interfaceList can't have a default
);
enumField @15 : Void; # TODO
enumField @15 : TestEnum = corge;
interfaceField @16 : Void; # TODO
voidList @17 : List(Void) = [void, void, void, void, void, void];
......@@ -137,6 +148,6 @@ struct TestDefaults {
(textField = "structlist 1"),
(textField = "structlist 2"),
(textField = "structlist 3")];
enumList @32 : List(Void); # TODO
enumList @32 : List(TestEnum) = [foo, garply];
interfaceList @33 : List(Void); # TODO
}
......@@ -298,10 +298,10 @@ requireSequentialNumbering kind items = Active () (loop undefined (-1) sortedIte
message = printf "Skipped number %d. %s must be numbered sequentially starting \
\from zero." (prev + 1) kind
requireFieldNumbersInRange fieldNums =
Active () [ fieldNumError num pos | Located pos num <- fieldNums, num > maxFieldNumber ] where
fieldNumError num = newErrorMessage (Message
(printf "Field number %d too large; maximum is %d." num maxFieldNumber))
requireOrdinalsInRange ordinals =
Active () [ ordinalError num pos | Located pos num <- ordinals, num > maxOrdinal ] where
ordinalError num = newErrorMessage (Message
(printf "Ordinal %d too large; maximum is %d." num maxOrdinal))
requireNoDuplicateNames :: [Declaration] -> Status()
requireNoDuplicateNames decls = Active () (loop (List.sort locatedNames)) where
......@@ -497,7 +497,9 @@ compileDecl scope (EnumDecl (Located _ name) decls) =
CompiledMemberStatus name (feedback (\desc -> do
(members, memberMap, options, statements) <- compileChildDecls desc decls
requireNoDuplicateNames decls
requireSequentialNumbering "Enum values" [ num | EnumValueDecl _ num _ <- decls ]
let numbers = [ num | EnumValueDecl _ num _ <- decls ]
requireSequentialNumbering "Enum values" numbers
requireOrdinalsInRange numbers
return (DescEnum EnumDesc
{ enumName = name
, enumParent = scope
......@@ -527,7 +529,7 @@ compileDecl scope (StructDecl (Located _ name) decls) =
let fieldNums = [ num | FieldDecl _ num _ _ _ _ <- decls ] ++
[ num | UnionDecl _ num _ <- decls ]
requireSequentialNumbering "Fields" fieldNums
requireFieldNumbersInRange fieldNums
requireOrdinalsInRange fieldNums
return (let
fields = [d | DescField d <- members]
unions = [d | DescUnion d <- members]
......@@ -611,7 +613,9 @@ compileDecl scope (InterfaceDecl (Located _ name) decls) =
CompiledMemberStatus name (feedback (\desc -> do
(members, memberMap, options, statements) <- compileChildDecls desc decls
requireNoDuplicateNames decls
requireSequentialNumbering "Methods" [ num | MethodDecl _ num _ _ _ <- decls ]
let numbers = [ num | MethodDecl _ num _ _ _ <- decls ]
requireSequentialNumbering "Methods" numbers
requireOrdinalsInRange numbers
return (DescInterface InterfaceDesc
{ interfaceName = name
, interfaceParent = scope
......
......@@ -25,11 +25,8 @@ module Parser (parseFile) where
import Text.Parsec hiding (tokens)
import Text.Parsec.Error(newErrorMessage, Message(Message))
import Text.Parsec.Pos(newPos)
import Text.Printf(printf)
import Token
import Grammar
import Semantics(maxFieldNumber, maxMethodNumber)
import Lexer (lexer)
import Control.Monad.Identity
......@@ -147,21 +144,11 @@ typeExpression = do
suffixes <- option [] (parenthesizedList typeExpression)
return (TypeExpression name suffixes)
nameWithOrdinal :: Integer -> TokenParser (Located String, Located Integer)
nameWithOrdinal maxNumber = do
nameWithOrdinal :: TokenParser (Located String, Located Integer)
nameWithOrdinal = do
name <- located varIdentifier
atSign
ordinal <- located literalInt
if locatedValue ordinal > maxNumber - 32 && locatedValue ordinal <= maxNumber
then exclamationPoint
<|> failNonFatal (locatedPos ordinal)
(printf "%d is nearing maximum of %d. Be sure to plan for future extensibility \
\before you run out of numbers, e.g. by declaring a new nested type which \
\can hold future declarations. To acknowledge this warning, add an \
\exclamation point after the number, i.e.: %s@%d!"
(locatedValue ordinal) maxNumber (locatedValue name)
(locatedValue ordinal))
else optional exclamationPoint
return (name, ordinal)
topLine :: Maybe [Located Statement] -> TokenParser Declaration
......@@ -199,9 +186,7 @@ enumLine Nothing = optionDecl <|> enumValueDecl []
enumLine (Just statements) = enumValueDecl statements
enumValueDecl statements = do
name <- located varIdentifier
equalsSign
value <- located literalInt
(name, value) <- nameWithOrdinal
children <- parseBlock enumValueLine statements
return (EnumValueDecl name value children)
......@@ -221,7 +206,7 @@ structLine (Just statements) = typeDecl statements <|> unionDecl statements <|>
unionDecl statements = do
unionKeyword
(name, ordinal) <- nameWithOrdinal maxFieldNumber
(name, ordinal) <- nameWithOrdinal
children <- parseBlock unionLine statements
return (UnionDecl name ordinal children)
......@@ -230,7 +215,7 @@ unionLine Nothing = optionDecl <|> fieldDecl []
unionLine (Just statements) = fieldDecl statements
fieldDecl statements = do
(name, ordinal) <- nameWithOrdinal maxFieldNumber
(name, ordinal) <- nameWithOrdinal
union <- optionMaybe (inKeyword >> located varIdentifier)
colon
t <- typeExpression
......@@ -273,7 +258,7 @@ interfaceLine Nothing = optionDecl <|> constantDecl <|> methodDecl []
interfaceLine (Just statements) = typeDecl statements <|> methodDecl statements
methodDecl statements = do
(name, ordinal) <- nameWithOrdinal maxMethodNumber
(name, ordinal) <- nameWithOrdinal
params <- parenthesizedList paramDecl
colon
t <- typeExpression
......
......@@ -33,13 +33,9 @@ import Text.Printf(printf)
import Control.Monad(join)
import Util(delimit)
-- Field counts are 8-bit, therefore there cannot be more than 255 fields, therefore the max field
-- number is 254.
maxFieldNumber = 254 :: Integer
-- Limiting method counts is not as important technically, but obviously it would be insane to have
-- anywhere near 2^16 methods.
maxMethodNumber = 65534 :: Integer
-- Field counts are 16-bit, therefore there cannot be more than 65535 fields, therefore the max
-- ordinal is 65534.
maxOrdinal = 65534 :: Integer
type ByteString = [Word8]
......@@ -404,7 +400,7 @@ descToCode indent (DescConstant desc) = printf "%sconst %s: %s = %s;\n" indent
descToCode indent (DescEnum desc) = printf "%senum %s%s" indent
(enumName desc)
(blockCode indent (enumStatements desc))
descToCode indent (DescEnumValue desc) = printf "%s%s = %d%s" indent
descToCode indent (DescEnumValue desc) = printf "%s%s @%d%s" indent
(enumValueName desc) (enumValueNumber desc) (maybeBlockCode indent $ enumValueStatements desc)
descToCode indent (DescStruct desc) = printf "%sstruct %s%s" indent
(structName desc)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment