Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
34e70d5a
Commit
34e70d5a
authored
Jul 26, 2013
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
More compiler work. Almost to the point of basic functionality... almost...
parent
a75f59bf
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
862 additions
and
234 deletions
+862
-234
compiler.c++
c++/src/capnp/compiler/compiler.c++
+6
-6
compiler.h
c++/src/capnp/compiler/compiler.h
+2
-3
grammar.capnp
c++/src/capnp/compiler/grammar.capnp
+3
-0
lexer.c++
c++/src/capnp/compiler/lexer.c++
+3
-3
lexer.h
c++/src/capnp/compiler/lexer.h
+4
-4
module-loader.c++
c++/src/capnp/compiler/module-loader.c++
+342
-0
module-loader.h
c++/src/capnp/compiler/module-loader.h
+62
-0
node-translator.c++
c++/src/capnp/compiler/node-translator.c++
+394
-199
node-translator.h
c++/src/capnp/compiler/node-translator.h
+21
-6
parser.c++
c++/src/capnp/compiler/parser.c++
+15
-10
parser.h
c++/src/capnp/compiler/parser.h
+3
-3
dynamic.h
c++/src/capnp/dynamic.h
+2
-0
string.h
c++/src/kj/string.h
+5
-0
No files found.
c++/src/capnp/compiler/compiler.c++
View file @
34e70d5a
...
...
@@ -183,7 +183,7 @@ private:
class
Compiler
::
CompiledModule
{
public
:
CompiledModule
(
const
Compiler
::
Impl
&
compiler
,
const
Module
<
ParsedFile
::
Reader
>
&
parserModule
);
CompiledModule
(
const
Compiler
::
Impl
&
compiler
,
const
Module
&
parserModule
);
const
Compiler
::
Impl
&
getCompiler
()
const
{
return
compiler
;
}
...
...
@@ -196,7 +196,7 @@ public:
private
:
const
Compiler
::
Impl
&
compiler
;
const
Module
<
ParsedFile
::
Reader
>
&
parserModule
;
const
Module
&
parserModule
;
MallocMessageBuilder
contentArena
;
ParsedFile
::
Reader
content
;
Node
rootNode
;
...
...
@@ -206,7 +206,7 @@ class Compiler::Impl: public SchemaLoader::LazyLoadCallback {
public
:
Impl
();
const
CompiledModule
&
add
(
const
Module
<
ParsedFile
::
Reader
>
&
parsedModule
)
const
;
const
CompiledModule
&
add
(
const
Module
&
parsedModule
)
const
;
struct
Workspace
{
// Scratch space where stuff can be allocated while working. The Workspace is available
...
...
@@ -264,7 +264,7 @@ private:
uint
workspaceRefcount
=
0
;
// Count of threads that have entered the compiler.
typedef
std
::
unordered_map
<
Module
<
ParsedFile
::
Reader
>
*
,
kj
::
Own
<
CompiledModule
>>
ModuleMap
;
typedef
std
::
unordered_map
<
Module
*
,
kj
::
Own
<
CompiledModule
>>
ModuleMap
;
kj
::
MutexGuarded
<
ModuleMap
>
modules
;
// Map of parser modules to compiler modules.
...
...
@@ -627,7 +627,7 @@ Schema Compiler::Node::resolveFinalSchema(uint64_t id) const {
// =======================================================================================
Compiler
::
CompiledModule
::
CompiledModule
(
const
Compiler
::
Impl
&
compiler
,
const
Module
<
ParsedFile
::
Reader
>
&
parserModule
)
const
Compiler
::
Impl
&
compiler
,
const
Module
&
parserModule
)
:
compiler
(
compiler
),
parserModule
(
parserModule
),
content
(
parserModule
.
loadContent
(
contentArena
.
getOrphanage
())),
rootNode
(
*
this
)
{}
...
...
@@ -635,7 +635,7 @@ Compiler::CompiledModule::CompiledModule(
kj
::
Maybe
<
const
Compiler
::
CompiledModule
&>
Compiler
::
CompiledModule
::
importRelative
(
kj
::
StringPtr
importPath
)
const
{
return
parserModule
.
importRelative
(
importPath
).
map
(
[
this
](
const
Module
<
ParsedFile
::
Reader
>
&
module
)
->
const
Compiler
::
CompiledModule
&
{
[
this
](
const
Module
&
module
)
->
const
Compiler
::
CompiledModule
&
{
return
compiler
.
add
(
module
);
});
}
...
...
c++/src/capnp/compiler/compiler.h
View file @
34e70d5a
...
...
@@ -32,7 +32,6 @@
namespace
capnp
{
namespace
compiler
{
template
<
typename
ContentType
>
class
Module
:
public
ErrorReporter
{
public
:
virtual
kj
::
StringPtr
getLocalName
()
const
=
0
;
...
...
@@ -43,7 +42,7 @@ public:
// The name of the module file relative to the source tree. Used to decide where to output
// generated code and to form the `displayName` in the schema.
virtual
ContentType
loadContent
(
Orphanage
orphanage
)
const
=
0
;
virtual
Orphan
<
ParsedFile
>
loadContent
(
Orphanage
orphanage
)
const
=
0
;
// Loads the module content, using the given orphanage to allocate objects if necessary.
virtual
kj
::
Maybe
<
const
Module
&>
importRelative
(
kj
::
StringPtr
importPath
)
const
=
0
;
...
...
@@ -77,7 +76,7 @@ public:
// use EAGER mode.
};
Schema
add
(
Module
<
ParsedFile
::
Reader
>
&
module
,
Mode
mode
)
const
;
Schema
add
(
Module
&
module
,
Mode
mode
)
const
;
// Add a module to the Compiler, returning its root Schema object.
const
SchemaLoader
&
getLoader
()
const
;
...
...
c++/src/capnp/compiler/grammar.capnp
View file @
34e70d5a
...
...
@@ -69,6 +69,9 @@ struct DeclName {
memberPath @4 :List(LocatedText);
# List of `.member` suffixes.
startByte @5 :UInt32;
endByte @6 :UInt32;
}
struct TypeExpression {
...
...
c++/src/capnp/compiler/lexer.c++
View file @
34e70d5a
...
...
@@ -31,7 +31,7 @@ namespace compiler {
namespace
p
=
kj
::
parse
;
bool
lex
(
kj
::
ArrayPtr
<
const
char
>
input
,
LexedStatements
::
Builder
result
,
ErrorReporter
&
errorReporter
)
{
const
ErrorReporter
&
errorReporter
)
{
Lexer
lexer
(
Orphanage
::
getForMessageContaining
(
result
),
errorReporter
);
auto
parser
=
p
::
sequence
(
lexer
.
getParsers
().
statementSequence
,
p
::
endOfInput
);
...
...
@@ -53,7 +53,7 @@ bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
}
bool
lex
(
kj
::
ArrayPtr
<
const
char
>
input
,
LexedTokens
::
Builder
result
,
ErrorReporter
&
errorReporter
)
{
const
ErrorReporter
&
errorReporter
)
{
Lexer
lexer
(
Orphanage
::
getForMessageContaining
(
result
),
errorReporter
);
auto
parser
=
p
::
sequence
(
lexer
.
getParsers
().
tokenSequence
,
p
::
endOfInput
);
...
...
@@ -138,7 +138,7 @@ constexpr auto docComment = p::optional(p::sequence(
}
// namespace
Lexer
::
Lexer
(
Orphanage
orphanageParam
,
ErrorReporter
&
errorReporterParam
)
Lexer
::
Lexer
(
Orphanage
orphanageParam
,
const
ErrorReporter
&
errorReporterParam
)
:
orphanage
(
orphanageParam
),
errorReporter
(
errorReporterParam
)
{
// Note that because passing an lvalue to a parser constructor uses it by-referencee, it's safe
...
...
c++/src/capnp/compiler/lexer.h
View file @
34e70d5a
...
...
@@ -33,9 +33,9 @@ namespace capnp {
namespace
compiler
{
bool
lex
(
kj
::
ArrayPtr
<
const
char
>
input
,
LexedStatements
::
Builder
result
,
ErrorReporter
&
errorReporter
);
const
ErrorReporter
&
errorReporter
);
bool
lex
(
kj
::
ArrayPtr
<
const
char
>
input
,
LexedTokens
::
Builder
result
,
ErrorReporter
&
errorReporter
);
const
ErrorReporter
&
errorReporter
);
// Lex the given source code, placing the results in `result`. Returns true if there
// were no errors, false if there were. Even when errors are present, the file may have partial
// content which can be fed into later stages of parsing in order to find more errors.
...
...
@@ -49,7 +49,7 @@ class Lexer {
// into your own parsers.
public
:
Lexer
(
Orphanage
orphanage
,
ErrorReporter
&
errorReporter
);
Lexer
(
Orphanage
orphanage
,
const
ErrorReporter
&
errorReporter
);
// `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is
// a pointer to the beginning of the input, used to compute byte offsets.
...
...
@@ -91,7 +91,7 @@ public:
private
:
Orphanage
orphanage
;
ErrorReporter
&
errorReporter
;
const
ErrorReporter
&
errorReporter
;
kj
::
Arena
arena
;
Parsers
parsers
;
};
...
...
c++/src/capnp/compiler/module-loader.c++
0 → 100644
View file @
34e70d5a
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "module-loader.h"
#include "lexer.h"
#include "parser.h"
#include <kj/vector.h>
#include <kj/mutex.h>
#include <kj/debug.h>
#include <kj/io.h>
#include <capnp/message.h>
#include <map>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
namespace
capnp
{
namespace
compiler
{
namespace
{
template
<
typename
T
>
size_t
findLargestElementBefore
(
const
kj
::
Vector
<
T
>&
vec
,
const
T
&
key
)
{
KJ_REQUIRE
(
vec
.
size
()
>
0
&&
vec
[
0
]
<=
key
);
size_t
lower
=
0
;
size_t
upper
=
vec
.
size
();
while
(
upper
-
lower
>
1
)
{
size_t
mid
=
(
lower
+
upper
)
/
2
;
if
(
vec
[
mid
]
>
key
)
{
upper
=
mid
;
}
else
{
lower
=
mid
;
}
}
return
lower
;
}
class
MmapDisposer
:
public
kj
::
ArrayDisposer
{
protected
:
void
disposeImpl
(
void
*
firstElement
,
size_t
elementSize
,
size_t
elementCount
,
size_t
capacity
,
void
(
*
destroyElement
)(
void
*
))
const
{
munmap
(
firstElement
,
elementSize
*
elementCount
);
}
};
constexpr
MmapDisposer
mmapDisposer
=
MmapDisposer
();
kj
::
Array
<
const
char
>
mmapForRead
(
kj
::
StringPtr
filename
)
{
int
fd
;
// We already established that the file exists, so this should not fail.
KJ_SYSCALL
(
fd
=
open
(
filename
.
cStr
(),
O_RDONLY
),
filename
);
kj
::
AutoCloseFd
closer
(
fd
);
struct
stat
stats
;
KJ_SYSCALL
(
fstat
(
fd
,
&
stats
));
const
void
*
mapping
=
mmap
(
NULL
,
stats
.
st_size
,
PROT_READ
,
MAP_SHARED
,
fd
,
0
);
if
(
mapping
==
MAP_FAILED
)
{
KJ_FAIL_SYSCALL
(
"mmap"
,
errno
,
filename
);
}
return
kj
::
Array
<
const
char
>
(
reinterpret_cast
<
const
char
*>
(
mapping
),
stats
.
st_size
,
mmapDisposer
);
}
static
char
*
canonicalizePath
(
char
*
path
)
{
// Taken from some old C code of mine.
// Preconditions:
// - path has already been determined to be relative, perhaps because the pointer actually points
// into the middle of some larger path string, in which case it must point to the character
// immediately after a '/'.
// Invariants:
// - src points to the beginning of a path component.
// - dst points to the location where the path component should end up, if it is not special.
// - src == path or src[-1] == '/'.
// - dst == path or dst[-1] == '/'.
char
*
src
=
path
;
char
*
dst
=
path
;
char
*
locked
=
dst
;
// dst cannot backtrack past this
char
*
partEnd
;
bool
hasMore
;
for
(;;)
{
while
(
*
src
==
'/'
)
{
// Skip duplicate slash.
++
src
;
}
partEnd
=
strchr
(
src
,
'/'
);
hasMore
=
partEnd
!=
NULL
;
if
(
hasMore
)
{
*
partEnd
=
'\0'
;
}
else
{
partEnd
=
src
+
strlen
(
src
);
}
if
(
strcmp
(
src
,
"."
)
==
0
)
{
// Skip it.
}
else
if
(
strcmp
(
src
,
".."
)
==
0
)
{
if
(
dst
>
locked
)
{
// Backtrack over last path component.
--
dst
;
while
(
dst
>
locked
&&
dst
[
-
1
]
!=
'/'
)
--
dst
;
}
else
{
locked
+=
3
;
goto
copy
;
}
}
else
{
// Copy if needed.
copy
:
if
(
dst
<
src
)
{
memmove
(
dst
,
src
,
partEnd
-
src
);
dst
+=
partEnd
-
src
;
}
else
{
dst
=
partEnd
;
}
*
dst
++
=
'/'
;
}
if
(
hasMore
)
{
src
=
partEnd
+
1
;
}
else
{
// Oops, we have to remove the trailing '/'.
if
(
dst
==
path
)
{
// Oops, there is no trailing '/'. We have to return ".".
strcpy
(
path
,
"."
);
return
path
+
1
;
}
else
{
// Remove the trailing '/'. Note that this means that opening the file will work even
// if it is not a directory, where normally it should fail on non-directories when a
// trailing '/' is present. If this is a problem, we need to add some sort of special
// handling for this case where we stat() it separately to check if it is a directory,
// because Ekam findInput will not accept a trailing '/'.
--
dst
;
*
dst
=
'\0'
;
return
dst
;
}
}
}
}
kj
::
String
canonicalizePath
(
kj
::
StringPtr
path
)
{
KJ_STACK_ARRAY
(
char
,
result
,
path
.
size
()
+
1
,
128
,
512
);
strcpy
(
result
.
begin
(),
path
.
begin
());
char
*
end
=
canonicalizePath
(
result
.
begin
());
return
kj
::
heapString
(
result
.
slice
(
0
,
end
-
result
.
begin
()));
}
kj
::
String
catPath
(
kj
::
StringPtr
base
,
kj
::
StringPtr
add
)
{
if
(
add
.
size
()
>
0
&&
add
[
0
]
==
'/'
)
{
return
kj
::
heapString
(
add
);
}
const
char
*
pos
=
base
.
end
();
while
(
pos
>
base
.
begin
()
&&
pos
[
-
1
]
!=
'/'
)
{
--
pos
;
}
return
kj
::
str
(
base
.
slice
(
0
,
pos
-
base
.
begin
()),
add
);
}
}
// namespace
class
ModuleLoader
::
Impl
{
public
:
Impl
(
int
errorFd
)
:
errorFd
(
errorFd
)
{}
void
addImportPath
(
kj
::
String
path
)
{
searchPath
.
add
(
kj
::
heapString
(
kj
::
mv
(
path
)));
}
kj
::
Maybe
<
const
Module
&>
loadModule
(
kj
::
StringPtr
localName
,
kj
::
StringPtr
sourceName
)
const
;
kj
::
Maybe
<
const
Module
&>
loadModuleFromSearchPath
(
kj
::
StringPtr
sourceName
)
const
;
void
writeError
(
kj
::
StringPtr
content
)
const
;
private
:
int
errorFd
;
kj
::
Vector
<
kj
::
String
>
searchPath
;
kj
::
MutexGuarded
<
std
::
map
<
kj
::
StringPtr
,
kj
::
Own
<
Module
>>>
modules
;
};
class
ModuleLoader
::
ModuleImpl
:
public
Module
{
public
:
ModuleImpl
(
const
ModuleLoader
::
Impl
&
loader
,
kj
::
String
localName
,
kj
::
String
sourceName
)
:
loader
(
loader
),
localName
(
kj
::
mv
(
localName
)),
sourceName
(
kj
::
mv
(
sourceName
))
{}
kj
::
StringPtr
getLocalName
()
const
override
{
return
localName
;
}
kj
::
StringPtr
getSourceName
()
const
override
{
return
sourceName
;
}
Orphan
<
ParsedFile
>
loadContent
(
Orphanage
orphanage
)
const
override
{
kj
::
Array
<
const
char
>
content
=
mmapForRead
(
localName
);
lineBreaks
.
get
([
&
](
kj
::
SpaceFor
<
kj
::
Vector
<
uint
>>&
space
)
{
auto
vec
=
space
.
construct
(
content
.
size
()
/
40
);
vec
->
add
(
0
);
for
(
const
char
*
pos
=
content
.
begin
();
pos
<
content
.
end
();
++
pos
)
{
if
(
*
pos
==
'\n'
)
{
vec
->
add
(
pos
+
1
-
content
.
begin
());
}
}
return
vec
;
});
MallocMessageBuilder
lexedBuilder
;
auto
statements
=
lexedBuilder
.
initRoot
<
LexedStatements
>
();
lex
(
content
,
statements
,
*
this
);
auto
parsed
=
orphanage
.
newOrphan
<
ParsedFile
>
();
parseFile
(
statements
.
getStatements
(),
parsed
.
get
(),
*
this
);
return
parsed
;
}
kj
::
Maybe
<
const
Module
&>
importRelative
(
kj
::
StringPtr
importPath
)
const
override
{
if
(
importPath
.
size
()
>
0
&&
importPath
[
0
]
==
'/'
)
{
return
loader
.
loadModuleFromSearchPath
(
importPath
.
slice
(
1
));
}
else
{
return
loader
.
loadModule
(
catPath
(
localName
,
importPath
),
catPath
(
sourceName
,
importPath
));
}
}
void
addError
(
uint32_t
startByte
,
uint32_t
endByte
,
kj
::
StringPtr
message
)
const
override
{
auto
&
lines
=
lineBreaks
.
get
(
[](
kj
::
SpaceFor
<
kj
::
Vector
<
uint
>>&
space
)
{
KJ_FAIL_REQUIRE
(
"Can't report errors until loadContent() is called."
);
return
space
.
construct
();
});
uint
startLine
=
findLargestElementBefore
(
lines
,
startByte
);
uint
startCol
=
startByte
-
lines
[
startLine
];
loader
.
writeError
(
kj
::
str
(
localName
,
":"
,
startLine
,
":"
,
startCol
,
": error: "
,
message
,
"
\n
"
));
}
private
:
const
ModuleLoader
::
Impl
&
loader
;
kj
::
String
localName
;
kj
::
String
sourceName
;
kj
::
Lazy
<
kj
::
Vector
<
uint
>>
lineBreaks
;
// Byte offsets of the first byte in each source line. The first element is always zero.
// Initialized the first time the module is loaded.
};
// =======================================================================================
kj
::
Maybe
<
const
Module
&>
ModuleLoader
::
Impl
::
loadModule
(
kj
::
StringPtr
localName
,
kj
::
StringPtr
sourceName
)
const
{
kj
::
String
canonicalLocalName
=
canonicalizePath
(
localName
);
kj
::
String
canonicalSourceName
=
canonicalizePath
(
sourceName
);
auto
locked
=
modules
.
lockExclusive
();
auto
iter
=
locked
->
find
(
canonicalLocalName
);
if
(
iter
!=
locked
->
end
())
{
// Return existing file.
return
*
iter
->
second
;
}
if
(
access
(
canonicalLocalName
.
cStr
(),
R_OK
)
<
0
)
{
// No such file.
return
nullptr
;
}
auto
module
=
kj
::
heap
<
ModuleImpl
>
(
*
this
,
kj
::
mv
(
canonicalLocalName
),
kj
::
mv
(
canonicalSourceName
));
auto
&
result
=
*
module
;
locked
->
insert
(
std
::
make_pair
(
result
.
getLocalName
(),
kj
::
mv
(
module
)));
return
result
;
}
kj
::
Maybe
<
const
Module
&>
ModuleLoader
::
Impl
::
loadModuleFromSearchPath
(
kj
::
StringPtr
sourceName
)
const
{
for
(
auto
&
search
:
searchPath
)
{
kj
::
String
candidate
=
kj
::
str
(
search
,
"/"
,
sourceName
);
char
*
end
=
canonicalizePath
(
candidate
.
begin
()
+
(
candidate
[
0
]
==
'/'
));
KJ_IF_MAYBE
(
module
,
loadModule
(
kj
::
heapString
(
candidate
.
slice
(
0
,
end
-
candidate
.
begin
())),
sourceName
))
{
return
*
module
;
}
}
return
nullptr
;
}
void
ModuleLoader
::
Impl
::
writeError
(
kj
::
StringPtr
content
)
const
{
const
char
*
pos
=
content
.
begin
();
while
(
pos
<
content
.
end
())
{
ssize_t
n
;
KJ_SYSCALL
(
n
=
write
(
errorFd
,
pos
,
content
.
end
()
-
pos
));
pos
+=
n
;
}
}
// =======================================================================================
ModuleLoader
::
ModuleLoader
(
int
errorFd
)
:
impl
(
kj
::
heap
<
Impl
>
(
errorFd
))
{}
ModuleLoader
::~
ModuleLoader
()
{}
void
ModuleLoader
::
addImportPath
(
kj
::
String
path
)
{
impl
->
addImportPath
(
kj
::
mv
(
path
));
}
kj
::
Maybe
<
const
Module
&>
ModuleLoader
::
loadModule
(
kj
::
StringPtr
localName
,
kj
::
StringPtr
sourceName
)
const
{
return
impl
->
loadModule
(
localName
,
sourceName
);
}
}
// namespace compiler
}
// namespace capnp
c++/src/capnp/compiler/module-loader.h
0 → 100644
View file @
34e70d5a
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CAPNP_COMPILER_MODULE_LOADER_H_
#define CAPNP_COMPILER_MODULE_LOADER_H_
#include "compiler.h"
#include <kj/memory.h>
#include <kj/array.h>
#include <kj/string.h>
namespace
capnp
{
namespace
compiler
{
class
ModuleLoader
{
public
:
explicit
ModuleLoader
(
int
errorFd
);
// Create a ModuleLoader that writes error messages to the given file descriptor.
KJ_DISALLOW_COPY
(
ModuleLoader
);
~
ModuleLoader
();
void
addImportPath
(
kj
::
String
path
);
// Add a directory to the list of paths that is searched for imports that start with a '/'.
kj
::
Maybe
<
const
Module
&>
loadModule
(
kj
::
StringPtr
localName
,
kj
::
StringPtr
sourceName
)
const
;
// Tries to load the module with the given filename. `localName` is the path to the file on
// disk (as you'd pass to open(2)), and `sourceName` is the canonical name it should be given
// in the schema (this is used e.g. to decide output file locations). Often, these are the same.
private
:
class
Impl
;
kj
::
Own
<
Impl
>
impl
;
class
ModuleImpl
;
};
}
// namespace compiler
}
// namespace capnp
#endif // CAPNP_COMPILER_MODULE_LOADER_H_
c++/src/capnp/compiler/node-translator.c++
View file @
34e70d5a
...
...
@@ -570,7 +570,7 @@ schema::Node::Reader NodeTranslator::finish() {
// `unfinishedValues`, invalidating iterators in the process.
for
(
size_t
i
=
0
;
i
<
unfinishedValues
.
size
();
i
++
)
{
auto
&
value
=
unfinishedValues
[
i
];
compile
FinalValue
(
value
.
source
,
value
.
type
,
value
.
target
);
compile
Value
(
value
.
source
,
value
.
type
,
value
.
target
,
false
);
}
return
wipNode
.
getReader
();
...
...
@@ -1217,7 +1217,15 @@ bool NodeTranslator::compileType(TypeExpression::Reader source, schema::Type::Bu
return
false
;
}
if
(
!
compileType
(
params
[
0
],
target
.
getBody
().
initListType
()))
{
auto
elementType
=
target
.
getBody
().
initListType
();
if
(
!
compileType
(
params
[
0
],
elementType
))
{
return
false
;
}
if
(
elementType
.
getBody
().
which
()
==
schema
::
Type
::
Body
::
OBJECT_TYPE
)
{
errorReporter
.
addErrorOn
(
source
,
"'List(Object)' is not supported."
);
// Seeing List(Object) later can mess things up, so change the type to Void.
elementType
.
getBody
().
setVoidType
();
return
false
;
}
...
...
@@ -1291,266 +1299,316 @@ void NodeTranslator::compileDefaultDefaultValue(
}
}
class
NodeTranslator
::
DynamicSlot
{
public
:
DynamicSlot
(
DynamicStruct
::
Builder
structBuilder
,
StructSchema
::
Member
member
)
:
type
(
FIELD
),
structBuilder
(
structBuilder
),
member
(
member
)
{}
DynamicSlot
(
DynamicList
::
Builder
listBuilder
,
uint
index
)
:
type
(
ELEMENT
),
listBuilder
(
listBuilder
),
index
(
index
)
{}
DynamicSlot
(
DynamicStruct
::
Builder
structBuilder
,
StructSchema
::
Member
member
,
StructSchema
structMemberSchema
)
:
type
(
STRUCT_OBJECT_FIELD
),
structBuilder
(
structBuilder
),
member
(
member
),
structMemberSchema
(
structMemberSchema
)
{}
DynamicSlot
(
DynamicStruct
::
Builder
structBuilder
,
StructSchema
::
Member
member
,
ListSchema
listMemberSchema
)
:
type
(
LIST_OBJECT_FIELD
),
structBuilder
(
structBuilder
),
member
(
member
),
listMemberSchema
(
listMemberSchema
)
{}
DynamicSlot
(
DynamicUnion
::
Builder
unionBuilder
,
StructSchema
::
Member
unionMember
)
:
type
(
UNION_MEMBER
),
unionBuilder
(
unionBuilder
),
unionMember
(
unionMember
)
{}
bool
wasSet
=
false
;
DynamicStruct
::
Builder
initStruct
()
{
wasSet
=
true
;
switch
(
type
)
{
case
FIELD
:
return
structBuilder
.
init
(
member
).
as
<
DynamicStruct
>
();
case
ELEMENT
:
return
listBuilder
[
index
].
as
<
DynamicStruct
>
();
case
STRUCT_OBJECT_FIELD
:
return
structBuilder
.
initObject
(
member
,
structMemberSchema
);
case
LIST_OBJECT_FIELD
:
KJ_FAIL_REQUIRE
(
"Value type mismatch."
);
case
UNION_MEMBER
:
return
unionBuilder
.
init
(
unionMember
).
as
<
DynamicStruct
>
();
}
KJ_FAIL_ASSERT
(
"can't get here"
);
}
DynamicList
::
Builder
initList
(
uint
size
)
{
wasSet
=
true
;
switch
(
type
)
{
case
FIELD
:
return
structBuilder
.
init
(
member
,
size
).
as
<
DynamicList
>
();
case
ELEMENT
:
return
listBuilder
.
init
(
index
,
size
).
as
<
DynamicList
>
();
case
STRUCT_OBJECT_FIELD
:
KJ_FAIL_REQUIRE
(
"Value type mismatch."
);
case
LIST_OBJECT_FIELD
:
return
structBuilder
.
initObject
(
member
,
listMemberSchema
,
size
);
case
UNION_MEMBER
:
return
unionBuilder
.
init
(
unionMember
,
size
).
as
<
DynamicList
>
();
}
KJ_FAIL_ASSERT
(
"can't get here"
);
}
DynamicUnion
::
Builder
getUnion
()
{
wasSet
=
true
;
switch
(
type
)
{
case
FIELD
:
return
structBuilder
.
get
(
member
).
as
<
DynamicUnion
>
();
case
ELEMENT
:
KJ_FAIL_REQUIRE
(
"Value type mismatch."
);
case
STRUCT_OBJECT_FIELD
:
KJ_FAIL_REQUIRE
(
"Value type mismatch."
);
case
LIST_OBJECT_FIELD
:
KJ_FAIL_REQUIRE
(
"Value type mismatch."
);
case
UNION_MEMBER
:
return
unionBuilder
.
init
(
unionMember
).
as
<
DynamicUnion
>
();
}
KJ_FAIL_ASSERT
(
"can't get here"
);
}
void
set
(
DynamicValue
::
Reader
value
)
{
wasSet
=
true
;
switch
(
type
)
{
case
FIELD
:
return
structBuilder
.
set
(
member
,
value
);
case
ELEMENT
:
return
listBuilder
.
set
(
index
,
value
);
case
STRUCT_OBJECT_FIELD
:
return
structBuilder
.
set
(
member
,
value
);
case
LIST_OBJECT_FIELD
:
return
structBuilder
.
set
(
member
,
value
);
case
UNION_MEMBER
:
return
unionBuilder
.
set
(
unionMember
,
value
);
}
KJ_FAIL_ASSERT
(
"can't get here"
);
}
kj
::
Maybe
<
uint64_t
>
getEnumType
()
{
// If the member is an enum, get its type ID. Otherwise return nullptr.
//
// This is really ugly.
switch
(
type
)
{
case
FIELD
:
return
enumIdForMember
(
member
);
case
ELEMENT
:
{
if
(
listBuilder
.
getSchema
().
whichElementType
()
==
schema
::
Type
::
Body
::
ENUM_TYPE
)
{
return
listBuilder
.
getSchema
().
getEnumElementType
().
getProto
().
getId
();
}
return
nullptr
;
}
case
STRUCT_OBJECT_FIELD
:
return
nullptr
;
case
LIST_OBJECT_FIELD
:
return
nullptr
;
case
UNION_MEMBER
:
return
enumIdForMember
(
unionMember
);
}
KJ_FAIL_ASSERT
(
"can't get here"
);
}
private
:
enum
Type
{
FIELD
,
ELEMENT
,
STRUCT_OBJECT_FIELD
,
LIST_OBJECT_FIELD
,
UNION_MEMBER
};
Type
type
;
union
{
struct
{
DynamicStruct
::
Builder
structBuilder
;
StructSchema
::
Member
member
;
union
{
StructSchema
structMemberSchema
;
ListSchema
listMemberSchema
;
};
};
struct
{
DynamicList
::
Builder
listBuilder
;
uint
index
;
};
struct
{
DynamicUnion
::
Builder
unionBuilder
;
StructSchema
::
Member
unionMember
;
};
};
static
kj
::
Maybe
<
uint64_t
>
enumIdForMember
(
StructSchema
::
Member
member
)
{
auto
body
=
member
.
getProto
().
getBody
();
if
(
body
.
which
()
==
schema
::
StructNode
::
Member
::
Body
::
FIELD_MEMBER
)
{
auto
typeBody
=
body
.
getFieldMember
().
getType
().
getBody
();
if
(
typeBody
.
which
()
==
schema
::
Type
::
Body
::
ENUM_TYPE
)
{
return
typeBody
.
getEnumType
();
}
}
return
nullptr
;
}
};
static
kj
::
StringPtr
getValueUnionMemberNameFor
(
schema
::
Type
::
Body
::
Which
type
)
{
switch
(
type
)
{
case
schema
:
:
Type
::
Body
::
VOID_TYPE
:
return
"voidValue"
;
case
schema
:
:
Type
::
Body
::
BOOL_TYPE
:
return
"boolValue"
;
case
schema
:
:
Type
::
Body
::
INT8_TYPE
:
return
"int8Value"
;
case
schema
:
:
Type
::
Body
::
INT16_TYPE
:
return
"int16Value"
;
case
schema
:
:
Type
::
Body
::
INT32_TYPE
:
return
"int32Value"
;
case
schema
:
:
Type
::
Body
::
INT64_TYPE
:
return
"int64Value"
;
case
schema
:
:
Type
::
Body
::
UINT8_TYPE
:
return
"uint8Value"
;
case
schema
:
:
Type
::
Body
::
UINT16_TYPE
:
return
"uint16Value"
;
case
schema
:
:
Type
::
Body
::
UINT32_TYPE
:
return
"uint32Value"
;
case
schema
:
:
Type
::
Body
::
UINT64_TYPE
:
return
"uint64Value"
;
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
return
"float32Value"
;
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
return
"float64Value"
;
case
schema
:
:
Type
::
Body
::
TEXT_TYPE
:
return
"textValue"
;
case
schema
:
:
Type
::
Body
::
DATA_TYPE
:
return
"dataValue"
;
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
return
"listValue"
;
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
return
"enumValue"
;
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
return
"structValue"
;
case
schema
:
:
Type
::
Body
::
INTERFACE_TYPE
:
return
"interfaceValue"
;
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
return
"objectValue"
;
}
KJ_FAIL_ASSERT
(
"Unknown type."
);
}
void
NodeTranslator
::
compileBootstrapValue
(
ValueExpression
::
Reader
source
,
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Builder
target
)
{
switch
(
type
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
case
schema
:
:
Type
::
Body
::
INTERFACE_TYPE
:
// Handle later.
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
unfinishedValues
.
add
(
UnfinishedValue
{
source
,
type
,
target
});
return
;
break
;
default
:
// Primitive value.
compileValue
(
source
,
type
,
target
,
true
);
break
;
}
}
void
NodeTranslator
::
compileValue
(
ValueExpression
::
Reader
source
,
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Builder
target
,
bool
isBootstrap
)
{
auto
valueUnion
=
toDynamic
(
target
).
get
(
"body"
).
as
<
DynamicUnion
>
();
auto
member
=
valueUnion
.
getSchema
().
getMemberByName
(
getValueUnionMemberNameFor
(
type
.
getBody
().
which
()));
DynamicSlot
slot
(
valueUnion
,
member
);
compileValue
(
source
,
slot
,
isBootstrap
);
if
(
!
slot
.
wasSet
)
{
// An error should have been reported already. Initialize to a reasonable default.
compileDefaultDefaultValue
(
type
,
target
);
}
}
void
NodeTranslator
::
compileValue
(
ValueExpression
::
Reader
src
,
DynamicSlot
&
dst
,
bool
isBootstrap
)
{
// We rely on the dynamic API to detect type errors and throw exceptions.
//
// TODO(cleanup): We should perhaps ensure that all exceptions that this might throw are
// recoverable, so that this doesn't crash if -fno-exceptions is enabled. Or create a better
// way to test for type compatibility without throwing.
KJ_IF_MAYBE
(
exception
,
kj
::
runCatchingExceptions
(
[
&
]()
{
compileValueInner
(
src
,
dst
,
isBootstrap
);
}))
{
errorReporter
.
addErrorOn
(
src
,
exception
->
getDescription
());
dst
.
wasSet
=
false
;
}
}
switch
(
source
.
getBody
().
which
())
{
void
NodeTranslator
::
compileValueInner
(
ValueExpression
::
Reader
src
,
DynamicSlot
&
dst
,
bool
isBootstrap
)
{
switch
(
src
.
getBody
().
which
())
{
case
ValueExpression
:
:
Body
::
NAME
:
{
auto
name
=
s
ource
.
getBody
().
getName
();
auto
name
=
s
rc
.
getBody
().
getName
();
bool
isBare
=
name
.
getBase
().
which
()
==
DeclName
::
Base
::
RELATIVE_NAME
&&
name
.
getMemberPath
().
size
()
==
0
;
if
(
isBare
)
{
// The name is just a bare identifier. It may be a literal value or an enumerant.
kj
::
StringPtr
id
=
name
.
getBase
().
getRelativeName
().
getValue
();
switch
(
type
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
VOID_TYPE
:
if
(
id
==
"void"
)
{
target
.
getBody
().
setVoidValue
();
return
;
KJ_IF_MAYBE
(
enumId
,
dst
.
getEnumType
())
{
auto
enumSchema
=
resolver
.
resolveMaybeBootstrapSchema
(
*
enumId
).
asEnum
();
KJ_IF_MAYBE
(
enumerant
,
enumSchema
.
findEnumerantByName
(
id
))
{
dst
.
set
(
DynamicEnum
(
*
enumerant
))
;
}
break
;
case
schema
:
:
Type
::
Body
::
BOOL_TYPE
:
if
(
id
==
"true"
)
{
target
.
getBody
().
setBoolValue
(
true
);
return
;
}
else
{
// Interpret known constant values.
if
(
id
==
"void"
)
{
dst
.
set
(
Void
::
VOID
);
}
else
if
(
id
==
"true"
)
{
dst
.
set
(
true
);
}
else
if
(
id
==
"false"
)
{
target
.
getBody
().
setBoolValue
(
false
);
return
;
}
break
;
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
if
(
id
==
"nan"
)
{
target
.
getBody
().
setFloat32Value
(
std
::
numeric_limits
<
float
>::
quiet_NaN
());
return
;
}
else
if
(
id
==
"inf"
)
{
target
.
getBody
().
setFloat32Value
(
std
::
numeric_limits
<
float
>::
infinity
());
return
;
}
break
;
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
if
(
id
==
"nan"
)
{
target
.
getBody
().
setFloat64Value
(
std
::
numeric_limits
<
double
>::
quiet_NaN
());
return
;
dst
.
set
(
false
);
}
else
if
(
id
==
"nan"
)
{
dst
.
set
(
std
::
numeric_limits
<
double
>::
quiet_NaN
());
}
else
if
(
id
==
"inf"
)
{
target
.
getBody
().
setFloat64Value
(
std
::
numeric_limits
<
double
>::
infinity
());
return
;
}
break
;
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
KJ_IF_MAYBE
(
enumerant
,
resolver
.
resolveMaybeBootstrapSchema
(
type
.
getBody
().
getEnumType
()).
asEnum
().
findEnumerantByName
(
id
))
{
target
.
getBody
().
setEnumValue
(
enumerant
->
getOrdinal
());
return
;
}
break
;
default
:
break
;
}
dst
.
set
(
std
::
numeric_limits
<
double
>::
infinity
());
}
// Haven't resolved the name yet. Try looking it up.
KJ_IF_MAYBE
(
resolved
,
resolver
.
resolve
(
source
.
getBody
().
getName
()))
{
if
(
resolved
->
kind
!=
Declaration
::
Body
::
CONST_DECL
)
{
errorReporter
.
addErrorOn
(
source
,
kj
::
str
(
"'"
,
declNameString
(
name
),
"' does not refer to a constant."
));
compileDefaultDefaultValue
(
type
,
target
);
break
;
}
// We can get the bootstrap version of the constant here because if it has a
// non-bootstrap-time value then it's an error anyway.
Schema
constSchema
=
resolver
.
resolveMaybeBootstrapSchema
(
resolved
->
id
);
auto
constReader
=
constSchema
.
getProto
().
getBody
().
getConstNode
();
copyValue
(
constReader
.
getValue
(),
constReader
.
getType
(),
target
,
type
,
source
);
if
(
isBare
)
{
// A fully unqualified identifier looks like it might refer to a constant visible in the
// current scope, but if that's really what the user wanted, we want them to use a
// qualified name to make it more obvious. Report an error.
Schema
scope
=
resolver
.
resolveMaybeBootstrapSchema
(
constSchema
.
getProto
().
getScopeId
());
auto
scopeReader
=
scope
.
getProto
();
kj
::
StringPtr
parent
;
if
(
scopeReader
.
getBody
().
which
()
==
schema
::
Node
::
Body
::
FILE_NODE
)
{
parent
=
""
;
}
else
{
parent
=
scopeReader
.
getDisplayName
().
slice
(
scopeReader
.
getDisplayNamePrefixLength
());
}
kj
::
StringPtr
id
=
name
.
getBase
().
getRelativeName
().
getValue
();
errorReporter
.
addErrorOn
(
source
,
kj
::
str
(
"Constant names must be qualified to avoid confusion. Please replace '"
,
declNameString
(
name
),
"' with '"
,
parent
,
"."
,
id
,
"', if that's what you intended."
)
);
if
(
!
dst
.
wasSet
)
{
// Haven't resolved the name yet. Try looking up a constant.
KJ_IF_MAYBE
(
constValue
,
readConstant
(
src
.
getBody
().
getName
(),
isBootstrap
,
src
))
{
dst
.
set
(
*
constValue
);
}
}
break
;
}
case
ValueExpression
:
:
Body
::
POSITIVE_INT
:
{
uint64_t
value
=
source
.
getBody
().
getPositiveInt
();
uint64_t
limit
=
std
::
numeric_limits
<
uint64_t
>::
max
();
switch
(
type
.
getBody
().
which
())
{
#define HANDLE_TYPE(discrim, name, type) \
case schema::Type::Body::discrim##_TYPE: \
limit = std::numeric_limits<type>::max(); \
target.getBody().set##name##Value(value); \
break;
HANDLE_TYPE
(
INT8
,
Int8
,
int8_t
);
HANDLE_TYPE
(
INT16
,
Int16
,
int16_t
);
HANDLE_TYPE
(
INT32
,
Int32
,
int32_t
);
HANDLE_TYPE
(
INT64
,
Int64
,
int64_t
);
HANDLE_TYPE
(
UINT8
,
Uint8
,
uint8_t
);
HANDLE_TYPE
(
UINT16
,
Uint16
,
uint16_t
);
HANDLE_TYPE
(
UINT32
,
Uint32
,
uint32_t
);
HANDLE_TYPE
(
UINT64
,
Uint64
,
uint64_t
);
#undef HANDLE_TYPE
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
target
.
getBody
().
setFloat32Value
(
value
);
case
ValueExpression
:
:
Body
::
POSITIVE_INT
:
dst
.
set
(
src
.
getBody
().
getPositiveInt
());
break
;
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
target
.
getBody
().
setFloat64Value
(
value
);
break
;
default
:
errorReporter
.
addErrorOn
(
source
,
"Type/value mismatch."
);
compileDefaultDefaultValue
(
type
,
target
);
break
;
}
if
(
value
>
limit
)
{
errorReporter
.
addErrorOn
(
source
,
"Value out-of-range for type."
);
case
ValueExpression
:
:
Body
::
NEGATIVE_INT
:
{
uint64_t
nValue
=
src
.
getBody
().
getNegativeInt
();
if
(
nValue
>
(
std
::
numeric_limits
<
uint64_t
>::
max
()
>>
1
)
+
1
)
{
errorReporter
.
addErrorOn
(
src
,
"Integer is too big to be negative."
);
}
else
{
dst
.
set
(
kj
::
implicitCast
<
int64_t
>
(
-
nValue
));
}
break
;
}
case
ValueExpression
:
:
Body
::
NEGATIVE_INT
:
{
uint64_t
value
=
source
.
getBody
().
getNegativeInt
();
uint64_t
limit
=
std
::
numeric_limits
<
uint64_t
>::
max
();
switch
(
type
.
getBody
().
which
())
{
#define HANDLE_TYPE(discrim, name, type) \
case schema::Type::Body::discrim##_TYPE: \
limit = kj::implicitCast<uint64_t>(std::numeric_limits<type>::max()) + 1; \
target.getBody().set##name##Value(-value); \
case
ValueExpression
:
:
Body
::
FLOAT
:
dst
.
set
(
src
.
getBody
().
getFloat
());
break
;
HANDLE_TYPE
(
INT8
,
Int8
,
int8_t
);
HANDLE_TYPE
(
INT16
,
Int16
,
int16_t
);
HANDLE_TYPE
(
INT32
,
Int32
,
int32_t
);
HANDLE_TYPE
(
INT64
,
Int64
,
int64_t
);
#undef HANDLE_TYPE
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
target
.
getBody
().
setFloat32Value
(
-
kj
::
implicitCast
<
float
>
(
value
));
break
;
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
target
.
getBody
().
setFloat64Value
(
-
kj
::
implicitCast
<
double
>
(
value
));
break
;
default
:
errorReporter
.
addErrorOn
(
source
,
"Type/value mismatch."
);
compileDefaultDefaultValue
(
type
,
target
);
case
ValueExpression
:
:
Body
::
STRING
:
dst
.
set
(
src
.
getBody
().
getString
());
break
;
}
if
(
value
>
limit
)
{
errorReporter
.
addErrorOn
(
source
,
"Value out-of-range for type."
);
case
ValueExpression
:
:
Body
::
LIST
:
{
auto
srcList
=
src
.
getBody
().
getList
();
auto
dstList
=
dst
.
initList
(
srcList
.
size
());
for
(
uint
i
=
0
;
i
<
srcList
.
size
();
i
++
)
{
DynamicSlot
slot
(
dstList
,
i
);
compileValue
(
srcList
[
i
],
slot
,
isBootstrap
);
}
break
;
}
case
ValueExpression
:
:
Body
::
FLOAT
:
{
switch
(
type
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
target
.
getBody
().
setFloat32Value
(
source
.
getBody
().
getFloat
());
break
;
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
target
.
getBody
().
setFloat64Value
(
source
.
getBody
().
getFloat
());
break
;
default
:
errorReporter
.
addErrorOn
(
source
,
"Type/value mismatch."
);
compileDefaultDefaultValue
(
type
,
target
);
break
;
case
ValueExpression
:
:
Body
::
STRUCT_VALUE
:
{
auto
srcStruct
=
src
.
getBody
().
getStructValue
();
auto
dstStruct
=
dst
.
initStruct
();
auto
dstSchema
=
dstStruct
.
getSchema
();
for
(
auto
assignment
:
srcStruct
)
{
auto
fieldName
=
assignment
.
getFieldName
();
KJ_IF_MAYBE
(
member
,
dstSchema
.
findMemberByName
(
fieldName
.
getValue
()))
{
DynamicSlot
slot
(
dstStruct
,
*
member
);
compileValue
(
assignment
.
getValue
(),
slot
,
isBootstrap
);
}
else
{
errorReporter
.
addErrorOn
(
fieldName
,
kj
::
str
(
"Value has no field named '"
,
fieldName
.
getValue
(),
"'."
));
}
}
break
;
}
case
ValueExpression
:
:
Body
::
UNION_VALUE
:
{
auto
srcUnion
=
src
.
getBody
().
getUnionValue
();
auto
dstUnion
=
dst
.
getUnion
();
case
ValueExpression
:
:
Body
::
STRING
:
{
switch
(
type
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
TEXT_TYPE
:
target
.
getBody
().
setTextValue
(
source
.
getBody
().
getString
());
break
;
case
schema
:
:
Type
::
Body
::
DATA_TYPE
:
{
auto
str
=
source
.
getBody
().
getString
();
target
.
getBody
().
setDataValue
(
kj
::
arrayPtr
(
reinterpret_cast
<
const
byte
*>
(
str
.
begin
()),
str
.
size
()));
break
;
auto
fieldName
=
srcUnion
.
getFieldName
();
KJ_IF_MAYBE
(
member
,
dstUnion
.
getSchema
().
findMemberByName
(
fieldName
.
getValue
()))
{
DynamicSlot
slot
(
dstUnion
,
*
member
);
compileValue
(
srcUnion
.
getValue
(),
slot
,
isBootstrap
);
}
else
{
errorReporter
.
addErrorOn
(
fieldName
,
kj
::
str
(
"Union has no field named '"
,
fieldName
.
getValue
(),
"'."
));
}
default
:
errorReporter
.
addErrorOn
(
source
,
"Type/value mismatch."
);
compileDefaultDefaultValue
(
type
,
target
);
break
;
}
}
case
ValueExpression
:
:
Body
::
LIST
:
case
ValueExpression
:
:
Body
::
STRUCT_VALUE
:
case
ValueExpression
:
:
Body
::
UNION_VALUE
:
// If the type matched, these cases should have been handled earlier.
errorReporter
.
addErrorOn
(
source
,
"Type/value mismatch."
);
compileDefaultDefaultValue
(
type
,
target
);
break
;
case
ValueExpression
:
:
Body
::
UNKNOWN
:
// Ignore earlier error.
compileDefaultDefaultValue
(
type
,
target
);
break
;
}
}
void
NodeTranslator
::
compileFinalValue
(
ValueExpression
::
Reader
source
,
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Builder
target
)
{
}
void
NodeTranslator
::
copyValue
(
schema
::
Value
::
Reader
src
,
schema
::
Type
::
Reader
srcType
,
schema
::
Value
::
Builder
dst
,
schema
::
Type
::
Reader
dstType
,
ValueExpression
::
Reader
errorLocation
)
{
DynamicUnion
::
Reader
srcBody
=
DynamicStruct
::
Reader
(
src
).
get
(
"body"
).
as
<
DynamicUnion
>
();
DynamicUnion
::
Builder
dstBody
=
DynamicStruct
::
Builder
(
dst
).
get
(
"body"
).
as
<
DynamicUnion
>
();
kj
::
StringPtr
dstFieldName
;
switch
(
dstType
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
VOID_TYPE
:
dstFieldName
=
"voidValue"
;
break
;
case
schema
:
:
Type
::
Body
::
BOOL_TYPE
:
dstFieldName
=
"boolValue"
;
break
;
case
schema
:
:
Type
::
Body
::
INT8_TYPE
:
dstFieldName
=
"int8Value"
;
break
;
case
schema
:
:
Type
::
Body
::
INT16_TYPE
:
dstFieldName
=
"int16Value"
;
break
;
case
schema
:
:
Type
::
Body
::
INT32_TYPE
:
dstFieldName
=
"int32Value"
;
break
;
case
schema
:
:
Type
::
Body
::
INT64_TYPE
:
dstFieldName
=
"int64Value"
;
break
;
case
schema
:
:
Type
::
Body
::
UINT8_TYPE
:
dstFieldName
=
"uint8Value"
;
break
;
case
schema
:
:
Type
::
Body
::
UINT16_TYPE
:
dstFieldName
=
"uint16Value"
;
break
;
case
schema
:
:
Type
::
Body
::
UINT32_TYPE
:
dstFieldName
=
"uint32Value"
;
break
;
case
schema
:
:
Type
::
Body
::
UINT64_TYPE
:
dstFieldName
=
"uint64Value"
;
break
;
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
dstFieldName
=
"float32Value"
;
break
;
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
dstFieldName
=
"float64Value"
;
break
;
case
schema
:
:
Type
::
Body
::
TEXT_TYPE
:
dstFieldName
=
"textValue"
;
break
;
case
schema
:
:
Type
::
Body
::
DATA_TYPE
:
dstFieldName
=
"dataValue"
;
break
;
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
dstFieldName
=
"listValue"
;
break
;
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
dstFieldName
=
"enumValue"
;
break
;
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
dstFieldName
=
"structValue"
;
break
;
case
schema
:
:
Type
::
Body
::
INTERFACE_TYPE
:
dstFieldName
=
"interfaceValue"
;
break
;
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
dstFieldName
=
"objectValue"
;
break
;
}
kj
::
StringPtr
dstFieldName
=
getValueUnionMemberNameFor
(
dstType
.
getBody
().
which
());
KJ_IF_MAYBE
(
which
,
srcBody
.
which
())
{
// Setting a value via the dynamic API implements the implicit conversions that we want, with
...
...
@@ -1567,10 +1625,147 @@ void NodeTranslator::copyValue(schema::Value::Reader src, schema::Type::Reader s
}
}
kj
::
Maybe
<
DynamicValue
::
Reader
>
NodeTranslator
::
readConstant
(
DeclName
::
Reader
name
,
bool
isBootstrap
,
ValueExpression
::
Reader
errorLocation
)
{
KJ_IF_MAYBE
(
resolved
,
resolver
.
resolve
(
name
))
{
if
(
resolved
->
kind
!=
Declaration
::
Body
::
CONST_DECL
)
{
errorReporter
.
addErrorOn
(
errorLocation
,
kj
::
str
(
"'"
,
declNameString
(
name
),
"' does not refer to a constant."
));
return
nullptr
;
}
// If we're bootstrapping, then we know we're expecting a primitive value, so if the
// constant turns out to be non-primitive, we'll error out anyway. If we're not
// bootstrapping, we may be compiling a non-primitive value and so we need the final
// version of the constant to make sure its value is filled in.
//
// We need to be very careful not to query this Schema's dependencies because if it is
// a final schema then this query could trigger a lazy load which would deadlock.
Schema
constSchema
=
isBootstrap
?
resolver
.
resolveMaybeBootstrapSchema
(
resolved
->
id
)
:
resolver
.
resolveFinalSchema
(
resolved
->
id
);
auto
constReader
=
constSchema
.
getProto
().
getBody
().
getConstNode
();
auto
constValue
=
toDynamic
(
constReader
.
getValue
()).
get
(
"body"
).
as
<
DynamicUnion
>
().
get
();
if
(
constValue
.
getType
()
==
DynamicValue
::
OBJECT
)
{
// We need to assign an appropriate schema to this object.
DynamicObject
objValue
=
constValue
.
as
<
DynamicObject
>
();
auto
constType
=
constReader
.
getType
();
switch
(
constType
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
constValue
=
objValue
.
as
(
resolver
.
resolveMaybeBootstrapSchema
(
constType
.
getBody
().
getStructType
()).
asStruct
());
break
;
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
constValue
=
objValue
.
as
(
makeListSchemaOf
(
constType
.
getBody
().
getListType
()));
break
;
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
// Fine as-is.
break
;
default
:
KJ_FAIL_ASSERT
(
"Unrecognized Object-typed member of schema::Value::body."
);
break
;
}
}
if
(
name
.
getBase
().
which
()
==
DeclName
::
Base
::
RELATIVE_NAME
&&
name
.
getMemberPath
().
size
()
==
0
)
{
// A fully unqualified identifier looks like it might refer to a constant visible in the
// current scope, but if that's really what the user wanted, we want them to use a
// qualified name to make it more obvious. Report an error.
Schema
scope
=
resolver
.
resolveMaybeBootstrapSchema
(
constSchema
.
getProto
().
getScopeId
());
auto
scopeReader
=
scope
.
getProto
();
kj
::
StringPtr
parent
;
if
(
scopeReader
.
getBody
().
which
()
==
schema
::
Node
::
Body
::
FILE_NODE
)
{
parent
=
""
;
}
else
{
parent
=
scopeReader
.
getDisplayName
().
slice
(
scopeReader
.
getDisplayNamePrefixLength
());
}
kj
::
StringPtr
id
=
name
.
getBase
().
getRelativeName
().
getValue
();
errorReporter
.
addErrorOn
(
errorLocation
,
kj
::
str
(
"Constant names must be qualified to avoid confusion. Please replace '"
,
declNameString
(
name
),
"' with '"
,
parent
,
"."
,
id
,
"', if that's what you intended."
));
}
return
constValue
;
}
else
{
return
nullptr
;
}
}
ListSchema
NodeTranslator
::
makeListSchemaOf
(
schema
::
Type
::
Reader
elementType
)
{
auto
body
=
elementType
.
getBody
();
switch
(
body
.
which
())
{
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
return
ListSchema
::
of
(
resolver
.
resolveMaybeBootstrapSchema
(
body
.
getEnumType
()).
asEnum
());
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
return
ListSchema
::
of
(
resolver
.
resolveMaybeBootstrapSchema
(
body
.
getStructType
()).
asStruct
());
case
schema
:
:
Type
::
Body
::
INTERFACE_TYPE
:
return
ListSchema
::
of
(
resolver
.
resolveMaybeBootstrapSchema
(
body
.
getInterfaceType
())
.
asInterface
());
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
return
ListSchema
::
of
(
makeListSchemaOf
(
body
.
getListType
()));
default
:
return
ListSchema
::
of
(
body
.
which
());
}
}
Orphan
<
List
<
schema
::
Annotation
>>
NodeTranslator
::
compileAnnotationApplications
(
List
<
Declaration
::
AnnotationApplication
>::
Reader
annotations
,
kj
::
StringPtr
targetsFlagName
)
{
if
(
annotations
.
size
()
==
0
)
{
// Return null.
return
Orphan
<
List
<
schema
::
Annotation
>>
();
}
Orphanage
orphanage
=
Orphanage
::
getForMessageContaining
(
wipNode
.
get
());
auto
result
=
orphanage
.
newOrphan
<
List
<
schema
::
Annotation
>>
(
annotations
.
size
());
auto
builder
=
result
.
get
();
for
(
uint
i
=
0
;
i
<
annotations
.
size
();
i
++
)
{
Declaration
::
AnnotationApplication
::
Reader
annotation
=
annotations
[
i
];
schema
::
Annotation
::
Builder
annotationBuilder
=
builder
[
i
];
auto
name
=
annotation
.
getName
();
KJ_IF_MAYBE
(
decl
,
resolver
.
resolve
(
name
))
{
if
(
decl
->
kind
!=
Declaration
::
Body
::
ANNOTATION_DECL
)
{
errorReporter
.
addErrorOn
(
name
,
kj
::
str
(
"'"
,
declNameString
(
name
),
"' is not an annotation."
));
}
else
{
annotationBuilder
.
setId
(
decl
->
id
);
auto
node
=
resolver
.
resolveMaybeBootstrapSchema
(
decl
->
id
).
getProto
()
.
getBody
().
getAnnotationNode
();
if
(
!
toDynamic
(
node
).
get
(
targetsFlagName
).
as
<
bool
>
())
{
errorReporter
.
addErrorOn
(
name
,
kj
::
str
(
"'"
,
declNameString
(
name
),
"' cannot be applied to this kind of declaration."
));
}
// Interpret the value.
auto
value
=
annotation
.
getValue
();
switch
(
value
.
which
())
{
case
Declaration
:
:
AnnotationApplication
::
Value
::
NONE
:
// No value, i.e. void.
if
(
node
.
getType
().
getBody
().
which
()
==
schema
::
Type
::
Body
::
VOID_TYPE
)
{
annotationBuilder
.
initValue
().
getBody
().
setVoidValue
();
}
else
{
errorReporter
.
addErrorOn
(
name
,
kj
::
str
(
"'"
,
declNameString
(
name
),
"' requires a value."
));
compileDefaultDefaultValue
(
node
.
getType
(),
annotationBuilder
.
initValue
());
}
break
;
case
Declaration
:
:
AnnotationApplication
::
Value
::
EXPRESSION
:
compileBootstrapValue
(
value
.
getExpression
(),
node
.
getType
(),
annotationBuilder
.
initValue
());
break
;
}
}
}
}
return
result
;
}
}
// namespace compiler
...
...
c++/src/capnp/compiler/node-translator.h
View file @
34e70d5a
...
...
@@ -138,13 +138,21 @@ private:
void
compileBootstrapValue
(
ValueExpression
::
Reader
source
,
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Builder
target
);
// Interprets the value expression and initializes `target` with the result. If some parts of
// the value cannot be built at bootstrap time, they'll be added to `unfinishedValues`
// automatically for later processing.
// Calls compileValue() if this value should be interpreted at bootstrap time. Otheriwse,
// adds the value to `unfinishedValues` for later evaluation.
void
compileFinalValue
(
ValueExpression
::
Reader
source
,
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Builder
target
);
// Compile a previously-unfinished value. See `unfinishedValues`.
void
compileValue
(
ValueExpression
::
Reader
source
,
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Builder
target
,
bool
isBootstrap
);
// Interprets the value expression and initializes `target` with the result.
class
DynamicSlot
;
void
compileValue
(
ValueExpression
::
Reader
src
,
DynamicSlot
&
dst
,
bool
isBootstrap
);
// Fill in `dst` (which effectively points to a struct field or list element) with the given
// value.
void
compileValueInner
(
ValueExpression
::
Reader
src
,
DynamicSlot
&
dst
,
bool
isBootstrap
);
// Helper for compileValue().
void
copyValue
(
schema
::
Value
::
Reader
src
,
schema
::
Type
::
Reader
srcType
,
schema
::
Value
::
Builder
dst
,
schema
::
Type
::
Reader
dstType
,
...
...
@@ -152,6 +160,13 @@ private:
// Copy a value from one schema to another, possibly coercing the type if compatible, or
// reporting an error otherwise.
kj
::
Maybe
<
DynamicValue
::
Reader
>
readConstant
(
DeclName
::
Reader
name
,
bool
isBootstrap
,
ValueExpression
::
Reader
errorLocation
);
// Get the value of the given constant.
ListSchema
makeListSchemaOf
(
schema
::
Type
::
Reader
elementType
);
// Construct a list schema representing a list of elements of the given type.
Orphan
<
List
<
schema
::
Annotation
>>
compileAnnotationApplications
(
List
<
Declaration
::
AnnotationApplication
>::
Reader
annotations
,
kj
::
StringPtr
targetsFlagName
);
...
...
c++/src/capnp/compiler/parser.c++
View file @
34e70d5a
...
...
@@ -51,7 +51,7 @@ uint64_t randomId() {
}
// namespace
void
parseFile
(
List
<
Statement
>::
Reader
statements
,
ParsedFile
::
Builder
result
,
ErrorReporter
&
errorReporter
)
{
const
ErrorReporter
&
errorReporter
)
{
CapnpParser
parser
(
Orphanage
::
getForMessageContaining
(
result
),
errorReporter
);
kj
::
Vector
<
Orphan
<
Declaration
>>
decls
(
statements
.
size
());
...
...
@@ -203,7 +203,7 @@ class ParseListItems {
// Transformer that parses all items in the input token sequence list using the given parser.
public
:
constexpr
ParseListItems
(
ItemParser
&&
itemParser
,
ErrorReporter
&
errorReporter
)
constexpr
ParseListItems
(
ItemParser
&&
itemParser
,
const
ErrorReporter
&
errorReporter
)
:
itemParser
(
p
::
sequence
(
kj
::
fwd
<
ItemParser
>
(
itemParser
),
p
::
endOfInput
)),
errorReporter
(
errorReporter
)
{}
...
...
@@ -241,11 +241,12 @@ public:
private
:
decltype
(
p
::
sequence
(
kj
::
instance
<
ItemParser
>
(),
p
::
endOfInput
))
itemParser
;
ErrorReporter
&
errorReporter
;
const
ErrorReporter
&
errorReporter
;
};
template
<
typename
ItemParser
>
constexpr
auto
parenthesizedList
(
ItemParser
&&
itemParser
,
ErrorReporter
&
errorReporter
)
->
decltype
(
constexpr
auto
parenthesizedList
(
ItemParser
&&
itemParser
,
const
ErrorReporter
&
errorReporter
)
->
decltype
(
transform
(
rawParenthesizedList
,
ParseListItems
<
ItemParser
>
(
kj
::
fwd
<
ItemParser
>
(
itemParser
),
errorReporter
)))
{
return
transform
(
rawParenthesizedList
,
ParseListItems
<
ItemParser
>
(
...
...
@@ -253,7 +254,8 @@ constexpr auto parenthesizedList(ItemParser&& itemParser, ErrorReporter& errorRe
}
template
<
typename
ItemParser
>
constexpr
auto
bracketedList
(
ItemParser
&&
itemParser
,
ErrorReporter
&
errorReporter
)
->
decltype
(
constexpr
auto
bracketedList
(
ItemParser
&&
itemParser
,
const
ErrorReporter
&
errorReporter
)
->
decltype
(
transform
(
rawBracketedList
,
ParseListItems
<
ItemParser
>
(
kj
::
fwd
<
ItemParser
>
(
itemParser
),
errorReporter
)))
{
return
transform
(
rawBracketedList
,
ParseListItems
<
ItemParser
>
(
...
...
@@ -313,9 +315,9 @@ void initLocation(kj::parse::Span<List<Token>::Reader::Iterator> location,
// =======================================================================================
CapnpParser
::
CapnpParser
(
Orphanage
orphanageParam
,
ErrorReporter
&
errorReporterParam
)
CapnpParser
::
CapnpParser
(
Orphanage
orphanageParam
,
const
ErrorReporter
&
errorReporterParam
)
:
orphanage
(
orphanageParam
),
errorReporter
(
errorReporterParam
)
{
parsers
.
declName
=
arena
.
copy
(
p
::
transform
(
parsers
.
declName
=
arena
.
copy
(
p
::
transform
WithLocation
(
p
::
sequence
(
p
::
oneOf
(
p
::
transform
(
p
::
sequence
(
keyword
(
"import"
),
stringLiteral
),
...
...
@@ -337,12 +339,15 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP
return
result
;
})),
p
::
many
(
p
::
sequence
(
op
(
"."
),
identifier
))),
[
this
](
Orphan
<
DeclName
>&&
result
,
kj
::
Array
<
Located
<
Text
::
Reader
>>&&
memberPath
)
[
this
](
kj
::
parse
::
Span
<
List
<
Token
>::
Reader
::
Iterator
>
location
,
Orphan
<
DeclName
>&&
result
,
kj
::
Array
<
Located
<
Text
::
Reader
>>&&
memberPath
)
->
Orphan
<
DeclName
>
{
auto
builder
=
result
.
get
().
initMemberPath
(
memberPath
.
size
());
auto
builder
=
result
.
get
();
auto
pathBuilder
=
builder
.
initMemberPath
(
memberPath
.
size
());
for
(
size_t
i
=
0
;
i
<
memberPath
.
size
();
i
++
)
{
memberPath
[
i
].
copyTo
(
b
uilder
[
i
]);
memberPath
[
i
].
copyTo
(
pathB
uilder
[
i
]);
}
initLocation
(
location
,
builder
);
return
kj
::
mv
(
result
);
}));
...
...
c++/src/capnp/compiler/parser.h
View file @
34e70d5a
...
...
@@ -34,7 +34,7 @@ namespace capnp {
namespace
compiler
{
void
parseFile
(
List
<
Statement
>::
Reader
statements
,
ParsedFile
::
Builder
result
,
ErrorReporter
&
errorReporter
);
const
ErrorReporter
&
errorReporter
);
// Parse a list of statements to build a ParsedFile.
//
// If any errors are reported, then the output is not usable. However, it may be passed on through
...
...
@@ -45,7 +45,7 @@ class CapnpParser {
// them into your own parsers.
public
:
CapnpParser
(
Orphanage
orphanage
,
ErrorReporter
&
errorReporter
);
CapnpParser
(
Orphanage
orphanage
,
const
ErrorReporter
&
errorReporter
);
// `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is
// a pointer to the beginning of the input, used to compute byte offsets.
...
...
@@ -127,7 +127,7 @@ public:
private
:
Orphanage
orphanage
;
ErrorReporter
&
errorReporter
;
const
ErrorReporter
&
errorReporter
;
kj
::
Arena
arena
;
Parsers
parsers
;
};
...
...
c++/src/capnp/dynamic.h
View file @
34e70d5a
...
...
@@ -109,6 +109,8 @@ DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value);
class
DynamicEnum
{
public
:
DynamicEnum
()
=
default
;
inline
DynamicEnum
(
EnumSchema
::
Enumerant
enumerant
)
:
schema
(
enumerant
.
getContainingEnum
()),
value
(
enumerant
.
getOrdinal
())
{}
template
<
typename
T
,
typename
=
kj
::
EnableIf
<
kind
<
T
>
()
==
Kind
::
ENUM
>>
inline
DynamicEnum
(
T
&&
value
)
:
DynamicEnum
(
toDynamic
(
value
))
{}
...
...
c++/src/kj/string.h
View file @
34e70d5a
...
...
@@ -138,6 +138,11 @@ public:
inline
bool
startsWith
(
const
StringPtr
&
other
)
const
{
return
StringPtr
(
*
this
).
startsWith
(
other
);}
inline
bool
endsWith
(
const
StringPtr
&
other
)
const
{
return
StringPtr
(
*
this
).
endsWith
(
other
);
}
inline
StringPtr
slice
(
size_t
start
)
const
{
return
StringPtr
(
*
this
).
slice
(
start
);
}
inline
ArrayPtr
<
const
char
>
slice
(
size_t
start
,
size_t
end
)
const
{
return
StringPtr
(
*
this
).
slice
(
start
,
end
);
}
private
:
Array
<
char
>
content
;
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment