mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-26 11:40:44 +00:00
- added the script compiler's front end.
This commit is contained in:
parent
99d3dc67ae
commit
006916a0a6
14 changed files with 15971 additions and 1 deletions
|
@ -197,7 +197,6 @@ if( NO_OPENAL )
|
||||||
add_definitions( -DNO_OPENAL=1 )
|
add_definitions( -DNO_OPENAL=1 )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# Decide on SSE setup
|
# Decide on SSE setup
|
||||||
|
|
||||||
# SSE only matters on 32-bit targets. We check compiler flags to know if we can do it.
|
# SSE only matters on 32-bit targets. We check compiler flags to know if we can do it.
|
||||||
|
@ -561,6 +560,10 @@ else()
|
||||||
endif( NOT NO_OPENMP )
|
endif( NOT NO_OPENMP )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/zcc-parse.c ${CMAKE_CURRENT_BINARY_DIR}/zcc-parse.h
|
||||||
|
COMMAND lemon -C${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/frontend/zcc-parse.lemon
|
||||||
|
DEPENDS lemon ${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/frontend/zcc-parse.lemon )
|
||||||
|
|
||||||
add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h
|
add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h
|
||||||
COMMAND re2c --no-generation-date -s -o ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h ${CMAKE_CURRENT_SOURCE_DIR}/common/engine/sc_man_scanner.re
|
COMMAND re2c --no-generation-date -s -o ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h ${CMAKE_CURRENT_SOURCE_DIR}/common/engine/sc_man_scanner.re
|
||||||
DEPENDS re2c ${CMAKE_CURRENT_SOURCE_DIR}/common/engine/sc_man_scanner.re )
|
DEPENDS re2c ${CMAKE_CURRENT_SOURCE_DIR}/common/engine/sc_man_scanner.re )
|
||||||
|
@ -617,6 +620,7 @@ file( GLOB HEADER_FILES
|
||||||
common/scripting/jit/*h
|
common/scripting/jit/*h
|
||||||
common/scripting/interface/*.h
|
common/scripting/interface/*.h
|
||||||
common/scripting/backend/*.h
|
common/scripting/backend/*.h
|
||||||
|
common/scripting/frontend/*.h
|
||||||
|
|
||||||
build/src/*.h
|
build/src/*.h
|
||||||
platform/win32/*.h
|
platform/win32/*.h
|
||||||
|
@ -635,6 +639,9 @@ set( NOT_COMPILED_SOURCE_FILES
|
||||||
${OTHER_SYSTEM_SOURCES}
|
${OTHER_SYSTEM_SOURCES}
|
||||||
sc_man_scanner.h
|
sc_man_scanner.h
|
||||||
common/engine/sc_man_scanner.re
|
common/engine/sc_man_scanner.re
|
||||||
|
common/scripting/frontend/zcc-parse.lemon
|
||||||
|
zcc-parse.c
|
||||||
|
zcc-parse.h
|
||||||
platform/win32/zutil.natvis
|
platform/win32/zutil.natvis
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -797,9 +804,14 @@ set (PCH_SOURCES
|
||||||
common/scripting/core/types.cpp
|
common/scripting/core/types.cpp
|
||||||
common/scripting/core/scopebarrier.cpp
|
common/scripting/core/scopebarrier.cpp
|
||||||
common/scripting/core/vmdisasm.cpp
|
common/scripting/core/vmdisasm.cpp
|
||||||
|
common/scripting/core/imports.cpp
|
||||||
common/scripting/vm/vmexec.cpp
|
common/scripting/vm/vmexec.cpp
|
||||||
common/scripting/vm/vmframe.cpp
|
common/scripting/vm/vmframe.cpp
|
||||||
common/scripting/interface/stringformat.cpp
|
common/scripting/interface/stringformat.cpp
|
||||||
|
#common/scripting/interface/exports.cpp
|
||||||
|
common/scripting/frontend/ast.cpp
|
||||||
|
common/scripting/frontend/zcc_compile.cpp
|
||||||
|
common/scripting/frontend/zcc_parser.cpp
|
||||||
common/scripting/backend/vmbuilder.cpp
|
common/scripting/backend/vmbuilder.cpp
|
||||||
common/scripting/backend/codegen.cpp
|
common/scripting/backend/codegen.cpp
|
||||||
|
|
||||||
|
@ -965,6 +977,7 @@ include_directories(
|
||||||
common/scripting/jit
|
common/scripting/jit
|
||||||
common/scripting/core
|
common/scripting/core
|
||||||
common/scripting/interface
|
common/scripting/interface
|
||||||
|
common/scripting/frontend
|
||||||
common/scripting/backend
|
common/scripting/backend
|
||||||
${CMAKE_BINARY_DIR}/libraries/gdtoa
|
${CMAKE_BINARY_DIR}/libraries/gdtoa
|
||||||
|
|
||||||
|
@ -1083,6 +1096,7 @@ source_group("Common\\Fonts" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/co
|
||||||
source_group("Common\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/.+")
|
source_group("Common\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/.+")
|
||||||
source_group("Common\\Scripting" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/.+")
|
source_group("Common\\Scripting" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/.+")
|
||||||
source_group("Common\\Scripting\\Interface" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/interface/.+")
|
source_group("Common\\Scripting\\Interface" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/interface/.+")
|
||||||
|
source_group("Common\\Scripting\\Frontend" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/frontend/.+" FILES ${CMAKE_CURRENT_BINARY_DIR}/zcc-parse.c ${CMAKE_CURRENT_BINARY_DIR}/zcc-parse.h)
|
||||||
source_group("Common\\Scripting\\Backend" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/backend/.+")
|
source_group("Common\\Scripting\\Backend" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/backend/.+")
|
||||||
source_group("Common\\Scripting\\Core" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/core/.+")
|
source_group("Common\\Scripting\\Core" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/core/.+")
|
||||||
source_group("Common\\Scripting\\JIT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/jit/.+")
|
source_group("Common\\Scripting\\JIT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/jit/.+")
|
||||||
|
|
253
source/common/scripting/core/imports.cpp
Normal file
253
source/common/scripting/core/imports.cpp
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
** thingdef_data.cpp
|
||||||
|
**
|
||||||
|
** DECORATE data tables
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2002-2020 Christoph Oelckers
|
||||||
|
** Copyright 2004-2008 Randy Heit
|
||||||
|
** 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.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||||
|
** covered by the terms of the GNU General Public License as published by
|
||||||
|
** the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
** your option) any later version.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "gstrings.h"
|
||||||
|
#include "v_font.h"
|
||||||
|
#include "menu/menu.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "dictionary.h"
|
||||||
|
#include "vm.h"
|
||||||
|
#include "symbols.h"
|
||||||
|
|
||||||
|
static TArray<AFuncDesc> AFTable;
|
||||||
|
static TArray<FieldDesc> FieldTable;
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
template <typename Desc>
|
||||||
|
static int CompareClassNames(const char* const aname, const Desc& b)
|
||||||
|
{
|
||||||
|
// ++ to get past the prefix letter of the native class name, which gets omitted by the FName for the class.
|
||||||
|
const char* bname = b.ClassName;
|
||||||
|
if ('\0' != *bname) ++bname;
|
||||||
|
return stricmp(aname, bname);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Desc>
|
||||||
|
static int CompareClassNames(const Desc& a, const Desc& b)
|
||||||
|
{
|
||||||
|
// ++ to get past the prefix letter of the native class name, which gets omitted by the FName for the class.
|
||||||
|
const char* aname = a.ClassName;
|
||||||
|
if ('\0' != *aname) ++aname;
|
||||||
|
return CompareClassNames(aname, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Find a function by name using a binary search
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
AFuncDesc *FindFunction(PContainerType *cls, const char * string)
|
||||||
|
{
|
||||||
|
int min = 0, max = AFTable.Size() - 1;
|
||||||
|
|
||||||
|
while (min <= max)
|
||||||
|
{
|
||||||
|
int mid = (min + max) / 2;
|
||||||
|
int lexval = CompareClassNames(cls->TypeName.GetChars(), AFTable[mid]);
|
||||||
|
if (lexval == 0) lexval = stricmp(string, AFTable[mid].FuncName);
|
||||||
|
if (lexval == 0)
|
||||||
|
{
|
||||||
|
return &AFTable[mid];
|
||||||
|
}
|
||||||
|
else if (lexval > 0)
|
||||||
|
{
|
||||||
|
min = mid + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
max = mid - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Find a function by name using a binary search
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FieldDesc *FindField(PContainerType *cls, const char * string)
|
||||||
|
{
|
||||||
|
int min = 0, max = FieldTable.Size() - 1;
|
||||||
|
const char * cname = cls ? cls->TypeName.GetChars() : "";
|
||||||
|
|
||||||
|
while (min <= max)
|
||||||
|
{
|
||||||
|
int mid = (min + max) / 2;
|
||||||
|
int lexval = CompareClassNames(cname, FieldTable[mid]);
|
||||||
|
if (lexval == 0) lexval = stricmp(string, FieldTable[mid].FieldName);
|
||||||
|
if (lexval == 0)
|
||||||
|
{
|
||||||
|
return &FieldTable[mid];
|
||||||
|
}
|
||||||
|
else if (lexval > 0)
|
||||||
|
{
|
||||||
|
min = mid + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
max = mid - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Find an action function in AActor's table
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
VMFunction *FindVMFunction(PClass *cls, const char *name)
|
||||||
|
{
|
||||||
|
auto f = dyn_cast<PFunction>(cls->FindSymbol(name, true));
|
||||||
|
return f == nullptr ? nullptr : f->Variants[0].Implementation;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Sorting helpers
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
static int funccmp(const void * a, const void * b)
|
||||||
|
{
|
||||||
|
int res = CompareClassNames(*(AFuncDesc*)a, *(AFuncDesc*)b);
|
||||||
|
if (res == 0) res = stricmp(((AFuncDesc*)a)->FuncName, ((AFuncDesc*)b)->FuncName);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fieldcmp(const void * a, const void * b)
|
||||||
|
{
|
||||||
|
int res = CompareClassNames(*(FieldDesc*)a, *(FieldDesc*)b);
|
||||||
|
if (res == 0) res = stricmp(((FieldDesc*)a)->FieldName, ((FieldDesc*)b)->FieldName);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Initialization
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void InitImports()
|
||||||
|
{
|
||||||
|
auto fontstruct = NewStruct("FFont", nullptr, true);
|
||||||
|
fontstruct->Size = sizeof(FFont);
|
||||||
|
fontstruct->Align = alignof(FFont);
|
||||||
|
NewPointer(fontstruct, false)->InstallHandlers(
|
||||||
|
[](FSerializer &ar, const char *key, const void *addr)
|
||||||
|
{
|
||||||
|
ar(key, *(FFont **)addr);
|
||||||
|
},
|
||||||
|
[](FSerializer &ar, const char *key, void *addr)
|
||||||
|
{
|
||||||
|
Serialize<FFont>(ar, key, *(FFont **)addr, nullptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a sorted list of native action functions
|
||||||
|
AFTable.Clear();
|
||||||
|
if (AFTable.Size() == 0)
|
||||||
|
{
|
||||||
|
FAutoSegIterator probe(ARegHead, ARegTail);
|
||||||
|
|
||||||
|
while (*++probe != NULL)
|
||||||
|
{
|
||||||
|
AFuncDesc *afunc = (AFuncDesc *)*probe;
|
||||||
|
assert(afunc->VMPointer != NULL);
|
||||||
|
*(afunc->VMPointer) = new VMNativeFunction(afunc->Function, afunc->FuncName);
|
||||||
|
(*(afunc->VMPointer))->PrintableName.Format("%s.%s [Native]", afunc->ClassName+1, afunc->FuncName);
|
||||||
|
(*(afunc->VMPointer))->DirectNativeCall = afunc->DirectNative;
|
||||||
|
AFTable.Push(*afunc);
|
||||||
|
}
|
||||||
|
AFTable.ShrinkToFit();
|
||||||
|
qsort(&AFTable[0], AFTable.Size(), sizeof(AFTable[0]), funccmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldTable.Clear();
|
||||||
|
if (FieldTable.Size() == 0)
|
||||||
|
{
|
||||||
|
FAutoSegIterator probe(FRegHead, FRegTail);
|
||||||
|
|
||||||
|
while (*++probe != NULL)
|
||||||
|
{
|
||||||
|
FieldDesc *afield = (FieldDesc *)*probe;
|
||||||
|
FieldTable.Push(*afield);
|
||||||
|
}
|
||||||
|
FieldTable.ShrinkToFit();
|
||||||
|
qsort(&FieldTable[0], FieldTable.Size(), sizeof(FieldTable[0]), fieldcmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// SetImplicitArgs
|
||||||
|
//
|
||||||
|
// Adds the parameters implied by the function flags.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void SetImplicitArgs(TArray<PType *> *args, TArray<uint32_t> *argflags, TArray<FName> *argnames, PContainerType *cls, uint32_t funcflags, int useflags)
|
||||||
|
{
|
||||||
|
// Must be called before adding any other arguments.
|
||||||
|
assert(args == nullptr || args->Size() == 0);
|
||||||
|
assert(argflags == nullptr || argflags->Size() == 0);
|
||||||
|
|
||||||
|
if (funcflags & VARF_Method)
|
||||||
|
{
|
||||||
|
// implied self pointer
|
||||||
|
if (args != nullptr) args->Push(NewPointer(cls, !!(funcflags & VARF_ReadOnly)));
|
||||||
|
if (argflags != nullptr) argflags->Push(VARF_Implicit | VARF_ReadOnly);
|
||||||
|
if (argnames != nullptr) argnames->Push(NAME_self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -267,3 +267,9 @@ struct FNamespaceManager
|
||||||
|
|
||||||
extern FNamespaceManager Namespaces;
|
extern FNamespaceManager Namespaces;
|
||||||
void RemoveUnusedSymbols();
|
void RemoveUnusedSymbols();
|
||||||
|
|
||||||
|
struct AFuncDesc;
|
||||||
|
struct FieldDesc;
|
||||||
|
AFuncDesc *FindFunction(PContainerType *cls, const char * string);
|
||||||
|
FieldDesc *FindField(PContainerType *cls, const char * string);
|
||||||
|
void SetImplicitArgs(TArray<PType*>* args, TArray<uint32_t>* argflags, TArray<FName>* argnames, PContainerType* cls, uint32_t funcflags, int useflags);
|
||||||
|
|
982
source/common/scripting/frontend/ast.cpp
Normal file
982
source/common/scripting/frontend/ast.cpp
Normal file
|
@ -0,0 +1,982 @@
|
||||||
|
/*
|
||||||
|
** ast.cpp
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright -2016 Randy Heit
|
||||||
|
** 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.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "dobject.h"
|
||||||
|
#include "vmintern.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "zcc_parser.h"
|
||||||
|
#include "zcc-parse.h"
|
||||||
|
#include "printf.h"
|
||||||
|
|
||||||
|
class FLispString;
|
||||||
|
extern void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *);
|
||||||
|
|
||||||
|
static const char *BuiltInTypeNames[] =
|
||||||
|
{
|
||||||
|
"sint8", "uint8",
|
||||||
|
"sint16", "uint16",
|
||||||
|
"sint32", "uint32_t",
|
||||||
|
"intauto",
|
||||||
|
|
||||||
|
"bool",
|
||||||
|
"float64", "floatauto",
|
||||||
|
"string",
|
||||||
|
"vector2",
|
||||||
|
"vector3",
|
||||||
|
"name",
|
||||||
|
|
||||||
|
"color",
|
||||||
|
"state",
|
||||||
|
"sound",
|
||||||
|
|
||||||
|
"usertype",
|
||||||
|
"nativetype",
|
||||||
|
"let",
|
||||||
|
};
|
||||||
|
|
||||||
|
class FLispString
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
operator FString &() { return Str; }
|
||||||
|
|
||||||
|
FLispString()
|
||||||
|
{
|
||||||
|
NestDepth = Column = 0;
|
||||||
|
WrapWidth = 200;
|
||||||
|
NeedSpace = false;
|
||||||
|
ConsecOpens = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Open(const char *label)
|
||||||
|
{
|
||||||
|
size_t labellen = label != NULL ? strlen(label) : 0;
|
||||||
|
CheckWrap(labellen + 1 + NeedSpace);
|
||||||
|
if (NeedSpace)
|
||||||
|
{
|
||||||
|
Str << ' ';
|
||||||
|
ConsecOpens = 0;
|
||||||
|
}
|
||||||
|
Str << '(';
|
||||||
|
ConsecOpens++;
|
||||||
|
if (label != NULL)
|
||||||
|
{
|
||||||
|
Str.AppendCStrPart(label, labellen);
|
||||||
|
}
|
||||||
|
Column += labellen + 1 + NeedSpace;
|
||||||
|
NestDepth++;
|
||||||
|
NeedSpace = (label != NULL);
|
||||||
|
}
|
||||||
|
void Close()
|
||||||
|
{
|
||||||
|
assert(NestDepth != 0);
|
||||||
|
Str << ')';
|
||||||
|
Column++;
|
||||||
|
NestDepth--;
|
||||||
|
NeedSpace = true;
|
||||||
|
}
|
||||||
|
void Break()
|
||||||
|
{
|
||||||
|
// Don't break if not needed.
|
||||||
|
if (Column != NestDepth)
|
||||||
|
{
|
||||||
|
if (NeedSpace)
|
||||||
|
{
|
||||||
|
ConsecOpens = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // Move hanging ( characters to the new line
|
||||||
|
Str.Truncate(Str.Len() - ConsecOpens);
|
||||||
|
NestDepth -= ConsecOpens;
|
||||||
|
}
|
||||||
|
Str << '\n';
|
||||||
|
Column = NestDepth;
|
||||||
|
NeedSpace = false;
|
||||||
|
if (NestDepth > 0)
|
||||||
|
{
|
||||||
|
Str.AppendFormat("%*s", (int)NestDepth, "");
|
||||||
|
}
|
||||||
|
if (ConsecOpens > 0)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < ConsecOpens; ++i)
|
||||||
|
{
|
||||||
|
Str << '(';
|
||||||
|
}
|
||||||
|
NestDepth += ConsecOpens;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool CheckWrap(size_t len)
|
||||||
|
{
|
||||||
|
if (len + Column > WrapWidth)
|
||||||
|
{
|
||||||
|
Break();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void Add(const char *str, size_t len)
|
||||||
|
{
|
||||||
|
CheckWrap(len + NeedSpace);
|
||||||
|
if (NeedSpace)
|
||||||
|
{
|
||||||
|
Str << ' ';
|
||||||
|
}
|
||||||
|
Str.AppendCStrPart(str, len);
|
||||||
|
Column += len + NeedSpace;
|
||||||
|
NeedSpace = true;
|
||||||
|
}
|
||||||
|
void Add(const char *str)
|
||||||
|
{
|
||||||
|
Add(str, strlen(str));
|
||||||
|
}
|
||||||
|
void Add(FString &str)
|
||||||
|
{
|
||||||
|
Add(str.GetChars(), str.Len());
|
||||||
|
}
|
||||||
|
void AddName(FName name)
|
||||||
|
{
|
||||||
|
size_t namelen = strlen(name.GetChars());
|
||||||
|
CheckWrap(namelen + 2 + NeedSpace);
|
||||||
|
if (NeedSpace)
|
||||||
|
{
|
||||||
|
NeedSpace = false;
|
||||||
|
Str << ' ';
|
||||||
|
}
|
||||||
|
Str << '\'' << name.GetChars() << '\'';
|
||||||
|
Column += namelen + 2 + NeedSpace;
|
||||||
|
NeedSpace = true;
|
||||||
|
}
|
||||||
|
void AddChar(char c)
|
||||||
|
{
|
||||||
|
Add(&c, 1);
|
||||||
|
}
|
||||||
|
void AddInt(int i, bool un=false)
|
||||||
|
{
|
||||||
|
char buf[16];
|
||||||
|
size_t len;
|
||||||
|
if (!un)
|
||||||
|
{
|
||||||
|
len = mysnprintf(buf, countof(buf), "%d", i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
len = mysnprintf(buf, countof(buf), "%uu", i);
|
||||||
|
}
|
||||||
|
Add(buf, len);
|
||||||
|
}
|
||||||
|
void AddHex(unsigned x)
|
||||||
|
{
|
||||||
|
char buf[10];
|
||||||
|
size_t len = mysnprintf(buf, countof(buf), "%08x", x);
|
||||||
|
Add(buf, len);
|
||||||
|
}
|
||||||
|
void AddFloat(double f, bool single)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
size_t len = mysnprintf(buf, countof(buf), "%.4f", f);
|
||||||
|
if (single)
|
||||||
|
{
|
||||||
|
buf[len++] = 'f';
|
||||||
|
buf[len] = '\0';
|
||||||
|
}
|
||||||
|
Add(buf, len);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
FString Str;
|
||||||
|
size_t NestDepth;
|
||||||
|
size_t Column;
|
||||||
|
size_t WrapWidth;
|
||||||
|
size_t ConsecOpens;
|
||||||
|
bool NeedSpace;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void PrintNode(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
assert(TreeNodePrinter[NUM_AST_NODE_TYPES-1] != NULL);
|
||||||
|
if (node->NodeType >= 0 && node->NodeType < NUM_AST_NODE_TYPES)
|
||||||
|
{
|
||||||
|
TreeNodePrinter[node->NodeType](out, node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.Open("unknown-node-type");
|
||||||
|
out.AddInt(node->NodeType);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintNodes(FLispString &out, ZCC_TreeNode *node, bool newlist=true, bool addbreaks=false)
|
||||||
|
{
|
||||||
|
ZCC_TreeNode *p;
|
||||||
|
|
||||||
|
if (node == NULL)
|
||||||
|
{
|
||||||
|
out.Add("nil", 3);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (newlist)
|
||||||
|
{
|
||||||
|
out.Open(NULL);
|
||||||
|
}
|
||||||
|
p = node;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (addbreaks)
|
||||||
|
{
|
||||||
|
out.Break();
|
||||||
|
}
|
||||||
|
PrintNode(out, p);
|
||||||
|
p = p->SiblingNext;
|
||||||
|
} while (p != node);
|
||||||
|
if (newlist)
|
||||||
|
{
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintBuiltInType(FLispString &out, EZCCBuiltinType type)
|
||||||
|
{
|
||||||
|
assert(ZCC_NUM_BUILT_IN_TYPES == countof(BuiltInTypeNames));
|
||||||
|
if (unsigned(type) >= unsigned(ZCC_NUM_BUILT_IN_TYPES))
|
||||||
|
{
|
||||||
|
char buf[30];
|
||||||
|
size_t len = mysnprintf(buf, countof(buf), "bad-type-%u", type);
|
||||||
|
out.Add(buf, len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.Add(BuiltInTypeNames[type]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintIdentifier(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_Identifier *inode = (ZCC_Identifier *)node;
|
||||||
|
out.Open("identifier");
|
||||||
|
out.AddName(inode->Id);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStringConst(FLispString &out, FString str)
|
||||||
|
{
|
||||||
|
FString outstr;
|
||||||
|
outstr << '"';
|
||||||
|
for (size_t i = 0; i < str.Len(); ++i)
|
||||||
|
{
|
||||||
|
if (str[i] == '"')
|
||||||
|
{
|
||||||
|
outstr << "\"";
|
||||||
|
}
|
||||||
|
else if (str[i] == '\\')
|
||||||
|
{
|
||||||
|
outstr << "\\\\";
|
||||||
|
}
|
||||||
|
else if (str[i] >= 32)
|
||||||
|
{
|
||||||
|
outstr << str[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outstr.AppendFormat("\\x%02X", str[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outstr << '"';
|
||||||
|
out.Add(outstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintClass(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_Class *cnode = (ZCC_Class *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("class");
|
||||||
|
out.AddName(cnode->NodeName);
|
||||||
|
PrintNodes(out, cnode->ParentName);
|
||||||
|
PrintNodes(out, cnode->Replaces);
|
||||||
|
out.AddHex(cnode->Flags);
|
||||||
|
PrintNodes(out, cnode->Body, false, true);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStruct(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_Struct *snode = (ZCC_Struct *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("struct");
|
||||||
|
out.AddName(snode->NodeName);
|
||||||
|
PrintNodes(out, snode->Body, false, true);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintProperty(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_Property *snode = (ZCC_Property *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("property");
|
||||||
|
out.AddName(snode->NodeName);
|
||||||
|
PrintNodes(out, snode->Body, false, true);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintFlagDef(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_FlagDef *snode = (ZCC_FlagDef *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("flagdef");
|
||||||
|
out.AddName(snode->NodeName);
|
||||||
|
out.AddName(snode->RefName);
|
||||||
|
out.AddInt(snode->BitValue);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStaticArrayState(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
auto *snode = (ZCC_StaticArrayStatement *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("static-array");
|
||||||
|
out.AddName(snode->Id);
|
||||||
|
PrintNodes(out, snode->Values, false, true);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintEnum(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_Enum *enode = (ZCC_Enum *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("enum");
|
||||||
|
out.AddName(enode->NodeName);
|
||||||
|
PrintBuiltInType(out, enode->EnumType);
|
||||||
|
out.Add(enode->Elements == NULL ? "nil" : "...", 3);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintEnumTerminator(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
out.Open("enum-term");
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStates(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_States *snode = (ZCC_States *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("states");
|
||||||
|
PrintNodes(out, snode->Flags, false, true);
|
||||||
|
PrintNodes(out, snode->Body, false, true);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStatePart(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
out.Open("state-part");
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStateLabel(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_StateLabel *snode = (ZCC_StateLabel *)node;
|
||||||
|
out.Open("state-label");
|
||||||
|
out.AddName(snode->Label);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStateStop(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
out.Open("state-stop");
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStateWait(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
out.Open("state-wait");
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStateFail(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
out.Open("state-fail");
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStateLoop(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
out.Open("state-loop");
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStateGoto(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_StateGoto *snode = (ZCC_StateGoto *)node;
|
||||||
|
out.Open("state-goto");
|
||||||
|
PrintNodes(out, snode->Qualifier);
|
||||||
|
PrintNodes(out, snode->Label);
|
||||||
|
PrintNodes(out, snode->Offset);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStateLine(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_StateLine *snode = (ZCC_StateLine *)node;
|
||||||
|
out.Open("state-line");
|
||||||
|
out.Add(*(snode->Sprite));
|
||||||
|
PrintNodes(out, snode->Duration);
|
||||||
|
if (snode->bNoDelay) out.Add("nodelay", 7);
|
||||||
|
if (snode->bBright) out.Add("bright", 6);
|
||||||
|
if (snode->bFast) out.Add("fast", 4);
|
||||||
|
if (snode->bSlow) out.Add("slow", 4);
|
||||||
|
if (snode->bCanRaise) out.Add("canraise", 8);
|
||||||
|
out.Add(*(snode->Frames));
|
||||||
|
PrintNodes(out, snode->Offset);
|
||||||
|
PrintNodes(out, snode->Action, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintVarName(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_VarName *vnode = (ZCC_VarName *)node;
|
||||||
|
out.Open("var-name");
|
||||||
|
PrintNodes(out, vnode->ArraySize);
|
||||||
|
out.AddName(vnode->Name);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintVarInit(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_VarInit *vnode = (ZCC_VarInit *)node;
|
||||||
|
out.Open("var-init");
|
||||||
|
PrintNodes(out, vnode->ArraySize);
|
||||||
|
PrintNodes(out, vnode->Init);
|
||||||
|
if (vnode->InitIsArray) out.Add("array", 5);
|
||||||
|
out.AddName(vnode->Name);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintType(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_Type *tnode = (ZCC_Type *)node;
|
||||||
|
out.Open("bad-type");
|
||||||
|
PrintNodes(out, tnode->ArraySize);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintBasicType(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_BasicType *tnode = (ZCC_BasicType *)node;
|
||||||
|
out.Open("basic-type");
|
||||||
|
PrintNodes(out, tnode->ArraySize);
|
||||||
|
PrintBuiltInType(out, tnode->Type);
|
||||||
|
if (tnode->Type == ZCC_UserType || tnode->Type == ZCC_NativeType)
|
||||||
|
{
|
||||||
|
if (tnode->Type == ZCC_NativeType) out.Add("@", 1);
|
||||||
|
PrintNodes(out, tnode->UserType, false);
|
||||||
|
}
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintMapType(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_MapType *tnode = (ZCC_MapType *)node;
|
||||||
|
out.Open("map-type");
|
||||||
|
PrintNodes(out, tnode->ArraySize);
|
||||||
|
PrintNodes(out, tnode->KeyType);
|
||||||
|
PrintNodes(out, tnode->ValueType);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintDynArrayType(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_DynArrayType *tnode = (ZCC_DynArrayType *)node;
|
||||||
|
out.Open("dyn-array-type");
|
||||||
|
PrintNodes(out, tnode->ArraySize);
|
||||||
|
PrintNodes(out, tnode->ElementType);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintClassType(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ClassType *tnode = (ZCC_ClassType *)node;
|
||||||
|
out.Open("class-type");
|
||||||
|
PrintNodes(out, tnode->ArraySize);
|
||||||
|
PrintNodes(out, tnode->Restriction);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OpenExprType(FLispString &out, EZCCExprType type)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
|
||||||
|
if (unsigned(type) < PEX_COUNT_OF)
|
||||||
|
{
|
||||||
|
mysnprintf(buf, countof(buf), "expr %d", type);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mysnprintf(buf, countof(buf), "bad-pex-%u", type);
|
||||||
|
}
|
||||||
|
out.Open(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExpression(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_Expression *enode = (ZCC_Expression *)node;
|
||||||
|
OpenExprType(out, enode->Operation);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExprID(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ExprID *enode = (ZCC_ExprID *)node;
|
||||||
|
assert(enode->Operation == PEX_ID);
|
||||||
|
out.Open("expr-id");
|
||||||
|
out.AddName(enode->Identifier);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExprTypeRef(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ExprTypeRef *enode = (ZCC_ExprTypeRef *)node;
|
||||||
|
assert(enode->Operation == PEX_TypeRef);
|
||||||
|
out.Open("expr-type-ref");
|
||||||
|
if (enode->RefType == TypeSInt8) { out.Add("sint8"); }
|
||||||
|
else if (enode->RefType == TypeUInt8) { out.Add("uint8"); }
|
||||||
|
else if (enode->RefType == TypeSInt16) { out.Add("sint16"); }
|
||||||
|
else if (enode->RefType == TypeSInt32) { out.Add("sint32"); }
|
||||||
|
else if (enode->RefType == TypeFloat32) { out.Add("float32"); }
|
||||||
|
else if (enode->RefType == TypeFloat64) { out.Add("float64"); }
|
||||||
|
else if (enode->RefType == TypeString) { out.Add("string"); }
|
||||||
|
else if (enode->RefType == TypeName) { out.Add("name"); }
|
||||||
|
else if (enode->RefType == TypeColor) { out.Add("color"); }
|
||||||
|
else if (enode->RefType == TypeSound) { out.Add("sound"); }
|
||||||
|
else { out.Add("other"); }
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExprConstant(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ExprConstant *enode = (ZCC_ExprConstant *)node;
|
||||||
|
assert(enode->Operation == PEX_ConstValue);
|
||||||
|
out.Open("expr-const");
|
||||||
|
if (enode->Type == TypeString)
|
||||||
|
{
|
||||||
|
PrintStringConst(out, *enode->StringVal);
|
||||||
|
}
|
||||||
|
else if (enode->Type == TypeFloat64)
|
||||||
|
{
|
||||||
|
out.AddFloat(enode->DoubleVal, false);
|
||||||
|
}
|
||||||
|
else if (enode->Type == TypeFloat32)
|
||||||
|
{
|
||||||
|
out.AddFloat(enode->DoubleVal, true);
|
||||||
|
}
|
||||||
|
else if (enode->Type == TypeName)
|
||||||
|
{
|
||||||
|
out.AddName(ENamedName(enode->IntVal));
|
||||||
|
}
|
||||||
|
else if (enode->Type->isIntCompatible())
|
||||||
|
{
|
||||||
|
out.AddInt(enode->IntVal, static_cast<PInt *>(enode->Type)->Unsigned);
|
||||||
|
}
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExprFuncCall(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ExprFuncCall *enode = (ZCC_ExprFuncCall *)node;
|
||||||
|
assert(enode->Operation == PEX_FuncCall);
|
||||||
|
out.Open("expr-func-call");
|
||||||
|
PrintNodes(out, enode->Function);
|
||||||
|
PrintNodes(out, enode->Parameters, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExprClassCast(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ClassCast *enode = (ZCC_ClassCast *)node;
|
||||||
|
assert(enode->Operation == PEX_ClassCast);
|
||||||
|
out.Open("expr-class-cast");
|
||||||
|
out.AddName(enode->ClassName);
|
||||||
|
PrintNodes(out, enode->Parameters, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStaticArray(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_StaticArrayStatement *enode = (ZCC_StaticArrayStatement *)node;
|
||||||
|
out.Open("static-array-stmt");
|
||||||
|
PrintNodes(out, enode->Type, false);
|
||||||
|
out.AddName(enode->Id);
|
||||||
|
PrintNodes(out, enode->Values, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExprMemberAccess(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ExprMemberAccess *enode = (ZCC_ExprMemberAccess *)node;
|
||||||
|
assert(enode->Operation == PEX_MemberAccess);
|
||||||
|
out.Open("expr-member-access");
|
||||||
|
PrintNodes(out, enode->Left);
|
||||||
|
out.AddName(enode->Right);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExprUnary(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ExprUnary *enode = (ZCC_ExprUnary *)node;
|
||||||
|
OpenExprType(out, enode->Operation);
|
||||||
|
PrintNodes(out, enode->Operand, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExprBinary(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ExprBinary *enode = (ZCC_ExprBinary *)node;
|
||||||
|
OpenExprType(out, enode->Operation);
|
||||||
|
PrintNodes(out, enode->Left);
|
||||||
|
PrintNodes(out, enode->Right);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExprTrinary(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ExprTrinary *enode = (ZCC_ExprTrinary *)node;
|
||||||
|
OpenExprType(out, enode->Operation);
|
||||||
|
PrintNodes(out, enode->Test);
|
||||||
|
PrintNodes(out, enode->Left);
|
||||||
|
PrintNodes(out, enode->Right);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintVectorInitializer(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_VectorValue *enode = (ZCC_VectorValue *)node;
|
||||||
|
OpenExprType(out, enode->Operation);
|
||||||
|
PrintNodes(out, enode->X);
|
||||||
|
PrintNodes(out, enode->Y);
|
||||||
|
PrintNodes(out, enode->Z);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintFuncParam(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_FuncParm *pnode = (ZCC_FuncParm *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("func-parm");
|
||||||
|
out.AddName(pnode->Label);
|
||||||
|
PrintNodes(out, pnode->Value, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintStatement(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
out.Open("statement");
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintCompoundStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_CompoundStmt *snode = (ZCC_CompoundStmt *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("compound-stmt");
|
||||||
|
PrintNodes(out, snode->Content, false, true);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintDefault(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_Default *snode = (ZCC_Default *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("default");
|
||||||
|
PrintNodes(out, snode->Content, false, true);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintContinueStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
out.Break();
|
||||||
|
out.Open("continue-stmt");
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintBreakStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
out.Break();
|
||||||
|
out.Open("break-stmt");
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintReturnStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ReturnStmt *snode = (ZCC_ReturnStmt *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("return-stmt");
|
||||||
|
PrintNodes(out, snode->Values, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintExpressionStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ExpressionStmt *snode = (ZCC_ExpressionStmt *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("expression-stmt");
|
||||||
|
PrintNodes(out, snode->Expression, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintIterationStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_IterationStmt *snode = (ZCC_IterationStmt *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("iteration-stmt");
|
||||||
|
out.Add((snode->CheckAt == ZCC_IterationStmt::Start) ? "start" : "end");
|
||||||
|
out.Break();
|
||||||
|
PrintNodes(out, snode->LoopCondition);
|
||||||
|
out.Break();
|
||||||
|
PrintNodes(out, snode->LoopBumper);
|
||||||
|
out.Break();
|
||||||
|
PrintNodes(out, snode->LoopStatement);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintIfStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_IfStmt *snode = (ZCC_IfStmt *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("if-stmt");
|
||||||
|
PrintNodes(out, snode->Condition);
|
||||||
|
out.Break();
|
||||||
|
PrintNodes(out, snode->TruePath);
|
||||||
|
out.Break();
|
||||||
|
PrintNodes(out, snode->FalsePath);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintSwitchStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_SwitchStmt *snode = (ZCC_SwitchStmt *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("switch-stmt");
|
||||||
|
PrintNodes(out, snode->Condition);
|
||||||
|
out.Break();
|
||||||
|
PrintNodes(out, snode->Content, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintCaseStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_CaseStmt *snode = (ZCC_CaseStmt *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("case-stmt");
|
||||||
|
PrintNodes(out, snode->Condition, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BadAssignOp(FLispString &out, int op)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
size_t len = mysnprintf(buf, countof(buf), "assign-op-%d", op);
|
||||||
|
out.Add(buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintAssignStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_AssignStmt *snode = (ZCC_AssignStmt *)node;
|
||||||
|
out.Open("assign-stmt");
|
||||||
|
PrintNodes(out, snode->Dests);
|
||||||
|
PrintNodes(out, snode->Sources);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintLocalVarStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_LocalVarStmt *snode = (ZCC_LocalVarStmt *)node;
|
||||||
|
out.Open("local-var-stmt");
|
||||||
|
PrintNodes(out, snode->Type);
|
||||||
|
PrintNodes(out, snode->Vars);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintFuncParamDecl(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_FuncParamDecl *dnode = (ZCC_FuncParamDecl *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("func-param-decl");
|
||||||
|
PrintNodes(out, dnode->Type);
|
||||||
|
out.AddName(dnode->Name);
|
||||||
|
out.AddHex(dnode->Flags);
|
||||||
|
PrintNodes(out, dnode->Default);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintConstantDef(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_ConstantDef *dnode = (ZCC_ConstantDef *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("constant-def");
|
||||||
|
out.AddName(dnode->NodeName);
|
||||||
|
PrintNodes(out, dnode->Value, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintDeclarator(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_Declarator *dnode = (ZCC_Declarator *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("declarator");
|
||||||
|
out.AddHex(dnode->Flags);
|
||||||
|
PrintNodes(out, dnode->Type);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintVarDeclarator(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_VarDeclarator *dnode = (ZCC_VarDeclarator *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("var-declarator");
|
||||||
|
out.AddHex(dnode->Flags);
|
||||||
|
PrintNodes(out, dnode->Type);
|
||||||
|
PrintNodes(out, dnode->Names);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintFuncDeclarator(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
ZCC_FuncDeclarator *dnode = (ZCC_FuncDeclarator *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("func-declarator");
|
||||||
|
out.AddHex(dnode->Flags);
|
||||||
|
PrintNodes(out, dnode->UseFlags);
|
||||||
|
PrintNodes(out, dnode->Type);
|
||||||
|
out.AddName(dnode->Name);
|
||||||
|
PrintNodes(out, dnode->Params);
|
||||||
|
PrintNodes(out, dnode->Body, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintDeclFlags(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
auto dnode = (ZCC_DeclFlags *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("decl-flags");
|
||||||
|
out.AddHex(dnode->Flags);
|
||||||
|
PrintNodes(out, dnode->Id);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintFlagStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
auto dnode = (ZCC_FlagStmt *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("flag-stmt");
|
||||||
|
PrintNodes(out, dnode->name, false);
|
||||||
|
out.AddInt(dnode->set);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintPropertyStmt(FLispString &out, ZCC_TreeNode *node)
|
||||||
|
{
|
||||||
|
auto dnode = (ZCC_PropertyStmt *)node;
|
||||||
|
out.Break();
|
||||||
|
out.Open("property-stmt");
|
||||||
|
PrintNodes(out, dnode->Prop, false);
|
||||||
|
PrintNodes(out, dnode->Values, false);
|
||||||
|
out.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *) =
|
||||||
|
{
|
||||||
|
PrintIdentifier,
|
||||||
|
PrintClass,
|
||||||
|
PrintStruct,
|
||||||
|
PrintEnum,
|
||||||
|
PrintEnumTerminator,
|
||||||
|
PrintStates,
|
||||||
|
PrintStatePart,
|
||||||
|
PrintStateLabel,
|
||||||
|
PrintStateStop,
|
||||||
|
PrintStateWait,
|
||||||
|
PrintStateFail,
|
||||||
|
PrintStateLoop,
|
||||||
|
PrintStateGoto,
|
||||||
|
PrintStateLine,
|
||||||
|
PrintVarName,
|
||||||
|
PrintVarInit,
|
||||||
|
PrintType,
|
||||||
|
PrintBasicType,
|
||||||
|
PrintMapType,
|
||||||
|
PrintDynArrayType,
|
||||||
|
PrintClassType,
|
||||||
|
PrintExpression,
|
||||||
|
PrintExprID,
|
||||||
|
PrintExprTypeRef,
|
||||||
|
PrintExprConstant,
|
||||||
|
PrintExprFuncCall,
|
||||||
|
PrintExprMemberAccess,
|
||||||
|
PrintExprUnary,
|
||||||
|
PrintExprBinary,
|
||||||
|
PrintExprTrinary,
|
||||||
|
PrintFuncParam,
|
||||||
|
PrintStatement,
|
||||||
|
PrintCompoundStmt,
|
||||||
|
PrintContinueStmt,
|
||||||
|
PrintBreakStmt,
|
||||||
|
PrintReturnStmt,
|
||||||
|
PrintExpressionStmt,
|
||||||
|
PrintIterationStmt,
|
||||||
|
PrintIfStmt,
|
||||||
|
PrintSwitchStmt,
|
||||||
|
PrintCaseStmt,
|
||||||
|
PrintAssignStmt,
|
||||||
|
PrintLocalVarStmt,
|
||||||
|
PrintFuncParamDecl,
|
||||||
|
PrintConstantDef,
|
||||||
|
PrintDeclarator,
|
||||||
|
PrintVarDeclarator,
|
||||||
|
PrintFuncDeclarator,
|
||||||
|
PrintDefault,
|
||||||
|
PrintFlagStmt,
|
||||||
|
PrintPropertyStmt,
|
||||||
|
PrintVectorInitializer,
|
||||||
|
PrintDeclFlags,
|
||||||
|
PrintExprClassCast,
|
||||||
|
PrintStaticArrayState,
|
||||||
|
PrintProperty,
|
||||||
|
PrintFlagDef,
|
||||||
|
};
|
||||||
|
|
||||||
|
FString ZCC_PrintAST(ZCC_TreeNode *root)
|
||||||
|
{
|
||||||
|
FLispString out;
|
||||||
|
PrintNodes(out, root);
|
||||||
|
return out;
|
||||||
|
}
|
2129
source/common/scripting/frontend/zcc-parse.lemon
Normal file
2129
source/common/scripting/frontend/zcc-parse.lemon
Normal file
File diff suppressed because it is too large
Load diff
3039
source/common/scripting/frontend/zcc_compile.cpp
Normal file
3039
source/common/scripting/frontend/zcc_compile.cpp
Normal file
File diff suppressed because it is too large
Load diff
173
source/common/scripting/frontend/zcc_compile.h
Normal file
173
source/common/scripting/frontend/zcc_compile.h
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
#ifndef ZCC_COMPILE_H
|
||||||
|
#define ZCC_COMPILE_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "codegen.h"
|
||||||
|
|
||||||
|
struct Baggage;
|
||||||
|
struct FPropertyInfo;
|
||||||
|
class AActor;
|
||||||
|
class FxExpression;
|
||||||
|
typedef TDeletingArray<FxExpression*> FArgumentList;
|
||||||
|
|
||||||
|
|
||||||
|
struct ZCC_StructWork
|
||||||
|
{
|
||||||
|
PSymbolTable TreeNodes;
|
||||||
|
ZCC_Struct *strct;
|
||||||
|
ZCC_Class *OuterDef;
|
||||||
|
PClass *Outer;
|
||||||
|
PSymbolTreeNode *node;
|
||||||
|
TArray<ZCC_Enum *> Enums;
|
||||||
|
TArray<ZCC_ConstantDef *> Constants;
|
||||||
|
TArray<ZCC_VarDeclarator *> Fields;
|
||||||
|
TArray<ZCC_FuncDeclarator *> Functions;
|
||||||
|
TArray<ZCC_StaticArrayStatement *> Arrays;
|
||||||
|
|
||||||
|
ZCC_StructWork()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ZCC_StructWork(ZCC_Struct * s, PSymbolTreeNode *n, ZCC_Class *outer)
|
||||||
|
{
|
||||||
|
strct = s;
|
||||||
|
node = n;
|
||||||
|
OuterDef = outer;
|
||||||
|
Outer = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
FName NodeName() const
|
||||||
|
{
|
||||||
|
return strct->NodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
PContainerType *Type()
|
||||||
|
{
|
||||||
|
return strct->Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ClassWork : public ZCC_StructWork
|
||||||
|
{
|
||||||
|
ZCC_Class *cls;
|
||||||
|
TArray<ZCC_Default *> Defaults;
|
||||||
|
TArray<ZCC_States *> States;
|
||||||
|
TArray<ZCC_Property *> Properties;
|
||||||
|
TArray<ZCC_FlagDef *> FlagDefs;
|
||||||
|
|
||||||
|
ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n)
|
||||||
|
{
|
||||||
|
strct = s;
|
||||||
|
cls = s;
|
||||||
|
node = n;
|
||||||
|
OuterDef = nullptr;
|
||||||
|
Outer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PClass *ClassType()
|
||||||
|
{
|
||||||
|
return static_cast<PClassType *>(strct->Type)->Descriptor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_MixinWork
|
||||||
|
{
|
||||||
|
PSymbolTable TreeNodes;
|
||||||
|
ZCC_MixinDef *mixin;
|
||||||
|
PSymbolTreeNode *node;
|
||||||
|
|
||||||
|
ZCC_MixinWork()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ZCC_MixinWork(ZCC_MixinDef *m, PSymbolTreeNode *n)
|
||||||
|
{
|
||||||
|
mixin = m;
|
||||||
|
node = n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_PropertyWork
|
||||||
|
{
|
||||||
|
ZCC_Property *prop;
|
||||||
|
PSymbolTable *outputtable;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ConstantWork
|
||||||
|
{
|
||||||
|
ZCC_ConstantDef *node;
|
||||||
|
PContainerType *cls;
|
||||||
|
PSymbolTable *Outputtable;
|
||||||
|
ExpVal constval;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ZCCCompiler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ZCCCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PNamespace *outnamespace, int lumpnum, const VersionInfo & ver);
|
||||||
|
~ZCCCompiler();
|
||||||
|
int Compile();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char * GetStringConst(FxExpression *ex, FCompileContext &ctx);
|
||||||
|
|
||||||
|
int IntConstFromNode(ZCC_TreeNode *node, PContainerType *cls);
|
||||||
|
FString StringConstFromNode(ZCC_TreeNode *node, PContainerType *cls);
|
||||||
|
ZCC_MixinDef *ResolveMixinStmt(ZCC_MixinStmt *mixinStmt, EZCCMixinType type);
|
||||||
|
void ProcessClass(ZCC_Class *node, PSymbolTreeNode *tnode);
|
||||||
|
void ProcessStruct(ZCC_Struct *node, PSymbolTreeNode *tnode, ZCC_Class *outer);
|
||||||
|
void ProcessMixin(ZCC_MixinDef *cnode, PSymbolTreeNode *treenode);
|
||||||
|
void CreateStructTypes();
|
||||||
|
void CreateClassTypes();
|
||||||
|
void CopyConstants(TArray<ZCC_ConstantWork> &dest, TArray<ZCC_ConstantDef*> &Constants, PContainerType *cls, PSymbolTable *ot);
|
||||||
|
void CompileAllConstants();
|
||||||
|
void AddConstant(ZCC_ConstantWork &constant);
|
||||||
|
bool CompileConstant(ZCC_ConstantWork *def);
|
||||||
|
void CompileArrays(ZCC_StructWork *work);
|
||||||
|
|
||||||
|
void CompileAllFields();
|
||||||
|
bool CompileFields(PContainerType *type, TArray<ZCC_VarDeclarator *> &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false);
|
||||||
|
FString FlagsToString(uint32_t flags);
|
||||||
|
PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember);
|
||||||
|
PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PContainerType *cls, bool *nosize);
|
||||||
|
PType *ResolveUserType(ZCC_BasicType *type, PSymbolTable *sym, bool nativetype);
|
||||||
|
|
||||||
|
void CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool forclass);
|
||||||
|
|
||||||
|
void InitFunctions();
|
||||||
|
|
||||||
|
TArray<ZCC_ConstantDef *> Constants;
|
||||||
|
TArray<ZCC_StructWork *> Structs;
|
||||||
|
TArray<ZCC_ClassWork *> Classes;
|
||||||
|
TArray<ZCC_MixinWork *> Mixins;
|
||||||
|
TArray<ZCC_PropertyWork *> Properties;
|
||||||
|
VersionInfo mVersion;
|
||||||
|
|
||||||
|
PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false);
|
||||||
|
|
||||||
|
ZCC_Expression *NodeFromSymbol(PSymbol *sym, ZCC_Expression *source, PSymbolTable *table);
|
||||||
|
ZCC_ExprConstant *NodeFromSymbolConst(PSymbolConst *sym, ZCC_Expression *idnode);
|
||||||
|
ZCC_ExprTypeRef *NodeFromSymbolType(PSymbolType *sym, ZCC_Expression *idnode);
|
||||||
|
|
||||||
|
|
||||||
|
void Warn(ZCC_TreeNode *node, const char *msg, ...) GCCPRINTF(3,4);
|
||||||
|
void Error(ZCC_TreeNode *node, const char *msg, ...) GCCPRINTF(3,4);
|
||||||
|
void MessageV(ZCC_TreeNode *node, const char *txtcolor, const char *msg, va_list argptr);
|
||||||
|
|
||||||
|
FxExpression *ConvertAST(PContainerType *cclass, ZCC_TreeNode *ast);
|
||||||
|
FxExpression *ConvertNode(ZCC_TreeNode *node, bool substitute= false);
|
||||||
|
FxExpression *ConvertImplicitScopeNode(ZCC_TreeNode *node, ZCC_Statement *nested);
|
||||||
|
FArgumentList &ConvertNodeList(FArgumentList &, ZCC_TreeNode *head);
|
||||||
|
|
||||||
|
DObject *Outer;
|
||||||
|
PContainerType *ConvertClass; // class type to be used when resoving symbols while converting an AST
|
||||||
|
PSymbolTable *GlobalTreeNodes;
|
||||||
|
PNamespace *OutNamespace;
|
||||||
|
ZCC_AST &AST;
|
||||||
|
int Lump;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ZCC_InitConversions();
|
||||||
|
|
||||||
|
#endif
|
76
source/common/scripting/frontend/zcc_exprlist.h
Normal file
76
source/common/scripting/frontend/zcc_exprlist.h
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// Name Token used in the code generator
|
||||||
|
xx(Nil, TK_None)
|
||||||
|
|
||||||
|
xx(ID, TK_Identifier)
|
||||||
|
xx(Super, TK_Super)
|
||||||
|
xx(Null, TK_Null)
|
||||||
|
xx(ConstValue, TK_Const)
|
||||||
|
xx(FuncCall, '(')
|
||||||
|
xx(ArrayAccess, TK_Array)
|
||||||
|
xx(MemberAccess, '.')
|
||||||
|
xx(ClassCast, TK_Class)
|
||||||
|
xx(TypeRef, TK_Class)
|
||||||
|
xx(Vector, TK_Vector2)
|
||||||
|
|
||||||
|
xx(PostInc, TK_Incr)
|
||||||
|
xx(PostDec, TK_Decr)
|
||||||
|
|
||||||
|
xx(PreInc, TK_Incr)
|
||||||
|
xx(PreDec, TK_Decr)
|
||||||
|
xx(Negate, '-')
|
||||||
|
xx(AntiNegate, '+')
|
||||||
|
xx(BitNot, '~')
|
||||||
|
xx(BoolNot, '!')
|
||||||
|
xx(SizeOf, TK_SizeOf)
|
||||||
|
xx(AlignOf, TK_AlignOf)
|
||||||
|
|
||||||
|
xx(Add, '+')
|
||||||
|
xx(Sub, '-')
|
||||||
|
xx(Mul, '*')
|
||||||
|
xx(Div, '/')
|
||||||
|
xx(Mod, '%')
|
||||||
|
xx(Pow, TK_MulMul)
|
||||||
|
xx(CrossProduct, TK_Cross)
|
||||||
|
xx(DotProduct, TK_Dot)
|
||||||
|
xx(LeftShift, TK_LShift)
|
||||||
|
xx(RightShift, TK_RShift)
|
||||||
|
xx(URightShift, TK_URShift)
|
||||||
|
xx(Concat, TK_DotDot)
|
||||||
|
|
||||||
|
xx(LT, '<')
|
||||||
|
xx(LTEQ, TK_Leq)
|
||||||
|
xx(GT, '>')
|
||||||
|
xx(GTEQ, TK_Geq)
|
||||||
|
xx(LTGTEQ, TK_LtGtEq)
|
||||||
|
xx(Is, TK_Is)
|
||||||
|
|
||||||
|
xx(EQEQ, TK_Eq)
|
||||||
|
xx(NEQ, TK_Neq)
|
||||||
|
xx(APREQ, TK_ApproxEq)
|
||||||
|
|
||||||
|
xx(BitAnd, '&')
|
||||||
|
xx(BitOr, '|')
|
||||||
|
xx(BitXor, '^')
|
||||||
|
xx(BoolAnd, TK_AndAnd)
|
||||||
|
xx(BoolOr, TK_OrOr)
|
||||||
|
|
||||||
|
xx(Assign, '=')
|
||||||
|
xx(AddAssign, '+') // these are what the code generator needs, not what they represent.
|
||||||
|
xx(SubAssign, '-')
|
||||||
|
xx(MulAssign, '*')
|
||||||
|
xx(DivAssign, '/')
|
||||||
|
xx(ModAssign, '%')
|
||||||
|
xx(LshAssign, TK_LShift)
|
||||||
|
xx(RshAssign, TK_RShift)
|
||||||
|
xx(URshAssign, TK_URShift)
|
||||||
|
xx(AndAssign, '&')
|
||||||
|
xx(OrAssign, '|')
|
||||||
|
xx(XorAssign, '^')
|
||||||
|
|
||||||
|
xx(Scope, TK_ColonColon)
|
||||||
|
|
||||||
|
xx(Trinary, '?')
|
||||||
|
|
||||||
|
xx(Cast, TK_Coerce)
|
||||||
|
|
||||||
|
#undef xx
|
1381
source/common/scripting/frontend/zcc_parser.cpp
Normal file
1381
source/common/scripting/frontend/zcc_parser.cpp
Normal file
File diff suppressed because it is too large
Load diff
621
source/common/scripting/frontend/zcc_parser.h
Normal file
621
source/common/scripting/frontend/zcc_parser.h
Normal file
|
@ -0,0 +1,621 @@
|
||||||
|
#ifndef ZCC_PARSER_H
|
||||||
|
#define ZCC_PARSER_H
|
||||||
|
|
||||||
|
#include "memarena.h"
|
||||||
|
#include "sc_man.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
struct ZCCToken
|
||||||
|
{
|
||||||
|
template <typename... Ts>
|
||||||
|
struct TLargest;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct TLargest<T>
|
||||||
|
{
|
||||||
|
using Type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename U, typename... Ts>
|
||||||
|
struct TLargest<T, U, Ts...>
|
||||||
|
{
|
||||||
|
using Type = typename TLargest<
|
||||||
|
typename std::conditional<
|
||||||
|
(sizeof(T) > sizeof(U)), T, U
|
||||||
|
>::type, Ts...
|
||||||
|
>::Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int Int;
|
||||||
|
double Float;
|
||||||
|
FString *String;
|
||||||
|
TLargest<decltype(Int), decltype(Float), decltype(String)>::Type Largest;
|
||||||
|
};
|
||||||
|
int SourceLoc;
|
||||||
|
|
||||||
|
ENamedName Name() { return ENamedName(Int); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Variable / Function / Class modifiers
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ZCC_Native = 1 << 0,
|
||||||
|
ZCC_Static = 1 << 1,
|
||||||
|
ZCC_Private = 1 << 2,
|
||||||
|
ZCC_Protected = 1 << 3,
|
||||||
|
ZCC_Latent = 1 << 4,
|
||||||
|
ZCC_Final = 1 << 5,
|
||||||
|
ZCC_Meta = 1 << 6,
|
||||||
|
ZCC_Action = 1 << 7,
|
||||||
|
ZCC_Deprecated = 1 << 8,
|
||||||
|
ZCC_ReadOnly = 1 << 9,
|
||||||
|
ZCC_FuncConst = 1 << 10,
|
||||||
|
ZCC_Abstract = 1 << 11,
|
||||||
|
ZCC_Extension = 1 << 12,
|
||||||
|
ZCC_Virtual = 1 << 13,
|
||||||
|
ZCC_Override = 1 << 14,
|
||||||
|
ZCC_Transient = 1 << 15,
|
||||||
|
ZCC_VarArg = 1 << 16,
|
||||||
|
ZCC_UIFlag = 1 << 17, // there's also token called ZCC_UI
|
||||||
|
ZCC_Play = 1 << 18,
|
||||||
|
ZCC_ClearScope = 1 << 19,
|
||||||
|
ZCC_VirtualScope = 1 << 20,
|
||||||
|
ZCC_Version = 1 << 21,
|
||||||
|
ZCC_Internal = 1 << 22,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function parameter modifiers
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ZCC_In = 1 << 0,
|
||||||
|
ZCC_Out = 1 << 1,
|
||||||
|
ZCC_Optional = 1 << 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Syntax tree structures.
|
||||||
|
// [pbeta] Any changes to AST node structure or new node types require TreeNodeDeepCopy in zcc_parser.cpp to be updated!
|
||||||
|
enum EZCCTreeNodeType
|
||||||
|
{
|
||||||
|
AST_Identifier,
|
||||||
|
AST_Class,
|
||||||
|
AST_Struct,
|
||||||
|
AST_Enum,
|
||||||
|
AST_EnumTerminator,
|
||||||
|
AST_States,
|
||||||
|
AST_StatePart,
|
||||||
|
AST_StateLabel,
|
||||||
|
AST_StateStop,
|
||||||
|
AST_StateWait,
|
||||||
|
AST_StateFail,
|
||||||
|
AST_StateLoop,
|
||||||
|
AST_StateGoto,
|
||||||
|
AST_StateLine,
|
||||||
|
AST_VarName,
|
||||||
|
AST_VarInit,
|
||||||
|
AST_Type,
|
||||||
|
AST_BasicType,
|
||||||
|
AST_MapType,
|
||||||
|
AST_DynArrayType,
|
||||||
|
AST_ClassType,
|
||||||
|
AST_Expression,
|
||||||
|
AST_ExprID,
|
||||||
|
AST_ExprTypeRef,
|
||||||
|
AST_ExprConstant,
|
||||||
|
AST_ExprFuncCall,
|
||||||
|
AST_ExprMemberAccess,
|
||||||
|
AST_ExprUnary,
|
||||||
|
AST_ExprBinary,
|
||||||
|
AST_ExprTrinary,
|
||||||
|
AST_FuncParm,
|
||||||
|
AST_Statement,
|
||||||
|
AST_CompoundStmt,
|
||||||
|
AST_ContinueStmt,
|
||||||
|
AST_BreakStmt,
|
||||||
|
AST_ReturnStmt,
|
||||||
|
AST_ExpressionStmt,
|
||||||
|
AST_IterationStmt,
|
||||||
|
AST_IfStmt,
|
||||||
|
AST_SwitchStmt,
|
||||||
|
AST_CaseStmt,
|
||||||
|
AST_AssignStmt,
|
||||||
|
AST_LocalVarStmt,
|
||||||
|
AST_FuncParamDecl,
|
||||||
|
AST_ConstantDef,
|
||||||
|
AST_Declarator,
|
||||||
|
AST_VarDeclarator,
|
||||||
|
AST_FuncDeclarator,
|
||||||
|
AST_Default,
|
||||||
|
AST_FlagStmt,
|
||||||
|
AST_PropertyStmt,
|
||||||
|
AST_VectorValue,
|
||||||
|
AST_DeclFlags,
|
||||||
|
AST_ClassCast,
|
||||||
|
AST_StaticArrayStatement,
|
||||||
|
AST_Property,
|
||||||
|
AST_FlagDef,
|
||||||
|
AST_MixinDef,
|
||||||
|
AST_MixinStmt,
|
||||||
|
|
||||||
|
NUM_AST_NODE_TYPES
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EZCCBuiltinType
|
||||||
|
{
|
||||||
|
ZCC_SInt8,
|
||||||
|
ZCC_UInt8,
|
||||||
|
ZCC_SInt16,
|
||||||
|
ZCC_UInt16, // smaller than 32 bit types are only valid in structs, classes and arrays.
|
||||||
|
ZCC_SInt32,
|
||||||
|
ZCC_UInt32,
|
||||||
|
ZCC_IntAuto, // for enums, autoselect appropriately sized int
|
||||||
|
|
||||||
|
ZCC_Bool,
|
||||||
|
ZCC_Float64,
|
||||||
|
ZCC_FloatAuto, // 32-bit in structs/classes, 64-bit everywhere else
|
||||||
|
ZCC_String,
|
||||||
|
ZCC_Vector2,
|
||||||
|
ZCC_Vector3,
|
||||||
|
ZCC_Name,
|
||||||
|
|
||||||
|
ZCC_Color, // special types for ZDoom.
|
||||||
|
ZCC_State,
|
||||||
|
ZCC_Sound,
|
||||||
|
|
||||||
|
ZCC_UserType,
|
||||||
|
ZCC_NativeType,
|
||||||
|
ZCC_Let,
|
||||||
|
|
||||||
|
ZCC_NUM_BUILT_IN_TYPES
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EZCCMixinType
|
||||||
|
{
|
||||||
|
ZCC_Mixin_Class,
|
||||||
|
|
||||||
|
ZCC_NUM_MIXIN_TYPES
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EZCCExprType
|
||||||
|
{
|
||||||
|
#define xx(a,z) PEX_##a,
|
||||||
|
#include "zcc_exprlist.h"
|
||||||
|
|
||||||
|
PEX_COUNT_OF
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_TreeNode
|
||||||
|
{
|
||||||
|
// This tree node's siblings are stored in a circular linked list.
|
||||||
|
// When you get back to this node, you know you've been through
|
||||||
|
// the whole list.
|
||||||
|
ZCC_TreeNode *SiblingNext;
|
||||||
|
ZCC_TreeNode *SiblingPrev;
|
||||||
|
|
||||||
|
// can't use FScriptPosition, because the string wouldn't have a chance to
|
||||||
|
// destruct if we did that.
|
||||||
|
FString *SourceName;
|
||||||
|
int SourceLump;
|
||||||
|
int SourceLoc;
|
||||||
|
|
||||||
|
// Node type is one of the node types above, which corresponds with
|
||||||
|
// one of the structures below.
|
||||||
|
EZCCTreeNodeType NodeType;
|
||||||
|
|
||||||
|
operator FScriptPosition()
|
||||||
|
{
|
||||||
|
return FScriptPosition(*SourceName, SourceLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void AppendTreeNodeSibling(ZCC_TreeNode *thisnode, ZCC_TreeNode *sibling);
|
||||||
|
|
||||||
|
struct ZCC_Identifier : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
ENamedName Id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_NamedNode : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
ENamedName NodeName;
|
||||||
|
PSymbolType *Symbol;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_Struct : ZCC_NamedNode
|
||||||
|
{
|
||||||
|
uint32_t Flags;
|
||||||
|
ZCC_TreeNode *Body;
|
||||||
|
PContainerType *Type;
|
||||||
|
VersionInfo Version;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_Property : ZCC_NamedNode
|
||||||
|
{
|
||||||
|
ZCC_TreeNode *Body;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_FlagDef : ZCC_NamedNode
|
||||||
|
{
|
||||||
|
ENamedName RefName;
|
||||||
|
int BitValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_Class : ZCC_Struct
|
||||||
|
{
|
||||||
|
ZCC_Identifier *ParentName;
|
||||||
|
ZCC_Identifier *Replaces;
|
||||||
|
|
||||||
|
PClass *CType() { return static_cast<PClassType *>(Type)->Descriptor; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_MixinDef : ZCC_NamedNode
|
||||||
|
{
|
||||||
|
ZCC_TreeNode *Body;
|
||||||
|
|
||||||
|
EZCCMixinType MixinType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_Enum : ZCC_NamedNode
|
||||||
|
{
|
||||||
|
EZCCBuiltinType EnumType;
|
||||||
|
struct ZCC_ConstantDef *Elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_EnumTerminator : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_States : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
struct ZCC_StatePart *Body;
|
||||||
|
ZCC_Identifier *Flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_StatePart : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_StateLabel : ZCC_StatePart
|
||||||
|
{
|
||||||
|
ENamedName Label;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_StateStop : ZCC_StatePart
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_StateWait : ZCC_StatePart
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_StateFail : ZCC_StatePart
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_StateLoop : ZCC_StatePart
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_Expression : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
EZCCExprType Operation;
|
||||||
|
PType *Type;
|
||||||
|
|
||||||
|
// Repurposes this node as an error node
|
||||||
|
void ToErrorNode()
|
||||||
|
{
|
||||||
|
Type = TypeError;
|
||||||
|
Operation = PEX_Nil;
|
||||||
|
NodeType = AST_Expression;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_StateGoto : ZCC_StatePart
|
||||||
|
{
|
||||||
|
ZCC_Identifier *Qualifier;
|
||||||
|
ZCC_Identifier *Label;
|
||||||
|
ZCC_Expression *Offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_StateLine : ZCC_StatePart
|
||||||
|
{
|
||||||
|
FString *Sprite;
|
||||||
|
BITFIELD bBright : 1;
|
||||||
|
BITFIELD bFast : 1;
|
||||||
|
BITFIELD bSlow : 1;
|
||||||
|
BITFIELD bNoDelay : 1;
|
||||||
|
BITFIELD bCanRaise : 1;
|
||||||
|
FString *Frames;
|
||||||
|
ZCC_Expression *Duration;
|
||||||
|
ZCC_Expression *Offset;
|
||||||
|
ZCC_ExprConstant *Lights;
|
||||||
|
ZCC_TreeNode *Action;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_VarName : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
ENamedName Name;
|
||||||
|
ZCC_Expression *ArraySize; // NULL if not an array
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_VarInit : ZCC_VarName
|
||||||
|
{
|
||||||
|
ZCC_Expression *Init;
|
||||||
|
bool InitIsArray; // this is needed to distinguish one-element arrays from raw elements.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_Type : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
ZCC_Expression *ArraySize; // NULL if not an array
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_BasicType : ZCC_Type
|
||||||
|
{
|
||||||
|
EZCCBuiltinType Type;
|
||||||
|
ZCC_Identifier *UserType;
|
||||||
|
bool isconst;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_MapType : ZCC_Type
|
||||||
|
{
|
||||||
|
ZCC_Type *KeyType;
|
||||||
|
ZCC_Type *ValueType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_DynArrayType : ZCC_Type
|
||||||
|
{
|
||||||
|
ZCC_Type *ElementType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ClassType : ZCC_Type
|
||||||
|
{
|
||||||
|
ZCC_Identifier *Restriction;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ExprID : ZCC_Expression
|
||||||
|
{
|
||||||
|
ENamedName Identifier;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ExprTypeRef : ZCC_Expression
|
||||||
|
{
|
||||||
|
PType *RefType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ExprConstant : ZCC_Expression
|
||||||
|
{
|
||||||
|
// [pbeta] The ZCC_ExprConstant case in TreeNodeDeepCopy in zcc_parser.cpp
|
||||||
|
// must be updated if this union is changed!
|
||||||
|
union
|
||||||
|
{
|
||||||
|
FString *StringVal;
|
||||||
|
int IntVal;
|
||||||
|
unsigned int UIntVal;
|
||||||
|
double DoubleVal;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_FuncParm : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
ZCC_Expression *Value;
|
||||||
|
ENamedName Label;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ExprFuncCall : ZCC_Expression
|
||||||
|
{
|
||||||
|
ZCC_Expression *Function;
|
||||||
|
ZCC_FuncParm *Parameters;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ClassCast : ZCC_Expression
|
||||||
|
{
|
||||||
|
ENamedName ClassName;
|
||||||
|
ZCC_FuncParm *Parameters;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ExprMemberAccess : ZCC_Expression
|
||||||
|
{
|
||||||
|
ZCC_Expression *Left;
|
||||||
|
ENamedName Right;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ExprUnary : ZCC_Expression
|
||||||
|
{
|
||||||
|
ZCC_Expression *Operand;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ExprBinary : ZCC_Expression
|
||||||
|
{
|
||||||
|
ZCC_Expression *Left;
|
||||||
|
ZCC_Expression *Right;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ExprTrinary : ZCC_Expression
|
||||||
|
{
|
||||||
|
ZCC_Expression *Test;
|
||||||
|
ZCC_Expression *Left;
|
||||||
|
ZCC_Expression *Right;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_VectorValue : ZCC_Expression
|
||||||
|
{
|
||||||
|
ZCC_Expression *X, *Y, *Z;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_Statement : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_StaticArrayStatement : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Type *Type;
|
||||||
|
ENamedName Id;
|
||||||
|
ZCC_Expression *Values;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_CompoundStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Statement *Content;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ContinueStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_BreakStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ReturnStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Expression *Values;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ExpressionStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Expression *Expression;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_IterationStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Expression *LoopCondition;
|
||||||
|
ZCC_Statement *LoopStatement;
|
||||||
|
ZCC_Statement *LoopBumper;
|
||||||
|
|
||||||
|
// Should the loop condition be checked at the
|
||||||
|
// start of the loop (before the LoopStatement)
|
||||||
|
// or at the end (after the LoopStatement)?
|
||||||
|
enum { Start, End } CheckAt;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_IfStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Expression *Condition;
|
||||||
|
ZCC_Statement *TruePath;
|
||||||
|
ZCC_Statement *FalsePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_SwitchStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Expression *Condition;
|
||||||
|
ZCC_Statement *Content;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_CaseStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
// A NULL Condition represents the default branch
|
||||||
|
ZCC_Expression *Condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_AssignStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Expression *Dests;
|
||||||
|
ZCC_Expression *Sources;
|
||||||
|
int AssignOp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_LocalVarStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Type *Type;
|
||||||
|
ZCC_VarInit *Vars;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_FuncParamDecl : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
ZCC_Type *Type;
|
||||||
|
ZCC_Expression *Default;
|
||||||
|
ENamedName Name;
|
||||||
|
int Flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_DeclFlags : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
ZCC_Identifier *Id;
|
||||||
|
FString *DeprecationMessage;
|
||||||
|
VersionInfo Version;
|
||||||
|
int Flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_ConstantDef : ZCC_NamedNode
|
||||||
|
{
|
||||||
|
ZCC_Expression *Value;
|
||||||
|
PSymbolConst *Symbol;
|
||||||
|
ZCC_Enum *Type; // gets set when the constant originates from an enum.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_Declarator : ZCC_TreeNode
|
||||||
|
{
|
||||||
|
ZCC_Type *Type;
|
||||||
|
int Flags;
|
||||||
|
VersionInfo Version;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A variable in a class or struct.
|
||||||
|
struct ZCC_VarDeclarator : ZCC_Declarator
|
||||||
|
{
|
||||||
|
ZCC_VarName *Names;
|
||||||
|
FString *DeprecationMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A function in a class.
|
||||||
|
struct ZCC_FuncDeclarator : ZCC_Declarator
|
||||||
|
{
|
||||||
|
ZCC_FuncParamDecl *Params;
|
||||||
|
ENamedName Name;
|
||||||
|
ZCC_Statement *Body;
|
||||||
|
ZCC_Identifier *UseFlags;
|
||||||
|
FString *DeprecationMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_Default : ZCC_CompoundStmt
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_PropertyStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Identifier *Prop;
|
||||||
|
ZCC_Expression *Values;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct ZCC_FlagStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ZCC_Identifier *name;
|
||||||
|
bool set;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCC_MixinStmt : ZCC_Statement
|
||||||
|
{
|
||||||
|
ENamedName MixinName;
|
||||||
|
};
|
||||||
|
|
||||||
|
FString ZCC_PrintAST(ZCC_TreeNode *root);
|
||||||
|
|
||||||
|
|
||||||
|
struct ZCC_AST
|
||||||
|
{
|
||||||
|
ZCC_AST() : TopNode(NULL) {}
|
||||||
|
ZCC_TreeNode *InitNode(size_t size, EZCCTreeNodeType type, ZCC_TreeNode *basis);
|
||||||
|
|
||||||
|
FSharedStringArena Strings;
|
||||||
|
FMemArena SyntaxArena;
|
||||||
|
struct ZCC_TreeNode *TopNode;
|
||||||
|
VersionInfo ParseVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ZCCParseState : public ZCC_AST
|
||||||
|
{
|
||||||
|
ZCCParseState(FScanner *scanner = nullptr) : sc(scanner) {}
|
||||||
|
ZCC_TreeNode *InitNode(size_t size, EZCCTreeNodeType type);
|
||||||
|
|
||||||
|
FScanner *sc;
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *GetMixinTypeString(EZCCMixinType type);
|
||||||
|
|
||||||
|
ZCC_TreeNode *TreeNodeDeepCopy(ZCC_AST *ast, ZCC_TreeNode *orig, bool copySiblings);
|
||||||
|
|
||||||
|
#endif
|
14
tools/lemon/CMakeLists.txt
Normal file
14
tools/lemon/CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
cmake_minimum_required( VERSION 2.8.7 )
|
||||||
|
|
||||||
|
if( NOT CMAKE_CROSSCOMPILING )
|
||||||
|
set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG" )
|
||||||
|
|
||||||
|
add_executable( lemon lemon.c )
|
||||||
|
set( CROSS_EXPORTS ${CROSS_EXPORTS} lemon PARENT_SCOPE )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Lemon wants lempar.c in its directory
|
||||||
|
add_custom_command( TARGET lemon
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND echo $<TARGET_FILE_DIR:lemon>
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/lempar.c $<TARGET_FILE_DIR:lemon> )
|
5449
tools/lemon/lemon.c
Normal file
5449
tools/lemon/lemon.c
Normal file
File diff suppressed because it is too large
Load diff
861
tools/lemon/lemon.html
Normal file
861
tools/lemon/lemon.html
Normal file
|
@ -0,0 +1,861 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>The Lemon Parser Generator</title>
|
||||||
|
</head>
|
||||||
|
<body bgcolor=white>
|
||||||
|
<h1 align=center>The Lemon Parser Generator</h1>
|
||||||
|
|
||||||
|
<p>Lemon is an LALR(1) parser generator for C or C++.
|
||||||
|
It does the same job as ``bison'' and ``yacc''.
|
||||||
|
But lemon is not another bison or yacc clone. It
|
||||||
|
uses a different grammar syntax which is designed to
|
||||||
|
reduce the number of coding errors. Lemon also uses a more
|
||||||
|
sophisticated parsing engine that is faster than yacc and
|
||||||
|
bison and which is both reentrant and thread-safe.
|
||||||
|
Furthermore, Lemon implements features that can be used
|
||||||
|
to eliminate resource leaks, making is suitable for use
|
||||||
|
in long-running programs such as graphical user interfaces
|
||||||
|
or embedded controllers.</p>
|
||||||
|
|
||||||
|
<p>This document is an introduction to the Lemon
|
||||||
|
parser generator.</p>
|
||||||
|
|
||||||
|
<h2>Theory of Operation</h2>
|
||||||
|
|
||||||
|
<p>The main goal of Lemon is to translate a context free grammar (CFG)
|
||||||
|
for a particular language into C code that implements a parser for
|
||||||
|
that language.
|
||||||
|
The program has two inputs:
|
||||||
|
<ul>
|
||||||
|
<li>The grammar specification.
|
||||||
|
<li>A parser template file.
|
||||||
|
</ul>
|
||||||
|
Typically, only the grammar specification is supplied by the programmer.
|
||||||
|
Lemon comes with a default parser template which works fine for most
|
||||||
|
applications. But the user is free to substitute a different parser
|
||||||
|
template if desired.</p>
|
||||||
|
|
||||||
|
<p>Depending on command-line options, Lemon will generate between
|
||||||
|
one and three files of outputs.
|
||||||
|
<ul>
|
||||||
|
<li>C code to implement the parser.
|
||||||
|
<li>A header file defining an integer ID for each terminal symbol.
|
||||||
|
<li>An information file that describes the states of the generated parser
|
||||||
|
automaton.
|
||||||
|
</ul>
|
||||||
|
By default, all three of these output files are generated.
|
||||||
|
The header file is suppressed if the ``-m'' command-line option is
|
||||||
|
used and the report file is omitted when ``-q'' is selected.</p>
|
||||||
|
|
||||||
|
<p>The grammar specification file uses a ``.y'' suffix, by convention.
|
||||||
|
In the examples used in this document, we'll assume the name of the
|
||||||
|
grammar file is ``gram.y''. A typical use of Lemon would be the
|
||||||
|
following command:
|
||||||
|
<pre>
|
||||||
|
lemon gram.y
|
||||||
|
</pre>
|
||||||
|
This command will generate three output files named ``gram.c'',
|
||||||
|
``gram.h'' and ``gram.out''.
|
||||||
|
The first is C code to implement the parser. The second
|
||||||
|
is the header file that defines numerical values for all
|
||||||
|
terminal symbols, and the last is the report that explains
|
||||||
|
the states used by the parser automaton.</p>
|
||||||
|
|
||||||
|
<h3>Command Line Options</h3>
|
||||||
|
|
||||||
|
<p>The behavior of Lemon can be modified using command-line options.
|
||||||
|
You can obtain a list of the available command-line options together
|
||||||
|
with a brief explanation of what each does by typing
|
||||||
|
<pre>
|
||||||
|
lemon -?
|
||||||
|
</pre>
|
||||||
|
As of this writing, the following command-line options are supported:
|
||||||
|
<ul>
|
||||||
|
<li><tt>-b</tt>
|
||||||
|
<li><tt>-c</tt>
|
||||||
|
<li><tt>-g</tt>
|
||||||
|
<li><tt>-m</tt>
|
||||||
|
<li><tt>-q</tt>
|
||||||
|
<li><tt>-s</tt>
|
||||||
|
<li><tt>-x</tt>
|
||||||
|
</ul>
|
||||||
|
The ``-b'' option reduces the amount of text in the report file by
|
||||||
|
printing only the basis of each parser state, rather than the full
|
||||||
|
configuration.
|
||||||
|
The ``-c'' option suppresses action table compression. Using -c
|
||||||
|
will make the parser a little larger and slower but it will detect
|
||||||
|
syntax errors sooner.
|
||||||
|
The ``-g'' option causes no output files to be generated at all.
|
||||||
|
Instead, the input grammar file is printed on standard output but
|
||||||
|
with all comments, actions and other extraneous text deleted. This
|
||||||
|
is a useful way to get a quick summary of a grammar.
|
||||||
|
The ``-m'' option causes the output C source file to be compatible
|
||||||
|
with the ``makeheaders'' program.
|
||||||
|
Makeheaders is a program that automatically generates header files
|
||||||
|
from C source code. When the ``-m'' option is used, the header
|
||||||
|
file is not output since the makeheaders program will take care
|
||||||
|
of generated all header files automatically.
|
||||||
|
The ``-q'' option suppresses the report file.
|
||||||
|
Using ``-s'' causes a brief summary of parser statistics to be
|
||||||
|
printed. Like this:
|
||||||
|
<pre>
|
||||||
|
Parser statistics: 74 terminals, 70 nonterminals, 179 rules
|
||||||
|
340 states, 2026 parser table entries, 0 conflicts
|
||||||
|
</pre>
|
||||||
|
Finally, the ``-x'' option causes Lemon to print its version number
|
||||||
|
and copyright information
|
||||||
|
and then stop without attempting to read the grammar or generate a parser.</p>
|
||||||
|
|
||||||
|
<h3>The Parser Interface</h3>
|
||||||
|
|
||||||
|
<p>Lemon doesn't generate a complete, working program. It only generates
|
||||||
|
a few subroutines that implement a parser. This section describes
|
||||||
|
the interface to those subroutines. It is up to the programmer to
|
||||||
|
call these subroutines in an appropriate way in order to produce a
|
||||||
|
complete system.</p>
|
||||||
|
|
||||||
|
<p>Before a program begins using a Lemon-generated parser, the program
|
||||||
|
must first create the parser.
|
||||||
|
A new parser is created as follows:
|
||||||
|
<pre>
|
||||||
|
void *pParser = ParseAlloc( malloc );
|
||||||
|
</pre>
|
||||||
|
The ParseAlloc() routine allocates and initializes a new parser and
|
||||||
|
returns a pointer to it.
|
||||||
|
The actual data structure used to represent a parser is opaque --
|
||||||
|
its internal structure is not visible or usable by the calling routine.
|
||||||
|
For this reason, the ParseAlloc() routine returns a pointer to void
|
||||||
|
rather than a pointer to some particular structure.
|
||||||
|
The sole argument to the ParseAlloc() routine is a pointer to the
|
||||||
|
subroutine used to allocate memory. Typically this means ``malloc()''.</p>
|
||||||
|
|
||||||
|
<p>After a program is finished using a parser, it can reclaim all
|
||||||
|
memory allocated by that parser by calling
|
||||||
|
<pre>
|
||||||
|
ParseFree(pParser, free);
|
||||||
|
</pre>
|
||||||
|
The first argument is the same pointer returned by ParseAlloc(). The
|
||||||
|
second argument is a pointer to the function used to release bulk
|
||||||
|
memory back to the system.</p>
|
||||||
|
|
||||||
|
<p>After a parser has been allocated using ParseAlloc(), the programmer
|
||||||
|
must supply the parser with a sequence of tokens (terminal symbols) to
|
||||||
|
be parsed. This is accomplished by calling the following function
|
||||||
|
once for each token:
|
||||||
|
<pre>
|
||||||
|
Parse(pParser, hTokenID, sTokenData, pArg);
|
||||||
|
</pre>
|
||||||
|
The first argument to the Parse() routine is the pointer returned by
|
||||||
|
ParseAlloc().
|
||||||
|
The second argument is a small positive integer that tells the parse the
|
||||||
|
type of the next token in the data stream.
|
||||||
|
There is one token type for each terminal symbol in the grammar.
|
||||||
|
The gram.h file generated by Lemon contains #define statements that
|
||||||
|
map symbolic terminal symbol names into appropriate integer values.
|
||||||
|
(A value of 0 for the second argument is a special flag to the
|
||||||
|
parser to indicate that the end of input has been reached.)
|
||||||
|
The third argument is the value of the given token. By default,
|
||||||
|
the type of the third argument is integer, but the grammar will
|
||||||
|
usually redefine this type to be some kind of structure.
|
||||||
|
Typically the second argument will be a broad category of tokens
|
||||||
|
such as ``identifier'' or ``number'' and the third argument will
|
||||||
|
be the name of the identifier or the value of the number.</p>
|
||||||
|
|
||||||
|
<p>The Parse() function may have either three or four arguments,
|
||||||
|
depending on the grammar. If the grammar specification file request
|
||||||
|
it, the Parse() function will have a fourth parameter that can be
|
||||||
|
of any type chosen by the programmer. The parser doesn't do anything
|
||||||
|
with this argument except to pass it through to action routines.
|
||||||
|
This is a convenient mechanism for passing state information down
|
||||||
|
to the action routines without having to use global variables.</p>
|
||||||
|
|
||||||
|
<p>A typical use of a Lemon parser might look something like the
|
||||||
|
following:
|
||||||
|
<pre>
|
||||||
|
01 ParseTree *ParseFile(const char *zFilename){
|
||||||
|
02 Tokenizer *pTokenizer;
|
||||||
|
03 void *pParser;
|
||||||
|
04 Token sToken;
|
||||||
|
05 int hTokenId;
|
||||||
|
06 ParserState sState;
|
||||||
|
07
|
||||||
|
08 pTokenizer = TokenizerCreate(zFilename);
|
||||||
|
09 pParser = ParseAlloc( malloc );
|
||||||
|
10 InitParserState(&sState);
|
||||||
|
11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
|
||||||
|
12 Parse(pParser, hTokenId, sToken, &sState);
|
||||||
|
13 }
|
||||||
|
14 Parse(pParser, 0, sToken, &sState);
|
||||||
|
15 ParseFree(pParser, free );
|
||||||
|
16 TokenizerFree(pTokenizer);
|
||||||
|
17 return sState.treeRoot;
|
||||||
|
18 }
|
||||||
|
</pre>
|
||||||
|
This example shows a user-written routine that parses a file of
|
||||||
|
text and returns a pointer to the parse tree.
|
||||||
|
(We've omitted all error-handling from this example to keep it
|
||||||
|
simple.)
|
||||||
|
We assume the existence of some kind of tokenizer which is created
|
||||||
|
using TokenizerCreate() on line 8 and deleted by TokenizerFree()
|
||||||
|
on line 16. The GetNextToken() function on line 11 retrieves the
|
||||||
|
next token from the input file and puts its type in the
|
||||||
|
integer variable hTokenId. The sToken variable is assumed to be
|
||||||
|
some kind of structure that contains details about each token,
|
||||||
|
such as its complete text, what line it occurs on, etc. </p>
|
||||||
|
|
||||||
|
<p>This example also assumes the existence of structure of type
|
||||||
|
ParserState that holds state information about a particular parse.
|
||||||
|
An instance of such a structure is created on line 6 and initialized
|
||||||
|
on line 10. A pointer to this structure is passed into the Parse()
|
||||||
|
routine as the optional 4th argument.
|
||||||
|
The action routine specified by the grammar for the parser can use
|
||||||
|
the ParserState structure to hold whatever information is useful and
|
||||||
|
appropriate. In the example, we note that the treeRoot field of
|
||||||
|
the ParserState structure is left pointing to the root of the parse
|
||||||
|
tree.</p>
|
||||||
|
|
||||||
|
<p>The core of this example as it relates to Lemon is as follows:
|
||||||
|
<pre>
|
||||||
|
ParseFile(){
|
||||||
|
pParser = ParseAlloc( malloc );
|
||||||
|
while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){
|
||||||
|
Parse(pParser, hTokenId, sToken);
|
||||||
|
}
|
||||||
|
Parse(pParser, 0, sToken);
|
||||||
|
ParseFree(pParser, free );
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
Basically, what a program has to do to use a Lemon-generated parser
|
||||||
|
is first create the parser, then send it lots of tokens obtained by
|
||||||
|
tokenizing an input source. When the end of input is reached, the
|
||||||
|
Parse() routine should be called one last time with a token type
|
||||||
|
of 0. This step is necessary to inform the parser that the end of
|
||||||
|
input has been reached. Finally, we reclaim memory used by the
|
||||||
|
parser by calling ParseFree().</p>
|
||||||
|
|
||||||
|
<p>There is one other interface routine that should be mentioned
|
||||||
|
before we move on.
|
||||||
|
The ParseTrace() function can be used to generate debugging output
|
||||||
|
from the parser. A prototype for this routine is as follows:
|
||||||
|
<pre>
|
||||||
|
ParseTrace(FILE *stream, char *zPrefix);
|
||||||
|
</pre>
|
||||||
|
After this routine is called, a short (one-line) message is written
|
||||||
|
to the designated output stream every time the parser changes states
|
||||||
|
or calls an action routine. Each such message is prefaced using
|
||||||
|
the text given by zPrefix. This debugging output can be turned off
|
||||||
|
by calling ParseTrace() again with a first argument of NULL (0).</p>
|
||||||
|
|
||||||
|
<h3>Differences With YACC and BISON</h3>
|
||||||
|
|
||||||
|
<p>Programmers who have previously used the yacc or bison parser
|
||||||
|
generator will notice several important differences between yacc and/or
|
||||||
|
bison and Lemon.
|
||||||
|
<ul>
|
||||||
|
<li>In yacc and bison, the parser calls the tokenizer. In Lemon,
|
||||||
|
the tokenizer calls the parser.
|
||||||
|
<li>Lemon uses no global variables. Yacc and bison use global variables
|
||||||
|
to pass information between the tokenizer and parser.
|
||||||
|
<li>Lemon allows multiple parsers to be running simultaneously. Yacc
|
||||||
|
and bison do not.
|
||||||
|
</ul>
|
||||||
|
These differences may cause some initial confusion for programmers
|
||||||
|
with prior yacc and bison experience.
|
||||||
|
But after years of experience using Lemon, I firmly
|
||||||
|
believe that the Lemon way of doing things is better.</p>
|
||||||
|
|
||||||
|
<h2>Input File Syntax</h2>
|
||||||
|
|
||||||
|
<p>The main purpose of the grammar specification file for Lemon is
|
||||||
|
to define the grammar for the parser. But the input file also
|
||||||
|
specifies additional information Lemon requires to do its job.
|
||||||
|
Most of the work in using Lemon is in writing an appropriate
|
||||||
|
grammar file.</p>
|
||||||
|
|
||||||
|
<p>The grammar file for lemon is, for the most part, free format.
|
||||||
|
It does not have sections or divisions like yacc or bison. Any
|
||||||
|
declaration can occur at any point in the file.
|
||||||
|
Lemon ignores whitespace (except where it is needed to separate
|
||||||
|
tokens) and it honors the same commenting conventions as C and C++.</p>
|
||||||
|
|
||||||
|
<h3>Terminals and Nonterminals</h3>
|
||||||
|
|
||||||
|
<p>A terminal symbol (token) is any string of alphanumeric
|
||||||
|
and underscore characters
|
||||||
|
that begins with an upper case letter.
|
||||||
|
A terminal can contain lower class letters after the first character,
|
||||||
|
but the usual convention is to make terminals all upper case.
|
||||||
|
A nonterminal, on the other hand, is any string of alphanumeric
|
||||||
|
and underscore characters than begins with a lower case letter.
|
||||||
|
Again, the usual convention is to make nonterminals use all lower
|
||||||
|
case letters.</p>
|
||||||
|
|
||||||
|
<p>In Lemon, terminal and nonterminal symbols do not need to
|
||||||
|
be declared or identified in a separate section of the grammar file.
|
||||||
|
Lemon is able to generate a list of all terminals and nonterminals
|
||||||
|
by examining the grammar rules, and it can always distinguish a
|
||||||
|
terminal from a nonterminal by checking the case of the first
|
||||||
|
character of the name.</p>
|
||||||
|
|
||||||
|
<p>Yacc and bison allow terminal symbols to have either alphanumeric
|
||||||
|
names or to be individual characters included in single quotes, like
|
||||||
|
this: ')' or '$'. Lemon does not allow this alternative form for
|
||||||
|
terminal symbols. With Lemon, all symbols, terminals and nonterminals,
|
||||||
|
must have alphanumeric names.</p>
|
||||||
|
|
||||||
|
<h3>Grammar Rules</h3>
|
||||||
|
|
||||||
|
<p>The main component of a Lemon grammar file is a sequence of grammar
|
||||||
|
rules.
|
||||||
|
Each grammar rule consists of a nonterminal symbol followed by
|
||||||
|
the special symbol ``::='' and then a list of terminals and/or nonterminals.
|
||||||
|
The rule is terminated by a period.
|
||||||
|
The list of terminals and nonterminals on the right-hand side of the
|
||||||
|
rule can be empty.
|
||||||
|
Rules can occur in any order, except that the left-hand side of the
|
||||||
|
first rule is assumed to be the start symbol for the grammar (unless
|
||||||
|
specified otherwise using the <tt>%start</tt> directive described below.)
|
||||||
|
A typical sequence of grammar rules might look something like this:
|
||||||
|
<pre>
|
||||||
|
expr ::= expr PLUS expr.
|
||||||
|
expr ::= expr TIMES expr.
|
||||||
|
expr ::= LPAREN expr RPAREN.
|
||||||
|
expr ::= VALUE.
|
||||||
|
</pre>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>There is one non-terminal in this example, ``expr'', and five
|
||||||
|
terminal symbols or tokens: ``PLUS'', ``TIMES'', ``LPAREN'',
|
||||||
|
``RPAREN'' and ``VALUE''.</p>
|
||||||
|
|
||||||
|
<p>Like yacc and bison, Lemon allows the grammar to specify a block
|
||||||
|
of C code that will be executed whenever a grammar rule is reduced
|
||||||
|
by the parser.
|
||||||
|
In Lemon, this action is specified by putting the C code (contained
|
||||||
|
within curly braces <tt>{...}</tt>) immediately after the
|
||||||
|
period that closes the rule.
|
||||||
|
For example:
|
||||||
|
<pre>
|
||||||
|
expr ::= expr PLUS expr. { printf("Doing an addition...\n"); }
|
||||||
|
</pre>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>In order to be useful, grammar actions must normally be linked to
|
||||||
|
their associated grammar rules.
|
||||||
|
In yacc and bison, this is accomplished by embedding a ``$$'' in the
|
||||||
|
action to stand for the value of the left-hand side of the rule and
|
||||||
|
symbols ``$1'', ``$2'', and so forth to stand for the value of
|
||||||
|
the terminal or nonterminal at position 1, 2 and so forth on the
|
||||||
|
right-hand side of the rule.
|
||||||
|
This idea is very powerful, but it is also very error-prone. The
|
||||||
|
single most common source of errors in a yacc or bison grammar is
|
||||||
|
to miscount the number of symbols on the right-hand side of a grammar
|
||||||
|
rule and say ``$7'' when you really mean ``$8''.</p>
|
||||||
|
|
||||||
|
<p>Lemon avoids the need to count grammar symbols by assigning symbolic
|
||||||
|
names to each symbol in a grammar rule and then using those symbolic
|
||||||
|
names in the action.
|
||||||
|
In yacc or bison, one would write this:
|
||||||
|
<pre>
|
||||||
|
expr -> expr PLUS expr { $$ = $1 + $3; };
|
||||||
|
</pre>
|
||||||
|
But in Lemon, the same rule becomes the following:
|
||||||
|
<pre>
|
||||||
|
expr(A) ::= expr(B) PLUS expr(C). { A = B+C; }
|
||||||
|
</pre>
|
||||||
|
In the Lemon rule, any symbol in parentheses after a grammar rule
|
||||||
|
symbol becomes a place holder for that symbol in the grammar rule.
|
||||||
|
This place holder can then be used in the associated C action to
|
||||||
|
stand for the value of that symbol.<p>
|
||||||
|
|
||||||
|
<p>The Lemon notation for linking a grammar rule with its reduce
|
||||||
|
action is superior to yacc/bison on several counts.
|
||||||
|
First, as mentioned above, the Lemon method avoids the need to
|
||||||
|
count grammar symbols.
|
||||||
|
Secondly, if a terminal or nonterminal in a Lemon grammar rule
|
||||||
|
includes a linking symbol in parentheses but that linking symbol
|
||||||
|
is not actually used in the reduce action, then an error message
|
||||||
|
is generated.
|
||||||
|
For example, the rule
|
||||||
|
<pre>
|
||||||
|
expr(A) ::= expr(B) PLUS expr(C). { A = B; }
|
||||||
|
</pre>
|
||||||
|
will generate an error because the linking symbol ``C'' is used
|
||||||
|
in the grammar rule but not in the reduce action.</p>
|
||||||
|
|
||||||
|
<p>The Lemon notation for linking grammar rules to reduce actions
|
||||||
|
also facilitates the use of destructors for reclaiming memory
|
||||||
|
allocated by the values of terminals and nonterminals on the
|
||||||
|
right-hand side of a rule.</p>
|
||||||
|
|
||||||
|
<h3>Precedence Rules</h3>
|
||||||
|
|
||||||
|
<p>Lemon resolves parsing ambiguities in exactly the same way as
|
||||||
|
yacc and bison. A shift-reduce conflict is resolved in favor
|
||||||
|
of the shift, and a reduce-reduce conflict is resolved by reducing
|
||||||
|
whichever rule comes first in the grammar file.</p>
|
||||||
|
|
||||||
|
<p>Just like in
|
||||||
|
yacc and bison, Lemon allows a measure of control
|
||||||
|
over the resolution of paring conflicts using precedence rules.
|
||||||
|
A precedence value can be assigned to any terminal symbol
|
||||||
|
using the %left, %right or %nonassoc directives. Terminal symbols
|
||||||
|
mentioned in earlier directives have a lower precedence that
|
||||||
|
terminal symbols mentioned in later directives. For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%left AND.
|
||||||
|
%left OR.
|
||||||
|
%nonassoc EQ NE GT GE LT LE.
|
||||||
|
%left PLUS MINUS.
|
||||||
|
%left TIMES DIVIDE MOD.
|
||||||
|
%right EXP NOT.
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>In the preceding sequence of directives, the AND operator is
|
||||||
|
defined to have the lowest precedence. The OR operator is one
|
||||||
|
precedence level higher. And so forth. Hence, the grammar would
|
||||||
|
attempt to group the ambiguous expression
|
||||||
|
<pre>
|
||||||
|
a AND b OR c
|
||||||
|
</pre>
|
||||||
|
like this
|
||||||
|
<pre>
|
||||||
|
a AND (b OR c).
|
||||||
|
</pre>
|
||||||
|
The associativity (left, right or nonassoc) is used to determine
|
||||||
|
the grouping when the precedence is the same. AND is left-associative
|
||||||
|
in our example, so
|
||||||
|
<pre>
|
||||||
|
a AND b AND c
|
||||||
|
</pre>
|
||||||
|
is parsed like this
|
||||||
|
<pre>
|
||||||
|
(a AND b) AND c.
|
||||||
|
</pre>
|
||||||
|
The EXP operator is right-associative, though, so
|
||||||
|
<pre>
|
||||||
|
a EXP b EXP c
|
||||||
|
</pre>
|
||||||
|
is parsed like this
|
||||||
|
<pre>
|
||||||
|
a EXP (b EXP c).
|
||||||
|
</pre>
|
||||||
|
The nonassoc precedence is used for non-associative operators.
|
||||||
|
So
|
||||||
|
<pre>
|
||||||
|
a EQ b EQ c
|
||||||
|
</pre>
|
||||||
|
is an error.</p>
|
||||||
|
|
||||||
|
<p>The precedence of non-terminals is transferred to rules as follows:
|
||||||
|
The precedence of a grammar rule is equal to the precedence of the
|
||||||
|
left-most terminal symbol in the rule for which a precedence is
|
||||||
|
defined. This is normally what you want, but in those cases where
|
||||||
|
you want to precedence of a grammar rule to be something different,
|
||||||
|
you can specify an alternative precedence symbol by putting the
|
||||||
|
symbol in square braces after the period at the end of the rule and
|
||||||
|
before any C-code. For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
expr = MINUS expr. [NOT]
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>This rule has a precedence equal to that of the NOT symbol, not the
|
||||||
|
MINUS symbol as would have been the case by default.</p>
|
||||||
|
|
||||||
|
<p>With the knowledge of how precedence is assigned to terminal
|
||||||
|
symbols and individual
|
||||||
|
grammar rules, we can now explain precisely how parsing conflicts
|
||||||
|
are resolved in Lemon. Shift-reduce conflicts are resolved
|
||||||
|
as follows:
|
||||||
|
<ul>
|
||||||
|
<li> If either the token to be shifted or the rule to be reduced
|
||||||
|
lacks precedence information, then resolve in favor of the
|
||||||
|
shift, but report a parsing conflict.
|
||||||
|
<li> If the precedence of the token to be shifted is greater than
|
||||||
|
the precedence of the rule to reduce, then resolve in favor
|
||||||
|
of the shift. No parsing conflict is reported.
|
||||||
|
<li> If the precedence of the token it be shifted is less than the
|
||||||
|
precedence of the rule to reduce, then resolve in favor of the
|
||||||
|
reduce action. No parsing conflict is reported.
|
||||||
|
<li> If the precedences are the same and the shift token is
|
||||||
|
right-associative, then resolve in favor of the shift.
|
||||||
|
No parsing conflict is reported.
|
||||||
|
<li> If the precedences are the same the the shift token is
|
||||||
|
left-associative, then resolve in favor of the reduce.
|
||||||
|
No parsing conflict is reported.
|
||||||
|
<li> Otherwise, resolve the conflict by doing the shift and
|
||||||
|
report the parsing conflict.
|
||||||
|
</ul>
|
||||||
|
Reduce-reduce conflicts are resolved this way:
|
||||||
|
<ul>
|
||||||
|
<li> If either reduce rule
|
||||||
|
lacks precedence information, then resolve in favor of the
|
||||||
|
rule that appears first in the grammar and report a parsing
|
||||||
|
conflict.
|
||||||
|
<li> If both rules have precedence and the precedence is different
|
||||||
|
then resolve the dispute in favor of the rule with the highest
|
||||||
|
precedence and do not report a conflict.
|
||||||
|
<li> Otherwise, resolve the conflict by reducing by the rule that
|
||||||
|
appears first in the grammar and report a parsing conflict.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Special Directives</h3>
|
||||||
|
|
||||||
|
<p>The input grammar to Lemon consists of grammar rules and special
|
||||||
|
directives. We've described all the grammar rules, so now we'll
|
||||||
|
talk about the special directives.</p>
|
||||||
|
|
||||||
|
<p>Directives in lemon can occur in any order. You can put them before
|
||||||
|
the grammar rules, or after the grammar rules, or in the mist of the
|
||||||
|
grammar rules. It doesn't matter. The relative order of
|
||||||
|
directives used to assign precedence to terminals is important, but
|
||||||
|
other than that, the order of directives in Lemon is arbitrary.</p>
|
||||||
|
|
||||||
|
<p>Lemon supports the following special directives:
|
||||||
|
<ul>
|
||||||
|
<li><tt>%destructor</tt>
|
||||||
|
<li><tt>%extra_argument</tt>
|
||||||
|
<li><tt>%include</tt>
|
||||||
|
<li><tt>%left</tt>
|
||||||
|
<li><tt>%name</tt>
|
||||||
|
<li><tt>%nonassoc</tt>
|
||||||
|
<li><tt>%parse_accept</tt>
|
||||||
|
<li><tt>%parse_failure </tt>
|
||||||
|
<li><tt>%right</tt>
|
||||||
|
<li><tt>%stack_overflow</tt>
|
||||||
|
<li><tt>%stack_size</tt>
|
||||||
|
<li><tt>%start_symbol</tt>
|
||||||
|
<li><tt>%syntax_error</tt>
|
||||||
|
<li><tt>%token_destructor</tt>
|
||||||
|
<li><tt>%token_prefix</tt>
|
||||||
|
<li><tt>%token_type</tt>
|
||||||
|
<li><tt>%type</tt>
|
||||||
|
</ul>
|
||||||
|
Each of these directives will be described separately in the
|
||||||
|
following sections:</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%destructor</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %destructor directive is used to specify a destructor for
|
||||||
|
a non-terminal symbol.
|
||||||
|
(See also the %token_destructor directive which is used to
|
||||||
|
specify a destructor for terminal symbols.)</p>
|
||||||
|
|
||||||
|
<p>A non-terminal's destructor is called to dispose of the
|
||||||
|
non-terminal's value whenever the non-terminal is popped from
|
||||||
|
the stack. This includes all of the following circumstances:
|
||||||
|
<ul>
|
||||||
|
<li> When a rule reduces and the value of a non-terminal on
|
||||||
|
the right-hand side is not linked to C code.
|
||||||
|
<li> When the stack is popped during error processing.
|
||||||
|
<li> When the ParseFree() function runs.
|
||||||
|
</ul>
|
||||||
|
The destructor can do whatever it wants with the value of
|
||||||
|
the non-terminal, but its design is to deallocate memory
|
||||||
|
or other resources held by that non-terminal.</p>
|
||||||
|
|
||||||
|
<p>Consider an example:
|
||||||
|
<pre>
|
||||||
|
%type nt {void*}
|
||||||
|
%destructor nt { free($$); }
|
||||||
|
nt(A) ::= ID NUM. { A = malloc( 100 ); }
|
||||||
|
</pre>
|
||||||
|
This example is a bit contrived but it serves to illustrate how
|
||||||
|
destructors work. The example shows a non-terminal named
|
||||||
|
``nt'' that holds values of type ``void*''. When the rule for
|
||||||
|
an ``nt'' reduces, it sets the value of the non-terminal to
|
||||||
|
space obtained from malloc(). Later, when the nt non-terminal
|
||||||
|
is popped from the stack, the destructor will fire and call
|
||||||
|
free() on this malloced space, thus avoiding a memory leak.
|
||||||
|
(Note that the symbol ``$$'' in the destructor code is replaced
|
||||||
|
by the value of the non-terminal.)</p>
|
||||||
|
|
||||||
|
<p>It is important to note that the value of a non-terminal is passed
|
||||||
|
to the destructor whenever the non-terminal is removed from the
|
||||||
|
stack, unless the non-terminal is used in a C-code action. If
|
||||||
|
the non-terminal is used by C-code, then it is assumed that the
|
||||||
|
C-code will take care of destroying it if it should really
|
||||||
|
be destroyed. More commonly, the value is used to build some
|
||||||
|
larger structure and we don't want to destroy it, which is why
|
||||||
|
the destructor is not called in this circumstance.</p>
|
||||||
|
|
||||||
|
<p>By appropriate use of destructors, it is possible to
|
||||||
|
build a parser using Lemon that can be used within a long-running
|
||||||
|
program, such as a GUI, that will not leak memory or other resources.
|
||||||
|
To do the same using yacc or bison is much more difficult.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%extra_argument</tt> directive</h4>
|
||||||
|
|
||||||
|
The %extra_argument directive instructs Lemon to add a 4th parameter
|
||||||
|
to the parameter list of the Parse() function it generates. Lemon
|
||||||
|
doesn't do anything itself with this extra argument, but it does
|
||||||
|
make the argument available to C-code action routines, destructors,
|
||||||
|
and so forth. For example, if the grammar file contains:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%extra_argument { MyStruct *pAbc }
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>Then the Parse() function generated will have an 4th parameter
|
||||||
|
of type ``MyStruct*'' and all action routines will have access to
|
||||||
|
a variable named ``pAbc'' that is the value of the 4th parameter
|
||||||
|
in the most recent call to Parse().</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%include</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %include directive specifies C code that is included at the
|
||||||
|
top of the generated parser. You can include any text you want --
|
||||||
|
the Lemon parser generator copies to blindly. If you have multiple
|
||||||
|
%include directives in your grammar file, their values are concatenated
|
||||||
|
before being put at the beginning of the generated parser.</p>
|
||||||
|
|
||||||
|
<p>The %include directive is very handy for getting some extra #include
|
||||||
|
preprocessor statements at the beginning of the generated parser.
|
||||||
|
For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%include {#include <unistd.h>}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>This might be needed, for example, if some of the C actions in the
|
||||||
|
grammar call functions that are prototyed in unistd.h.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%left</tt> directive</h4>
|
||||||
|
|
||||||
|
The %left directive is used (along with the %right and
|
||||||
|
%nonassoc directives) to declare precedences of terminal
|
||||||
|
symbols. Every terminal symbol whose name appears after
|
||||||
|
a %left directive but before the next period (``.'') is
|
||||||
|
given the same left-associative precedence value. Subsequent
|
||||||
|
%left directives have higher precedence. For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%left AND.
|
||||||
|
%left OR.
|
||||||
|
%nonassoc EQ NE GT GE LT LE.
|
||||||
|
%left PLUS MINUS.
|
||||||
|
%left TIMES DIVIDE MOD.
|
||||||
|
%right EXP NOT.
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>Note the period that terminates each %left, %right or %nonassoc
|
||||||
|
directive.</p>
|
||||||
|
|
||||||
|
<p>LALR(1) grammars can get into a situation where they require
|
||||||
|
a large amount of stack space if you make heavy use or right-associative
|
||||||
|
operators. For this reason, it is recommended that you use %left
|
||||||
|
rather than %right whenever possible.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%name</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>By default, the functions generated by Lemon all begin with the
|
||||||
|
five-character string ``Parse''. You can change this string to something
|
||||||
|
different using the %name directive. For instance:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%name Abcde
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>Putting this directive in the grammar file will cause Lemon to generate
|
||||||
|
functions named
|
||||||
|
<ul>
|
||||||
|
<li> AbcdeAlloc(),
|
||||||
|
<li> AbcdeFree(),
|
||||||
|
<li> AbcdeTrace(), and
|
||||||
|
<li> Abcde().
|
||||||
|
</ul>
|
||||||
|
The %name directive allows you to generator two or more different
|
||||||
|
parsers and link them all into the same executable.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%nonassoc</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>This directive is used to assign non-associative precedence to
|
||||||
|
one or more terminal symbols. See the section on precedence rules
|
||||||
|
or on the %left directive for additional information.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%parse_accept</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %parse_accept directive specifies a block of C code that is
|
||||||
|
executed whenever the parser accepts its input string. To ``accept''
|
||||||
|
an input string means that the parser was able to process all tokens
|
||||||
|
without error.</p>
|
||||||
|
|
||||||
|
<p>For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%parse_accept {
|
||||||
|
printf("parsing complete!\n");
|
||||||
|
}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h4>The <tt>%parse_failure</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %parse_failure directive specifies a block of C code that
|
||||||
|
is executed whenever the parser fails complete. This code is not
|
||||||
|
executed until the parser has tried and failed to resolve an input
|
||||||
|
error using is usual error recovery strategy. The routine is
|
||||||
|
only invoked when parsing is unable to continue.</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%parse_failure {
|
||||||
|
fprintf(stderr,"Giving up. Parser is hopelessly lost...\n");
|
||||||
|
}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<h4>The <tt>%right</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>This directive is used to assign right-associative precedence to
|
||||||
|
one or more terminal symbols. See the section on precedence rules
|
||||||
|
or on the %left directive for additional information.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%stack_overflow</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %stack_overflow directive specifies a block of C code that
|
||||||
|
is executed if the parser's internal stack ever overflows. Typically
|
||||||
|
this just prints an error message. After a stack overflow, the parser
|
||||||
|
will be unable to continue and must be reset.</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%stack_overflow {
|
||||||
|
fprintf(stderr,"Giving up. Parser stack overflow\n");
|
||||||
|
}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>You can help prevent parser stack overflows by avoiding the use
|
||||||
|
of right recursion and right-precedence operators in your grammar.
|
||||||
|
Use left recursion and and left-precedence operators instead, to
|
||||||
|
encourage rules to reduce sooner and keep the stack size down.
|
||||||
|
For example, do rules like this:
|
||||||
|
<pre>
|
||||||
|
list ::= list element. // left-recursion. Good!
|
||||||
|
list ::= .
|
||||||
|
</pre>
|
||||||
|
Not like this:
|
||||||
|
<pre>
|
||||||
|
list ::= element list. // right-recursion. Bad!
|
||||||
|
list ::= .
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h4>The <tt>%stack_size</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>If stack overflow is a problem and you can't resolve the trouble
|
||||||
|
by using left-recursion, then you might want to increase the size
|
||||||
|
of the parser's stack using this directive. Put an positive integer
|
||||||
|
after the %stack_size directive and Lemon will generate a parse
|
||||||
|
with a stack of the requested size. The default value is 100.</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%stack_size 2000
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<h4>The <tt>%start_symbol</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>By default, the start-symbol for the grammar that Lemon generates
|
||||||
|
is the first non-terminal that appears in the grammar file. But you
|
||||||
|
can choose a different start-symbol using the %start_symbol directive.</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%start_symbol prog
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<h4>The <tt>%token_destructor</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>The %destructor directive assigns a destructor to a non-terminal
|
||||||
|
symbol. (See the description of the %destructor directive above.)
|
||||||
|
This directive does the same thing for all terminal symbols.</p>
|
||||||
|
|
||||||
|
<p>Unlike non-terminal symbols which may each have a different data type
|
||||||
|
for their values, terminals all use the same data type (defined by
|
||||||
|
the %token_type directive) and so they use a common destructor. Other
|
||||||
|
than that, the token destructor works just like the non-terminal
|
||||||
|
destructors.</p>
|
||||||
|
|
||||||
|
<h4>The <tt>%token_prefix</tt> directive</h4>
|
||||||
|
|
||||||
|
<p>Lemon generates #defines that assign small integer constants
|
||||||
|
to each terminal symbol in the grammar. If desired, Lemon will
|
||||||
|
add a prefix specified by this directive
|
||||||
|
to each of the #defines it generates.
|
||||||
|
So if the default output of Lemon looked like this:
|
||||||
|
<pre>
|
||||||
|
#define AND 1
|
||||||
|
#define MINUS 2
|
||||||
|
#define OR 3
|
||||||
|
#define PLUS 4
|
||||||
|
</pre>
|
||||||
|
You can insert a statement into the grammar like this:
|
||||||
|
<pre>
|
||||||
|
%token_prefix TOKEN_
|
||||||
|
</pre>
|
||||||
|
to cause Lemon to produce these symbols instead:
|
||||||
|
<pre>
|
||||||
|
#define TOKEN_AND 1
|
||||||
|
#define TOKEN_MINUS 2
|
||||||
|
#define TOKEN_OR 3
|
||||||
|
#define TOKEN_PLUS 4
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h4>The <tt>%token_type</tt> and <tt>%type</tt> directives</h4>
|
||||||
|
|
||||||
|
<p>These directives are used to specify the data types for values
|
||||||
|
on the parser's stack associated with terminal and non-terminal
|
||||||
|
symbols. The values of all terminal symbols must be of the same
|
||||||
|
type. This turns out to be the same data type as the 3rd parameter
|
||||||
|
to the Parse() function generated by Lemon. Typically, you will
|
||||||
|
make the value of a terminal symbol by a pointer to some kind of
|
||||||
|
token structure. Like this:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%token_type {Token*}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>If the data type of terminals is not specified, the default value
|
||||||
|
is ``int''.</p>
|
||||||
|
|
||||||
|
<p>Non-terminal symbols can each have their own data types. Typically
|
||||||
|
the data type of a non-terminal is a pointer to the root of a parse-tree
|
||||||
|
structure that contains all information about that non-terminal.
|
||||||
|
For example:</p>
|
||||||
|
|
||||||
|
<p><pre>
|
||||||
|
%type expr {Expr*}
|
||||||
|
</pre></p>
|
||||||
|
|
||||||
|
<p>Each entry on the parser's stack is actually a union containing
|
||||||
|
instances of all data types for every non-terminal and terminal symbol.
|
||||||
|
Lemon will automatically use the correct element of this union depending
|
||||||
|
on what the corresponding non-terminal or terminal symbol is. But
|
||||||
|
the grammar designer should keep in mind that the size of the union
|
||||||
|
will be the size of its largest element. So if you have a single
|
||||||
|
non-terminal whose data type requires 1K of storage, then your 100
|
||||||
|
entry parser stack will require 100K of heap space. If you are willing
|
||||||
|
and able to pay that price, fine. You just need to know.</p>
|
||||||
|
|
||||||
|
<h3>Error Processing</h3>
|
||||||
|
|
||||||
|
<p>After extensive experimentation over several years, it has been
|
||||||
|
discovered that the error recovery strategy used by yacc is about
|
||||||
|
as good as it gets. And so that is what Lemon uses.</p>
|
||||||
|
|
||||||
|
<p>When a Lemon-generated parser encounters a syntax error, it
|
||||||
|
first invokes the code specified by the %syntax_error directive, if
|
||||||
|
any. It then enters its error recovery strategy. The error recovery
|
||||||
|
strategy is to begin popping the parsers stack until it enters a
|
||||||
|
state where it is permitted to shift a special non-terminal symbol
|
||||||
|
named ``error''. It then shifts this non-terminal and continues
|
||||||
|
parsing. But the %syntax_error routine will not be called again
|
||||||
|
until at least three new tokens have been successfully shifted.</p>
|
||||||
|
|
||||||
|
<p>If the parser pops its stack until the stack is empty, and it still
|
||||||
|
is unable to shift the error symbol, then the %parse_failed routine
|
||||||
|
is invoked and the parser resets itself to its start state, ready
|
||||||
|
to begin parsing a new file. This is what will happen at the very
|
||||||
|
first syntax error, of course, if there are no instances of the
|
||||||
|
``error'' non-terminal in your grammar.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
972
tools/lemon/lempar.c
Normal file
972
tools/lemon/lempar.c
Normal file
|
@ -0,0 +1,972 @@
|
||||||
|
/*
|
||||||
|
** 2000-05-29
|
||||||
|
**
|
||||||
|
** The author disclaims copyright to this source code. In place of
|
||||||
|
** a legal notice, here is a blessing:
|
||||||
|
**
|
||||||
|
** May you do good and not evil.
|
||||||
|
** May you find forgiveness for yourself and forgive others.
|
||||||
|
** May you share freely, never taking more than you give.
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
** Driver template for the LEMON parser generator.
|
||||||
|
**
|
||||||
|
** The "lemon" program processes an LALR(1) input grammar file, then uses
|
||||||
|
** this template to construct a parser. The "lemon" program inserts text
|
||||||
|
** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
|
||||||
|
** interstitial "-" characters) contained in this template is changed into
|
||||||
|
** the value of the %name directive from the grammar. Otherwise, the content
|
||||||
|
** of this template is copied straight through into the generate parser
|
||||||
|
** source file.
|
||||||
|
**
|
||||||
|
** The following is the concatenation of all %include directives from the
|
||||||
|
** input grammar file:
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define CDECL __cdecl
|
||||||
|
#else
|
||||||
|
#define CDECL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/************ Begin %include sections from the grammar ************************/
|
||||||
|
%%
|
||||||
|
/**************** End of %include directives **********************************/
|
||||||
|
/* These constants specify the various numeric values for terminal symbols
|
||||||
|
** in a format understandable to "makeheaders". This section is blank unless
|
||||||
|
** "lemon" is run with the "-m" command-line option.
|
||||||
|
***************** Begin makeheaders token definitions *************************/
|
||||||
|
%%
|
||||||
|
/**************** End makeheaders token definitions ***************************/
|
||||||
|
|
||||||
|
/* The next section is a series of control #defines.
|
||||||
|
** various aspects of the generated parser.
|
||||||
|
** YYCODETYPE is the data type used to store the integer codes
|
||||||
|
** that represent terminal and non-terminal symbols.
|
||||||
|
** "unsigned char" is used if there are fewer than
|
||||||
|
** 256 symbols. Larger types otherwise.
|
||||||
|
** YYNOCODE is a number of type YYCODETYPE that is not used for
|
||||||
|
** any terminal or nonterminal symbol.
|
||||||
|
** YYFALLBACK If defined, this indicates that one or more tokens
|
||||||
|
** (also known as: "terminal symbols") have fall-back
|
||||||
|
** values which should be used if the original symbol
|
||||||
|
** would not parse. This permits keywords to sometimes
|
||||||
|
** be used as identifiers, for example.
|
||||||
|
** YYACTIONTYPE is the data type used for "action codes" - numbers
|
||||||
|
** that indicate what to do in response to the next
|
||||||
|
** token.
|
||||||
|
** ParseTOKENTYPE is the data type used for minor type for terminal
|
||||||
|
** symbols. Background: A "minor type" is a semantic
|
||||||
|
** value associated with a terminal or non-terminal
|
||||||
|
** symbols. For example, for an "ID" terminal symbol,
|
||||||
|
** the minor type might be the name of the identifier.
|
||||||
|
** Each non-terminal can have a different minor type.
|
||||||
|
** Terminal symbols all have the same minor type, though.
|
||||||
|
** This macros defines the minor type for terminal
|
||||||
|
** symbols.
|
||||||
|
** YYMINORTYPE is the data type used for all minor types.
|
||||||
|
** This is typically a union of many types, one of
|
||||||
|
** which is ParseTOKENTYPE. The entry in the union
|
||||||
|
** for terminal symbols is called "yy0".
|
||||||
|
** YYSTACKDEPTH is the maximum depth of the parser's stack. If
|
||||||
|
** zero the stack is dynamically sized using realloc()
|
||||||
|
** ParseARG_SDECL A static variable declaration for the %extra_argument
|
||||||
|
** ParseARG_PDECL A parameter declaration for the %extra_argument
|
||||||
|
** ParseARG_STORE Code to store %extra_argument into yypParser
|
||||||
|
** ParseARG_FETCH Code to extract %extra_argument from yypParser
|
||||||
|
** YYERRORSYMBOL is the code number of the error symbol. If not
|
||||||
|
** defined, then do no error processing.
|
||||||
|
** YYNSTATE the combined number of states.
|
||||||
|
** YYNRULE the number of rules in the grammar
|
||||||
|
** YY_MAX_SHIFT Maximum value for shift actions
|
||||||
|
** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
|
||||||
|
** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
|
||||||
|
** YY_MIN_REDUCE Maximum value for reduce actions
|
||||||
|
** YY_ERROR_ACTION The yy_action[] code for syntax error
|
||||||
|
** YY_ACCEPT_ACTION The yy_action[] code for accept
|
||||||
|
** YY_NO_ACTION The yy_action[] code for no-op
|
||||||
|
*/
|
||||||
|
#ifndef INTERFACE
|
||||||
|
# define INTERFACE 1
|
||||||
|
#endif
|
||||||
|
/************* Begin control #defines *****************************************/
|
||||||
|
%%
|
||||||
|
/************* End control #defines *******************************************/
|
||||||
|
|
||||||
|
/* Define the yytestcase() macro to be a no-op if is not already defined
|
||||||
|
** otherwise.
|
||||||
|
**
|
||||||
|
** Applications can choose to define yytestcase() in the %include section
|
||||||
|
** to a macro that can assist in verifying code coverage. For production
|
||||||
|
** code the yytestcase() macro should be turned off. But it is useful
|
||||||
|
** for testing.
|
||||||
|
*/
|
||||||
|
#ifndef yytestcase
|
||||||
|
# define yytestcase(X)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Next are the tables used to determine what action to take based on the
|
||||||
|
** current state and lookahead token. These tables are used to implement
|
||||||
|
** functions that take a state number and lookahead value and return an
|
||||||
|
** action integer.
|
||||||
|
**
|
||||||
|
** Suppose the action integer is N. Then the action is determined as
|
||||||
|
** follows
|
||||||
|
**
|
||||||
|
** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead
|
||||||
|
** token onto the stack and goto state N.
|
||||||
|
**
|
||||||
|
** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
|
||||||
|
** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
|
||||||
|
**
|
||||||
|
** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
|
||||||
|
** and YY_MAX_REDUCE
|
||||||
|
**
|
||||||
|
** N == YY_ERROR_ACTION A syntax error has occurred.
|
||||||
|
**
|
||||||
|
** N == YY_ACCEPT_ACTION The parser accepts its input.
|
||||||
|
**
|
||||||
|
** N == YY_NO_ACTION No such action. Denotes unused
|
||||||
|
** slots in the yy_action[] table.
|
||||||
|
**
|
||||||
|
** The action table is constructed as a single large table named yy_action[].
|
||||||
|
** Given state S and lookahead X, the action is computed as either:
|
||||||
|
**
|
||||||
|
** (A) N = yy_action[ yy_shift_ofst[S] + X ]
|
||||||
|
** (B) N = yy_default[S]
|
||||||
|
**
|
||||||
|
** The (A) formula is preferred. The B formula is used instead if:
|
||||||
|
** (1) The yy_shift_ofst[S]+X value is out of range, or
|
||||||
|
** (2) yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or
|
||||||
|
** (3) yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT.
|
||||||
|
** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that
|
||||||
|
** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
|
||||||
|
** Hence only tests (1) and (2) need to be evaluated.)
|
||||||
|
**
|
||||||
|
** The formulas above are for computing the action when the lookahead is
|
||||||
|
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
|
||||||
|
** a reduce action) then the yy_reduce_ofst[] array is used in place of
|
||||||
|
** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
|
||||||
|
** YY_SHIFT_USE_DFLT.
|
||||||
|
**
|
||||||
|
** The following are the tables generated in this section:
|
||||||
|
**
|
||||||
|
** yy_action[] A single table containing all actions.
|
||||||
|
** yy_lookahead[] A table containing the lookahead for each entry in
|
||||||
|
** yy_action. Used to detect hash collisions.
|
||||||
|
** yy_shift_ofst[] For each state, the offset into yy_action for
|
||||||
|
** shifting terminals.
|
||||||
|
** yy_reduce_ofst[] For each state, the offset into yy_action for
|
||||||
|
** shifting non-terminals after a reduce.
|
||||||
|
** yy_default[] Default action for each state.
|
||||||
|
**
|
||||||
|
*********** Begin parsing tables **********************************************/
|
||||||
|
%%
|
||||||
|
/********** End of lemon-generated parsing tables *****************************/
|
||||||
|
|
||||||
|
/* The next table maps tokens (terminal symbols) into fallback tokens.
|
||||||
|
** If a construct like the following:
|
||||||
|
**
|
||||||
|
** %fallback ID X Y Z.
|
||||||
|
**
|
||||||
|
** appears in the grammar, then ID becomes a fallback token for X, Y,
|
||||||
|
** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
|
||||||
|
** but it does not parse, the type of the token is changed to ID and
|
||||||
|
** the parse is retried before an error is thrown.
|
||||||
|
**
|
||||||
|
** This feature can be used, for example, to cause some keywords in a language
|
||||||
|
** to revert to identifiers if they keyword does not apply in the context where
|
||||||
|
** it appears.
|
||||||
|
*/
|
||||||
|
#ifdef YYFALLBACK
|
||||||
|
static const YYCODETYPE yyFallback[] = {
|
||||||
|
%%
|
||||||
|
};
|
||||||
|
#endif /* YYFALLBACK */
|
||||||
|
|
||||||
|
/* The following structure represents a single element of the
|
||||||
|
** parser's stack. Information stored includes:
|
||||||
|
**
|
||||||
|
** + The state number for the parser at this level of the stack.
|
||||||
|
**
|
||||||
|
** + The value of the token stored at this level of the stack.
|
||||||
|
** (In other words, the "major" token.)
|
||||||
|
**
|
||||||
|
** + The semantic value stored at this level of the stack. This is
|
||||||
|
** the information used by the action routines in the grammar.
|
||||||
|
** It is sometimes called the "minor" token.
|
||||||
|
**
|
||||||
|
** After the "shift" half of a SHIFTREDUCE action, the stateno field
|
||||||
|
** actually contains the reduce action for the second half of the
|
||||||
|
** SHIFTREDUCE.
|
||||||
|
*/
|
||||||
|
struct yyStackEntry {
|
||||||
|
YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */
|
||||||
|
YYCODETYPE major; /* The major token value. This is the code
|
||||||
|
** number for the token at this stack level */
|
||||||
|
YYMINORTYPE minor; /* The user-supplied minor token value. This
|
||||||
|
** is the value of the token */
|
||||||
|
};
|
||||||
|
typedef struct yyStackEntry yyStackEntry;
|
||||||
|
|
||||||
|
/* The state of the parser is completely contained in an instance of
|
||||||
|
** the following structure */
|
||||||
|
struct yyParser {
|
||||||
|
yyStackEntry *yytos; /* Pointer to top element of the stack */
|
||||||
|
#ifdef YYTRACKMAXSTACKDEPTH
|
||||||
|
int yyhwm; /* High-water mark of the stack */
|
||||||
|
#endif
|
||||||
|
#ifndef YYNOERRORRECOVERY
|
||||||
|
int yyerrcnt; /* Shifts left before out of the error */
|
||||||
|
#endif
|
||||||
|
ParseARG_SDECL /* A place to hold %extra_argument */
|
||||||
|
#if YYSTACKDEPTH<=0
|
||||||
|
int yystksz; /* Current side of the stack */
|
||||||
|
yyStackEntry *yystack; /* The parser's stack */
|
||||||
|
yyStackEntry yystk0; /* First stack entry */
|
||||||
|
#else
|
||||||
|
yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
typedef struct yyParser yyParser;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#include <stdio.h>
|
||||||
|
static FILE *yyTraceFILE = 0;
|
||||||
|
static char *yyTracePrompt = 0;
|
||||||
|
#endif /* NDEBUG */
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
/*
|
||||||
|
** Turn parser tracing on by giving a stream to which to write the trace
|
||||||
|
** and a prompt to preface each trace message. Tracing is turned off
|
||||||
|
** by making either argument NULL
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** <ul>
|
||||||
|
** <li> A FILE* to which trace output should be written.
|
||||||
|
** If NULL, then tracing is turned off.
|
||||||
|
** <li> A prefix string written at the beginning of every
|
||||||
|
** line of trace output. If NULL, then tracing is
|
||||||
|
** turned off.
|
||||||
|
** </ul>
|
||||||
|
**
|
||||||
|
** Outputs:
|
||||||
|
** None.
|
||||||
|
*/
|
||||||
|
void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
|
||||||
|
yyTraceFILE = TraceFILE;
|
||||||
|
yyTracePrompt = zTracePrompt;
|
||||||
|
if( yyTraceFILE==0 ) yyTracePrompt = 0;
|
||||||
|
else if( yyTracePrompt==0 ) yyTraceFILE = 0;
|
||||||
|
}
|
||||||
|
#endif /* NDEBUG */
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
/* For tracing shifts, the names of all terminals and nonterminals
|
||||||
|
** are required. The following table supplies these names */
|
||||||
|
static const char *const yyTokenName[] = {
|
||||||
|
%%
|
||||||
|
};
|
||||||
|
#endif /* NDEBUG */
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
/* For tracing reduce actions, the names of all rules are required.
|
||||||
|
*/
|
||||||
|
static const char *const yyRuleName[] = {
|
||||||
|
%%
|
||||||
|
};
|
||||||
|
#endif /* NDEBUG */
|
||||||
|
|
||||||
|
|
||||||
|
#if YYSTACKDEPTH<=0
|
||||||
|
/*
|
||||||
|
** Try to increase the size of the parser stack. Return the number
|
||||||
|
** of errors. Return 0 on success.
|
||||||
|
*/
|
||||||
|
static int yyGrowStack(yyParser *p){
|
||||||
|
int newSize;
|
||||||
|
int idx;
|
||||||
|
yyStackEntry *pNew;
|
||||||
|
|
||||||
|
newSize = p->yystksz*2 + 100;
|
||||||
|
idx = p->yytos ? (int)(p->yytos - p->yystack) : 0;
|
||||||
|
if( p->yystack==&p->yystk0 ){
|
||||||
|
pNew = (yyStackEntry *)malloc(newSize*sizeof(pNew[0]));
|
||||||
|
if( pNew ) pNew[0] = p->yystk0;
|
||||||
|
}else{
|
||||||
|
pNew = (yyStackEntry *)realloc(p->yystack, newSize*sizeof(pNew[0]));
|
||||||
|
}
|
||||||
|
if( pNew ){
|
||||||
|
p->yystack = pNew;
|
||||||
|
p->yytos = &p->yystack[idx];
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
|
||||||
|
yyTracePrompt, p->yystksz, newSize);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
p->yystksz = newSize;
|
||||||
|
}
|
||||||
|
return pNew==0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Datatype of the argument to the memory allocated passed as the
|
||||||
|
** second argument to ParseAlloc() below. This can be changed by
|
||||||
|
** putting an appropriate #define in the %include section of the input
|
||||||
|
** grammar.
|
||||||
|
*/
|
||||||
|
#ifndef YYMALLOCARGTYPE
|
||||||
|
# define YYMALLOCARGTYPE size_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This function allocates a new parser.
|
||||||
|
** The only argument is a pointer to a function which works like
|
||||||
|
** malloc.
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** A pointer to the function used to allocate memory.
|
||||||
|
**
|
||||||
|
** Outputs:
|
||||||
|
** A pointer to a parser. This pointer is used in subsequent calls
|
||||||
|
** to Parse and ParseFree.
|
||||||
|
*/
|
||||||
|
void *ParseAlloc(void *(CDECL *mallocProc)(YYMALLOCARGTYPE)){
|
||||||
|
yyParser *pParser;
|
||||||
|
pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
|
||||||
|
if( pParser ){
|
||||||
|
#ifdef YYTRACKMAXSTACKDEPTH
|
||||||
|
pParser->yyhwm = 0;
|
||||||
|
#endif
|
||||||
|
#if YYSTACKDEPTH<=0
|
||||||
|
pParser->yytos = NULL;
|
||||||
|
pParser->yystack = NULL;
|
||||||
|
pParser->yystksz = 0;
|
||||||
|
if( yyGrowStack(pParser) ){
|
||||||
|
pParser->yystack = &pParser->yystk0;
|
||||||
|
pParser->yystksz = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifndef YYNOERRORRECOVERY
|
||||||
|
pParser->yyerrcnt = -1;
|
||||||
|
#endif
|
||||||
|
pParser->yytos = pParser->yystack;
|
||||||
|
pParser->yystack[0].stateno = 0;
|
||||||
|
pParser->yystack[0].major = 0;
|
||||||
|
}
|
||||||
|
return pParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following function deletes the "minor type" or semantic value
|
||||||
|
** associated with a symbol. The symbol can be either a terminal
|
||||||
|
** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
|
||||||
|
** a pointer to the value to be deleted. The code used to do the
|
||||||
|
** deletions is derived from the %destructor and/or %token_destructor
|
||||||
|
** directives of the input grammar.
|
||||||
|
*/
|
||||||
|
static void yy_destructor(
|
||||||
|
yyParser *yypParser, /* The parser */
|
||||||
|
YYCODETYPE yymajor, /* Type code for object to destroy */
|
||||||
|
YYMINORTYPE *yypminor /* The object to be destroyed */
|
||||||
|
){
|
||||||
|
ParseARG_FETCH;
|
||||||
|
switch( yymajor ){
|
||||||
|
/* Here is inserted the actions which take place when a
|
||||||
|
** terminal or non-terminal is destroyed. This can happen
|
||||||
|
** when the symbol is popped from the stack during a
|
||||||
|
** reduce or during error processing or when a parser is
|
||||||
|
** being destroyed before it is finished parsing.
|
||||||
|
**
|
||||||
|
** Note: during a reduce, the only symbols destroyed are those
|
||||||
|
** which appear on the RHS of the rule, but which are *not* used
|
||||||
|
** inside the C code.
|
||||||
|
*/
|
||||||
|
/********* Begin destructor definitions ***************************************/
|
||||||
|
%%
|
||||||
|
/********* End destructor definitions *****************************************/
|
||||||
|
default: break; /* If no destructor action specified: do nothing */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Pop the parser's stack once.
|
||||||
|
**
|
||||||
|
** If there is a destructor routine associated with the token which
|
||||||
|
** is popped from the stack, then call it.
|
||||||
|
*/
|
||||||
|
static void yy_pop_parser_stack(yyParser *pParser){
|
||||||
|
yyStackEntry *yytos;
|
||||||
|
assert( pParser->yytos!=0 );
|
||||||
|
assert( pParser->yytos > pParser->yystack );
|
||||||
|
yytos = pParser->yytos--;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sPopping %s\n",
|
||||||
|
yyTracePrompt,
|
||||||
|
yyTokenName[yytos->major]);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
yy_destructor(pParser, yytos->major, &yytos->minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Deallocate and destroy a parser. Destructors are called for
|
||||||
|
** all stack elements before shutting the parser down.
|
||||||
|
**
|
||||||
|
** If the YYPARSEFREENEVERNULL macro exists (for example because it
|
||||||
|
** is defined in a %include section of the input grammar) then it is
|
||||||
|
** assumed that the input pointer is never NULL.
|
||||||
|
*/
|
||||||
|
void ParseFree(
|
||||||
|
void *p, /* The parser to be deleted */
|
||||||
|
void (CDECL *freeProc)(void*) /* Function used to reclaim memory */
|
||||||
|
){
|
||||||
|
yyParser *pParser = (yyParser*)p;
|
||||||
|
#ifndef YYPARSEFREENEVERNULL
|
||||||
|
if( pParser==0 ) return;
|
||||||
|
#endif
|
||||||
|
while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
|
||||||
|
#if YYSTACKDEPTH<=0
|
||||||
|
if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
|
||||||
|
#endif
|
||||||
|
(*freeProc)((void*)pParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the peak depth of the stack for a parser.
|
||||||
|
*/
|
||||||
|
#ifdef YYTRACKMAXSTACKDEPTH
|
||||||
|
int ParseStackPeak(void *p){
|
||||||
|
yyParser *pParser = (yyParser*)p;
|
||||||
|
return pParser->yyhwm;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find the appropriate action for a parser given the terminal
|
||||||
|
** look-ahead token iLookAhead.
|
||||||
|
*/
|
||||||
|
static unsigned int yy_find_shift_action(
|
||||||
|
yyParser *pParser, /* The parser */
|
||||||
|
YYCODETYPE iLookAhead /* The look-ahead token */
|
||||||
|
){
|
||||||
|
int i;
|
||||||
|
int stateno = pParser->yytos->stateno;
|
||||||
|
|
||||||
|
if( stateno>=YY_MIN_REDUCE ) return stateno;
|
||||||
|
assert( stateno <= YY_SHIFT_COUNT );
|
||||||
|
do{
|
||||||
|
i = yy_shift_ofst[stateno];
|
||||||
|
assert( iLookAhead!=YYNOCODE );
|
||||||
|
i += iLookAhead;
|
||||||
|
if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
|
||||||
|
#ifdef YYFALLBACK
|
||||||
|
YYCODETYPE iFallback; /* Fallback token */
|
||||||
|
if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
|
||||||
|
&& (iFallback = yyFallback[iLookAhead])!=0 ){
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
|
||||||
|
yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
|
||||||
|
iLookAhead = iFallback;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef YYWILDCARD
|
||||||
|
{
|
||||||
|
int j = i - iLookAhead + YYWILDCARD;
|
||||||
|
if(
|
||||||
|
#if YY_SHIFT_MIN+YYWILDCARD<0
|
||||||
|
j>=0 &&
|
||||||
|
#endif
|
||||||
|
#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
|
||||||
|
j<YY_ACTTAB_COUNT &&
|
||||||
|
#endif
|
||||||
|
yy_lookahead[j]==YYWILDCARD && iLookAhead>0
|
||||||
|
){
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
|
||||||
|
yyTracePrompt, yyTokenName[iLookAhead],
|
||||||
|
yyTokenName[YYWILDCARD]);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif /* NDEBUG */
|
||||||
|
return yy_action[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* YYWILDCARD */
|
||||||
|
return yy_default[stateno];
|
||||||
|
}else{
|
||||||
|
return yy_action[i];
|
||||||
|
}
|
||||||
|
}while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Find the appropriate action for a parser given the non-terminal
|
||||||
|
** look-ahead token iLookAhead.
|
||||||
|
*/
|
||||||
|
static int yy_find_reduce_action(
|
||||||
|
int stateno, /* Current state number */
|
||||||
|
YYCODETYPE iLookAhead /* The look-ahead token */
|
||||||
|
){
|
||||||
|
int i;
|
||||||
|
#ifdef YYERRORSYMBOL
|
||||||
|
if( stateno>YY_REDUCE_COUNT ){
|
||||||
|
return yy_default[stateno];
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
assert( stateno<=YY_REDUCE_COUNT );
|
||||||
|
#endif
|
||||||
|
i = yy_reduce_ofst[stateno];
|
||||||
|
assert( i!=YY_REDUCE_USE_DFLT );
|
||||||
|
assert( iLookAhead!=YYNOCODE );
|
||||||
|
i += iLookAhead;
|
||||||
|
#ifdef YYERRORSYMBOL
|
||||||
|
if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
|
||||||
|
return yy_default[stateno];
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
assert( i>=0 && i<YY_ACTTAB_COUNT );
|
||||||
|
assert( yy_lookahead[i]==iLookAhead );
|
||||||
|
#endif
|
||||||
|
return yy_action[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following routine is called if the stack overflows.
|
||||||
|
*/
|
||||||
|
static void yyStackOverflow(yyParser *yypParser){
|
||||||
|
ParseARG_FETCH;
|
||||||
|
yypParser->yytos--;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
|
||||||
|
/* Here code is inserted which will execute if the parser
|
||||||
|
** stack every overflows */
|
||||||
|
/******** Begin %stack_overflow code ******************************************/
|
||||||
|
%%
|
||||||
|
/******** End %stack_overflow code ********************************************/
|
||||||
|
ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Print tracing information for a SHIFT action
|
||||||
|
*/
|
||||||
|
#ifndef NDEBUG
|
||||||
|
static void yyTraceShift(yyParser *yypParser, int yyNewState){
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
if( yyNewState<YYNSTATE ){
|
||||||
|
fprintf(yyTraceFILE,"%sShift '%s', go to state %d\n",
|
||||||
|
yyTracePrompt,yyTokenName[yypParser->yytos->major],
|
||||||
|
yyNewState);
|
||||||
|
}else{
|
||||||
|
fprintf(yyTraceFILE,"%sShift '%s'\n",
|
||||||
|
yyTracePrompt,yyTokenName[yypParser->yytos->major]);
|
||||||
|
}
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
# define yyTraceShift(X,Y)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Perform a shift action.
|
||||||
|
*/
|
||||||
|
static void yy_shift(
|
||||||
|
yyParser *yypParser, /* The parser to be shifted */
|
||||||
|
int yyNewState, /* The new state to shift in */
|
||||||
|
int yyMajor, /* The major token to shift in */
|
||||||
|
ParseTOKENTYPE yyMinor /* The minor token to shift in */
|
||||||
|
){
|
||||||
|
yyStackEntry *yytos;
|
||||||
|
yypParser->yytos++;
|
||||||
|
#ifdef YYTRACKMAXSTACKDEPTH
|
||||||
|
if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
|
||||||
|
yypParser->yyhwm++;
|
||||||
|
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if YYSTACKDEPTH>0
|
||||||
|
if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH] ){
|
||||||
|
yyStackOverflow(yypParser);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){
|
||||||
|
if( yyGrowStack(yypParser) ){
|
||||||
|
yyStackOverflow(yypParser);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if( yyNewState > YY_MAX_SHIFT ){
|
||||||
|
yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
|
||||||
|
}
|
||||||
|
yytos = yypParser->yytos;
|
||||||
|
yytos->stateno = (YYACTIONTYPE)yyNewState;
|
||||||
|
yytos->major = (YYCODETYPE)yyMajor;
|
||||||
|
yytos->minor.yy0 = yyMinor;
|
||||||
|
yyTraceShift(yypParser, yyNewState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following table contains information about every rule that
|
||||||
|
** is used during the reduce.
|
||||||
|
*/
|
||||||
|
static const struct {
|
||||||
|
YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
|
||||||
|
unsigned char nrhs; /* Number of right-hand side symbols in the rule */
|
||||||
|
} yyRuleInfo[] = {
|
||||||
|
%%
|
||||||
|
};
|
||||||
|
|
||||||
|
static void yy_accept(yyParser*); /* Forward Declaration */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Perform a reduce action and the shift that must immediately
|
||||||
|
** follow the reduce.
|
||||||
|
*/
|
||||||
|
static void yy_reduce(
|
||||||
|
yyParser *yypParser, /* The parser */
|
||||||
|
unsigned int yyruleno /* Number of the rule by which to reduce */
|
||||||
|
){
|
||||||
|
int yygoto; /* The next state */
|
||||||
|
int yyact; /* The next action */
|
||||||
|
yyStackEntry *yymsp; /* The top of the parser's stack */
|
||||||
|
int yysize; /* Amount to pop the stack */
|
||||||
|
ParseARG_FETCH;
|
||||||
|
yymsp = yypParser->yytos;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
|
||||||
|
yysize = yyRuleInfo[yyruleno].nrhs;
|
||||||
|
fprintf(yyTraceFILE, "%sReduce [%s], go to state %d.\n", yyTracePrompt,
|
||||||
|
yyRuleName[yyruleno], yymsp[-yysize].stateno);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif /* NDEBUG */
|
||||||
|
|
||||||
|
/* Check that the stack is large enough to grow by a single entry
|
||||||
|
** if the RHS of the rule is empty. This ensures that there is room
|
||||||
|
** enough on the stack to push the LHS value */
|
||||||
|
if( yyRuleInfo[yyruleno].nrhs==0 ){
|
||||||
|
#ifdef YYTRACKMAXSTACKDEPTH
|
||||||
|
if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
|
||||||
|
yypParser->yyhwm++;
|
||||||
|
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if YYSTACKDEPTH>0
|
||||||
|
if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH-1] ){
|
||||||
|
yyStackOverflow(yypParser);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
|
||||||
|
if( yyGrowStack(yypParser) ){
|
||||||
|
yyStackOverflow(yypParser);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
yymsp = yypParser->yytos;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( yyruleno ){
|
||||||
|
/* Beginning here are the reduction cases. A typical example
|
||||||
|
** follows:
|
||||||
|
** case 0:
|
||||||
|
** #line <lineno> <grammarfile>
|
||||||
|
** { ... } // User supplied code
|
||||||
|
** #line <lineno> <thisfile>
|
||||||
|
** break;
|
||||||
|
*/
|
||||||
|
/********** Begin reduce actions **********************************************/
|
||||||
|
%%
|
||||||
|
/********** End reduce actions ************************************************/
|
||||||
|
};
|
||||||
|
assert( yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
|
||||||
|
yygoto = yyRuleInfo[yyruleno].lhs;
|
||||||
|
yysize = yyRuleInfo[yyruleno].nrhs;
|
||||||
|
yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
|
||||||
|
if( yyact <= YY_MAX_SHIFTREDUCE ){
|
||||||
|
if( yyact>YY_MAX_SHIFT ){
|
||||||
|
yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
|
||||||
|
}
|
||||||
|
yymsp -= yysize-1;
|
||||||
|
yypParser->yytos = yymsp;
|
||||||
|
yymsp->stateno = (YYACTIONTYPE)yyact;
|
||||||
|
yymsp->major = (YYCODETYPE)yygoto;
|
||||||
|
yyTraceShift(yypParser, yyact);
|
||||||
|
}else{
|
||||||
|
assert( yyact == YY_ACCEPT_ACTION );
|
||||||
|
yypParser->yytos -= yysize;
|
||||||
|
yy_accept(yypParser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following code executes when the parse fails
|
||||||
|
*/
|
||||||
|
#ifndef YYNOERRORRECOVERY
|
||||||
|
static void yy_parse_failed(
|
||||||
|
yyParser *yypParser /* The parser */
|
||||||
|
){
|
||||||
|
ParseARG_FETCH;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
|
||||||
|
/* Here code is inserted which will be executed whenever the
|
||||||
|
** parser fails */
|
||||||
|
/************ Begin %parse_failure code ***************************************/
|
||||||
|
%%
|
||||||
|
/************ End %parse_failure code *****************************************/
|
||||||
|
ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
||||||
|
}
|
||||||
|
#endif /* YYNOERRORRECOVERY */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following code executes when a syntax error first occurs.
|
||||||
|
*/
|
||||||
|
static void yy_syntax_error(
|
||||||
|
yyParser *yypParser, /* The parser */
|
||||||
|
int yymajor, /* The major type of the error token */
|
||||||
|
ParseTOKENTYPE yyminor /* The minor type of the error token */
|
||||||
|
){
|
||||||
|
ParseARG_FETCH;
|
||||||
|
#define TOKEN yyminor
|
||||||
|
/************ Begin %syntax_error code ****************************************/
|
||||||
|
%%
|
||||||
|
/************ End %syntax_error code ******************************************/
|
||||||
|
ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following is executed when the parser accepts
|
||||||
|
*/
|
||||||
|
static void yy_accept(
|
||||||
|
yyParser *yypParser /* The parser */
|
||||||
|
){
|
||||||
|
ParseARG_FETCH;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifndef YYNOERRORRECOVERY
|
||||||
|
yypParser->yyerrcnt = -1;
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
assert( yypParser->yytos==yypParser->yystack );
|
||||||
|
#else
|
||||||
|
while (yypParser->yytos>yypParser->yystack) yy_pop_parser_stack(yypParser);
|
||||||
|
#endif
|
||||||
|
/* Here code is inserted which will be executed whenever the
|
||||||
|
** parser accepts */
|
||||||
|
/*********** Begin %parse_accept code *****************************************/
|
||||||
|
%%
|
||||||
|
/*********** End %parse_accept code *******************************************/
|
||||||
|
ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The main parser program.
|
||||||
|
** The first argument is a pointer to a structure obtained from
|
||||||
|
** "ParseAlloc" which describes the current state of the parser.
|
||||||
|
** The second argument is the major token number. The third is
|
||||||
|
** the minor token. The fourth optional argument is whatever the
|
||||||
|
** user wants (and specified in the grammar) and is available for
|
||||||
|
** use by the action routines.
|
||||||
|
**
|
||||||
|
** Inputs:
|
||||||
|
** <ul>
|
||||||
|
** <li> A pointer to the parser (an opaque structure.)
|
||||||
|
** <li> The major token number.
|
||||||
|
** <li> The minor token number.
|
||||||
|
** <li> An option argument of a grammar-specified type.
|
||||||
|
** </ul>
|
||||||
|
**
|
||||||
|
** Outputs:
|
||||||
|
** None.
|
||||||
|
*/
|
||||||
|
void Parse(
|
||||||
|
void *yyp, /* The parser */
|
||||||
|
int yymajor, /* The major token code number */
|
||||||
|
ParseTOKENTYPE yyminor /* The value for the token */
|
||||||
|
ParseARG_PDECL /* Optional %extra_argument parameter */
|
||||||
|
){
|
||||||
|
YYMINORTYPE yyminorunion;
|
||||||
|
unsigned int yyact; /* The parser action. */
|
||||||
|
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
|
||||||
|
int yyendofinput; /* True if we are at the end of input */
|
||||||
|
#endif
|
||||||
|
#ifdef YYERRORSYMBOL
|
||||||
|
int yyerrorhit = 0; /* True if yymajor has invoked an error */
|
||||||
|
#endif
|
||||||
|
yyParser *yypParser; /* The parser */
|
||||||
|
|
||||||
|
yypParser = (yyParser*)yyp;
|
||||||
|
assert( yypParser->yytos!=0 );
|
||||||
|
#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
|
||||||
|
yyendofinput = (yymajor==0);
|
||||||
|
#endif
|
||||||
|
ParseARG_STORE;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sInput '%s'\n",yyTracePrompt,yyTokenName[yymajor]);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
do{
|
||||||
|
yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
|
||||||
|
if( yyact <= YY_MAX_SHIFTREDUCE ){
|
||||||
|
yy_shift(yypParser,yyact,yymajor,yyminor);
|
||||||
|
#ifndef YYNOERRORRECOVERY
|
||||||
|
yypParser->yyerrcnt--;
|
||||||
|
#endif
|
||||||
|
yymajor = YYNOCODE;
|
||||||
|
}else if( yyact <= YY_MAX_REDUCE ){
|
||||||
|
yy_reduce(yypParser,yyact-YY_MIN_REDUCE);
|
||||||
|
}else{
|
||||||
|
assert( yyact == YY_ERROR_ACTION );
|
||||||
|
yyminorunion.yy0 = yyminor;
|
||||||
|
#ifdef YYERRORSYMBOL
|
||||||
|
int yymx;
|
||||||
|
#endif
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef YYERRORSYMBOL
|
||||||
|
/* A syntax error has occurred.
|
||||||
|
** The response to an error depends upon whether or not the
|
||||||
|
** grammar defines an error token "ERROR".
|
||||||
|
**
|
||||||
|
** This is what we do if the grammar does define ERROR:
|
||||||
|
**
|
||||||
|
** * Call the %syntax_error function.
|
||||||
|
**
|
||||||
|
** * Begin popping the stack until we enter a state where
|
||||||
|
** it is legal to shift the error symbol, then shift
|
||||||
|
** the error symbol.
|
||||||
|
**
|
||||||
|
** * Set the error count to three.
|
||||||
|
**
|
||||||
|
** * Begin accepting and shifting new tokens. No new error
|
||||||
|
** processing will occur until three tokens have been
|
||||||
|
** shifted successfully.
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
if( yypParser->yyerrcnt<0 ){
|
||||||
|
yy_syntax_error(yypParser,yymajor,yyminor);
|
||||||
|
}
|
||||||
|
yymx = yypParser->yytos->major;
|
||||||
|
if( yymx==YYERRORSYMBOL || yyerrorhit ){
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
fprintf(yyTraceFILE,"%sDiscard input token %s\n",
|
||||||
|
yyTracePrompt,yyTokenName[yymajor]);
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
|
||||||
|
yymajor = YYNOCODE;
|
||||||
|
}else{
|
||||||
|
while( yypParser->yytos >= yypParser->yystack
|
||||||
|
&& yymx != YYERRORSYMBOL
|
||||||
|
&& (yyact = yy_find_reduce_action(
|
||||||
|
yypParser->yytos->stateno,
|
||||||
|
YYERRORSYMBOL)) >= YY_MIN_REDUCE
|
||||||
|
){
|
||||||
|
yy_pop_parser_stack(yypParser);
|
||||||
|
}
|
||||||
|
if( yypParser->yytos < yypParser->yystack || yymajor==0 ){
|
||||||
|
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
|
||||||
|
yy_parse_failed(yypParser);
|
||||||
|
#ifndef YYNOERRORRECOVERY
|
||||||
|
yypParser->yyerrcnt = -1;
|
||||||
|
#endif
|
||||||
|
yymajor = YYNOCODE;
|
||||||
|
}else if( yymx!=YYERRORSYMBOL ){
|
||||||
|
yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yypParser->yyerrcnt = 3;
|
||||||
|
yyerrorhit = 1;
|
||||||
|
#elif defined(YYNOERRORRECOVERY)
|
||||||
|
/* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
|
||||||
|
** do any kind of error recovery. Instead, simply invoke the syntax
|
||||||
|
** error routine and continue going as if nothing had happened.
|
||||||
|
**
|
||||||
|
** Applications can set this macro (for example inside %include) if
|
||||||
|
** they intend to abandon the parse upon the first syntax error seen.
|
||||||
|
*/
|
||||||
|
yy_syntax_error(yypParser,yymajor, yyminor);
|
||||||
|
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
|
||||||
|
yymajor = YYNOCODE;
|
||||||
|
|
||||||
|
#else /* YYERRORSYMBOL is not defined */
|
||||||
|
/* This is what we do if the grammar does not define ERROR:
|
||||||
|
**
|
||||||
|
** * Report an error message, and throw away the input token.
|
||||||
|
**
|
||||||
|
** * If the input token is $, then fail the parse.
|
||||||
|
**
|
||||||
|
** As before, subsequent error messages are suppressed until
|
||||||
|
** three input tokens have been successfully shifted.
|
||||||
|
*/
|
||||||
|
if( yypParser->yyerrcnt<=0 ){
|
||||||
|
yy_syntax_error(yypParser,yymajor, yyminor);
|
||||||
|
}
|
||||||
|
yypParser->yyerrcnt = 3;
|
||||||
|
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
|
||||||
|
if( yyendofinput ){
|
||||||
|
yy_parse_failed(yypParser);
|
||||||
|
#ifndef YYNOERRORRECOVERY
|
||||||
|
yypParser->yyerrcnt = -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
yymajor = YYNOCODE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}while( yymajor!=YYNOCODE && yypParser->yytos>yypParser->yystack );
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if( yyTraceFILE ){
|
||||||
|
yyStackEntry *i;
|
||||||
|
char cDiv = '[';
|
||||||
|
fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
|
||||||
|
for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
|
||||||
|
fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);
|
||||||
|
cDiv = ' ';
|
||||||
|
}
|
||||||
|
fprintf(yyTraceFILE,"]\n");
|
||||||
|
fflush(yyTraceFILE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
Loading…
Reference in a new issue