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
0da57538
Commit
0da57538
authored
Jul 25, 2013
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Compiler core WIP.
parent
0391156a
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
2191 additions
and
15 deletions
+2191
-15
capnpc2.c++
c++/src/capnp/compiler/capnpc2.c++
+4
-4
compiler.c++
c++/src/capnp/compiler/compiler.c++
+723
-0
compiler.h
c++/src/capnp/compiler/compiler.h
+99
-0
error-reporter.h
c++/src/capnp/compiler/error-reporter.h
+9
-1
grammar.capnp
c++/src/capnp/compiler/grammar.capnp
+30
-1
lexer-test.c++
c++/src/capnp/compiler/lexer-test.c++
+1
-1
node-translator.c++
c++/src/capnp/compiler/node-translator.c++
+1142
-0
node-translator.h
c++/src/capnp/compiler/node-translator.h
+154
-0
parser.c++
c++/src/capnp/compiler/parser.c++
+4
-3
schema.capnp
c++/src/capnp/schema.capnp
+21
-1
array.h
c++/src/kj/array.h
+2
-2
vector.h
c++/src/kj/vector.h
+2
-2
No files found.
c++/src/capnp/compiler/capnpc2.c++
View file @
0da57538
...
...
@@ -31,10 +31,10 @@
#include "../message.h"
#include <iostream>
class
C
out
ErrorReporter
:
public
capnp
::
compiler
::
ErrorReporter
{
class
C
err
ErrorReporter
:
public
capnp
::
compiler
::
ErrorReporter
{
public
:
void
addError
(
uint32_t
startByte
,
uint32_t
endByte
,
kj
::
StringPtr
message
)
override
{
std
::
c
out
<<
"input:"
<<
startByte
<<
"-"
<<
endByte
<<
": "
<<
message
.
cStr
()
<<
std
::
endl
;
void
addError
(
uint32_t
startByte
,
uint32_t
endByte
,
kj
::
StringPtr
message
)
const
override
{
std
::
c
err
<<
"input:"
<<
startByte
<<
"-"
<<
endByte
<<
": "
<<
message
.
cStr
()
<<
std
::
endl
;
}
};
...
...
@@ -52,7 +52,7 @@ int main(int argc, char* argv[]) {
input
.
addAll
(
buffer
,
buffer
+
n
);
}
C
out
ErrorReporter
errorReporter
;
C
err
ErrorReporter
errorReporter
;
std
::
cout
<<
"=========================================================================
\n
"
<<
"lex
\n
"
...
...
c++/src/capnp/compiler/compiler.c++
0 → 100644
View file @
0da57538
// 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 "compiler.h"
#include <kj/mutex.h>
#include <kj/arena.h>
#include <kj/vector.h>
#include <kj/debug.h>
#include <capnp/message.h>
#include <map>
#include <unordered_map>
#include "node-translator.h"
#include "md5.h"
namespace
capnp
{
namespace
compiler
{
class
Compiler
::
Alias
{
public
:
Alias
(
const
Node
&
parent
,
const
DeclName
::
Reader
&
targetName
)
:
parent
(
parent
),
targetName
(
targetName
)
{}
kj
::
Maybe
<
const
Node
&>
getTarget
()
const
;
private
:
const
Node
&
parent
;
DeclName
::
Reader
targetName
;
kj
::
Lazy
<
kj
::
Maybe
<
const
Node
&>>
target
;
};
class
Compiler
::
Node
:
public
NodeTranslator
::
Resolver
{
// Passes through four states:
// - Stub: On initial construction, the Node is just a placeholder object. Its ID has been
// determined, and it is placed in its parent's member table as well as the compiler's
// nodes-by-ID table.
// - Expanded: Nodes have been constructed for all of this Node's nested children. This happens
// the first time a lookup is performed for one of those children.
// - Bootstrap: A NodeTranslator has been built and advanced to the bootstrap phase.
// - Finished: A final Schema object has been constructed.
public
:
explicit
Node
(
CompiledModule
&
module
);
// Create a root node representing the given file. May
Node
(
const
Node
&
parent
,
const
Declaration
::
Reader
&
declaration
);
// Create a child node.
Node
(
kj
::
StringPtr
name
,
Declaration
::
Body
::
Which
kind
);
// Create a dummy node representing a built-in declaration, like "Int32" or "true".
uint64_t
getId
()
{
return
id
;
}
Declaration
::
Body
::
Which
getKind
()
{
return
kind
;
}
kj
::
Maybe
<
const
Node
&>
lookupMember
(
kj
::
StringPtr
name
)
const
;
// Find a direct member of this node with the given name.
kj
::
Maybe
<
const
Node
&>
lookupLexical
(
kj
::
StringPtr
name
)
const
;
// Look up the given name first as a member of this Node, then in its parent, and so on, until
// it is found or there are no more parents to search.
kj
::
Maybe
<
const
Node
&>
lookup
(
const
DeclName
::
Reader
&
name
)
const
;
// Resolve an arbitrary DeclName to a Node.
Schema
getBootstrapOrFinalSchema
()
const
;
Schema
getFinalSchema
()
const
;
void
addError
(
kj
::
StringPtr
error
)
const
;
// Report an error on this Node.
// implements NodeTranslator::Resolver -----------------------------
kj
::
Maybe
<
ResolvedName
>
resolve
(
const
DeclName
::
Reader
&
name
)
const
override
;
kj
::
Maybe
<
Schema
>
resolveMaybeBootstrapSchema
(
uint64_t
id
)
const
override
;
kj
::
Maybe
<
Schema
>
resolveFinalSchema
(
uint64_t
id
)
const
override
;
private
:
const
CompiledModule
*
module
;
// null iff isBuiltin is true
kj
::
Maybe
<
const
Node
&>
parent
;
Declaration
::
Reader
declaration
;
// AST of the declaration parsed from the schema file. May become invalid once the content
// state has reached FINISHED.
uint64_t
id
;
// The ID of this node, either taken from the AST or computed based on the parent. Or, a dummy
// value, if duplicates were detected.
kj
::
StringPtr
displayName
;
// Fully-qualified display name for this node. For files, this is just the file name, otherwise
// it is "filename:Path.To.Decl".
Declaration
::
Body
::
Which
kind
;
// Kind of node.
bool
isBuiltin
;
// Whether this is a bulit-in declaration, like "Int32" or "true".
uint32_t
startByte
;
uint32_t
endByte
;
// Start and end byte for reporting general errors.
struct
Content
{
inline
Content
()
:
state
(
STUB
)
{}
enum
State
{
STUB
,
EXPANDED
,
BOOTSTRAP
,
FINISHED
};
State
state
;
// Indicates which fields below are valid. Must update with atomic-release semantics.
inline
bool
stateHasReached
(
State
minimumState
)
const
{
return
__atomic_load_n
(
&
state
,
__ATOMIC_ACQUIRE
)
>=
minimumState
;
}
inline
void
advanceState
(
State
newState
)
{
__atomic_store_n
(
&
state
,
newState
,
__ATOMIC_RELEASE
);
}
// EXPANDED ------------------------------------
typedef
std
::
multimap
<
kj
::
StringPtr
,
kj
::
Own
<
Node
>>
NestedNodesMap
;
NestedNodesMap
nestedNodes
;
// Filled in when lookupMember() is first called. multimap in case of duplicate member names --
// we still want to compile them, even if it's an error.
typedef
std
::
multimap
<
kj
::
StringPtr
,
kj
::
Own
<
Alias
>>
AliasMap
;
AliasMap
aliases
;
//
// BOOTSTRAP -----------------------------------
NodeTranslator
*
translator
;
// Node translator, allocated in the bootstrap arena.
Schema
bootstrapSchema
;
// The schema built in the bootstrap loader.
// FINISHED ------------------------------------
Schema
finalSchema
;
// The complete schema as loaded by the compiler's main SchemaLoader.
};
kj
::
MutexGuarded
<
Content
>
content
;
// ---------------------------------------------
static
uint64_t
generateId
(
uint64_t
parentId
,
kj
::
StringPtr
declName
,
Declaration
::
Id
::
Reader
declId
);
// Extract the ID from the declaration, or if it has none, generate one based on the name and
// parent ID.
static
kj
::
StringPtr
joinDisplayName
(
const
kj
::
Arena
&
arena
,
const
Node
&
parent
,
kj
::
StringPtr
declName
);
// Join the parent's display name with the child's unqualified name to construct the child's
// display name.
const
Content
&
getContent
(
Content
::
State
minimumState
)
const
;
// Advances the content to at least the given state and returns it. Does not lock if the content
// is already at or past the given state.
};
class
Compiler
::
CompiledModule
{
public
:
CompiledModule
(
const
Compiler
::
Impl
&
compiler
,
const
Module
<
ParsedFile
::
Reader
>&
parserModule
);
const
Compiler
::
Impl
&
getCompiler
()
const
{
return
compiler
;
}
const
ErrorReporter
&
getErrorReporter
()
const
{
return
parserModule
;
}
const
ParsedFile
::
Reader
&
getParsedFile
()
const
{
return
content
;
}
const
Node
&
getRootNode
()
const
{
return
rootNode
;
}
kj
::
StringPtr
getSourceName
()
const
{
return
parserModule
.
getSourceName
();
}
kj
::
Maybe
<
const
CompiledModule
&>
importRelative
(
kj
::
StringPtr
importPath
)
const
;
private
:
const
Compiler
::
Impl
&
compiler
;
const
Module
<
ParsedFile
::
Reader
>&
parserModule
;
MallocMessageBuilder
contentArena
;
ParsedFile
::
Reader
content
;
Node
rootNode
;
};
class
Compiler
::
Impl
:
public
SchemaLoader
::
LazyLoadCallback
{
public
:
Impl
();
const
CompiledModule
&
add
(
const
Module
<
ParsedFile
::
Reader
>&
parsedModule
)
const
;
struct
Workspace
{
// Scratch space where stuff can be allocated while working. The Workspace is available
// whenever nodes are actively being compiled, then is destroyed once control exits the
// compiler. Note that since nodes are compiled lazily, a new Workspace may have to be
// constructed in order to compile more nodes later.
kj
::
Arena
arena
;
// Arena for allocating temporary native objects.
Orphanage
orphanage
;
// Orphanage for allocating temporary Cap'n Proto objects.
SchemaLoader
bootstrapLoader
;
// Loader used to load bootstrap schemas. The bootstrap schema nodes are similar to the final
// versions except that any value expressions which depend on knowledge of other types (e.g.
// default values for struct fields) are left unevaluated (the values in the schema are empty).
// These bootstrap schemas can then be plugged into the dynamic API and used to evaluate these
// remaining values.
};
const
kj
::
Arena
&
getNodeArena
()
const
{
return
nodeArena
;
}
// Arena where nodes and other permanent objects should be allocated.
const
SchemaLoader
&
getFinalLoader
()
const
{
return
finalLoader
;
}
// Schema loader containing final versions of schemas.
const
Workspace
&
getWorkspace
()
const
{
return
*
workspace
.
getWithoutLock
();
}
// Temporary workspace that can be used to construct bootstrap objects.
uint64_t
addNode
(
uint64_t
desiredId
,
Node
&
node
)
const
;
// Add the given node to the by-ID map under the given ID. If another node with the same ID
// already exists, choose a new one arbitrarily and use that instead. Return the ID that was
// finally used.
kj
::
Maybe
<
const
Node
&>
findNode
(
uint64_t
id
)
const
;
kj
::
Maybe
<
const
Node
&>
lookupBuiltin
(
kj
::
StringPtr
name
)
const
;
void
load
(
const
SchemaLoader
&
loader
,
uint64_t
id
)
const
override
;
private
:
kj
::
Arena
nodeArena
;
// Arena used to allocate nodes and other permanent objects.
SchemaLoader
finalLoader
;
// The loader where we put final output of the compiler.
kj
::
MutexGuarded
<
const
Workspace
*>
workspace
;
// The entry points to the Compiler allocate a Workspace on the stack, lock this mutex, and set
// the pointer to point at said stack workspace. The rest of the compiler can assume that this
// Workspace is active.
uint
workspaceRefcount
=
0
;
// Count of threads that have entered the compiler.
typedef
std
::
unordered_map
<
Module
<
ParsedFile
::
Reader
>*
,
kj
::
Own
<
CompiledModule
>>
ModuleMap
;
kj
::
MutexGuarded
<
ModuleMap
>
modules
;
// Map of parser modules to compiler modules.
typedef
std
::
unordered_map
<
uint64_t
,
const
Node
*>
NodeMap
;
kj
::
MutexGuarded
<
NodeMap
>
nodesById
;
// Map of nodes by ID.
std
::
map
<
kj
::
StringPtr
,
kj
::
Own
<
Node
>>
builtinDecls
;
// Map of built-in declarations, like "Int32" and "List", which make up the global scope.
mutable
uint64_t
nextBogusId
=
1000
;
};
// =======================================================================================
kj
::
Maybe
<
const
Compiler
::
Node
&>
Compiler
::
Alias
::
getTarget
()
const
{
return
target
.
get
([
this
](
kj
::
SpaceFor
<
kj
::
Maybe
<
const
Node
&>>&
space
)
{
return
space
.
construct
(
parent
.
lookup
(
targetName
));
});
}
// =======================================================================================
Compiler
::
Node
::
Node
(
CompiledModule
&
module
)
:
module
(
&
module
),
parent
(
nullptr
),
declaration
(
module
.
getParsedFile
().
getRoot
()),
id
(
generateId
(
0
,
declaration
.
getName
().
getValue
(),
declaration
.
getId
())),
displayName
(
module
.
getSourceName
()),
kind
(
declaration
.
getBody
().
which
()),
isBuiltin
(
false
)
{
auto
name
=
declaration
.
getName
();
if
(
name
.
getValue
().
size
()
>
0
)
{
startByte
=
name
.
getStartByte
();
endByte
=
name
.
getEndByte
();
}
else
{
startByte
=
declaration
.
getStartByte
();
endByte
=
declaration
.
getEndByte
();
}
id
=
module
.
getCompiler
().
addNode
(
id
,
*
this
);
}
Compiler
::
Node
::
Node
(
const
Node
&
parent
,
const
Declaration
::
Reader
&
declaration
)
:
module
(
parent
.
module
),
parent
(
parent
),
declaration
(
declaration
),
id
(
generateId
(
parent
.
id
,
declaration
.
getName
().
getValue
(),
declaration
.
getId
())),
displayName
(
joinDisplayName
(
parent
.
module
->
getCompiler
().
getNodeArena
(),
parent
,
declaration
.
getName
().
getValue
())),
kind
(
declaration
.
getBody
().
which
()),
isBuiltin
(
false
)
{
auto
name
=
declaration
.
getName
();
if
(
name
.
getValue
().
size
()
>
0
)
{
startByte
=
name
.
getStartByte
();
endByte
=
name
.
getEndByte
();
}
else
{
startByte
=
declaration
.
getStartByte
();
endByte
=
declaration
.
getEndByte
();
}
id
=
module
->
getCompiler
().
addNode
(
id
,
*
this
);
}
Compiler
::
Node
::
Node
(
kj
::
StringPtr
name
,
Declaration
::
Body
::
Which
kind
)
:
module
(
nullptr
),
parent
(
nullptr
),
id
(
0
),
displayName
(
name
),
kind
(
kind
),
isBuiltin
(
true
),
startByte
(
0
),
endByte
(
0
)
{}
uint64_t
Compiler
::
Node
::
generateId
(
uint64_t
parentId
,
kj
::
StringPtr
declName
,
Declaration
::
Id
::
Reader
declId
)
{
if
(
declId
.
which
()
==
Declaration
::
Id
::
UID
)
{
return
declId
.
getUid
().
getValue
();
}
// No explicit ID. Compute it by MD5 hashing the concatenation of the parent ID and the
// declaration name, and then taking the first 8 bytes.
kj
::
byte
parentIdBytes
[
sizeof
(
uint64_t
)];
for
(
uint
i
=
0
;
i
<
sizeof
(
uint64_t
);
i
++
)
{
parentIdBytes
[
i
]
=
(
parentId
>>
(
i
*
8
))
&
0xff
;
}
Md5
md5
;
md5
.
update
(
kj
::
arrayPtr
(
parentIdBytes
,
KJ_ARRAY_SIZE
(
parentIdBytes
)));
md5
.
update
(
declName
);
kj
::
ArrayPtr
<
const
kj
::
byte
>
resultBytes
=
md5
.
finish
();
uint64_t
result
=
0
;
for
(
uint
i
=
0
;
i
<
sizeof
(
uint64_t
);
i
++
)
{
result
=
(
result
<<
8
)
|
resultBytes
[
i
];
}
return
result
;
}
kj
::
StringPtr
Compiler
::
Node
::
joinDisplayName
(
const
kj
::
Arena
&
arena
,
const
Node
&
parent
,
kj
::
StringPtr
declName
)
{
kj
::
ArrayPtr
<
char
>
result
=
arena
.
allocateArray
<
char
>
(
parent
.
displayName
.
size
()
+
declName
.
size
()
+
2
);
size_t
separatorPos
=
parent
.
displayName
.
size
();
memcpy
(
result
.
begin
(),
parent
.
displayName
.
begin
(),
separatorPos
);
result
[
separatorPos
]
=
parent
.
parent
==
nullptr
?
':'
:
'.'
;
memcpy
(
result
.
begin
()
+
separatorPos
+
1
,
declName
.
begin
(),
declName
.
size
());
result
[
result
.
size
()
-
1
]
=
'\0'
;
return
kj
::
StringPtr
(
declName
.
begin
(),
declName
.
size
()
-
1
);
}
const
Compiler
::
Node
::
Content
&
Compiler
::
Node
::
getContent
(
Content
::
State
minimumState
)
const
{
KJ_REQUIRE
(
!
isBuiltin
,
"illegal method call for built-in declaration"
);
if
(
content
.
getWithoutLock
().
stateHasReached
(
minimumState
))
{
return
content
.
getWithoutLock
();
}
auto
locked
=
content
.
lockExclusive
();
switch
(
locked
->
state
)
{
case
Content
:
:
STUB
:
{
if
(
minimumState
<=
Content
::
STUB
)
break
;
// Expand the child nodes.
auto
&
arena
=
module
->
getCompiler
().
getNodeArena
();
for
(
auto
nestedDecl
:
declaration
.
getNestedDecls
())
{
switch
(
nestedDecl
.
getBody
().
which
())
{
case
Declaration
:
:
Body
::
FILE_DECL
:
case
Declaration
:
:
Body
::
CONST_DECL
:
case
Declaration
:
:
Body
::
ANNOTATION_DECL
:
case
Declaration
:
:
Body
::
ENUM_DECL
:
case
Declaration
:
:
Body
::
STRUCT_DECL
:
case
Declaration
:
:
Body
::
INTERFACE_DECL
:
{
kj
::
Own
<
Node
>
subNode
=
arena
.
allocateOwn
<
Node
>
(
*
this
,
nestedDecl
);
kj
::
StringPtr
name
=
nestedDecl
.
getName
().
getValue
();
locked
->
nestedNodes
.
insert
(
std
::
make_pair
(
name
,
kj
::
mv
(
subNode
)));
break
;
}
case
Declaration
:
:
Body
::
USING_DECL
:
{
kj
::
Own
<
Alias
>
alias
=
arena
.
allocateOwn
<
Alias
>
(
*
this
,
nestedDecl
.
getBody
().
getUsingDecl
().
getTarget
());
kj
::
StringPtr
name
=
nestedDecl
.
getName
().
getValue
();
locked
->
aliases
.
insert
(
std
::
make_pair
(
name
,
kj
::
mv
(
alias
)));
break
;
}
case
Declaration
:
:
Body
::
ENUMERANT_DECL
:
case
Declaration
:
:
Body
::
FIELD_DECL
:
case
Declaration
:
:
Body
::
UNION_DECL
:
case
Declaration
:
:
Body
::
GROUP_DECL
:
case
Declaration
:
:
Body
::
METHOD_DECL
:
case
Declaration
:
:
Body
::
NAKED_ID
:
case
Declaration
:
:
Body
::
NAKED_ANNOTATION
:
// Not a node. Skip.
break
;
default
:
KJ_FAIL_ASSERT
(
"unknown declaration type"
,
nestedDecl
);
break
;
}
}
locked
->
advanceState
(
Content
::
EXPANDED
);
// no break
}
case
Content
:
:
EXPANDED
:
{
if
(
minimumState
<=
Content
::
EXPANDED
)
break
;
// Construct the NodeTranslator.
auto
&
workspace
=
module
->
getCompiler
().
getWorkspace
();
auto
schemaNode
=
workspace
.
orphanage
.
newOrphan
<
schema
::
Node
>
();
auto
builder
=
schemaNode
.
get
();
builder
.
setId
(
id
);
builder
.
setDisplayName
(
displayName
);
KJ_IF_MAYBE
(
p
,
parent
)
{
builder
.
setScopeId
(
p
->
id
);
}
auto
nestedIter
=
builder
.
initNestedNodes
(
locked
->
nestedNodes
.
size
()).
begin
();
for
(
auto
&
entry
:
locked
->
nestedNodes
)
{
nestedIter
->
setName
(
entry
.
first
);
nestedIter
->
setId
(
entry
.
second
->
id
);
++
nestedIter
;
}
locked
->
translator
=
&
workspace
.
arena
.
allocate
<
NodeTranslator
>
(
*
this
,
module
->
getErrorReporter
(),
declaration
,
kj
::
mv
(
schemaNode
));
locked
->
bootstrapSchema
=
workspace
.
bootstrapLoader
.
loadOnce
(
locked
->
translator
->
getBootstrapNode
());
// If the Workspace is destroyed while this Node is still in the BOOTSTRAP state,
// revert it to the EXPANDED state, because the NodeTranslator is no longer valid in this
// case.
Content
*
contentPtr
=
locked
.
get
();
workspace
.
arena
.
copy
(
kj
::
defer
([
contentPtr
]()
{
if
(
contentPtr
->
state
==
Content
::
BOOTSTRAP
)
{
contentPtr
->
state
=
Content
::
EXPANDED
;
}
}));
locked
->
advanceState
(
Content
::
BOOTSTRAP
);
// no break
}
case
Content
:
:
BOOTSTRAP
:
{
if
(
minimumState
<=
Content
::
BOOTSTRAP
)
break
;
// Create the final schema.
locked
->
finalSchema
=
module
->
getCompiler
().
getFinalLoader
().
loadOnce
(
locked
->
translator
->
finish
());
locked
->
advanceState
(
Content
::
FINISHED
);
// no break
}
case
Content
:
:
FINISHED
:
break
;
}
return
*
locked
;
}
kj
::
Maybe
<
const
Compiler
::
Node
&>
Compiler
::
Node
::
lookupMember
(
kj
::
StringPtr
name
)
const
{
if
(
isBuiltin
)
return
nullptr
;
auto
&
content
=
getContent
(
Content
::
EXPANDED
);
{
auto
iter
=
content
.
nestedNodes
.
find
(
name
);
if
(
iter
!=
content
.
nestedNodes
.
end
())
{
return
*
iter
->
second
;
}
}
{
auto
iter
=
content
.
aliases
.
find
(
name
);
if
(
iter
!=
content
.
aliases
.
end
())
{
return
iter
->
second
->
getTarget
();
}
}
return
nullptr
;
}
kj
::
Maybe
<
const
Compiler
::
Node
&>
Compiler
::
Node
::
lookupLexical
(
kj
::
StringPtr
name
)
const
{
KJ_REQUIRE
(
!
isBuiltin
,
"illegal method call for built-in declaration"
);
auto
result
=
lookupMember
(
name
);
if
(
result
==
nullptr
)
{
KJ_IF_MAYBE
(
p
,
parent
)
{
result
=
p
->
lookupLexical
(
name
);
}
else
{
result
=
module
->
getCompiler
().
lookupBuiltin
(
name
);
}
}
return
result
;
}
kj
::
Maybe
<
const
Compiler
::
Node
&>
Compiler
::
Node
::
lookup
(
const
DeclName
::
Reader
&
name
)
const
{
KJ_REQUIRE
(
!
isBuiltin
,
"illegal method call for built-in declaration"
);
const
Node
*
node
=
nullptr
;
auto
base
=
name
.
getBase
();
switch
(
base
.
which
())
{
case
DeclName
:
:
Base
::
ABSOLUTE_NAME
:
{
auto
absoluteName
=
base
.
getAbsoluteName
();
KJ_IF_MAYBE
(
n
,
module
->
getRootNode
().
lookupMember
(
absoluteName
.
getValue
()))
{
node
=
&*
n
;
}
else
{
module
->
getErrorReporter
().
addErrorOn
(
absoluteName
,
kj
::
str
(
"not defined: "
,
absoluteName
.
getValue
()));
return
nullptr
;
}
break
;
}
case
DeclName
:
:
Base
::
RELATIVE_NAME
:
{
auto
relativeName
=
base
.
getRelativeName
();
KJ_IF_MAYBE
(
n
,
lookupLexical
(
relativeName
.
getValue
()))
{
node
=
&*
n
;
}
else
{
module
->
getErrorReporter
().
addErrorOn
(
relativeName
,
kj
::
str
(
"not defined: "
,
relativeName
.
getValue
()));
return
nullptr
;
}
break
;
}
case
DeclName
:
:
Base
::
IMPORT_NAME
:
{
auto
importName
=
base
.
getImportName
();
KJ_IF_MAYBE
(
m
,
module
->
importRelative
(
importName
.
getValue
()))
{
node
=
&
m
->
getRootNode
();
}
else
{
module
->
getErrorReporter
().
addErrorOn
(
importName
,
kj
::
str
(
"import failed: "
,
importName
.
getValue
()));
return
nullptr
;
}
break
;
}
}
KJ_ASSERT
(
node
!=
nullptr
);
for
(
auto
partName
:
name
.
getMemberPath
())
{
KJ_IF_MAYBE
(
member
,
node
->
lookupMember
(
partName
.
getValue
()))
{
node
=
&*
member
;
}
else
{
module
->
getErrorReporter
().
addErrorOn
(
partName
,
kj
::
str
(
"no such member: "
,
partName
.
getValue
()));
return
nullptr
;
}
}
return
*
node
;
}
Schema
Compiler
::
Node
::
getBootstrapOrFinalSchema
()
const
{
auto
&
content
=
getContent
(
Content
::
BOOTSTRAP
);
if
(
__atomic_load_n
(
&
content
.
state
,
__ATOMIC_ACQUIRE
)
==
Content
::
FINISHED
)
{
return
content
.
finalSchema
;
}
else
{
return
content
.
bootstrapSchema
;
}
}
Schema
Compiler
::
Node
::
getFinalSchema
()
const
{
return
getContent
(
Content
::
FINISHED
).
finalSchema
;
}
void
Compiler
::
Node
::
addError
(
kj
::
StringPtr
error
)
const
{
module
->
getErrorReporter
().
addError
(
startByte
,
endByte
,
error
);
}
kj
::
Maybe
<
NodeTranslator
::
Resolver
::
ResolvedName
>
Compiler
::
Node
::
resolve
(
const
DeclName
::
Reader
&
name
)
const
{
return
lookup
(
name
).
map
([](
const
Node
&
node
)
{
return
ResolvedName
{
node
.
id
,
node
.
kind
};
});
}
kj
::
Maybe
<
Schema
>
Compiler
::
Node
::
resolveMaybeBootstrapSchema
(
uint64_t
id
)
const
{
return
module
->
getCompiler
().
findNode
(
id
).
map
(
[](
const
Node
&
node
)
{
return
node
.
getBootstrapOrFinalSchema
();
});
}
kj
::
Maybe
<
Schema
>
Compiler
::
Node
::
resolveFinalSchema
(
uint64_t
id
)
const
{
return
module
->
getCompiler
().
findNode
(
id
).
map
(
[](
const
Node
&
node
)
{
return
node
.
getFinalSchema
();
});
}
// =======================================================================================
Compiler
::
CompiledModule
::
CompiledModule
(
const
Compiler
::
Impl
&
compiler
,
const
Module
<
ParsedFile
::
Reader
>&
parserModule
)
:
compiler
(
compiler
),
parserModule
(
parserModule
),
content
(
parserModule
.
loadContent
(
contentArena
.
getOrphanage
())),
rootNode
(
*
this
)
{}
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
&
{
return
compiler
.
add
(
module
);
});
}
// =======================================================================================
Compiler
::
Impl
::
Impl
()
:
finalLoader
(
*
this
),
workspace
(
nullptr
)
{
// Reflectively interpret the members of Declaration.body. Any member prefixed by "builtin"
// defines a builtin declaration visible in the global scope.
StructSchema
::
Union
declBodySchema
=
Schema
::
from
<
Declaration
>
().
getMemberByName
(
"body"
).
asUnion
();
for
(
auto
member
:
declBodySchema
.
getMembers
())
{
auto
name
=
member
.
getProto
().
getName
();
if
(
name
.
startsWith
(
"builtin"
))
{
kj
::
StringPtr
symbolName
;
if
(
name
.
endsWith
(
"Value"
))
{
// e.g. "builtinTrueValue" -- transform this to "true".
auto
substr
=
name
.
slice
(
strlen
(
"builtin"
),
name
.
size
()
-
strlen
(
"Value"
));
kj
::
ArrayPtr
<
char
>
array
=
nodeArena
.
allocateArray
<
char
>
(
substr
.
size
()
+
1
);
memcpy
(
array
.
begin
(),
substr
.
begin
(),
substr
.
size
());
array
[
substr
.
size
()]
=
'\0'
;
array
[
0
]
+=
'a'
-
'A'
;
symbolName
=
kj
::
StringPtr
(
array
.
begin
(),
array
.
size
()
-
1
);
}
else
{
// e.g. "builtinVoid" -- transform this to "Void".
symbolName
=
name
.
slice
(
strlen
(
"builtin"
));
}
builtinDecls
[
symbolName
]
=
nodeArena
.
allocateOwn
<
Node
>
(
symbolName
,
static_cast
<
Declaration
::
Body
::
Which
>
(
member
.
getIndex
()));
}
}
}
uint64_t
Compiler
::
Impl
::
addNode
(
uint64_t
desiredId
,
Node
&
node
)
const
{
auto
lock
=
nodesById
.
lockExclusive
();
for
(;;)
{
auto
insertResult
=
lock
->
insert
(
std
::
make_pair
(
desiredId
,
&
node
));
if
(
insertResult
.
second
)
{
return
desiredId
;
}
// Only report an error if this ID is not bogus. Actual IDs specified in the original source
// code are required to have the upper bit set. Anything else must have been manufactured
// at some point to cover up an error.
if
(
desiredId
&
(
1ull
<<
63
))
{
node
.
addError
(
kj
::
str
(
"Duplicate ID @0x"
,
kj
::
hex
(
desiredId
),
"."
));
insertResult
.
first
->
second
->
addError
(
kj
::
str
(
"ID @0x"
,
kj
::
hex
(
desiredId
),
" originally used here."
));
}
// Assign a new bogus ID.
desiredId
=
__atomic_fetch_add
(
&
nextBogusId
,
1
,
__ATOMIC_RELAXED
);
}
}
kj
::
Maybe
<
const
Compiler
::
Node
&>
Compiler
::
Impl
::
findNode
(
uint64_t
id
)
const
{
auto
lock
=
nodesById
.
lockShared
();
auto
iter
=
lock
->
find
(
id
);
if
(
iter
==
lock
->
end
())
{
return
nullptr
;
}
else
{
return
*
iter
->
second
;
}
}
kj
::
Maybe
<
const
Compiler
::
Node
&>
Compiler
::
Impl
::
lookupBuiltin
(
kj
::
StringPtr
name
)
const
{
auto
iter
=
builtinDecls
.
find
(
name
);
if
(
iter
==
builtinDecls
.
end
())
{
return
nullptr
;
}
else
{
return
*
iter
->
second
;
}
}
void
Compiler
::
Impl
::
load
(
const
SchemaLoader
&
loader
,
uint64_t
id
)
const
{
KJ_IF_MAYBE
(
node
,
findNode
(
id
))
{
if
(
&
loader
==
&
finalLoader
)
{
Workspace
workspace
;
auto
lock
=
this
->
workspace
.
lockExclusive
();
*
lock
=
&
workspace
;
KJ_DEFER
(
*
lock
=
nullptr
);
node
->
getFinalSchema
();
}
else
{
// Must be the bootstrap loader.
node
->
getBootstrapOrFinalSchema
();
}
}
}
}
// namespace compiler
}
// namespace capnp
c++/src/capnp/compiler/compiler.h
0 → 100644
View file @
0da57538
// 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_COMPILER_H_
#define CAPNP_COMPILER_COMPILER_H_
#include <capnp/compiler/grammar.capnp.h>
#include <capnp/schema.capnp.h>
#include <capnp/schema-loader.h>
#include "error-reporter.h"
namespace
capnp
{
namespace
compiler
{
template
<
typename
ContentType
>
class
Module
:
public
ErrorReporter
{
public
:
virtual
kj
::
StringPtr
getLocalName
()
const
=
0
;
// Typically, the absolute or cwd-relative path name of the module file, used in error messages.
// This is only for display purposes.
virtual
kj
::
StringPtr
getSourceName
()
const
=
0
;
// 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
;
// Loads the module content, using the given orphanage to allocate objects if necessary.
virtual
kj
::
Maybe
<
const
Module
&>
importRelative
(
kj
::
StringPtr
importPath
)
const
=
0
;
// Find another module, relative to this one. Importing the same logical module twice should
// produce the exact same object, comparable by identity. These objects are owned by some
// outside pool that outlives the Compiler instance.
};
class
Compiler
{
// Cross-links separate modules (schema files) and translates them into schema nodes.
public
:
explicit
Compiler
();
~
Compiler
();
KJ_DISALLOW_COPY
(
Compiler
);
enum
Mode
{
EAGER
,
// Completely traverse the module's parse tree and translate it into schema nodes before
// returning from add().
LAZY
// Only interpret the module's definitions when they are requested. The main advantage of this
// mode is that imports will only be loaded if they are actually needed.
//
// Since the parse tree is traversed lazily, any particular schema node only becomes findable
// by ID (using the SchemaLoader) once one of its neighbors in the graph has been examined.
// As long as you are only traversing the graph -- only looking up IDs that you obtained from
// other schema nodes from the same loader -- you shouldn't be able to tell the difference.
// But if you receive IDs from some external source and want to look those up, you'd better
// use EAGER mode.
};
Schema
add
(
Module
<
ParsedFile
::
Reader
>&
module
,
Mode
mode
)
const
;
// Add a module to the Compiler, returning its root Schema object.
const
SchemaLoader
&
getLoader
()
const
;
// Get a SchemaLoader backed by this compiler. Schema nodes will be lazily constructed as you
// traverse them using this loader.
private
:
class
Impl
;
kj
::
Own
<
Impl
>
impl
;
class
CompiledModule
;
class
Node
;
class
Alias
;
};
}
// namespace compiler
}
// namespace capnp
#endif // CAPNP_COMPILER_COMPILER_H_
c++/src/capnp/compiler/error-reporter.h
View file @
0da57538
...
...
@@ -34,10 +34,18 @@ class ErrorReporter {
public
:
virtual
~
ErrorReporter
()
noexcept
(
false
);
virtual
void
addError
(
uint32_t
startByte
,
uint32_t
endByte
,
kj
::
StringPtr
message
)
=
0
;
virtual
void
addError
(
uint32_t
startByte
,
uint32_t
endByte
,
kj
::
StringPtr
message
)
const
=
0
;
// Report an error at the given location in the input text. `startByte` and `endByte` indicate
// the span of text that is erroneous. They may be equal, in which case the parser was only
// able to identify where the error begins, not where it ends.
template
<
typename
T
>
inline
void
addErrorOn
(
T
&&
decl
,
kj
::
StringPtr
message
)
const
{
// Works for any `T` that defines `getStartByte()` and `getEndByte()` methods, which many
// of the Cap'n Proto types defined in `grammar.capnp` do.
addError
(
decl
.
getStartByte
(),
decl
.
getEndByte
(),
message
);
}
};
}
// namespace compiler
...
...
c++/src/capnp/compiler/grammar.capnp
View file @
0da57538
...
...
@@ -139,6 +139,7 @@ struct Declaration {
docComment @20 :Text;
body @6 union {
fileDecl @24 :File;
usingDecl @7 :Using;
constDecl @8 :Const;
enumDecl @9 :Enum;
...
...
@@ -154,10 +155,38 @@ struct Declaration {
nakedId @21 :LocatedInteger;
nakedAnnotation @22 :AnnotationApplication;
# A floating UID or annotation (allowed at the file top level).
# The following declaration types are not produced by the parser, but are declared here
# so that the compiler can handle symbol name lookups more uniformly.
#
# New union members added here will magically become visible in the global scope.
# "builtinFoo" becomes visible as "Foo", while "builtinFooValue" becomes visible as "foo".
builtinVoid @25 :Void;
builtinBool @26 :Void;
builtinInt8 @27 :Void;
builtinInt16 @28 :Void;
builtinInt32 @29 :Void;
builtinInt64 @30 :Void;
builtinUInt8 @31 :Void;
builtinUInt16 @32 :Void;
builtinUInt32 @33 :Void;
builtinUInt64 @34 :Void;
builtinFloat32 @35 :Void;
builtinFloat64 @36 :Void;
builtinText @37 :Void;
builtinData @38 :Void;
builtinList @39 :Void;
builtinObject @40 :Void;
builtinTrueValue @41 :Void;
builtinFalseValue @42 :Void;
builtinVoidValue @43 :Void;
}
struct File {}
struct Using {
target @0 :
TypeExpression
;
target @0 :
DeclName
;
}
struct Const {
...
...
c++/src/capnp/compiler/lexer-test.c++
View file @
0da57538
...
...
@@ -31,7 +31,7 @@ namespace {
class
TestFailingErrorReporter
:
public
ErrorReporter
{
public
:
void
addError
(
uint32_t
startByte
,
uint32_t
endByte
,
kj
::
StringPtr
message
)
override
{
void
addError
(
uint32_t
startByte
,
uint32_t
endByte
,
kj
::
StringPtr
message
)
const
override
{
ADD_FAILURE
()
<<
"Parse failed: ("
<<
startByte
<<
"-"
<<
endByte
<<
") "
<<
message
.
cStr
();
}
};
...
...
c++/src/capnp/compiler/node-translator.c++
0 → 100644
View file @
0da57538
// 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 "node-translator.h"
#include <kj/debug.h>
#include <kj/arena.h>
#include <set>
#include <map>
#include <limits.h>
namespace
capnp
{
namespace
compiler
{
class
NodeTranslator
::
StructLayout
{
// Massive, disgusting class which implements the layout algorithm, which decides the offset
// for each field.
public
:
template
<
typename
UIntType
>
struct
HoleSet
{
inline
HoleSet
()
{}
// Represents a set of "holes" within a segment of allocated space, up to one hole of each
// power-of-two size between 1 bit and 32 bits.
//
// The amount of "used" space in a struct's data segment can always be represented as a
// combination of a word count and a HoleSet. The HoleSet represents the space lost to
// "padding".
//
// There can never be more than one hole of any particular size. Why is this? Well, consider
// that every data field has a power-of-two size, every field must be aligned to a multiple of
// its size, and the maximum size of a single field is 64 bits. If we need to add a new field
// of N bits, there are two possibilities:
// 1. A hole of size N or larger exists. In this case, we find the smallest hole that is at
// least N bits. Let's say that that hole has size M. We allocate the first N bits of the
// hole to the new field. The remaining M - N bits become a series of holes of sizes N*2,
// N*4, ..., M / 2. We know no holes of these sizes existed before because we chose M to be
// the smallest available hole larger than N. So, there is still no more than one hole of
// each size, and no hole larger than any hole that existed previously.
// 2. No hole equal or larger N exists. In that case we extend the data section's size by one
// word, creating a new 64-bit hole at the end. We then allocate N bits from it, creating
// a series of holes between N and 64 bits, as described in point (1). Thus, again, there
// is still at most one hole of each size, and the largest hole is 32 bits.
UIntType
holes
[
6
]
=
{
0
,
0
,
0
,
0
,
0
,
0
};
// The offset of each hole as a multiple of its size. A value of zero indicates that no hole
// exists. Notice that it is impossible for any actual hole to have an offset of zero, because
// the first field allocated is always placed at the very beginning of the section. So either
// the section has a size of zero (in which case there are no holes), or offset zero is
// already allocated and therefore cannot be a hole.
kj
::
Maybe
<
UIntType
>
tryAllocate
(
UIntType
lgSize
)
{
// Try to find space for a field of size lgSize^2 within the set of holes. If found,
// remove it from the holes, and return its offset (as a multiple of its size). If there
// is no such space, returns zero (no hole can be at offset zero, as explained above).
if
(
holes
[
lgSize
]
!=
0
)
{
UIntType
result
=
holes
[
lgSize
];
holes
[
lgSize
]
=
0
;
return
result
;
}
else
{
KJ_IF_MAYBE
(
next
,
tryAllocate
(
lgSize
+
1
))
{
UIntType
result
=
*
next
*
2
;
holes
[
lgSize
]
=
result
+
1
;
return
result
;
}
else
{
return
nullptr
;
}
}
}
uint
assertHoleAndAllocate
(
UIntType
lgSize
)
{
KJ_ASSERT
(
holes
[
lgSize
]
!=
0
);
uint
result
=
holes
[
lgSize
];
holes
[
lgSize
]
=
0
;
return
result
;
}
void
addHolesAtEnd
(
UIntType
lgSize
,
UIntType
offset
,
UIntType
limitLgSize
=
KJ_ARRAY_SIZE
(
holes
))
{
// Add new holes of progressively larger sizes in the range [lgSize, limitLgSize) starting
// from the given offset. The idea is that you just allocated an lgSize-sized field from
// an limitLgSize-sized space, such as a newly-added word on the end of the data segment.
KJ_DREQUIRE
(
limitLgSize
<=
KJ_ARRAY_SIZE
(
holes
));
while
(
lgSize
<
limitLgSize
)
{
KJ_DREQUIRE
(
holes
[
lgSize
]
==
0
);
KJ_DREQUIRE
(
offset
%
2
==
1
);
holes
[
lgSize
]
=
offset
;
++
lgSize
;
offset
=
(
offset
+
1
)
/
2
;
}
}
bool
tryExpand
(
UIntType
oldLgSize
,
uint
oldOffset
,
uint
expansionFactor
)
{
// Try to expand the value at the given location by combining it with subsequent holes, so
// as to expand the location to be 2^expansionFactor times the size that it started as.
// (In other words, the new lgSize is oldLgSize + expansionFactor.)
if
(
expansionFactor
==
0
)
{
// No expansion requested.
return
true
;
}
if
(
holes
[
oldLgSize
]
!=
oldOffset
+
1
)
{
// The space immediately after the location is not a hole.
return
false
;
}
// We can expand the location by one factor by combining it with a hole. Try to further
// expand from there to the number of factors requested.
if
(
tryExpand
(
oldLgSize
+
1
,
oldOffset
>>
1
,
expansionFactor
-
1
))
{
// Success. Consume the hole.
holes
[
oldLgSize
]
=
0
;
return
true
;
}
else
{
return
false
;
}
}
kj
::
Maybe
<
int
>
smallestAtLeast
(
uint
size
)
{
// Return the size of the smallest hole that is equal to or larger than the given size.
for
(
uint
i
=
size
;
i
<
KJ_ARRAY_SIZE
(
holes
);
i
++
)
{
if
(
holes
[
i
]
!=
0
)
{
return
i
;
}
}
return
nullptr
;
}
};
struct
StructOrGroup
{
// Abstract interface for scopes in which fields can be added.
virtual
uint
addData
(
uint
lgSize
)
=
0
;
virtual
uint
addPointer
()
=
0
;
virtual
bool
tryExpandData
(
uint
oldLgSize
,
uint
oldOffset
,
uint
expansionFactor
)
=
0
;
// Try to expand the given previously-allocated space by 2^expansionFactor. Succeeds --
// returning true -- if the following space happens to be empty, making this expansion possible.
// Otherwise, returns false.
};
struct
Top
:
public
StructOrGroup
{
uint
dataWordCount
=
0
;
uint
pointerCount
=
0
;
// Size of the struct so far.
HoleSet
<
uint
>
holes
;
uint
addData
(
uint
lgSize
)
override
{
KJ_IF_MAYBE
(
hole
,
holes
.
tryAllocate
(
lgSize
))
{
return
*
hole
;
}
else
{
uint
offset
=
dataWordCount
++
<<
(
6
-
lgSize
);
holes
.
addHolesAtEnd
(
lgSize
,
offset
+
1
);
return
offset
;
}
}
uint
addPointer
()
override
{
return
pointerCount
++
;
}
bool
tryExpandData
(
uint
oldLgSize
,
uint
oldOffset
,
uint
expansionFactor
)
override
{
return
holes
.
tryExpand
(
oldLgSize
,
oldOffset
,
expansionFactor
);
}
Top
()
=
default
;
KJ_DISALLOW_COPY
(
Top
);
};
struct
Union
{
struct
DataLocation
{
uint
lgSize
;
uint
offset
;
bool
tryExpandTo
(
Union
&
u
,
uint
newLgSize
)
{
if
(
newLgSize
<=
lgSize
)
{
return
true
;
}
else
if
(
u
.
parent
.
tryExpandData
(
lgSize
,
offset
,
newLgSize
-
lgSize
))
{
offset
>>=
(
newLgSize
-
lgSize
);
lgSize
=
newLgSize
;
return
true
;
}
else
{
return
false
;
}
}
};
StructOrGroup
&
parent
;
uint
groupCount
=
0
;
kj
::
Maybe
<
int
>
discriminantOffset
;
kj
::
Vector
<
DataLocation
>
dataLocations
;
kj
::
Vector
<
uint
>
pointerLocations
;
inline
Union
(
StructOrGroup
&
parent
)
:
parent
(
parent
)
{}
KJ_DISALLOW_COPY
(
Union
);
uint
addNewDataLocation
(
uint
lgSize
)
{
// Add a whole new data location to the union with the given size.
uint
offset
=
parent
.
addData
(
lgSize
);
dataLocations
.
add
(
DataLocation
{
lgSize
,
offset
});
return
offset
;
}
uint
addNewPointerLocation
()
{
// Add a whole new pointer location to the union with the given size.
return
pointerLocations
.
add
(
parent
.
addPointer
());
}
void
newGroup
()
{
if
(
++
groupCount
==
2
)
{
addDiscriminant
();
}
}
bool
addDiscriminant
()
{
if
(
discriminantOffset
==
nullptr
)
{
discriminantOffset
=
parent
.
addData
(
4
);
// 2^4 = 16 bits
return
true
;
}
else
{
return
false
;
}
}
};
struct
Group
:
public
StructOrGroup
{
public
:
class
DataLocationUsage
{
public
:
DataLocationUsage
()
:
isUsed
(
false
)
{}
explicit
DataLocationUsage
(
uint
lgSize
)
:
isUsed
(
true
),
lgSizeUsed
(
lgSize
)
{}
kj
::
Maybe
<
uint
>
smallestHoleAtLeast
(
Union
::
DataLocation
&
location
,
uint
lgSize
)
{
// Find the smallest single hole that is at least the given size. This is used to find the
// optimal place to allocate each field -- it is placed in the smallest slot where it fits,
// to reduce fragmentation.
if
(
!
isUsed
)
{
// The location is effectively one big hole.
return
location
.
lgSize
;
}
else
if
(
lgSize
>=
lgSizeUsed
)
{
// Requested size is at least our current usage, so clearly won't fit in any current
// holes, but if the location's size is larger than what we're using, we'd be able to
// expand.
if
(
lgSize
<
location
.
lgSize
)
{
return
lgSize
;
}
else
{
return
nullptr
;
}
}
else
KJ_IF_MAYBE
(
result
,
holes
.
smallestAtLeast
(
lgSize
))
{
// There's a hole.
return
*
result
;
}
else
{
// The requested size is smaller than what we're already using, but there are no holes
// available. If we could double our size, then we could allocate in the new space.
if
(
lgSizeUsed
<
location
.
lgSize
)
{
// We effectively create a new hole the same size as the current usage.
return
lgSizeUsed
;
}
else
{
return
nullptr
;
}
}
}
uint
allocateFromHole
(
Group
&
group
,
Union
::
DataLocation
&
location
,
uint
lgSize
)
{
// Allocate the given space from an existing hole, given smallestHoleAtLeast() already
// returned non-null indicating such a hole exists.
uint
result
;
if
(
!
isUsed
)
{
// The location is totally unused, so just allocate from the beginning.
KJ_DASSERT
(
lgSize
<=
location
.
lgSize
,
"Did smallestHoleAtLeast() really find a hole?"
);
result
=
0
;
isUsed
=
true
;
lgSizeUsed
=
lgSize
;
}
else
if
(
lgSize
>=
lgSizeUsed
)
{
// Requested size is at least our current usage, so clearly won't fit in any holes.
// We must expand to double the requested size, and return the second half.
KJ_DASSERT
(
lgSize
<
location
.
lgSize
,
"Did smallestHoleAtLeast() really find a hole?"
);
holes
.
addHolesAtEnd
(
lgSizeUsed
,
1
,
lgSize
);
lgSizeUsed
=
lgSize
+
1
;
result
=
1
;
}
else
KJ_IF_MAYBE
(
hole
,
holes
.
tryAllocate
(
lgSize
))
{
// Found a hole.
result
=
*
hole
;
}
else
{
// The requested size is smaller than what we're using so far, but didn't fit in a
// hole. We should double our "used" size, then allocate from the new space.
KJ_DASSERT
(
lgSizeUsed
<
location
.
lgSize
,
"Did smallestHoleAtLeast() really find a hole?"
);
result
=
1
<<
(
lgSizeUsed
-
lgSize
);
holes
.
addHolesAtEnd
(
lgSize
,
result
+
1
,
lgSizeUsed
);
lgSizeUsed
+=
1
;
}
// Adjust the offset according to the location's offset before returning.
uint
locationOffset
=
location
.
offset
<<
(
location
.
lgSize
-
lgSize
);
return
locationOffset
+
result
;
}
kj
::
Maybe
<
uint
>
tryAllocateByExpanding
(
Group
&
group
,
Union
::
DataLocation
&
location
,
uint
lgSize
)
{
// Attempt to allocate the given size by requesting that the parent union expand this
// location to fit. This is used if smallestHoleAtLeast() already determined that there
// are no holes that would fit, so we don't bother checking that.
if
(
!
isUsed
)
{
if
(
location
.
tryExpandTo
(
group
.
parent
,
lgSize
))
{
isUsed
=
true
;
lgSizeUsed
=
lgSize
;
return
kj
::
implicitCast
<
uint
>
(
0
);
}
else
{
return
nullptr
;
}
}
else
{
uint
newSize
=
kj
::
max
(
lgSizeUsed
,
lgSize
)
+
1
;
if
(
tryExpandUsage
(
group
,
location
,
newSize
))
{
return
holes
.
assertHoleAndAllocate
(
lgSize
);
}
else
{
return
nullptr
;
}
}
}
kj
::
Maybe
<
uint
>
tryAllocate
(
Group
&
group
,
Union
::
DataLocation
&
location
,
uint
lgSize
)
{
if
(
isUsed
)
{
// We've already used some space in this location. Try to allocate from a hole.
uint
result
;
KJ_IF_MAYBE
(
hole
,
holes
.
tryAllocate
(
lgSize
))
{
result
=
*
hole
;
}
else
{
// Failure. But perhaps we could expand the location to include a new hole which would
// be big enough for the value.
uint
neededSizeUsed
;
if
(
lgSize
<=
lgSizeUsed
)
{
// We are already at least as big as the desired size, so doubling should be good
// enough. The new value will be located just past the end of our current used
// space.
neededSizeUsed
=
lgSizeUsed
+
1
;
result
=
1
<<
(
lgSizeUsed
-
lgSize
);
}
else
{
// We are smaller than the desired size, so we'll have to grow to 2x the desired size.
// The new value will be at an offset of 1x its own size.
neededSizeUsed
=
lgSize
+
1
;
result
=
1
;
}
if
(
!
tryExpandUsage
(
group
,
location
,
neededSizeUsed
))
{
return
nullptr
;
}
holes
.
addHolesAtEnd
(
lgSize
,
result
+
1
,
neededSizeUsed
-
1
);
}
// OK, we found space. Adjust the offset according to the location's offset before
// returning.
uint
locationOffset
=
location
.
offset
<<
(
location
.
lgSize
-
lgSize
);
return
locationOffset
+
result
;
}
else
{
// We haven't used this location at all yet.
if
(
location
.
lgSize
<
lgSize
)
{
// Not enough space. Try to expand the location.
if
(
!
location
.
tryExpandTo
(
group
.
parent
,
lgSize
))
{
// Couldn't expand. This location is not viable.
return
nullptr
;
}
}
// Either the location was already big enough, or we expanded it.
KJ_DASSERT
(
location
.
lgSize
>=
lgSize
);
// Just mark the first part used for now.
lgSizeUsed
=
lgSize
;
// Return the offset, adjusted to be appropriate for the size.
return
location
.
offset
<<
(
location
.
lgSize
-
lgSize
);
}
}
bool
tryExpand
(
Group
&
group
,
Union
::
DataLocation
&
location
,
uint
oldLgSize
,
uint
oldOffset
,
uint
expansionFactor
)
{
if
(
oldOffset
==
0
&&
lgSizeUsed
==
oldLgSize
)
{
// This location contains exactly the requested data, so just expand the whole thing.
return
tryExpandUsage
(
group
,
location
,
oldLgSize
+
expansionFactor
);
}
else
{
// This location contains the requested data plus other stuff. Therefore the data cannot
// possibly expand past the end of the space we've already marked used without either
// overlapping with something else or breaking alignment rules. We only have to combine
// it with holes.
return
holes
.
tryExpand
(
oldLgSize
,
oldOffset
,
expansionFactor
);
}
}
private
:
bool
isUsed
;
// Whether or not this location has been used at all by the group.
uint8_t
lgSizeUsed
;
// Amount of space from the location which is "used". This is the minimum size needed to
// cover all allocated space. Only meaningful if `isUsed` is true.
HoleSet
<
uint8_t
>
holes
;
// Indicates holes present in the space designated by `lgSizeUsed`. The offsets in this
// HoleSet are relative to the beginning of this particular data location, not the beginning
// of the struct.
bool
tryExpandUsage
(
Group
&
group
,
Union
::
DataLocation
&
location
,
uint
desiredUsage
)
{
if
(
desiredUsage
>
location
.
lgSize
)
{
// Need to expand the underlying slot.
if
(
!
location
.
tryExpandTo
(
group
.
parent
,
desiredUsage
))
{
return
false
;
}
}
// Underlying slot is big enough, so expand our size and update holes.
holes
.
addHolesAtEnd
(
lgSizeUsed
,
1
,
desiredUsage
);
lgSizeUsed
=
desiredUsage
;
return
true
;
}
};
Union
&
parent
;
kj
::
Vector
<
DataLocationUsage
>
parentDataLocationUsage
;
// Vector corresponding to the parent union's `dataLocations`, indicating how much of each
// location has already been allocated.
uint
parentPointerLocationUsage
=
0
;
// Number of parent's pointer locations that have been used by this group.
inline
Group
(
Union
&
parent
)
:
parent
(
parent
)
{
parent
.
newGroup
();
}
KJ_DISALLOW_COPY
(
Group
);
uint
addData
(
uint
lgSize
)
override
{
uint
bestSize
=
UINT_MAX
;
kj
::
Maybe
<
uint
>
bestLocation
=
nullptr
;
for
(
uint
i
=
0
;
i
<
parent
.
dataLocations
.
size
();
i
++
)
{
// If we haven't seen this DataLocation yet, add a corresponding DataLocationUsage.
if
(
parentDataLocationUsage
.
size
()
==
i
)
{
parentDataLocationUsage
.
add
();
}
auto
&
usage
=
parentDataLocationUsage
[
i
];
KJ_IF_MAYBE
(
hole
,
usage
.
smallestHoleAtLeast
(
parent
.
dataLocations
[
i
],
lgSize
))
{
if
(
*
hole
<
bestSize
)
{
bestSize
=
*
hole
;
bestLocation
=
i
;
}
}
}
KJ_IF_MAYBE
(
best
,
bestLocation
)
{
return
parentDataLocationUsage
[
*
best
].
allocateFromHole
(
*
this
,
parent
.
dataLocations
[
*
best
],
lgSize
);
}
// There are no holes at all in the union big enough to fit this field. Go back through all
// of the locations and attempt to expand them to fit.
for
(
uint
i
=
0
;
i
<
parent
.
dataLocations
.
size
();
i
++
)
{
KJ_IF_MAYBE
(
result
,
parentDataLocationUsage
[
i
].
tryAllocateByExpanding
(
*
this
,
parent
.
dataLocations
[
i
],
lgSize
))
{
return
*
result
;
}
}
// Couldn't find any space in the existing locations, so add a new one.
uint
result
=
parent
.
addNewDataLocation
(
lgSize
);
parentDataLocationUsage
.
add
(
lgSize
);
return
result
;
}
uint
addPointer
()
override
{
if
(
parentPointerLocationUsage
<
parent
.
pointerLocations
.
size
())
{
return
parent
.
pointerLocations
[
parentPointerLocationUsage
++
];
}
else
{
parentPointerLocationUsage
++
;
return
parent
.
addNewPointerLocation
();
}
}
bool
tryExpandData
(
uint
oldLgSize
,
uint
oldOffset
,
uint
expansionFactor
)
override
{
if
(
oldLgSize
+
expansionFactor
>
6
||
(
oldOffset
&
((
1
<<
expansionFactor
)
-
1
))
!=
0
)
{
// Expansion is not possible because the new size is too large or the offset is not
// properly-aligned.
}
for
(
uint
i
=
0
;
i
<
parentDataLocationUsage
.
size
();
i
++
)
{
auto
&
location
=
parent
.
dataLocations
[
i
];
if
(
location
.
lgSize
>=
oldLgSize
&&
oldOffset
>>
(
location
.
lgSize
-
oldLgSize
)
==
location
.
offset
)
{
// The location we're trying to expand is a subset of this data location.
auto
&
usage
=
parentDataLocationUsage
[
i
];
// Adjust the offset to be only within this location.
uint
localOldOffset
=
oldOffset
-
(
location
.
offset
<<
(
location
.
lgSize
-
oldLgSize
));
// Try to expand.
return
usage
.
tryExpand
(
*
this
,
location
,
oldLgSize
,
localOldOffset
,
expansionFactor
);
}
}
KJ_FAIL_ASSERT
(
"Tried to expand field that was never allocated."
);
return
false
;
}
};
Top
&
getTop
();
private
:
Top
top
;
};
// =======================================================================================
NodeTranslator
::
NodeTranslator
(
const
Resolver
&
resolver
,
const
ErrorReporter
&
errorReporter
,
const
Declaration
::
Reader
&
decl
,
Orphan
<
schema
::
Node
>
wipNodeParam
)
:
resolver
(
resolver
),
errorReporter
(
errorReporter
),
wipNode
(
kj
::
mv
(
wipNodeParam
))
{
compileNode
(
decl
,
wipNode
.
get
());
}
schema
::
Node
::
Reader
NodeTranslator
::
finish
()
{
// Careful about iteration here: compileFinalValue() may actually add more elements to
// `unfinishedValues`, invalidating iterators in the process.
for
(
size_t
i
=
0
;
i
<
unfinishedValues
.
size
();
i
++
)
{
auto
&
value
=
unfinishedValues
[
i
];
compileFinalValue
(
value
.
source
,
value
.
type
,
value
.
target
);
}
return
wipNode
.
getReader
();
}
void
NodeTranslator
::
compileNode
(
Declaration
::
Reader
decl
,
schema
::
Node
::
Builder
builder
)
{
checkMembers
(
decl
.
getNestedDecls
(),
decl
.
getBody
().
which
());
switch
(
decl
.
getBody
().
which
())
{
case
Declaration
:
:
Body
::
FILE_DECL
:
compileFile
(
decl
,
builder
.
getBody
().
initFileNode
());
break
;
case
Declaration
:
:
Body
::
CONST_DECL
:
compileConst
(
decl
.
getBody
().
getConstDecl
(),
builder
.
getBody
().
initConstNode
());
break
;
case
Declaration
:
:
Body
::
ANNOTATION_DECL
:
compileAnnotation
(
decl
.
getBody
().
getAnnotationDecl
(),
builder
.
getBody
().
initAnnotationNode
());
break
;
case
Declaration
:
:
Body
::
ENUM_DECL
:
compileEnum
(
decl
.
getBody
().
getEnumDecl
(),
decl
.
getNestedDecls
(),
builder
.
getBody
().
initEnumNode
());
break
;
case
Declaration
:
:
Body
::
STRUCT_DECL
:
compileStruct
(
decl
.
getBody
().
getStructDecl
(),
decl
.
getNestedDecls
(),
builder
.
getBody
().
initStructNode
());
break
;
case
Declaration
:
:
Body
::
INTERFACE_DECL
:
compileInterface
(
decl
.
getBody
().
getInterfaceDecl
(),
decl
.
getNestedDecls
(),
builder
.
getBody
().
initInterfaceNode
());
break
;
default
:
KJ_FAIL_REQUIRE
(
"This Declaration is not a node."
);
break
;
}
// TODO(now): annotations
}
void
NodeTranslator
::
checkMembers
(
List
<
Declaration
>::
Reader
nestedDecls
,
Declaration
::
Body
::
Which
parentKind
)
{
std
::
map
<
uint
,
Declaration
::
Reader
>
ordinals
;
std
::
map
<
kj
::
StringPtr
,
LocatedText
::
Reader
>
names
;
for
(
auto
decl
:
nestedDecls
)
{
{
auto
name
=
decl
.
getName
();
auto
nameText
=
name
.
getValue
();
auto
insertResult
=
names
.
insert
(
std
::
make_pair
(
nameText
,
name
));
if
(
!
insertResult
.
second
)
{
errorReporter
.
addErrorOn
(
name
,
kj
::
str
(
"'"
,
nameText
,
"' is already defined in this scope."
));
errorReporter
.
addErrorOn
(
insertResult
.
first
->
second
,
kj
::
str
(
"'"
,
nameText
,
"' previously defined here."
));
}
}
switch
(
decl
.
getBody
().
which
())
{
case
Declaration
:
:
Body
::
USING_DECL
:
case
Declaration
:
:
Body
::
CONST_DECL
:
case
Declaration
:
:
Body
::
ENUM_DECL
:
case
Declaration
:
:
Body
::
STRUCT_DECL
:
case
Declaration
:
:
Body
::
INTERFACE_DECL
:
case
Declaration
:
:
Body
::
ANNOTATION_DECL
:
switch
(
parentKind
)
{
case
Declaration
:
:
Body
::
FILE_DECL
:
case
Declaration
:
:
Body
::
STRUCT_DECL
:
case
Declaration
:
:
Body
::
INTERFACE_DECL
:
// OK.
break
;
default
:
errorReporter
.
addErrorOn
(
decl
,
"This kind of declaration doesn't belong here."
);
break
;
}
break
;
case
Declaration
:
:
Body
::
ENUMERANT_DECL
:
if
(
parentKind
!=
Declaration
::
Body
::
ENUM_DECL
)
{
errorReporter
.
addErrorOn
(
decl
,
"Enumerants can only appear in enums."
);
}
break
;
case
Declaration
:
:
Body
::
METHOD_DECL
:
if
(
parentKind
!=
Declaration
::
Body
::
INTERFACE_DECL
)
{
errorReporter
.
addErrorOn
(
decl
,
"Methods can only appear in interfaces."
);
}
break
;
case
Declaration
:
:
Body
::
FIELD_DECL
:
case
Declaration
:
:
Body
::
UNION_DECL
:
case
Declaration
:
:
Body
::
GROUP_DECL
:
switch
(
parentKind
)
{
case
Declaration
:
:
Body
::
STRUCT_DECL
:
case
Declaration
:
:
Body
::
UNION_DECL
:
case
Declaration
:
:
Body
::
GROUP_DECL
:
// OK.
break
;
default
:
errorReporter
.
addErrorOn
(
decl
,
"This declaration can only appear in structs."
);
break
;
}
break
;
default
:
errorReporter
.
addErrorOn
(
decl
,
"This kind of declaration doesn't belong here."
);
break
;
}
}
}
void
NodeTranslator
::
disallowNested
(
List
<
Declaration
>::
Reader
nestedDecls
)
{
for
(
auto
decl
:
nestedDecls
)
{
errorReporter
.
addErrorOn
(
decl
,
"Nested declaration not allowed here."
);
}
}
static
void
findImports
(
DynamicValue
::
Reader
value
,
std
::
set
<
kj
::
StringPtr
>&
output
)
{
switch
(
value
.
getType
())
{
case
DynamicValue
:
:
STRUCT
:
{
auto
structValue
=
value
.
as
<
DynamicStruct
>
();
StructSchema
schema
=
structValue
.
getSchema
();
if
(
schema
==
Schema
::
from
<
DeclName
>
())
{
auto
declName
=
structValue
.
as
<
DeclName
>
();
if
(
declName
.
getBase
().
which
()
==
DeclName
::
Base
::
IMPORT_NAME
)
{
output
.
insert
(
declName
.
getBase
().
getImportName
().
getValue
());
}
}
else
{
for
(
auto
member
:
schema
.
getMembers
())
{
if
(
structValue
.
has
(
member
))
{
findImports
(
structValue
.
get
(
member
),
output
);
}
}
}
break
;
}
case
DynamicValue
:
:
LIST
:
for
(
auto
element
:
value
.
as
<
DynamicList
>
())
{
findImports
(
element
,
output
);
}
break
;
default
:
break
;
}
}
void
NodeTranslator
::
compileFile
(
Declaration
::
Reader
decl
,
schema
::
FileNode
::
Builder
builder
)
{
std
::
set
<
kj
::
StringPtr
>
imports
;
findImports
(
decl
,
imports
);
auto
list
=
builder
.
initImports
(
imports
.
size
());
auto
iter
=
imports
.
begin
();
for
(
auto
element
:
list
)
{
element
.
setName
(
*
iter
++
);
}
KJ_ASSERT
(
iter
==
imports
.
end
());
}
void
NodeTranslator
::
compileConst
(
Declaration
::
Const
::
Reader
decl
,
schema
::
ConstNode
::
Builder
builder
)
{
auto
typeBuilder
=
builder
.
initType
();
if
(
compileType
(
decl
.
getType
(),
typeBuilder
))
{
compileBootstrapValue
(
decl
.
getValue
(),
typeBuilder
.
asReader
(),
builder
.
initValue
());
}
}
void
NodeTranslator
::
compileAnnotation
(
Declaration
::
Annotation
::
Reader
decl
,
schema
::
AnnotationNode
::
Builder
builder
)
{
compileType
(
decl
.
getType
(),
builder
.
initType
());
// Dynamically copy over the values of all of the "targets" members.
DynamicStruct
::
Reader
src
=
decl
;
DynamicStruct
::
Builder
dst
=
builder
;
for
(
auto
srcMember
:
src
.
getSchema
().
getMembers
())
{
kj
::
StringPtr
memberName
=
srcMember
.
getProto
().
getName
();
if
(
memberName
.
startsWith
(
"targets"
))
{
auto
dstMember
=
dst
.
getSchema
().
getMemberByName
(
memberName
);
dst
.
set
(
dstMember
,
src
.
get
(
srcMember
));
}
}
}
class
NodeTranslator
::
DuplicateOrdinalDetector
{
public
:
DuplicateOrdinalDetector
(
const
ErrorReporter
&
errorReporter
)
:
errorReporter
(
errorReporter
)
{}
void
check
(
LocatedInteger
::
Reader
ordinal
)
{
if
(
ordinal
.
getValue
()
<
expectedOrdinal
)
{
errorReporter
.
addErrorOn
(
ordinal
,
"Duplicate ordinal number."
);
KJ_IF_MAYBE
(
last
,
lastOrdinalLocation
)
{
errorReporter
.
addErrorOn
(
*
last
,
kj
::
str
(
"Ordinal @"
,
last
->
getValue
(),
" originally used here."
));
// Don't report original again.
lastOrdinalLocation
=
nullptr
;
}
}
else
if
(
ordinal
.
getValue
()
>
expectedOrdinal
)
{
errorReporter
.
addErrorOn
(
ordinal
,
kj
::
str
(
"Skipped ordinal @"
,
expectedOrdinal
,
". Ordinals must be sequential with no "
"holes."
));
}
else
{
++
expectedOrdinal
;
lastOrdinalLocation
=
ordinal
;
}
}
private
:
const
ErrorReporter
&
errorReporter
;
uint
expectedOrdinal
=
0
;
kj
::
Maybe
<
LocatedInteger
::
Reader
>
lastOrdinalLocation
;
};
void
NodeTranslator
::
compileEnum
(
Declaration
::
Enum
::
Reader
decl
,
List
<
Declaration
>::
Reader
members
,
schema
::
EnumNode
::
Builder
builder
)
{
// maps ordinal -> (code order, declaration)
std
::
multimap
<
uint
,
std
::
pair
<
uint
,
Declaration
::
Reader
>>
enumerants
;
uint
codeOrder
=
0
;
for
(
auto
member
:
members
)
{
if
(
member
.
getBody
().
which
()
==
Declaration
::
Body
::
ENUMERANT_DECL
)
{
enumerants
.
insert
(
std
::
make_pair
(
member
.
getId
().
getOrdinal
().
getValue
(),
std
::
make_pair
(
codeOrder
++
,
member
)));
}
}
auto
list
=
builder
.
initEnumerants
(
enumerants
.
size
());
uint
i
=
0
;
DuplicateOrdinalDetector
dupDetector
(
errorReporter
);
for
(
auto
&
entry
:
enumerants
)
{
uint
codeOrder
=
entry
.
second
.
first
;
Declaration
::
Reader
enumerantDecl
=
entry
.
second
.
second
;
dupDetector
.
check
(
enumerantDecl
.
getId
().
getOrdinal
());
auto
enumerantBuilder
=
list
[
i
];
enumerantBuilder
.
setName
(
enumerantDecl
.
getName
().
getValue
());
enumerantBuilder
.
setCodeOrder
(
codeOrder
);
enumerantBuilder
.
adoptAnnotations
(
compileAnnotationApplications
(
enumerantDecl
.
getAnnotations
(),
"targetsEnumerant"
));
}
}
// -------------------------------------------------------------------
class
NodeTranslator
::
StructTranslator
{
public
:
explicit
StructTranslator
(
NodeTranslator
&
translator
)
:
translator
(
translator
),
errorReporter
(
translator
.
errorReporter
)
{}
KJ_DISALLOW_COPY
(
StructTranslator
);
void
translate
(
Declaration
::
Struct
::
Reader
decl
,
List
<
Declaration
>::
Reader
members
,
schema
::
StructNode
::
Builder
builder
)
{
// Build the member-info-by-ordinal map.
MemberInfo
root
(
layout
.
getTop
());
traverseGroup
(
members
,
root
);
// Init the root.
root
.
memberSchemas
=
builder
.
initMembers
(
root
.
childCount
);
// Go through each member in ordinal order, building each member schema.
DuplicateOrdinalDetector
dupDetector
(
errorReporter
);
for
(
auto
&
entry
:
membersByOrdinal
)
{
MemberInfo
&
member
=
*
entry
.
second
;
if
(
member
.
decl
.
getId
().
which
()
==
Declaration
::
Id
::
ORDINAL
)
{
dupDetector
.
check
(
member
.
decl
.
getId
().
getOrdinal
());
}
schema
::
StructNode
::
Member
::
Builder
builder
=
member
.
parent
->
getMemberSchema
(
member
.
index
);
builder
.
setName
(
member
.
decl
.
getName
().
getValue
());
builder
.
setOrdinal
(
entry
.
first
);
builder
.
setCodeOrder
(
member
.
codeOrder
);
kj
::
StringPtr
targetsFlagName
;
switch
(
member
.
decl
.
getBody
().
which
())
{
case
Declaration
:
:
Body
::
FIELD_DECL
:
{
auto
fieldReader
=
member
.
decl
.
getBody
().
getFieldDecl
();
auto
fieldBuilder
=
builder
.
getBody
().
initFieldMember
();
auto
typeBuilder
=
fieldBuilder
.
initType
();
if
(
translator
.
compileType
(
fieldReader
.
getType
(),
typeBuilder
))
{
switch
(
fieldReader
.
getDefaultValue
().
which
())
{
case
Declaration
:
:
Field
::
DefaultValue
::
VALUE
:
translator
.
compileBootstrapValue
(
fieldReader
.
getDefaultValue
().
getValue
(),
typeBuilder
,
fieldBuilder
.
initDefaultValue
());
break
;
case
Declaration
:
:
Field
::
DefaultValue
::
NONE
:
translator
.
compileDefaultDefaultValue
(
typeBuilder
,
fieldBuilder
.
initDefaultValue
());
break
;
}
int
lgSize
=
-
1
;
switch
(
typeBuilder
.
getBody
().
which
())
{
case
schema
:
:
Type
::
Body
::
VOID_TYPE
:
lgSize
=
-
1
;
break
;
case
schema
:
:
Type
::
Body
::
BOOL_TYPE
:
lgSize
=
0
;
break
;
case
schema
:
:
Type
::
Body
::
INT8_TYPE
:
lgSize
=
3
;
break
;
case
schema
:
:
Type
::
Body
::
INT16_TYPE
:
lgSize
=
4
;
break
;
case
schema
:
:
Type
::
Body
::
INT32_TYPE
:
lgSize
=
5
;
break
;
case
schema
:
:
Type
::
Body
::
INT64_TYPE
:
lgSize
=
6
;
break
;
case
schema
:
:
Type
::
Body
::
UINT8_TYPE
:
lgSize
=
3
;
break
;
case
schema
:
:
Type
::
Body
::
UINT16_TYPE
:
lgSize
=
4
;
break
;
case
schema
:
:
Type
::
Body
::
UINT32_TYPE
:
lgSize
=
5
;
break
;
case
schema
:
:
Type
::
Body
::
UINT64_TYPE
:
lgSize
=
6
;
break
;
case
schema
:
:
Type
::
Body
::
FLOAT32_TYPE
:
lgSize
=
5
;
break
;
case
schema
:
:
Type
::
Body
::
FLOAT64_TYPE
:
lgSize
=
6
;
break
;
case
schema
:
:
Type
::
Body
::
TEXT_TYPE
:
lgSize
=
-
2
;
break
;
case
schema
:
:
Type
::
Body
::
DATA_TYPE
:
lgSize
=
-
2
;
break
;
case
schema
:
:
Type
::
Body
::
LIST_TYPE
:
lgSize
=
-
2
;
break
;
case
schema
:
:
Type
::
Body
::
ENUM_TYPE
:
lgSize
=
4
;
break
;
case
schema
:
:
Type
::
Body
::
STRUCT_TYPE
:
lgSize
=
-
2
;
break
;
case
schema
:
:
Type
::
Body
::
INTERFACE_TYPE
:
lgSize
=
-
2
;
break
;
case
schema
:
:
Type
::
Body
::
OBJECT_TYPE
:
lgSize
=
-
2
;
break
;
}
if
(
lgSize
==
-
2
)
{
// pointer
fieldBuilder
.
setOffset
(
member
.
fieldScope
->
addPointer
());
}
else
if
(
lgSize
==
-
1
)
{
// void
fieldBuilder
.
setOffset
(
0
);
}
else
{
fieldBuilder
.
setOffset
(
member
.
fieldScope
->
addData
(
lgSize
));
}
}
targetsFlagName
=
"targetsField"
;
break
;
}
case
Declaration
:
:
Body
::
UNION_DECL
:
if
(
member
.
decl
.
getId
().
which
()
==
Declaration
::
Id
::
ORDINAL
)
{
if
(
!
member
.
unionScope
->
addDiscriminant
())
{
errorReporter
.
addErrorOn
(
member
.
decl
.
getId
().
getOrdinal
(),
"Union ordinal, if specified, must be greater than no more than one of its "
"member ordinals (i.e. there can only be one field retroactively unionized)."
);
}
}
lateUnions
.
add
(
&
member
);
// No need to fill in members as this is done automatically elsewhere.
targetsFlagName
=
"targetsUnion"
;
break
;
case
Declaration
:
:
Body
::
GROUP_DECL
:
// Nothing to do here; members are filled in automatically elsewhere.
targetsFlagName
=
"targetsGroup"
;
break
;
default:
KJ_FAIL_ASSERT
(
"Unexpected member type."
);
break
;
}
builder
.
adoptAnnotations
(
translator
.
compileAnnotationApplications
(
member
.
decl
.
getAnnotations
(),
targetsFlagName
));
}
// OK, all members are built. The only thing left is the late unions.
for
(
auto
member
:
lateUnions
)
{
member
->
unionScope
->
addDiscriminant
();
// if it hasn't happened already
KJ_IF_MAYBE
(
offset
,
member
->
unionScope
->
discriminantOffset
)
{
member
->
parent
->
getMemberSchema
(
member
->
index
).
getBody
().
getUnionMember
()
.
setDiscriminantOffset
(
*
offset
);
}
else
{
KJ_FAIL_ASSERT
(
"addDiscriminant() didn't set the offset?"
);
}
}
}
private
:
NodeTranslator
&
translator
;
const
ErrorReporter
&
errorReporter
;
StructLayout
layout
;
kj
::
Arena
arena
;
struct
MemberInfo
{
MemberInfo
*
parent
;
// The MemberInfo for the parent scope.
uint
codeOrder
;
// Code order within the parent.
uint
childCount
=
0
;
// Number of children this member has.
uint
index
=
0
;
Declaration
::
Reader
decl
;
List
<
schema
::
StructNode
::
Member
>::
Builder
memberSchemas
;
union
{
StructLayout
::
StructOrGroup
*
fieldScope
;
// If this member is a field, the scope of that field. This will be used to assign an
// offset for the field when going through in ordinal order.
//
// If the member is a group, this is is the group itself.
StructLayout
::
Union
*
unionScope
;
// If this member is a union, this is the union. This will be used to assign a discriminant
// offset.
};
inline
MemberInfo
(
StructLayout
::
Top
&
topScope
)
:
parent
(
nullptr
),
codeOrder
(
0
),
fieldScope
(
&
topScope
)
{}
inline
MemberInfo
(
MemberInfo
&
parent
,
uint
codeOrder
,
const
Declaration
::
Reader
&
decl
,
StructLayout
::
StructOrGroup
&
fieldScope
)
:
parent
(
&
parent
),
codeOrder
(
codeOrder
),
decl
(
decl
),
fieldScope
(
&
fieldScope
)
{}
inline
MemberInfo
(
MemberInfo
&
parent
,
uint
codeOrder
,
const
Declaration
::
Reader
&
decl
,
StructLayout
::
Union
&
unionScope
)
:
parent
(
&
parent
),
codeOrder
(
codeOrder
),
decl
(
decl
),
unionScope
(
&
unionScope
)
{}
schema
::
StructNode
::
Member
::
Builder
getMemberSchema
(
uint
childIndex
)
{
// Get the schema builder for the child member at the given index. This lazily/dynamically
// builds the builder tree.
KJ_REQUIRE
(
childIndex
<
childCount
);
if
(
memberSchemas
.
size
()
==
0
)
{
switch
(
decl
.
getBody
().
which
())
{
case
Declaration
:
:
Body
::
FIELD_DECL
:
KJ_FAIL_ASSERT
(
"Fields don't have members."
);
break
;
case
Declaration
:
:
Body
::
UNION_DECL
:
memberSchemas
=
parent
->
getMemberSchema
(
index
).
getBody
()
.
initUnionMember
().
initMembers
(
childCount
);
break
;
case
Declaration
:
:
Body
::
GROUP_DECL
:
memberSchemas
=
parent
->
getMemberSchema
(
index
).
getBody
()
.
initGroupMember
().
initMembers
(
childCount
);
break
;
default
:
KJ_FAIL_ASSERT
(
"Unexpected member type."
);
break
;
}
}
return
memberSchemas
[
childIndex
];
}
};
std
::
multimap
<
uint
,
MemberInfo
*>
membersByOrdinal
;
// For fields, the key is the ordinal. For unions and groups, the key is the lowest ordinal
// number among their members, or the union's explicit ordinal number if it has one.
kj
::
Vector
<
MemberInfo
*>
lateUnions
;
// Unions that need to have their discriminant offsets filled in after layout is complete.
uint
traverseUnion
(
List
<
Declaration
>::
Reader
members
,
MemberInfo
&
parent
)
{
uint
minOrdinal
=
UINT_MAX
;
uint
codeOrder
=
0
;
if
(
members
.
size
()
<
2
)
{
errorReporter
.
addErrorOn
(
parent
.
decl
,
"Union must have at least two members."
);
}
for
(
auto
member
:
members
)
{
uint
ordinal
=
0
;
MemberInfo
*
memberInfo
=
nullptr
;
switch
(
member
.
getBody
().
which
())
{
case
Declaration
:
:
Body
::
FIELD_DECL
:
{
StructLayout
::
Group
&
singletonGroup
=
arena
.
allocate
<
StructLayout
::
Group
>
(
*
parent
.
unionScope
);
memberInfo
=
&
arena
.
allocate
<
MemberInfo
>
(
parent
,
codeOrder
++
,
member
,
singletonGroup
);
ordinal
=
member
.
getId
().
getOrdinal
().
getValue
();
break
;
}
case
Declaration
:
:
Body
::
UNION_DECL
:
errorReporter
.
addErrorOn
(
member
,
"Unions cannot contain unions."
);
break
;
case
Declaration
:
:
Body
::
GROUP_DECL
:
{
StructLayout
::
Group
&
group
=
arena
.
allocate
<
StructLayout
::
Group
>
(
*
parent
.
unionScope
);
memberInfo
=
&
arena
.
allocate
<
MemberInfo
>
(
parent
,
codeOrder
++
,
member
,
group
);
ordinal
=
traverseGroup
(
member
.
getNestedDecls
(),
*
memberInfo
);
break
;
}
default:
// Ignore others.
break
;
}
if
(
memberInfo
!=
nullptr
)
{
memberInfo
->
index
=
parent
.
childCount
++
;
membersByOrdinal
.
insert
(
std
::
make_pair
(
ordinal
,
memberInfo
));
minOrdinal
=
kj
::
min
(
minOrdinal
,
ordinal
);
}
}
return
minOrdinal
;
}
uint
traverseGroup
(
List
<
Declaration
>::
Reader
members
,
MemberInfo
&
parent
)
{
uint
minOrdinal
=
UINT_MAX
;
uint
codeOrder
=
0
;
if
(
members
.
size
()
<
2
)
{
errorReporter
.
addErrorOn
(
parent
.
decl
,
"Group must have at least two members."
);
}
for
(
auto
member
:
members
)
{
uint
ordinal
=
0
;
MemberInfo
*
memberInfo
=
nullptr
;
switch
(
member
.
getBody
().
which
())
{
case
Declaration
:
:
Body
::
FIELD_DECL
:
{
memberInfo
=
&
arena
.
allocate
<
MemberInfo
>
(
parent
,
codeOrder
++
,
member
,
*
parent
.
fieldScope
);
break
;
}
case
Declaration
:
:
Body
::
UNION_DECL
:
{
StructLayout
::
Union
&
unionLayout
=
arena
.
allocate
<
StructLayout
::
Union
>
(
*
parent
.
fieldScope
);
memberInfo
=
&
arena
.
allocate
<
MemberInfo
>
(
parent
,
codeOrder
++
,
member
,
unionLayout
);
ordinal
=
traverseUnion
(
member
.
getNestedDecls
(),
*
memberInfo
);
if
(
member
.
getId
().
which
()
==
Declaration
::
Id
::
ORDINAL
)
{
ordinal
=
member
.
getId
().
getOrdinal
().
getValue
();
}
break
;
}
case
Declaration
:
:
Body
::
GROUP_DECL
:
errorReporter
.
addErrorOn
(
member
,
"Groups should only appear inside unions."
);
break
;
default:
// Ignore others.
break
;
}
if
(
memberInfo
!=
nullptr
)
{
memberInfo
->
index
=
parent
.
childCount
++
;
membersByOrdinal
.
insert
(
std
::
make_pair
(
ordinal
,
memberInfo
));
minOrdinal
=
kj
::
min
(
minOrdinal
,
ordinal
);
}
}
return
minOrdinal
;
}
};
void
NodeTranslator
::
compileStruct
(
Declaration
::
Struct
::
Reader
decl
,
List
<
Declaration
>::
Reader
members
,
schema
::
StructNode
::
Builder
builder
)
{
}
// -------------------------------------------------------------------
void
NodeTranslator
::
compileInterface
(
Declaration
::
Interface
::
Reader
decl
,
List
<
Declaration
>::
Reader
members
,
schema
::
InterfaceNode
::
Builder
builder
)
{
}
void
NodeTranslator
::
compileBootstrapValue
(
ValueExpression
::
Reader
source
,
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Builder
target
)
{
}
void
NodeTranslator
::
compileFinalValue
(
ValueExpression
::
Reader
source
,
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Builder
target
)
{
}
Orphan
<
List
<
schema
::
Annotation
>>
NodeTranslator
::
compileAnnotationApplications
(
List
<
Declaration
::
AnnotationApplication
>::
Reader
annotations
,
kj
::
StringPtr
targetsFlagName
)
{
}
}
// namespace compiler
}
// namespace capnp
c++/src/capnp/compiler/node-translator.h
0 → 100644
View file @
0da57538
// 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_NODE_TRANSLATOR_H_
#define CAPNP_COMPILER_NODE_TRANSLATOR_H_
#include <capnp/orphan.h>
#include <capnp/compiler/grammar.capnp.h>
#include <capnp/schema.capnp.h>
#include <capnp/schema-loader.h>
#include <capnp/dynamic.h>
#include <kj/vector.h>
#include "error-reporter.h"
namespace
capnp
{
namespace
compiler
{
class
NodeTranslator
{
// Translates one node in the schema from AST form to final schema form. A "node" is anything
// that has a unique ID, such as structs, enums, constants, and annotations, but not fields,
// unions, enumerants, or methods (the latter set have 16-bit ordinals but not 64-bit global IDs).
public
:
class
Resolver
{
// Callback class used to find other nodes relative to this one.
public
:
struct
ResolvedName
{
uint64_t
id
;
Declaration
::
Body
::
Which
kind
;
};
virtual
kj
::
Maybe
<
ResolvedName
>
resolve
(
const
DeclName
::
Reader
&
name
)
const
=
0
;
// Look up the given name, relative to this node, and return basic information about the
// target.
virtual
kj
::
Maybe
<
Schema
>
resolveMaybeBootstrapSchema
(
uint64_t
id
)
const
=
0
;
// Get the schema for the given ID. Returning either a bootstrap schema or a final schema
// is acceptable.
virtual
kj
::
Maybe
<
Schema
>
resolveFinalSchema
(
uint64_t
id
)
const
=
0
;
// Get the final schema for the given ID. A bootstrap schema is not acceptable.
};
NodeTranslator
(
const
Resolver
&
resolver
,
const
ErrorReporter
&
errorReporter
,
const
Declaration
::
Reader
&
decl
,
Orphan
<
schema
::
Node
>
wipNode
);
// Construct a NodeTranslator to translate the given declaration. The wipNode starts out with
// `displayName`, `id`, `scopeId`, and `nestedNodes` already initialized. The `NodeTranslator`
// fills in the rest.
schema
::
Node
::
Reader
getBootstrapNode
()
{
return
wipNode
.
getReader
();
}
// Get an incomplete version of the node in which pointer-typed value expressions have not yet
// been translated. Instead, for all `schema.Value` objects representing pointer-type values,
// the value is set to an appropriate "empty" value. This version of the schema can be used to
// bootstrap the dynamic API which can then in turn be used to encode the missing complex values.
//
// If the final node has already been built, this will actually return the final node (in fact,
// it's the same node object).
schema
::
Node
::
Reader
finish
();
// Finish translating the node (including filling in all the pieces that are missing from the
// bootstrap node) and return it.
private
:
const
Resolver
&
resolver
;
const
ErrorReporter
&
errorReporter
;
Orphan
<
schema
::
Node
>
wipNode
;
// The work-in-progress schema node.
struct
UnfinishedValue
{
ValueExpression
::
Reader
source
;
schema
::
Type
::
Reader
type
;
schema
::
Value
::
Builder
target
;
};
kj
::
Vector
<
UnfinishedValue
>
unfinishedValues
;
// List of values in `wipNode` which have not yet been interpreted, because they are structs
// or lists and as such interpreting them require using the types' schemas (to take advantage
// of the dynamic API). Once bootstrap schemas have been built, they can be used to interpret
// these values.
void
compileNode
(
Declaration
::
Reader
decl
,
schema
::
Node
::
Builder
builder
);
void
checkMembers
(
List
<
Declaration
>::
Reader
nestedDecls
,
Declaration
::
Body
::
Which
parentKind
);
// Check the given member list for errors, including detecting duplicate names and detecting
// out-of-place declarations.
void
disallowNested
(
List
<
Declaration
>::
Reader
nestedDecls
);
// Complain if the nested decl list is non-empty.
void
compileFile
(
Declaration
::
Reader
decl
,
schema
::
FileNode
::
Builder
builder
);
void
compileConst
(
Declaration
::
Const
::
Reader
decl
,
schema
::
ConstNode
::
Builder
builder
);
void
compileAnnotation
(
Declaration
::
Annotation
::
Reader
decl
,
schema
::
AnnotationNode
::
Builder
builder
);
class
DuplicateOrdinalDetector
;
class
StructLayout
;
class
StructTranslator
;
void
compileEnum
(
Declaration
::
Enum
::
Reader
decl
,
List
<
Declaration
>::
Reader
members
,
schema
::
EnumNode
::
Builder
builder
);
void
compileStruct
(
Declaration
::
Struct
::
Reader
decl
,
List
<
Declaration
>::
Reader
members
,
schema
::
StructNode
::
Builder
builder
);
void
compileInterface
(
Declaration
::
Interface
::
Reader
decl
,
List
<
Declaration
>::
Reader
members
,
schema
::
InterfaceNode
::
Builder
builder
);
// The `members` arrays contain only members with ordinal numbers, in code order. Other members
// are handled elsewhere.
bool
compileType
(
TypeExpression
::
Reader
source
,
schema
::
Type
::
Builder
target
);
// Returns false if there was a problem, in which case value expressions of this type should
// not be parsed.
void
compileDefaultDefaultValue
(
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Builder
target
);
// Initializes `target` to contain the "default default" value for `type`.
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.
void
compileFinalValue
(
ValueExpression
::
Reader
source
,
schema
::
Type
::
Reader
type
,
schema
::
Value
::
Builder
target
);
// Compile a previously-unfinished value. See `unfinishedValues`.
Orphan
<
List
<
schema
::
Annotation
>>
compileAnnotationApplications
(
List
<
Declaration
::
AnnotationApplication
>::
Reader
annotations
,
kj
::
StringPtr
targetsFlagName
);
};
}
// namespace compiler
}
// namespace capnp
#endif // CAPNP_COMPILER_NODE_TRANSLATOR_H_
c++/src/capnp/compiler/parser.c++
View file @
0da57538
...
...
@@ -57,6 +57,7 @@ void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result,
kj
::
Vector
<
Orphan
<
Declaration
::
AnnotationApplication
>>
annotations
;
auto
fileDecl
=
result
.
getRoot
();
fileDecl
.
getBody
().
initFileDecl
();
for
(
auto
statement
:
statements
)
{
KJ_IF_MAYBE
(
decl
,
parser
.
parseStatement
(
statement
,
parser
.
getParsers
().
fileLevelDecl
))
{
...
...
@@ -556,13 +557,13 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP
// -----------------------------------------------------------------
parsers
.
usingDecl
=
arena
.
copy
(
p
::
transform
(
p
::
sequence
(
keyword
(
"using"
),
identifier
,
op
(
"="
),
parsers
.
typeExpression
),
[
this
](
Located
<
Text
::
Reader
>&&
name
,
Orphan
<
TypeExpression
>&&
type
)
->
DeclParserResult
{
p
::
sequence
(
keyword
(
"using"
),
identifier
,
op
(
"="
),
parsers
.
declName
),
[
this
](
Located
<
Text
::
Reader
>&&
name
,
Orphan
<
DeclName
>&&
target
)
->
DeclParserResult
{
auto
decl
=
orphanage
.
newOrphan
<
Declaration
>
();
auto
builder
=
decl
.
get
();
name
.
copyTo
(
builder
.
initName
());
// no id, no annotations for using decl
builder
.
getBody
().
initUsingDecl
().
adoptTarget
(
kj
::
mv
(
t
ype
));
builder
.
getBody
().
initUsingDecl
().
adoptTarget
(
kj
::
mv
(
t
arget
));
return
DeclParserResult
(
kj
::
mv
(
decl
));
}));
...
...
c++/src/capnp/schema.capnp
View file @
0da57538
...
...
@@ -151,7 +151,17 @@ struct FileNode {
imports @0 :List(Import);
struct Import {
id @0 :Id;
# ID of the imported file.
# DEPRECATED: ID of the imported file. This is no longer filled in because it is hostile to
# lazy importing: since this import list appears in the FileNode, and since the FileNode must
# necessarily be cosntructed if any schemas in the file are used, the implication of listing
# import IDs here is that if a schema file is used at all, all of its imports must be parsed,
# just to get their IDs. We'd much rather delay parsing a file until something inside it is
# actually used.
#
# In any case, this import list's main reason for existing is to make it easy to generate
# the appropriate #include statements in C++. The IDs of files aren't needed for that.
#
# TODO(someday): Perhaps provide an alternative way to identify the remote file.
name @1 :Text;
# Name which *this* file used to refer to the foreign file. This may be a relative name.
...
...
@@ -197,6 +207,11 @@ struct StructNode {
name @0 :Text;
ordinal @1 :UInt16;
# For fields, the ordinal number. For unions, if an explicit ordinal was given, that number.
# Otherwise, for unions and groups, this is the ordinal of the lowest-numbered field in the
# union/group.
#
# TODO(someday): When revamping the meta-schema, move this into Field.
codeOrder @2 :UInt16;
# Indicates where this member appeared in the code, relative to other members.
...
...
@@ -214,6 +229,7 @@ struct StructNode {
fieldMember @5 :Field;
unionMember @6 :Union;
groupMember @7 :Group;
}
}
...
...
@@ -236,6 +252,10 @@ struct StructNode {
# consumers should skip member types that they don't understand. The first member in this list
# gets discriminant value zero, the next gets one, and so on.
}
struct Group {
members @0 :List(Member);
}
}
struct EnumNode {
...
...
c++/src/kj/array.h
View file @
0da57538
...
...
@@ -325,10 +325,10 @@ public:
}
template
<
typename
...
Params
>
void
add
(
Params
&&
...
params
)
{
T
&
add
(
Params
&&
...
params
)
{
KJ_IREQUIRE
(
pos
<
endPtr
,
"Added too many elements to ArrayBuilder."
);
ctor
(
*
pos
,
kj
::
fwd
<
Params
>
(
params
)...);
++
pos
;
return
*
pos
++
;
}
template
<
typename
Container
>
...
...
c++/src/kj/vector.h
View file @
0da57538
...
...
@@ -70,9 +70,9 @@ public:
}
template
<
typename
...
Params
>
inline
void
add
(
Params
&&
...
params
)
{
inline
T
&
add
(
Params
&&
...
params
)
{
if
(
builder
.
isFull
())
grow
();
builder
.
add
(
kj
::
fwd
<
Params
>
(
params
)...);
return
builder
.
add
(
kj
::
fwd
<
Params
>
(
params
)...);
}
template
<
typename
Iterator
>
...
...
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