summaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/c/AUTHORS5
-rw-r--r--src/runtime/c/COPYING.LESSER165
-rw-r--r--src/runtime/c/Doxyfile1632
-rw-r--r--src/runtime/c/DoxygenLayout.xml188
-rw-r--r--src/runtime/c/Makefile19
-rw-r--r--src/runtime/c/Makefile.am112
-rw-r--r--src/runtime/c/README119
-rw-r--r--src/runtime/c/configure.ac66
-rw-r--r--src/runtime/c/doxygen.am169
-rw-r--r--src/runtime/c/gfcc-term.c203
-rw-r--r--src/runtime/c/gfcc-term.h65
-rw-r--r--src/runtime/c/gfcc-tree.c61
-rw-r--r--src/runtime/c/gfcc-tree.h49
-rw-r--r--src/runtime/c/gu/assert.c53
-rw-r--r--src/runtime/c/gu/assert.h61
-rw-r--r--src/runtime/c/gu/bits.c53
-rw-r--r--src/runtime/c/gu/bits.h151
-rw-r--r--src/runtime/c/gu/choice.c73
-rw-r--r--src/runtime/c/gu/choice.h37
-rw-r--r--src/runtime/c/gu/defs.c4
-rw-r--r--src/runtime/c/gu/defs.h217
-rw-r--r--src/runtime/c/gu/dump.c411
-rw-r--r--src/runtime/c/gu/dump.h34
-rw-r--r--src/runtime/c/gu/enum.c7
-rw-r--r--src/runtime/c/gu/enum.h35
-rw-r--r--src/runtime/c/gu/exn.c72
-rw-r--r--src/runtime/c/gu/exn.h195
-rw-r--r--src/runtime/c/gu/file.c73
-rw-r--r--src/runtime/c/gu/file.h14
-rw-r--r--src/runtime/c/gu/fun.c1
-rw-r--r--src/runtime/c/gu/fun.h65
-rw-r--r--src/runtime/c/gu/hash.c77
-rw-r--r--src/runtime/c/gu/hash.h40
-rw-r--r--src/runtime/c/gu/in.c421
-rw-r--r--src/runtime/c/gu/in.h146
-rw-r--r--src/runtime/c/gu/intern.c59
-rw-r--r--src/runtime/c/gu/intern.h24
-rw-r--r--src/runtime/c/gu/list.c59
-rw-r--r--src/runtime/c/gu/list.h140
-rw-r--r--src/runtime/c/gu/log.c79
-rw-r--r--src/runtime/c/gu/log.h65
-rw-r--r--src/runtime/c/gu/map.c353
-rw-r--r--src/runtime/c/gu/map.h121
-rw-r--r--src/runtime/c/gu/mem.c346
-rw-r--r--src/runtime/c/gu/mem.h276
-rw-r--r--src/runtime/c/gu/out.c302
-rw-r--r--src/runtime/c/gu/out.h166
-rw-r--r--src/runtime/c/gu/prime.c154
-rw-r--r--src/runtime/c/gu/prime.h16
-rw-r--r--src/runtime/c/gu/read.c15
-rw-r--r--src/runtime/c/gu/read.h31
-rw-r--r--src/runtime/c/gu/seq.c245
-rw-r--r--src/runtime/c/gu/seq.h198
-rw-r--r--src/runtime/c/gu/str.c85
-rw-r--r--src/runtime/c/gu/str.h29
-rw-r--r--src/runtime/c/gu/string.c270
-rw-r--r--src/runtime/c/gu/string.h125
-rw-r--r--src/runtime/c/gu/sysdeps.h30
-rw-r--r--src/runtime/c/gu/type.c229
-rw-r--r--src/runtime/c/gu/type.h454
-rw-r--r--src/runtime/c/gu/ucs.c135
-rw-r--r--src/runtime/c/gu/ucs.h53
-rw-r--r--src/runtime/c/gu/utf8.c220
-rw-r--r--src/runtime/c/gu/utf8.h67
-rw-r--r--src/runtime/c/gu/variant.c100
-rw-r--r--src/runtime/c/gu/variant.h167
-rw-r--r--src/runtime/c/gu/write.c174
-rw-r--r--src/runtime/c/gu/write.h64
-rw-r--r--src/runtime/c/gu/yaml.c339
-rw-r--r--src/runtime/c/gu/yaml.h38
-rw-r--r--src/runtime/c/guconfig.h.in2
-rw-r--r--src/runtime/c/libgu.pc.in10
-rw-r--r--src/runtime/c/libpgf.pc.in11
-rw-r--r--src/runtime/c/m4/ax_prog_doxygen.m4533
-rw-r--r--src/runtime/c/m4/c_ext.m489
-rw-r--r--src/runtime/c/pgf.h14
-rw-r--r--src/runtime/c/pgf/data.c251
-rw-r--r--src/runtime/c/pgf/data.h388
-rw-r--r--src/runtime/c/pgf/edsl.h20
-rw-r--r--src/runtime/c/pgf/expr.c334
-rw-r--r--src/runtime/c/pgf/expr.h284
-rw-r--r--src/runtime/c/pgf/linearize.c613
-rw-r--r--src/runtime/c/pgf/linearize.h156
-rw-r--r--src/runtime/c/pgf/loader.c396
-rw-r--r--src/runtime/c/pgf/panic.c8
-rw-r--r--src/runtime/c/pgf/panic.h6
-rw-r--r--src/runtime/c/pgf/parser.c697
-rw-r--r--src/runtime/c/pgf/parser.h127
-rw-r--r--src/runtime/c/pgf/pgf.h78
-rw-r--r--src/runtime/c/pgf/reader.c843
-rw-r--r--src/runtime/c/pgf/type.h22
-rw-r--r--src/runtime/c/pgf/unloader.c248
-rw-r--r--src/runtime/c/utils/pgf-translate.c179
-rw-r--r--src/runtime/c/utils/pgf2yaml.c29
94 files changed, 14363 insertions, 1296 deletions
diff --git a/src/runtime/c/AUTHORS b/src/runtime/c/AUTHORS
new file mode 100644
index 000000000..298a87926
--- /dev/null
+++ b/src/runtime/c/AUTHORS
@@ -0,0 +1,5 @@
+Libpgf was written by:
+
+Lauri Alanko <lealanko@ling.helsinki.fi>
+
+Based on the original PGF implementation by Krasimir Angelov.
diff --git a/src/runtime/c/COPYING.LESSER b/src/runtime/c/COPYING.LESSER
new file mode 100644
index 000000000..65c5ca88a
--- /dev/null
+++ b/src/runtime/c/COPYING.LESSER
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/src/runtime/c/Doxyfile b/src/runtime/c/Doxyfile
new file mode 100644
index 000000000..e3646aac3
--- /dev/null
+++ b/src/runtime/c/Doxyfile
@@ -0,0 +1,1632 @@
+# Doxyfile 1.7.1 -*- conf -*-
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = $(PROJECT)
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = $(VERSION)
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = $(DOCDIR)
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES = pool="@param pool The pool to allocate from."
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = $(DEVDOC)
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = $(DEVDOC)
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+# When DEVDOC=NO, we only scan the headers, and want to document static
+# inline functions.
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = $(DEVDOC)
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = $(DEVDOC)
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = $(DEVDOC)
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 0 # hide by default
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE = DoxygenLayout.xml
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = NO
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = $(INPUT)
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = $(DEVDOC)
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = $(DEVDOC)
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = $(DEVDOC)
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = $(DEVDOC)
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = $(DEVDOC)
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = $(GENERATE_HTMLHELP)
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = $(GENERATE_CHI)
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = $(GENERATE_LATEX)
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = $(PAPER_SIZE)
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = $(GENERATE_RTF)
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = $(GENERATE_MAN)
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = $(GENERATE_XML)
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = DOXYGEN=1
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED = GU_PRIVATE
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = $(HAVE_DOT)
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans.ttf
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = $(DEVDOC)
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = $(DEVDOC)
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/src/runtime/c/DoxygenLayout.xml b/src/runtime/c/DoxygenLayout.xml
new file mode 100644
index 000000000..fb1f188fd
--- /dev/null
+++ b/src/runtime/c/DoxygenLayout.xml
@@ -0,0 +1,188 @@
+<doxygenlayout version="1.0">
+ <!-- Navigation index tabs for HTML output -->
+ <navindex>
+ <tab type="mainpage" visible="yes" title=""/>
+ <tab type="pages" visible="yes" title="" intro=""/>
+ <tab type="modules" visible="yes" title="" intro=""/>
+ <tab type="namespaces" visible="yes" title="">
+ <tab type="namespacelist" visible="yes" title="" intro=""/>
+ <tab type="namespacemembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="classes" visible="yes" title="">
+ <tab type="classlist" visible="yes" title="" intro=""/>
+ <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
+ <tab type="hierarchy" visible="yes" title="" intro=""/>
+ <tab type="classmembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="files" visible="yes" title="">
+ <tab type="filelist" visible="yes" title="" intro=""/>
+ <tab type="globals" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="dirs" visible="yes" title="" intro=""/>
+ <tab type="examples" visible="yes" title="" intro=""/>
+ </navindex>
+
+ <!-- Layout definition for a class page -->
+ <class>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <inheritancegraph visible="$CLASS_GRAPH"/>
+ <collaborationgraph visible="$COLLABORATION_GRAPH"/>
+ <allmemberslink visible="yes"/>
+ <memberdecl>
+ <nestedclasses visible="yes" title=""/>
+ <publictypes title=""/>
+ <publicslots title=""/>
+ <signals title=""/>
+ <publicmethods title=""/>
+ <publicstaticmethods title=""/>
+ <publicattributes title=""/>
+ <publicstaticattributes title=""/>
+ <protectedtypes title=""/>
+ <protectedslots title=""/>
+ <protectedmethods title=""/>
+ <protectedstaticmethods title=""/>
+ <protectedattributes title=""/>
+ <protectedstaticattributes title=""/>
+ <packagetypes title=""/>
+ <packagemethods title=""/>
+ <packagestaticmethods title=""/>
+ <packageattributes title=""/>
+ <packagestaticattributes title=""/>
+ <properties title=""/>
+ <events title=""/>
+ <privatetypes title=""/>
+ <privateslots title=""/>
+ <privatemethods title=""/>
+ <privatestaticmethods title=""/>
+ <privateattributes title=""/>
+ <privatestaticattributes title=""/>
+ <friends title=""/>
+ <related title="" subtitle=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <constructors title=""/>
+ <functions title=""/>
+ <related title=""/>
+ <variables title=""/>
+ <properties title=""/>
+ <events title=""/>
+ </memberdef>
+ <usedfiles visible="$SHOW_USED_FILES"/>
+ <authorsection visible="yes"/>
+ </class>
+
+ <!-- Layout definition for a namespace page -->
+ <namespace>
+ <briefdescription visible="yes"/>
+ <memberdecl>
+ <nestednamespaces visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </namespace>
+
+ <!-- Layout definition for a file page -->
+ <file>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <includegraph visible="$INCLUDE_GRAPH"/>
+ <includedbygraph visible="$INCLUDED_BY_GRAPH"/>
+ <sourcelink visible="yes"/>
+ <detaileddescription title=""/>
+ <memberdecl>
+ <membergroups visible="yes"/>
+ <namespaces visible="yes" title=""/>
+ <enums title=""/>
+ <classes visible="yes" title=""/>
+ <typedefs title=""/>
+ <functions title=""/>
+ <defines title=""/>
+ <variables title=""/>
+ </memberdecl>
+ <memberdef>
+ <enums title=""/>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <functions title=""/>
+ <defines title=""/>
+ <variables title=""/>
+ </memberdef>
+ <authorsection/>
+ </file>
+
+ <!-- Layout definition for a group page -->
+ <group>
+ <briefdescription visible="yes"/>
+ <groupgraph visible="$GROUP_GRAPHS"/>
+ <detaileddescription title=""/>
+ <memberdecl>
+ <membergroups visible="yes"/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <classes visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <dirs visible="yes" title=""/>
+ <nestedgroups visible="yes" title=""/>
+ <files visible="yes" title=""/>
+ <typedefs title=""/>
+ <defines title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ </memberdecl>
+ <memberdef>
+ <pagedocs/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <functions title=""/>
+ <defines title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </group>
+
+ <!-- Layout definition for a directory page -->
+ <directory>
+ <briefdescription visible="yes"/>
+ <directorygraph visible="yes"/>
+ <memberdecl>
+ <dirs visible="yes"/>
+ <files visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ </directory>
+</doxygenlayout>
diff --git a/src/runtime/c/Makefile b/src/runtime/c/Makefile
deleted file mode 100644
index 72ac7ea79..000000000
--- a/src/runtime/c/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-CC = gcc
-CFLAGS += -O2 -W -Wall
-
-.PHONY: all clean
-
-all: libgfcc.a
-
-libgfcc.a: gfcc-tree.o gfcc-term.o
- ar r $@ $^
-
-gfcc-tree.o: gfcc-tree.c gfcc-tree.h
- $(CC) $(CFLAGS) -c -o $@ $<
-
-gfcc-term.o: gfcc-term.c gfcc-term.h
- $(CC) $(CFLAGS) -c -o $@ $<
-
-clean:
- -rm -f libgfcc.a
- -rm -f *.o
diff --git a/src/runtime/c/Makefile.am b/src/runtime/c/Makefile.am
new file mode 100644
index 000000000..0930dc58a
--- /dev/null
+++ b/src/runtime/c/Makefile.am
@@ -0,0 +1,112 @@
+
+LDADD = libgu.la
+
+lib_LTLIBRARIES = libgu.la libpgf.la
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libgu.pc libpgf.pc
+
+configincludedir = $(libdir)/libgu/include
+nodist_configinclude_HEADERS = guconfig.h
+
+guincludedir=$(includedir)/gu
+guinclude_HEADERS = \
+ gu/assert.h \
+ gu/bits.h \
+ gu/choice.h \
+ gu/defs.h \
+ gu/dump.h \
+ gu/enum.h \
+ gu/exn.h \
+ gu/file.h \
+ gu/fun.h \
+ gu/hash.h \
+ gu/in.h \
+ gu/intern.h \
+ gu/list.h \
+ gu/log.h \
+ gu/map.h \
+ gu/mem.h \
+ gu/out.h \
+ gu/prime.h \
+ gu/read.h \
+ gu/seq.h \
+ gu/str.h \
+ gu/string.h \
+ gu/sysdeps.h \
+ gu/type.h \
+ gu/ucs.h \
+ gu/utf8.h \
+ gu/variant.h \
+ gu/write.h \
+ gu/yaml.h
+
+pgfincludedir=$(includedir)/pgf
+pgfinclude_HEADERS = \
+ pgf/data.h \
+ pgf/expr.h \
+ pgf/linearize.h \
+ pgf/parser.h \
+ pgf/pgf.h
+
+libgu_la_SOURCES = \
+ gu/assert.c \
+ gu/bits.c \
+ gu/choice.c \
+ gu/defs.c \
+ gu/seq.c \
+ gu/dump.c \
+ gu/enum.c \
+ gu/exn.c \
+ gu/file.c \
+ gu/fun.c \
+ gu/hash.c \
+ gu/in.c \
+ gu/intern.c \
+ gu/list.c \
+ gu/log.c \
+ gu/map.c \
+ gu/mem.c \
+ gu/out.c \
+ gu/prime.c \
+ gu/read.c \
+ gu/str.c \
+ gu/string.c \
+ gu/type.c \
+ gu/utf8.c \
+ gu/write.c \
+ gu/ucs.c \
+ gu/variant.c \
+ gu/yaml.c
+
+libpgf_la_SOURCES = \
+ pgf/data.c \
+ pgf/data.h \
+ pgf/edsl.h \
+ pgf/expr.c \
+ pgf/expr.h \
+ pgf/parser.c \
+ pgf/parser.h \
+ pgf/reader.c \
+ pgf/linearize.c
+
+bin_PROGRAMS = \
+ utils/pgf2yaml \
+ utils/pgf-translate
+
+utils_pgf2yaml_SOURCES = utils/pgf2yaml.c
+utils_pgf2yaml_LDADD = libpgf.la libgu.la
+
+utils_pgf_translate_SOURCES = utils/pgf-translate.c
+utils_pgf_translate_LDADD = libpgf.la libgu.la
+
+AUTOMAKE_OPTIONS = foreign subdir-objects dist-bzip2
+ACLOCAL_AMFLAGS = -I m4
+include doxygen.am
+
+EXTRA_DIST = \
+ Doxyfile \
+ DoxygenLayout.xml \
+ libgu.pc.in \
+ libpgf.pc.in \
+ guconfig.h.in
diff --git a/src/runtime/c/README b/src/runtime/c/README
new file mode 100644
index 000000000..a48eb8909
--- /dev/null
+++ b/src/runtime/c/README
@@ -0,0 +1,119 @@
+
+This is a preview release of libpgf, a native-code library for parsing
+and linearization of the PGF grammars produced by the Grammatical
+Framework <http://www.grammaticalframework.org/>.
+
+This release is not yet ready for production use: essential
+functionality is still missing, the API is still likely to change, and
+the documentation is incomplete. This release is primarily meant for
+developers who are interested in using libpgf, and who wish to
+contribute to its design.
+
+
+PREREQUISITES
+-------------
+
+This is a self-contained library: only a C99-conformant C compiler is
+needed. The code is mostly portable C, although it makes some very
+general assumptions about the architecture (mostly regarding the
+representation of addresses) that should hold on modern systems. Still,
+the code has only been tested on Linux-x86(-64) so far. Reports of
+porting problems on other platforms are appreciated.
+
+Although the code "only" requires C99-conformance, it seems that many
+compilers fail at it subtly. In particular:
+
+- Clang does not currently support "extern inline" properly.
+
+- Sun C 5.9 apparently has a bug in its treatment of sizeof on compound
+ array literals.
+
+As a consequence, these compilers cannot be used in the current state of
+the code. Modern versions of GCC, on the other hand, seem to work fine.
+
+
+INSTALLING
+----------
+
+This is a standard GNU Autotools package. Read the attached INSTALL file
+for generic installation instructions. There are currently no
+interesting special configuration options.
+
+Pkg-config configuration files for the library are also provided.
+
+
+STATUS
+------
+
+Currently only very basic PGF functionality is supported, enough to
+translate sentences of the Phrasebook grammar in the GF distribution.
+Among missing features are:
+
+- querying a parser for tokens expected next
+- literals and custom categories
+- higher-order abstract syntax variables
+- type checking and inference
+- generation of random syntax trees
+
+Most of these will eventually get added.
+
+
+PROGRAMS
+--------
+
+There are two small programs included. These are mainly for testing
+purposes and for demonstrating how to use the library.
+
+The pgf2yaml program simply reads a PGF file from the standard input and
+dumps it to the standard output in YAML <http://yaml.org/> format.
+
+The pgf-translate program translates sentences of one language in a PGF
+grammar into another. It is invoked:
+
+pgf-translate PGF CAT FROM_LANG TO_LANG
+
+Where PGF is a PGF file, CAT is the name of the category whose sentences
+are to be translated, and FROM_LANG and TO_LANG are names of concrete
+grammars within the PGF file.
+
+The program prompts for a line containing a full sentence of the
+specified category in the source language, and displays the syntax trees
+and destination language linearizations of all possible parses of that
+sentence.
+
+
+LIBGU
+-----
+
+Along with libpgf proper, this distribution includes libgu, a
+general-purpose utility library that libpgf is based on. Libgu is usable
+independently of libpgf, and may eventually be split into a separate
+package. Do give it a try if you are looking for a library to make C
+programming less painful.
+
+
+DOCUMENTATION
+-------------
+
+Documentation is still fragmentary, but some of the most important
+headers have documentation comments. If you have Doxygen
+<http://doxygen.org/> installed, "make doxygen-doc" will generate HTML
+documentation for the library.
+
+The sources in utils/pgf-translate.c have some comments which may also
+clarify how to use the library.
+
+
+FEEDBACK
+--------
+
+Please report bugs to the Grammatical Framework bug tracker
+<https://code.google.com/p/grammatical-framework/issues/>.
+
+For general questions, comments and suggestions on libpgf, write to the
+GF mailing list at <mailto:gf-dev@googlegroups.com> or
+<https://groups.google.com/group/gf-dev>.
+
+For questions and comments that are related to the core libgu library,
+but not to PGF, please write directly to the author at
+<mailto:lealanko@ling.helsinki.fi>.
diff --git a/src/runtime/c/configure.ac b/src/runtime/c/configure.ac
new file mode 100644
index 000000000..ade514745
--- /dev/null
+++ b/src/runtime/c/configure.ac
@@ -0,0 +1,66 @@
+AC_INIT(Portable Grammar Format library, 0.1.pre,
+ https://code.google.com/p/grammatical-framework/,
+ libpgf)
+AC_PREREQ(2.58)
+
+AC_CONFIG_SRCDIR([gu/mem.c])
+AC_CONFIG_AUX_DIR([scripts])
+AC_CONFIG_MACRO_DIR([m4])
+
+AM_INIT_AUTOMAKE([1.7.9])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+PKG_PROG_PKG_CONFIG
+
+AC_CONFIG_HEADERS([config.h guconfig.h])
+
+AM_MAINTAINER_MODE([enable])
+AC_CHECK_LIB(m,nan)
+AC_PROG_MAKE_SET
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_PROG_CC
+AC_PROG_CC_C99
+AM_PROG_CC_C_O
+
+[if [ "x$GCC" = "xyes" ]; then
+ CFLAGS="$CFLAGS\
+ -Wall\
+ -Wextra\
+ -Wno-missing-field-initializers\
+ -Wno-unused-parameter"
+fi]
+
+
+AC_C_ALIGNOF
+AC_C_FAM_IN_MEM
+AC_C_STATEMENT_EXPRESSIONS
+
+m4_define([ORIG_DEFINE],m4_defn([AC_DEFINE]))
+m4_define([_ORIG_DEFINE],m4_defn([_AC_DEFINE]))
+m4_pushdef([AC_DEFINE],[ORIG_DEFINE([GU_$1],[$2],[$3])])
+AC_C_ASCII
+m4_popdef([AC_DEFINE])
+
+dnl Doxygen support
+DX_PS_FEATURE(OFF)
+DX_PDF_FEATURE(OFF)
+AC_DEFUN([DX_FEATURE_dev], OFF)
+DX_ARG_ABLE(dev, [include internal development documentation],
+ [],
+ [],
+ [],
+ [DX_ENV_APPEND(DEVDOC, YES)]
+ [DX_ENV_APPEND(INPUT, $srcdir)],
+ [DX_ENV_APPEND(DEVDOC, NO)
+ DX_ENV_APPEND(INPUT,
+ [\$(guinclude_HEADERS) \$(pgfinclude_HEADERS)])])
+DX_INIT_DOXYGEN(libpgf)
+
+
+AC_CONFIG_FILES([Makefile
+ libgu.pc
+ libpgf.pc
+ ])
+
+AC_OUTPUT
diff --git a/src/runtime/c/doxygen.am b/src/runtime/c/doxygen.am
new file mode 100644
index 000000000..ed935770b
--- /dev/null
+++ b/src/runtime/c/doxygen.am
@@ -0,0 +1,169 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html
+# ===========================================================================
+
+# LICENSE
+#
+# Copyright (c) 2009 Oren Ben-Kiki <oren@ben-kiki.org>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+## --------------------------------- ##
+## Format-independent Doxygen rules. ##
+## --------------------------------- ##
+
+if DX_COND_doc
+
+## ------------------------------- ##
+## Rules specific for HTML output. ##
+## ------------------------------- ##
+
+if DX_COND_html
+
+DX_CLEAN_HTML = @DX_DOCDIR@/html
+
+endif DX_COND_html
+
+## ------------------------------ ##
+## Rules specific for CHM output. ##
+## ------------------------------ ##
+
+if DX_COND_chm
+
+DX_CLEAN_CHM = @DX_DOCDIR@/chm
+
+if DX_COND_chi
+
+DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi
+
+endif DX_COND_chi
+
+endif DX_COND_chm
+
+## ------------------------------ ##
+## Rules specific for MAN output. ##
+## ------------------------------ ##
+
+if DX_COND_man
+
+DX_CLEAN_MAN = @DX_DOCDIR@/man
+
+endif DX_COND_man
+
+## ------------------------------ ##
+## Rules specific for RTF output. ##
+## ------------------------------ ##
+
+if DX_COND_rtf
+
+DX_CLEAN_RTF = @DX_DOCDIR@/rtf
+
+endif DX_COND_rtf
+
+## ------------------------------ ##
+## Rules specific for XML output. ##
+## ------------------------------ ##
+
+if DX_COND_xml
+
+DX_CLEAN_XML = @DX_DOCDIR@/xml
+
+endif DX_COND_xml
+
+## ----------------------------- ##
+## Rules specific for PS output. ##
+## ----------------------------- ##
+
+if DX_COND_ps
+
+DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps
+
+DX_PS_GOAL = doxygen-ps
+
+doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps
+
+@DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag
+ cd @DX_DOCDIR@/latex; \
+ rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+ $(DX_LATEX) refman.tex; \
+ $(MAKEINDEX_PATH) refman.idx; \
+ $(DX_LATEX) refman.tex; \
+ countdown=5; \
+ while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+ refman.log > /dev/null 2>&1 \
+ && test $$countdown -gt 0; do \
+ $(DX_LATEX) refman.tex; \
+ countdown=`expr $$countdown - 1`; \
+ done; \
+ $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi
+
+endif DX_COND_ps
+
+## ------------------------------ ##
+## Rules specific for PDF output. ##
+## ------------------------------ ##
+
+if DX_COND_pdf
+
+DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf
+
+DX_PDF_GOAL = doxygen-pdf
+
+doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf
+
+@DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag
+ cd @DX_DOCDIR@/latex; \
+ rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+ $(DX_PDFLATEX) refman.tex; \
+ $(DX_MAKEINDEX) refman.idx; \
+ $(DX_PDFLATEX) refman.tex; \
+ countdown=5; \
+ while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+ refman.log > /dev/null 2>&1 \
+ && test $$countdown -gt 0; do \
+ $(DX_PDFLATEX) refman.tex; \
+ countdown=`expr $$countdown - 1`; \
+ done; \
+ mv refman.pdf ../@PACKAGE@.pdf
+
+endif DX_COND_pdf
+
+## ------------------------------------------------- ##
+## Rules specific for LaTeX (shared for PS and PDF). ##
+## ------------------------------------------------- ##
+
+if DX_COND_latex
+
+DX_CLEAN_LATEX = @DX_DOCDIR@/latex
+
+endif DX_COND_latex
+
+.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag
+
+doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+@DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS)
+ rm -rf @DX_DOCDIR@
+ $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG)
+
+DX_CLEANFILES = \
+ @DX_DOCDIR@/@PACKAGE@.tag \
+ -r \
+ $(DX_CLEAN_HTML) \
+ $(DX_CLEAN_CHM) \
+ $(DX_CLEAN_CHI) \
+ $(DX_CLEAN_MAN) \
+ $(DX_CLEAN_RTF) \
+ $(DX_CLEAN_XML) \
+ $(DX_CLEAN_PS) \
+ $(DX_CLEAN_PDF) \
+ $(DX_CLEAN_LATEX)
+
+endif DX_COND_doc
diff --git a/src/runtime/c/gfcc-term.c b/src/runtime/c/gfcc-term.c
deleted file mode 100644
index b427479e6..000000000
--- a/src/runtime/c/gfcc-term.c
+++ /dev/null
@@ -1,203 +0,0 @@
-#include "gfcc-term.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-static void *buffer = NULL;
-static size_t current;
-
-extern void term_alloc_pool(size_t size) {
- if (buffer == NULL)
- buffer = malloc(size);
- current = 0;
-}
-
-extern void term_free_pool() {
- if (buffer != NULL)
- free(buffer);
- buffer = NULL;
-}
-
-extern void *term_alloc(size_t size) {
- void *off = buffer + current;
- current += size;
- return off;
-}
-
-static inline Term *create_term(TermType type, int n) {
- Term *t = (Term*)term_alloc(sizeof(Term) + n * sizeof(Term *));
- t->type = type;
- t->value.size = n; /* FIXME: hack! */
- return t;
-}
-
-extern Term *term_array(int n, ...) {
- Term *t = create_term(TERM_ARRAY, n);
- va_list ap;
- int i;
-
- va_start(ap, n);
- for (i = 0; i < n; i++) {
- term_set_child(t, i, va_arg(ap, Term *));
- }
- va_end(ap);
-
- return t;
-}
-
-extern Term *term_seq(int n, ...) {
- Term *t = create_term(TERM_SEQUENCE, n);
- va_list ap;
- int i;
-
- va_start(ap, n);
- for (i = 0; i < n; i++) {
- term_set_child(t, i, va_arg(ap, Term *));
- }
- va_end(ap);
-
- return t;
-}
-
-extern Term *term_variants(int n, ...) {
- Term *t = create_term(TERM_VARIANTS, n);
- va_list ap;
- int i;
-
- va_start(ap, n);
- for (i = 0; i < n; i++) {
- term_set_child(t, i, va_arg(ap, Term *));
- }
- va_end(ap);
-
- return t;
-}
-
-extern Term *term_glue(int n, ...) {
- Term *t = create_term(TERM_GLUE, n);
- va_list ap;
- int i;
-
- va_start(ap, n);
- for (i = 0; i < n; i++) {
- term_set_child(t, i, va_arg(ap, Term *));
- }
- va_end(ap);
-
- return t;
-}
-
-extern Term *term_rp(Term *t1, Term *t2) {
- Term *t = create_term(TERM_RECORD_PARAM, 2);
- term_set_child(t, 0, t1);
- term_set_child(t, 1, t2);
- return t;
-}
-
-extern Term *term_suffix(const char *pref, Term *suf) {
- Term *t = create_term(TERM_SUFFIX_TABLE, 2);
- term_set_child(t,0,term_str(pref));
- term_set_child(t,1,suf);
- return t;
-}
-
-extern Term *term_str(const char *s) {
- Term *t = create_term(TERM_STRING, 0);
- t->value.string_value = s;
- return t;
-}
-
-extern Term *term_int(int i) {
- Term *t = create_term(TERM_INTEGER,0);
- t->value.integer_value = i;
- return t;
-}
-
-extern Term *term_meta() {
- return create_term(TERM_META, 0);
-}
-
-
-
-extern Term *term_sel_int(Term *t, int i) {
- switch (t->type) {
- case TERM_ARRAY:
- return term_get_child(t,i);
- case TERM_SUFFIX_TABLE:
- return term_glue(2,
- term_get_child(t,0),
- term_sel_int(term_get_child(t,1),i));
- case TERM_META:
- return t;
- default:
- fprintf(stderr,"Error: term_sel_int %d %d\n", t->type, i);
- exit(1);
- return NULL;
- }
-}
-
-extern Term *term_sel(Term *t1, Term *t2) {
- switch (t2->type) {
- case TERM_INTEGER:
- return term_sel_int(t1, t2->value.integer_value);
- case TERM_RECORD_PARAM:
- return term_sel(t1,term_get_child(t2,0));
- case TERM_META:
- return term_sel_int(t1,0);
- default:
- fprintf(stderr,"Error: term_sel %d %d\n", t1->type, t2->type);
- exit(1);
- return 0;
- }
-}
-
-
-
-static void term_print_sep(FILE *stream, Term *t, const char *sep) {
- int n = t->value.size;
- int i;
-
- for (i = 0; i < n; i++) {
- term_print(stream, term_get_child(t,i));
- if (i < n-1) {
- fputs(sep, stream);
- }
- }
-}
-
-extern void term_print(FILE *stream, Term *t) {
- switch (t->type) {
- case TERM_ARRAY:
- term_print(stream, term_get_child(t,0));
- break;
- case TERM_SEQUENCE:
- term_print_sep(stream, t, " ");
- break;
- case TERM_VARIANTS:
- term_print_sep(stream, t, "/");
- break;
- case TERM_GLUE:
- term_print_sep(stream, t, "");
- break;
- case TERM_RECORD_PARAM:
- term_print(stream, term_get_child(t,0));
- break;
- case TERM_SUFFIX_TABLE:
- term_print(stream, term_get_child(t,0));
- term_print(stream, term_get_child(t,1));
- break;
- case TERM_META:
- fputs("?", stream);
- break;
- case TERM_STRING:
- fputs(t->value.string_value, stream);
- break;
- case TERM_INTEGER:
- fprintf(stream, "%d", t->value.integer_value);
- break;
- default:
- fprintf(stderr,"Error: term_print %d\n", t->type);
- exit(1);
- }
-}
diff --git a/src/runtime/c/gfcc-term.h b/src/runtime/c/gfcc-term.h
deleted file mode 100644
index d1307259d..000000000
--- a/src/runtime/c/gfcc-term.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef GFCC_TERM_H
-#define GFCC_TERM_H
-
-#include <stdio.h>
-
-typedef enum {
- /* size = variable */
- TERM_ARRAY,
- TERM_SEQUENCE,
- TERM_VARIANTS,
- TERM_GLUE,
- /* size = 2 */
- TERM_RECORD_PARAM,
- TERM_SUFFIX_TABLE,
- /* size = 0 */
- TERM_META,
- TERM_STRING,
- TERM_INTEGER
-} TermType;
-
-struct Term_ {
- TermType type;
- union {
- const char *string_value;
- int integer_value;
- int size;
- } value;
- struct Term_ *args[0];
-};
-
-typedef struct Term_ Term;
-
-
-
-static inline Term *term_get_child(Term *t, int n) {
- return t->args[n];
-}
-
-static inline void term_set_child(Term *t, int n, Term *c) {
- t->args[n] = c;
-}
-
-extern void term_alloc_pool(size_t size);
-extern void term_free_pool();
-extern void *term_alloc(size_t size);
-
-
-extern Term *term_array(int n, ...);
-extern Term *term_seq(int n, ...);
-extern Term *term_variants(int n, ...);
-extern Term *term_glue(int n, ...);
-
-extern Term *term_rp(Term *t1, Term *t2);
-extern Term *term_suffix(const char *pref, Term *suf);
-extern Term *term_str(const char *s);
-extern Term *term_int(int i);
-extern Term *term_meta();
-
-extern Term *term_sel_int(Term *t, int i);
-extern Term *term_sel(Term *t1, Term *t2);
-
-
-extern void term_print(FILE *stream, Term *t);
-
-#endif
diff --git a/src/runtime/c/gfcc-tree.c b/src/runtime/c/gfcc-tree.c
deleted file mode 100644
index 6cd8759be..000000000
--- a/src/runtime/c/gfcc-tree.c
+++ /dev/null
@@ -1,61 +0,0 @@
-#include "gfcc-tree.h"
-
-#include <stdlib.h>
-
-
-extern int arity(Tree *t) {
- switch (t->type) {
- case ATOM_STRING:
- case ATOM_INTEGER:
- case ATOM_DOUBLE:
- case ATOM_META:
- return 0;
- default:
- return t->value.size;
- }
-}
-
-static Tree *create_tree(atom_type c, int n) {
- Tree *t = (Tree *)malloc(sizeof(Tree) + n * sizeof(Tree *));
- t->type = c;
- return t;
-}
-
-extern Tree *tree_string(const char *s) {
- Tree *t = create_tree(ATOM_STRING, 0);
- t->value.string_value = s;
- return t;
-}
-
-extern Tree *tree_integer(int i) {
- Tree *t = create_tree(ATOM_INTEGER, 0);
- t->value.integer_value = i;
- return t;
-}
-
-extern Tree *tree_double(double d) {
- Tree *t = create_tree(ATOM_DOUBLE, 0);
- t->value.double_value = d;
- return t;
-}
-
-extern Tree *tree_meta() {
- return create_tree(ATOM_META, 0);
-}
-
-extern Tree *tree_fun(atom_type f, int n) {
- Tree *t = create_tree(f, n);
- t->value.size = n;
- return t;
-}
-
-
-extern void tree_free(Tree *t) {
- int n = arity(t);
- int i;
-
- for (i = 0; i < n; i++) {
- tree_free(tree_get_child(t,i));
- }
- free(t);
-}
diff --git a/src/runtime/c/gfcc-tree.h b/src/runtime/c/gfcc-tree.h
deleted file mode 100644
index cc8f0fcab..000000000
--- a/src/runtime/c/gfcc-tree.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef GFCC_TREE_H
-#define GFCC_TREE_H
-
-typedef enum {
- ATOM_STRING,
- ATOM_INTEGER,
- ATOM_DOUBLE,
- ATOM_META,
- ATOM_FIRST_FUN
-} atom_type;
-
-struct Tree_{
- atom_type type;
- union {
- const char *string_value;
- int integer_value;
- double double_value;
- int size;
- } value;
- struct Tree_ *args[0];
-};
-
-typedef struct Tree_ Tree;
-
-static inline Tree *tree_get_child(Tree *t, int n) {
- return t->args[n];
-}
-
-static inline void tree_set_child(Tree *t, int n, Tree *a) {
- t->args[n] = a;
-}
-
-extern int arity(Tree *t);
-
-
-extern Tree *tree_string(const char *s);
-
-extern Tree *tree_integer(int i);
-
-extern Tree *tree_double(double d);
-
-extern Tree *tree_meta();
-
-extern Tree *tree_fun(atom_type f, int n);
-
-
-extern void tree_free(Tree *t);
-
-#endif
diff --git a/src/runtime/c/gu/assert.c b/src/runtime/c/gu/assert.c
new file mode 100644
index 000000000..e111912d3
--- /dev/null
+++ b/src/runtime/c/gu/assert.c
@@ -0,0 +1,53 @@
+#include <gu/assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+const char*
+gu_assert_mode_descs[] = {
+ [GU_ASSERT_PRECOND] = "precondition failed",
+ [GU_ASSERT_POSTCOND] = "postcondition failed",
+ [GU_ASSERT_ASSERTION] = "assertion failed",
+ [GU_ASSERT_NEVER] = "control should not reach here",
+};
+
+void
+gu_abort_v_(GuAssertMode mode,
+ const char* file, const char* func, int line,
+ const char* msg_fmt, va_list args)
+{
+ const char* desc = gu_assert_mode_descs[mode];
+ (void) fprintf(stderr, "%s (%s:%d): %s\n", func, file, line, desc);
+ if (msg_fmt != NULL) {
+ (void) fputc('\t', stderr);
+ (void) vfprintf(stderr, msg_fmt, args);
+ (void) fputc('\n', stderr);
+ }
+ abort();
+}
+
+void
+gu_abort_(GuAssertMode mode,
+ const char* file, const char* func, int line,
+ const char* msg_fmt, ...)
+{
+ va_list args;
+ va_start(args, msg_fmt);
+ gu_abort_v_(mode, file, func, line, msg_fmt, args);
+ va_end(args);
+}
+
+
+void
+gu_fatal(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fputs("Fatal error", stderr);
+ if (fmt) {
+ fputs(": ", stderr);
+ (void) vfprintf(stderr, fmt, args);
+ }
+ fputc('\n', stderr);
+ abort();
+}
diff --git a/src/runtime/c/gu/assert.h b/src/runtime/c/gu/assert.h
new file mode 100644
index 000000000..9d7ecc15f
--- /dev/null
+++ b/src/runtime/c/gu/assert.h
@@ -0,0 +1,61 @@
+#ifndef GU_ASSERT_H_
+#define GU_ASSERT_H_
+
+#include <gu/defs.h>
+
+typedef enum {
+ GU_ASSERT_PRECOND,
+ GU_ASSERT_ASSERTION,
+ GU_ASSERT_POSTCOND,
+ GU_ASSERT_NEVER
+} GuAssertMode;
+
+void
+gu_abort_v_(GuAssertMode mode,
+ const char* file, const char* func, int line,
+ const char* msg_fmt, va_list args);
+
+void
+gu_abort_(GuAssertMode mode,
+ const char* file, const char* func, int line,
+ const char* msg_fmt, ...);
+
+#ifndef NDEBUG
+#define gu_assertion_(mode_, expr_, ...) \
+ GU_BEGIN \
+ if (!(expr_)) { \
+ gu_abort_(mode_, __FILE__, __func__, __LINE__, __VA_ARGS__); \
+ } \
+ GU_END
+#else
+// this should prevent unused variable warnings when a variable is only used
+// in an assertion
+#define gu_assertion_(mode_, expr_, ...) \
+ GU_BEGIN \
+ (void) (sizeof (expr_)); \
+ GU_END
+#endif
+
+
+#define gu_require(expr) \
+ gu_assertion_(GU_ASSERT_PRECOND, expr, "%s", #expr)
+
+#define gu_assert_msg(expr, ...) \
+ gu_assertion_(GU_ASSERT_ASSERTION, expr, __VA_ARGS__)
+
+#define gu_assert(expr) \
+ gu_assert_msg(expr, "%s", #expr)
+
+#define gu_ensure(expr) \
+ gu_assertion_(GU_ASSERT_POSTCOND, expr, "%s", #expr)
+
+#define gu_impossible_msg(...) \
+ gu_assertion_(GU_ASSERT_ASSERTION, false, __VA_ARGS__)
+
+#define gu_impossible() \
+ gu_impossible_msg(NULL)
+
+void
+gu_fatal(const char* fmt, ...);
+
+#endif /* GU_ASSERT_H_ */
diff --git a/src/runtime/c/gu/bits.c b/src/runtime/c/gu/bits.c
new file mode 100644
index 000000000..9126b0448
--- /dev/null
+++ b/src/runtime/c/gu/bits.c
@@ -0,0 +1,53 @@
+#include <gu/bits.h>
+
+#include <limits.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+unsigned gu_ceil2e(unsigned u)
+{
+ u--;
+ u |= u >> 1;
+ u |= u >> 2;
+ u |= u >> 4;
+ u |= u >> 8;
+#if UINT_MAX > UINT16_MAX
+ u |= u >> 16;
+#endif
+#if UINT_MAX > UINT32_MAX
+ u |= u >> 32;
+#endif
+ u++;
+ return u;
+}
+
+GU_DEFINE_TYPE(GuIntDecodeExn, abstract, _);
+
+double
+gu_decode_double(uint64_t u)
+{
+ bool sign = u >> 63;
+ unsigned rawexp = u >> 52 & 0x7ff;
+ uint64_t mantissa = u & 0xfffffffffffff;
+ double ret;
+
+ if (rawexp == 0x7ff) {
+ if (mantissa == 0) {
+ ret = INFINITY;
+ } else {
+ // At least glibc supports specifying the
+ // mantissa like this.
+ int len = snprintf(NULL, 0, "0x%" PRIx64, mantissa);
+ char buf[len + 1];
+ snprintf(buf, len + 1, "0x%" PRIx64, mantissa);
+ ret = nan(buf);
+ }
+ } else {
+ uint64_t m = rawexp ? 1ULL << 52 | mantissa : mantissa << 1;
+ ret = ldexp((double) m, rawexp - 1075);
+ }
+ return sign ? copysign(ret, -1.0) : ret;
+}
diff --git a/src/runtime/c/gu/bits.h b/src/runtime/c/gu/bits.h
new file mode 100644
index 000000000..9ba1b0c8e
--- /dev/null
+++ b/src/runtime/c/gu/bits.h
@@ -0,0 +1,151 @@
+#ifndef GU_BITS_H_
+#define GU_BITS_H_
+
+#include <gu/defs.h>
+#include <gu/assert.h>
+
+
+#define GU_WORD_BITS (sizeof(GuWord) * CHAR_BIT)
+
+
+/*
+ * Based on the Bit Twiddling Hacks collection by Sean Eron Anderson
+ * <http://graphics.stanford.edu/~seander/bithacks.html>
+ */
+
+unsigned gu_ceil2e(unsigned i);
+
+static inline int
+gu_sign(int i) {
+ return (i > 0) - (i < 0);
+}
+
+static inline size_t
+gu_ceildiv(size_t size, size_t div)
+{
+ return (size + div - 1) / div;
+}
+
+static inline bool
+gu_aligned(uintptr_t addr, size_t alignment)
+{
+ gu_require(alignment == gu_ceil2e(alignment));
+ return (addr & (alignment - 1)) == 0;
+}
+
+static inline uintptr_t
+gu_align_forward(uintptr_t addr, size_t alignment) {
+ gu_require(alignment == gu_ceil2e(alignment));
+ uintptr_t mask = alignment - 1;
+ return (addr + mask) & ~mask;
+}
+
+static inline uintptr_t
+gu_align_backward(uintptr_t addr, size_t alignment) {
+ gu_require(alignment == gu_ceil2e(alignment));
+ return addr & ~(alignment - 1);
+}
+
+static inline bool
+gu_bits_test(const GuWord* bitmap, int idx) {
+ return !!(bitmap[idx / GU_WORD_BITS] & 1 << (idx % GU_WORD_BITS));
+}
+
+static inline void
+gu_bits_set(GuWord* bitmap, int idx) {
+ bitmap[idx / GU_WORD_BITS] |= ((GuWord) 1) << (idx % GU_WORD_BITS);
+}
+
+static inline void
+gu_bits_clear(GuWord* bitmap, int idx) {
+ bitmap[idx / GU_WORD_BITS] &= ~(((GuWord) 1) << (idx % GU_WORD_BITS));
+}
+
+static inline size_t
+gu_bits_size(size_t n_bits) {
+ return gu_ceildiv(n_bits, GU_WORD_BITS) * sizeof(GuWord);
+}
+
+static inline void*
+gu_word_ptr(GuWord w)
+{
+ return (void*) w;
+}
+
+static inline GuWord
+gu_ptr_word(void* p)
+{
+ return (GuWord) p;
+}
+
+#define GuOpaque() struct { GuWord w_; }
+
+typedef GuWord GuTagged;
+
+#define GU_TAG_MAX (sizeof(GuWord) - 1)
+
+static inline size_t
+gu_tagged_tag(GuTagged t) {
+ return (int) (t & (sizeof(GuWord) - 1));
+}
+
+static inline void*
+gu_tagged_ptr(GuTagged w) {
+ return (void*) gu_align_backward(w, sizeof(GuWord));
+}
+
+static inline GuTagged
+gu_tagged(void* ptr, size_t tag) {
+ gu_require(tag < sizeof(GuWord));
+ uintptr_t u = (uintptr_t) ptr;
+ gu_require(gu_align_backward(u, sizeof(GuWord)) == u);
+ return (GuWord) { u | tag };
+}
+
+#include <gu/exn.h>
+#include <gu/type.h>
+
+extern GU_DECLARE_TYPE(GuIntDecodeExn, abstract);
+
+#define GU_DECODE_2C_(u_, t_, umax_, posmax_, tmin_, err_) \
+ (((u_) <= (posmax_)) \
+ ? (t_) (u_) \
+ : (tmin_) + ((t_) ((umax_) - (u_))) < 0 \
+ ? -1 - ((t_) ((umax_) - (u_))) \
+ : (gu_raise(err_, GuIntDecodeExn), -1))
+
+
+static inline int8_t
+gu_decode_2c8(uint8_t u, GuExn* err)
+{
+ return GU_DECODE_2C_(u, int8_t, UINT8_C(0xff),
+ INT8_C(0x7f), INT8_MIN, err);
+}
+
+static inline int16_t
+gu_decode_2c16(uint16_t u, GuExn* err)
+{
+ return GU_DECODE_2C_(u, int16_t, UINT16_C(0xffff),
+ INT16_C(0x7fff), INT16_MIN, err);
+}
+
+static inline int32_t
+gu_decode_2c32(uint32_t u, GuExn* err)
+{
+ return GU_DECODE_2C_(u, int32_t, UINT32_C(0xffffffff),
+ INT32_C(0x7fffffff), INT32_MIN, err);
+}
+
+static inline int64_t
+gu_decode_2c64(uint64_t u, GuExn* err)
+{
+ return GU_DECODE_2C_(u, int64_t, UINT64_C(0xffffffffffffffff),
+ INT64_C(0x7fffffffffffffff), INT64_MIN, err);
+}
+
+double
+gu_decode_double(uint64_t u);
+
+
+
+#endif // GU_BITS_H_
diff --git a/src/runtime/c/gu/choice.c b/src/runtime/c/gu/choice.c
new file mode 100644
index 000000000..b1e4a3a5e
--- /dev/null
+++ b/src/runtime/c/gu/choice.c
@@ -0,0 +1,73 @@
+#include <gu/choice.h>
+#include <gu/seq.h>
+#include <gu/assert.h>
+#include <gu/log.h>
+
+struct GuChoice {
+ GuBuf* path;
+ size_t path_idx;
+};
+
+GuChoice*
+gu_new_choice(GuPool* pool)
+{
+ GuChoice* ch = gu_new(GuChoice, pool);
+ ch->path = gu_new_buf(uint8_t, pool);
+ ch->path_idx = 0;
+ return ch;
+}
+
+GuChoiceMark
+gu_choice_mark(GuChoice* ch)
+{
+ gu_assert(ch->path_idx <= gu_buf_length(ch->path));
+ gu_debug("%p@%d: mark", ch, ch->path_idx);
+ return (GuChoiceMark){ch->path_idx};
+}
+
+void
+gu_choice_reset(GuChoice* ch, GuChoiceMark mark)
+{
+ gu_assert(ch->path_idx <= gu_buf_length(ch->path));
+ gu_debug("%p@%d: reset %d", ch, ch->path_idx, mark.path_idx);
+ gu_require(mark.path_idx <= ch->path_idx );
+ ch->path_idx = mark.path_idx;
+}
+
+int
+gu_choice_next(GuChoice* ch, int n_choices)
+{
+ gu_assert(n_choices >= 0);
+ gu_require(n_choices <= UINT8_MAX);
+ gu_assert(ch->path_idx <= gu_buf_length(ch->path));
+ if (n_choices == 0) {
+ return -1;
+ }
+ int i = 0;
+ if (gu_buf_length(ch->path) > ch->path_idx) {
+ i = (int) gu_buf_get(ch->path, uint8_t, ch->path_idx);
+ gu_assert(i <= n_choices);
+ } else {
+ gu_buf_push(ch->path, uint8_t, n_choices);
+ i = n_choices;
+ }
+ int ret = (i == 0) ? -1 : n_choices - i;
+ gu_debug("%p@%d: %d", ch, ch->path_idx, ret);
+ ch->path_idx++;
+ return ret;
+}
+
+bool
+gu_choice_advance(GuChoice* ch)
+{
+ gu_assert(ch->path_idx <= gu_buf_length(ch->path));
+
+ while (gu_buf_length(ch->path) > ch->path_idx) {
+ uint8_t last = gu_buf_pop(ch->path, uint8_t);
+ if (last > 1) {
+ gu_buf_push(ch->path, uint8_t, last-1);
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/runtime/c/gu/choice.h b/src/runtime/c/gu/choice.h
new file mode 100644
index 000000000..aea76dde8
--- /dev/null
+++ b/src/runtime/c/gu/choice.h
@@ -0,0 +1,37 @@
+#ifndef GU_CHOICE_H_
+#define GU_CHOICE_H_
+
+#include <gu/mem.h>
+
+typedef struct GuChoice GuChoice;
+
+typedef struct GuChoiceMark GuChoiceMark;
+
+GuChoice*
+gu_new_choice(GuPool* pool);
+
+int
+gu_choice_next(GuChoice* ch, int n_choices);
+
+GuChoiceMark
+gu_choice_mark(GuChoice* ch);
+
+void
+gu_choice_reset(GuChoice* ch, GuChoiceMark mark);
+
+bool
+gu_choice_advance(GuChoice* ch);
+
+
+// private
+
+struct GuChoiceMark {
+ size_t path_idx;
+};
+
+
+
+
+
+
+#endif // GU_CHOICE_H_
diff --git a/src/runtime/c/gu/defs.c b/src/runtime/c/gu/defs.c
new file mode 100644
index 000000000..2c3772b7c
--- /dev/null
+++ b/src/runtime/c/gu/defs.c
@@ -0,0 +1,4 @@
+#include <gu/defs.h>
+
+void* const gu_null = NULL;
+GuStruct* const gu_null_struct = NULL;
diff --git a/src/runtime/c/gu/defs.h b/src/runtime/c/gu/defs.h
new file mode 100644
index 000000000..9fa62b39b
--- /dev/null
+++ b/src/runtime/c/gu/defs.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libgu.
+ *
+ * Libgu is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libgu is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libgu. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file
+ *
+ * Miscellaneous macros.
+ */
+
+#ifndef GU_DEFS_H_
+#define GU_DEFS_H_
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <gu/sysdeps.h>
+
+#define gu_container(mem_p, container_type, member) \
+ ((container_type*)(((uint8_t*) (mem_p)) - offsetof(container_type, member)))
+/**< Find the address of a containing structure.
+ *
+ * If @c s has type @c t*, where @c t is a struct or union type with a
+ * member @m, then <tt>GU_CONTAINER_P(&s->m, t, m) == s</tt>.
+ *
+ * @param mem_p Pointer to the member of a structure.
+ * @param container_type The type of the containing structure.
+ * @param member The name of the member of @a container_type
+ * @return The address of the containing structure.
+ *
+ * @hideinitializer */
+
+
+#define gu_member_p(struct_p_, offset_) \
+ ((void*)&((uint8_t*)(struct_p_))[offset_])
+
+#define gu_member(t_, struct_p_, offset_) \
+ (*(t_*)gu_member_p(struct_p_, offset_))
+
+#ifdef GU_ALIGNOF
+# define gu_alignof GU_ALIGNOF
+# define GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
+#else
+# define gu_alignof(t_) \
+ ((size_t)(offsetof(struct { char c_; t_ e_; }, e_)))
+# ifdef GU_CAN_HAVE_FAM_IN_MEMBER
+# define GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
+# endif
+#endif
+
+#define GU_PLIT(type, expr) \
+ ((type[1]){ expr })
+
+#define GU_LVALUE(type, expr) \
+ (*((type[1]){ expr }))
+
+#define GU_COMMA ,
+
+#define GU_ARRAY_LEN(t,a) (sizeof((const t[])a) / sizeof(t))
+
+#define GU_ID(...) __VA_ARGS__
+
+// This trick is by Laurent Deniau <laurent.deniau@cern.ch>
+#define GU_N_ARGS(...) \
+ GU_N_ARGS_(__VA_ARGS__, \
+ 31,30,29,28,27,26,25,24, \
+ 23,22,21,20,19,18,17,16, \
+ 15,14,13,12,11,10,9,8, \
+ 7,6,5,4,3,2,1,0)
+#define GU_N_ARGS_(...) GU_N_ARGS__(__VA_ARGS__)
+#define GU_N_ARGS__(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p, \
+ q,r,s,t,u,v,w,x,y,z,aa,ab,ac,ad,ae,N,...) \
+ N
+
+#define GU_ARG1(a1, ...) a1
+#define GU_ARG2(a1, a2, ...) a2
+
+#define GU_BEGIN do {
+#define GU_END } while (false)
+
+#define GU_NOP GU_BEGIN (void) 0; GU_END
+
+/**< @hideinitializer */
+
+//
+// Assert
+//
+
+#define GU_MAX(a_, b_) ((a_) > (b_) ? (a_) : (b_))
+#define GU_MIN(a_, b_) ((a_) < (b_) ? (a_) : (b_))
+
+static inline int
+gu_max(int a, int b) {
+ return GU_MAX(a, b);
+}
+
+static inline int
+gu_min(int a, int b) {
+ return GU_MIN(a, b);
+}
+
+#ifdef GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
+#define gu_flex_alignof gu_alignof
+#else
+#define gu_flex_alignof(t) 0
+#endif
+
+static inline size_t
+gu_flex_size(size_t ssize, size_t offset, int n_elems, size_t e_size)
+{
+ return GU_MAX(ssize, offset + n_elems * e_size);
+}
+
+#define GU_FLEX_SIZE(type, flex_member, n_elems) \
+ gu_flex_size(sizeof(type), offsetof(type, flex_member), \
+ n_elems, sizeof(((type*)NULL)->flex_member[0]))
+
+
+// The following are directly from gmacros.h in GLib
+
+#define GU_PASTE_ARGS(id1_,id2_) \
+ id1_ ## id2_
+
+#define GU_PASTE(id1_, id2_) \
+ GU_PASTE_ARGS(id1_, id2_)
+
+#define GU_STATIC_ASSERT(expr_) \
+ typedef struct { \
+ char static_assert[(expr_) ? 1 : -1]; \
+ } GU_PASTE(GuStaticAssert_, __LINE__)
+
+
+#define GU_ENSURE_TYPE(T, EXPR) \
+ ((void)(sizeof(*(T*)NULL=(EXPR))),(EXPR))
+
+#define GU_END_DECLS \
+ extern void gu_dummy_(void)
+
+extern void* const gu_null;
+
+// Dummy struct used for generic struct pointers
+typedef struct GuStruct GuStruct;
+
+extern GuStruct* const gu_null_struct;
+
+typedef uintptr_t GuWord;
+
+#define GU_WORD_MAX UINTPTR_MAX
+
+// TODO: use max_align_t once C1X is supported
+typedef union {
+ char c;
+ short s;
+ int i;
+ long l;
+ long long ll;
+ intmax_t im;
+ float f;
+ double d;
+ long double ld;
+ void* p;
+ void (*fp)();
+} GuMaxAlign;
+
+#define gu_alloca(N) \
+ (((union { GuMaxAlign align_; uint8_t buf_[N]; }){{0}}).buf_)
+
+
+// For Doxygen
+#define GU_PRIVATE /** @private */
+
+#ifdef GU_GNUC
+# define GU_LIKELY(EXPR) __builtin_expect(EXPR, 1)
+# define GU_UNLIKELY(EXPR) __builtin_expect(EXPR, 0)
+# define GU_IS_CONSTANT(EXPR) __builtin_constant_p(EXPR)
+#else
+# define GU_LIKELY(EXPR) (EXPR)
+# define GU_UNLIKELY(EXPR) (EXPR)
+# ifdef GU_OPTIMIZE_SIZE
+# define GU_IS_CONSTANT(EXPR) false
+# else
+# define GU_IS_CONSTANT(EXPR) true
+# endif
+#endif
+
+// Splint annotations
+#define GU_ONLY GU_SPLINT(only)
+#define GU_NULL GU_SPLINT(null)
+#define GU_NOTNULL GU_SPLINT(notnull)
+#define GU_RETURNED GU_SPLINT(returned)
+#define GU_ABSTRACT GU_SPLINT(abstract)
+#define GU_IMMUTABLE GU_SPLINT(immutable)
+#define GU_NOTREACHED GU_SPLINT(notreached)
+#define GU_UNUSED GU_SPLINT(unused) GU_GNUC_ATTR(unused)
+#define GU_OUT GU_SPLINT(out)
+#define GU_IN GU_SPLINT(in)
+#define GU_NORETURN GU_SPLINT(noreturn) GU_GNUC_ATTR(noreturn)
+#define GU_MODIFIES(x) GU_SPLINT(modifies x)
+
+#endif // GU_DEFS_H_
diff --git a/src/runtime/c/gu/dump.c b/src/runtime/c/gu/dump.c
new file mode 100644
index 000000000..0329f5932
--- /dev/null
+++ b/src/runtime/c/gu/dump.c
@@ -0,0 +1,411 @@
+#include <gu/dump.h>
+#include <gu/list.h>
+#include <gu/variant.h>
+#include <gu/seq.h>
+#include <gu/assert.h>
+#include <gu/str.h>
+#include <gu/file.h>
+
+GuDump*
+gu_new_dump(GuWriter* wtr, GuTypeTable* dumpers, GuExn* err, GuPool* pool)
+{
+ GuDump* ctx = gu_new(GuDump, pool);
+ ctx->pool = pool;
+ if (dumpers == NULL) {
+ dumpers = &gu_dump_table;
+ }
+ ctx->dumpers = gu_new_type_map(dumpers, pool);
+ ctx->yaml = gu_new_yaml(wtr, err, pool);
+ ctx->data = gu_new_addr_map(void, void*, &gu_null, pool);
+ ctx->print_address = false;
+ return ctx;
+}
+
+void
+gu_dump(GuType* type, const void* value, GuDump* ctx)
+{
+ GuDumpFn* dumper = gu_type_map_get(ctx->dumpers, type);
+ if (ctx->print_address) {
+ GuPool* pool = gu_new_pool();
+ GuString s = gu_format_string(pool, "%p", value);
+ gu_yaml_comment(ctx->yaml, s);
+ gu_pool_free(pool);
+ }
+ (*dumper)(dumper, type, value, ctx);
+}
+
+void
+gu_dump_stderr(GuType* type, const void* value, GuExn* err)
+{
+ GuPool* pool = gu_new_pool();
+ GuOut* out = gu_file_out(stderr, pool);
+#if 0
+ GuWriter* wtr = gu_locale_writer(out, pool);
+#else
+ GuWriter* wtr = gu_new_utf8_writer(out, pool);
+#endif
+ GuDump* ctx = gu_new_dump(wtr, NULL, err, pool);
+ gu_dump(type, value, ctx);
+ gu_pool_free(pool);
+}
+
+static void
+gu_dump_scalar(GuDump* ctx, const char* fmt, ...)
+{
+ GuPool* tmp_pool = gu_local_pool();
+ va_list args;
+ va_start(args, fmt);
+ GuString s = gu_format_string_v(fmt, args, tmp_pool);
+ va_end(args);
+ gu_yaml_scalar(ctx->yaml, s);
+ gu_pool_free(tmp_pool);
+}
+
+static void
+gu_dump_str_scalar(GuDump* ctx, const char* str)
+{
+ GuPool* tmp_pool = gu_local_pool();
+ GuString s = gu_str_string(str, tmp_pool);
+ gu_yaml_scalar(ctx->yaml, s);
+ gu_pool_free(tmp_pool);
+}
+
+static void
+gu_dump_null(GuDump* ctx)
+{
+ gu_yaml_tag_secondary(ctx->yaml, "null");
+ gu_yaml_scalar(ctx->yaml, gu_empty_string);
+}
+
+static void
+gu_dump_int(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ (void) type;
+ const int* ip = p;
+ gu_dump_scalar(ctx, "%d", *ip);
+}
+
+static void
+gu_dump_uint16(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ (void) type;
+ const uint16_t* ip = p;
+ gu_dump_scalar(ctx, "%" PRIu16, *ip);
+}
+
+static void
+gu_dump_size(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) (dumper && type);
+ const size_t* zp = p;
+ gu_dump_scalar(ctx, "%zu", *zp);
+}
+
+
+
+static void
+gu_dump_double(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ (void) type;
+ const double* dp = p;
+ gu_dump_scalar(ctx, "%lf", *dp);
+}
+
+static const char gu_dump_length_key[] = "gu_dump_length_key";
+
+static void
+gu_dump_length(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ (void) type;
+ const GuLength* ip = p;
+ gu_dump_scalar(ctx, "%d", *ip);
+ GuLength* lenp = gu_map_get(ctx->data, gu_dump_length_key, void*);
+ if (lenp != NULL) {
+ *lenp = *ip;
+ }
+}
+
+static void
+gu_dump_str(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ (void) type;
+ const GuStr* sp = p;
+ gu_dump_str_scalar(ctx, *sp);
+}
+
+static void
+gu_dump_string(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ (void) type;
+ const GuString* sp = p;
+ gu_yaml_scalar(ctx->yaml, *sp);
+}
+
+
+// For _non-shared_ pointers.
+static void
+gu_dump_pointer(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ GuPointerType* ptype = (GuPointerType*) type;
+ void* const* pp = p;
+ if (*pp == NULL) {
+ gu_dump_null(ctx);
+ } else {
+ gu_dump(ptype->pointed_type, *pp, ctx);
+ }
+}
+
+typedef struct {
+ GuMapItor itor;
+ GuMapType* mtype;
+ GuDump* ctx;
+} GuDumpMapFn;
+
+static void
+gu_dump_map_itor(GuMapItor* self, const void* key, void* value, GuExn* err)
+{
+ (void) err;
+ GuDumpMapFn* clo = (GuDumpMapFn*) self;
+ gu_dump(clo->mtype->key_type, key, clo->ctx);
+ gu_dump(clo->mtype->value_type, value, clo->ctx);
+}
+
+static void
+gu_dump_map(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ GuMapType* mtype = (GuMapType*) type;
+ GuMap* map = (GuMap*) p;
+ gu_yaml_begin_mapping(ctx->yaml);
+ GuDumpMapFn clo = { { gu_dump_map_itor }, mtype, ctx };
+ gu_map_iter(map, &clo.itor, NULL);
+ gu_yaml_end(ctx->yaml);
+}
+
+
+static void
+gu_dump_struct(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ GuStructRepr* srepr = (GuStructRepr*) type;
+ gu_yaml_begin_mapping(ctx->yaml);
+ const uint8_t* data = p;
+ GuLength* old_lenp = gu_map_get(ctx->data, gu_dump_length_key, void*);
+ GuLength len = (GuLength)-1;
+ gu_map_put(ctx->data, gu_dump_length_key, void*, &len);
+
+ for (int i = 0; i < srepr->members.len; i++) {
+ const GuMember* member = &srepr->members.elems[i];
+ gu_dump_str_scalar(ctx, member->name);
+ const uint8_t* memp = &data[member->offset];
+ if (member->is_flex) {
+ // Flexible array member
+ gu_assert(len != (GuLength)-1);
+ size_t mem_s = gu_type_size(member->type);
+ gu_yaml_begin_sequence(ctx->yaml);
+ for (GuLength i = 0; i < len; i++) {
+ gu_dump(member->type, &memp[i * mem_s], ctx);
+ }
+ gu_yaml_end(ctx->yaml);
+ } else {
+ gu_dump(member->type, memp, ctx);
+ }
+ }
+ gu_yaml_end(ctx->yaml);
+ if (old_lenp) {
+ gu_map_set(ctx->data, gu_dump_length_key, void*, old_lenp);
+ }
+}
+
+static void
+gu_dump_alias(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ GuTypeAlias* alias = gu_type_cast(type, alias);
+
+ gu_dump(alias->type, p, ctx);
+}
+
+static const char gu_dump_reference_key[] = "reference";
+
+static bool
+gu_dump_anchor(GuDump* ctx, const void* p)
+{
+ GuMap* map = gu_map_get(ctx->data, gu_dump_reference_key, void*);
+ if (map == NULL) {
+ map = gu_new_addr_map(void, GuYamlAnchor,
+ &gu_yaml_null_anchor, ctx->pool);
+ gu_map_put(ctx->data, gu_dump_reference_key, void*, map);
+ }
+ GuYamlAnchor a = gu_map_get(map, p, GuYamlAnchor);
+ if (a == gu_yaml_null_anchor) {
+ a = gu_yaml_anchor(ctx->yaml);
+ gu_map_put(map, p, GuYamlAnchor, a);
+ return true;
+ } else {
+ gu_yaml_alias(ctx->yaml, a);
+ return false;
+ }
+}
+
+static void
+gu_dump_referenced(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ GuTypeAlias* alias = gu_type_cast(type, alias);
+ bool created = gu_dump_anchor(ctx, p);
+ if (created) {
+ gu_dump(alias->type, p, ctx);
+ } else {
+ // gu_assert(false);
+ }
+}
+
+static void
+gu_dump_reference(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ (void) type;
+ void* const* pp = p;
+ bool created = gu_dump_anchor(ctx, *pp);
+ if (created) {
+ // gu_assert(false);
+ GuPointerType* ptype = (GuPointerType*) type;
+ gu_dump(ptype->pointed_type, *pp, ctx);
+ }
+}
+
+static void
+gu_dump_shared(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ void* const* pp = p;
+ if (*pp == NULL) {
+ gu_dump_null(ctx);
+ } else {
+ bool created = gu_dump_anchor(ctx, *pp);
+ if (created) {
+ GuPointerType* ptype = (GuPointerType*) type;
+ gu_dump(ptype->pointed_type, *pp, ctx);
+ }
+ }
+}
+
+static void
+gu_dump_list(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ GuListType* ltype = (GuListType*) type;
+ const uint8_t* up = p;
+ int len = * (const int*) p;
+ size_t elem_size = gu_type_size(ltype->elem_type);
+ gu_yaml_begin_sequence(ctx->yaml);
+ for (int i = 0; i < len; i++) {
+ ptrdiff_t offset = ltype->elems_offset + i * elem_size;
+ gu_dump(ltype->elem_type, &up[offset], ctx);
+ }
+ gu_yaml_end(ctx->yaml);
+}
+
+static void
+gu_dump_variant(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ GuVariantType* vtype = gu_type_cast(type, GuVariant);
+ const GuVariant* vp = p;
+ int tag = gu_variant_tag(*vp);
+ for (int i = 0; i < vtype->ctors.len; i++) {
+ GuConstructor* ctor = &vtype->ctors.elems[i];
+ if (ctor->c_tag == tag) {
+ gu_yaml_begin_mapping(ctx->yaml);
+ gu_dump_str_scalar(ctx, ctor->c_name);
+ void* data = gu_variant_data(*vp);
+ gu_dump(ctor->type, data, ctx);
+ gu_yaml_end(ctx->yaml);
+ return;
+ }
+ }
+ gu_assert(false);
+}
+
+
+static void
+gu_dump_enum(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ GuEnumType* etype = gu_type_cast(type, enum);
+ GuEnumConstant* cp = gu_enum_value(etype, p);
+ gu_assert(cp != NULL);
+ gu_dump_str_scalar(ctx, cp->name);
+}
+
+static void
+gu_dump_seq(GuDumpFn* dumper, GuType* type, const void* p,
+ GuDump* ctx)
+{
+ (void) dumper;
+ GuSeqType* dtype = gu_type_cast(type, GuSeq);
+ size_t elem_size = gu_type_size(dtype->elem_type);
+ const GuSeq* seqp = p;
+ GuSeq seq = *seqp;
+ if (gu_seq_is_null(seq)) {
+ gu_dump_null(ctx);
+ return;
+ }
+ size_t len = gu_seq_length(seq);
+ const uint8_t* data = gu_seq_data(seq);
+ gu_yaml_begin_sequence(ctx->yaml);
+ for (size_t i = 0; i < len; i++) {
+ const void* elemp = &data[i * elem_size];
+ gu_dump(dtype->elem_type, elemp, ctx);
+ }
+ gu_yaml_end(ctx->yaml);
+}
+
+
+GuTypeTable
+gu_dump_table = GU_TYPETABLE(
+ GU_SLIST_0,
+ { gu_kind(int), gu_fn(gu_dump_int) },
+ { gu_kind(uint16_t), gu_fn(gu_dump_uint16) },
+ { gu_kind(size_t), gu_fn(gu_dump_size) },
+ { gu_kind(GuStr), gu_fn(gu_dump_str) },
+ { gu_kind(GuString), gu_fn(gu_dump_string) },
+ { gu_kind(struct), gu_fn(gu_dump_struct) },
+ { gu_kind(pointer), gu_fn(gu_dump_pointer) },
+ { gu_kind(GuMap), gu_fn(gu_dump_map) },
+ { gu_kind(alias), gu_fn(gu_dump_alias) },
+ { gu_kind(reference), gu_fn(gu_dump_reference) },
+ { gu_kind(referenced), gu_fn(gu_dump_referenced) },
+ { gu_kind(shared), gu_fn(gu_dump_shared) },
+ { gu_kind(GuList), gu_fn(gu_dump_list) },
+ { gu_kind(GuSeq), gu_fn(gu_dump_seq) },
+ { gu_kind(GuLength), gu_fn(gu_dump_length) },
+ { gu_kind(GuVariant), gu_fn(gu_dump_variant) },
+ { gu_kind(double), gu_fn(gu_dump_double) },
+ { gu_kind(enum), gu_fn(gu_dump_enum) },
+ );
diff --git a/src/runtime/c/gu/dump.h b/src/runtime/c/gu/dump.h
new file mode 100644
index 000000000..7d6f10a77
--- /dev/null
+++ b/src/runtime/c/gu/dump.h
@@ -0,0 +1,34 @@
+#ifndef GU_DUMP_H_
+#define GU_DUMP_H_
+
+#include <gu/defs.h>
+#include <gu/yaml.h>
+#include <gu/type.h>
+#include <gu/map.h>
+
+typedef struct GuDump GuDump;
+
+struct GuDump {
+ GuPool* pool;
+ GuYaml* yaml;
+ GuMap* data;
+ GuTypeMap* dumpers;
+ bool print_address;
+};
+
+typedef void (*GuDumpFn)(GuFn* self, GuType* type, const void* value, GuDump* ctx);
+
+GuDump*
+gu_new_dump(GuWriter* wtr, GuTypeTable* dumpers, GuExn* err, GuPool* pool);
+
+void
+gu_dump(GuType* type, const void* value, GuDump* ctx);
+
+void
+gu_dump_stderr(GuType* type, const void* value, GuExn* err);
+
+extern GuTypeTable
+gu_dump_table;
+
+
+#endif // GU_DUMP_H_
diff --git a/src/runtime/c/gu/enum.c b/src/runtime/c/gu/enum.c
new file mode 100644
index 000000000..fa8595a55
--- /dev/null
+++ b/src/runtime/c/gu/enum.c
@@ -0,0 +1,7 @@
+#include <gu/enum.h>
+
+void
+gu_enum_next(GuEnum* en, void* to, GuPool* pool)
+{
+ en->next(en, to, pool);
+}
diff --git a/src/runtime/c/gu/enum.h b/src/runtime/c/gu/enum.h
new file mode 100644
index 000000000..88cd5ba6a
--- /dev/null
+++ b/src/runtime/c/gu/enum.h
@@ -0,0 +1,35 @@
+#ifndef GU_ENUM_H_
+#define GU_ENUM_H_
+
+#include <gu/mem.h>
+
+typedef struct GuEnum GuEnum;
+
+struct GuEnum {
+ void (*next)(GuEnum* self, void* to, GuPool* pool);
+};
+
+void
+gu_enum_next(GuEnum* en, void* to, GuPool* pool);
+
+#ifdef GU_GNUC
+
+#define gu_next(ENUM, T, POOL) \
+ ({ \
+ T gu_next_tmp_; \
+ gu_enum_next((ENUM), &gu_next_tmp_, (POOL)); \
+ gu_next_tmp_; \
+ })
+#else
+static inline void*
+gu_enum_next_(GuEnum* en, void* to, GuPool* pool)
+{
+ gu_enum_next(en, to, pool);
+ return to;
+}
+#define gu_next(ENUM, T, POOL) \
+ (*(T*)gu_enum_next_((ENUM), &(T){0}, (POOL)))
+
+#endif
+
+#endif /* GU_ENUM_H_ */
diff --git a/src/runtime/c/gu/exn.c b/src/runtime/c/gu/exn.c
new file mode 100644
index 000000000..7bcfeb088
--- /dev/null
+++ b/src/runtime/c/gu/exn.c
@@ -0,0 +1,72 @@
+#include <gu/exn.h>
+#include <gu/assert.h>
+
+
+GuExn*
+gu_new_exn(GuExn* parent, GuKind* catch, GuPool* pool)
+{
+ return gu_new_s(pool, GuExn,
+ .state = GU_EXN_OK,
+ .parent = parent,
+ .catch = catch,
+ .caught = NULL,
+ .data.pool = pool,
+ .data.data = NULL);
+}
+
+void
+gu_exn_block(GuExn* err)
+{
+ if (err && err->state == GU_EXN_RAISED) {
+ err->state = GU_EXN_BLOCKED;
+ }
+}
+
+void
+gu_exn_unblock(GuExn* err)
+{
+ if (err && err->state == GU_EXN_BLOCKED) {
+ err->state = GU_EXN_RAISED;
+ }
+}
+
+GuExnData*
+gu_exn_raise_debug_(GuExn* base, GuType* type,
+ const char* filename, const char* func, int lineno)
+{
+ gu_require(type);
+
+ // TODO: log the error, once there's a system for dumping
+ // error objects.
+
+ GuExn* err = base;
+
+ while (err && !(err->catch && gu_type_has_kind(type, err->catch))) {
+ err->state = GU_EXN_RAISED;
+ err = err->parent;
+ }
+ if (!err) {
+ gu_abort_(GU_ASSERT_ASSERTION, filename, func, lineno,
+ "Unexpected error raised");
+ }
+ GuExnState old_state = err->state;
+ err->state = GU_EXN_RAISED;
+ if (old_state == GU_EXN_OK) {
+ err->caught = type;
+ if (err->data.pool) {
+ return &err->data;
+ }
+ }
+ // Exceptian had already been raised, possibly blocked, or no
+ // exception value is required.
+ return NULL;
+}
+
+GuExnData*
+gu_exn_raise_(GuExn* base, GuType* type)
+{
+ return gu_exn_raise_debug_(base, type, NULL, NULL, -1);
+}
+
+
+GU_DEFINE_TYPE(GuErrno, signed, _);
diff --git a/src/runtime/c/gu/exn.h b/src/runtime/c/gu/exn.h
new file mode 100644
index 000000000..66ca107e3
--- /dev/null
+++ b/src/runtime/c/gu/exn.h
@@ -0,0 +1,195 @@
+#ifndef GU_EXN_H_
+#define GU_EXN_H_
+
+#include <gu/mem.h>
+#include <gu/type.h>
+
+/** @file
+ *
+ * @defgroup GuExn Exceptions
+ * Defined in <gu/exn.h>.
+ * @{
+ */
+
+/// An exception frame.
+typedef struct GuExn GuExn;
+
+/// @private
+typedef enum {
+ GU_EXN_RAISED,
+ GU_EXN_OK,
+ GU_EXN_BLOCKED
+} GuExnState;
+
+typedef struct GuExnData GuExnData;
+
+/// A structure for storing exception values.
+struct GuExnData
+/**
+ * When an exception is raised, if there is an associated value, it
+ * must be allocated from a pool that still exists when control
+ * returns to the handler of that exception. This structure is used to
+ * communicate the exception from the raiser to the handler: the
+ * handler sets #pool when setting up the exception frame, and the
+ * raiser uses that pool to allocate the value and stores that in
+ * #data. When control returns to the handler, it reads the value from
+ * there.
+ */
+{
+
+ /// The pool that the exception value should be allocated from.
+ GuPool* const pool;
+
+ /// The exception value.
+ const void* data;
+};
+
+struct GuExn {
+ /// @privatesection
+ GuExnState state;
+ GuExn* parent;
+ GuKind* catch;
+ GuType* caught;
+ GuExnData data;
+};
+
+
+/// @name Creating exception frames
+//@{
+
+
+/// Allocate a new local exception frame.
+#define gu_exn(parent_, catch_, pool_) &(GuExn){ \
+ .state = GU_EXN_OK, \
+ .parent = parent_, \
+ .catch = gu_kind(catch_), \
+ .caught = NULL, \
+ .data.pool = pool_, \
+ .data.data = NULL \
+}
+
+
+/// Allocate a new exception frame.
+GuExn*
+gu_new_exn(GuExn* parent, GuKind* catch_kind, GuPool* pool);
+
+
+
+static inline bool
+gu_exn_is_raised(GuExn* err) {
+ return err && (err->state == GU_EXN_RAISED);
+}
+
+static inline void
+gu_exn_clear(GuExn* err) {
+ err->caught = NULL;
+ err->state = GU_EXN_OK;
+}
+
+
+
+GuType*
+gu_exn_caught(GuExn* err);
+
+const void*
+gu_exn_caught_data(GuExn* err);
+
+/// Temporarily block a raised exception.
+void
+gu_exn_block(GuExn* err);
+
+/// Show again a blocked exception.
+void
+gu_exn_unblock(GuExn* err);
+
+//@private
+GuExnData*
+gu_exn_raise_(GuExn* err, GuType* type);
+
+//@private
+GuExnData*
+gu_exn_raise_debug_(GuExn* err, GuType* type,
+ const char* filename, const char* func, int lineno);
+
+#ifdef NDEBUG
+#define gu_exn_raise(err_, type_) \
+ gu_exn_raise_(err_, type_)
+#else
+#define gu_exn_raise(err_, type_) \
+ gu_exn_raise_debug_(err_, type_, \
+ __FILE__, __func__, __LINE__)
+#endif
+
+/// Raise an exception.
+#define gu_raise(exn, T) \
+ gu_exn_raise(exn, gu_type(T))
+/**<
+ * @param exn The current exception frame.
+ *
+ * @param T The C type of the exception to raise.
+ *
+ * @return A #GuExnData object that can be used to store the exception value, or
+ * \c NULL if no value is required.
+ *
+ * @note The associated #GuType object for type \p T must be visible.
+ */
+
+#define gu_raise_new(error_, t_, pool_, expr_) \
+ GU_BEGIN \
+ GuExnData* gu_raise_err_ = gu_raise(error_, t_); \
+ if (gu_raise_err_) { \
+ GuPool* pool_ = gu_raise_err_->pool; \
+ gu_raise_err_->data = expr_; \
+ } \
+ GU_END
+
+#define gu_raise_i(error_, t_, ...) \
+ gu_raise_new(error_, t_, gu_raise_pool_, gu_new_i(gu_raise_pool_, t_, __VA_ARGS__))
+
+
+/// Check the status of the current exception frame
+static inline bool
+gu_ok(GuExn* exn) {
+ return !GU_UNLIKELY(gu_exn_is_raised(exn));
+}
+/**<
+ * @return \c false if an exception has been raised in the frame \p exn
+ * and it has not been blocked, \c true otherwise.
+ */
+
+
+/// Return from current function if an exception has been raised.
+#define gu_return_on_exn(exn_, retval_) \
+ GU_BEGIN \
+ if (gu_exn_is_raised(exn_)) return retval_; \
+ GU_END
+/**<
+ * @showinitializer
+ */
+
+
+#include <errno.h>
+
+typedef int GuErrno;
+
+extern GU_DECLARE_TYPE(GuErrno, signed);
+
+
+
+#define gu_raise_errno(error_) \
+ gu_raise_i(error_, GuErrno, errno)
+
+#if 0
+
+typedef void (*GuExnPrintFn)(GuFn* clo, void* err, FILE* out);
+
+extern GuTypeTable gu_exn_default_printer;
+
+void
+gu_exn_print(GuExn* err, FILE* out, GuTypeMap printer_map);
+
+#endif
+
+/** @} */
+
+#endif // GU_EXN_H_
diff --git a/src/runtime/c/gu/file.c b/src/runtime/c/gu/file.c
new file mode 100644
index 000000000..ed1956537
--- /dev/null
+++ b/src/runtime/c/gu/file.c
@@ -0,0 +1,73 @@
+#include <gu/file.h>
+
+typedef struct GuFileOutStream GuFileOutStream;
+
+struct GuFileOutStream {
+ GuOutStream stream;
+ FILE* file;
+};
+
+static size_t
+gu_file_output(GuOutStream* stream, const uint8_t* buf, size_t len, GuExn* err)
+{
+ GuFileOutStream* fos = gu_container(stream, GuFileOutStream, stream);
+ errno = 0;
+ size_t wrote = fwrite(buf, 1, len, fos->file);
+ if (wrote < len) {
+ if (ferror(fos->file)) {
+ gu_raise_errno(err);
+ }
+ }
+ return wrote;
+}
+
+static void
+gu_file_flush(GuOutStream* stream, GuExn* err)
+{
+ GuFileOutStream* fos = gu_container(stream, GuFileOutStream, stream);
+ errno = 0;
+ if (fflush(fos->file) != 0) {
+ gu_raise_errno(err);
+ }
+}
+
+GuOut*
+gu_file_out(FILE* file, GuPool* pool)
+{
+ GuFileOutStream* fos = gu_new_i(pool, GuFileOutStream,
+ .stream.output = gu_file_output,
+ .stream.flush = gu_file_flush,
+ .file = file);
+ return gu_new_out(&fos->stream, pool);
+}
+
+
+typedef struct GuFileInStream GuFileInStream;
+
+struct GuFileInStream {
+ GuInStream stream;
+ FILE* file;
+};
+
+static size_t
+gu_file_input(GuInStream* stream, uint8_t* buf, size_t sz, GuExn* err)
+{
+ GuFileInStream* fis = gu_container(stream, GuFileInStream, stream);
+ errno = 0;
+ size_t got = fread(buf, 1, sz, fis->file);
+ if (got == 0) {
+ if (ferror(fis->file)) {
+ gu_raise_errno(err);
+ }
+ }
+ return got;
+}
+
+GuIn*
+gu_file_in(FILE* file, GuPool* pool)
+{
+ GuFileInStream* fis = gu_new_s(pool, GuFileInStream,
+ .stream.input = gu_file_input,
+ .file = file);
+ return gu_new_in(&fis->stream, pool);
+}
diff --git a/src/runtime/c/gu/file.h b/src/runtime/c/gu/file.h
new file mode 100644
index 000000000..1bb3fb00a
--- /dev/null
+++ b/src/runtime/c/gu/file.h
@@ -0,0 +1,14 @@
+#ifndef GU_FILE_H_
+#define GU_FILE_H_
+
+#include <gu/in.h>
+#include <gu/out.h>
+#include <stdio.h>
+
+GuOut*
+gu_file_out(FILE* file, GuPool* pool);
+
+GuIn*
+gu_file_in(FILE* file, GuPool* pool);
+
+#endif // GU_FILE_H_
diff --git a/src/runtime/c/gu/fun.c b/src/runtime/c/gu/fun.c
new file mode 100644
index 000000000..ca777c490
--- /dev/null
+++ b/src/runtime/c/gu/fun.c
@@ -0,0 +1 @@
+#include <gu/fun.h>
diff --git a/src/runtime/c/gu/fun.h b/src/runtime/c/gu/fun.h
new file mode 100644
index 000000000..0004e9923
--- /dev/null
+++ b/src/runtime/c/gu/fun.h
@@ -0,0 +1,65 @@
+#ifndef GU_FUN_H_
+#define GU_FUN_H_
+
+#include <gu/defs.h>
+
+typedef void (*GuFn)();
+typedef void (*GuFn0)(GuFn* clo);
+typedef void (*GuFn1)(GuFn* clo, void* arg1);
+typedef void (*GuFn2)(GuFn* clo, void* arg1, void* arg2);
+
+#define gu_fn(fn_) (&(GuFn){ fn_ })
+
+static inline void
+gu_apply0(GuFn* fn) {
+ (*fn)(fn);
+}
+
+static inline void
+gu_apply1(GuFn* fn, void* arg1) {
+ (*fn)(fn, arg1);
+}
+
+static inline void
+gu_apply2(GuFn* fn, void* arg1, void* arg2) {
+ (*fn)(fn, arg1, arg2);
+}
+
+#define gu_apply(fn_, ...) \
+ ((fn_)->fn((fn_), __VA_ARGS__))
+
+typedef struct GuClo0 GuClo0;
+
+struct GuClo0 {
+ GuFn fn;
+};
+
+typedef struct GuClo1 GuClo1;
+
+struct GuClo1 {
+ GuFn fn;
+ void *env1;
+};
+
+typedef struct GuClo2 GuClo2;
+struct GuClo2 {
+ GuFn fn;
+ void *env1;
+ void *env2;
+};
+
+typedef struct GuClo3 GuClo3;
+struct GuClo3 {
+ GuFn fn;
+ void *env1;
+ void *env2;
+ void *env3;
+};
+
+typedef const struct GuEquality GuEquality;
+
+struct GuEquality {
+ bool (*is_equal)(GuEquality* self, const void* a, const void* b);
+};
+
+#endif // GU_FUN_H_
diff --git a/src/runtime/c/gu/hash.c b/src/runtime/c/gu/hash.c
new file mode 100644
index 000000000..1666263e6
--- /dev/null
+++ b/src/runtime/c/gu/hash.c
@@ -0,0 +1,77 @@
+#include <gu/hash.h>
+
+GuHash
+gu_hash_bytes(GuHash h, const uint8_t* buf, size_t len)
+{
+ for (size_t n = 0; n < len; n++) {
+ h = gu_hash_byte(h, buf[n]);
+ }
+ return h;
+}
+
+static bool
+gu_int_eq_fn(GuEquality* self, const void* p1, const void* p2)
+{
+ (void) self;
+ const int* ip1 = p1;
+ const int* ip2 = p2;
+ return *ip1 == *ip2;
+}
+
+static GuHash
+gu_int_hash_fn(GuHasher* self, const void* p)
+{
+ (void) self;
+ return (GuHash) *(const int*) p;
+}
+
+GuHasher gu_int_hasher[1] = {
+ {
+ { gu_int_eq_fn },
+ gu_int_hash_fn
+ }
+};
+
+static bool
+gu_addr_eq_fn(GuEquality* self, const void* p1, const void* p2)
+{
+ (void) self;
+ return (p1 == p2);
+}
+
+static GuHash
+gu_addr_hash_fn(GuHasher* self, const void* p)
+{
+ (void) self;
+ return (GuHash) (uintptr_t) p;
+}
+
+GuHasher gu_addr_hasher[1] = {
+ {
+ { gu_addr_eq_fn },
+ gu_addr_hash_fn
+ }
+};
+
+static bool
+gu_word_eq_fn(GuEquality* self, const void* p1, const void* p2)
+{
+ (void) self;
+ const GuWord* wp1 = p1;
+ const GuWord* wp2 = p2;
+ return (*wp1 == *wp2);
+}
+
+static GuHash
+gu_word_hash_fn(GuHasher* self, const void* p)
+{
+ (void) self;
+ return (GuHash) (uintptr_t) p;
+}
+
+GuHasher gu_word_hasher[1] = {
+ {
+ { gu_word_eq_fn },
+ gu_word_hash_fn
+ }
+};
diff --git a/src/runtime/c/gu/hash.h b/src/runtime/c/gu/hash.h
new file mode 100644
index 000000000..e16c2f454
--- /dev/null
+++ b/src/runtime/c/gu/hash.h
@@ -0,0 +1,40 @@
+#ifndef GU_HASH_H_
+#define GU_HASH_H_
+
+#include <gu/fun.h>
+
+typedef GuWord GuHash;
+
+static inline GuHash
+gu_hash_ptr(void* ptr)
+{
+ return (GuHash) ptr;
+}
+
+
+static inline GuHash
+gu_hash_byte(GuHash h, uint8_t u)
+{
+ // Paul Larson's simple byte hash
+ return h * 101 + u;
+}
+
+
+GuHash
+gu_hash_bytes(GuHash h, const uint8_t* buf, size_t len);
+
+typedef const struct GuHasher GuHasher;
+
+struct GuHasher {
+ GuEquality eq;
+ GuHash (*hash)(GuHasher* self, const void* p);
+};
+
+
+extern GuHasher gu_int_hasher[1];
+
+extern GuHasher gu_addr_hasher[1];
+
+extern GuHasher gu_word_hasher[1];
+
+#endif // GU_HASH_H_
diff --git a/src/runtime/c/gu/in.c b/src/runtime/c/gu/in.c
new file mode 100644
index 000000000..4238475f8
--- /dev/null
+++ b/src/runtime/c/gu/in.c
@@ -0,0 +1,421 @@
+#include <gu/in.h>
+#include <gu/bits.h>
+#include <math.h>
+
+GU_DEFINE_TYPE(GuEOF, abstract, _);
+
+
+static bool
+gu_in_is_buffering(GuIn* in)
+{
+ return (in->buf_end != NULL);
+}
+
+static void
+gu_in_end_buffering(GuIn* in, GuExn* err)
+{
+ if (!gu_in_is_buffering(in)) {
+ return;
+ }
+ if (in->stream->end_buffer) {
+ size_t len = ((ptrdiff_t) in->buf_size) + in->buf_curr;
+ in->stream->end_buffer(in->stream, len, err);
+ }
+ in->buf_curr = 0;
+ in->buf_size = 0;
+ in->buf_end = NULL;
+}
+
+static bool
+gu_in_begin_buffering(GuIn* in, GuExn* err)
+{
+ if (gu_in_is_buffering(in)) {
+ if (in->buf_curr < 0) {
+ return true;
+ } else {
+ gu_in_end_buffering(in, err);
+ if (!gu_ok(err)) return false;
+ }
+ }
+ if (!in->stream->begin_buffer) {
+ return false;
+ }
+ size_t sz = 0;
+ const uint8_t* new_buf =
+ in->stream->begin_buffer(in->stream, &sz, err);
+ if (new_buf) {
+ in->buf_end = &new_buf[sz];
+ in->buf_curr = -(ptrdiff_t) sz;
+ in->buf_size = sz;
+ return true;
+ }
+ return false;
+}
+
+static size_t
+gu_in_input(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
+{
+ if (sz == 0) {
+ return 0;
+ }
+ gu_in_end_buffering(in, err);
+ if (!gu_ok(err)) {
+ return 0;
+ }
+ GuInStream* stream = in->stream;
+ if (stream->input) {
+ return stream->input(stream, dst, sz, err);
+ }
+ gu_raise(err, GuEOF);
+ return 0;
+}
+
+size_t
+gu_in_some(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
+{
+ gu_require(sz <= PTRDIFF_MAX);
+ if (!gu_in_begin_buffering(in, err)) {
+ if (!gu_ok(err)) return 0;
+ return gu_in_input(in, dst, sz, err);
+ }
+ size_t real_sz = GU_MIN(sz, (size_t)(-in->buf_curr));
+ memcpy(dst, &in->buf_end[in->buf_curr], real_sz);
+ in->buf_curr += real_sz;
+ return real_sz;
+}
+
+void
+gu_in_bytes_(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
+{
+ size_t avail_sz = GU_MIN(sz, (size_t)(-in->buf_curr));
+ memcpy(dst, &in->buf_end[in->buf_curr], avail_sz);
+ in->buf_curr += avail_sz;
+ if (avail_sz < sz) {
+ gu_in_input(in, &dst[avail_sz], sz - avail_sz, err);
+ }
+}
+
+const uint8_t*
+gu_in_begin_span(GuIn* in, size_t *sz_out, GuExn* err)
+{
+ if (!gu_in_begin_buffering(in, err)) {
+ return NULL;
+ }
+ *sz_out = (size_t) -in->buf_curr;
+ return &in->buf_end[in->buf_curr];
+}
+
+void
+gu_in_end_span(GuIn* in, size_t consumed)
+{
+ gu_require(consumed <= (size_t) -in->buf_curr);
+ in->buf_curr += (ptrdiff_t) consumed;
+}
+
+uint8_t
+gu_in_u8_(GuIn* in, GuExn* err)
+{
+ if (gu_in_begin_buffering(in, err) && in->buf_curr < 0) {
+ return in->buf_end[in->buf_curr++];
+ }
+ uint8_t u = 0;
+ size_t r = gu_in_input(in, &u, 1, err);
+ if (r < 1) {
+ gu_raise(err, GuEOF);
+ return 0;
+ }
+ return u;
+}
+
+static uint64_t
+gu_in_be(GuIn* in, GuExn* err, int n)
+{
+ uint8_t buf[8];
+ gu_in_bytes(in, buf, n, err);
+ uint64_t u = 0;
+ for (int i = 0; i < n; i++) {
+ u = u << 8 | buf[i];
+ }
+ return u;
+}
+
+static uint64_t
+gu_in_le(GuIn* in, GuExn* err, int n)
+{
+ uint8_t buf[8];
+ gu_in_bytes(in, buf, n, err);
+ uint64_t u = 0;
+ for (int i = 0; i < n; i++) {
+ u = u << 8 | buf[i];
+ }
+ return u;
+}
+
+int8_t
+gu_in_s8(GuIn* in, GuExn* err)
+{
+ return gu_decode_2c8(gu_in_u8(in, err), err);
+}
+
+
+uint16_t
+gu_in_u16le(GuIn* in, GuExn* err)
+{
+ return gu_in_le(in, err, 2);
+}
+
+int16_t
+gu_in_s16le(GuIn* in, GuExn* err)
+{
+ return gu_decode_2c16(gu_in_u16le(in, err), err);
+}
+
+uint16_t
+gu_in_u16be(GuIn* in, GuExn* err)
+{
+ return gu_in_be(in, err, 2);
+}
+
+int16_t
+gu_in_s16be(GuIn* in, GuExn* err)
+{
+ return gu_decode_2c16(gu_in_u16be(in, err), err);
+}
+
+
+uint32_t
+gu_in_u32le(GuIn* in, GuExn* err)
+{
+ return gu_in_le(in, err, 4);
+}
+
+int32_t
+gu_in_s32le(GuIn* in, GuExn* err)
+{
+ return gu_decode_2c32(gu_in_u32le(in, err), err);
+}
+
+uint32_t
+gu_in_u32be(GuIn* in, GuExn* err)
+{
+ return gu_in_be(in, err, 4);
+}
+
+int32_t
+gu_in_s32be(GuIn* in, GuExn* err)
+{
+ return gu_decode_2c32(gu_in_u32be(in, err), err);
+}
+
+
+uint64_t
+gu_in_u64le(GuIn* in, GuExn* err)
+{
+ return gu_in_le(in, err, 8);
+}
+
+int64_t
+gu_in_s64le(GuIn* in, GuExn* err)
+{
+ return gu_decode_2c64(gu_in_u64le(in, err), err);
+}
+
+uint64_t
+gu_in_u64be(GuIn* in, GuExn* err)
+{
+ return gu_in_be(in, err, 8);
+}
+
+int64_t
+gu_in_s64be(GuIn* in, GuExn* err)
+{
+ return gu_decode_2c64(gu_in_u64be(in, err), err);
+}
+
+double
+gu_in_f64le(GuIn* in, GuExn* err)
+{
+ return gu_decode_double(gu_in_u64le(in, err));
+}
+
+double
+gu_in_f64be(GuIn* in, GuExn* err)
+{
+ return gu_decode_double(gu_in_u64le(in, err));
+}
+
+
+static void
+gu_in_fini(GuFinalizer* fin)
+{
+ GuIn* in = gu_container(fin, GuIn, fini);
+ GuPool* pool = gu_local_pool();
+ GuExn* err = gu_exn(NULL, type, pool);
+ gu_in_end_buffering(in, err);
+ gu_pool_free(pool);
+}
+
+GuIn
+gu_init_in(GuInStream* stream)
+{
+ return (GuIn) {
+ .buf_end = NULL,
+ .buf_curr = 0,
+ .buf_size = 0,
+ .stream = stream,
+ .fini.fn = gu_in_fini
+ };
+}
+
+GuIn*
+gu_new_in(GuInStream* stream, GuPool* pool)
+{
+ GuIn* in = gu_new(GuIn, pool);
+ *in = gu_init_in(stream);
+ return in;
+}
+
+
+typedef struct GuProxyInStream GuProxyInStream;
+
+struct GuProxyInStream {
+ GuInStream stream;
+ GuIn* real_in;
+};
+
+static const uint8_t*
+gu_proxy_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
+{
+ GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
+ return gu_in_begin_span(pis->real_in, sz_out, err);
+}
+
+static void
+gu_proxy_in_end_buffer(GuInStream* self, size_t sz, GuExn* err)
+{
+ GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
+ gu_in_end_span(pis->real_in, sz);
+}
+
+static size_t
+gu_proxy_in_input(GuInStream* self, uint8_t* dst, size_t sz, GuExn* err)
+{
+ GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
+ return gu_in_some(pis->real_in, dst, sz, err);
+}
+
+GuInStream*
+gu_in_proxy_stream(GuIn* in, GuPool* pool)
+{
+ return &gu_new_s(
+ pool, GuProxyInStream,
+ .stream.begin_buffer = gu_proxy_in_begin_buffer,
+ .stream.end_buffer = gu_proxy_in_end_buffer,
+ .stream.input = gu_proxy_in_input,
+ .real_in = in)->stream;
+}
+
+enum {
+ GU_BUFFERED_IN_BUF_SIZE = 4096
+};
+
+typedef struct GuBufferedInStream GuBufferedInStream;
+
+struct GuBufferedInStream {
+ GuInStream stream;
+ size_t alloc;
+ size_t have;
+ size_t curr;
+ GuIn* in;
+ uint8_t buf[];
+};
+
+static const uint8_t*
+gu_buffered_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
+{
+ GuBufferedInStream* bis =
+ gu_container(self, GuBufferedInStream, stream);
+ if (bis->curr == bis->have) {
+ bis->curr = 0;
+ bis->have = gu_in_some(bis->in, bis->buf, bis->alloc, err);
+ if (!gu_ok(err)) return NULL;
+ }
+ *sz_out = bis->have - bis->curr;
+ return &bis->buf[bis->curr];
+}
+
+static void
+gu_buffered_in_end_buffer(GuInStream* self, size_t consumed, GuExn* err)
+{
+ GuBufferedInStream* bis =
+ gu_container(self, GuBufferedInStream, stream);
+ gu_require(consumed < bis->have - bis->curr);
+ bis->curr += consumed;
+}
+
+static size_t
+gu_buffered_in_input(GuInStream* self, uint8_t* dst, size_t sz, GuExn* err)
+{
+ GuBufferedInStream* bis =
+ gu_container(self, GuBufferedInStream, stream);
+ return gu_in_some(bis->in, dst, sz, err);
+}
+
+GuIn*
+gu_buffered_in(GuIn* in, size_t buf_sz, GuPool* pool)
+{
+ GuBufferedInStream* bis = gu_new_flex(pool, GuBufferedInStream,
+ buf, buf_sz);
+ bis->stream = (GuInStream) {
+ .begin_buffer = gu_buffered_in_begin_buffer,
+ .end_buffer = gu_buffered_in_end_buffer,
+ .input = gu_buffered_in_input
+ };
+ bis->have = bis->curr = 0;
+ bis->alloc = buf_sz;
+ return gu_new_in(&bis->stream, pool);
+}
+
+typedef struct GuDataIn GuDataIn;
+
+struct GuDataIn {
+ GuInStream stream;
+ const uint8_t* data;
+ size_t sz;
+};
+
+static const uint8_t*
+gu_data_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
+{
+ (void) err;
+ GuDataIn* di = gu_container(self, GuDataIn, stream);
+ const uint8_t* buf = di->data;
+ if (buf) {
+ *sz_out = di->sz;
+ di->data = NULL;
+ di->sz = 0;
+ }
+ return buf;
+}
+
+GuIn*
+gu_data_in(const uint8_t* data, size_t sz, GuPool* pool)
+{
+ GuDataIn* di = gu_new_s(pool, GuDataIn,
+ .stream.begin_buffer = gu_data_in_begin_buffer,
+ .data = data,
+ .sz = sz);
+ return gu_new_in(&di->stream, pool);
+}
+
+extern inline uint8_t
+gu_in_u8(GuIn* restrict in, GuExn* err);
+
+extern inline void
+gu_in_bytes(GuIn* in, uint8_t* buf, size_t sz, GuExn* err);
+
+extern inline int
+gu_in_peek_u8(GuIn* restrict in);
+
+extern inline void
+gu_in_consume(GuIn* restrict in, size_t sz);
diff --git a/src/runtime/c/gu/in.h b/src/runtime/c/gu/in.h
new file mode 100644
index 000000000..c85a6581b
--- /dev/null
+++ b/src/runtime/c/gu/in.h
@@ -0,0 +1,146 @@
+#ifndef GU_IN_H_
+#define GU_IN_H_
+
+#include <gu/defs.h>
+#include <gu/exn.h>
+#include <gu/assert.h>
+
+typedef struct GuInStream GuInStream;
+
+struct GuInStream {
+ const uint8_t* (*begin_buffer)(GuInStream* self, size_t* sz_out,
+ GuExn* err);
+ void (*end_buffer)(GuInStream* self, size_t consumed, GuExn* err);
+ size_t (*input)(GuInStream* self, uint8_t* buf, size_t max_sz,
+ GuExn* err);
+};
+
+typedef struct GuIn GuIn;
+
+struct GuIn {
+ const uint8_t* restrict buf_end;
+ ptrdiff_t buf_curr;
+ size_t buf_size;
+ GuInStream* stream;
+ GuFinalizer fini;
+};
+
+
+GuIn
+gu_init_in(GuInStream* stream);
+
+GuIn*
+gu_new_in(GuInStream* stream, GuPool* pool);
+
+GuInStream*
+gu_in_proxy_stream(GuIn* in, GuPool* pool);
+
+const uint8_t*
+gu_in_begin_span(GuIn* in, size_t *sz_out, GuExn* err);
+
+void
+gu_in_end_span(GuIn* in, size_t consumed);
+
+size_t
+gu_in_some(GuIn* in, uint8_t* buf, size_t max_len, GuExn* err);
+
+inline void
+gu_in_bytes(GuIn* in, uint8_t* buf, size_t sz, GuExn* err)
+{
+ gu_require(sz < PTRDIFF_MAX);
+ ptrdiff_t curr = in->buf_curr;
+ ptrdiff_t new_curr = curr + (ptrdiff_t) sz;
+ if (GU_UNLIKELY(new_curr > 0)) {
+ extern void gu_in_bytes_(GuIn* in, uint8_t* buf, size_t sz,
+ GuExn* err);
+ gu_in_bytes_(in, buf, sz, err);
+ return;
+ }
+ memcpy(buf, &in->buf_end[curr], sz);
+ in->buf_curr = new_curr;
+}
+
+inline int
+gu_in_peek_u8(GuIn* restrict in)
+{
+ if (GU_UNLIKELY(in->buf_curr == 0)) {
+ return -1;
+ }
+ return in->buf_end[in->buf_curr];
+}
+
+inline void
+gu_in_consume(GuIn* restrict in, size_t sz)
+{
+ gu_require((ptrdiff_t) sz + in->buf_curr <= 0);
+ in->buf_curr += sz;
+}
+
+
+inline uint8_t
+gu_in_u8(GuIn* restrict in, GuExn* err)
+{
+ if (GU_UNLIKELY(in->buf_curr == 0)) {
+ extern uint8_t gu_in_u8_(GuIn* restrict in, GuExn* err);
+ return gu_in_u8_(in, err);
+ }
+ return in->buf_end[in->buf_curr++];
+}
+
+int8_t
+gu_in_s8(GuIn* in, GuExn* err);
+
+uint16_t
+gu_in_u16le(GuIn* in, GuExn* err);
+
+uint16_t
+gu_in_u16be(GuIn* in, GuExn* err);
+
+int16_t
+gu_in_s16le(GuIn* in, GuExn* err);
+
+int16_t
+gu_in_s16be(GuIn* in, GuExn* err);
+
+uint32_t
+gu_in_u32le(GuIn* in, GuExn* err);
+
+uint32_t
+gu_in_u32be(GuIn* in, GuExn* err);
+
+int32_t
+gu_in_s32le(GuIn* in, GuExn* err);
+
+int32_t
+gu_in_s32be(GuIn* in, GuExn* err);
+
+uint64_t
+gu_in_u64le(GuIn* in, GuExn* err);
+
+uint64_t
+gu_in_u64be(GuIn* in, GuExn* err);
+
+int64_t
+gu_in_s64le(GuIn* in, GuExn* err);
+
+int64_t
+gu_in_s64be(GuIn* in, GuExn* err);
+
+double
+gu_in_f64le(GuIn* in, GuExn* err);
+
+double
+gu_in_f64be(GuIn* in, GuExn* err);
+
+GuIn*
+gu_buffered_in(GuIn* in, size_t sz, GuPool* pool);
+
+GuIn*
+gu_data_in(const uint8_t* buf, size_t size, GuPool* pool);
+
+
+extern GU_DECLARE_TYPE(GuEOF, abstract);
+
+#include <gu/type.h>
+
+#endif // GU_IN_H_
diff --git a/src/runtime/c/gu/intern.c b/src/runtime/c/gu/intern.c
new file mode 100644
index 000000000..35eaa1c5f
--- /dev/null
+++ b/src/runtime/c/gu/intern.c
@@ -0,0 +1,59 @@
+#include "intern.h"
+
+struct GuIntern {
+ GuPool* str_pool;
+ GuMap* map;
+};
+
+GuIntern*
+gu_new_intern(GuPool* str_pool, GuPool* pool)
+{
+ GuIntern* intern = gu_new(GuIntern, pool);
+ intern->str_pool = str_pool;
+ intern->map = gu_new_set(const char*, gu_str_hasher, pool);
+ return intern;
+}
+
+const char*
+gu_intern_str(GuIntern* intern, const char* cstr)
+{
+ const char* const* strp = gu_map_find_key(intern->map, &cstr);
+ if (strp) {
+ return *strp;
+ }
+ const char* str = gu_strdup(cstr, intern->str_pool);
+ gu_map_insert(intern->map, &str);
+ return str;
+}
+
+
+
+
+struct GuSymTable {
+ GuPool* sym_pool;
+ GuMap* map;
+};
+
+GuSymTable*
+gu_new_symtable(GuPool* sym_pool, GuPool* pool)
+{
+ GuSymTable* tab = gu_new(GuSymTable, pool);
+ tab->sym_pool = sym_pool;
+ tab->map = gu_new_set(GuSymbol, gu_string_hasher, pool);
+ return tab;
+}
+
+GuSymbol
+gu_symtable_intern(GuSymTable* tab, GuString string)
+{
+ if (gu_string_is_stable(string)) {
+ return string;
+ }
+ const GuSymbol* symp = gu_map_find_key(tab->map, &string);
+ if (symp) {
+ return *symp;
+ }
+ GuSymbol sym = gu_string_copy(string, tab->sym_pool);
+ gu_map_insert(tab->map, &sym);
+ return sym;
+}
diff --git a/src/runtime/c/gu/intern.h b/src/runtime/c/gu/intern.h
new file mode 100644
index 000000000..bf9e9b321
--- /dev/null
+++ b/src/runtime/c/gu/intern.h
@@ -0,0 +1,24 @@
+#ifndef GU_INTERN_H_
+#define GU_INTERN_H_
+
+#include <gu/map.h>
+#include <gu/str.h>
+#include <gu/string.h>
+
+typedef struct GuIntern GuIntern;
+
+GuIntern* gu_new_intern(GuPool* str_pool, GuPool* pool);
+const char* gu_intern_str(GuIntern* intern, const char* cstr);
+
+
+typedef struct GuSymTable GuSymTable;
+
+typedef GuString GuSymbol;
+
+GuSymTable*
+gu_new_symtable(GuPool* sym_pool, GuPool* pool);
+
+GuSymbol
+gu_symtable_intern(GuSymTable* symtab, GuString string);
+
+#endif /* GU_INTERN_H_ */
diff --git a/src/runtime/c/gu/list.c b/src/runtime/c/gu/list.c
new file mode 100644
index 000000000..d98a42e41
--- /dev/null
+++ b/src/runtime/c/gu/list.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libgu.
+ *
+ * Libgu is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libgu is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libgu. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gu/list.h>
+#include <gu/assert.h>
+#include <string.h>
+
+static const int gu_list_empty = 0;
+
+void* gu_list_alloc(GuPool* pool, size_t base_size, size_t elem_size,
+ int n_elems, size_t alignment)
+{
+ gu_assert(n_elems >= 0);
+ if (n_elems == 0) {
+ return (void*) &gu_list_empty;
+ }
+ // XXX: use gu_flex_size, use offset of elems
+ void* p = gu_malloc_aligned(pool, base_size + elem_size * n_elems,
+ alignment);
+ *(int*) p = n_elems;
+ return p;
+}
+
+
+GU_DEFINE_KIND(GuList, abstract);
+
+// GU_DEFINE_TYPE(GuStrs, GuList, gu_type(GuStr));
+// GU_DEFINE_TYPE(GuStrsP, pointer, gu_type(GuStrs));
+
+void*
+gu_list_type_alloc(GuListType* ltype, int n_elems, GuPool* pool)
+{
+ return gu_list_alloc(pool, ltype->size,
+ gu_type_size(ltype->elem_type),
+ n_elems, ltype->align);
+}
+
+void*
+gu_list_type_index(GuListType* ltype, void* list, int i)
+{
+ uint8_t* p = list;
+ return &p[ltype->elems_offset + i * gu_type_size(ltype->elem_type)];
+}
diff --git a/src/runtime/c/gu/list.h b/src/runtime/c/gu/list.h
new file mode 100644
index 000000000..f2add157f
--- /dev/null
+++ b/src/runtime/c/gu/list.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libgu.
+ *
+ * Libgu is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libgu is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libgu. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file
+ *
+ * Lists.
+ */
+
+#ifndef GU_LIST_H_
+#define GU_LIST_H_
+
+#include <gu/mem.h>
+
+
+#define GuList(t) \
+ struct { \
+ const int len; \
+ t elems[]; \
+ }
+
+void* gu_list_alloc(GuPool* pool, size_t base_size, size_t elem_size,
+ int n_elems, size_t alignment);
+
+#define gu_new_list(t, pool, n) \
+ ((t*) gu_list_alloc(pool, \
+ sizeof(t), \
+ sizeof(((t*)NULL)->elems[0]), \
+ (n), \
+ gu_flex_alignof(t)))
+
+static inline int
+gu_list_length(const void* list)
+{
+ return *(const int*) list;
+}
+
+#define gu_list_elems(lst) \
+ ((lst)->elems)
+
+#define gu_list_index(lst, i) \
+ (gu_list_elems(lst)[i])
+
+typedef GuList(void*) GuPointers;
+//typedef GuList(uint8_t) GuBytes;
+
+typedef GuList(int) GuInts;
+
+
+#define GuListN(t_, len_) \
+ struct { \
+ int len; \
+ t elems[len_]; \
+ }
+
+#define gu_list_(qual_, t_, ...) \
+ ((qual_ GuList(t_) *) \
+ ((qual_ GuListN(t_, (sizeof((t_[]){__VA_ARGS__}) / sizeof(t_)))[]){ \
+ __VA_ARGS__ \
+ }))
+
+#define gu_list(t_, ...) \
+ gu_list_(, t_, __VA_ARGS__)
+
+#define gu_clist(t_, ...) \
+ gu_list_(const, t_, __VA_ARGS__)
+
+#define GuSList(t) \
+ const struct { \
+ int len; \
+ t* elems; \
+ }
+
+#define GU_SLIST_0 { .len = 0, .elems = NULL }
+
+#define GU_SLIST(t, ...) \
+ { \
+ .len = GU_ARRAY_LEN(t,GU_ID({__VA_ARGS__})), \
+ .elems = ((t[]){__VA_ARGS__}) \
+ }
+
+
+#include <gu/type.h>
+
+//
+// list
+//
+
+typedef const struct GuListType GuListType, GuType_GuList;
+
+struct GuListType {
+ GuType_abstract abstract_base;
+ size_t size;
+ size_t align;
+ GuType* elem_type;
+ ptrdiff_t elems_offset;
+};
+
+#define GU_TYPE_INIT_GuList(k_, t_, elem_type_) { \
+ .abstract_base = GU_TYPE_INIT_abstract(k_, t_, _), \
+ .size = sizeof(t_), \
+ .align = gu_alignof(t_), \
+ .elem_type = elem_type_, \
+ .elems_offset = offsetof(t_, elems) \
+}
+
+extern GU_DECLARE_KIND(GuList);
+
+void*
+gu_list_type_alloc(GuListType* ltype, int n_elems, GuPool* pool);
+
+void*
+gu_list_type_index(GuListType* ltype, void* list, int i);
+
+#include <gu/str.h>
+
+
+typedef GuList(GuStr) GuStrs;
+typedef GuStrs* GuStrsP;
+
+extern GU_DECLARE_TYPE(GuStrs, GuList);
+extern GU_DECLARE_TYPE(GuStrsP, pointer);
+
+
+#endif // GU_LIST_H_
diff --git a/src/runtime/c/gu/log.c b/src/runtime/c/gu/log.c
new file mode 100644
index 000000000..399646c50
--- /dev/null
+++ b/src/runtime/c/gu/log.c
@@ -0,0 +1,79 @@
+#include <gu/defs.h>
+#include <gu/log.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+static int gu_log_depth = 0;
+
+static bool
+gu_log_match(const char* pat, size_t patlen, const char* str)
+{
+ if (patlen > 0 && pat[patlen-1] == '*') {
+ return strncmp(pat, str, patlen-1) == 0;
+ } else if (strlen(str) == patlen) {
+ return strncmp(pat, str, patlen) == 0;
+ }
+ return false;
+}
+
+static bool
+gu_log_enabled(const char* func, const char* file)
+{
+ const char* cfg = getenv("GU_LOG");
+ if (cfg == NULL) {
+ return false;
+ }
+ const char* p = cfg;
+ while (true) {
+ size_t len = strcspn(p, ",");
+ if (gu_log_match(p, len, func)) {
+ return true;
+ }
+ if (gu_log_match(p, len, file)) {
+ return true;
+ }
+ if (p[len] == '\0') {
+ break;
+ }
+ p = &p[len + 1];
+ }
+ return false;
+}
+
+
+void
+gu_log_full_v(GuLogKind kind, const char* func, const char* file, int line,
+ const char* fmt, va_list args)
+{
+ (void) (kind && line);
+ if (!gu_log_enabled(func, file)) {
+ return;
+ }
+ if (kind == GU_LOG_KIND_EXIT) {
+ gu_log_depth--;
+ }
+ if (fmt) {
+ int indent = gu_min(32 + gu_log_depth, 48);
+ fprintf(stderr, "%-*s: ", indent, func);
+ vfprintf(stderr, fmt, args);
+ fputc('\n', stderr);
+ fflush(stderr);
+ }
+ if (kind == GU_LOG_KIND_ENTER) {
+ gu_log_depth++;
+ }
+}
+
+void
+gu_log_full(GuLogKind kind, const char* func, const char* file, int line,
+ const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ gu_log_full_v(kind, func, file, line, fmt, args);
+ va_end(args);
+}
+
diff --git a/src/runtime/c/gu/log.h b/src/runtime/c/gu/log.h
new file mode 100644
index 000000000..ec9ecdf75
--- /dev/null
+++ b/src/runtime/c/gu/log.h
@@ -0,0 +1,65 @@
+#ifndef GU_LOG_H_
+#define GU_LOG_H_
+
+#include <stdarg.h>
+
+typedef enum GuLogKind {
+ GU_LOG_KIND_ENTER,
+ GU_LOG_KIND_EXIT,
+ GU_LOG_KIND_DEBUG,
+ GU_LOG_KIND_ERROR
+} GuLogKind;
+
+void
+gu_log_full(GuLogKind kind, const char* func, const char* file, int line,
+ const char* fmt, ...);
+
+
+void
+gu_log_full_v(GuLogKind kind, const char* func, const char* file, int line,
+ const char* fmt, va_list args);
+
+
+#ifndef NDEBUG
+
+#define gu_logv(kind_, fmt_, args_) \
+ gu_log_full_v(kind_, __func__, __FILE__, __LINE__, fmt_, args_)
+
+#define gu_log(kind_, ...) \
+ gu_log_full(kind_, __func__, __FILE__, __LINE__, __VA_ARGS__)
+
+#else
+
+static inline void
+gu_logv(GuLogKind kind, const char* fmt, va_list args)
+{
+ (void) kind;
+ (void) fmt;
+ (void) args;
+}
+
+static inline void
+gu_log(GuLogKind kind, const char* fmt, ...)
+{
+ (void) kind;
+ (void) fmt;
+}
+
+#endif
+
+
+
+
+#define gu_enter(...) \
+ gu_log(GU_LOG_KIND_ENTER, __VA_ARGS__)
+
+#define gu_exit(...) \
+ gu_log(GU_LOG_KIND_EXIT, __VA_ARGS__)
+
+#define gu_debug(...) \
+ gu_log(GU_LOG_KIND_DEBUG, __VA_ARGS__)
+
+#define gu_debugv(kind_, fmt_, args_) \
+ gu_logv(GU_LOG_KIND_DEBUG, fmt_, args_)
+
+#endif // GU_LOG_H_
diff --git a/src/runtime/c/gu/map.c b/src/runtime/c/gu/map.c
new file mode 100644
index 000000000..2ee77bb23
--- /dev/null
+++ b/src/runtime/c/gu/map.c
@@ -0,0 +1,353 @@
+#include <gu/defs.h>
+#include <gu/mem.h>
+#include <gu/type.h>
+#include <gu/map.h>
+#include <gu/assert.h>
+#include <gu/prime.h>
+#include <gu/log.h>
+
+typedef enum {
+ GU_MAP_GENERIC,
+ GU_MAP_ADDR,
+ GU_MAP_WORD
+} GuMapKind;
+
+typedef struct GuMapData GuMapData;
+
+struct GuMapData {
+ uint8_t* keys;
+ uint8_t* values;
+ size_t n_occupied;
+ size_t n_entries;
+ size_t zero_idx;
+};
+
+struct GuMap {
+ GuMapKind const kind;
+ GuHasher* const hasher;
+ size_t const key_size;
+ size_t const value_size;
+ const void* default_value;
+ GuMapData data;
+
+ GuFinalizer fin;
+};
+
+static void
+gu_map_finalize(GuFinalizer* fin)
+{
+ GuMap* map = gu_container(fin, GuMap, fin);
+ gu_mem_buf_free(map->data.keys);
+ if (map->value_size) {
+ gu_mem_buf_free(map->data.values);
+ }
+}
+
+static const GuWord gu_map_empty_key = 0;
+
+static bool
+gu_map_buf_is_zero(const uint8_t* p, size_t sz) {
+ while (sz >= sizeof(GuWord)) {
+ sz -= sizeof(GuWord);
+ if (memcmp(&p[sz], &gu_map_empty_key, sizeof(GuWord)) != 0) {
+ return false;
+ }
+ }
+ return (memcmp(p, &gu_map_empty_key, sz) == 0);
+}
+
+static bool
+gu_map_entry_is_free(GuMap* map, GuMapData* data, size_t idx)
+{
+ if (idx == data->zero_idx) {
+ return false;
+ } else if (map->kind == GU_MAP_ADDR) {
+ const void* key = ((const void**)data->keys)[idx];
+ return key == NULL;
+ } else if (map->kind == GU_MAP_WORD) {
+ GuWord key = ((GuWord*)data->keys)[idx];
+ return key == 0;
+ }
+ gu_assert(map->kind == GU_MAP_GENERIC);
+ const void* key = &data->keys[idx * map->key_size];
+ return gu_map_buf_is_zero(key, map->key_size);
+}
+
+static bool
+gu_map_lookup(GuMap* map, const void* key, size_t* idx_out)
+{
+ size_t n = map->data.n_entries;
+ switch (map->kind) {
+ case GU_MAP_GENERIC: {
+ GuHasher* hasher = map->hasher;
+ GuEquality* eq = (GuEquality*) hasher;
+ GuHash hash = hasher->hash(hasher, key);
+ size_t idx = hash % n;
+ size_t offset = (hash % (n - 2)) + 1;
+ size_t key_size = map->key_size;
+ while (true) {
+ void* entry_key = &map->data.keys[idx * key_size];
+ if (gu_map_buf_is_zero(entry_key, key_size) &&
+ map->data.zero_idx != idx) {
+ *idx_out = idx;
+ return false;
+ } else if (eq->is_equal(eq, key, entry_key)) {
+ *idx_out = idx;
+ return true;
+ }
+ idx = (idx + offset) % n;
+ }
+ gu_impossible();
+ break;
+ }
+ case GU_MAP_ADDR: {
+ GuHash hash = (GuHash) key;
+ size_t idx = hash % n;
+ size_t offset = (hash % (n - 2)) + 1;
+ while (true) {
+ const void* entry_key =
+ ((const void**)map->data.keys)[idx];
+ if (entry_key == NULL && map->data.zero_idx != idx) {
+ *idx_out = idx;
+ return false;
+ } else if (entry_key == key) {
+ *idx_out = idx;
+ return true;
+ }
+ idx = (idx + offset) % n;
+ }
+ gu_impossible();
+ break;
+ }
+ case GU_MAP_WORD: {
+ GuWord w = *(const GuWord*)key;
+ GuHash hash = (GuHash) w;
+ size_t idx = hash % n;
+ size_t offset = (hash % (n - 2)) + 1;
+ while (true) {
+ GuWord entry_key = ((GuWord*)map->data.keys)[idx];
+ if (entry_key == 0 && map->data.zero_idx != idx) {
+ *idx_out = idx;
+ return false;
+ } else if (entry_key == w) {
+ *idx_out = idx;
+ return true;
+ }
+ idx = (idx + offset) % n;
+ }
+ gu_impossible();
+ break;
+ }
+ default:
+ gu_impossible();
+ }
+ gu_impossible();
+ return false;
+}
+
+
+static void
+gu_map_resize(GuMap* map)
+{
+ GuMapData* data = &map->data;
+ GuMapData old_data = *data;
+ size_t req_entries =
+ gu_twin_prime_sup(GU_MAX(11, map->data.n_occupied * 4 / 3 + 1));
+
+ size_t key_size = map->key_size;
+ size_t key_alloc = 0;
+ data->keys = gu_mem_buf_alloc(req_entries * key_size, &key_alloc);
+
+ size_t value_size = map->value_size;
+ size_t value_alloc = 0;
+ if (value_size) {
+ data->values = gu_mem_buf_alloc(req_entries * value_size,
+ &value_alloc);
+ memset(data->values, 0, value_alloc);
+ }
+
+ data->n_entries = gu_twin_prime_inf(value_size ?
+ GU_MIN(key_alloc / key_size,
+ value_alloc / value_size)
+ : key_alloc / key_size);
+ switch (map->kind) {
+ case GU_MAP_GENERIC:
+ case GU_MAP_WORD:
+ memset(data->keys, 0, key_alloc);
+ break;
+ case GU_MAP_ADDR:
+ for (size_t i = 0; i < data->n_entries; i++) {
+ ((const void**)data->keys)[i] = NULL;
+ }
+ break;
+ default:
+ gu_impossible();
+ }
+
+ gu_assert(data->n_entries > data->n_occupied);
+ gu_debug("Resized to %d entries", data->n_entries);
+
+ data->n_occupied = 0;
+ data->zero_idx = SIZE_MAX;
+
+ for (size_t i = 0; i < old_data.n_entries; i++) {
+ if (gu_map_entry_is_free(map, &old_data, i)) {
+ continue;
+ }
+ void* old_key = &old_data.keys[i * key_size];
+ if (map->kind == GU_MAP_ADDR) {
+ old_key = *(void**)old_key;
+ }
+ void* old_value = &old_data.values[i * value_size];
+
+ memcpy(gu_map_insert(map, old_key),
+ old_value, map->value_size);
+ }
+
+ gu_mem_buf_free(old_data.keys);
+ if (value_size) {
+ gu_mem_buf_free(old_data.values);
+ }
+}
+
+
+static bool
+gu_map_maybe_resize(GuMap* map)
+{
+ if (map->data.n_entries <=
+ map->data.n_occupied + (map->data.n_occupied / 4)) {
+ gu_map_resize(map);
+ return true;
+ }
+ return false;
+}
+
+void*
+gu_map_find(GuMap* map, const void* key)
+{
+ size_t idx;
+ bool found = gu_map_lookup(map, key, &idx);
+ if (found) {
+ return &map->data.values[idx * map->value_size];
+ }
+ return NULL;
+}
+
+const void*
+gu_map_find_default(GuMap* map, const void* key)
+{
+ void* p = gu_map_find(map, key);
+ return p ? p : map->default_value;
+}
+
+const void*
+gu_map_find_key(GuMap* map, const void* key)
+{
+ size_t idx;
+ bool found = gu_map_lookup(map, key, &idx);
+ if (found) {
+ return &map->data.keys[idx * map->key_size];
+ }
+ return NULL;
+}
+
+
+void*
+gu_map_insert(GuMap* map, const void* key)
+{
+ size_t idx;
+ bool found = gu_map_lookup(map, key, &idx);
+ if (!found) {
+ if (gu_map_maybe_resize(map)) {
+ found = gu_map_lookup(map, key, &idx);
+ gu_assert(!found);
+ }
+ if (map->kind == GU_MAP_ADDR) {
+ ((const void**)map->data.keys)[idx] = key;
+ } else {
+ memcpy(&map->data.keys[idx * map->key_size],
+ key, map->key_size);
+ }
+ if (map->default_value) {
+ memcpy(&map->data.values[idx * map->value_size],
+ map->default_value, map->value_size);
+ }
+ if (gu_map_entry_is_free(map, &map->data, idx)) {
+ gu_assert(map->data.zero_idx == SIZE_MAX);
+ map->data.zero_idx = idx;
+ }
+ map->data.n_occupied++;
+ }
+ return &map->data.values[idx * map->value_size];
+}
+
+void
+gu_map_iter(GuMap* map, GuMapItor* itor, GuExn* err)
+{
+ for (size_t i = 0; i < map->data.n_entries && gu_ok(err); i++) {
+ if (gu_map_entry_is_free(map, &map->data, i)) {
+ continue;
+ }
+ const void* key = &map->data.keys[i * map->key_size];
+ void* value = &map->data.values[i * map->value_size];
+ if (map->kind == GU_MAP_ADDR) {
+ key = *(const void* const*) key;
+ }
+ itor->fn(itor, key, value, err);
+ }
+}
+
+static const uint8_t gu_map_no_values[1] = { 0 };
+
+GuMap*
+gu_make_map(size_t key_size, GuHasher* hasher,
+ size_t value_size, const void* default_value,
+ GuPool* pool)
+{
+ GuMapKind kind =
+ ((!hasher || hasher == gu_addr_hasher)
+ ? GU_MAP_ADDR
+ : (key_size == sizeof(GuWord) && hasher == gu_word_hasher)
+ ? GU_MAP_WORD
+ : GU_MAP_GENERIC);
+ if (kind == GU_MAP_ADDR) {
+ key_size = sizeof(GuWord);
+ }
+ GuMapData data = {
+ .n_occupied = 0,
+ .n_entries = 0,
+ .keys = NULL,
+ .values = value_size ? NULL : (uint8_t*) gu_map_no_values,
+ .zero_idx = SIZE_MAX
+ };
+ GuMap* map = gu_new_i(
+ pool, GuMap,
+ .default_value = default_value,
+ .hasher = hasher,
+ .data = data,
+ .key_size = key_size,
+ .value_size = value_size,
+ .fin.fn = gu_map_finalize,
+ .kind = kind
+ );
+ gu_pool_finally(pool, &map->fin);
+ gu_map_resize(map);
+ return map;
+}
+
+GuMap*
+gu_map_type_make(GuMapType* mtype, GuPool* pool)
+{
+ size_t key_size = 0;
+ if (mtype->hasher && mtype->hasher != gu_addr_hasher) {
+ key_size = gu_type_size(mtype->key_type);
+ }
+ size_t value_size = gu_type_size(mtype->value_type);
+ return gu_make_map(key_size, mtype->hasher,
+ value_size, mtype->default_value, pool);
+}
+
+GU_DEFINE_KIND(GuMap, abstract);
+// GU_DEFINE_KIND(GuIntMap, GuMap);
+
+
diff --git a/src/runtime/c/gu/map.h b/src/runtime/c/gu/map.h
new file mode 100644
index 000000000..6523a8057
--- /dev/null
+++ b/src/runtime/c/gu/map.h
@@ -0,0 +1,121 @@
+#ifndef GU_MAP_H_
+#define GU_MAP_H_
+
+#include <gu/hash.h>
+#include <gu/mem.h>
+#include <gu/exn.h>
+
+typedef const struct GuMapItor GuMapItor;
+
+struct GuMapItor {
+ void (*fn)(GuMapItor* self, const void* key, void* value,
+ GuExn *err);
+};
+
+typedef struct GuMap GuMap;
+
+GuMap*
+gu_make_map(size_t key_size, GuHasher* hasher,
+ size_t value_size, const void* default_value,
+ GuPool* pool);
+
+#define gu_new_map(K, HASHER, V, DV, POOL) \
+ (gu_make_map(sizeof(K), (HASHER), sizeof(V), (DV), (POOL)))
+
+#define gu_new_set(K, HASHER, POOL) \
+ (gu_make_map(sizeof(K), (HASHER), 0, NULL, (POOL)))
+
+#define gu_new_addr_map(K, V, DV, POOL) \
+ (gu_make_map(0, NULL, sizeof(V), (DV), (POOL)))
+
+size_t
+gu_map_count(GuMap* map);
+
+void*
+gu_map_find_full(GuMap* ht, void* key_inout);
+
+const void*
+gu_map_find_default(GuMap* ht, const void* key);
+
+#define gu_map_get(MAP, KEYP, V) \
+ (*(V*)gu_map_find_default((MAP), (KEYP)))
+
+void*
+gu_map_find(GuMap* ht, const void* key);
+
+#define gu_map_set(MAP, KEYP, V, VAL) \
+ GU_BEGIN \
+ V* gu_map_set_p_ = gu_map_find((MAP), (KEYP)); \
+ *gu_map_set_p_ = (VAL); \
+ GU_END
+
+const void*
+gu_map_find_key(GuMap* ht, const void* key);
+
+static inline bool
+gu_map_has(GuMap* ht, const void* key)
+{
+ return gu_map_find_key(ht, key) != NULL;
+}
+
+
+void*
+gu_map_insert(GuMap* ht, const void* key);
+
+#define gu_map_put(MAP, KEYP, V, VAL) \
+ GU_BEGIN \
+ V* gu_map_put_p_ = gu_map_insert((MAP), (KEYP)); \
+ *gu_map_put_p_ = (VAL); \
+ GU_END
+
+void
+gu_map_iter(GuMap* ht, GuMapItor* itor, GuExn* err);
+
+
+typedef GuMap GuIntMap;
+
+#define gu_new_int_map(VAL_T, DEFAULT, POOL) \
+ gu_new_map(int, gu_int_hasher, VAL_T, DEFAULT, POOL)
+
+
+#if defined(GU_TYPE_H_) && !defined(GU_MAP_H_TYPE_)
+#define GU_MAP_H_TYPE_
+
+extern GU_DECLARE_KIND(GuMap);
+
+typedef const struct GuMapType GuMapType, GuType_GuMap;
+
+struct GuMapType {
+ GuType_abstract abstract_base;
+ GuHasher* hasher;
+ GuType* key_type;
+ GuType* value_type;
+ const void* default_value;
+};
+
+GuMap*
+gu_map_type_make(GuMapType* mtype, GuPool* pool);
+
+#define gu_map_type_new(MAP_T, POOL) \
+ gu_map_type_make(gu_type_cast(gu_type(MAP_T), GuMap), (POOL))
+
+#define GU_TYPE_INIT_GuMap(k_, t_, kt_, h_, vt_, dv_) \
+ { \
+ .abstract_base = GU_TYPE_INIT_abstract(k_, t_, _), \
+ .hasher = h_, \
+ .key_type = kt_, \
+ .value_type = vt_, \
+ .default_value = dv_ \
+ }
+
+#define gu_type__GuIntMap gu_type__GuMap
+
+typedef GuType_GuMap GuType_GuIntMap;
+
+#define GU_TYPE_INIT_GuIntMap(KIND, MAP_T, VAL_T, DEFAULT) \
+ GU_TYPE_INIT_GuMap(KIND, MAP_T, gu_type(int), gu_int_hasher, \
+ VAL_T, DEFAULT)
+
+#endif
+
+#endif // GU_MAP_H_
diff --git a/src/runtime/c/gu/mem.c b/src/runtime/c/gu/mem.c
new file mode 100644
index 000000000..649105a6a
--- /dev/null
+++ b/src/runtime/c/gu/mem.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libgu.
+ *
+ * Libgu is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libgu is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libgu. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gu/mem.h>
+#include <gu/fun.h>
+#include <gu/bits.h>
+#include <gu/assert.h>
+#include <gu/log.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef USE_VALGRIND
+#include <valgrind/valgrind.h>
+#define VG(X) X
+#else
+#define VG(X) GU_NOP
+#endif
+
+static const size_t
+// Maximum request size for a chunk. The actual maximum chunk size
+// may be somewhat larger.
+gu_mem_chunk_max_size = 1024 * sizeof(void*),
+
+// number of bytes to allocate in the pool when it is created
+ gu_mem_pool_initial_size = 24 * sizeof(void*),
+
+// Pool allocations larger than this will get their own chunk if
+// there's no room in the current one. Allocations smaller than this may trigger
+// the creation of a new chunk, in which case the remaining space in
+// the current chunk is left unused (internal fragmentation).
+ gu_mem_max_shared_alloc = 64 * sizeof(void*),
+
+// Should not be smaller than the granularity for malloc
+ gu_mem_unit_size = 2 * sizeof(void*),
+
+/* Malloc tuning: the additional memory used by malloc next to the
+ allocated object */
+ gu_malloc_overhead = sizeof(size_t);
+
+static void*
+gu_mem_realloc(void* p, size_t size)
+{
+ void* buf = realloc(p, size);
+ if (size != 0 && buf == NULL) {
+ gu_fatal("Memory allocation failed");
+ }
+ gu_debug("%p %zu -> %p", p, size, buf); // strictly illegal
+ return buf;
+}
+
+static void*
+gu_mem_alloc(size_t size)
+{
+ void* buf = malloc(size);
+ if (buf == NULL) {
+ gu_fatal("Memory allocation failed");
+ }
+ gu_debug("%zu -> %p", size, buf);
+ return buf;
+}
+
+static void
+gu_mem_free(void* p)
+{
+ gu_debug("%p", p);
+ free(p);
+}
+
+static size_t
+gu_mem_padovan(size_t min)
+{
+ // This could in principle be done faster with Q-matrices for
+ // Padovan numbers, but not really worth it for our commonly
+ // small numbers.
+ if (min <= 5) {
+ return min;
+ }
+ size_t a = 7, b = 9, c = 12;
+ while (min > a) {
+ if (b < a) {
+ // overflow
+ return SIZE_MAX;
+ }
+ size_t tmp = a + b;
+ a = b;
+ b = c;
+ c = tmp;
+ }
+ return a;
+}
+
+void*
+gu_mem_buf_realloc(void* old_buf, size_t min_size, size_t* real_size_out)
+{
+ size_t min_blocks = ((min_size + gu_malloc_overhead - 1) /
+ gu_mem_unit_size) + 1;
+ size_t blocks = gu_mem_padovan(min_blocks);
+ size_t size = blocks * gu_mem_unit_size - gu_malloc_overhead;
+ void* buf = gu_mem_realloc(old_buf, size);
+ *real_size_out = buf ? size : 0;
+ return buf;
+}
+void*
+gu_mem_buf_alloc(size_t min_size, size_t* real_size_out)
+{
+ return gu_mem_buf_realloc(NULL, min_size, real_size_out);
+}
+
+void
+gu_mem_buf_free(void* buf)
+{
+ gu_mem_free(buf);
+}
+
+
+typedef struct GuMemChunk GuMemChunk;
+
+struct GuMemChunk {
+ GuMemChunk* next;
+ uint8_t data[];
+};
+
+typedef struct GuFinalizerNode GuFinalizerNode;
+
+struct GuFinalizerNode {
+ GuFinalizerNode* next;
+ GuFinalizer* fin;
+};
+
+enum GuPoolFlags {
+ GU_POOL_LOCAL = 1 << 0
+};
+
+struct GuPool {
+ uint8_t* curr_buf; // actually GuMemChunk*
+ GuMemChunk* chunks;
+ GuFinalizerNode* finalizers;
+ uint16_t flags;
+ uint16_t left_edge;
+ uint16_t right_edge;
+ uint16_t curr_size;
+ uint8_t init_buf[];
+};
+
+static GuPool*
+gu_init_pool(uint8_t* buf, size_t sz)
+{
+ gu_require(gu_aligned((uintptr_t) (void*) buf, gu_alignof(GuPool)));
+ gu_require(sz >= sizeof(GuPool));
+ GuPool* pool = (GuPool*) buf;
+ pool->flags = 0;
+ pool->curr_size = sz;
+ pool->curr_buf = (uint8_t*) pool;
+ pool->chunks = NULL;
+ pool->finalizers = NULL;
+ pool->left_edge = offsetof(GuPool, init_buf);
+ pool->right_edge = sz;
+ VG(VALGRIND_CREATE_MEMPOOL(pool, 0, false));
+ return pool;
+}
+
+GuPool*
+gu_local_pool_(uint8_t* buf, size_t sz)
+{
+ GuPool* pool = gu_init_pool(buf, sz);
+ pool->flags |= GU_POOL_LOCAL;
+ gu_debug("%p", pool);
+ return pool;
+}
+
+GuPool*
+gu_new_pool(void)
+{
+ size_t sz = GU_FLEX_SIZE(GuPool, init_buf, gu_mem_pool_initial_size);
+ uint8_t* buf = gu_mem_buf_alloc(sz, &sz);
+ GuPool* pool = gu_init_pool(buf, sz);
+ gu_debug("%p", pool);
+ return pool;
+}
+
+static void
+gu_pool_expand(GuPool* pool, size_t req)
+{
+ size_t real_req = GU_MAX(req, GU_MIN(((size_t)pool->curr_size) + 1,
+ gu_mem_chunk_max_size));
+ gu_assert(real_req >= sizeof(GuMemChunk));
+ size_t size = 0;
+ GuMemChunk* chunk = gu_mem_buf_alloc(real_req, &size);
+ chunk->next = pool->chunks;
+ pool->chunks = chunk;
+ pool->curr_buf = (uint8_t*) chunk;
+ pool->left_edge = offsetof(GuMemChunk, data);
+ pool->right_edge = pool->curr_size = size;
+ // size should always fit in uint16_t
+ gu_assert((size_t) pool->right_edge == size);
+}
+
+static size_t
+gu_mem_advance(size_t old_pos, size_t pre_align, size_t pre_size,
+ size_t align, size_t size)
+{
+ size_t p = gu_align_forward(old_pos, pre_align);
+ p += pre_size;
+ p = gu_align_forward(p, align);
+ p += size;
+ return p;
+}
+
+static void*
+gu_pool_malloc_aligned(GuPool* pool, size_t pre_align, size_t pre_size,
+ size_t align, size_t size)
+{
+ gu_require(size <= gu_mem_max_shared_alloc);
+ size_t pos = gu_mem_advance(pool->left_edge, pre_align, pre_size,
+ align, size);
+ if (pos > (size_t) pool->right_edge) {
+ pos = gu_mem_advance(offsetof(GuMemChunk, data),
+ pre_align, pre_size, align, size);
+ gu_pool_expand(pool, pos);
+ gu_assert(pos <= pool->right_edge);
+ }
+ pool->left_edge = pos;
+ uint8_t* addr = &pool->curr_buf[pos - size];
+ VG(VALGRIND_MEMPOOL_ALLOC(pool, addr - pre_size, size + pre_size ));
+ return addr;
+}
+
+static size_t
+gu_pool_avail(GuPool* pool)
+{
+ return (size_t) pool->right_edge - (size_t) pool->left_edge;
+}
+
+void*
+gu_pool_malloc_unaligned(GuPool* pool, size_t size)
+{
+ if (size > gu_pool_avail(pool)) {
+ gu_pool_expand(pool, offsetof(GuMemChunk, data) + size);
+ gu_assert(size <= gu_pool_avail(pool));
+ }
+ pool->right_edge -= size;
+ void* addr = &pool->curr_buf[pool->right_edge];
+ VG(VALGRIND_MEMPOOL_ALLOC(pool, addr, size));
+ return addr;
+}
+
+void*
+gu_malloc_prefixed(GuPool* pool, size_t pre_align, size_t pre_size,
+ size_t align, size_t size)
+{
+ gu_enter("-> %p %zu %zu %zu %zu",
+ pool, pre_align, pre_size, align, size);
+ void* ret = NULL;
+ if (pre_align == 0) {
+ pre_align = gu_alignof(GuMaxAlign);
+ }
+ if (align == 0) {
+ align = gu_alignof(GuMaxAlign);
+ }
+ size_t full_size = gu_mem_advance(offsetof(GuMemChunk, data),
+ pre_align, pre_size, align, size);
+ if (full_size > gu_mem_max_shared_alloc) {
+ GuMemChunk* chunk = gu_mem_alloc(full_size);
+ chunk->next = pool->chunks;
+ pool->chunks = chunk;
+ uint8_t* addr = &chunk->data[full_size - size
+ - offsetof(GuMemChunk, data)];
+ VG(VALGRIND_MEMPOOL_ALLOC(pool, addr - pre_size,
+ pre_size + size));
+ ret = addr;
+ } else if (pre_align == 1 && align == 1) {
+ uint8_t* buf = gu_pool_malloc_unaligned(pool, pre_size + size);
+ ret = &buf[pre_size];
+ } else {
+ ret = gu_pool_malloc_aligned(pool, pre_align, pre_size,
+ align, size);
+ }
+ gu_exit("<- %p", ret);
+ return ret;
+}
+
+void*
+gu_malloc_aligned(GuPool* pool, size_t size, size_t align)
+{
+ if (align == 0) {
+ align = GU_MIN(size, gu_alignof(GuMaxAlign));
+ }
+ void* ret = gu_malloc_prefixed(pool, 1, 0, align, size);
+ return ret;
+}
+
+
+void
+gu_pool_finally(GuPool* pool, GuFinalizer* finalizer)
+{
+ GuFinalizerNode* node = gu_new(GuFinalizerNode, pool);
+ node->next = pool->finalizers;
+ node->fin = finalizer;
+ pool->finalizers = node;
+}
+
+void
+gu_pool_free(GuPool* pool)
+{
+ gu_debug("%p", pool);
+ GuFinalizerNode* node = pool->finalizers;
+ while (node) {
+ GuFinalizerNode* next = node->next;
+ node->fin->fn(node->fin);
+ node = next;
+ }
+ GuMemChunk* chunk = pool->chunks;
+ while (chunk) {
+ GuMemChunk* next = chunk->next;
+ gu_mem_buf_free(chunk);
+ chunk = next;
+ }
+ VG(VALGRIND_DESTROY_MEMPOOL(pool));
+ if (!pool->flags & GU_POOL_LOCAL) {
+ gu_mem_buf_free(pool);
+ }
+}
+
+
+extern inline void* gu_malloc(GuPool* pool, size_t size);
+
+extern inline void*
+gu_malloc_init_aligned(GuPool* pool, size_t size, size_t alignment,
+ const void* init);
+
diff --git a/src/runtime/c/gu/mem.h b/src/runtime/c/gu/mem.h
new file mode 100644
index 000000000..4369f4036
--- /dev/null
+++ b/src/runtime/c/gu/mem.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libgu.
+ *
+ * Libgu is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libgu is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libgu. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file
+ *
+ * Memory allocation tools.
+ */
+
+#ifndef GU_MEM_H_
+#define GU_MEM_H_
+
+#include <gu/defs.h>
+#include <gu/fun.h>
+
+/** @defgroup GuPool Memory pools */
+//@{
+
+
+/// A memory pool.
+typedef struct GuPool GuPool;
+
+/// @name Creating a pool
+//@{
+
+
+/// Create a new memory pool.
+GU_ONLY GuPool*
+gu_new_pool(void);
+
+/**<
+ * @return A new memory pool.
+ */
+
+
+//@private
+GuPool*
+gu_local_pool_(uint8_t* init_buf, size_t sz);
+
+//@private
+#define GU_LOCAL_POOL_INIT_SIZE (16 * sizeof(GuWord))
+
+
+/// Create a stack-allocated memory pool.
+#define gu_local_pool() \
+ gu_local_pool_(gu_alloca(GU_LOCAL_POOL_INIT_SIZE), \
+ GU_LOCAL_POOL_INIT_SIZE)
+/**<
+ * @return A memory pool whose first chunk is allocated directly from
+ * the stack. This makes its creation faster, and more suitable for
+ * functions that usually allocate only a little memory from the pool
+ * until it is freed.
+ *
+ * @note The pool created with #gu_local_pool \e must be freed with
+ * #gu_pool_free before the end of the block where #gu_local_pool was
+ * called.
+ *
+ * @note Because #gu_local_pool uses relatively much stack space, it
+ * should not be used in the bodies of recursive functions.
+ */
+
+
+//@}
+/// @name Destroying a pool
+//@{
+
+
+/// Free a memory pool and all objects allocated from it.
+void
+gu_pool_free(GU_ONLY GuPool* pool);
+/**<
+ * When the pool is freed, all finalizers registered by
+ * #gu_pool_finally on \p pool are invoked in reverse order of
+ * registration.
+ *
+ * @note After the pool is freed, all objects allocated from it become
+ * invalid and may no longer be used. */
+
+//@}
+/// @name Allocating from a pool
+//@{
+
+
+/// Allocate memory with a specified alignment.
+void*
+gu_malloc_aligned(GuPool* pool, size_t size, size_t alignment);
+
+void*
+gu_malloc_prefixed(GuPool* pool, size_t pre_align, size_t pre_size,
+ size_t align, size_t size);
+
+/// Allocate memory from a pool.
+inline void*
+gu_malloc(GuPool* pool, size_t size) {
+ return gu_malloc_aligned(pool, size, 0);
+}
+
+#include <string.h>
+
+//@private
+static inline void*
+gu_malloc_init_aligned(GuPool* pool, size_t size, size_t alignment,
+ const void* init)
+{
+ void* p = gu_malloc_aligned(pool, size, alignment);
+ memcpy(p, init, size);
+ return p;
+}
+
+//@private
+static inline void*
+gu_malloc_init(GuPool* pool, size_t size, const void* init)
+{
+ return gu_malloc_init_aligned(pool, size, 0, init);
+}
+
+
+/** Allocate memory to store an array of objects of a given type. */
+
+#define gu_new_n(type, n, pool) \
+ ((type*)gu_malloc_aligned((pool), \
+ sizeof(type) * (n), \
+ gu_alignof(type)))
+/**<
+ * @param type The C type of the objects to allocate.
+ *
+ * @param n The number of objects to allocate.
+ *
+ * @param pool The memory pool to allocate from.
+ *
+ * @return A pointer to a heap-allocated array of \p n uninitialized
+ * objects of type \p type.
+ */
+
+
+/** Allocate memory to store an object of a given type. */
+
+#define gu_new(type, pool) \
+ gu_new_n(type, 1, pool)
+/**<
+ * @param type The C type of the object to allocate.
+ *
+ * @param pool The memory pool to allocate from.
+ *
+ * @return A pointer to a heap-allocated uninitialized object of type
+ * \p type.
+ */
+
+
+#define gu_new_prefixed(pre_type, type, pool) \
+ ((type*)(gu_malloc_prefixed((pool), \
+ gu_alignof(pre_type), sizeof(pre_type), \
+ gu_alignof(type), sizeof(type))))
+
+
+
+
+
+#ifdef GU_HAVE_STATEMENT_EXPRESSIONS
+#define gu_new_i(pool, type, ...) \
+ ({ \
+ type *gu_new_p_ = gu_new(type, pool); \
+ memcpy((void*) gu_new_p_, &(type){ __VA_ARGS__ }, \
+ sizeof(type)); \
+ gu_new_p_; \
+ })
+#else // GU_HAVE_STATEMENT_EXPRESSIONS
+#define gu_new_i(pool, type, ...) \
+ ((type*)gu_malloc_init_aligned((pool), sizeof(type), \
+ gu_alignof(type), \
+ &(type){ __VA_ARGS__ }))
+#endif // GU_HAVE_STATEMENT_EXPRESSIONS
+
+/** @def gu_new_i(pool, type, ...)
+ *
+ * Allocate and initialize an object.
+ *
+ * @param pool The pool to allocate from.
+ *
+ * @param type The C type of the object to allocate.
+ *
+ * @param ... An initializer list for the object to allocate.
+ */
+
+#define gu_new_s gu_new_i
+
+// Alas, there's no portable way to get the alignment of flex structs.
+#define gu_new_flex(pool_, type_, flex_member_, n_elems_) \
+ ((type_ *)gu_malloc_aligned( \
+ (pool_), \
+ GU_FLEX_SIZE(type_, flex_member_, n_elems_), \
+ gu_flex_alignof(type_)))
+
+
+//@}
+/// @name Finalizers
+//@{
+
+
+typedef struct GuFinalizer GuFinalizer;
+
+struct GuFinalizer {
+ void (*fn)(GuFinalizer* self);
+ ///< @param self A pointer to this finalizer.
+};
+
+/// Register a finalizer.
+void gu_pool_finally(GuPool* pool, GuFinalizer* fini);
+
+/**< Register \p fini to be called when \p pool is destroyed. The
+ * finalizers are called in reverse order of registration.
+ */
+
+
+//@}
+//@}
+
+/** @defgroup GuMemBuf Memory buffers
+ *
+ * Resizable blocks of heap-allocated memory. These operations differ
+ * from standard \c malloc, \c realloc and \c free -functions in that
+ * memory buffers are not allocated by exact size. Instead, a minimum
+ * size is requested, and the returned buffer may be larger. This
+ * gives the memory allocator more flexibility when the client code
+ * can make use of larger buffers than requested.
+ * */
+
+//@{
+
+
+/// Allocate a new memory buffer.
+GU_ONLY void*
+gu_mem_buf_alloc(size_t min_size, size_t* real_size);
+/**<
+ * @param min_size The minimum acceptable size for a returned memory block.
+ *
+ * @param[out] real_size The actual size of the returned memory
+ * block. This is never less than \p min_size.
+ *
+ * @return A pointer to the memory buffer.
+ */
+
+
+/// Allocate a new memory buffer to replace an old one.
+GU_ONLY void*
+gu_mem_buf_realloc(
+ GU_NULL GU_ONLY GU_RETURNED
+ void* buf,
+ size_t min_size,
+ size_t* real_size_out);
+
+
+/// Free a memory buffer.
+void
+gu_mem_buf_free(GU_ONLY void* buf);
+
+
+//@}
+
+
+#endif // GU_MEM_H_
diff --git a/src/runtime/c/gu/out.c b/src/runtime/c/gu/out.c
new file mode 100644
index 000000000..61df2c184
--- /dev/null
+++ b/src/runtime/c/gu/out.c
@@ -0,0 +1,302 @@
+#include <gu/seq.h>
+#include <gu/out.h>
+
+
+
+GuOut
+gu_init_out(GuOutStream* stream)
+{
+ gu_require(stream != NULL);
+ GuOut out = {
+ .buf_end = NULL,
+ .buf_curr = 0,
+ .stream = stream,
+ .fini.fn = NULL
+ };
+ return out;
+}
+
+static bool
+gu_out_is_buffering(GuOut* out)
+{
+ return !!out->buf_end;
+}
+
+
+static void
+gu_out_end_buf(GuOut* out, GuExn* err)
+{
+ if (!gu_out_is_buffering(out)) {
+ return;
+ }
+ GuOutStream* stream = out->stream;
+ size_t curr_len = ((ptrdiff_t)out->buf_size) + out->buf_curr;
+ stream->end_buf(stream, curr_len, err);
+ out->buf_end = NULL;
+ out->buf_size = out->buf_curr = 0;
+}
+
+static bool
+gu_out_begin_buf(GuOut* out, size_t req, GuExn* err)
+{
+ GuOutStream* stream = out->stream;
+ if (gu_out_is_buffering(out)) {
+ if (out->buf_curr < 0) {
+ return true;
+ } else {
+ gu_out_end_buf(out, err);
+ if (!gu_ok(err)) {
+ return false;
+ }
+ }
+ }
+ if (stream->begin_buf) {
+ size_t sz = 0;
+ uint8_t* buf = stream->begin_buf(stream, req, &sz, err);
+ gu_assert(sz <= PTRDIFF_MAX);
+ if (buf) {
+ out->buf_end = &buf[sz];
+ out->buf_curr = -(ptrdiff_t) sz;
+ out->buf_size = sz;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+static void
+gu_out_fini(GuFinalizer* self)
+{
+ GuOut* out = gu_container(self, GuOut, fini);
+ if (gu_out_is_buffering(out)) {
+ GuPool* pool = gu_local_pool();
+ GuExn* err = gu_new_exn(NULL, gu_kind(type), pool);
+ gu_out_end_buf(out, err);
+ gu_pool_free(pool);
+ }
+}
+
+GuOut*
+gu_new_out(GuOutStream* stream, GuPool* pool)
+{
+ GuOut* out = gu_new(GuOut, pool);
+ *out = gu_init_out(stream);
+ out->fini.fn = gu_out_fini;
+ gu_pool_finally(pool, &out->fini);
+ return out;
+}
+
+extern inline bool
+gu_out_try_buf_(GuOut* out, const uint8_t* src, size_t len);
+
+
+extern inline size_t
+gu_out_bytes(GuOut* out, const uint8_t* buf, size_t len, GuExn* err);
+
+static size_t
+gu_out_output(GuOut* out, const uint8_t* src, size_t len, GuExn* err)
+{
+ gu_out_end_buf(out, err);
+ if (!gu_ok(err)) {
+ return 0;
+ }
+ return out->stream->output(out->stream, src, len, err);
+}
+
+
+
+
+void
+gu_out_flush(GuOut* out, GuExn* err)
+{
+ GuOutStream* stream = out->stream;
+ if (out->buf_end) {
+ gu_out_end_buf(out, err);
+ if (!gu_ok(err)) {
+ return;
+ }
+ }
+ if (stream->flush) {
+ stream->flush(stream, err);
+ }
+}
+
+uint8_t*
+gu_out_begin_span(GuOut* out, size_t req, size_t* sz_out, GuExn* err)
+{
+ if (!out->buf_end && !gu_out_begin_buf(out, req, err)) {
+ return NULL;
+ }
+ *sz_out = -out->buf_curr;
+ return &out->buf_end[out->buf_curr];
+}
+
+void
+gu_out_end_span(GuOut* out, size_t sz)
+{
+ ptrdiff_t new_curr = (ptrdiff_t) sz + out->buf_curr;
+ gu_require(new_curr <= 0);
+ out->buf_curr = new_curr;
+}
+
+size_t
+gu_out_bytes_(GuOut* restrict out, const uint8_t* restrict src, size_t len,
+ GuExn* err)
+{
+ if (!gu_ok(err)) {
+ return 0;
+ } else if (gu_out_try_buf_(out, src, len)) {
+ return len;
+ }
+ if (gu_out_begin_buf(out, len, err)) {
+ if (gu_out_try_buf_(out, src, len)) {
+ return len;
+ }
+ }
+ return gu_out_output(out, src, len, err);
+}
+
+
+void gu_out_u8_(GuOut* restrict out, uint8_t u, GuExn* err)
+{
+ if (gu_out_begin_buf(out, 1, err)) {
+ if (gu_out_try_u8_(out, u)) {
+ return;
+ }
+ }
+ gu_out_output(out, &u, 1, err);
+}
+
+
+extern inline void
+gu_out_u8(GuOut* restrict out, uint8_t u, GuExn* err);
+
+extern inline void
+gu_out_s8(GuOut* restrict out, int8_t i, GuExn* err);
+
+extern inline bool
+gu_out_is_buffered(GuOut* out);
+
+extern inline bool
+gu_out_try_u8_(GuOut* restrict out, uint8_t u);
+
+
+
+
+
+
+
+
+
+typedef struct GuProxyOutStream GuProxyOutStream;
+
+struct GuProxyOutStream {
+ GuOutStream stream;
+ GuOut* real_out;
+};
+
+
+static uint8_t*
+gu_proxy_out_buf_begin(GuOutStream* self, size_t req, size_t* sz_out,
+ GuExn* err)
+{
+ GuProxyOutStream* pos =
+ gu_container(self, GuProxyOutStream, stream);
+ return gu_out_begin_span(pos->real_out, req, sz_out, err);
+}
+
+static void
+gu_proxy_out_buf_end(GuOutStream* self, size_t sz, GuExn* err)
+{
+ GuProxyOutStream* pos =
+ gu_container(self, GuProxyOutStream, stream);
+ gu_out_end_span(pos->real_out, sz);
+}
+
+static size_t
+gu_proxy_out_output(GuOutStream* self, const uint8_t* src, size_t sz,
+ GuExn* err)
+{
+ GuProxyOutStream* pos =
+ gu_container(self, GuProxyOutStream, stream);
+ return gu_out_bytes(pos->real_out, src, sz, err);
+}
+
+static void
+gu_proxy_out_flush(GuOutStream* self, GuExn* err)
+{
+ GuProxyOutStream* pos =
+ gu_container(self, GuProxyOutStream, stream);
+ gu_out_flush(pos->real_out, err);
+}
+
+
+GuOutStream*
+gu_out_proxy_stream(GuOut* out, GuPool* pool)
+{
+ return &gu_new_s(pool, GuProxyOutStream,
+ .stream.begin_buf = gu_proxy_out_buf_begin,
+ .stream.end_buf = gu_proxy_out_buf_end,
+ .stream.output = gu_proxy_out_output,
+ .stream.flush = gu_proxy_out_flush,
+ .real_out = out)->stream;
+}
+
+
+
+typedef struct GuBufferedOutStream GuBufferedOutStream;
+
+struct GuBufferedOutStream {
+ GuProxyOutStream pstream;
+ size_t sz;
+ uint8_t buf[];
+};
+
+static uint8_t*
+gu_buffered_out_buf_begin(GuOutStream* self, size_t req, size_t* sz_out,
+ GuExn* err)
+{
+ (void) (req && err);
+ GuBufferedOutStream* b =
+ gu_container(self, GuBufferedOutStream, pstream.stream);
+ *sz_out = b->sz;
+ return b->buf;
+}
+
+static void
+gu_buffered_out_buf_end(GuOutStream* self, size_t sz, GuExn* err)
+{
+ GuBufferedOutStream* b =
+ gu_container(self, GuBufferedOutStream, pstream.stream);
+ gu_require(sz <= b->sz);
+ gu_out_bytes(b->pstream.real_out, b->buf, sz, err);
+}
+
+GuOut*
+gu_new_buffered_out(GuOut* out, size_t sz, GuPool* pool)
+{
+ GuBufferedOutStream* b =
+ gu_new_flex(pool, GuBufferedOutStream, buf, sz);
+ b->pstream.stream = (GuOutStream) {
+ .begin_buf = gu_buffered_out_buf_begin,
+ .end_buf = gu_buffered_out_buf_end,
+ .output = gu_proxy_out_output,
+ .flush = gu_proxy_out_flush
+ };
+ b->pstream.real_out = out;
+ b->sz = sz;
+ return gu_new_out(&b->pstream.stream, pool);
+}
+
+GuOut*
+gu_out_buffered(GuOut* out, GuPool* pool)
+{
+ if (gu_out_is_buffered(out)) {
+ return out;
+ }
+ return gu_new_buffered_out(out, 4096, pool);
+}
+
+
diff --git a/src/runtime/c/gu/out.h b/src/runtime/c/gu/out.h
new file mode 100644
index 000000000..df2638c72
--- /dev/null
+++ b/src/runtime/c/gu/out.h
@@ -0,0 +1,166 @@
+#ifndef GU_OUT_H_
+#define GU_OUT_H_
+
+#include <gu/defs.h>
+#include <gu/assert.h>
+#include <gu/exn.h>
+
+typedef struct GuOut GuOut;
+
+typedef struct GuOutStream GuOutStream;
+
+struct GuOutStream {
+ uint8_t* (*begin_buf)(GuOutStream* self, size_t req, size_t* sz_out,
+ GuExn* err);
+ void (*end_buf)(GuOutStream* self, size_t span, GuExn* err);
+ size_t (*output)(GuOutStream* self, const uint8_t* buf, size_t size,
+ GuExn* err);
+ void (*flush)(GuOutStream* self, GuExn* err);
+};
+
+
+struct GuOut {
+ uint8_t* restrict buf_end;
+ ptrdiff_t buf_curr;
+ size_t buf_size;
+ GuOutStream* stream;
+ GuFinalizer fini;
+};
+
+
+GuOut
+gu_init_out(GuOutStream* stream);
+
+GuOut*
+gu_new_out(GuOutStream* stream, GuPool* pool);
+
+inline bool
+gu_out_is_buffered(GuOut* out)
+{
+ return !!out->stream->begin_buf;
+}
+
+GuOutStream*
+gu_out_proxy_stream(GuOut* out, GuPool* pool);
+
+GuOut*
+gu_new_buffered_out(GuOut* out, size_t buf_sz, GuPool* pool);
+
+GuOut*
+gu_out_buffered(GuOut* out, GuPool* pool);
+
+uint8_t*
+gu_out_begin_span(GuOut* out, size_t req, size_t* sz_out, GuExn* err);
+
+uint8_t*
+gu_out_force_span(GuOut* out, size_t min, size_t max, size_t* sz_out,
+ GuExn* err);
+
+void
+gu_out_end_span(GuOut* out, size_t sz);
+
+size_t
+gu_out_bytes_(GuOut* restrict out, const uint8_t* restrict src,
+ size_t len, GuExn* err);
+
+inline bool
+gu_out_try_buf_(GuOut* restrict out, const uint8_t* restrict src, size_t len)
+{
+ gu_require(len <= PTRDIFF_MAX);
+ ptrdiff_t curr = out->buf_curr;
+ ptrdiff_t new_curr = curr + (ptrdiff_t) len;
+ if (GU_UNLIKELY(new_curr > 0)) {
+ return false;
+ }
+ memcpy(&out->buf_end[curr], src, len);
+ out->buf_curr = new_curr;
+ return true;
+}
+
+inline size_t
+gu_out_bytes(GuOut* restrict out, const uint8_t* restrict src, size_t len,
+ GuExn* err)
+{
+ if (GU_LIKELY(gu_out_try_buf_(out, src, len))) {
+ return len;
+ }
+ return gu_out_bytes_(out, src, len, err);
+}
+
+void
+gu_out_flush(GuOut* out, GuExn* err);
+
+inline bool
+gu_out_try_u8_(GuOut* restrict out, uint8_t u)
+{
+ ptrdiff_t curr = out->buf_curr;
+ ptrdiff_t new_curr = curr + 1;
+ if (GU_UNLIKELY(new_curr > 0)) {
+ return false;
+ }
+ out->buf_end[curr] = u;
+ out->buf_curr = new_curr;
+ return true;
+}
+
+inline void
+gu_out_u8(GuOut* restrict out, uint8_t u, GuExn* err)
+{
+ if (GU_UNLIKELY(!gu_out_try_u8_(out, u))) {
+ extern void gu_out_u8_(GuOut* restrict out, uint8_t u,
+ GuExn* err);
+ gu_out_u8_(out, u, err);
+ }
+}
+
+inline void
+gu_out_s8(GuOut* restrict out, int8_t i, GuExn* err)
+{
+ gu_out_u8(out, (uint8_t) i, err);
+}
+
+
+
+void
+gu_out_u16le(GuOut* out, uint16_t u, GuExn* err);
+
+void
+gu_out_u16be(GuOut* out, uint16_t u, GuExn* err);
+
+void
+gu_out_s16le(GuOut* out, int16_t u, GuExn* err);
+
+void
+gu_out_s16be(GuOut* out, int16_t u, GuExn* err);
+
+void
+gu_out_u32le(GuOut* out, uint32_t u, GuExn* err);
+
+void
+gu_out_u32be(GuOut* out, uint32_t u, GuExn* err);
+
+void
+gu_out_s32le(GuOut* out, int32_t u, GuExn* err);
+
+void
+gu_out_s32be(GuOut* out, int32_t u, GuExn* err);
+
+void
+gu_out_u64le(GuOut* out, uint64_t u, GuExn* err);
+
+void
+gu_out_u64be(GuOut* out, uint64_t u, GuExn* err);
+
+void
+gu_out_s64le(GuOut* out, int64_t u, GuExn* err);
+
+void
+gu_out_s64be(GuOut* out, int64_t u, GuExn* err);
+
+void
+gu_out_f64le(GuOut* out, double d, GuExn* err);
+
+void
+gu_out_f64be(GuOut* out, double d, GuExn* err);
+
+#endif // GU_OUT_H_
diff --git a/src/runtime/c/gu/prime.c b/src/runtime/c/gu/prime.c
new file mode 100644
index 000000000..6452f8777
--- /dev/null
+++ b/src/runtime/c/gu/prime.c
@@ -0,0 +1,154 @@
+#include <gu/defs.h>
+#include <gu/assert.h>
+
+static const uint32_t gu_prime_wheel_mask = 0UL
+ | 1 << 1
+ | 1 << 7
+ | 1 << 11
+ | 1 << 13
+ | 1 << 17
+ | 1 << 19
+ | 1 << 23
+ | 1 << 29;
+
+static bool
+gu_prime_wheel(int i)
+{
+ gu_assert(i >= 0 && i < 30);
+ return !!(gu_prime_wheel_mask & (1 << i));
+}
+
+static const uint32_t gu_small_prime_mask = 0UL
+ | 1 << 2
+ | 1 << 3
+ | 1 << 5
+ | 1 << 7
+ | 1 << 11
+ | 1 << 13
+ | 1 << 17
+ | 1 << 19
+ | 1 << 23
+ | 1 << 29
+ | 1U << 31;
+
+static bool
+gu_is_wheel_prime(int u)
+{
+ gu_assert(u > 30 && u % 2 != 0 && u % 3 != 0 && u % 5 != 0);
+ int d = 0;
+ int i = 7;
+ goto start;
+ while (d * d <= u) {
+ for (i = 1; i <= 29; i+=2) {
+ start:
+ if (gu_prime_wheel(i) && u % (d + i) == 0) {
+ return false;
+ }
+ }
+ d += 30;
+ }
+ return true;
+}
+
+int
+gu_prime_inf(int i)
+{
+ if (i < 2) {
+ return 0;
+ } else if (i < 32) {
+ while (!(gu_small_prime_mask & (1 << i))) {
+ i--;
+ }
+ return i;
+ }
+
+ int d = (i - 1) | 1;
+ int r = d % 30;
+
+ while (!gu_prime_wheel(r) || !gu_is_wheel_prime(d)) {
+ d -= 2;
+ r -= 2;
+ if (r < 0) {
+ r += 30;
+ }
+ }
+ return d;
+}
+
+int
+gu_prime_sup(int i)
+{
+ if (i <= 2) {
+ return 2;
+ } else if (i < 32) {
+ while (!(gu_small_prime_mask & (1 << i))) {
+ i++;
+ }
+ return i;
+ }
+
+ int d = i | 1;
+ int r = d % 30;
+
+ while (!gu_prime_wheel(r) || !gu_is_wheel_prime(d)) {
+ d += 2;
+ r += 2;
+ if (r > 30) {
+ r -= 30;
+ }
+ }
+ return d;
+}
+
+bool
+gu_is_prime(int i)
+{
+ if (i < 2) {
+ return false;
+ } else if (i < 30) {
+ return !!(gu_small_prime_mask & (1 << i));
+ } else if (!gu_prime_wheel(i % 30)) {
+ return false;
+ } else {
+ return gu_is_wheel_prime(i);
+ }
+}
+
+bool
+gu_is_twin_prime(int i)
+{
+ return gu_is_prime(i) && gu_is_prime(i - 2);
+}
+
+int
+gu_twin_prime_inf(int i)
+{
+ while (true) {
+ i = gu_prime_inf(i);
+ if (i == 0) {
+ return 0;
+ } else if (gu_is_prime(i - 2)) {
+ return i;
+ }
+ i = i - 4;
+ }
+ return i;
+}
+
+int
+gu_twin_prime_sup(int i)
+{
+ if (i <= 5) {
+ return 5;
+ }
+ i = i - 2;
+ while (true) {
+ i = gu_prime_sup(i);
+ if (gu_is_prime(i + 2)) {
+ return i + 2;
+ }
+ i = i + 4;
+ }
+ return i;
+}
+
diff --git a/src/runtime/c/gu/prime.h b/src/runtime/c/gu/prime.h
new file mode 100644
index 000000000..2ae0617f8
--- /dev/null
+++ b/src/runtime/c/gu/prime.h
@@ -0,0 +1,16 @@
+#ifndef GU_PRIME_H_
+#define GU_PRIME_H_
+
+#include <gu/defs.h>
+
+bool gu_is_prime(int i);
+
+bool gu_is_twin_prime(int i);
+
+int gu_prime_inf(int i);
+int gu_twin_prime_inf(int i);
+
+int gu_prime_sup(int i);
+int gu_twin_prime_sup(int i);
+
+#endif // GU_PRIME_H_
diff --git a/src/runtime/c/gu/read.c b/src/runtime/c/gu/read.c
new file mode 100644
index 000000000..245c59f2c
--- /dev/null
+++ b/src/runtime/c/gu/read.c
@@ -0,0 +1,15 @@
+#include <gu/read.h>
+
+extern inline GuUCS
+gu_read_ucs(GuReader* rdr, GuExn* err);
+
+extern inline char
+gu_getc(GuReader* rdr, GuExn* err);
+
+GuReader*
+gu_new_utf8_reader(GuIn* utf8_in, GuPool* pool)
+{
+ GuReader* rdr = gu_new(GuReader, pool);
+ rdr->in_ = gu_init_in(gu_in_proxy_stream(utf8_in, pool));
+ return rdr;
+}
diff --git a/src/runtime/c/gu/read.h b/src/runtime/c/gu/read.h
new file mode 100644
index 000000000..f2496975d
--- /dev/null
+++ b/src/runtime/c/gu/read.h
@@ -0,0 +1,31 @@
+#ifndef GU_READ_H_
+#define GU_READ_H_
+
+#include <gu/in.h>
+#include <gu/ucs.h>
+#include <gu/utf8.h>
+
+typedef struct GuReader GuReader;
+
+struct GuReader {
+ GuIn in_;
+};
+
+inline GuUCS
+gu_read_ucs(GuReader* rdr, GuExn* err)
+{
+ return gu_in_utf8(&rdr->in_, err);
+}
+
+inline char
+gu_getc(GuReader* rdr, GuExn* err)
+{
+ return gu_in_utf8_char(&rdr->in_, err);
+}
+
+GuReader*
+gu_new_utf8_reader(GuIn* utf8_in, GuPool* pool);
+/**< @todo Implement. */
+
+
+#endif // GU_READ_H_
diff --git a/src/runtime/c/gu/seq.c b/src/runtime/c/gu/seq.c
new file mode 100644
index 000000000..660cf5af6
--- /dev/null
+++ b/src/runtime/c/gu/seq.c
@@ -0,0 +1,245 @@
+#include <gu/out.h>
+#include <gu/seq.h>
+#include <gu/fun.h>
+#include <gu/assert.h>
+#include <string.h>
+
+
+struct GuBuf {
+ uint8_t* data;
+ size_t elem_size;
+ size_t avail_len;
+ GuFinalizer fin;
+};
+
+GuBuf*
+gu_seq_buf(GuSeq seq)
+{
+ gu_require(gu_tagged_tag(seq.w_) == 0);
+ return gu_word_ptr(seq.w_);
+}
+
+GuSeq
+gu_buf_seq(GuBuf* buf)
+{
+ return (GuSeq) { .w_ = gu_ptr_word(buf) };
+}
+
+size_t
+gu_buf_length(GuBuf* dyn)
+{
+ return (size_t)(((GuWord*)(void*)dyn)[-1] >> 1);
+}
+
+size_t
+gu_buf_avail(GuBuf* buf)
+{
+ return buf->avail_len;
+}
+
+
+static void
+gu_buf_set_length(GuBuf* dyn, size_t new_len)
+{
+ ((GuWord*)(void*)dyn)[-1] = ((GuWord) new_len) << 1 | 0x1;
+}
+
+static void
+gu_buf_fini(GuFinalizer* fin)
+{
+ GuBuf* buf = gu_container(fin, GuBuf, fin);
+ gu_mem_buf_free(buf->data);
+}
+
+GuBuf*
+gu_make_buf(size_t elem_size, GuPool* pool)
+{
+ GuBuf* buf = gu_new_prefixed(unsigned, GuBuf, pool);
+ gu_buf_set_length(buf, 0);
+ buf->elem_size = elem_size;
+ buf->data = NULL;
+ buf->avail_len = 0;
+ buf->fin.fn = gu_buf_fini;
+ gu_pool_finally(pool, &buf->fin);
+ gu_buf_set_length(buf, 0);
+ return buf;
+}
+
+static const GuWord gu_empty_seq[2] = {0, 0};
+
+GuSeq
+gu_make_seq(size_t elem_size, size_t length, GuPool* pool)
+{
+ size_t size = elem_size * length;
+ if (0 < length && length <= GU_TAG_MAX) {
+ void* buf = gu_malloc(pool, size);
+ return (GuSeq) { gu_tagged(buf, length) };
+ } else if (size == 0) {
+ return (GuSeq) { gu_tagged((void*)&gu_empty_seq[1], 0) };
+ } else {
+ void* buf = gu_malloc_prefixed(pool,
+ gu_alignof(GuWord),
+ sizeof(GuWord),
+ 0, size);
+ ((GuWord*) buf)[-1] = ((GuWord) length) << 1;
+ return (GuSeq) { gu_tagged(buf, 0) };
+ }
+}
+
+static void
+gu_buf_require(GuBuf* buf, size_t req_len)
+{
+ if (req_len <= buf->avail_len) {
+ return;
+ }
+ size_t req_size = buf->elem_size * req_len;
+ size_t real_size;
+ buf->data = gu_mem_buf_realloc(buf->data, req_size,
+ &real_size);
+ buf->avail_len = real_size / buf->elem_size;
+}
+
+void*
+gu_buf_data(GuBuf* buf)
+{
+ return buf->data;
+}
+
+void*
+gu_buf_extend_n(GuBuf* buf, size_t n_elems)
+{
+ size_t len = gu_buf_length(buf);
+ size_t new_len = len + n_elems;
+ gu_buf_require(buf, new_len);
+ gu_buf_set_length(buf, new_len);
+ return &buf->data[buf->elem_size * len];
+}
+
+void*
+gu_buf_extend(GuBuf* buf)
+{
+ return gu_buf_extend_n(buf, 1);
+}
+
+void
+gu_buf_push_n(GuBuf* buf, const void* data, size_t n_elems)
+{
+
+ void* p = gu_buf_extend_n(buf, n_elems);
+ memcpy(p, data, buf->elem_size * n_elems);
+}
+
+const void*
+gu_buf_trim_n(GuBuf* buf, size_t n_elems)
+{
+ gu_require(n_elems <= gu_buf_length(buf));
+ size_t new_len = gu_buf_length(buf) - n_elems;
+ gu_buf_set_length(buf, new_len);
+ return &buf->data[buf->elem_size * new_len];
+}
+
+const void*
+gu_buf_trim(GuBuf* buf)
+{
+ return gu_buf_trim_n(buf, 1);
+}
+
+void
+gu_buf_pop_n(GuBuf* buf, size_t n_elems, void* data_out)
+{
+ const void* p = gu_buf_trim_n(buf, n_elems);
+ memcpy(data_out, p, buf->elem_size * n_elems);
+}
+
+GuSeq
+gu_buf_freeze(GuBuf* buf, GuPool* pool)
+{
+ size_t len = gu_buf_length(buf);
+ GuSeq seq = gu_make_seq(buf->elem_size, len, pool);
+ void* bufdata = gu_buf_data(buf);
+ void* seqdata = gu_seq_data(seq);
+ memcpy(seqdata, bufdata, buf->elem_size * len);
+ return seq;
+}
+
+typedef struct GuBufOut GuBufOut;
+struct GuBufOut
+{
+ GuOutStream stream;
+ GuBuf* buf;
+};
+
+static size_t
+gu_buf_out_output(GuOutStream* stream, const uint8_t* src, size_t sz,
+ GuExn* err)
+{
+ (void) err;
+ GuBufOut* bout = gu_container(stream, GuBufOut, stream);
+ GuBuf* buf = bout->buf;
+ gu_assert(sz % buf->elem_size == 0);
+ size_t len = sz / buf->elem_size;
+ gu_buf_push_n(bout->buf, src, len);
+ return len;
+}
+
+static uint8_t*
+gu_buf_outbuf_begin(GuOutStream* stream, size_t req, size_t* sz_out, GuExn* err)
+{
+ (void) req;
+ (void) err;
+ GuBufOut* bout = gu_container(stream, GuBufOut, stream);
+ GuBuf* buf = bout->buf;
+ size_t esz = buf->elem_size;
+ size_t len = gu_buf_length(buf);
+ gu_buf_require(buf, len + (req + esz - 1) / esz);
+ size_t avail = buf->avail_len;
+ gu_assert(len < avail);
+ *sz_out = esz * (avail - len);
+ return &buf->data[len * esz];
+}
+
+static void
+gu_buf_outbuf_end(GuOutStream* stream, size_t sz, GuExn* err)
+{
+ (void) err;
+ GuBufOut* bout = gu_container(stream, GuBufOut, stream);
+ GuBuf* buf = bout->buf;
+ size_t len = gu_buf_length(buf);
+ size_t elem_size = buf->elem_size;
+ gu_require(sz % elem_size == 0);
+ gu_require(sz < elem_size * (len - buf->avail_len));
+ gu_buf_set_length(buf, len + (sz / elem_size));
+}
+
+GuOut*
+gu_buf_out(GuBuf* buf, GuPool* pool)
+{
+ GuBufOut* bout = gu_new_i(pool, GuBufOut,
+ .stream.output = gu_buf_out_output,
+ .stream.begin_buf = gu_buf_outbuf_begin,
+ .stream.end_buf = gu_buf_outbuf_end,
+ .buf = buf);
+ return gu_new_out(&bout->stream, pool);
+}
+
+const GuSeq
+gu_null_seq = GU_NULL_SEQ;
+
+
+#include <gu/type.h>
+
+GU_DEFINE_KIND(GuSeq, GuOpaque);
+GU_DEFINE_KIND(GuBuf, abstract);
+
+GU_DEFINE_TYPE(GuChars, GuSeq, gu_type(char));
+GU_DEFINE_TYPE(GuBytes, GuSeq, gu_type(uint8_t));
+
+char*
+gu_chars_str(GuChars chars, GuPool* pool)
+{
+ size_t len = gu_seq_length(chars);
+ char* data = gu_seq_data(chars);
+ char* str = gu_new_str(len, pool);
+ memcpy(str, data, len);
+ return str;
+}
diff --git a/src/runtime/c/gu/seq.h b/src/runtime/c/gu/seq.h
new file mode 100644
index 000000000..257d71e5f
--- /dev/null
+++ b/src/runtime/c/gu/seq.h
@@ -0,0 +1,198 @@
+#ifndef GU_SEQ_H_
+#define GU_SEQ_H_
+
+#include <gu/mem.h>
+#include <gu/bits.h>
+
+
+typedef struct GuBuf GuBuf;
+
+typedef GuOpaque() GuSeq;
+
+GuSeq
+gu_make_seq(size_t elem_size, size_t len, GuPool* pool);
+
+#define gu_new_seq(T, N, POOL) \
+ gu_make_seq(sizeof(T), (N), (POOL))
+
+static inline size_t
+gu_seq_length(GuSeq seq)
+{
+ GuWord w = seq.w_;
+ size_t tag = gu_tagged_tag(w);
+ if (tag == 0) {
+ GuWord* p = gu_tagged_ptr(w);
+ return (size_t) (p[-1] >> 1);
+ }
+ return tag;
+}
+
+static inline void*
+gu_seq_data(GuSeq seq)
+{
+ GuWord w = seq.w_;
+ int tag = gu_tagged_tag(w);
+ void* ptr = gu_tagged_ptr(w);
+ if (tag == 0) {
+ GuWord* p = ptr;
+ if (p[-1] & 0x1) {
+ return *(uint8_t**) ptr;
+ }
+ }
+ return ptr;
+}
+
+static inline bool
+gu_seq_is_null(GuSeq seq)
+{
+ return (gu_tagged_ptr(seq.w_)) == NULL;
+}
+
+
+#define gu_seq_index(SEQ, T, I) \
+ (&((T*)gu_seq_data(SEQ))[I])
+
+#define gu_seq_get(SEQ, T, I) \
+ (*gu_seq_index(SEQ, T, I))
+
+#define gu_seq_set(SEQ, T, I, V) \
+ GU_BEGIN \
+ (*gu_seq_index(SEQ, T, I) = (V)); \
+ GU_END
+
+
+
+
+GuBuf*
+gu_seq_buf(GuSeq seq);
+
+GuSeq
+gu_buf_seq(GuBuf* buf);
+
+GuBuf*
+gu_make_buf(size_t elem_size, GuPool* pool);
+
+#define gu_new_buf(T, POOL) \
+ gu_make_buf(sizeof(T), (POOL))
+
+size_t
+gu_buf_length(GuBuf* buf);
+
+size_t
+gu_buf_avail(GuBuf* buf);
+
+void*
+gu_buf_data(GuBuf* buf);
+
+#define gu_buf_index(BUF, T, I) \
+ (&((T*)gu_buf_data(BUF))[I])
+
+#define gu_buf_get(BUF, T, I) \
+ (*gu_buf_index(BUF, T, I))
+
+#define gu_buf_set(BUF, T, I) \
+ GU_BEGIN \
+ (*gu_buf_index(BUF, T, I) = (V)); \
+ GU_END
+
+void
+gu_buf_push_n(GuBuf* buf, const void* elems, size_t n_elems);
+
+void*
+gu_buf_extend_n(GuBuf* buf, size_t n_elems);
+
+void*
+gu_buf_extend(GuBuf* buf);
+
+#define gu_buf_push(BUF, T, VAL) \
+ GU_BEGIN \
+ ((*(T*)gu_buf_extend(BUF)) = (VAL)); \
+ GU_END
+
+void
+gu_buf_pop_n(GuBuf* buf, size_t n_elems, void* data_out);
+
+const void*
+gu_buf_trim_n(GuBuf* buf, size_t n_elems);
+
+const void*
+gu_buf_trim(GuBuf* buf);
+
+#define gu_buf_pop(BUF, T) \
+ (*(T*)gu_buf_trim(BUF))
+
+void
+gu_seq_resize_tail(GuSeq seq, ptrdiff_t change);
+
+#if 0
+void
+gu_buf_resize_head(GuBuf* buf, ptrdiff_t change);
+
+void
+gu_buf_unshift(GuBuf* buf, const void* data, size_t size);
+
+void
+gu_buf_shift(GuBuf* buf, size_t size, void* data_out);
+#endif
+
+GuSeq
+gu_buf_freeze(GuBuf* buf, GuPool* pool);
+
+extern const GuSeq gu_null_seq;
+
+#define GU_NULL_SEQ { .w_ = (GuWord)(void*)NULL }
+
+typedef GuSeq GuChars;
+typedef GuSeq GuBytes;
+typedef GuBuf GuCharBuf;
+typedef GuBuf GuByteBuf;
+
+char*
+gu_chars_str(GuChars chars, GuPool* pool);
+
+#endif // GU_SEQ_H_
+
+#if defined(GU_OUT_H_) && !defined(GU_SEQ_H_OUT_)
+#define GU_SEQ_H_OUT_
+
+GuOut*
+gu_buf_out(GuBuf* buf, GuPool* pool);
+
+#endif
+
+
+#if defined(GU_TYPE_H_) && !defined(GU_SEQ_H_TYPE_)
+#define GU_SEQ_H_TYPE_
+
+extern GU_DECLARE_KIND(GuSeq);
+extern GU_DECLARE_KIND(GuBuf);
+
+struct GuSeqType {
+ GuType_GuOpaque opaque_base;
+ GuType* elem_type;
+};
+
+typedef const struct GuSeqType GuSeqType, GuType_GuSeq;
+
+#define GU_TYPE_INIT_GuSeq(k_, t_, elem_type_) { \
+ .opaque_base = GU_TYPE_INIT_GuOpaque(k_, t_, _), \
+ .elem_type = elem_type_, \
+}
+
+typedef struct GuBufType GuBufType, GuType_GuBuf;
+
+struct GuBufType {
+ GuType_abstract abstract_base;
+ GuType* elem_type;
+};
+
+#define GU_TYPE_INIT_GuBuf(KIND, BUF_T, ELEM_T) { \
+ .abstract_base = GU_TYPE_INIT_abstract(KIND, BUF_T, _), \
+ .elem_type = ELEM_T \
+}
+
+extern GU_DECLARE_TYPE(GuChars, GuSeq);
+extern GU_DECLARE_TYPE(GuBytes, GuSeq);
+
+#endif
+
diff --git a/src/runtime/c/gu/str.c b/src/runtime/c/gu/str.c
new file mode 100644
index 000000000..073781b23
--- /dev/null
+++ b/src/runtime/c/gu/str.c
@@ -0,0 +1,85 @@
+#include <gu/assert.h>
+#include <gu/str.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+const char gu_empty_str[] = "";
+const char* const gu_null_str = NULL;
+
+char*
+gu_new_str(size_t size, GuPool* pool)
+{
+ char* str = gu_new_n(char, size + 1, pool);
+ memset(str, '\0', size + 1);
+ return str;
+}
+
+char*
+gu_strdup(const char* cstr, GuPool* pool)
+{
+ int len = strlen(cstr);
+ char* str = gu_new_str(len, pool);
+ memcpy(str, cstr, len);
+ return str;
+}
+
+bool
+gu_str_eq(GuStr s1, GuStr s2)
+{
+ return (strcmp(s1, s2)) == 0;
+}
+
+static bool
+gu_str_is_equal(GuEquality* self, const void* p1, const void* p2)
+{
+ (void) self;
+ const GuStr* sp1 = p1;
+ const GuStr* sp2 = p2;
+ return gu_str_eq(*sp1, *sp2);
+}
+
+static GuHash
+gu_str_hasher_hash(GuHasher* self, const void* p)
+{
+ (void) self;
+ GuHash h = 0;
+ const GuStr* sp = p;
+ for (const char* s = *sp; *s != '\0'; s++) {
+ h = 101 * h + (unsigned char) *s;
+ }
+ return h;
+}
+
+GuHasher gu_str_hasher[1] = {
+ {
+ .eq = { .is_equal = gu_str_is_equal },
+ .hash = gu_str_hasher_hash
+ }
+};
+
+GU_DEFINE_TYPE(GuStr, repr, _);
+
+char*
+gu_vasprintf(const char* fmt, va_list args, GuPool* pool)
+{
+ va_list args2;
+ va_copy(args2, args);
+ int len = vsnprintf(NULL, 0, fmt, args2);
+ gu_assert_msg(len >= 0, "Invalid format string: \"%s\"", fmt);
+ va_end(args2);
+ char* str = gu_new_str(len, pool);
+ vsnprintf(str, len + 1, fmt, args);
+ return str;
+}
+
+char*
+gu_asprintf(GuPool* pool, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ char* str = gu_vasprintf(fmt, args, pool);
+ va_end(args);
+ return str;
+}
diff --git a/src/runtime/c/gu/str.h b/src/runtime/c/gu/str.h
new file mode 100644
index 000000000..d40f57b08
--- /dev/null
+++ b/src/runtime/c/gu/str.h
@@ -0,0 +1,29 @@
+#ifndef GU_STR_H_
+#define GU_STR_H_
+
+#include <gu/mem.h>
+#include <gu/hash.h>
+
+extern const char gu_empty_str[];
+extern const char* const gu_null_str;
+
+typedef const char* GuStr;
+
+char* gu_new_str(size_t size, GuPool* pool);
+
+char* gu_strdup(const char* str, GuPool* pool);
+
+bool
+gu_str_eq(GuStr s1, GuStr s2);
+
+extern GuHasher gu_str_hasher[1];
+
+#include <gu/type.h>
+
+extern GU_DECLARE_TYPE(GuStr, repr);
+
+char* gu_vasprintf(const char* fmt, va_list args, GuPool* pool);
+
+char* gu_asprintf(GuPool* pool, const char* fmt, ...);
+
+#endif // GU_STR_H_
diff --git a/src/runtime/c/gu/string.c b/src/runtime/c/gu/string.c
new file mode 100644
index 000000000..b24eeeb06
--- /dev/null
+++ b/src/runtime/c/gu/string.c
@@ -0,0 +1,270 @@
+#include <gu/type.h>
+#include <gu/out.h>
+#include <gu/seq.h>
+#include <gu/map.h>
+#include <gu/string.h>
+#include <gu/utf8.h>
+#include <gu/assert.h>
+#include "config.h"
+
+const GuString gu_empty_string = { 1 };
+
+struct GuStringBuf {
+ GuByteBuf* bbuf;
+ GuWriter* wtr;
+};
+
+GuStringBuf*
+gu_string_buf(GuPool* pool)
+{
+ GuBuf* buf = gu_new_buf(uint8_t, pool);
+ GuOut* out = gu_buf_out(buf, pool);
+ GuWriter* wtr = gu_new_utf8_writer(out, pool);
+ return gu_new_s(pool, GuStringBuf,
+ .bbuf = buf,
+ .wtr = wtr);
+}
+
+GuWriter*
+gu_string_buf_writer(GuStringBuf* sb)
+{
+ return sb->wtr;
+}
+
+static GuString
+gu_utf8_string(const uint8_t* buf, size_t sz, GuPool* pool)
+{
+ if (sz < GU_MIN(sizeof(GuWord), 128)) {
+ GuWord w = 0;
+ for (size_t n = 0; n < sz; n++) {
+ w = w << 8 | buf[n];
+ }
+ w = w << 8 | (sz << 1) | 1;
+ return (GuString) { w };
+ }
+ uint8_t* p = NULL;
+ if (sz < 256) {
+ p = gu_malloc_aligned(pool, 1 + sz, 2);
+ p[0] = (uint8_t) sz;
+ } else {
+ uint8_t* p =
+ gu_malloc_prefixed(pool, gu_alignof(size_t),
+ sizeof(size_t), 1, 1 + sizeof(sz));
+ ((size_t*) p)[-1] = sz;
+ p[0] = 0;
+ }
+ memcpy(&p[1], buf, sz);
+ return (GuString) { (GuWord) (void*) p };
+}
+
+
+
+GuString
+gu_string_buf_freeze(GuStringBuf* sb, GuPool* pool)
+{
+ gu_writer_flush(sb->wtr, NULL);
+ uint8_t* data = gu_buf_data(sb->bbuf);
+ size_t len = gu_buf_length(sb->bbuf);
+ return gu_utf8_string(data, len, pool);
+}
+
+GuReader*
+gu_string_reader(GuString s, GuPool* pool)
+{
+ GuWord w = s.w_;
+ uint8_t* buf = NULL;
+ size_t len = 0;
+ if (w & 1) {
+ len = (w & 0xff) >> 1;
+ buf = gu_new_n(uint8_t, len, pool);
+ for (int i = len - 1; i >= 0; i--) {
+ w >>= 8;
+ buf[i] = w & 0xff;
+ }
+ } else {
+ uint8_t* p = (void*) w;
+ len = (p[0] == 0) ? ((size_t*) p)[-1] : p[0];
+ buf = &p[1];
+ }
+ GuIn* in = gu_data_in(buf, len, pool);
+ GuReader* rdr = gu_new_utf8_reader(in, pool);
+ return rdr;
+}
+
+static bool
+gu_string_is_long(GuString s)
+{
+ return !(s.w_ & 1);
+}
+
+bool
+gu_string_is_stable(GuString s)
+{
+ return !gu_string_is_long(s);
+}
+
+static size_t
+gu_string_long_length(GuString s)
+{
+ gu_assert(gu_string_is_long(s));
+ uint8_t* p = (void*) s.w_;
+ uint8_t len = p[0];
+ if (len > 0) {
+ return len;
+ }
+ return ((size_t*) p)[-1];
+}
+
+size_t
+gu_string_length(GuString s)
+{
+ if (gu_string_is_long(s)) {
+ return gu_string_long_length(s);
+ }
+ return (s.w_ & 0xff) >> 1;
+}
+
+static uint8_t*
+gu_string_long_data(GuString s)
+{
+ gu_require(gu_string_is_long(s));
+ uint8_t* p = (void*) s.w_;
+ return &p[1];
+}
+
+GuString
+gu_string_copy(GuString string, GuPool* pool)
+{
+ if (gu_string_is_long(string)) {
+ uint8_t* data = gu_string_long_data(string);
+ size_t len = gu_string_long_length(string);
+ return gu_utf8_string(data, len, pool);
+ } else {
+ return string;
+ }
+}
+
+
+void
+gu_string_write(GuString s, GuWriter* wtr, GuExn* err)
+{
+ GuWord w = s.w_;
+ uint8_t buf[sizeof(GuWord)];
+ uint8_t* src;
+ size_t sz;
+ if (w & 1) {
+ sz = (w & 0xff) >> 1;
+ gu_assert(sz <= sizeof(GuWord));
+ size_t i = sz;
+ while (i > 0) {
+ w >>= 8;
+ buf[--i] = w & 0xff;
+ }
+ src = buf;
+ } else {
+ uint8_t* p = (void*) w;
+ sz = (p[0] == 0) ? ((size_t*) p)[-1] : p[0];
+ src = &p[1];
+ }
+ gu_utf8_write(src, sz, wtr, err);
+}
+
+GuString
+gu_format_string_v(const char* fmt, va_list args, GuPool* pool)
+{
+ GuPool* tmp_pool = gu_local_pool();
+ GuStringBuf* sb = gu_string_buf(tmp_pool);
+ GuWriter* wtr = gu_string_buf_writer(sb);
+ gu_vprintf(fmt, args, wtr, NULL);
+ gu_writer_flush(wtr, NULL);
+ GuString s = gu_string_buf_freeze(sb, pool);
+ gu_pool_free(tmp_pool);
+ return s;
+}
+
+GuString
+gu_format_string(GuPool* pool, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ GuString s = gu_format_string_v(fmt, args, pool);
+ va_end(args);
+ return s;
+}
+
+GuString
+gu_str_string(const char* str, GuPool* pool)
+{
+#ifdef GU_CHAR_ASCII
+ return gu_utf8_string((const uint8_t*) str, strlen(str), pool);
+#else
+ GuPool* tmp_pool = gu_local_pool();
+ GuStringBuf* sb = gu_string_buf(tmp_pool);
+ GuWriter* wtr = gu_string_buf_writer(sb);
+ gu_puts(str, wtr, NULL);
+ gu_writer_flush(wtr, NULL);
+ GuString s = gu_string_buf_freeze(sb, pool);
+ gu_pool_free(tmp_pool);
+ return s;
+#endif
+}
+
+GuWord
+gu_string_hash(GuString s)
+{
+ if (s.w_ & 1) {
+ return s.w_;
+ }
+ size_t len = gu_string_length(s);
+ uint8_t* data = gu_string_long_data(s);
+ return gu_hash_bytes(0, data, len);
+}
+
+bool
+gu_string_eq(GuString s1, GuString s2)
+{
+ if (s1.w_ == s2.w_) {
+ return true;
+ } else if (gu_string_is_long(s1) && gu_string_is_long(s2)) {
+ size_t len1 = gu_string_long_length(s1);
+ size_t len2 = gu_string_long_length(s2);
+ if (len1 != len2) {
+ return false;
+ }
+ uint8_t* data1 = gu_string_long_data(s1);
+ uint8_t* data2 = gu_string_long_data(s2);
+ return (memcmp(data1, data2, len1) == 0);
+ }
+ return false;
+
+}
+
+
+static GuHash
+gu_string_hasher_hash(GuHasher* self, const void* p)
+{
+ (void) self;
+ const GuString* sp = p;
+ return gu_string_hash(*sp);
+}
+
+static bool
+gu_string_eq_fn(GuEquality* self, const void* p1, const void* p2)
+{
+ (void) self;
+ const GuString* sp1 = p1;
+ const GuString* sp2 = p2;
+ return gu_string_eq(*sp1, *sp2);
+}
+
+GuHasher gu_string_hasher[1] = {
+ {
+ .eq = { gu_string_eq_fn },
+ .hash = gu_string_hasher_hash
+ }
+};
+
+
+GU_DEFINE_TYPE(GuString, GuOpaque, _);
+GU_DEFINE_TYPE(GuStrings, GuSeq, gu_type(GuString));
+GU_DEFINE_KIND(GuStringMap, GuMap);
diff --git a/src/runtime/c/gu/string.h b/src/runtime/c/gu/string.h
new file mode 100644
index 000000000..385b162ed
--- /dev/null
+++ b/src/runtime/c/gu/string.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2011 University of Helsinki.
+ *
+ * This file is part of libgu.
+ *
+ * Libgu is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libgu is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libgu. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GU_STRING_H_
+#define GU_STRING_H_
+
+#include <gu/bits.h>
+#include <gu/read.h>
+#include <gu/write.h>
+
+typedef GuOpaque() GuString;
+
+extern const GuString gu_empty_string;
+
+GuString
+gu_string_copy(GuString string, GuPool* pool);
+
+void
+gu_string_write(GuString string, GuWriter* wtr, GuExn* err);
+
+GuReader*
+gu_string_reader(GuString string, GuPool* pool);
+
+bool
+gu_string_is_stable(GuString string);
+
+GuString
+gu_ucs_string(const GuUCS* ubuf, size_t len, GuPool* pool);
+
+typedef struct GuStringBuf GuStringBuf;
+
+GuStringBuf*
+gu_string_buf(GuPool* pool);
+
+GuWriter*
+gu_string_buf_writer(GuStringBuf* sb);
+
+GuString
+gu_string_buf_freeze(GuStringBuf* sb, GuPool* pool);
+
+GuString
+gu_format_string_v(const char* fmt, va_list args, GuPool* pool);
+
+GuString
+gu_format_string(GuPool* pool, const char* fmt, ...);
+
+GuString
+gu_str_string(const char* str, GuPool* pool);
+
+#endif // GU_STRING_H_
+
+#if defined(GU_HASH_H_) && !defined(GU_STRING_H_HASH_)
+#define GU_STRING_H_HASH_
+
+uintptr_t
+gu_string_hash(GuString s);
+
+extern GuHasher gu_string_hasher[1];
+
+bool
+gu_string_eq(GuString s1, GuString s2);
+#endif
+
+#ifdef GU_TYPE_H_
+# ifndef GU_STRING_H_TYPE_
+# define GU_STRING_H_TYPE_
+
+extern GU_DECLARE_TYPE(GuString, GuOpaque);
+# endif
+
+# if defined(GU_SEQ_H_) && !defined(GU_STRING_H_SEQ_TYPE_)
+# define GU_STRING_H_SEQ_TYPE_
+extern GU_DECLARE_TYPE(GuStrings, GuSeq);
+# endif
+
+# if defined(GU_MAP_H_TYPE_) && !defined(GU_STRING_H_MAP_TYPE_)
+# define GU_STRING_H_MAP_TYPE_
+
+extern GU_DECLARE_KIND(GuStringMap);
+typedef GuType_GuMap GuType_GuStringMap;
+
+#define GU_TYPE_INIT_GuStringMap(KIND, MAP_T, VAL_T, DEFAULT) \
+ GU_TYPE_INIT_GuMap(KIND, MAP_T, \
+ gu_type(GuString), gu_string_hasher, \
+ VAL_T, DEFAULT)
+
+# endif
+#endif
+
+
+#if defined(GU_SEQ_H_) && !defined(GU_STRING_H_SEQ_)
+#define GU_STRING_H_SEQ_
+
+typedef GuSeq GuStrings;
+// typedef GuBuf GuStringBuf;
+
+#endif
+
+
+#if defined(GU_MAP_H_) && !defined(GU_STRING_H_MAP_)
+#define GU_STRING_H_MAP_
+
+typedef GuMap GuStringMap;
+
+#define gu_new_string_map(VAL_T, DEFAULT, POOL) \
+ gu_new_map(GuString, gu_string_hasher, (VAL_T), (DEFAULT), (POOL))
+
+#endif
+
diff --git a/src/runtime/c/gu/sysdeps.h b/src/runtime/c/gu/sysdeps.h
new file mode 100644
index 000000000..114e1c40d
--- /dev/null
+++ b/src/runtime/c/gu/sysdeps.h
@@ -0,0 +1,30 @@
+#ifndef GU_SYSDEPS_H_
+#define GU_SYSDEPS_H_
+
+#include <guconfig.h>
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+# define GU_GNUC
+#endif
+
+#ifdef GU_GNUC
+# define GU_ALIGNOF __alignof
+# define GU_HAVE_STATEMENT_EXPRESSIONS
+# define GU_GNUC_ATTR(x) __attribute__(( x ))
+# if defined(__OPTIMIZE_SIZE__)
+# define GU_OPTIMIZE_SIZE
+# elif defined(__OPTIMIZE__)
+# define GU_OPTIMIZE_SPEED
+# endif
+#else
+# define GU_GNUC_ATTR(x)
+#endif
+
+#ifdef S_SPLINT_S
+# define GU_SPLINT(x) %{ x %}
+#else
+# define GU_SPLINT(x)
+#endif
+
+
+#endif // GU_SYSDEPS_H_
diff --git a/src/runtime/c/gu/type.c b/src/runtime/c/gu/type.c
new file mode 100644
index 000000000..13fbfa42d
--- /dev/null
+++ b/src/runtime/c/gu/type.c
@@ -0,0 +1,229 @@
+
+#include <gu/type.h>
+#include <gu/assert.h>
+#include <gu/map.h>
+
+GuKind GU_TYPE_IDENT(type)[1] = {{ .super = NULL }};
+
+GU_DEFINE_KIND(alias, type);
+GU_DEFINE_KIND(typedef, alias);
+GU_DEFINE_KIND(referenced, alias);
+
+GU_DEFINE_KIND(repr, type);
+GU_DEFINE_KIND(GuOpaque, repr);
+
+GU_DEFINE_KIND(abstract, type);
+
+GU_DEFINE_KIND(struct, repr);
+
+GU_DEFINE_KIND(pointer, repr);
+GU_DEFINE_KIND(reference, pointer);
+GU_DEFINE_KIND(shared, pointer);
+
+GU_DEFINE_KIND(primitive, repr);
+
+// sizeof(void) is illegal, so do this manually
+GuPrimType GU_TYPE_IDENT(void)[1] = {{
+ .repr_base = {
+ .type_base = {
+ .kind_base = {
+ .super = gu_kind(primitive),
+ },
+ },
+ .size = 0,
+ .align = 1,
+ },
+ .name = "void",
+}};
+
+GU_DEFINE_KIND(integer, primitive);
+GU_DEFINE_TYPE(char, integer, _);
+
+GU_DEFINE_KIND(signed, integer);
+GU_DEFINE_TYPE(int, signed, _);
+GU_DEFINE_TYPE(int8_t, signed, _);
+GU_DEFINE_TYPE(int16_t, signed, _);
+GU_DEFINE_TYPE(int32_t, signed, _);
+GU_DEFINE_TYPE(int64_t, signed, _);
+GU_DEFINE_TYPE(intptr_t, signed, _);
+GU_DEFINE_TYPE(intmax_t, signed, _);
+
+GU_DEFINE_KIND(unsigned, integer);
+GU_DEFINE_TYPE(uint8_t, unsigned, _);
+GU_DEFINE_TYPE(uint16_t, unsigned, _);
+GU_DEFINE_TYPE(uint32_t, unsigned, _);
+GU_DEFINE_TYPE(uint64_t, unsigned, _);
+GU_DEFINE_TYPE(uintmax_t, unsigned, _);
+GU_DEFINE_TYPE(size_t, unsigned, _);
+
+GU_DEFINE_TYPE(GuLength, unsigned, _);
+
+GU_DEFINE_KIND(GuFloating, primitive);
+GU_DEFINE_TYPE(float, GuFloating, _);
+GU_DEFINE_TYPE(double, GuFloating, _);
+GU_DEFINE_TYPE(GuLongDouble, GuFloating, _);
+
+
+GU_DEFINE_KIND(enum, repr);
+
+bool gu_type_has_kind(GuType* type, GuKind* kind)
+{
+ GuKind* k = (GuKind*)type;
+ while (k != NULL) {
+ if (k == kind) {
+ return true;
+ }
+ k = k->super;
+ }
+ return false;
+}
+
+
+struct GuTypeMap {
+ GuMap* map;
+};
+
+static void
+gu_type_map_init(GuTypeMap* tmap, GuTypeTable* table)
+{
+ for (int i = 0; i < table->parents.len; i++) {
+ gu_type_map_init(tmap, table->parents.elems[i]);
+ }
+ for (int i = 0; i < table->entries.len; i++) {
+ GuTypeTableEntry* e = &table->entries.elems[i];
+ gu_map_put(tmap->map, e->kind, void*, e->val);
+ }
+}
+
+GuTypeMap*
+gu_new_type_map(GuTypeTable* table, GuPool* pool)
+{
+ GuTypeMap* tmap =
+ gu_new_i(pool, GuTypeMap,
+ .map = gu_new_map(GuKind, NULL, void*, &gu_null, pool));
+ gu_type_map_init(tmap, table);
+ return tmap;
+}
+
+bool
+gu_struct_has_flex(GuStructRepr* srepr)
+{
+ for (int i = 0; i < srepr->members.len; i++) {
+ if (srepr->members.elems[i].is_flex) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void*
+gu_type_map_get(GuTypeMap* tmap, GuType* type)
+{
+ GuKind* kind = (GuKind*)type;
+ while (kind != NULL) {
+ void* val = gu_map_get(tmap->map, kind, void*);
+ if (val != NULL) {
+ return val;
+ }
+ kind = kind->super;
+ }
+ return NULL;
+}
+
+const void*
+gu_type_dyn_cast(GuType* type, GuKind* kind)
+{
+ if (gu_type_has_kind(type, kind)) {
+ return type;
+ }
+ return NULL;
+}
+
+
+const void*
+gu_type_check_cast(GuType* type, GuKind* kind)
+{
+ gu_assert(gu_type_has_kind(type, kind));
+ return type;
+}
+
+GuTypeRepr*
+gu_type_repr(GuType* type)
+{
+ GuTypeAlias* alias;
+ while ((alias = gu_type_try_cast(type, alias))) {
+ type = alias->type;
+ }
+ return gu_type_try_cast(type, repr);
+}
+
+size_t
+gu_type_size(GuType* type)
+{
+ GuTypeRepr* repr = gu_type_repr(type);
+ return repr ? repr->size : 0;
+}
+
+GuEnumConstant*
+gu_enum_value(GuEnumType* etype, const void* enump)
+{
+ size_t esize = etype->repr_base.size;
+#define CHECK_ENUM_TYPE(t_) do { \
+ if (esize == sizeof(t_)) { \
+ t_ c = *(const t_*)enump; \
+ for (int i = 0; i < etype->constants.len; i++) { \
+ GuEnumConstant* cp = &etype->constants.elems[i]; \
+ t_ d = *(const t_*)cp->enum_value; \
+ if (c == d) { \
+ return cp; \
+ } \
+ } \
+ return NULL; \
+ } \
+ } while (false)
+
+ CHECK_ENUM_TYPE(int);
+ CHECK_ENUM_TYPE(char);
+ CHECK_ENUM_TYPE(short);
+ CHECK_ENUM_TYPE(long);
+ CHECK_ENUM_TYPE(long long);
+
+ return NULL;
+}
+
+void*
+gu_type_malloc(GuType* type, GuPool* pool)
+{
+ GuTypeRepr* repr = gu_type_repr(type);
+ gu_assert(repr);
+ return gu_malloc_aligned(pool, repr->size, repr->align);
+}
+
+#if 0
+
+typedef const struct GuPtrConvFns GuPtrConvFns;
+
+struct GuPtrConvFns {
+ void* (*get)(const void* pp);
+ void (*set)(void** pp, void* p);
+};
+
+#define GU_TYPE_PTR_DEFINE_GETSET(name_, t_) \
+ static void* gu_type_##name_##_ptr_get(const void* pp) { \
+ return *(t_* const*) pp; \
+ } \
+ \
+ static void gu_type_##name_##_ptr_set(void* pp, void* p) { \
+ *(t_**) pp = p; \
+ } \
+ static GuPtrConvFns gu_ptr_conv_##name_ = { \
+ .get = gu_type_##name_##_ptr_get, \
+ .set = gu_type_##name_##_ptr_set \
+ }
+
+GU_TYPE_PTR_DEFINE_GETSET(void, void);
+GU_TYPE_PTR_DEFINE_GETSET(struct, GuStruct);
+GU_TYPE_PTR_DEFINE_GETSET(int, int);
+
+
+#endif
diff --git a/src/runtime/c/gu/type.h b/src/runtime/c/gu/type.h
new file mode 100644
index 000000000..777b2e1f9
--- /dev/null
+++ b/src/runtime/c/gu/type.h
@@ -0,0 +1,454 @@
+
+#ifndef GU_TYPE_H_
+#define GU_TYPE_H_
+
+#include <gu/defs.h>
+
+//
+// kind
+//
+
+typedef const struct GuKind GuKind;
+
+struct GuKind {
+ GuKind* super;
+};
+
+// Use GU_PASTE here so k_ can be preprocessor-expanded
+#define GU_TYPE_IDENT(k_) GU_PASTE(gu_type__,k_)
+
+#define gu_kind(k_) ((GuKind*)GU_TYPE_IDENT(k_))
+
+#define GU_DECLARE_KIND(k_) \
+ GuKind GU_TYPE_IDENT(k_)[1]
+
+extern GU_DECLARE_KIND(kind);
+
+#define GU_DEFINE_KIND(k_, super_k_) \
+ GuKind GU_TYPE_IDENT(k_)[1] = {{ .super = gu_kind(super_k_) }}
+
+//
+// type
+//
+
+typedef const struct GuType GuType;
+
+struct GuType {
+ GuKind kind_base;
+};
+
+typedef GuType GuType_type;
+
+extern GU_DECLARE_KIND(type);
+
+#define GU_TYPE_INIT_type(k_, t_, _) { .kind_base = { .super = gu_kind(k_) } }
+
+#define gu_type(t_) ((GuType*)gu_kind(t_))
+
+
+#define GU_KIND_TYPE(k_) GU_PASTE(GuType_,k_)
+
+// This cannot be used indirectly, since we don't want to pp-expand k_.
+// We must inline the body into other macros.
+#define GU_TYPE_INIT(k_, ...) \
+ GU_TYPE_INIT_##k_(k_, __VA_ARGS__)
+
+//#define GU_TYPE_LIT(k_, ...)
+// ((GuType*)(GuType_##k_[]){GU_TYPE_INIT(k_, __VA_ARGS__)})
+#define GU_TYPE_LIT(k_, ...) \
+ ((GuType*)&(GU_KIND_TYPE(k_)) GU_TYPE_INIT_##k_(k_, __VA_ARGS__))
+
+#define GU_DECLARE_TYPE(t_, k_) \
+ GU_KIND_TYPE(k_) GU_TYPE_IDENT(t_)[1]
+
+//#define GU_DEFINE_TYPE(t_, k_, ...)
+// GuType_##k_ GU_TYPE_IDENT(t_) = GU_TYPE_INIT(k_, t_, __VA_ARGS__)
+#define GU_DEFINE_TYPE(t_, k_, ...) \
+ GU_KIND_TYPE(k_) GU_TYPE_IDENT(t_)[1] = \
+ { GU_TYPE_INIT_##k_(k_, t_, __VA_ARGS__) }
+
+#define GU_DEFINE_TYPE_ALIAS(t1_, t2_) \
+ static GuType* const GU_TYPE_IDENT(t1_) = gu_type(t2_)
+
+
+//
+// abstract
+//
+
+typedef GuType GuType_abstract;
+
+#define GU_TYPE_INIT_abstract(k_, t_, _) \
+ GU_TYPE_INIT_type(k_, t_, _)
+
+extern GU_DECLARE_KIND(abstract);
+
+
+//
+// repr
+//
+
+typedef struct GuTypeRepr GuTypeRepr, GuType_repr;
+
+struct GuTypeRepr {
+ GuType type_base;
+ uint16_t size;
+ uint16_t align;
+};
+
+#define GU_TYPE_INIT_repr(k_, t_, _) { \
+ .type_base = GU_TYPE_INIT_type(k_, t_, _), \
+ .size = sizeof(t_), \
+ .align = gu_alignof(t_) \
+ }
+
+extern GU_DECLARE_KIND(repr);
+
+
+
+//
+// GuOpaque
+//
+
+typedef GuType_repr GuType_GuOpaque;
+
+#define GU_TYPE_INIT_GuOpaque GU_TYPE_INIT_repr
+
+extern GU_DECLARE_KIND(GuOpaque);
+
+//
+// pointer
+//
+
+typedef const struct GuPointerType GuPointerType, GuType_pointer;
+
+struct GuPointerType {
+ GuType_repr repr_base;
+ GuType* pointed_type;
+};
+
+#define GU_TYPE_INIT_pointer(k_, t_, pointed_) \
+ { \
+ .repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
+ .pointed_type = pointed_ \
+}
+
+
+extern GU_DECLARE_KIND(pointer);
+
+#define gu_ptr_type(t_) \
+ GU_TYPE_LIT(pointer, t_*, gu_type(t_))
+
+
+
+
+
+
+
+//
+// alias
+//
+
+
+typedef const struct GuTypeAlias GuTypeAlias, GuType_alias;
+
+struct GuTypeAlias {
+ GuType type_base;
+ GuType* type;
+};
+
+#define GU_TYPE_INIT_alias(k_, t_, type_) { \
+ .type_base = GU_TYPE_INIT_type(k_, t_, _), \
+ .type = type_ \
+}
+
+extern GU_DECLARE_KIND(alias);
+
+//
+// typedef
+//
+
+typedef const struct GuTypeDef GuTypeDef, GuType_typedef;
+
+struct GuTypeDef {
+ GuType_alias alias_base;
+ const char* name;
+};
+
+#define GU_TYPE_INIT_typedef(k_, t_, type_) { \
+ .alias_base = GU_TYPE_INIT_alias(k_, t_, type_), \
+ .name = #t_, \
+}
+
+extern GU_DECLARE_KIND(typedef);
+
+#define GU_DEFINE_TYPEDEF_X(t_, dk_, k_, ...) \
+ GU_DEFINE_TYPE(t_, dk_, GU_TYPE_LIT(k_, t_, __VA_ARGS__))
+
+#define GU_DEFINE_TYPEDEF(t_, ...) \
+ GU_DEFINE_TYPEDEF_X(t_, typedef, __VA_ARGS__)
+
+
+
+//
+// referenced
+//
+
+extern GU_DECLARE_KIND(referenced);
+
+typedef GuType_alias GuType_referenced;
+
+#define GU_TYPE_INIT_referenced GU_TYPE_INIT_alias
+
+
+
+#include <gu/list.h>
+
+//
+// struct
+//
+
+typedef const struct GuStructRepr GuStructRepr, GuType_struct;
+
+typedef const struct GuMember GuMember;
+
+struct GuMember {
+ ptrdiff_t offset;
+ const char* name;
+ GuType* type;
+ bool is_flex;
+};
+
+struct GuStructRepr {
+ GuType_repr repr_base;
+ const char* name;
+ GuSList(GuMember) members;
+};
+
+extern GU_DECLARE_KIND(struct);
+
+#define GU_MEMBER_AUX_(struct_, member_, type_, is_flex_) \
+ { \
+ .offset = offsetof(struct_, member_), \
+ .name = #member_, \
+ .type = type_, \
+ .is_flex = is_flex_, \
+ }
+
+#define GU_MEMBER_V(struct_, member_, type_) \
+ GU_MEMBER_AUX_(struct_, member_, type_, false)
+
+#define GU_MEMBER(s_, m_, t_) \
+ GU_MEMBER_V(s_, m_, gu_type(t_))
+
+#define GU_MEMBER_P(s_, m_, t_) \
+ GU_MEMBER_V(s_, m_, gu_ptr_type(t_))
+
+#define GU_MEMBER_S(s_, m_, t_) \
+ GU_MEMBER_V(s_, m_, gu_shared_ptr_type(t_))
+
+#define GU_FLEX_MEMBER_V(struct_, member_, type_) \
+ GU_MEMBER_AUX_(struct_, member_, type_, true)
+
+#define GU_FLEX_MEMBER(s_, m_, t_) \
+ GU_FLEX_MEMBER_V(s_, m_, gu_type(t_))
+
+#define GU_FLEX_MEMBER_P(s_, m_, t_) \
+ GU_FLEX_MEMBER_V(s_, m_, gu_ptr_type(t_))
+
+
+#define GU_TYPE_INIT_struct(k_, t_, ...) { \
+ .repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
+ .name = #t_, \
+ .members = GU_SLIST(GuMember, __VA_ARGS__) \
+}
+
+bool
+gu_struct_has_flex(GuStructRepr* srepr);
+
+
+//
+// reference
+//
+
+typedef GuType_pointer GuType_reference;
+
+#define GU_TYPE_INIT_reference GU_TYPE_INIT_pointer
+
+extern GU_DECLARE_KIND(reference);
+
+
+//
+// shared
+//
+
+typedef GuType_pointer GuType_shared;
+
+#define GU_TYPE_INIT_shared GU_TYPE_INIT_pointer
+
+extern GU_DECLARE_KIND(shared);
+
+#define gu_shared_ptr_type(t_) \
+ GU_TYPE_LIT(shared, t_*, gu_type(t_))
+
+//
+// primitives
+//
+
+typedef const struct GuPrimType GuPrimType, GuType_primitive;
+
+struct GuPrimType {
+ GuType_repr repr_base;
+ const char* name;
+};
+
+#define GU_TYPE_INIT_primitive(k_, t_, _) { \
+ .repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
+ .name = #t_ \
+}
+
+extern GU_DECLARE_KIND(primitive);
+extern GU_DECLARE_TYPE(void, primitive);
+
+#define GU_TYPE_INIT_integer GU_TYPE_INIT_primitive
+typedef GuType_primitive GuType_integer;
+extern GU_DECLARE_KIND(integer);
+extern GU_DECLARE_TYPE(char, integer);
+
+#define GU_TYPE_INIT_signed GU_TYPE_INIT_integer
+typedef GuType_integer GuType_signed;
+extern GU_DECLARE_KIND(signed);
+extern GU_DECLARE_TYPE(int, signed);
+extern GU_DECLARE_TYPE(int8_t, signed);
+extern GU_DECLARE_TYPE(int16_t, signed);
+extern GU_DECLARE_TYPE(int32_t, signed);
+extern GU_DECLARE_TYPE(int64_t, signed);
+extern GU_DECLARE_TYPE(intptr_t, signed);
+extern GU_DECLARE_TYPE(intmax_t, signed);
+
+#define GU_TYPE_INIT_unsigned GU_TYPE_INIT_integer
+typedef GuType_integer GuType_unsigned;
+extern GU_DECLARE_KIND(unsigned);
+extern GU_DECLARE_TYPE(uint8_t, unsigned);
+extern GU_DECLARE_TYPE(uint16_t, unsigned);
+extern GU_DECLARE_TYPE(uint32_t, unsigned);
+extern GU_DECLARE_TYPE(uint64_t, unsigned);
+extern GU_DECLARE_TYPE(uintmax_t, unsigned);
+extern GU_DECLARE_TYPE(size_t, unsigned);
+
+typedef size_t GuLength;
+extern GU_DECLARE_TYPE(GuLength, unsigned); // TODO: get rid
+
+
+#define GU_TYPE_INIT_GuFloating GU_TYPE_INIT_primitive
+typedef GuType_primitive GuType_GuFloating;
+extern GU_DECLARE_KIND(GuFloating);
+extern GU_DECLARE_TYPE(float, GuFloating);
+extern GU_DECLARE_TYPE(double, GuFloating);
+typedef long double GuLongDouble;
+extern GU_DECLARE_TYPE(GuLongDouble, GuFloating);
+
+
+
+//
+// enum
+//
+
+extern GU_DECLARE_KIND(enum);
+
+typedef const struct GuEnumConstant GuEnumConstant;
+
+struct GuEnumConstant {
+ const char* name;
+ int64_t value;
+ const void* enum_value;
+};
+
+typedef const struct GuEnumType GuEnumType, GuType_enum;
+
+struct GuEnumType {
+ GuType_repr repr_base;
+ GuSList(GuEnumConstant) constants;
+};
+
+#define GU_ENUM_C(t_, x) { \
+ .name = #x, \
+ .value = x, \
+ .enum_value = (const t_[1]){ x } \
+ }
+
+#define GU_TYPE_INIT_enum(k_, t_, ...) { \
+ .repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
+ .constants = GU_SLIST(GuEnumConstant, __VA_ARGS__) \
+}
+
+GuEnumConstant*
+gu_enum_value(GuEnumType* etype, const void* enump);
+
+
+
+
+bool gu_type_has_kind(const GuType* type, const GuKind* kind);
+
+
+
+
+typedef const struct GuTypeTableEntry GuTypeTableEntry;
+
+struct GuTypeTableEntry {
+ GuKind* kind;
+ void* val;
+};
+
+typedef const struct GuTypeTable GuTypeTable;
+
+struct GuTypeTable {
+ GuSList(const GuTypeTable*) parents;
+ GuSList(GuTypeTableEntry) entries;
+};
+
+#define GU_TYPETABLE(parents_, ...) { \
+ .parents = parents_, \
+ .entries = GU_SLIST(GuTypeTableEntry, \
+ __VA_ARGS__) \
+ }
+
+typedef struct GuTypeMap GuTypeMap;
+
+GuTypeMap*
+gu_new_type_map(GuTypeTable* table, GuPool* pool);
+
+void*
+gu_type_map_get(GuTypeMap* tmap, GuType* type);
+
+size_t
+gu_type_size(GuType* type);
+
+GuTypeRepr*
+gu_type_repr(GuType* type);
+
+const void*
+gu_type_check_cast(GuType* t, GuKind* k);
+
+const void*
+gu_type_dyn_cast(GuType* t, GuKind* k);
+
+#define gu_type_try_cast(type_, k_) \
+ ((GU_KIND_TYPE(k_)*)gu_type_dyn_cast(type_, gu_kind(k_)))
+
+#ifndef NDEBUG
+#define gu_type_cast(type_, k_) \
+ ((GU_KIND_TYPE(k_)*)gu_type_check_cast(type_, gu_kind(k_)))
+#else
+#define gu_type_cast(type_, k_) \
+ ((GU_KIND_TYPE(k_)*)(type_))
+#endif
+
+void* gu_type_malloc(GuType* type, GuPool* pool);
+
+#if 0
+void* gu_type_ptr_get(GuType* type, const void* pp);
+void gu_type_ptr_set(GuType* type, void* pp, void* p);
+#endif
+
+
+#endif // GU_TYPE_H_
diff --git a/src/runtime/c/gu/ucs.c b/src/runtime/c/gu/ucs.c
new file mode 100644
index 000000000..34649f36a
--- /dev/null
+++ b/src/runtime/c/gu/ucs.c
@@ -0,0 +1,135 @@
+#include <gu/ucs.h>
+#include <gu/assert.h>
+#include <guconfig.h>
+
+GU_DEFINE_TYPE(GuUCSExn, abstract, _);
+
+
+#ifdef GU_CHAR_ASCII
+
+bool
+gu_char_is_valid(char c)
+{
+ if (c < 0) {
+ return false;
+ } else if (c < 64) {
+ return UINT64_C(0xffffffef00003f81) & (UINT64_C(1) << c);
+ }
+#if CHAR_MAX > 127 // Let's avoid spurious warnings
+ else if (c > 127) {
+ return false;
+ }
+#endif
+ return UINT64_C(0x7ffffffefffffffe) & (UINT64_C(1) << (c - 64));
+}
+
+char
+gu_ucs_char(GuUCS uc, GuExn* err)
+{
+ if (0 <= uc && uc <= 127) {
+ char c = (char) uc;
+ if (gu_char_is_valid(c)) {
+ return c;
+ }
+ }
+ gu_raise(err, GuUCSExn);
+ return 0;
+}
+
+#else // defined(GU_CHAR_ASCII)
+
+static const char gu_ucs_ascii[128] =
+ "\0\0\0\0\0\0\0\a\b\t\n\v\f\r\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ " !\"#\0%&'()*+,-./0123456789:;<=>?"
+ "\0ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ "\0abcdefghijklmnopqrstuvwxyz{|}~\0";
+
+const uint8_t gu_ucs_ascii_reverse_[CHAR_MAX] = {
+ ['\0'] = 0x00, ['\a'] = 0x07, ['\b'] = 0x08, ['\t'] = 0x09,
+ ['\n'] = 0x0a, ['\v'] = 0x0b, ['\f'] = 0x0c, ['\r'] = 0x0d,
+ [' '] = 0x20, ['!'] = 0x21, ['"'] = 0x22, ['#'] = 0x23, ['%'] = 0x25,
+ ['&'] = 0x26, ['\''] = 0x27, ['('] = 0x28, [')'] = 0x29, ['*'] = 0x2a,
+ ['+'] = 0x2b, [','] = 0x2c, ['-'] = 0x2d, ['.'] = 0x2e, ['/'] = 0x2f,
+ ['0'] = 0x30, ['1'] = 0x31, ['2'] = 0x32, ['3'] = 0x33, ['4'] = 0x34,
+ ['5'] = 0x35, ['6'] = 0x36, ['7'] = 0x37, ['8'] = 0x38, ['9'] = 0x39,
+ [':'] = 0x3a, [';'] = 0x3b, ['<'] = 0x3c, ['='] = 0x3d, ['>'] = 0x3e,
+ ['?'] = 0x3f, ['A'] = 0x41, ['B'] = 0x42, ['C'] = 0x43, ['D'] = 0x44,
+ ['E'] = 0x45, ['F'] = 0x46, ['G'] = 0x47, ['H'] = 0x48, ['I'] = 0x49,
+ ['J'] = 0x4a, ['K'] = 0x4b, ['L'] = 0x4c, ['M'] = 0x4d, ['N'] = 0x4e,
+ ['O'] = 0x4f, ['P'] = 0x50, ['Q'] = 0x51, ['R'] = 0x52, ['S'] = 0x53,
+ ['T'] = 0x54, ['U'] = 0x55, ['V'] = 0x56, ['W'] = 0x57, ['X'] = 0x58,
+ ['Y'] = 0x59, ['Z'] = 0x5a, ['['] = 0x5b, ['\\'] = 0x5c, [']'] = 0x5d,
+ ['^'] = 0x5e, ['_'] = 0x5f, ['a'] = 0x61, ['b'] = 0x62, ['c'] = 0x63,
+ ['d'] = 0x64, ['e'] = 0x65, ['f'] = 0x66, ['g'] = 0x67, ['h'] = 0x68,
+ ['i'] = 0x69, ['j'] = 0x6a, ['k'] = 0x6b, ['l'] = 0x6c, ['m'] = 0x6d,
+ ['n'] = 0x6e, ['o'] = 0x6f, ['p'] = 0x70, ['q'] = 0x71, ['r'] = 0x72,
+ ['s'] = 0x73, ['t'] = 0x74, ['u'] = 0x75, ['v'] = 0x76, ['w'] = 0x77,
+ ['x'] = 0x78, ['y'] = 0x79, ['z'] = 0x7a, ['{'] = 0x7b, ['|'] = 0x7c,
+ ['}'] = 0x7d, ['~'] = 0x7e
+};
+
+
+bool
+gu_char_is_valid(char c)
+{
+ if (c > 0) {
+ return (gu_ucs_ascii_reverse_[(int) c] > 0);
+ }
+ return (c == '\0');
+}
+
+char
+gu_ucs_char(GuUCS uc, GuExn* err)
+{
+ if (uc == 0) {
+ return '\0';
+ } else if (0 < uc && uc <= 127) {
+ char c = gu_ucs_ascii[uc];
+ if (c != '\0') {
+ return (unsigned char) c;
+ }
+ }
+ gu_raise(err, GuUCSExn);
+ return 0;
+}
+
+#endif
+
+size_t
+gu_str_to_ucs(const char* cbuf, size_t len, GuUCS* ubuf, GuExn* err)
+{
+ size_t n = 0;
+ while (n < len) {
+ char c = cbuf[n];
+ if (!gu_char_is_valid(c)) {
+ gu_raise(err, GuUCSExn);
+ return n;
+ }
+ ubuf[n] = gu_char_ucs(c);
+ n++;
+ }
+ return n;
+}
+
+size_t
+gu_ucs_to_str(const GuUCS* ubuf, size_t len, char* cbuf, GuExn* err)
+{
+ size_t n = 0;
+ while (n < len) {
+ char c = gu_ucs_char(ubuf[n], err);
+ if (!gu_ok(err)) {
+ break;
+ }
+ cbuf[n] = c;
+ n++;
+ }
+ return n;
+}
+
+
+extern inline bool
+gu_ucs_valid(GuUCS ucs);
+
+extern inline GuUCS
+gu_char_ucs(char c);
diff --git a/src/runtime/c/gu/ucs.h b/src/runtime/c/gu/ucs.h
new file mode 100644
index 000000000..f1662a602
--- /dev/null
+++ b/src/runtime/c/gu/ucs.h
@@ -0,0 +1,53 @@
+#ifndef GU_UCS_H_
+#define GU_UCS_H_
+
+#include <gu/defs.h>
+#include <gu/exn.h>
+#include <gu/assert.h>
+
+
+#if defined(__STDC_ISO_10646__) && WCHAR_MAX >= 0x10FFFF
+#include <wchar.h>
+#define GU_UCS_WCHAR
+typedef wchar_t GuUCS;
+#else
+typedef int32_t GuUCS;
+#endif
+
+#define GU_UCS_MAX ((GuUCS)(0x10FFFF))
+
+bool
+gu_char_is_valid(char c);
+
+inline bool
+gu_ucs_valid(GuUCS ucs)
+{
+ return ucs >= 0 && ucs <= GU_UCS_MAX;
+}
+
+inline GuUCS
+gu_char_ucs(char c)
+{
+ gu_require(gu_char_is_valid(c));
+#ifdef GU_CHAR_ASCII
+ GuUCS u = (GuUCS) c;
+#else
+ extern const uint8_t gu_ucs_ascii_reverse_[CHAR_MAX];
+ GuUCS u = gu_ucs_ascii_reverse_[(unsigned char) c];
+#endif
+ gu_ensure(u < 0x80);
+ return u;
+}
+
+char
+gu_ucs_char(GuUCS uc, GuExn* err);
+
+size_t
+gu_str_to_ucs(const char* cbuf, size_t len, GuUCS* ubuf, GuExn* err);
+
+size_t
+gu_ucs_to_str(const GuUCS* ubuf, size_t len, char* cbuf, GuExn* err);
+
+extern GU_DECLARE_TYPE(GuUCSExn, abstract);
+
+#endif // GU_ISO10646_H_
diff --git a/src/runtime/c/gu/utf8.c b/src/runtime/c/gu/utf8.c
new file mode 100644
index 000000000..a416c2dac
--- /dev/null
+++ b/src/runtime/c/gu/utf8.c
@@ -0,0 +1,220 @@
+#include <gu/assert.h>
+#include <gu/utf8.h>
+#include <guconfig.h>
+
+GuUCS
+gu_utf8_decode(const uint8_t** src_inout)
+{
+ const uint8_t* src = *src_inout;
+ uint8_t c = src[0];
+ if (c < 0x80) {
+ *src_inout = src + 1;
+ return (GuUCS) c;
+ }
+ size_t len = (c < 0xe0 ? 1 :
+ c < 0xf0 ? 2 :
+ 3);
+ uint32_t mask = 0x07071f7f;
+ uint32_t u = c & (mask >> (len * 8));
+ for (size_t i = 1; i <= len; i++) {
+ c = src[i];
+ u = u << 6 | (c & 0x3f);
+ }
+ *src_inout = &src[len + 1];
+ return (GuUCS) u;
+}
+
+GuUCS
+gu_in_utf8_(GuIn* in, GuExn* err)
+{
+ uint8_t c = gu_in_u8(in, err);
+ if (!gu_ok(err)) {
+ return 0;
+ }
+ int len = (c < 0x80 ? 0 :
+ c < 0xc2 ? -1 :
+ c < 0xe0 ? 1 :
+ c < 0xf0 ? 2 :
+ c < 0xf5 ? 3 :
+ -1);
+ if (len < 0) {
+ goto fail;
+ } else if (len == 0) {
+ return c;
+ }
+ static const uint8_t mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 };
+ uint32_t u = c & mask[len];
+ uint8_t buf[3];
+ // If reading the extra bytes causes EOF, it is an encoding
+ // error, not a legitimate end of character stream.
+ GuExn* tmp_err = gu_exn(err, GuEOF, NULL);
+ gu_in_bytes(in, buf, len, tmp_err);
+ if (tmp_err->caught) {
+ goto fail;
+ }
+ if (!gu_ok(err)) {
+ return 0;
+ }
+ for (int i = 0; i < len; i++) {
+ c = buf[i];
+ if ((c & 0xc0) != 0x80) {
+ goto fail;
+ }
+ u = u << 6 | (c & 0x3f);
+ }
+ GuUCS ucs = (GuUCS) u;
+ if (!gu_ucs_valid(ucs)) {
+ goto fail;
+ }
+ return ucs;
+
+fail:
+ gu_raise(err, GuUCSExn);
+ return 0;
+}
+
+
+size_t
+gu_advance_utf8(GuUCS ucs, uint8_t* buf)
+{
+ gu_require(gu_ucs_valid(ucs));
+ if (ucs < 0x80) {
+ buf[0] = (uint8_t) ucs;
+ return 1;
+ } else if (ucs < 0x800) {
+ buf[0] = 0xc0 | (ucs >> 6);
+ buf[1] = 0x80 | (ucs & 0x3f);
+ return 2;
+ } else if (ucs < 0x10000) {
+ buf[0] = 0xe0 | (ucs >> 12);
+ buf[1] = 0x80 | ((ucs >> 6) & 0x3f);
+ buf[2] = 0x80 | (ucs & 0x3f);
+ return 3;
+ } else {
+ buf[0] = 0xf0 | (ucs >> 18);
+ buf[1] = 0x80 | ((ucs >> 12) & 0x3f);
+ buf[2] = 0x80 | ((ucs >> 6) & 0x3f);
+ buf[3] = 0x80 | (ucs & 0x3f);
+ return 4;
+ }
+}
+
+char
+gu_in_utf8_char_(GuIn* in, GuExn* err)
+{
+ return gu_ucs_char(gu_in_utf8(in, err), err);
+}
+
+void
+gu_out_utf8_long_(GuUCS ucs, GuOut* out, GuExn* err)
+{
+ uint8_t buf[4];
+ size_t sz = gu_advance_utf8(ucs, buf);
+ switch (sz) {
+ case 2:
+ gu_out_bytes(out, buf, 2, err);
+ break;
+ case 3:
+ gu_out_bytes(out, buf, 3, err);
+ break;
+ case 4:
+ gu_out_bytes(out, buf, 4, err);
+ break;
+ default:
+ gu_impossible();
+ }
+}
+
+extern inline void
+gu_out_utf8(GuUCS ucs, GuOut* out, GuExn* err);
+
+static size_t
+gu_utf32_out_utf8_buffered_(const GuUCS* src, size_t len, GuOut* out,
+ GuExn* err)
+{
+ size_t src_i = 0;
+ while (src_i < len) {
+ size_t dst_sz;
+ uint8_t* dst = gu_out_begin_span(out, len - src_i, &dst_sz, err);
+ if (!gu_ok(err)) {
+ return src_i;
+ }
+ if (!dst) {
+ gu_out_utf8(src[src_i], out, err);
+ if (!gu_ok(err)) {
+ return src_i;
+ }
+ src_i++;
+ break;
+ }
+ size_t dst_i = 0;
+ while (true) {
+ size_t safe = (dst_sz - dst_i) / 4;
+ size_t end = GU_MIN(len, src_i + safe);
+ if (end == src_i) {
+ break;
+ }
+ do {
+ GuUCS ucs = src[src_i++];
+ dst_i += gu_advance_utf8(ucs, &dst[dst_i]);
+ } while (src_i < end);
+ }
+ gu_out_end_span(out, dst_i);
+ }
+ return src_i;
+}
+
+size_t
+gu_utf32_out_utf8(const GuUCS* src, size_t len, GuOut* out, GuExn* err)
+{
+ if (gu_out_is_buffered(out)) {
+ return gu_utf32_out_utf8_buffered_(src, len, out, err);
+ }
+ for (size_t i = 0; i < len; i++) {
+ gu_out_utf8(src[i], out, err);
+ if (!gu_ok(err)) {
+ return i;
+ }
+ }
+ return len;
+
+}
+
+#ifndef GU_CHAR_ASCII
+
+void gu_str_out_utf8_(const char* str, GuOut* out, GuExn* err)
+{
+ size_t len = strlen(str);
+ size_t sz = 0;
+ uint8_t* buf = gu_out_begin_span(out, len, &sz, err);
+ if (!gu_ok(err)) {
+ return;
+ }
+ if (buf != NULL && sz < len) {
+ gu_out_end_span(out, 0);
+ buf = NULL;
+ }
+ GuPool* tmp_pool = buf ? NULL : gu_local_pool();
+ buf = buf ? buf : gu_new_n(uint8_t, len, tmp_pool);
+ for (size_t i = 0; i < len; i++) {
+ GuUCS ucs = gu_char_ucs(str[i]);
+ buf[i] = (uint8_t) ucs;
+ }
+ if (tmp_pool) {
+ gu_out_bytes(out, buf, len, err);
+ gu_pool_free(tmp_pool);
+ } else {
+ gu_out_end_span(out, len);
+ }
+}
+
+#endif
+
+extern inline void
+gu_str_out_utf8(const char* str, GuOut* out, GuExn* err);
+
+extern inline GuUCS
+gu_in_utf8(GuIn* in, GuExn* err);
+
+extern inline char
+gu_in_utf8_char(GuIn* in, GuExn* err);
diff --git a/src/runtime/c/gu/utf8.h b/src/runtime/c/gu/utf8.h
new file mode 100644
index 000000000..053d8664e
--- /dev/null
+++ b/src/runtime/c/gu/utf8.h
@@ -0,0 +1,67 @@
+#ifndef GU_UTF8_H_
+#define GU_UTF8_H_
+
+#include <gu/in.h>
+#include <gu/out.h>
+#include <gu/ucs.h>
+
+inline GuUCS
+gu_in_utf8(GuIn* in, GuExn* err)
+{
+ int i = gu_in_peek_u8(in);
+ if (i >= 0 && i < 0x80) {
+ gu_in_consume(in, 1);
+ return (GuUCS) i;
+ }
+ extern GuUCS gu_in_utf8_(GuIn* in, GuExn* err);
+ return gu_in_utf8_(in, err);
+}
+
+
+inline char
+gu_in_utf8_char(GuIn* in, GuExn* err)
+{
+#ifdef GU_CHAR_ASCII
+ int i = gu_in_peek_u8(in);
+ if (i >= 0 && i < 0x80) {
+ gu_in_consume(in, 1);
+ return (char) i;
+ }
+#endif
+ extern char gu_in_utf8_char_(GuIn* in, GuExn* err);
+ return gu_in_utf8_char_(in, err);
+}
+
+void
+gu_out_utf8_long_(GuUCS ucs, GuOut* out, GuExn* err);
+
+inline void
+gu_out_utf8(GuUCS ucs, GuOut* out, GuExn* err)
+{
+ gu_require(gu_ucs_valid(ucs));
+ if (GU_LIKELY(ucs < 0x80)) {
+ gu_out_u8(out, ucs, err);
+ } else {
+ gu_out_utf8_long_(ucs, out, err);
+ }
+}
+
+size_t
+gu_utf32_out_utf8(const GuUCS* src, size_t len, GuOut* out, GuExn* err);
+
+GuUCS
+gu_utf8_decode(const uint8_t** utf8);
+
+inline void
+gu_str_out_utf8(const char* str, GuOut* out, GuExn* err)
+{
+#ifdef GU_CHAR_ASCII
+ gu_out_bytes(out, (const uint8_t*) str, strlen(str), err);
+#else
+ extern void
+ gu_str_out_utf8_(const char* str, GuOut* out, GuExn* err);
+ gu_str_out_utf8_(str, out, err);
+#endif
+}
+
+#endif // GU_UTF8_H_
diff --git a/src/runtime/c/gu/variant.c b/src/runtime/c/gu/variant.c
new file mode 100644
index 000000000..62fdb92c1
--- /dev/null
+++ b/src/runtime/c/gu/variant.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libgu.
+ *
+ * Libgu is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libgu is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libgu. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "variant.h"
+#include "bits.h"
+
+enum {
+ GU_VARIANT_ALIGNMENT = sizeof(uintptr_t)
+};
+
+void*
+gu_alloc_variant(uint8_t tag, size_t size,
+ size_t align, GuVariant* variant_out, GuPool* pool)
+{
+ align = gu_max(align, GU_VARIANT_ALIGNMENT);
+ if (((size_t)tag) > GU_VARIANT_ALIGNMENT - 2) {
+ uint8_t* alloc = gu_malloc_aligned(pool, align + size, align);
+ alloc[align - 1] = tag;
+ void* p = &alloc[align];
+ variant_out->p = (uintptr_t)p;
+ return p;
+ }
+ void* p = gu_malloc_aligned(pool, size, align);
+ variant_out->p = ((uintptr_t)p) | (tag + 1);
+ return p;
+}
+
+GuVariant
+gu_make_variant(uint8_t tag, size_t size, size_t align, const void* init,
+ GuPool* pool)
+{
+ GuVariant v;
+ void* data = gu_alloc_variant(tag, size, align, &v, pool);
+ memcpy(data, init, size);
+ return v;
+}
+
+int
+gu_variant_tag(GuVariant variant)
+{
+ if (gu_variant_is_null(variant)) {
+ return GU_VARIANT_NULL;
+ }
+ int u = variant.p % GU_VARIANT_ALIGNMENT;
+ if (u == 0) {
+ uint8_t* mem = (uint8_t*)variant.p;
+ return mem[-1];
+ }
+ return u - 1;
+}
+
+void*
+gu_variant_data(GuVariant variant)
+{
+ if (gu_variant_is_null(variant)) {
+ return NULL;
+ }
+ return (void*)gu_align_backward(variant.p, GU_VARIANT_ALIGNMENT);
+}
+
+GuVariantInfo gu_variant_open(GuVariant variant)
+{
+ GuVariantInfo info = {
+ .tag = gu_variant_tag(variant),
+ .data = gu_variant_data(variant)
+ };
+ return info;
+}
+
+int
+gu_variant_intval(GuVariant variant)
+{
+ int u = variant.p % GU_VARIANT_ALIGNMENT;
+ if (u == 0) {
+ int* mem = (int*)variant.p;
+ return *mem;
+ }
+ return (variant.p / GU_VARIANT_ALIGNMENT);
+}
+
+const GuVariant gu_null_variant = { (GuWord) NULL };
+
+GU_DEFINE_KIND(GuVariant, repr);
+GU_DEFINE_KIND(GuVariantAsPtr, repr);
diff --git a/src/runtime/c/gu/variant.h b/src/runtime/c/gu/variant.h
new file mode 100644
index 000000000..a9bb10a4a
--- /dev/null
+++ b/src/runtime/c/gu/variant.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libgu.
+ *
+ * Libgu is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libgu is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libgu. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file
+ *
+ * Lightweight tagged data.
+ */
+
+#ifndef GU_VARIANT_H_
+#define GU_VARIANT_H_
+
+#include <gu/defs.h>
+#include <gu/mem.h>
+#include <gu/type.h>
+
+/** @name Variants
+ * @{
+ */
+
+typedef struct GuVariant GuVariant;
+
+
+void* gu_alloc_variant(uint8_t tag,
+ size_t size, size_t align,
+ GuVariant* variant_out, GuPool* pool);
+
+GuVariant gu_make_variant(uint8_t tag,
+ size_t size, size_t align,
+ const void* init, GuPool* pool);
+
+#define gu_new_variant(tag, type, variant_out, pool) \
+ ((type*)gu_alloc_variant(tag, sizeof(type), \
+ gu_alignof(type), variant_out, pool))
+
+/**<
+ * @hideinitializer */
+
+#define gu_new_variant_i(POOL, TAG, T, ...) \
+ gu_make_variant(TAG, sizeof(T), gu_alignof(T), \
+ &(T){ __VA_ARGS__ }, POOL)
+
+
+
+#define gu_new_flex_variant(tag, type, flex_mem, n_elems, variant_out, pool) \
+ ((type*)gu_alloc_variant(tag, \
+ GU_FLEX_SIZE(type, flex_mem, n_elems), \
+ gu_flex_alignof(type), \
+ variant_out, pool))
+/**<
+ * @hideinitializer */
+
+enum {
+ GU_VARIANT_NULL = -1
+};
+
+int gu_variant_tag(GuVariant variant);
+
+void* gu_variant_data(GuVariant variant);
+
+
+typedef struct GuVariantInfo GuVariantInfo;
+
+struct GuVariantInfo {
+ int tag;
+ void* data;
+};
+
+GuVariantInfo gu_variant_open(GuVariant variant);
+
+/** @privatesection */
+struct GuVariant {
+ uintptr_t p;
+ /**< @private */
+};
+
+/** @} */
+
+static inline void*
+gu_variant_to_ptr(GuVariant variant)
+{
+ return (void*)variant.p;
+}
+
+static inline GuVariant
+gu_variant_from_ptr(const void* p)
+{
+ GuVariant v = { (uintptr_t)p };
+ return v;
+}
+
+extern const GuVariant gu_null_variant;
+
+static inline bool
+gu_variant_is_null(GuVariant v) {
+ return ((void*)v.p == NULL);
+}
+
+
+// variant
+
+typedef const struct GuConstructor GuConstructor;
+
+struct GuConstructor {
+ int c_tag;
+ const char* c_name;
+ const GuType* type;
+};
+
+#define GU_CONSTRUCTOR_V(ctag, c_type) { \
+ .c_tag = ctag, \
+ .c_name = #ctag, \
+ .type = c_type \
+}
+
+#define GU_CONSTRUCTOR(ctag, t_) \
+ GU_CONSTRUCTOR_V(ctag, gu_type(t_))
+
+#define GU_CONSTRUCTOR_P(ctag, t_) \
+ GU_CONSTRUCTOR_V(ctag, gu_ptr_type(t_))
+
+#define GU_CONSTRUCTOR_S(ctag, t_, ...) \
+ GU_CONSTRUCTOR_V(ctag, GU_TYPE_LIT(struct, t_, __VA_ARGS__))
+
+#define GU_CONSTRUCTOR_S1(ctag, t_, mem1_, type1_) \
+ GU_CONSTRUCTOR_S(ctag, t_, \
+ GU_MEMBER(t_, mem1_, type1_))
+
+#define GU_CONSTRUCTOR_S2(ctag, t_, mem1_, type1_, mem2_, type2_) \
+ GU_CONSTRUCTOR_S(ctag, t_, \
+ GU_MEMBER(t_, mem1_, type1_), \
+ GU_MEMBER(t_, mem2_, type2_))
+
+
+
+typedef GuSList(GuConstructor) GuConstructors;
+
+typedef const struct GuVariantType GuVariantType, GuType_GuVariant;
+
+struct GuVariantType {
+ GuType_repr repr_base;
+ GuConstructors ctors;
+};
+
+#define GU_TYPE_INIT_GuVariant(k_, t_, ...) { \
+ .repr_base = GU_TYPE_INIT_repr(k_, GuVariant, _), \
+ .ctors = GU_SLIST(GuConstructor, __VA_ARGS__) \
+}
+
+extern GU_DECLARE_KIND(GuVariant);
+
+#endif // GU_VARIANT_H_
diff --git a/src/runtime/c/gu/write.c b/src/runtime/c/gu/write.c
new file mode 100644
index 000000000..69573fb0d
--- /dev/null
+++ b/src/runtime/c/gu/write.c
@@ -0,0 +1,174 @@
+#include <gu/write.h>
+
+
+size_t
+gu_utf32_write(const GuUCS* src, size_t len, GuWriter* wtr, GuExn* err)
+{
+ return gu_utf32_out_utf8(src, len, &wtr->out_, err);
+}
+
+
+void
+gu_vprintf(const char* fmt, va_list args, GuWriter* wtr, GuExn* err)
+{
+ GuPool* tmp_pool = gu_local_pool();
+ char* str = gu_vasprintf(fmt, args, tmp_pool);
+ gu_puts(str, wtr, err);
+ gu_pool_free(tmp_pool);
+}
+
+void
+gu_printf(GuWriter* wtr, GuExn* err, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ gu_vprintf(fmt, args, wtr, err);
+ va_end(args);
+}
+
+
+GuWriter*
+gu_new_utf8_writer(GuOut* utf8_out, GuPool* pool)
+{
+ GuOutStream* stream = gu_out_proxy_stream(utf8_out, pool);
+ GuWriter* wtr = gu_new(GuWriter, pool);
+ wtr->out_ = gu_init_out(stream);
+ return wtr;
+}
+
+
+#if 0
+#ifdef GU_UCS_WCHAR
+#include <stdlib.h>
+#include <wchar.h>
+static const mbstate_t gu_init_mbstate; // implicitly initialized to zero
+#endif
+
+typedef struct GuLocaleWriter GuLocaleWriter;
+
+struct GuLocaleWriter {
+ GuOutWriter owtr;
+#ifdef GU_UCS_WCHAR
+ mbstate_t ps;
+ size_t mb_cur_max;
+#endif
+};
+
+size_t
+gu_locale_writer_write(GuWriter* wtr, const uint8_t* utf8_src, size_t sz,
+ GuExn* err)
+{
+ GuLocaleWriter* lwtr = (GuLocaleWriter*) wtr;
+ size_t done = 0;
+ static const size_t bufsize = 256;
+#ifdef GU_UCS_WCHAR
+ size_t margin = lwtr->mb_cur_max;
+#else
+ size_t margin = 1;
+#endif
+ GuOut* out = lwtr->owtr.out;
+ if (gu_out_is_buffered(out)) {
+ while (done < sz) {
+ size_t dst_sz;
+ uint8_t* dst = gu_out_begin_span(out, &dst_sz);
+ if (!dst) {
+ break;
+ }
+ if (dst_sz <= margin) {
+ gu_out_end_span(out, 0);
+ break;
+ }
+ size_t end = dst_sz - margin;
+ const uint8_t*
+ size_t n = done;
+ while (n < sz && dst_i <= end) {
+#ifdef GU_UCS_WCHAR
+ GuUCS ucs = gu_
+ wchar_t wc = src[n];
+ size_t nb = wcrtomb((char*) p, wc, &lwtr->ps);
+#else
+ *p = (uint8_t) gu_ucs_char(buf[n], err);
+ size_t nb = 1;
+ if (!gu_ok(err)) {
+ gu_exn_clear(err);
+ nb = (size_t) -1;
+ }
+#endif
+ if (nb == (size_t) -1) {
+ *p++ = (uint8_t) '?';
+ } else {
+ p += nb;
+ }
+
+ }
+ for (
+
+ }
+
+
+
+ }
+
+ uint8_t cbuf[256];
+ while (done < size && gu_ok(err)) {
+ uint8_t* p = cbuf;
+ uint8_t* edge = &cbuf[bufsize - margin];
+ size_t n;
+ for (n = done; p <= edge && n < size; n++) {
+#ifdef GU_UCS_WCHAR
+ wchar_t wc = buf[n];
+ size_t nb = wcrtomb((char*) p, wc, &lwtr->ps);
+#else
+ *p = (uint8_t) gu_ucs_char(buf[n], err);
+ size_t nb = 1;
+ if (!gu_ok(err)) {
+ gu_exn_clear(err);
+ nb = (size_t) -1;
+ }
+#endif
+ if (nb == (size_t) -1) {
+ *p++ = (uint8_t) '?';
+ } else {
+ p += nb;
+ }
+ }
+ gu_out_bytes(lwtr->owtr.out, cbuf, p - cbuf, err);
+ if (gu_ok(err)) {
+ done = n;
+ }
+ }
+ return done;
+}
+
+GuWriter*
+gu_locale_writer(GuOut* out, GuPool* pool)
+{
+ GuLocaleWriter* lwtr = gu_new_s(
+ pool, GuLocaleWriter,
+ .wtr.out.output = gu_locale_writer_output,
+ .wtr.out.flush = gu_locale_writer_flush,
+ .out = out);
+#ifdef GU_UCS_WCHAR
+ lwtr->ps = gu_init_mbstate;
+ lwtr->mb_cur_max = MB_CUR_MAX;
+#endif
+ return (GuWriter*) lwtr;
+}
+
+#endif
+
+extern inline void
+gu_ucs_write(GuUCS ucs, GuWriter* wtr, GuExn* err);
+
+extern inline void
+gu_writer_flush(GuWriter* wtr, GuExn* err);
+
+extern inline void
+gu_putc(char c, GuWriter* wtr, GuExn* err);
+
+extern inline void
+gu_puts(const char* str, GuWriter* wtr, GuExn* err);
+
+extern inline size_t
+gu_utf8_write(const uint8_t* src, size_t sz, GuWriter* wtr, GuExn* err);
+
diff --git a/src/runtime/c/gu/write.h b/src/runtime/c/gu/write.h
new file mode 100644
index 000000000..414a7bc2c
--- /dev/null
+++ b/src/runtime/c/gu/write.h
@@ -0,0 +1,64 @@
+#ifndef GU_WRITE_H_
+#define GU_WRITE_H_
+
+#include <gu/exn.h>
+#include <gu/ucs.h>
+#include <gu/out.h>
+#include <gu/utf8.h>
+
+typedef struct GuWriter GuWriter;
+
+struct GuWriter {
+ GuOut out_;
+};
+
+size_t
+gu_utf32_write(const GuUCS* buf, size_t size, GuWriter* wtr, GuExn* err);
+
+inline void
+gu_writer_flush(GuWriter* wtr, GuExn* err)
+{
+ gu_out_flush(&wtr->out_, err);
+}
+
+inline void
+gu_ucs_write(GuUCS ucs, GuWriter* wtr, GuExn* err)
+{
+ gu_out_utf8(ucs, &wtr->out_, err);
+}
+
+inline void
+gu_putc(char c, GuWriter* wtr, GuExn* err)
+{
+ GuUCS ucs = gu_char_ucs(c);
+ gu_out_u8(&wtr->out_, (uint8_t) ucs, err);
+}
+
+inline void
+gu_puts(const char* str, GuWriter* wtr, GuExn* err)
+{
+ gu_str_out_utf8(str, &wtr->out_, err);
+}
+
+inline size_t
+gu_utf8_write(const uint8_t* src, size_t sz, GuWriter* wtr, GuExn* err)
+{
+ return gu_out_bytes(&wtr->out_, src, sz, err);
+}
+
+void
+gu_vprintf(const char* fmt, va_list args, GuWriter* wtr, GuExn* err);
+
+void
+gu_printf(GuWriter* wtr, GuExn* err, const char* fmt, ...);
+
+//GuWriter
+//gu_init_utf8_writer(GuOut* utf8_out);
+
+GuWriter*
+gu_new_utf8_writer(GuOut* utf8_out, GuPool* pool);
+
+GuWriter*
+gu_make_locale_writer(GuOut* locale_out, GuPool* pool);
+
+#endif // GU_WRITE_H_
diff --git a/src/runtime/c/gu/yaml.c b/src/runtime/c/gu/yaml.c
new file mode 100644
index 000000000..4d769a4aa
--- /dev/null
+++ b/src/runtime/c/gu/yaml.c
@@ -0,0 +1,339 @@
+#include <gu/yaml.h>
+#include <gu/seq.h>
+#include <gu/assert.h>
+#include <gu/read.h>
+#include <gu/ucs.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+
+const GuYamlAnchor gu_yaml_null_anchor = 0;
+
+typedef const struct GuYamlState GuYamlState;
+
+struct GuYamlState {
+ const char* prefix;
+ const char* suffix;
+ GuYamlState* next;
+};
+
+static const struct {
+ GuYamlState document, first_key, key, value, first_elem, elem;
+} gu_yaml_states = {
+ .document = {
+ .prefix = "---\n",
+ .suffix = "\n...\n",
+ .next = &gu_yaml_states.document,
+ },
+ .key = {
+ .prefix = "? ",
+ .next = &gu_yaml_states.value,
+ },
+ .value = {
+ .prefix = ": ",
+ .suffix = ",",
+ .next = &gu_yaml_states.key,
+ },
+ .elem = {
+ .suffix = ",",
+ .next = &gu_yaml_states.elem,
+ },
+};
+
+typedef const struct GuYamlFrameClass GuYamlFrameClass;
+
+struct GuYamlFrameClass {
+ const char* open;
+ GuYamlState* first;
+ const char* close;
+};
+
+static const struct {
+ GuYamlFrameClass document, mapping, sequence;
+} gu_yaml_frame_classes = {
+ .mapping = {
+ .open = "{",
+ .first = &gu_yaml_states.key,
+ .close = "}",
+ },
+ .sequence = {
+ .open = "[",
+ .first = &gu_yaml_states.elem,
+ .close = "]",
+ },
+};
+
+typedef struct GuYamlFrame GuYamlFrame;
+
+struct GuYamlFrame {
+ GuYamlFrameClass* klass;
+ GuYamlState* next;
+};
+
+typedef GuBuf GuYamlStack;
+
+struct GuYaml {
+ GuWriter* wtr;
+ GuExn* err;
+ GuPool* pool;
+ GuYamlState* state;
+ bool in_node;
+ bool have_anchor;
+ bool have_tag;
+ int next_anchor;
+ bool indent;
+ int indent_level;
+ bool indented;
+ GuYamlStack* stack;
+};
+
+
+GuYaml*
+gu_new_yaml(GuWriter* wtr, GuExn* err, GuPool* pool)
+{
+ GuYaml* yaml = gu_new(GuYaml, pool);
+ yaml->wtr = wtr;
+ yaml->pool = pool;
+ yaml->err = err;
+ yaml->state = &gu_yaml_states.document;
+ yaml->in_node = false;
+ yaml->have_anchor = false;
+ yaml->have_tag = false;
+ yaml->next_anchor = 1;
+ yaml->stack = gu_new_buf(GuYamlFrame, pool);
+ yaml->indent = true;
+ yaml->indent_level = 0;
+ yaml->indented = false;
+ return yaml;
+}
+
+static void
+gu_yaml_printf(GuYaml* yaml, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ gu_vprintf(fmt, args, yaml->wtr, yaml->err);
+ va_end(args);
+}
+
+static void
+gu_yaml_putc(GuYaml* yaml, char c)
+{
+ gu_putc(c, yaml->wtr, yaml->err);
+}
+
+static void
+gu_yaml_puts(GuYaml* yaml, const char* str)
+{
+ gu_puts(str, yaml->wtr, yaml->err);
+}
+
+static void
+gu_yaml_begin_line(GuYaml* yaml)
+{
+ if (yaml->indent && !yaml->indented) {
+ for (int i = 0; i < yaml->indent_level; i++) {
+ gu_yaml_putc(yaml, ' ');
+ }
+ yaml->indented = true;
+ }
+}
+
+static void
+gu_yaml_end_line(GuYaml* yaml)
+{
+ if (yaml->indent) {
+ gu_yaml_putc(yaml, '\n');
+ }
+ yaml->indented = false;
+}
+
+
+static void
+gu_yaml_begin_node(GuYaml* yaml)
+{
+ gu_yaml_begin_line(yaml);
+ if (!yaml->in_node) {
+ if (yaml->state->prefix != NULL) {
+ gu_yaml_puts(yaml, yaml->state->prefix);
+ }
+ yaml->in_node = true;
+ }
+}
+
+static void
+gu_yaml_end_node(GuYaml* yaml)
+{
+ gu_assert(yaml->in_node);
+ if (yaml->state->suffix != NULL) {
+ gu_yaml_puts(yaml, yaml->state->suffix);
+ }
+ gu_yaml_end_line(yaml);
+ yaml->in_node = false;
+ yaml->have_anchor = false;
+ yaml->have_tag = false;
+ if (yaml->state != NULL) {
+ yaml->state = yaml->state->next;
+ }
+}
+
+static void
+gu_yaml_begin(GuYaml* yaml, GuYamlFrameClass* klass)
+{
+ gu_yaml_begin_node(yaml);
+ gu_yaml_puts(yaml, klass->open);
+ gu_buf_push(yaml->stack, GuYamlFrame,
+ ((GuYamlFrame) { .klass = klass, .next = yaml->state}));
+ yaml->state = klass->first;
+ yaml->in_node = yaml->have_anchor = yaml->have_tag = false;
+ gu_yaml_end_line(yaml);
+ yaml->indent_level++;
+}
+
+void
+gu_yaml_begin_mapping(GuYaml* yaml)
+{
+ gu_yaml_begin(yaml, &gu_yaml_frame_classes.mapping);
+}
+
+void
+gu_yaml_begin_sequence(GuYaml* yaml)
+{
+ gu_yaml_begin(yaml, &gu_yaml_frame_classes.sequence);
+}
+
+void
+gu_yaml_end(GuYaml* yaml)
+{
+ gu_assert(!yaml->in_node);
+ yaml->indent_level--;
+ gu_yaml_begin_line(yaml);
+ GuYamlFrame f = gu_buf_pop(yaml->stack, GuYamlFrame);
+ gu_yaml_puts(yaml, f.klass->close);
+ yaml->state = f.next;
+ yaml->in_node = true;
+ gu_yaml_end_node(yaml);
+}
+
+
+void
+gu_yaml_scalar(GuYaml* yaml, GuString s)
+{
+ gu_yaml_begin_node(yaml);
+ gu_yaml_putc(yaml, '"');
+ GuPool* tmp_pool = gu_local_pool();
+ GuReader* rdr = gu_string_reader(s, tmp_pool);
+ GuExn* err = gu_exn(yaml->err, GuEOF, NULL);
+
+ static const char esc[0x20] = {
+ [0x00] = '0',
+ [0x07] = 'a', 'b', 't', 'n', 'v', 'f', 'r',
+ [0x1b] = 'e'
+ };
+
+ while (true) {
+ GuUCS u = gu_read_ucs(rdr, err);
+ if (!gu_ok(err)) {
+ break;
+ }
+ if (GU_LIKELY(u >= 0x20 && u < 0x7f)) {
+ if (GU_UNLIKELY(u == 0x22 || u == 0x5c)) {
+ gu_yaml_putc(yaml, '\\');
+ }
+ gu_ucs_write(u, yaml->wtr, yaml->err);
+ } else if (GU_UNLIKELY(u < 0x20 && esc[u])) {
+ gu_yaml_printf(yaml, "\\%c", esc[u]);
+ } else if (GU_UNLIKELY(u <= 0x9f)) {
+ gu_yaml_printf(yaml, "\\x%02x", (unsigned) u);
+ } else if (GU_UNLIKELY((u >= 0xd800 && u <= 0xdfff) ||
+ (u >= 0xfffe && u <= 0xffff))) {
+ gu_yaml_printf(yaml, "\\u%04x", (unsigned) u);
+ } else {
+ gu_ucs_write(u, yaml->wtr, yaml->err);
+ }
+ }
+ gu_pool_free(tmp_pool);
+ gu_yaml_putc(yaml, '"');
+ gu_yaml_end_node(yaml);
+}
+
+static void
+gu_yaml_tag(GuYaml* yaml, const char* format, ...)
+{
+ gu_yaml_begin_node(yaml);
+ gu_assert(!yaml->have_tag);
+ gu_yaml_putc(yaml, '!');
+ va_list args;
+ va_start(args, format);
+ gu_vprintf(format, args, yaml->wtr, yaml->err);
+ va_end(args);
+ gu_yaml_putc(yaml, ' ');
+ yaml->have_tag = true;
+}
+
+void
+gu_yaml_tag_primary(GuYaml* yaml, const char* tag)
+{
+ // TODO: check tag validity
+ gu_yaml_tag(yaml, "%s", tag);
+}
+
+void
+gu_yaml_tag_secondary(GuYaml* yaml, const char* tag)
+{
+ // TODO: check tag validity
+ gu_yaml_tag(yaml, "!%s", tag);
+}
+
+void
+gu_yaml_tag_named(GuYaml* yaml, const char* handle, const char* tag)
+{
+ // TODO: check tag validity
+ gu_yaml_tag(yaml, "%s!%s", handle, tag);
+}
+
+void
+gu_yaml_tag_verbatim(GuYaml* yaml, const char* uri)
+{
+ // XXX: uri escaping?
+ gu_yaml_tag(yaml, "<%s>", uri);
+}
+
+void
+gu_yaml_tag_non_specific(GuYaml* yaml)
+{
+ gu_yaml_tag(yaml, "");
+}
+
+GuYamlAnchor
+gu_yaml_anchor(GuYaml* yaml)
+{
+ gu_yaml_begin_node(yaml);
+ gu_assert(!yaml->have_anchor);
+ yaml->have_anchor = true;
+ int anchor = yaml->next_anchor++;
+ gu_yaml_printf(yaml, "&%d ", anchor);
+ return anchor;
+}
+
+void
+gu_yaml_alias(GuYaml* yaml, GuYamlAnchor anchor)
+{
+ gu_yaml_begin_node(yaml);
+ gu_assert(!yaml->have_anchor && !yaml->have_tag);
+ gu_yaml_printf(yaml, "*%d ", anchor);
+ gu_yaml_end_node(yaml);
+ return;
+}
+
+void gu_yaml_comment(GuYaml* yaml, GuString s)
+{
+ gu_yaml_begin_line(yaml);
+ gu_yaml_puts(yaml, "# ");
+ // TODO: verify no newlines in comment
+ gu_string_write(s, yaml->wtr, yaml->err);
+ gu_yaml_puts(yaml, "\n");
+ yaml->indented = false;
+}
+
diff --git a/src/runtime/c/gu/yaml.h b/src/runtime/c/gu/yaml.h
new file mode 100644
index 000000000..5a61d0786
--- /dev/null
+++ b/src/runtime/c/gu/yaml.h
@@ -0,0 +1,38 @@
+#ifndef GU_YAML_H_
+#define GU_YAML_H_
+
+#include <gu/mem.h>
+#include <gu/write.h>
+#include <gu/string.h>
+
+typedef struct GuYaml GuYaml;
+
+typedef int GuYamlAnchor;
+
+extern const GuYamlAnchor gu_yaml_null_anchor;
+
+GuYaml* gu_new_yaml(GuWriter* wtr, GuExn* err, GuPool* pool);
+
+GuYamlAnchor gu_yaml_anchor(GuYaml* yaml);
+
+void gu_yaml_tag_primary(GuYaml* yaml, const char* tag);
+void gu_yaml_tag_secondary(GuYaml* yaml, const char* tag);
+void gu_yaml_tag_named(GuYaml* yaml, const char* handle, const char* tag);
+void gu_yaml_tag_verbatim(GuYaml* yaml, const char* uri);
+void gu_yaml_tag_non_specific(GuYaml* yaml);
+void gu_yaml_comment(GuYaml* yaml, GuString comment);
+
+
+void gu_yaml_scalar(GuYaml* yaml, GuString scalar);
+
+void gu_yaml_alias(GuYaml* yaml, GuYamlAnchor anchor);
+
+void gu_yaml_begin_document(GuYaml* yaml);
+
+void gu_yaml_begin_sequence(GuYaml* yaml);
+
+void gu_yaml_begin_mapping(GuYaml* yaml);
+
+void gu_yaml_end(GuYaml* yaml);
+
+#endif // GU_YAML_H_
diff --git a/src/runtime/c/guconfig.h.in b/src/runtime/c/guconfig.h.in
new file mode 100644
index 000000000..d323bfbf7
--- /dev/null
+++ b/src/runtime/c/guconfig.h.in
@@ -0,0 +1,2 @@
+/* Define to 1 if character literals use ASCII encoding */
+#undef GU_CHAR_ASCII
diff --git a/src/runtime/c/libgu.pc.in b/src/runtime/c/libgu.pc.in
new file mode 100644
index 000000000..d1f7e8e3a
--- /dev/null
+++ b/src/runtime/c/libgu.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libgu
+Description: G(F|lib|othenburg) Utilities library
+Version: @VERSION@
+Libs: -L${libdir} -lgu
+Cflags: -I${includedir} -I${libdir}/libgu/include
diff --git a/src/runtime/c/libpgf.pc.in b/src/runtime/c/libpgf.pc.in
new file mode 100644
index 000000000..414388cbc
--- /dev/null
+++ b/src/runtime/c/libpgf.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libpgf
+Description: Portable Grammar Format library
+Requires: libgu
+Version: @VERSION@
+Libs: -L${libdir} -lpgf
+Cflags: -I${includedir}
diff --git a/src/runtime/c/m4/ax_prog_doxygen.m4 b/src/runtime/c/m4/ax_prog_doxygen.m4
new file mode 100644
index 000000000..6334fd412
--- /dev/null
+++ b/src/runtime/c/m4/ax_prog_doxygen.m4
@@ -0,0 +1,533 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# DX_INIT_DOXYGEN(PROJECT-NAME, DOXYFILE-PATH, [OUTPUT-DIR])
+# DX_DOXYGEN_FEATURE(ON|OFF)
+# DX_DOT_FEATURE(ON|OFF)
+# DX_HTML_FEATURE(ON|OFF)
+# DX_CHM_FEATURE(ON|OFF)
+# DX_CHI_FEATURE(ON|OFF)
+# DX_MAN_FEATURE(ON|OFF)
+# DX_RTF_FEATURE(ON|OFF)
+# DX_XML_FEATURE(ON|OFF)
+# DX_PDF_FEATURE(ON|OFF)
+# DX_PS_FEATURE(ON|OFF)
+#
+# DESCRIPTION
+#
+# The DX_*_FEATURE macros control the default setting for the given
+# Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for
+# generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML
+# help (for MS users), 'CHI' for generating a seperate .chi file by the
+# .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate
+# output formats. The environment variable DOXYGEN_PAPER_SIZE may be
+# specified to override the default 'a4wide' paper size.
+#
+# By default, HTML, PDF and PS documentation is generated as this seems to
+# be the most popular and portable combination. MAN pages created by
+# Doxygen are usually problematic, though by picking an appropriate subset
+# and doing some massaging they might be better than nothing. CHM and RTF
+# are specific for MS (note that you can't generate both HTML and CHM at
+# the same time). The XML is rather useless unless you apply specialized
+# post-processing to it.
+#
+# The macros mainly control the default state of the feature. The use can
+# override the default by specifying --enable or --disable. The macros
+# ensure that contradictory flags are not given (e.g.,
+# --enable-doxygen-html and --enable-doxygen-chm,
+# --enable-doxygen-anything with --disable-doxygen, etc.) Finally, each
+# feature will be automatically disabled (with a warning) if the required
+# programs are missing.
+#
+# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN
+# with the following parameters: a one-word name for the project for use
+# as a filename base etc., an optional configuration file name (the
+# default is 'Doxyfile', the same as Doxygen's default), and an optional
+# output directory name (the default is 'doxygen-doc').
+#
+# Automake Support
+#
+# The following is a template aminclude.am file for use with Automake.
+# Make targets and variables values are controlled by the various
+# DX_COND_* conditionals set by autoconf.
+#
+# The provided targets are:
+#
+# doxygen-doc: Generate all doxygen documentation.
+#
+# doxygen-run: Run doxygen, which will generate some of the
+# documentation (HTML, CHM, CHI, MAN, RTF, XML)
+# but will not do the post processing required
+# for the rest of it (PS, PDF, and some MAN).
+#
+# doxygen-man: Rename some doxygen generated man pages.
+#
+# doxygen-ps: Generate doxygen PostScript documentation.
+#
+# doxygen-pdf: Generate doxygen PDF documentation.
+#
+# Note that by default these are not integrated into the automake targets.
+# If doxygen is used to generate man pages, you can achieve this
+# integration by setting man3_MANS to the list of man pages generated and
+# then adding the dependency:
+#
+# $(man3_MANS): doxygen-doc
+#
+# This will cause make to run doxygen and generate all the documentation.
+#
+# The following variable is intended for use in Makefile.am:
+#
+# DX_CLEANFILES = everything to clean.
+#
+# Then add this variable to MOSTLYCLEANFILES.
+#
+# ----- begin aminclude.am -------------------------------------
+#
+# ## --------------------------------- ##
+# ## Format-independent Doxygen rules. ##
+# ## --------------------------------- ##
+#
+# if DX_COND_doc
+#
+# ## ------------------------------- ##
+# ## Rules specific for HTML output. ##
+# ## ------------------------------- ##
+#
+# if DX_COND_html
+#
+# DX_CLEAN_HTML = @DX_DOCDIR@/html
+#
+# endif DX_COND_html
+#
+# ## ------------------------------ ##
+# ## Rules specific for CHM output. ##
+# ## ------------------------------ ##
+#
+# if DX_COND_chm
+#
+# DX_CLEAN_CHM = @DX_DOCDIR@/chm
+#
+# if DX_COND_chi
+#
+# DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi
+#
+# endif DX_COND_chi
+#
+# endif DX_COND_chm
+#
+# ## ------------------------------ ##
+# ## Rules specific for MAN output. ##
+# ## ------------------------------ ##
+#
+# if DX_COND_man
+#
+# DX_CLEAN_MAN = @DX_DOCDIR@/man
+#
+# endif DX_COND_man
+#
+# ## ------------------------------ ##
+# ## Rules specific for RTF output. ##
+# ## ------------------------------ ##
+#
+# if DX_COND_rtf
+#
+# DX_CLEAN_RTF = @DX_DOCDIR@/rtf
+#
+# endif DX_COND_rtf
+#
+# ## ------------------------------ ##
+# ## Rules specific for XML output. ##
+# ## ------------------------------ ##
+#
+# if DX_COND_xml
+#
+# DX_CLEAN_XML = @DX_DOCDIR@/xml
+#
+# endif DX_COND_xml
+#
+# ## ----------------------------- ##
+# ## Rules specific for PS output. ##
+# ## ----------------------------- ##
+#
+# if DX_COND_ps
+#
+# DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps
+#
+# DX_PS_GOAL = doxygen-ps
+#
+# doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps
+#
+# @DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag
+# cd @DX_DOCDIR@/latex; \
+# rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+# $(DX_LATEX) refman.tex; \
+# $(MAKEINDEX_PATH) refman.idx; \
+# $(DX_LATEX) refman.tex; \
+# countdown=5; \
+# while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+# refman.log > /dev/null 2>&1 \
+# && test $$countdown -gt 0; do \
+# $(DX_LATEX) refman.tex; \
+# countdown=`expr $$countdown - 1`; \
+# done; \
+# $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi
+#
+# endif DX_COND_ps
+#
+# ## ------------------------------ ##
+# ## Rules specific for PDF output. ##
+# ## ------------------------------ ##
+#
+# if DX_COND_pdf
+#
+# DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf
+#
+# DX_PDF_GOAL = doxygen-pdf
+#
+# doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf
+#
+# @DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag
+# cd @DX_DOCDIR@/latex; \
+# rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+# $(DX_PDFLATEX) refman.tex; \
+# $(DX_MAKEINDEX) refman.idx; \
+# $(DX_PDFLATEX) refman.tex; \
+# countdown=5; \
+# while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+# refman.log > /dev/null 2>&1 \
+# && test $$countdown -gt 0; do \
+# $(DX_PDFLATEX) refman.tex; \
+# countdown=`expr $$countdown - 1`; \
+# done; \
+# mv refman.pdf ../@PACKAGE@.pdf
+#
+# endif DX_COND_pdf
+#
+# ## ------------------------------------------------- ##
+# ## Rules specific for LaTeX (shared for PS and PDF). ##
+# ## ------------------------------------------------- ##
+#
+# if DX_COND_latex
+#
+# DX_CLEAN_LATEX = @DX_DOCDIR@/latex
+#
+# endif DX_COND_latex
+#
+# .PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL)
+#
+# .INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+#
+# doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag
+#
+# doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+#
+# @DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS)
+# rm -rf @DX_DOCDIR@
+# $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG)
+#
+# DX_CLEANFILES = \
+# @DX_DOCDIR@/@PACKAGE@.tag \
+# -r \
+# $(DX_CLEAN_HTML) \
+# $(DX_CLEAN_CHM) \
+# $(DX_CLEAN_CHI) \
+# $(DX_CLEAN_MAN) \
+# $(DX_CLEAN_RTF) \
+# $(DX_CLEAN_XML) \
+# $(DX_CLEAN_PS) \
+# $(DX_CLEAN_PDF) \
+# $(DX_CLEAN_LATEX)
+#
+# endif DX_COND_doc
+#
+# ----- end aminclude.am ---------------------------------------
+#
+# LICENSE
+#
+# Copyright (c) 2009 Oren Ben-Kiki <oren@ben-kiki.org>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 10
+
+## ----------##
+## Defaults. ##
+## ----------##
+
+DX_ENV=""
+AC_DEFUN([DX_FEATURE_doc], ON)
+AC_DEFUN([DX_FEATURE_dot], ON)
+AC_DEFUN([DX_FEATURE_man], OFF)
+AC_DEFUN([DX_FEATURE_html], ON)
+AC_DEFUN([DX_FEATURE_chm], OFF)
+AC_DEFUN([DX_FEATURE_chi], OFF)
+AC_DEFUN([DX_FEATURE_rtf], OFF)
+AC_DEFUN([DX_FEATURE_xml], OFF)
+AC_DEFUN([DX_FEATURE_pdf], ON)
+AC_DEFUN([DX_FEATURE_ps], ON)
+
+## --------------- ##
+## Private macros. ##
+## --------------- ##
+
+# DX_ENV_APPEND(VARIABLE, VALUE)
+# ------------------------------
+# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen.
+AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])])
+
+# DX_DIRNAME_EXPR
+# ---------------
+# Expand into a shell expression prints the directory part of a path.
+AC_DEFUN([DX_DIRNAME_EXPR],
+ [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']])
+
+# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF)
+# -------------------------------------
+# Expands according to the M4 (static) status of the feature.
+AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])])
+
+# DX_REQUIRE_PROG(VARIABLE, PROGRAM)
+# ----------------------------------
+# Require the specified program to be found for the DX_CURRENT_FEATURE to work.
+AC_DEFUN([DX_REQUIRE_PROG], [
+AC_PATH_TOOL([$1], [$2])
+if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then
+ AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION])
+ AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0)
+fi
+])
+
+# DX_TEST_FEATURE(FEATURE)
+# ------------------------
+# Expand to a shell expression testing whether the feature is active.
+AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1])
+
+# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE)
+# -------------------------------------------------
+# Verify that a required features has the right state before trying to turn on
+# the DX_CURRENT_FEATURE.
+AC_DEFUN([DX_CHECK_DEPEND], [
+test "$DX_FLAG_$1" = "$2" \
+|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1,
+ requires, contradicts) doxygen-DX_CURRENT_FEATURE])
+])
+
+# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE)
+# ----------------------------------------------------------
+# Turn off the DX_CURRENT_FEATURE if the required feature is off.
+AC_DEFUN([DX_CLEAR_DEPEND], [
+test "$DX_FLAG_$1" = "$2" || AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0)
+])
+
+# DX_FEATURE_ARG(FEATURE, DESCRIPTION,
+# CHECK_DEPEND, CLEAR_DEPEND,
+# REQUIRE, DO-IF-ON, DO-IF-OFF)
+# --------------------------------------------
+# Parse the command-line option controlling a feature. CHECK_DEPEND is called
+# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND),
+# otherwise CLEAR_DEPEND is called to turn off the default state if a required
+# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional
+# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and
+# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature.
+AC_DEFUN([DX_ARG_ABLE], [
+ AC_DEFUN([DX_CURRENT_FEATURE], [$1])
+ AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2])
+ AC_ARG_ENABLE(doxygen-$1,
+ [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1],
+ [--enable-doxygen-$1]),
+ DX_IF_FEATURE([$1], [don't $2], [$2]))],
+ [
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+ AC_SUBST([DX_FLAG_$1], 1)
+ $3
+;; #(
+n|N|no|No|NO)
+ AC_SUBST([DX_FLAG_$1], 0)
+;; #(
+*)
+ AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1])
+;;
+esac
+], [
+AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)])
+$4
+])
+if DX_TEST_FEATURE([$1]); then
+ $5
+ :
+fi
+if DX_TEST_FEATURE([$1]); then
+ AM_CONDITIONAL(DX_COND_$1, :)
+ $6
+ :
+else
+ AM_CONDITIONAL(DX_COND_$1, false)
+ $7
+ :
+fi
+])
+
+## -------------- ##
+## Public macros. ##
+## -------------- ##
+
+# DX_XXX_FEATURE(DEFAULT_STATE)
+# -----------------------------
+AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])])
+AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])])
+AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])])
+AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])])
+AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])])
+AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])])
+AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])])
+AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])])
+AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])])
+AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])])
+
+# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR])
+# ---------------------------------------------------------
+# PROJECT also serves as the base name for the documentation files.
+# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc".
+AC_DEFUN([DX_INIT_DOXYGEN], [
+
+# Files:
+AC_SUBST([DX_PROJECT], [$1])
+AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])])
+AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])])
+
+# Environment variables used inside doxygen.cfg:
+DX_ENV_APPEND(SRCDIR, $srcdir)
+DX_ENV_APPEND(PROJECT, $DX_PROJECT)
+DX_ENV_APPEND(DOCDIR, $DX_DOCDIR)
+DX_ENV_APPEND(VERSION, $PACKAGE_VERSION)
+
+# Doxygen itself:
+DX_ARG_ABLE(doc, [generate any doxygen documentation],
+ [],
+ [],
+ [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen)
+ DX_REQUIRE_PROG([DX_PERL], perl)],
+ [DX_ENV_APPEND(PERL_PATH, $DX_PERL)])
+
+# Dot for graphics:
+DX_ARG_ABLE(dot, [generate graphics for doxygen documentation],
+ [DX_CHECK_DEPEND(doc, 1)],
+ [DX_CLEAR_DEPEND(doc, 1)],
+ [DX_REQUIRE_PROG([DX_DOT], dot)],
+ [DX_ENV_APPEND(HAVE_DOT, YES)
+ DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])],
+ [DX_ENV_APPEND(HAVE_DOT, NO)])
+
+# Man pages generation:
+DX_ARG_ABLE(man, [generate doxygen manual pages],
+ [DX_CHECK_DEPEND(doc, 1)],
+ [DX_CLEAR_DEPEND(doc, 1)],
+ [],
+ [DX_ENV_APPEND(GENERATE_MAN, YES)],
+ [DX_ENV_APPEND(GENERATE_MAN, NO)])
+
+# RTF file generation:
+DX_ARG_ABLE(rtf, [generate doxygen RTF documentation],
+ [DX_CHECK_DEPEND(doc, 1)],
+ [DX_CLEAR_DEPEND(doc, 1)],
+ [],
+ [DX_ENV_APPEND(GENERATE_RTF, YES)],
+ [DX_ENV_APPEND(GENERATE_RTF, NO)])
+
+# XML file generation:
+DX_ARG_ABLE(xml, [generate doxygen XML documentation],
+ [DX_CHECK_DEPEND(doc, 1)],
+ [DX_CLEAR_DEPEND(doc, 1)],
+ [],
+ [DX_ENV_APPEND(GENERATE_XML, YES)],
+ [DX_ENV_APPEND(GENERATE_XML, NO)])
+
+# (Compressed) HTML help generation:
+DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation],
+ [DX_CHECK_DEPEND(doc, 1)],
+ [DX_CLEAR_DEPEND(doc, 1)],
+ [DX_REQUIRE_PROG([DX_HHC], hhc)],
+ [DX_ENV_APPEND(HHC_PATH, $DX_HHC)
+ DX_ENV_APPEND(GENERATE_HTML, YES)
+ DX_ENV_APPEND(GENERATE_HTMLHELP, YES)],
+ [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)])
+
+# Seperate CHI file generation.
+DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file],
+ [DX_CHECK_DEPEND(chm, 1)],
+ [DX_CLEAR_DEPEND(chm, 1)],
+ [],
+ [DX_ENV_APPEND(GENERATE_CHI, YES)],
+ [DX_ENV_APPEND(GENERATE_CHI, NO)])
+
+# Plain HTML pages generation:
+DX_ARG_ABLE(html, [generate doxygen plain HTML documentation],
+ [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)],
+ [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)],
+ [],
+ [DX_ENV_APPEND(GENERATE_HTML, YES)],
+ [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)])
+
+# PostScript file generation:
+DX_ARG_ABLE(ps, [generate doxygen PostScript documentation],
+ [DX_CHECK_DEPEND(doc, 1)],
+ [DX_CLEAR_DEPEND(doc, 1)],
+ [DX_REQUIRE_PROG([DX_LATEX], latex)
+ DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
+ DX_REQUIRE_PROG([DX_DVIPS], dvips)
+ DX_REQUIRE_PROG([DX_EGREP], egrep)])
+
+# PDF file generation:
+DX_ARG_ABLE(pdf, [generate doxygen PDF documentation],
+ [DX_CHECK_DEPEND(doc, 1)],
+ [DX_CLEAR_DEPEND(doc, 1)],
+ [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex)
+ DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
+ DX_REQUIRE_PROG([DX_EGREP], egrep)])
+
+# LaTeX generation for PS and/or PDF:
+if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then
+ AM_CONDITIONAL(DX_COND_latex, :)
+ DX_ENV_APPEND(GENERATE_LATEX, YES)
+else
+ AM_CONDITIONAL(DX_COND_latex, false)
+ DX_ENV_APPEND(GENERATE_LATEX, NO)
+fi
+
+# Paper size for PS and/or PDF:
+AC_ARG_VAR(DOXYGEN_PAPER_SIZE,
+ [a4wide (default), a4, letter, legal or executive])
+case "$DOXYGEN_PAPER_SIZE" in
+#(
+"")
+ AC_SUBST(DOXYGEN_PAPER_SIZE, "")
+;; #(
+a4wide|a4|letter|legal|executive)
+ DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE)
+;; #(
+*)
+ AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE'])
+;;
+esac
+
+#For debugging:
+#echo DX_FLAG_doc=$DX_FLAG_doc
+#echo DX_FLAG_dot=$DX_FLAG_dot
+#echo DX_FLAG_man=$DX_FLAG_man
+#echo DX_FLAG_html=$DX_FLAG_html
+#echo DX_FLAG_chm=$DX_FLAG_chm
+#echo DX_FLAG_chi=$DX_FLAG_chi
+#echo DX_FLAG_rtf=$DX_FLAG_rtf
+#echo DX_FLAG_xml=$DX_FLAG_xml
+#echo DX_FLAG_pdf=$DX_FLAG_pdf
+#echo DX_FLAG_ps=$DX_FLAG_ps
+#echo DX_ENV=$DX_ENV
+])
diff --git a/src/runtime/c/m4/c_ext.m4 b/src/runtime/c/m4/c_ext.m4
new file mode 100644
index 000000000..7c35c7dc3
--- /dev/null
+++ b/src/runtime/c/m4/c_ext.m4
@@ -0,0 +1,89 @@
+# AC_C_ALIGNOF
+# ------------
+# Check whether the C compiler supports the alignof(type) operator
+AC_DEFUN([AC_C_ALIGNOF],
+[
+ AC_CACHE_CHECK([for alignof],ac_cv_c_alignof,
+ [ac_cv_c_alignof=no
+ for ac_kw in alignof __alignof __alignof__; do
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([], [int align = $ac_kw (int);])],
+ [ac_cv_c_alignof=$ac_kw; break])
+ done])
+ if test $ac_cv_c_alignof != no; then
+ AC_DEFINE([HAVE_ALIGNOF], 1,
+ [Define to 1 if alignof works on your compiler])
+ if test $ac_cv_c_alignof != alignof; then
+ AC_DEFINE_UNQUOTED([alignof], [$ac_cv_c_alignof],
+ [Define to the name of the alignof operator.])
+ fi
+ fi
+])
+
+# AC_C_FAM_IN_MEM
+# ---------------
+# Check whether the C compiler supports a flexible array member
+# in a struct that is the (last) member of a struct
+AC_DEFUN([AC_C_FAM_IN_MEM],
+[
+ AC_CACHE_CHECK([for flexible array members in struct members],
+ ac_cv_c_fam_in_mem,
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([
+ struct { struct { char foo[]; } bar; } baz;
+ ])],
+ [ac_cv_c_fam_in_mem=yes],
+ [ac_cv_c_fam_in_mem=no])])
+ if test $ac_cv_c_fam_in_mem = yes; then
+ AC_DEFINE([CAN_HAVE_FAM_IN_MEMBER], 1,
+ [Define to 1 if a struct with flexible array members can be
+ the last member of another struct.])
+ fi
+])
+
+
+## AC_C_STATEMENT_EXPRESSIONS
+AC_DEFUN([AC_C_STATEMENT_EXPRESSIONS],
+[
+ AC_CACHE_CHECK([for statement expressions],
+ ac_cv_c_statement_expressions,
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([], [int x = ({ int a = 42; a = a + 1; a; })])],
+ [ac_cv_c_statement_expressions=yes],
+ [ac_cv_c_statement_expressions=no])])
+ if test $ac_cv_c_statement_expressions = yes; then
+ AC_DEFINE([HAVE_STATEMENT_EXPRESSIONS], 1,
+ [Define to 1 if statement expressions are supported.])
+ fi
+])
+
+## AC_C_ASCII
+AC_DEFUN([AC_C_ASCII],
+[
+ AC_CACHE_CHECK([whether the execution character set uses ASCII],
+ ac_cv_c_ascii,
+ [AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([], [[
+
+int i;
+static const char ascii[128] =
+ "\0\0\0\0\0\0\0\a\b\t\n\v\f\r\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ " !\"#\0%&'()*+,-./0123456789:;<=>?"
+ "\0ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_"
+ "\0abcdefghijklmnopqrstuvwxyz{|}~\0";
+
+for (i = 0; i < 128; i++) {
+ if (ascii[i] && ascii[i] != (char) i) {
+ return 1;
+ }
+}
+
+]])],
+ [ac_cv_c_ascii=yes],
+ [ac_cv_c_ascii=no])])
+ if test $ac_cv_c_ascii = yes; then
+ AC_DEFINE([CHAR_ASCII], 1,
+ [Define to 1 if the encoding of the basic character set is ASCII.])
+ fi
+])
diff --git a/src/runtime/c/pgf.h b/src/runtime/c/pgf.h
deleted file mode 100644
index ff9e6aba9..000000000
--- a/src/runtime/c/pgf.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef PGF_H
-#define PGF_H
-
-typedef struct _CId *CId;
-typedef struct _String *String;
-typedef struct _Literal *Literal ;
-typedef struct _Type *Type ;
-typedef struct _Expr *Expr ;
-typedef struct _PGF *PGF ;
-
-PGF readPGF(char *filename);
-void freePGF(PGF pgf);
-
-#endif
diff --git a/src/runtime/c/pgf/data.c b/src/runtime/c/pgf/data.c
new file mode 100644
index 000000000..98cbf5427
--- /dev/null
+++ b/src/runtime/c/pgf/data.c
@@ -0,0 +1,251 @@
+#include "data.h"
+#include "expr.h"
+#include <gu/type.h>
+#include <gu/variant.h>
+#include <gu/assert.h>
+
+
+PgfCCat pgf_ccat_string = { NULL, GU_NULL_SEQ, -1 };
+PgfCCat pgf_ccat_int = { NULL, GU_NULL_SEQ, -2 };
+PgfCCat pgf_ccat_float = { NULL, GU_NULL_SEQ, -3 };
+PgfCCat pgf_ccat_var = { NULL, GU_NULL_SEQ, -4 };
+
+PgfCCatId
+pgf_literal_cat(PgfLiteral lit)
+{
+ switch (gu_variant_tag(lit)) {
+ case PGF_LITERAL_STR:
+ return &pgf_ccat_string;
+ case PGF_LITERAL_INT:
+ return &pgf_ccat_int;
+ case PGF_LITERAL_FLT:
+ return &pgf_ccat_float;
+ default:
+ gu_impossible();
+ return NULL;
+ }
+}
+
+bool
+pgf_tokens_equal(PgfTokens t1, PgfTokens t2)
+{
+ size_t len1 = gu_seq_length(t1);
+ size_t len2 = gu_seq_length(t2);
+ if (len1 != len2) {
+ return false;
+ }
+ for (size_t i = 0; i < len1; i++) {
+ GuString s1 = gu_seq_get(t1, PgfToken, i);
+ GuString s2 = gu_seq_get(t2, PgfToken, i);
+ if (!gu_string_eq(s1, s2)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+GU_DEFINE_TYPE(PgfTokens, GuSeq, gu_type(GuString));
+
+GU_DEFINE_TYPE(PgfCId, typedef, gu_type(GuString));
+
+GU_DEFINE_TYPE(GuStringL, GuList, gu_type(GuString));
+
+
+#define gu_type__PgfCIdMap gu_type__GuStringMap
+typedef GuType_GuStringMap GuType_PgfCIdMap;
+#define GU_TYPE_INIT_PgfCIdMap GU_TYPE_INIT_GuStringMap
+
+GU_DEFINE_TYPE(PgfCCat, struct,
+ GU_MEMBER_S(PgfCCat, cnccat, PgfCncCat),
+ GU_MEMBER(PgfCCat, prods, PgfProductionSeq));
+
+GU_DEFINE_TYPE(PgfCCatId, shared, gu_type(PgfCCat));
+
+GU_DEFINE_TYPE(PgfCCatIds, GuList, gu_type(PgfCCatId));
+
+GU_DEFINE_TYPE(PgfCCatSeq, GuSeq, gu_type(PgfCCatId));
+
+GU_DEFINE_TYPE(PgfAlternative, struct,
+ GU_MEMBER(PgfAlternative, form, PgfTokens),
+ GU_MEMBER_P(PgfAlternative, prefixes, GuStringL));
+
+
+GU_DEFINE_TYPE(
+ PgfSymbol, GuVariant,
+ GU_CONSTRUCTOR_S(
+ PGF_SYMBOL_CAT, PgfSymbolCat,
+ GU_MEMBER(PgfSymbolCat, d, int),
+ GU_MEMBER(PgfSymbolCat, r, int)),
+ GU_CONSTRUCTOR_S(
+ PGF_SYMBOL_LIT, PgfSymbolLit,
+ GU_MEMBER(PgfSymbolLit, d, int),
+ GU_MEMBER(PgfSymbolLit, r, int)),
+ GU_CONSTRUCTOR_S(
+ PGF_SYMBOL_VAR, PgfSymbolVar,
+ GU_MEMBER(PgfSymbolVar, d, int),
+ GU_MEMBER(PgfSymbolVar, r, int)),
+ GU_CONSTRUCTOR_S(
+ PGF_SYMBOL_KS, PgfSymbolKS,
+ GU_MEMBER(PgfSymbolKS, tokens, PgfTokens)),
+ GU_CONSTRUCTOR_S(
+ PGF_SYMBOL_KP, PgfSymbolKP,
+ GU_MEMBER(PgfSymbolKP, default_form, PgfTokens),
+ GU_MEMBER(PgfSymbolKP, n_forms, GuLength),
+ GU_FLEX_MEMBER(PgfSymbolKP, forms, PgfAlternative)));
+
+GU_DEFINE_TYPE(
+ PgfCncCat, struct,
+ GU_MEMBER(PgfCncCat, cid, PgfCId),
+ GU_MEMBER_P(PgfCncCat, cats, PgfCCatIds),
+ GU_MEMBER(PgfCncCat, n_lins, size_t),
+ GU_MEMBER_P(PgfCncCat, lindefs, PgfFunIds),
+ GU_MEMBER_P(PgfCncCat, labels, GuStringL));
+
+// GU_DEFINE_TYPE(PgfSequence, GuList, gu_ptr_type(PgfSymbol));
+// GU_DEFINE_TYPE(PgfSequence, GuList, gu_type(PgfSymbol));
+GU_DEFINE_TYPE(PgfSequence, GuSeq, gu_type(PgfSymbol));
+
+GU_DEFINE_TYPE(PgfFlags, GuStringMap, gu_type(PgfLiteral), &gu_null_variant);
+
+typedef PgfFlags* PgfFlagsP;
+
+GU_DEFINE_TYPE(PgfFlagsP, pointer, gu_type(PgfFlags));
+
+GU_DEFINE_TYPE(PgfSequences, GuList, gu_type(PgfSequence));
+
+GU_DEFINE_TYPE(PgfSeqId, typedef, gu_type(PgfSequence));
+
+GU_DEFINE_TYPE(
+ PgfCncFun, struct,
+ GU_MEMBER(PgfCncFun, fun, PgfCId),
+ GU_MEMBER(PgfCncFun, n_lins, GuLength),
+ GU_FLEX_MEMBER(PgfCncFun, lins, PgfSeqId));
+
+GU_DEFINE_TYPE(PgfCncFuns, GuList,
+ GU_TYPE_LIT(referenced, _, gu_ptr_type(PgfCncFun)));
+
+GU_DEFINE_TYPE(PgfFunId, shared, gu_type(PgfCncFun));
+
+GU_DEFINE_TYPE(PgfFunIds, GuList, gu_type(PgfFunId));
+
+GU_DEFINE_TYPE(
+ PgfPArg, struct,
+ GU_MEMBER_P(PgfPArg, hypos, PgfCCatIds),
+ GU_MEMBER(PgfPArg, ccat, PgfCCatId));
+
+GU_DEFINE_TYPE(PgfPArgs, GuSeq, gu_type(PgfPArg));
+
+GU_DEFINE_TYPE(
+ PgfProduction, GuVariant,
+ GU_CONSTRUCTOR_S(
+ PGF_PRODUCTION_APPLY, PgfProductionApply,
+ GU_MEMBER(PgfProductionApply, fun, PgfFunId),
+ GU_MEMBER(PgfProductionApply, args, PgfPArgs)),
+ GU_CONSTRUCTOR_S(
+ PGF_PRODUCTION_COERCE, PgfProductionCoerce,
+ GU_MEMBER(PgfProductionCoerce, coerce, PgfCCatId)),
+ GU_CONSTRUCTOR_S(
+ PGF_PRODUCTION_CONST, PgfProductionConst,
+ GU_MEMBER(PgfProductionConst, expr, PgfExpr),
+ GU_MEMBER(PgfProductionConst, n_toks, GuLength),
+ GU_FLEX_MEMBER(PgfProductionConst, toks, GuString)));
+
+GU_DEFINE_TYPE(PgfProductions, GuList, gu_type(PgfProduction));
+GU_DEFINE_TYPE(PgfProductionSeq, GuSeq, gu_type(PgfProduction));
+
+GU_DEFINE_TYPE(
+ PgfPatt, GuVariant,
+ GU_CONSTRUCTOR_S(
+ PGF_PATT_APP, PgfPattApp,
+ GU_MEMBER(PgfPattApp, ctor, PgfCId),
+ GU_MEMBER(PgfPattApp, n_args, GuLength),
+ GU_MEMBER(PgfPattApp, args, PgfPatt)),
+ GU_CONSTRUCTOR_S(
+ PGF_PATT_LIT, PgfPattLit,
+ GU_MEMBER(PgfPattLit, lit, PgfLiteral)),
+ GU_CONSTRUCTOR_S(
+ PGF_PATT_VAR, PgfPattVar,
+ GU_MEMBER(PgfPattVar, var, PgfCId)),
+ GU_CONSTRUCTOR_S(
+ PGF_PATT_AS, PgfPattAs,
+ GU_MEMBER(PgfPattAs, var, PgfCId),
+ GU_MEMBER(PgfPattAs, patt, PgfPatt)),
+ GU_CONSTRUCTOR(
+ PGF_PATT_WILD, void),
+ GU_CONSTRUCTOR_S(
+ PGF_PATT_IMPL_ARG, PgfPattImplArg,
+ GU_MEMBER(PgfPattImplArg, patt, PgfPatt)),
+ GU_CONSTRUCTOR_S(
+ PGF_PATT_TILDE, PgfPattTilde,
+ GU_MEMBER(PgfPattTilde, expr, PgfExpr)));
+
+GU_DEFINE_TYPE(
+ PgfEquation, struct,
+ GU_MEMBER(PgfEquation, body, PgfExpr),
+ GU_MEMBER(PgfEquation, n_patts, GuLength),
+ GU_MEMBER(PgfEquation, patts, PgfPatt));
+
+// Distinct type so we can give it special treatment in the reader
+GU_DEFINE_TYPE(PgfEquationsM, GuSeq, gu_type(PgfEquation));
+
+GU_DEFINE_TYPE(
+ PgfFunDecl, struct,
+ GU_MEMBER_P(PgfFunDecl, type, PgfType),
+ GU_MEMBER(PgfFunDecl, arity, int),
+ GU_MEMBER(PgfFunDecl, defns, PgfEquationsM),
+ GU_MEMBER(PgfFunDecl, prob, double));
+
+GU_DEFINE_TYPE(
+ PgfCatFun, struct,
+ GU_MEMBER(PgfCatFun, prob, double),
+ GU_MEMBER(PgfCatFun, fun, PgfCId));
+
+GU_DEFINE_TYPE(
+ PgfCat, struct,
+ GU_MEMBER(PgfCat, context, PgfHypos),
+ GU_MEMBER(PgfCat, n_functions, GuLength),
+ GU_FLEX_MEMBER(PgfCat, functions, PgfCatFun));
+
+
+GU_DEFINE_TYPE(
+ PgfAbstr, struct,
+ GU_MEMBER(PgfAbstr, aflags, PgfFlagsP),
+ GU_MEMBER_V(PgfAbstr, funs,
+ GU_TYPE_LIT(pointer, PgfCIdMap*,
+ GU_TYPE_LIT(PgfCIdMap, _,
+ gu_ptr_type(PgfFunDecl),
+ &gu_null_struct))),
+ GU_MEMBER_V(PgfAbstr, cats,
+ GU_TYPE_LIT(pointer, PgfCIdMap*,
+ GU_TYPE_LIT(PgfCIdMap, _,
+ gu_ptr_type(PgfCat),
+ &gu_null_struct))));
+
+GU_DEFINE_TYPE(
+ PgfPrintNames, PgfCIdMap, gu_type(GuString), NULL);
+
+GU_DEFINE_TYPE(
+ PgfConcr, struct,
+ GU_MEMBER(PgfConcr, cflags, PgfFlagsP),
+ GU_MEMBER_P(PgfConcr, printnames, PgfPrintNames),
+ GU_MEMBER_V(PgfConcr, cnccats,
+ GU_TYPE_LIT(pointer, PgfCIdMap*,
+ GU_TYPE_LIT(PgfCIdMap, _,
+ gu_ptr_type(PgfCncCat),
+ &gu_null_struct))));
+
+GU_DEFINE_TYPE(
+ PgfPGF, struct,
+ GU_MEMBER(PgfPGF, major_version, uint16_t),
+ GU_MEMBER(PgfPGF, minor_version, uint16_t),
+ GU_MEMBER(PgfPGF, gflags, PgfFlagsP),
+ GU_MEMBER(PgfPGF, absname, PgfCId),
+ GU_MEMBER(PgfPGF, abstract, PgfAbstr),
+ GU_MEMBER_V(PgfPGF, concretes,
+ GU_TYPE_LIT(pointer, PgfCIdMap*,
+ GU_TYPE_LIT(PgfCIdMap, _,
+ gu_ptr_type(PgfConcr),
+ &gu_null_struct))));
+
diff --git a/src/runtime/c/pgf/data.h b/src/runtime/c/pgf/data.h
index b17ea6d66..d80a19526 100644
--- a/src/runtime/c/pgf/data.h
+++ b/src/runtime/c/pgf/data.h
@@ -1,76 +1,330 @@
-#ifndef PGF_DATA_H
-#define PGF_DATA_H
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libpgf.
+ *
+ * Libpgf is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libpgf is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libpgf. If not, see <http://www.gnu.org/licenses/>.
+ */
-typedef int BindType;
+#ifndef PGF_DATA_H_
+#define PGF_DATA_H_
-#include "expr.h"
-#include "type.h"
+#include <gu/list.h>
+#include <gu/variant.h>
+#include <gu/map.h>
+#include <gu/string.h>
+#include <gu/type.h>
+#include <gu/seq.h>
+#include <pgf/pgf.h>
+#include <pgf/expr.h>
-struct _String {
- int len;
- unsigned int chars[];
+typedef struct PgfCCat PgfCCat;
+typedef PgfCCat* PgfCCatId;
+extern GU_DECLARE_TYPE(PgfCCat, struct);
+extern GU_DECLARE_TYPE(PgfCCatId, shared);
+typedef GuList(PgfCCatId) PgfCCatIds;
+extern GU_DECLARE_TYPE(PgfCCatIds, GuList);
+typedef GuSeq PgfCCatSeq;
+extern GU_DECLARE_TYPE(PgfCCatSeq, GuSeq);
+
+typedef struct PgfAbstr PgfAbstr;
+typedef struct PgfFunDecl PgfFunDecl;
+typedef struct PgfConcr PgfConcr;
+
+typedef int PgfLength;
+typedef struct GuVariant PgfSymbol;
+typedef struct PgfAlternative PgfAlternative;
+typedef struct PgfCncFun PgfCncFun;
+
+
+typedef GuSeq PgfSequence; // -> PgfSymbol
+
+typedef PgfCncFun* PgfFunId; // key to PgfCncFuns
+extern GU_DECLARE_TYPE(PgfFunId, shared);
+typedef GuList(PgfCncFun*) PgfCncFuns;
+extern GU_DECLARE_TYPE(PgfCncFuns, GuList);
+typedef GuList(PgfFunId) PgfFunIds;
+extern GU_DECLARE_TYPE(PgfFunIds, GuList);
+// typedef GuStringMap PgfCIdMap; // PgfCId -> ?
+#define PgfCIdMap GuStringMap
+typedef PgfCIdMap PgfFlags; // PgfCId -> PgfLiteral
+extern GU_DECLARE_TYPE(PgfFlags, GuMap);
+
+extern GU_DECLARE_TYPE(PgfType, struct);
+typedef GuVariant PgfProduction;
+typedef GuList(PgfProduction) PgfProductions;
+extern GU_DECLARE_TYPE(PgfProductions, GuList);
+typedef GuSeq PgfProductionSeq;
+extern GU_DECLARE_TYPE(PgfProductionSeq, GuSeq);
+
+typedef struct PgfCatFun PgfCatFun;
+typedef struct PgfCncCat PgfCncCat;
+extern GU_DECLARE_TYPE(PgfCncCat, struct);
+typedef GuVariant PgfPatt;
+
+typedef GuList(GuString) GuStringL;
+extern GU_DECLARE_TYPE(GuStringL, GuList);
+typedef GuSeq PgfTokens; // -> PgfToken
+extern GU_DECLARE_TYPE(PgfTokens, GuSeq);
+
+bool
+pgf_tokens_equal(PgfTokens t1, PgfTokens t2);
+
+
+
+typedef PgfExpr PgfTree;
+
+typedef struct PgfEquation PgfEquation;
+typedef GuSeq PgfEquations;
+typedef PgfEquations PgfEquationsM; // can be null
+extern GU_DECLARE_TYPE(PgfEquationsM, GuSeq);
+typedef struct PgfCat PgfCat;
+
+typedef PgfSequence PgfSeqId; // shared reference
+
+extern GU_DECLARE_TYPE(PgfSeqId, typedef);
+
+typedef GuList(PgfSequence) PgfSequences;
+
+extern GU_DECLARE_TYPE(PgfSequences, GuList);
+
+
+
+
+struct PgfAbstr {
+ PgfFlags* aflags;
+ PgfCIdMap* funs; // |-> PgfFunDecl*
+ PgfCIdMap* cats; // |-> PgfCat*
};
-struct _CId {
- int len;
- char chars[];
+struct PgfPGF {
+ uint16_t major_version;
+ uint16_t minor_version;
+ PgfFlags* gflags;
+ PgfCId absname;
+ PgfAbstr abstract;
+ PgfCIdMap* concretes; // |-> PgfConcr*
+ GuPool* pool;
};
-typedef struct _CIdList {
- int count;
- CId names[];
-} *CIdList;
-
-typedef struct _AbsCat {
- CId name;
- Context hypos;
- CIdList funs;
-} *AbsCat;
-
-typedef struct _AbsCats {
- int count;
- struct _AbsCat lst[];
-} *AbsCats;
-
-typedef struct _AbsFun {
- CId name;
- Type ty;
- int arrity;
- Equations equs;
-} *AbsFun;
-
-typedef struct _AbsFuns {
- int count;
- struct _AbsFun lst[];
-} *AbsFuns;
-
-struct _Flag {
- CId name;
- Literal value;
-} ;
-
-typedef struct _Flags {
- int count;
- struct _Flag values[];
-} *Flags;
-
-typedef struct _Abstract {
- CId name;
- Flags flags;
- AbsFuns funs;
- AbsCats cats;
-} *Abstract;
-
-typedef struct _Concrete {
- CId name;
- Flags flags;
-} *Concrete;
-
-struct _PGF {
- Flags flags;
- int nConcr;
- struct _Abstract abstract;
- struct _Concrete concretes[];
+extern GU_DECLARE_TYPE(PgfPGF, struct);
+
+struct PgfFunDecl {
+ PgfType* type;
+ int arity; // Only for computational defs?
+ PgfEquationsM defns; // maybe null
+ double prob;
+};
+
+struct PgfCatFun {
+ double prob;
+ PgfCId fun;
+};
+
+struct PgfCat {
+ // TODO: Add cid here
+ PgfHypos context;
+ GuLength n_functions;
+ PgfCatFun functions[]; // XXX: resolve to PgfFunDecl*?
+};
+
+
+struct PgfCncCat {
+ PgfCId cid;
+ PgfCCatIds* cats;
+ PgfFunIds* lindefs;
+ size_t n_lins;
+
+ GuStringL* labels;
+ /**< Labels for tuples. All nested tuples, records and tables
+ * in the GF linearization types are flattened into a single
+ * tuple in the corresponding PGF concrete category. This
+ * field holds the labels that indicate which GF field or
+ * parameter (or their combination) each tuple element
+ * represents. */
+};
+
+struct PgfCncFun {
+ PgfCId fun; // XXX: resolve to PgfFunDecl*?
+ GuLength n_lins;
+ PgfSeqId lins[];
+};
+
+struct PgfAlternative {
+ PgfTokens form;
+ /**< The form of this variant as a list of tokens. */
+
+ GuStringL* prefixes;
+ /**< The prefixes of the following symbol that trigger this
+ * form. */
+};
+
+struct PgfCCat {
+ PgfCncCat* cnccat;
+ PgfProductionSeq prods;
+ int fid;
+};
+
+extern PgfCCat pgf_ccat_string, pgf_ccat_int, pgf_ccat_float, pgf_ccat_var;
+
+typedef PgfCIdMap PgfPrintNames;
+extern GU_DECLARE_TYPE(PgfPrintNames, GuStringMap);
+
+struct PgfConcr {
+ PgfFlags* cflags;
+ PgfPrintNames* printnames;
+ PgfCIdMap* cnccats;
+ PgfCCatSeq extra_ccats;
};
-#endif
+extern GU_DECLARE_TYPE(PgfConcr, struct);
+
+typedef enum {
+ PGF_SYMBOL_CAT,
+ PGF_SYMBOL_LIT,
+ PGF_SYMBOL_VAR,
+ PGF_SYMBOL_KS,
+ PGF_SYMBOL_KP
+} PgfSymbolTag;
+
+typedef struct PgfSymbolIdx PgfSymbolIdx;
+
+struct PgfSymbolIdx {
+ int d;
+ int r;
+};
+
+typedef PgfSymbolIdx PgfSymbolCat, PgfSymbolLit, PgfSymbolVar;
+
+typedef struct {
+ PgfTokens tokens;
+} PgfSymbolKS;
+
+typedef struct PgfSymbolKP
+/** A prefix-dependent symbol. The form that this symbol takes
+ * depends on the form of a prefix of the following symbol. */
+{
+ PgfTokens default_form;
+ /**< Default form that this symbol takes if none of of the
+ * variant forms is triggered. */
+
+ GuLength n_forms;
+ PgfAlternative forms[];
+ /**< Variant forms whose choise depends on the following
+ * symbol. */
+} PgfSymbolKP;
+
+
+
+
+// PgfProduction
+
+typedef enum {
+ PGF_PRODUCTION_APPLY,
+ PGF_PRODUCTION_COERCE,
+ PGF_PRODUCTION_CONST
+} PgfProductionTag;
+
+typedef struct PgfPArg PgfPArg;
+
+struct PgfPArg {
+ PgfCCatId ccat;
+ PgfCCatIds* hypos;
+};
+
+GU_DECLARE_TYPE(PgfPArg, struct);
+
+typedef GuSeq PgfPArgs;
+
+GU_DECLARE_TYPE(PgfPArgs, GuSeq);
+
+typedef struct {
+ PgfFunId fun;
+ PgfPArgs args;
+} PgfProductionApply;
+
+typedef struct PgfProductionCoerce
+/** A coercion. This production is a logical union of the coercions of
+ * another FId. This allows common subsets of productions to be
+ * shared. */
+{
+ PgfCCatId coerce;
+} PgfProductionCoerce;
+
+typedef struct {
+ PgfExpr expr; // XXX
+ GuLength n_toks;
+ GuString toks[]; // XXX
+} PgfProductionConst;
+
+
+extern GU_DECLARE_TYPE(PgfProduction, GuVariant);
+extern GU_DECLARE_TYPE(PgfBindType, enum);
+extern GU_DECLARE_TYPE(PgfLiteral, GuVariant);
+
+
+PgfCCatId
+pgf_literal_cat(PgfLiteral lit);
+
+// PgfPatt
+
+typedef enum {
+ PGF_PATT_APP,
+ PGF_PATT_LIT,
+ PGF_PATT_VAR,
+ PGF_PATT_AS,
+ PGF_PATT_WILD,
+ PGF_PATT_IMPL_ARG,
+ PGF_PATT_TILDE,
+ PGF_PATT_NUM_TAGS
+} PgfPattTag;
+
+typedef struct {
+ PgfCId ctor;
+ GuLength n_args;
+ PgfPatt args[];
+} PgfPattApp;
+
+typedef struct {
+ PgfLiteral* lit;
+} PgfPattLit;
+
+typedef struct {
+ PgfCId var;
+} PgfPattVar;
+
+typedef struct {
+ PgfCId var;
+ PgfPatt patt;
+} PgfPattAs;
+
+typedef void PgfPattWild;
+
+typedef struct {
+ PgfPatt patt;
+} PgfPattImplArg;
+
+typedef struct {
+ PgfExpr expr;
+} PgfPattTilde;
+
+struct PgfEquation {
+ PgfExpr body;
+ GuLength n_patts;
+ PgfPatt patts[];
+};
+
+
+
+#endif /* PGF_PRIVATE_H_ */
diff --git a/src/runtime/c/pgf/edsl.h b/src/runtime/c/pgf/edsl.h
new file mode 100644
index 000000000..af21e6c15
--- /dev/null
+++ b/src/runtime/c/pgf/edsl.h
@@ -0,0 +1,20 @@
+#ifndef PGF_EDSL_H_
+#define PGF_EDSL_H_
+
+#include <pgf/expr.h>
+
+#define APP(f, a) \
+ gu_new_variant_i(PGF_EDSL_POOL, PGF_EXPR_APP, PgfExprApp, f, a)
+#define APP2(f, a1, a2) APP(APP(f, a1), a2)
+#define APP3(f, a1, a2, a3) APP2(APP(f, a1), a2, a3)
+
+#define VAR(s) \
+ gu_new_variant_i(PGF_EDSL_POOL, PGF_EXPR_FUN, PgfExprFun, gu_cstring(#s))
+
+#define APPV(s, a) APP(VAR(s), a)
+#define APPV2(s, a1, a2) APP2(VAR(s), a1, a2)
+#define APPV3(s, a1, a2, a3) APP3(VAR(s), a1, a2)
+
+
+
+#endif // PGF_EDSL_H_
diff --git a/src/runtime/c/pgf/expr.c b/src/runtime/c/pgf/expr.c
new file mode 100644
index 000000000..cd5d69928
--- /dev/null
+++ b/src/runtime/c/pgf/expr.c
@@ -0,0 +1,334 @@
+#include "expr.h"
+#include <gu/intern.h>
+#include <gu/assert.h>
+#include <ctype.h>
+
+
+PgfExpr
+pgf_expr_unwrap(PgfExpr expr)
+{
+ while (true) {
+ GuVariantInfo i = gu_variant_open(expr);
+ switch (i.tag) {
+ case PGF_EXPR_IMPL_ARG: {
+ PgfExprImplArg* eimpl = i.data;
+ expr = eimpl->expr;
+ break;
+ }
+ case PGF_EXPR_TYPED: {
+ PgfExprTyped* etyped = i.data;
+ expr = etyped->expr;
+ break;
+ }
+ default:
+ return expr;
+ }
+ }
+}
+
+int
+pgf_expr_arity(PgfExpr expr)
+{
+ int n = 0;
+ while (true) {
+ PgfExpr e = pgf_expr_unwrap(expr);
+ GuVariantInfo i = gu_variant_open(e);
+ switch (i.tag) {
+ case PGF_EXPR_APP: {
+ PgfExprApp* app = i.data;
+ expr = app->fun;
+ n = n + 1;
+ break;
+ }
+ case PGF_EXPR_FUN:
+ return n;
+ default:
+ return -1;
+ }
+ }
+}
+
+PgfApplication*
+pgf_expr_unapply(PgfExpr expr, GuPool* pool)
+{
+ int arity = pgf_expr_arity(expr);
+ if (arity < 0) {
+ return NULL;
+ }
+ PgfApplication* appl = gu_new_flex(pool, PgfApplication, args, arity);
+ appl->n_args = arity;
+ for (int n = arity - 1; n >= 0; n--) {
+ PgfExpr e = pgf_expr_unwrap(expr);
+ gu_assert(gu_variant_tag(e) == PGF_EXPR_APP);
+ PgfExprApp* app = gu_variant_data(e);
+ appl->args[n] = app->arg;
+ expr = app->fun;
+ }
+ PgfExpr e = pgf_expr_unwrap(expr);
+ gu_assert(gu_variant_tag(e) == PGF_EXPR_FUN);
+ PgfExprFun* fun = gu_variant_data(e);
+ appl->fun = fun->fun;
+ return appl;
+}
+
+GU_DEFINE_TYPE(PgfBindType, enum,
+ GU_ENUM_C(PgfBindType, PGF_BIND_TYPE_EXPLICIT),
+ GU_ENUM_C(PgfBindType, PGF_BIND_TYPE_IMPLICIT));
+
+GU_DEFINE_TYPE(PgfLiteral, GuVariant,
+ GU_CONSTRUCTOR_S(PGF_LITERAL_STR, PgfLiteralStr,
+ GU_MEMBER(PgfLiteralStr, val, GuString)),
+ GU_CONSTRUCTOR_S(PGF_LITERAL_INT, PgfLiteralInt,
+ GU_MEMBER(PgfLiteralInt, val, int)),
+ GU_CONSTRUCTOR_S(PGF_LITERAL_FLT, PgfLiteralFlt,
+ GU_MEMBER(PgfLiteralFlt, val, double)));
+
+GU_DECLARE_TYPE(PgfType, struct);
+
+GU_DEFINE_TYPE(PgfHypo, struct,
+ GU_MEMBER(PgfHypo, bindtype, PgfBindType),
+ GU_MEMBER(PgfHypo, cid, PgfCId),
+ GU_MEMBER_P(PgfHypo, type, PgfType));
+
+GU_DEFINE_TYPE(PgfHypos, GuSeq, gu_type(PgfHypo));
+
+GU_DEFINE_TYPE(PgfType, struct,
+ GU_MEMBER(PgfType, hypos, PgfHypos),
+ GU_MEMBER(PgfType, cid, PgfCId),
+ GU_MEMBER(PgfType, n_exprs, GuLength),
+ GU_FLEX_MEMBER(PgfType, exprs, PgfExpr));
+
+GU_DEFINE_TYPE(
+ PgfExpr, GuVariant,
+ GU_CONSTRUCTOR_S(
+ PGF_EXPR_ABS, PgfExprAbs,
+ GU_MEMBER(PgfExprAbs, bind_type, PgfBindType),
+ GU_MEMBER(PgfExprAbs, id, GuStr),
+ GU_MEMBER(PgfExprAbs, body, PgfExpr)),
+ GU_CONSTRUCTOR_S(
+ PGF_EXPR_APP, PgfExprApp,
+ GU_MEMBER(PgfExprApp, fun, PgfExpr),
+ GU_MEMBER(PgfExprApp, arg, PgfExpr)),
+ GU_CONSTRUCTOR_S(
+ PGF_EXPR_LIT, PgfExprLit,
+ GU_MEMBER(PgfExprLit, lit, PgfLiteral)),
+ GU_CONSTRUCTOR_S(
+ PGF_EXPR_META, PgfExprMeta,
+ GU_MEMBER(PgfExprMeta, id, int)),
+ GU_CONSTRUCTOR_S(
+ PGF_EXPR_FUN, PgfExprFun,
+ GU_MEMBER(PgfExprFun, fun, GuStr)),
+ GU_CONSTRUCTOR_S(
+ PGF_EXPR_VAR, PgfExprVar,
+ GU_MEMBER(PgfExprVar, var, int)),
+ GU_CONSTRUCTOR_S(
+ PGF_EXPR_TYPED, PgfExprTyped,
+ GU_MEMBER(PgfExprTyped, expr, PgfExpr),
+ GU_MEMBER_P(PgfExprTyped, type, PgfType)),
+ GU_CONSTRUCTOR_S(
+ PGF_EXPR_IMPL_ARG, PgfExprImplArg,
+ GU_MEMBER(PgfExprImplArg, expr, PgfExpr)));
+
+
+typedef struct PgfExprParser PgfExprParser;
+
+struct PgfExprParser {
+ GuReader* rdr;
+ GuIntern* intern;
+ GuExn* err;
+ GuPool* expr_pool;
+ const char* lookahead;
+ int next_char;
+};
+
+
+static const char pgf_expr_lpar[] = "(";
+static const char pgf_expr_rpar[] = ")";
+static const char pgf_expr_semic[] = ";";
+
+static char
+pgf_expr_parser_next(PgfExprParser* parser)
+{
+ if (parser->next_char >= 0) {
+ char ret = (char) parser->next_char;
+ parser->next_char = -1;
+ return ret;
+ }
+ return gu_getc(parser->rdr, parser->err);
+}
+
+static const char*
+pgf_expr_parser_lookahead(PgfExprParser* parser)
+{
+ if (parser->lookahead != NULL) {
+ return parser->lookahead;
+ }
+ const char* str = NULL;
+ char c;
+ do {
+ c = pgf_expr_parser_next(parser);
+ if (!gu_ok(parser->err)) {
+ return NULL;
+ }
+ } while (isspace(c));
+ switch (c) {
+ case '(':
+ str = pgf_expr_lpar;
+ break;
+ case ')':
+ str = pgf_expr_rpar;
+ break;
+ case ';':
+ str = pgf_expr_semic;
+ break;
+ default:
+ if (isalpha(c)) {
+ GuPool* tmp_pool = gu_new_pool();
+ GuCharBuf* chars = gu_new_buf(char, tmp_pool);
+ while (isalnum(c) || c == '_') {
+ gu_buf_push(chars, char, c);
+ c = pgf_expr_parser_next(parser);
+ if (!gu_ok(parser->err)) {
+ return NULL;
+ }
+ }
+ parser->next_char = (unsigned char) c;
+ char* tmp_str = gu_chars_str(gu_buf_seq(chars),
+ tmp_pool);
+ str = gu_intern_str(parser->intern, tmp_str);
+ gu_pool_free(tmp_pool);
+ }
+ }
+ parser->lookahead = str;
+ return str;
+}
+
+static bool
+pgf_expr_parser_token_is_id(const char* str)
+{
+ if (str == NULL || !str[0]) {
+ return false;
+ }
+ char c = str[0];
+ return (isalpha(c) || c == '_');
+}
+
+static void
+pgf_expr_parser_consume(PgfExprParser* parser)
+{
+ pgf_expr_parser_lookahead(parser);
+ parser->lookahead = NULL;
+}
+
+static PgfExpr
+pgf_expr_parser_expr(PgfExprParser* parser);
+
+static PgfExpr
+pgf_expr_parser_term(PgfExprParser* parser)
+{
+ const char* la = pgf_expr_parser_lookahead(parser);
+
+ if (la == pgf_expr_lpar) {
+ pgf_expr_parser_consume(parser);
+ PgfExpr expr = pgf_expr_parser_expr(parser);
+ la = pgf_expr_parser_lookahead(parser);
+ if (la == pgf_expr_rpar) {
+ pgf_expr_parser_consume(parser);
+ return expr;
+ }
+ } else if (pgf_expr_parser_token_is_id(la)) {
+ pgf_expr_parser_consume(parser);
+ GuString s = gu_str_string(la, parser->expr_pool);
+ return gu_new_variant_i(parser->expr_pool,
+ PGF_EXPR_FUN,
+ PgfExprFun,
+ s);
+ }
+ return gu_null_variant;
+}
+
+static PgfExpr
+pgf_expr_parser_expr(PgfExprParser* parser)
+{
+ PgfExpr expr = pgf_expr_parser_term(parser);
+ if (gu_variant_is_null(expr))
+ {
+ return expr;
+ }
+ while (true) {
+ PgfExpr arg = pgf_expr_parser_term(parser);
+ if (gu_variant_is_null(arg)) {
+ return expr;
+ }
+ expr = gu_new_variant_i(parser->expr_pool,
+ PGF_EXPR_APP,
+ PgfExprApp,
+ expr, arg);
+ }
+}
+
+
+
+PgfExpr
+pgf_read_expr(GuReader* rdr, GuPool* pool, GuExn* err)
+{
+ GuPool* tmp_pool = gu_new_pool();
+ PgfExprParser* parser = gu_new(PgfExprParser, tmp_pool);
+ parser->rdr = rdr;
+ parser->intern = gu_new_intern(pool, tmp_pool);
+ parser->expr_pool = pool;
+ parser->err = err;
+ parser->lookahead = NULL;
+ parser->next_char = -1;
+ PgfExpr expr = pgf_expr_parser_expr(parser);
+ const char* la = pgf_expr_parser_lookahead(parser);
+ if (la == pgf_expr_semic) {
+ pgf_expr_parser_consume(parser);
+ } else {
+ expr = gu_null_variant;
+ }
+ gu_pool_free(tmp_pool);
+ return expr;
+}
+
+static void
+pgf_expr_print_with_paren(PgfExpr expr, bool need_paren,
+ GuWriter* wtr, GuExn* err)
+{
+ GuVariantInfo ei = gu_variant_open(expr);
+ switch (ei.tag) {
+ case PGF_EXPR_FUN: {
+ PgfExprFun* fun = ei.data;
+ gu_string_write(fun->fun, wtr, err);
+ break;
+ }
+ case PGF_EXPR_APP: {
+ PgfExprApp* app = ei.data;
+ if (need_paren) {
+ gu_puts("(", wtr, err);
+ }
+ pgf_expr_print_with_paren(app->fun, false, wtr, err);
+ gu_puts(" ", wtr, err);
+ pgf_expr_print_with_paren(app->arg, true, wtr, err);
+ if (need_paren) {
+ gu_puts(")", wtr, err);
+ }
+ break;
+ }
+ case PGF_EXPR_ABS:
+ case PGF_EXPR_LIT:
+ case PGF_EXPR_META:
+ case PGF_EXPR_VAR:
+ case PGF_EXPR_TYPED:
+ case PGF_EXPR_IMPL_ARG:
+ gu_impossible();
+ break;
+ default:
+ gu_impossible();
+ }
+}
+
+void
+pgf_expr_print(PgfExpr expr, GuWriter* wtr, GuExn* err) {
+ pgf_expr_print_with_paren(expr, false, wtr, err);
+}
diff --git a/src/runtime/c/pgf/expr.h b/src/runtime/c/pgf/expr.h
index d4d2aaea2..7ecca30bd 100644
--- a/src/runtime/c/pgf/expr.h
+++ b/src/runtime/c/pgf/expr.h
@@ -1,144 +1,152 @@
-#ifndef PGF_EXPR_H
-#define PGF_EXPR_H
+#ifndef EXPR_H_
+#define EXPR_H_
-#define LIT_STR 0
-#define LIT_INT 1
-#define LIT_FLOAT 2
+#include <gu/read.h>
+#include <gu/write.h>
+#include <gu/variant.h>
+#include <gu/seq.h>
+#include <pgf/pgf.h>
-struct _Literal {
- int tag;
+/// Abstract syntax trees
+/// @file
+
+/// An abstract syntax tree
+typedef GuVariant PgfExpr;
+
+GU_DECLARE_TYPE(PgfExpr, GuVariant);
+
+typedef GuList(PgfExpr) PgfExprs;
+
+typedef struct PgfHypo PgfHypo;
+typedef struct PgfType PgfType;
+
+typedef int PgfMetaId;
+
+typedef enum {
+ PGF_BIND_TYPE_EXPLICIT,
+ PGF_BIND_TYPE_IMPLICIT
+} PgfBindType;
+
+// PgfLiteral
+
+typedef GuVariant PgfLiteral;
+
+
+typedef enum {
+ PGF_LITERAL_STR,
+ PGF_LITERAL_INT,
+ PGF_LITERAL_FLT,
+ PGF_LITERAL_NUM_TAGS
+} PgfLiteralTag;
+
+typedef struct {
+ GuStr val;
+} PgfLiteralStr;
+
+typedef struct {
+ int val;
+} PgfLiteralInt;
+
+typedef struct {
+ double val;
+} PgfLiteralFlt;
+
+
+
+struct PgfHypo {
+ PgfBindType bindtype;
+
+ PgfCId cid;
+ /**< Locally scoped name for the parameter if dependent types
+ * are used. "_" for normal parameters. */
+
+ PgfType* type;
+};
+
+typedef GuSeq PgfHypos;
+extern GU_DECLARE_TYPE(PgfHypos, GuSeq);
+
+struct PgfType {
+ PgfHypos hypos;
+ PgfCId cid; /// XXX: resolve to PgfCat*?
+ int n_exprs;
+ PgfExpr exprs[];
};
-typedef struct _LiteralStr {
- struct _Literal _;
- String val;
-} *LiteralStr;
-
-typedef struct _LiteralInt {
- struct _Literal _;
- int val;
-} *LiteralInt;
-
-typedef struct _LiteralFloat {
- struct _Literal _;
- double val;
-} *LiteralFloat;
-
-#define TAG_ABS 0
-#define TAG_APP 1
-#define TAG_LIT 2
-#define TAG_MET 3
-#define TAG_FUN 4
-#define TAG_VAR 5
-#define TAG_TYP 6
-#define TAG_IMP 7
-
-struct _Expr {
- int tag;
+
+typedef enum {
+ PGF_EXPR_ABS,
+ PGF_EXPR_APP,
+ PGF_EXPR_LIT,
+ PGF_EXPR_META,
+ PGF_EXPR_FUN,
+ PGF_EXPR_VAR,
+ PGF_EXPR_TYPED,
+ PGF_EXPR_IMPL_ARG,
+ PGF_EXPR_NUM_TAGS
+} PgfExprTag;
+
+typedef struct {
+ PgfBindType bind_type;
+ PgfCId id; //
+ PgfExpr body;
+} PgfExprAbs;
+
+typedef struct {
+ PgfExpr fun;
+ PgfExpr arg;
+} PgfExprApp;
+
+typedef struct {
+ PgfLiteral lit;
+} PgfExprLit;
+
+typedef struct {
+ PgfMetaId id;
+} PgfExprMeta;
+
+typedef struct {
+ PgfCId fun;
+} PgfExprFun;
+
+typedef struct {
+ int var;
+} PgfExprVar;
+
+/**< A variable. The value is a de Bruijn index to the environment,
+ * beginning from the innermost variable. */
+
+typedef struct {
+ PgfExpr expr;
+ PgfType* type;
+} PgfExprTyped;
+
+typedef struct {
+ PgfExpr expr;
+} PgfExprImplArg;
+
+int
+pgf_expr_arity(PgfExpr expr);
+
+PgfExpr
+pgf_expr_unwrap(PgfExpr expr);
+
+typedef struct PgfApplication PgfApplication;
+
+struct PgfApplication {
+ PgfCId fun;
+ int n_args;
+ PgfExpr args[];
};
-typedef struct _ExprAbs {
- struct _Expr _;
- BindType bt;
- CId var;
- Expr body;
-} *ExprAbs;
-
-typedef struct _ExprApp {
- struct _Expr _;
- Expr left, right;
-} *ExprApp;
-
-typedef struct _ExprLit {
- struct _Expr _;
- Literal lit;
-} *ExprLit;
-
-typedef struct _ExprMeta {
- struct _Expr _;
- int id;
-} *ExprMeta;
-
-typedef struct _ExprFun {
- struct _Expr _;
- CId fun;
-} *ExprFun;
-
-typedef struct _ExprVar {
- struct _Expr _;
- int index;
-} *ExprVar;
-
-typedef struct _ExprTyped {
- struct _Expr _;
- Expr e;
- Type ty;
-} *ExprTyped;
-
-typedef struct _ExprImplArg {
- struct _Expr _;
- Expr e;
-} *ExprImplArg;
-
-#define TAG_PAPP 0
-#define TAG_PVAR 1
-#define TAG_PAT 2
-#define TAG_PWILD 3
-#define TAG_PLIT 4
-#define TAG_PIMP 5
-#define TAG_PTILDE 6
-
-typedef struct _Patt {
- int tag;
-} *Patt;
-
-typedef struct _Patts {
- int count;
- Patt pats[];
-} *Patts;
-
-typedef struct _PattApp {
- struct _Patt _;
- CId fun;
- struct _Patts args;
-} *PattApp;
-
-typedef struct _PattVar {
- struct _Patt _;
- CId var;
-} *PattVar;
-
-typedef struct _PattAt {
- struct _Patt _;
- CId var;
- Patt pat;
-} *PattAt;
-
-typedef struct _PattWild {
- struct _Patt _;
-} *PattWild;
-
-typedef struct _PattLit {
- struct _Patt _;
- Literal lit;
-} *PattLit;
-
-typedef struct _PattImplArg {
- struct _Patt _;
- Patt pat;
-} *PattImplArg;
-
-typedef struct _PattTilde {
- struct _Patt _;
- Expr e;
-} *PattTilde;
-
-typedef struct _Equations {
- int count;
- struct _Equation {
- Patts lhs;
- Expr rhs;
- } equs[];
-} *Equations;
-
-#endif
+PgfApplication*
+pgf_expr_unapply(PgfExpr expr, GuPool* pool);
+
+
+PgfExpr
+pgf_read_expr(GuReader* rdr, GuPool* pool, GuExn* err);
+
+void
+pgf_expr_print(PgfExpr expr, GuWriter* wtr, GuExn* err);
+
+#endif /* EXPR_H_ */
diff --git a/src/runtime/c/pgf/linearize.c b/src/runtime/c/pgf/linearize.c
new file mode 100644
index 000000000..ce1b99b5b
--- /dev/null
+++ b/src/runtime/c/pgf/linearize.c
@@ -0,0 +1,613 @@
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libpgf.
+ *
+ * Libpgf is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libpgf is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libpgf. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "data.h"
+#include "linearize.h"
+#include <gu/map.h>
+#include <gu/fun.h>
+#include <gu/log.h>
+#include <gu/choice.h>
+#include <gu/seq.h>
+#include <gu/string.h>
+#include <gu/assert.h>
+#include <pgf/expr.h>
+
+typedef GuStringMap PgfLinInfer;
+typedef GuSeq PgfProdSeq;
+
+static GU_DEFINE_TYPE(PgfProdSeq, GuSeq, gu_type(PgfProduction));
+
+typedef struct PgfLinInferEntry PgfLinInferEntry;
+
+struct PgfLinInferEntry {
+ PgfCCat* cat;
+ PgfCncFun* fun;
+};
+
+static GU_DEFINE_TYPE(
+ PgfLinInferEntry, struct,
+ GU_MEMBER_P(PgfLinInferEntry, cat, PgfCCat)
+ // ,GU_MEMBER(PgfLinInferEntry, fun, ...)
+ );
+
+typedef GuBuf PgfLinInfers;
+static GU_DEFINE_TYPE(PgfLinInfers, GuBuf, gu_type(PgfLinInferEntry));
+
+typedef GuIntMap PgfCncProds;
+static GU_DEFINE_TYPE(PgfCncProds, GuIntMap, gu_type(PgfProdSeq),
+ &gu_null_seq);
+
+typedef GuStringMap PgfLinProds;
+static GU_DEFINE_TYPE(PgfLinProds, GuStringMap, gu_ptr_type(PgfCncProds),
+ &gu_null_struct);
+
+
+static GuHash
+pgf_lzr_cats_hash_fn(GuHasher* self, const void* p)
+{
+ (void) self;
+ PgfCCatIds* cats = *(PgfCCatIds* const*)p;
+ size_t len = gu_list_length(cats);
+ uintptr_t h = 0;
+ for (size_t i = 0; i < len; i++) {
+ h = 101 * h + (uintptr_t) gu_list_index(cats, i);
+ }
+ return h;
+}
+
+static bool
+pgf_lzr_cats_eq_fn(GuEquality* self, const void* p1, const void* p2)
+{
+ (void) self;
+ PgfCCatIds* cats1 = *(PgfCCatIds* const*) p1;
+ PgfCCatIds* cats2 = *(PgfCCatIds* const*) p2;
+ int len = gu_list_length(cats1);
+ if (gu_list_length(cats2) != len) {
+ return false;
+ }
+ for (int i = 0; i < len; i++) {
+ PgfCCat* cat1 = gu_list_index(cats1, i);
+ PgfCCat* cat2 = gu_list_index(cats2, i);
+ if (cat1 != cat2) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static GuHasher
+pgf_lzr_cats_hasher[1] = {
+ {
+ .eq = { pgf_lzr_cats_eq_fn },
+ .hash = pgf_lzr_cats_hash_fn
+ }
+};
+
+typedef GuMap PgfInferMap;
+static GU_DEFINE_TYPE(PgfInferMap, GuMap,
+ gu_ptr_type(PgfCCatIds), pgf_lzr_cats_hasher,
+ gu_ptr_type(PgfLinInfers), &gu_null_struct);
+
+typedef GuStringMap PgfFunIndices;
+static GU_DEFINE_TYPE(PgfFunIndices, GuStringMap, gu_ptr_type(PgfInferMap),
+ &gu_null_struct);
+
+typedef GuBuf PgfCCatBuf;
+static GU_DEFINE_TYPE(PgfCCatBuf, GuBuf, gu_ptr_type(PgfCCat));
+
+typedef GuMap PgfCoerceIdx;
+static GU_DEFINE_TYPE(PgfCoerceIdx, GuMap,
+ gu_type(PgfCCat), NULL,
+ gu_ptr_type(PgfCCatBuf), &gu_null_struct);
+
+struct PgfLzr {
+ PgfConcr* cnc;
+ GuPool* pool;
+ PgfFunIndices* fun_indices;
+ PgfCoerceIdx* coerce_idx;
+};
+
+GU_DEFINE_TYPE(
+ PgfLzr, struct,
+ GU_MEMBER_P(PgfLzr, cnc, PgfConcr),
+ GU_MEMBER_P(PgfLzr, fun_indices, PgfFunIndices),
+ GU_MEMBER_P(PgfLzr, coerce_idx, PgfCoerceIdx));
+
+
+
+
+static void
+pgf_lzr_add_infer_entry(PgfLzr* lzr,
+ PgfInferMap* infer_table,
+ PgfCCat* cat,
+ PgfProductionApply* papply)
+{
+ PgfPArgs args = papply->args;
+ size_t n_args = gu_seq_length(args);
+ PgfCCatIds* arg_cats = gu_new_list(PgfCCatIds, lzr->pool, n_args);
+ for (size_t i = 0; i < n_args; i++) {
+ // XXX: What about the hypos in the args?
+ gu_list_index(arg_cats, i) = gu_seq_get(args, PgfPArg, i).ccat;
+ }
+ gu_debug("%d,%d,%d -> %d, %s",
+ n_args > 0 ? gu_list_index(arg_cats, 0)->fid : -1,
+ n_args > 1 ? gu_list_index(arg_cats, 1)->fid : -1,
+ n_args > 2 ? gu_list_index(arg_cats, 2)->fid : -1,
+ cat->fid, papply->fun->fun);
+ PgfLinInfers* entries =
+ gu_map_get(infer_table, &arg_cats, PgfLinInfers*);
+ if (!entries) {
+ entries = gu_new_buf(PgfLinInferEntry, lzr->pool);
+ gu_map_put(infer_table, &arg_cats, PgfLinInfers*, entries);
+ } else {
+ // XXX: arg_cats is duplicate, we ought to free it
+ // Display warning?
+ }
+
+ PgfLinInferEntry entry = {
+ .cat = cat,
+ .fun = papply->fun
+ };
+ gu_buf_push(entries, PgfLinInferEntry, entry);
+}
+
+
+static void
+pgf_lzr_index(PgfLzr* lzr, PgfCCat* cat, PgfProduction prod)
+{
+ void* data = gu_variant_data(prod);
+ switch (gu_variant_tag(prod)) {
+ case PGF_PRODUCTION_APPLY: {
+ PgfProductionApply* papply = data;
+ PgfInferMap* infer =
+ gu_map_get(lzr->fun_indices, &papply->fun->fun,
+ PgfInferMap*);
+ gu_debug("index: %s -> %d", papply->fun->fun, cat->fid);
+ if (!infer) {
+ infer = gu_map_type_new(PgfInferMap, lzr->pool);
+ gu_map_put(lzr->fun_indices,
+ &papply->fun->fun, PgfInferMap*, infer);
+ }
+ pgf_lzr_add_infer_entry(lzr, infer, cat, papply);
+ break;
+ }
+ case PGF_PRODUCTION_COERCE: {
+ PgfProductionCoerce* pcoerce = data;
+ PgfCCatBuf* cats = gu_map_get(lzr->coerce_idx, pcoerce->coerce,
+ PgfCCatBuf*);
+ if (!cats) {
+ cats = gu_new_buf(PgfCCat*, lzr->pool);
+ gu_map_put(lzr->coerce_idx,
+ pcoerce->coerce, PgfCCatBuf*, cats);
+ }
+ gu_debug("coerce_idx: %d -> %d", pcoerce->coerce->fid, cat->fid);
+ gu_buf_push(cats, PgfCCat*, cat);
+ break;
+ }
+ default:
+ // Display warning?
+ break;
+ }
+}
+
+static void
+pgf_lzr_index_ccat(PgfLzr* lzr, PgfCCat* cat)
+{
+ gu_debug("ccat: %d", cat->fid);
+ if (gu_seq_is_null(cat->prods)) {
+ return;
+ }
+ size_t n_prods = gu_seq_length(cat->prods);
+ for (size_t i = 0; i < n_prods; i++) {
+ PgfProduction prod = gu_seq_get(cat->prods, PgfProduction, i);
+ pgf_lzr_index(lzr, cat, prod);
+ }
+}
+
+typedef struct {
+ GuMapItor fn;
+ PgfLzr* lzr;
+} PgfLzrIndexFn;
+
+static void
+pgf_lzr_index_cnccat_cb(GuMapItor* fn, const void* key, void* value,
+ GuExn* err)
+{
+ (void) (key && err);
+ PgfLzrIndexFn* clo = (PgfLzrIndexFn*) fn;
+ PgfCncCat** cnccatp = value;
+ PgfCncCat* cnccat = *cnccatp;
+ gu_enter("-> cnccat: %s", cnccat->cid);
+ int n_ccats = gu_list_length(cnccat->cats);
+ for (int i = 0; i < n_ccats; i++) {
+ PgfCCat* cat = gu_list_index(cnccat->cats, i);
+ if (cat) {
+ pgf_lzr_index_ccat(clo->lzr, cat);
+ }
+ }
+ gu_exit("<-");
+}
+
+
+PgfLzr*
+pgf_new_lzr(PgfConcr* cnc, GuPool* pool)
+{
+ PgfLzr* lzr = gu_new(PgfLzr, pool);
+ lzr->cnc = cnc;
+ lzr->pool = pool;
+ lzr->fun_indices = gu_map_type_new(PgfFunIndices, pool);
+ lzr->coerce_idx = gu_map_type_new(PgfCoerceIdx, pool);
+ PgfLzrIndexFn clo = { { pgf_lzr_index_cnccat_cb }, lzr };
+ gu_map_iter(cnc->cnccats, &clo.fn, NULL);
+ size_t n_extras = gu_seq_length(cnc->extra_ccats);
+ for (size_t i = 0; i < n_extras; i++) {
+ PgfCCat* cat = gu_seq_get(cnc->extra_ccats, PgfCCat*, i);
+ pgf_lzr_index_ccat(lzr, cat);
+ }
+ // TODO: prune productions with zero linearizations
+ return lzr;
+}
+
+typedef struct PgfLzn PgfLzn;
+
+struct PgfLzn {
+ PgfLzr* lzr;
+ GuChoice* ch;
+ PgfExpr expr;
+ GuEnum en;
+};
+
+
+//
+// PgfCncTree
+//
+
+typedef enum {
+ PGF_CNC_TREE_APP,
+ PGF_CNC_TREE_LIT,
+} PgfCncTreeTag;
+
+typedef struct PgfCncTreeApp PgfCncTreeApp;
+struct PgfCncTreeApp {
+ PgfCncFun* fun;
+ GuLength n_args;
+ PgfCncTree args[];
+};
+
+typedef struct PgfCncTreeLit PgfCncTreeLit;
+struct PgfCncTreeLit {
+ PgfLiteral lit;
+};
+
+
+static PgfCCat*
+pgf_lzn_pick_supercat(PgfLzn* lzn, PgfCCat* cat)
+{
+ gu_enter("->");
+ while (true) {
+ PgfCCatBuf* supers =
+ gu_map_get(lzn->lzr->coerce_idx, cat, PgfCCatBuf*);
+ if (!supers) {
+ break;
+ }
+ gu_debug("n_supers: %d", gu_buf_length(supers));
+ int ch = gu_choice_next(lzn->ch, gu_buf_length(supers) + 1);
+ gu_debug("choice: %d", ch);
+ if (ch == 0) {
+ break;
+ }
+ cat = gu_buf_get(supers, PgfCCat*, ch - 1);
+ }
+ gu_exit("<- %d", cat->fid);
+ return cat;
+}
+
+static PgfCCat*
+pgf_lzn_infer(PgfLzn* lzn, PgfExpr expr, GuPool* pool, PgfCncTree* ctree_out);
+
+static PgfCCat*
+pgf_lzn_infer_apply_try(PgfLzn* lzn, PgfApplication* appl,
+ PgfInferMap* infer, GuChoiceMark* marks,
+ PgfCCatIds* arg_cats, int* ip, int n_args,
+ GuPool* pool, PgfCncTreeApp* app_out)
+{
+ gu_enter("f: %s, *ip: %d, n_args: %d", appl->fun, *ip, n_args);
+ PgfCCat* ret = NULL;
+ while (*ip < n_args) {
+ PgfCncTree* arg_treep =
+ (app_out == NULL ? NULL : &app_out->args[*ip]);
+ PgfCCat* arg_i =
+ pgf_lzn_infer(lzn, appl->args[*ip], pool, arg_treep);
+ if (arg_i == NULL) {
+ goto finish;
+ }
+ arg_i = pgf_lzn_pick_supercat(lzn, arg_i);
+ gu_list_index(arg_cats, *ip) = arg_i;
+ marks[++*ip] = gu_choice_mark(lzn->ch);
+ }
+ PgfLinInfers* entries = gu_map_get(infer, &arg_cats, PgfLinInfers*);
+ if (!entries) {
+ goto finish;
+ }
+ size_t n_entries = gu_buf_length(entries);
+ int e = gu_choice_next(lzn->ch, n_entries);
+ gu_debug("entry %d of %d", e, n_entries);
+ if (e < 0) {
+ goto finish;
+ }
+ PgfLinInferEntry* entry = gu_buf_index(entries, PgfLinInferEntry, e);
+ if (app_out != NULL) {
+ app_out->fun = entry->fun;
+ }
+ ret = entry->cat;
+finish:
+ gu_exit("fid: %d", ret ? ret->fid : -1);
+ return ret;
+}
+typedef GuList(GuChoiceMark) PgfChoiceMarks;
+
+static PgfCCat*
+pgf_lzn_infer_application(PgfLzn* lzn, PgfApplication* appl,
+ GuPool* pool, PgfCncTree* ctree_out)
+{
+ PgfInferMap* infer =
+ gu_map_get(lzn->lzr->fun_indices, &appl->fun, PgfInferMap*);
+ gu_enter("-> f: %s, n_args: %d", appl->fun, appl->n_args);
+ if (infer == NULL) {
+ gu_exit("<- couldn't find f");
+ return NULL;
+ }
+ GuPool* tmp_pool = gu_new_pool();
+ PgfCCat* ret = NULL;
+ int n = appl->n_args;
+ PgfCCatIds* arg_cats = gu_new_list(PgfCCatIds, tmp_pool, n);
+
+ PgfCncTreeApp* appt = NULL;
+ if (ctree_out) {
+ appt = gu_new_flex_variant(PGF_CNC_TREE_APP, PgfCncTreeApp,
+ args, n, ctree_out, pool);
+ appt->n_args = n;
+ }
+
+ PgfChoiceMarks* marksl = gu_new_list(PgfChoiceMarks, tmp_pool, n + 1);
+ GuChoiceMark* marks = gu_list_elems(marksl);
+ int i = 0;
+ marks[i] = gu_choice_mark(lzn->ch);
+ while (true) {
+ ret = pgf_lzn_infer_apply_try(lzn, appl, infer,
+ marks, arg_cats,
+ &i, n, pool, appt);
+ if (ret != NULL) {
+ break;
+ }
+
+ do {
+ --i;
+ if (i < 0) {
+ goto finish;
+ }
+ gu_choice_reset(lzn->ch, marks[i]);
+ } while (!gu_choice_advance(lzn->ch));
+ }
+finish:
+ gu_pool_free(tmp_pool);
+ gu_exit("<- fid: %d", ret ? ret->fid : -1);
+ return ret;
+}
+
+static PgfCCat*
+pgf_lzn_infer(PgfLzn* lzn, PgfExpr expr, GuPool* pool, PgfCncTree* ctree_out)
+{
+ PgfCCat* ret = NULL;
+ GuPool* tmp_pool = gu_new_pool();
+ PgfApplication* appl = pgf_expr_unapply(expr, tmp_pool);
+ if (appl != NULL) {
+ ret = pgf_lzn_infer_application(lzn, appl, pool, ctree_out);
+ } else {
+ GuVariantInfo i = gu_variant_open(pgf_expr_unwrap(expr));
+ switch (i.tag) {
+ case PGF_EXPR_LIT: {
+ PgfExprLit* elit = i.data;
+ if (pool != NULL) {
+ *ctree_out = gu_new_variant_i(
+ pool, PGF_CNC_TREE_LIT,
+ PgfCncTreeLit,
+ .lit = elit->lit);
+ }
+ ret = pgf_literal_cat(elit->lit);
+ }
+ default:
+ // XXX: should we do something here?
+ break;
+ }
+ }
+ gu_pool_free(tmp_pool);
+ return ret;
+}
+
+
+static PgfCncTree
+pgf_lzn_next(PgfLzn* lzn, GuPool* pool)
+{
+ // XXX: rewrite this whole mess
+ PgfCncTree ctree = gu_null_variant;
+ if (gu_variant_is_null(lzn->expr)) {
+ return ctree;
+ }
+ PgfCCat* cat = NULL;
+ GuChoiceMark mark = gu_choice_mark(lzn->ch);
+ do {
+ cat = pgf_lzn_infer(lzn, lzn->expr, NULL, NULL);
+ gu_choice_reset(lzn->ch, mark);
+ } while (!cat && gu_choice_advance(lzn->ch));
+
+ if (cat) {
+ PgfCCat* cat2 = pgf_lzn_infer(lzn, lzn->expr, pool, &ctree);
+ gu_assert(cat == cat2);
+ gu_debug("fid: %d", cat->fid);
+ gu_choice_reset(lzn->ch, mark);
+ if (!gu_choice_advance(lzn->ch)) {
+ lzn->expr = gu_null_variant;
+ }
+ }
+ return ctree;
+}
+
+static void
+pgf_cnc_tree_enum_next(GuEnum* self, void* to, GuPool* pool)
+{
+ PgfLzn* lzn = gu_container(self, PgfLzn, en);
+ PgfCncTree* toc = to;
+ *toc = pgf_lzn_next(lzn, pool);
+}
+
+PgfCncTreeEnum*
+pgf_lzr_concretize(PgfLzr* lzr, PgfExpr expr, GuPool* pool)
+{
+ PgfLzn* lzn = gu_new(PgfLzn, pool);
+ lzn->lzr = lzr;
+ lzn->expr = expr;
+ lzn->ch = gu_new_choice(pool);
+ lzn->en.next = pgf_cnc_tree_enum_next;
+ return &lzn->en;
+}
+
+
+int
+pgf_cnc_tree_dimension(PgfCncTree ctree)
+{
+ GuVariantInfo cti = gu_variant_open(ctree);
+ switch (cti.tag) {
+ case PGF_CNC_TREE_LIT:
+ return 1;
+ case PGF_CNC_TREE_APP: {
+ PgfCncTreeApp* fapp = cti.data;
+ return fapp->fun->n_lins;
+ }
+ default:
+ gu_impossible();
+ return 0;
+ }
+}
+
+void
+pgf_lzr_linearize(PgfLzr* lzr, PgfCncTree ctree, size_t lin_idx, PgfLinFuncs** fnsp)
+{
+ PgfLinFuncs* fns = *fnsp;
+ GuVariantInfo cti = gu_variant_open(ctree);
+
+ switch (cti.tag) {
+ case PGF_CNC_TREE_LIT: {
+ gu_require(lin_idx == 0);
+ PgfCncTreeLit* flit = cti.data;
+ if (fns->expr_literal) {
+ fns->expr_literal(fnsp, flit->lit);
+ }
+ break;
+ }
+ case PGF_CNC_TREE_APP: {
+ PgfCncTreeApp* fapp = cti.data;
+ PgfCncFun* fun = fapp->fun;
+ if (fns->expr_apply) {
+ fns->expr_apply(fnsp, fun->fun, fapp->n_args);
+ }
+ gu_require(lin_idx < fun->n_lins);
+ PgfSequence seq = fun->lins[lin_idx];
+ size_t nsyms = gu_seq_length(seq);
+ PgfSymbol* syms = gu_seq_data(seq);
+ for (size_t i = 0; i < nsyms; i++) {
+ PgfSymbol sym = syms[i];
+ GuVariantInfo sym_i = gu_variant_open(sym);
+ switch (sym_i.tag) {
+ case PGF_SYMBOL_CAT:
+ case PGF_SYMBOL_VAR:
+ case PGF_SYMBOL_LIT: {
+ PgfSymbolIdx* sidx = sym_i.data;
+ gu_assert((unsigned) sidx->d < fapp->n_args);
+ PgfCncTree argf = fapp->args[sidx->d];
+ pgf_lzr_linearize(lzr, argf, sidx->r, fnsp);
+ break;
+ }
+ case PGF_SYMBOL_KS: {
+ PgfSymbolKS* ks = sym_i.data;
+ if (fns->symbol_tokens) {
+ fns->symbol_tokens(fnsp, ks->tokens);
+ }
+ break;
+ }
+ case PGF_SYMBOL_KP: {
+ // TODO: correct prefix-dependencies
+ PgfSymbolKP* kp = sym_i.data;
+ if (fns->symbol_tokens) {
+ fns->symbol_tokens(fnsp,
+ kp->default_form);
+ }
+ break;
+ }
+ default:
+ gu_impossible();
+ }
+ }
+ break;
+ } // case PGF_CNC_TREE_APP
+ default:
+ gu_impossible();
+ }
+}
+
+
+
+typedef struct PgfSimpleLin PgfSimpleLin;
+
+struct PgfSimpleLin {
+ PgfLinFuncs* funcs;
+ GuWriter* wtr;
+ GuExn* err;
+};
+
+static void
+pgf_file_lzn_symbol_tokens(PgfLinFuncs** funcs, PgfTokens toks)
+{
+ PgfSimpleLin* flin = gu_container(funcs, PgfSimpleLin, funcs);
+ if (!gu_ok(flin->err)) {
+ return;
+ }
+ size_t len = gu_seq_length(toks);
+ for (size_t i = 0; i < len; i++) {
+ PgfToken tok = gu_seq_get(toks, PgfToken, i);
+ gu_string_write(tok, flin->wtr, flin->err);
+ gu_putc(' ', flin->wtr, flin->err);
+ }
+}
+
+static PgfLinFuncs pgf_file_lin_funcs = {
+ .symbol_tokens = pgf_file_lzn_symbol_tokens
+};
+
+void
+pgf_lzr_linearize_simple(PgfLzr* lzr, PgfCncTree ctree,
+ size_t lin_idx, GuWriter* wtr, GuExn* err)
+{
+ PgfSimpleLin flin = {
+ .funcs = &pgf_file_lin_funcs,
+ .wtr = wtr,
+ .err = err
+ };
+ pgf_lzr_linearize(lzr, ctree, lin_idx, &flin.funcs);
+}
diff --git a/src/runtime/c/pgf/linearize.h b/src/runtime/c/pgf/linearize.h
new file mode 100644
index 000000000..db36343f2
--- /dev/null
+++ b/src/runtime/c/pgf/linearize.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2010-2011 University of Helsinki.
+ *
+ * This file is part of libpgf.
+ *
+ * Libpgf is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libpgf is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libpgf. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gu/type.h>
+#include <gu/dump.h>
+#include <gu/enum.h>
+#include <pgf/data.h>
+
+/// Linearization of abstract syntax trees.
+/// @file
+
+/** @name Linearizers
+ *
+ * Linearization begins by choosing a concrete category (#PgfConcr) for some
+ * grammar, and creating a new linearizer (#PgfLzr) which can then be used to
+ * linearize abstract syntax trees (#PgfExpr) of that grammar into the given
+ * concrete category.
+ *
+ * @{
+ */
+
+
+/// A linearizer.
+typedef struct PgfLzr PgfLzr;
+/**<
+ *
+ * A #PgfLzr object transforms abstract syntax trees of a PGF grammar
+ * into sequences of token events for a single concrete category of
+ * that grammar.
+ *
+ */
+GU_DECLARE_TYPE(PgfLzr, struct);
+
+
+/// Create a new linearizer.
+PgfLzr*
+pgf_new_lzr(PgfConcr* cnc, GuPool* pool);
+/**<
+ * @param cnc The concrete category to linearize to.
+ *
+ * @pool
+ *
+ * @return A new linearizer.
+ */
+
+/** @}
+ *
+ * @name Enumerating concrete syntax trees
+ *
+ * Because of the \c variants construct in GF, there may be several
+ * possible concrete syntax trees that correspond to a given abstract
+ * syntax tree. These can be enumerated with #pgf_lzr_concretize and
+ * #pgf_cnc_trees_next.
+ *
+ * @{
+ */
+
+
+/// A concrete syntax tree
+typedef GuVariant PgfCncTree;
+
+/// An enumeration of #PgfCncTree trees.
+typedef GuEnum PgfCncTreeEnum;
+
+/// Begin enumerating concrete syntax variants.
+PgfCncTreeEnum*
+pgf_lzr_concretize(PgfLzr* lzr, PgfExpr expr, GuPool* pool);
+
+/** @}
+ *
+ * @name Linearizing concrete syntax trees
+ *
+ * An individual concrete syntax tree has several different
+ * linearizations, corresponding to the various fields and cases of
+ * corresponding GF values. The number of these linearizations, called
+ * the \e dimension of the tree, can be retrieved with
+ * #pgf_cnc_tree_dimension.
+ *
+ * A single linearization of a concrete syntax tree is performed by
+ * #pgf_lzr_linearize. The linearization is realized as a sequence of
+ * events that are notified by calling the functions of a #PgfLinFuncs
+ * structure that the client provides.
+ *
+ * @{
+ */
+
+/// Callback functions for linearization.
+typedef struct PgfLinFuncs PgfLinFuncs;
+
+struct PgfLinFuncs
+{
+ /// Output tokens
+ void (*symbol_tokens)(PgfLinFuncs** self, PgfTokens toks);
+
+ void (*symbol_expr)(PgfLinFuncs** self,
+ int argno, PgfExpr expr, int lin_idx);
+
+ /// Begin application
+ void (*expr_apply)(PgfLinFuncs** self, PgfCId cid, int n_args);
+
+ /// Output literal
+ void (*expr_literal)(PgfLinFuncs** self, PgfLiteral lit);
+
+ void (*abort)(PgfLinFuncs** self);
+ void (*finish)(PgfLinFuncs** self);
+};
+
+
+
+
+
+/// Linearize a concrete syntax tree.
+void
+pgf_lzr_linearize(PgfLzr* lzr, PgfCncTree ctree, size_t lin_idx,
+ PgfLinFuncs** fnsp);
+
+
+/// Linearize a concrete syntax tree as space-separated tokens.
+void
+pgf_lzr_linearize_simple(PgfLzr* lzr, PgfCncTree ctree,
+ size_t lin_idx, GuWriter* wtr, GuExn* err);
+
+
+/// Return the dimension of a concrete syntax tree.
+int
+pgf_cnc_tree_dimension(PgfCncTree ctree);
+/**<
+ * @param ctree A concrete syntax tree.
+ *
+ * @return The dimension of the tree, i.e. the number of different
+ * linearizations the tree has.
+ */
+
+//@}
+
+
+
+extern GuTypeTable
+pgf_linearize_dump_table;
+
diff --git a/src/runtime/c/pgf/loader.c b/src/runtime/c/pgf/loader.c
deleted file mode 100644
index 0d2f40661..000000000
--- a/src/runtime/c/pgf/loader.c
+++ /dev/null
@@ -1,396 +0,0 @@
-#include "../pgf.h"
-#include "data.h"
-#include "panic.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-static int readTag(FILE *f) {
- return getc(f);
-}
-
-static int readInt16(FILE *f) {
- int x = getc(f);
- int y = getc(f);
- return ((x << 8) | y);
-}
-
-static int readInt(FILE *f) {
- unsigned int x = (unsigned int) getc(f);
- if (x <= 0x7f)
- return (int) x;
- else {
- unsigned int y = (unsigned int) readInt(f);
- return (int) ((y << 7) | (x & 0x7f)) ;
- }
-}
-
-static double readFloat(FILE *f) {
- double d;
- fread(&d, sizeof(d), 1, f);
- return d;
-}
-
-static String readString(FILE *f) {
- int len = readInt(f);
- String str = (String) malloc(sizeof(struct _CId)+len*sizeof(unsigned int));
- str->len = len;
-
- int i;
- for (i = 0; i < len; i++) {
- int c = fgetc(f);
- str->chars[i] = c;
- }
-
- return str;
-}
-
-static CId readCId(FILE *f) {
- int len = readInt(f);
- CId cid = (CId) malloc(sizeof(struct _CId)+len*sizeof(char));
- cid->len = len;
- fread(&cid->chars, sizeof(char), len, f);
- return cid;
-}
-
-static CIdList readCIdList(FILE *f) {
- int i;
- int count = readInt(f);
- CIdList list = (CIdList) malloc(sizeof(struct _CIdList)+count*sizeof(CId));
-
- list->count = count;
- for (i = 0; i < count; i++) {
- list->names[i] = readCId(f);
- }
-
- return list;
-}
-
-static Literal readLiteral(FILE *f) {
- int tag = readTag(f);
- switch (tag) {
- case LIT_STR:
- {
- LiteralStr lit = (LiteralStr) malloc(sizeof(struct _LiteralStr));
- lit->_.tag = tag;
- lit->val = readString(f);
- return ((Literal) lit);
- }
- case LIT_INT:
- {
- LiteralInt lit = (LiteralInt) malloc(sizeof(struct _LiteralInt));
- lit->_.tag = tag;
- lit->val = readInt(f);
- return ((Literal) lit);
- }
- case LIT_FLOAT:
- {
- LiteralFloat lit = (LiteralFloat) malloc(sizeof(struct _LiteralFloat));
- lit->_.tag = tag;
- lit->val = readFloat(f);
- return ((Literal) lit);
- }
- default:
- __pgf_panic("Unknown literal tag");
- }
-}
-
-static Flags readFlags(FILE *f) {
- int i;
- int count = readInt(f);
- Flags flags = (Flags) malloc(sizeof(struct _Flags)+count*sizeof(struct _Flag));
-
- flags->count = count;
- for (i = 0; i < count; i++) {
- flags->values[i].name = readCId(f);
- flags->values[i].value = readLiteral(f);
- }
-
- return flags;
-}
-
-static Context readContext(FILE *f);
-static Type readType(FILE *f);
-
-static Expr readExpr(FILE *f) {
- int tag = readTag(f);
-
- switch (tag) {
- case TAG_ABS:
- {
- ExprAbs e = (ExprAbs) malloc(sizeof(struct _ExprAbs));
- e->_.tag = tag;
- e->bt = readTag(f);
- e->var = readCId(f);
- e->body = readExpr(f);
- return ((Expr) e);
- }
- case TAG_APP:
- {
- ExprApp e = (ExprApp) malloc(sizeof(struct _ExprApp));
- e->_.tag = tag;
- e->left = readExpr(f);
- e->right = readExpr(f);
- return ((Expr) e);
- }
- case TAG_LIT:
- {
- ExprLit e = (ExprLit) malloc(sizeof(struct _ExprLit));
- e->_.tag = tag;
- e->lit = readLiteral(f);
- return ((Expr) e);
- }
- case TAG_MET:
- {
- ExprMeta e = (ExprMeta) malloc(sizeof(struct _ExprMeta));
- e->_.tag = tag;
- e->id = readInt(f);
- return ((Expr) e);
- }
- case TAG_FUN:
- {
- ExprFun e = (ExprFun) malloc(sizeof(struct _ExprFun));
- e->_.tag = tag;
- e->fun = readCId(f);
- return ((Expr) e);
- }
- case TAG_VAR:
- {
- ExprVar e = (ExprVar) malloc(sizeof(struct _ExprVar));
- e->_.tag = tag;
- e->index = readInt(f);
- return ((Expr) e);
- }
- case TAG_TYP:
- {
- ExprTyped e = (ExprTyped) malloc(sizeof(struct _ExprTyped));
- e->_.tag = tag;
- e->e = readExpr(f);
- e->ty = readType(f);
- return ((Expr) e);
- }
- case TAG_IMP:
- {
- ExprImplArg e = (ExprImplArg) malloc(sizeof(struct _ExprImplArg));
- e->_.tag = tag;
- e->e = readExpr(f);
- return ((Expr) e);
- }
- default:
- __pgf_panic("Unknown expression tag");
- }
-}
-
-static Type readType(FILE *f) {
- Context hypos = readContext(f);
- CId cat = readCId(f);
-
- int i;
- int count = readInt(f);
- Type ty = (Type) malloc(sizeof(struct _Type)+count*sizeof(Expr));
-
- ty->hypos = hypos;
- ty->cat = cat;
- ty->nArgs = count;
- for (i = 0; i < count; i++) {
- ty->args[i] = readExpr(f);
- }
-
- return ty;
-}
-
-static void readHypo(FILE *f, Hypo h) {
- h->bt = readTag(f);
- h->var = readCId(f);
- h->ty = readType(f);
-}
-
-static Context readContext(FILE *f) {
- int i;
- int size = readInt(f);
- Context ctxt = (Context) malloc(sizeof(struct _Context)+size*sizeof(struct _Hypo));
-
- ctxt->size = size;
- for (i = 0; i < size; i++) {
- readHypo(f, &ctxt->hypos[i]);
- }
-
- return ctxt;
-}
-
-static Patt readPatt(FILE *f) {
- int tag = readTag(f);
-
- switch (tag) {
- case TAG_PAPP:
- {
- CId fun = readCId(f);
-
- int i;
- int count = readInt(f);
- PattApp p = (PattApp) malloc(sizeof(struct _PattApp)+count*sizeof(Patt));
-
- p->_.tag = tag;
- p->fun = fun;
- p->args.count = count;
- for (i = 0; i < count; i++) {
- p->args.pats[i] = readPatt(f);
- }
-
- return ((Patt) p);
- }
- case TAG_PVAR:
- {
- PattVar p = (PattVar) malloc(sizeof(struct _PattVar));
- p->_.tag = tag;
- p->var = readCId(f);
- return ((Patt) p);
- }
- case TAG_PAT:
- {
- PattAt p = (PattAt) malloc(sizeof(struct _PattAt));
- p->_.tag = tag;
- p->var = readCId(f);
- p->pat = readPatt(f);
- return ((Patt) p);
- }
- case TAG_PWILD:
- {
- PattWild p = (PattWild) malloc(sizeof(struct _PattWild));
- p->_.tag = tag;
- return ((Patt) p);
- }
- case TAG_PLIT:
- {
- PattLit p = (PattLit) malloc(sizeof(struct _PattLit));
- p->_.tag = tag;
- p->lit = readLiteral(f);
- return ((Patt) p);
- }
- case TAG_PIMP:
- {
- PattImplArg p = (PattImplArg) malloc(sizeof(struct _PattImplArg));
- p->_.tag = tag;
- p->pat = readPatt(f);
- return ((Patt) p);
- }
- case TAG_PTILDE:
- {
- PattTilde p = (PattTilde) malloc(sizeof(struct _PattTilde));
- p->_.tag = tag;
- p->e = readExpr(f);
- return ((Patt) p);
- }
- default:
- __pgf_panic("Unknown pattern tag");
- }
-}
-
-static Patts readPatts(FILE *f) {
- int i;
- int count = readInt(f);
- Patts pats = (Patts) malloc(sizeof(struct _Patts)+count*sizeof(Patt));
-
- pats->count = count;
- for (i = 0; i < count; i++) {
- pats->pats[i] = readPatt(f);
- }
-
- return pats;
-}
-
-static Equations readEquations(FILE *f) {
- int i;
- int count = readInt(f);
- Equations equs = (Equations) malloc(sizeof(struct _Equations)+count*sizeof(struct _Equation));
-
- equs->count = count;
- for (i = 0; i < count; i++) {
- equs->equs[i].lhs = readPatts(f);
- equs->equs[i].rhs = readExpr(f);
- }
-
- return equs;
-}
-
-static void readAbsFun(FILE *f, AbsFun fun) {
- fun->name = readCId(f);
- fun->ty = readType(f);
- fun->arrity = readInt(f);
- if (readTag(f) != 0)
- fun->equs = readEquations(f);
- else
- fun->equs = NULL;
-}
-
-static AbsFuns readAbsFuns(FILE *f) {
- int i;
- int count = readInt(f);
- AbsFuns funs = (AbsFuns) malloc(sizeof(struct _AbsFuns)+count*sizeof(struct _AbsFun));
-
- funs->count = count;
- for (i = 0; i < count; i++) {
- readAbsFun(f, &funs->lst[i]);
- }
-
- return funs;
-}
-
-static void readAbsCat(FILE *f, AbsCat cat) {
- cat->name = readCId(f);
- cat->hypos = readContext(f);
- cat->funs = readCIdList(f);
-}
-
-static AbsCats readAbsCats(FILE *f) {
- int i;
- int count = readInt(f);
- AbsCats cats = (AbsCats) malloc(sizeof(struct _AbsCats)+count*sizeof(struct _AbsCat));
-
- cats->count = count;
- for (i = 0; i < count; i++) {
- readAbsCat(f, &cats->lst[i]);
- }
-
- return cats;
-}
-
-static void readAbstr(FILE *f, Abstract abstr) {
- abstr->name = readCId(f);
- abstr->flags = readFlags(f);
- abstr->funs = readAbsFuns(f);
- abstr->cats = readAbsCats(f);
-}
-
-static void readConcr(FILE *f, Concrete concr) {
- concr->name = readCId(f);
- concr->flags = readFlags(f);
-}
-
-PGF readPGF(char *filename) {
- FILE *f = fopen(filename, "rb");
- if (f == NULL)
- return NULL;
-
- int maj_ver = readInt16(f);
- int min_ver = readInt16(f);
-
- Flags flags = readFlags(f);
-
- struct _Abstract abstr;
- readAbstr(f, &abstr);
-
- int nConcr = readInt(f);
- PGF pgf = (PGF) malloc(sizeof(struct _PGF)+sizeof(Concrete)*nConcr);
-
- pgf->flags = flags;
- pgf->abstract = abstr;
- pgf->nConcr = nConcr;
-
- int i;
-// for (i = 0; i < nConcr; i++) {
-// readConcr(f, &pgf->concretes[i]);
-// }
-
- fclose(f);
- return pgf;
-}
diff --git a/src/runtime/c/pgf/panic.c b/src/runtime/c/pgf/panic.c
deleted file mode 100644
index 2a8553a83..000000000
--- a/src/runtime/c/pgf/panic.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "panic.h"
-#include <stdio.h>
-
-void __pgf_panic(char *msg) {
- printf("%s\n",msg);
- fflush(stdout);
- exit(1);
-}
diff --git a/src/runtime/c/pgf/panic.h b/src/runtime/c/pgf/panic.h
deleted file mode 100644
index 00ace7533..000000000
--- a/src/runtime/c/pgf/panic.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef PGF_PANIC_H
-#define PGF_PANIC_H
-
-void __pgf_panic(char *msg);
-
-#endif
diff --git a/src/runtime/c/pgf/parser.c b/src/runtime/c/pgf/parser.c
new file mode 100644
index 000000000..59ee66182
--- /dev/null
+++ b/src/runtime/c/pgf/parser.c
@@ -0,0 +1,697 @@
+#include <pgf/parser.h>
+#include <gu/choice.h>
+#include <gu/seq.h>
+#include <gu/assert.h>
+#include <gu/log.h>
+
+typedef struct PgfItem PgfItem;
+
+enum {
+ PGF_FID_SYNTHETIC = -999
+};
+
+typedef GuBuf PgfItemBuf;
+typedef GuList(PgfItemBuf*) PgfItemBufs;
+
+
+
+// GuString -> PgfItemBuf*
+typedef GuMap PgfTransitions;
+
+typedef GuBuf PgfCCatBuf;
+
+struct PgfParser {
+ PgfConcr* concr;
+};
+
+struct PgfParse {
+ PgfParser* parser;
+ PgfTransitions* transitions;
+ PgfCCatBuf* completed;
+};
+
+typedef struct PgfParseResult PgfParseResult;
+
+struct PgfParseResult {
+ PgfCCatBuf* completed;
+ GuChoice* choice;
+ PgfExprEnum en;
+};
+
+typedef struct PgfItemBase PgfItemBase;
+
+struct PgfItemBase {
+ PgfItemBuf* conts;
+ PgfCCat* ccat;
+ PgfProduction prod;
+ unsigned short lin_idx;
+};
+
+struct PgfItem {
+ PgfItemBase* base;
+ PgfPArgs args;
+ PgfSymbol curr_sym;
+ uint16_t seq_idx;
+ uint8_t tok_idx;
+ uint8_t alt;
+};
+
+typedef GuMap PgfContsMap;
+
+
+static GU_DEFINE_TYPE(PgfItemBuf, abstract, _);
+static GU_DEFINE_TYPE(PgfItemBufs, abstract, _);
+static GU_DEFINE_TYPE(PgfContsMap, GuMap,
+ gu_type(PgfCCat), NULL,
+ gu_ptr_type(PgfItemBufs), &gu_null_struct);
+
+static GU_DEFINE_TYPE(PgfGenCatMap, GuMap,
+ gu_type(PgfItemBuf), NULL,
+ gu_ptr_type(PgfCCat), &gu_null_struct);
+
+static GU_DEFINE_TYPE(PgfTransitions, GuStringMap,
+ gu_ptr_type(PgfItemBuf), &gu_null_struct);
+
+typedef GuMap PgfGenCatMap;
+
+typedef struct PgfParsing PgfParsing;
+
+struct PgfParsing {
+ PgfParse* parse;
+ GuPool* pool;
+ PgfContsMap* conts_map;
+ PgfGenCatMap* generated_cats;
+};
+
+static void
+pgf_parsing_add_transition(PgfParsing* parsing, PgfToken tok, PgfItem* item)
+{
+ gu_debug("%s -> %p", tok, item);
+ PgfTransitions* tmap = parsing->parse->transitions;
+ PgfItemBuf* items = gu_map_get(tmap, &tok, PgfItemBuf*);
+ if (!items) {
+ items = gu_new_buf(PgfItem*, parsing->pool);
+ gu_map_put(tmap, &tok, PgfItemBuf*, items);
+ }
+ gu_buf_push(items, PgfItem*, item);
+}
+
+static PgfItemBufs*
+pgf_parsing_get_contss(PgfParsing* parsing, PgfCCat* cat)
+{
+ PgfItemBufs* contss = gu_map_get(parsing->conts_map, cat, PgfItemBufs*);
+ if (!contss) {
+ size_t n_lins = cat->cnccat->n_lins;
+ contss = gu_new_list(PgfItemBufs, parsing->pool, n_lins);
+ for (size_t i = 0; i < n_lins; i++) {
+ gu_list_index(contss, i) = NULL;
+ }
+ gu_map_put(parsing->conts_map, cat, PgfItemBufs*, contss);
+ }
+ return contss;
+}
+
+
+static PgfItemBuf*
+pgf_parsing_get_conts(PgfParsing* parsing, PgfCCat* cat, size_t lin_idx)
+{
+ gu_require(lin_idx < cat->cnccat->n_lins);
+ PgfItemBufs* contss = pgf_parsing_get_contss(parsing, cat);
+ PgfItemBuf* conts = gu_list_index(contss, lin_idx);
+ if (!conts) {
+ conts = gu_new_buf(PgfItem*, parsing->pool);
+ gu_list_index(contss, lin_idx) = conts;
+ }
+ return conts;
+}
+
+static PgfCCat*
+pgf_parsing_create_completed(PgfParsing* parsing, PgfItemBuf* conts,
+ PgfCncCat* cnccat)
+{
+ PgfCCat* cat = gu_new(PgfCCat, parsing->pool);
+ cat->cnccat = cnccat;
+ cat->fid = PGF_FID_SYNTHETIC;
+ cat->prods = gu_buf_seq(gu_new_buf(PgfProduction, parsing->pool));
+ gu_map_put(parsing->generated_cats, conts, PgfCCat*, cat);
+ return cat;
+}
+
+static PgfCCat*
+pgf_parsing_get_completed(PgfParsing* parsing, PgfItemBuf* conts)
+{
+ return gu_map_get(parsing->generated_cats, conts, PgfCCat*);
+}
+
+static PgfSymbol
+pgf_item_base_symbol(PgfItemBase* ibase, size_t seq_idx, GuPool* pool)
+{
+ GuVariantInfo i = gu_variant_open(ibase->prod);
+ switch (i.tag) {
+ case PGF_PRODUCTION_APPLY: {
+ PgfProductionApply* papp = i.data;
+ PgfCncFun* fun = papp->fun;
+ gu_assert(ibase->lin_idx < fun->n_lins);
+ PgfSequence seq = fun->lins[ibase->lin_idx];
+ gu_assert(seq_idx <= gu_seq_length(seq));
+ if (seq_idx == gu_seq_length(seq)) {
+ return gu_null_variant;
+ } else {
+ return gu_seq_get(seq, PgfSymbol, seq_idx);
+ }
+ break;
+ }
+ case PGF_PRODUCTION_COERCE: {
+ gu_assert(seq_idx <= 1);
+ if (seq_idx == 1) {
+ return gu_null_variant;
+ } else {
+ return gu_new_variant_i(pool, PGF_SYMBOL_CAT,
+ PgfSymbolCat,
+ .d = 0, .r = ibase->lin_idx);
+ }
+ break;
+ }
+ default:
+ gu_impossible();
+ }
+ return gu_null_variant;
+}
+
+static PgfItem*
+pgf_new_item(PgfItemBase* base, GuPool* pool)
+{
+ PgfItem* item = gu_new(PgfItem, pool);
+ GuVariantInfo pi = gu_variant_open(base->prod);
+ switch (pi.tag) {
+ case PGF_PRODUCTION_APPLY: {
+ PgfProductionApply* papp = pi.data;
+ item->args = papp->args;
+ break;
+ }
+ case PGF_PRODUCTION_COERCE: {
+ PgfProductionCoerce* pcoerce = pi.data;
+ item->args = gu_new_seq(PgfPArg, 1, pool);
+ PgfPArg* parg = gu_seq_index(item->args, PgfPArg, 0);
+ parg->hypos = NULL;
+ parg->ccat = pcoerce->coerce;
+ break;
+ }
+ default:
+ gu_impossible();
+ }
+ item->base = base;
+ item->curr_sym = pgf_item_base_symbol(item->base, 0, pool);
+ item->seq_idx = 0;
+ item->tok_idx = 0;
+ item->alt = 0;
+ return item;
+}
+
+static PgfItem*
+pgf_item_copy(PgfItem* item, GuPool* pool)
+{
+ PgfItem* copy = gu_new(PgfItem, pool);
+ memcpy(copy, item, sizeof(PgfItem));
+ return copy;
+}
+
+static void
+pgf_item_advance(PgfItem* item, GuPool* pool)
+{
+ item->seq_idx++;
+ item->curr_sym = pgf_item_base_symbol(item->base, item->seq_idx, pool);
+}
+
+static void
+pgf_parsing_item(PgfParsing* parsing, PgfItem* item);
+
+static void
+pgf_parsing_combine(PgfParsing* parsing, PgfItem* cont, PgfCCat* cat)
+{
+ if (cont == NULL) {
+ gu_buf_push(parsing->parse->completed, PgfCCat*, cat);
+ return;
+ }
+ PgfItem* item = pgf_item_copy(cont, parsing->pool);
+ size_t nargs = gu_seq_length(cont->args);
+ item->args = gu_new_seq(PgfPArg, nargs, parsing->pool);
+ memcpy(gu_seq_data(item->args), gu_seq_data(cont->args),
+ nargs * sizeof(PgfPArg));
+ gu_assert(gu_variant_tag(item->curr_sym) == PGF_SYMBOL_CAT);
+ PgfSymbolCat* pcat = gu_variant_data(cont->curr_sym);
+ gu_seq_set(item->args, PgfPArg, pcat->d,
+ ((PgfPArg) { .hypos = NULL, .ccat = cat }));
+ pgf_item_advance(item, parsing->pool);
+ pgf_parsing_item(parsing, item);
+}
+
+static void
+pgf_parsing_production(PgfParsing* parsing, PgfCCat* cat, size_t lin_idx,
+ PgfProduction prod, PgfItemBuf* conts)
+{
+ PgfItemBase* base = gu_new(PgfItemBase, parsing->pool);
+ base->ccat = cat;
+ base->lin_idx = lin_idx;
+ base->prod = prod;
+ base->conts = conts;
+ PgfItem* item = pgf_new_item(base, parsing->pool);
+ pgf_parsing_item(parsing, item);
+}
+
+static void
+pgf_parsing_complete(PgfParsing* parsing, PgfItem* item)
+{
+ GuVariantInfo i = gu_variant_open(item->base->prod);
+ PgfProduction prod = gu_null_variant;
+ switch (i.tag) {
+ case PGF_PRODUCTION_APPLY: {
+ PgfProductionApply* papp = i.data;
+ PgfProductionApply* new_papp =
+ gu_new_variant(PGF_PRODUCTION_APPLY,
+ PgfProductionApply,
+ &prod, parsing->pool);
+ new_papp->fun = papp->fun;
+ new_papp->args = item->args;
+ break;
+ }
+ case PGF_PRODUCTION_COERCE: {
+ PgfProductionCoerce* new_pcoerce =
+ gu_new_variant(PGF_PRODUCTION_COERCE,
+ PgfProductionCoerce,
+ &prod, parsing->pool);
+ PgfPArg* parg = gu_seq_index(item->args, PgfPArg, 0);
+ gu_assert(!parg->hypos || !parg->hypos->len);
+ new_pcoerce->coerce = parg->ccat;
+ break;
+ }
+ default:
+ gu_impossible();
+ }
+ PgfItemBuf* conts = item->base->conts;
+ PgfCCat* cat = pgf_parsing_get_completed(parsing, conts);
+ if (cat != NULL) {
+ // The category has already been created. If it has also been
+ // predicted already, then process a new item for this production.
+ PgfItemBufs* contss = pgf_parsing_get_contss(parsing, cat);
+ size_t n_contss = gu_list_length(contss);
+ for (size_t i = 0; i < n_contss; i++) {
+ PgfItemBuf* conts2 = gu_list_index(contss, i);
+ /* If there are continuations for
+ * linearization index i, then (cat, i) has
+ * already been predicted. Add the new
+ * production immediately to the agenda,
+ * i.e. process it. */
+ if (conts2) {
+ pgf_parsing_production(parsing, cat, i,
+ prod, conts2);
+ }
+ }
+ } else {
+ cat = pgf_parsing_create_completed(parsing, conts,
+ item->base->ccat->cnccat);
+ size_t n_conts = gu_buf_length(conts);
+ for (size_t i = 0; i < n_conts; i++) {
+ PgfItem* cont = gu_buf_get(conts, PgfItem*, i);
+ pgf_parsing_combine(parsing, cont, cat);
+ }
+ }
+ GuBuf* prodbuf = gu_seq_buf(cat->prods);
+ gu_buf_push(prodbuf, PgfProduction, prod);
+}
+
+
+static void
+pgf_parsing_predict(PgfParsing* parsing, PgfItem* item,
+ PgfCCat* cat, size_t lin_idx)
+{
+ gu_enter("-> cat: %d", cat->fid);
+ if (gu_seq_is_null(cat->prods)) {
+ // Empty category
+ return;
+ }
+ PgfItemBuf* conts = pgf_parsing_get_conts(parsing, cat, lin_idx);
+ gu_buf_push(conts, PgfItem*, item);
+ if (gu_buf_length(conts) == 1) {
+ /* First time we encounter this linearization
+ * of this category at the current position,
+ * so predict it. */
+ PgfProductionSeq prods = cat->prods;
+ size_t n_prods = gu_seq_length(prods);
+ for (size_t i = 0; i < n_prods; i++) {
+ PgfProduction prod =
+ gu_seq_get(prods, PgfProduction, i);
+ pgf_parsing_production(parsing, cat, lin_idx,
+ prod, conts);
+ }
+ } else {
+ /* If it has already been completed, combine. */
+ PgfCCat* completed =
+ pgf_parsing_get_completed(parsing, conts);
+ if (completed) {
+ pgf_parsing_combine(parsing, item, completed);
+ }
+ }
+ gu_exit(NULL);
+}
+
+static void
+pgf_parsing_symbol(PgfParsing* parsing, PgfItem* item, PgfSymbol sym) {
+ switch (gu_variant_tag(sym)) {
+ case PGF_SYMBOL_CAT: {
+ PgfSymbolCat* scat = gu_variant_data(sym);
+ PgfPArg* parg = gu_seq_index(item->args, PgfPArg, scat->d);
+ gu_assert(!parg->hypos || !parg->hypos->len);
+ pgf_parsing_predict(parsing, item, parg->ccat, scat->r);
+ break;
+ }
+ case PGF_SYMBOL_KS: {
+ PgfSymbolKS* sks = gu_variant_data(sym);
+ gu_assert(item->tok_idx < gu_seq_length(sks->tokens));
+ PgfToken tok =
+ gu_seq_get(sks->tokens, PgfToken, item->tok_idx);
+ pgf_parsing_add_transition(parsing, tok, item);
+ break;
+ }
+ case PGF_SYMBOL_KP: {
+ PgfSymbolKP* skp = gu_variant_data(sym);
+ size_t idx = item->tok_idx;
+ uint8_t alt = item->alt;
+ gu_assert(idx < gu_seq_length(skp->default_form));
+ if (idx == 0) {
+ PgfToken tok = gu_seq_get(skp->default_form, PgfToken, 0);
+ pgf_parsing_add_transition(parsing, tok, item);
+ for (size_t i = 0; i < skp->n_forms; i++) {
+ PgfTokens toks = skp->forms[i].form;
+ PgfTokens toks2 = skp->default_form;
+ // XXX: do nubbing properly
+ bool skip = pgf_tokens_equal(toks, toks2);
+ for (size_t j = 0; j < i; j++) {
+ PgfTokens toks2 = skp->forms[j].form;
+ skip |= pgf_tokens_equal(toks, toks2);
+ }
+ if (skip) {
+ continue;
+ }
+ PgfToken tok = gu_seq_get(toks, PgfToken, 0);
+ pgf_parsing_add_transition(parsing, tok, item);
+ }
+ } else if (alt == 0) {
+ PgfToken tok =
+ gu_seq_get(skp->default_form, PgfToken, idx);
+ pgf_parsing_add_transition(parsing, tok, item);
+ } else {
+ gu_assert(alt <= skp->n_forms);
+ PgfToken tok = gu_seq_get(skp->forms[alt - 1].form,
+ PgfToken, idx);
+ pgf_parsing_add_transition(parsing, tok, item);
+ }
+ break;
+ }
+ case PGF_SYMBOL_LIT:
+ // XXX TODO proper support
+ break;
+ case PGF_SYMBOL_VAR:
+ // XXX TODO proper support
+ break;
+ default:
+ gu_impossible();
+ }
+}
+
+static void
+pgf_parsing_item(PgfParsing* parsing, PgfItem* item)
+{
+ GuVariantInfo i = gu_variant_open(item->base->prod);
+ switch (i.tag) {
+ case PGF_PRODUCTION_APPLY: {
+ PgfProductionApply* papp = i.data;
+ PgfCncFun* fun = papp->fun;
+ PgfSequence seq = fun->lins[item->base->lin_idx];
+ if (item->seq_idx == gu_seq_length(seq)) {
+ pgf_parsing_complete(parsing, item);
+ } else {
+ PgfSymbol sym =
+ gu_seq_get(seq, PgfSymbol, item->seq_idx);
+ pgf_parsing_symbol(parsing, item, sym);
+ }
+ break;
+ }
+ case PGF_PRODUCTION_COERCE: {
+ PgfProductionCoerce* pcoerce = i.data;
+ switch (item->seq_idx) {
+ case 0:
+ pgf_parsing_predict(parsing, item,
+ pcoerce->coerce,
+ item->base->lin_idx);
+ break;
+ case 1:
+ pgf_parsing_complete(parsing, item);
+ break;
+ default:
+ gu_impossible();
+ }
+ break;
+ }
+ default:
+ gu_impossible();
+ }
+}
+
+static bool
+pgf_parsing_scan_toks(PgfParsing* parsing, PgfItem* old_item,
+ PgfToken tok, int alt, PgfTokens toks)
+{
+ gu_assert(old_item->tok_idx < gu_seq_length(toks));
+ if (!gu_string_eq(gu_seq_get(toks, PgfToken, old_item->tok_idx),
+ tok)) {
+ return false;
+ }
+ PgfItem* item = pgf_item_copy(old_item, parsing->pool);
+ item->tok_idx++;
+ item->alt = alt;
+ if (item->tok_idx == gu_seq_length(toks)) {
+ item->tok_idx = 0;
+ pgf_item_advance(item, parsing->pool);
+ }
+ pgf_parsing_item(parsing, item);
+ return true;
+}
+
+static void
+pgf_parsing_scan(PgfParsing* parsing, PgfItem* item, PgfToken tok)
+{
+ bool succ = false;
+ GuVariantInfo i = gu_variant_open(item->curr_sym);
+ switch (i.tag) {
+ case PGF_SYMBOL_KS: {
+ PgfSymbolKS* ks = i.data;
+ succ = pgf_parsing_scan_toks(parsing, item, tok, 0,
+ ks->tokens);
+ break;
+ }
+ case PGF_SYMBOL_KP: {
+ PgfSymbolKP* kp = i.data;
+ size_t alt = item->alt;
+ if (item->tok_idx == 0) {
+ succ = pgf_parsing_scan_toks(parsing, item, tok, 0,
+ kp->default_form);
+ for (size_t i = 0; i < kp->n_forms; i++) {
+ // XXX: do nubbing properly
+ PgfTokens toks = kp->forms[i].form;
+ PgfTokens toks2 = kp->default_form;
+ bool skip = pgf_tokens_equal(toks, toks2);
+ for (size_t j = 0; j < i; j++) {
+ PgfTokens toks2 = kp->forms[j].form;
+ skip |= pgf_tokens_equal(toks, toks2);
+ }
+ if (!skip) {
+ succ |= pgf_parsing_scan_toks(
+ parsing, item, tok, i + 1,
+ kp->forms[i].form);
+ }
+ }
+ } else if (alt == 0) {
+ succ = pgf_parsing_scan_toks(parsing, item, tok, 0,
+ kp->default_form);
+ } else {
+ gu_assert(alt <= kp->n_forms);
+ succ = pgf_parsing_scan_toks(parsing, item, tok,
+ alt, kp->forms[alt - 1].form);
+ }
+ break;
+ }
+ default:
+ gu_impossible();
+ }
+ gu_assert(succ);
+}
+
+
+static PgfParsing*
+pgf_new_parsing(PgfParse* parse, GuPool* parse_pool, GuPool* out_pool)
+{
+ PgfParsing* parsing = gu_new(PgfParsing, out_pool);
+ parsing->parse = parse;
+ parsing->generated_cats = gu_map_type_new(PgfGenCatMap, out_pool);
+ parsing->conts_map = gu_map_type_new(PgfContsMap, out_pool);
+ parsing->pool = parse_pool;
+ return parsing;
+}
+
+static PgfParse*
+pgf_new_parse(PgfParser* parser, GuPool* pool)
+{
+ PgfParse* parse = gu_new(PgfParse, pool);
+ parse->parser = parser;
+ parse->transitions = gu_map_type_new(PgfTransitions, pool);
+ parse->completed = gu_new_buf(PgfCCat*, pool);
+ return parse;
+}
+
+PgfParse*
+pgf_parse_token(PgfParse* parse, PgfToken tok, GuPool* pool)
+{
+ PgfItemBuf* agenda =
+ gu_map_get(parse->transitions, &tok, PgfItemBuf*);
+ if (!agenda) {
+ return NULL;
+ }
+ PgfParse* next_parse = pgf_new_parse(parse->parser, pool);
+ GuPool* tmp_pool = gu_new_pool();
+ PgfParsing* parsing = pgf_new_parsing(next_parse, pool, tmp_pool);
+ size_t n_items = gu_buf_length(agenda);
+ for (size_t i = 0; i < n_items; i++) {
+ PgfItem* item = gu_buf_get(agenda, PgfItem*, i);
+ pgf_parsing_scan(parsing, item, tok);
+ }
+ gu_pool_free(tmp_pool);
+ return next_parse;
+}
+
+static PgfExpr
+pgf_cat_to_expr(PgfCCat* cat, GuChoice* choice, GuPool* pool);
+
+static PgfExpr
+pgf_production_to_expr(PgfProduction prod, GuChoice* choice, GuPool* pool)
+{
+ GuVariantInfo pi = gu_variant_open(prod);
+ switch (pi.tag) {
+ case PGF_PRODUCTION_APPLY: {
+ PgfProductionApply* papp = pi.data;
+ PgfExpr expr = gu_new_variant_i(pool, PGF_EXPR_FUN,
+ PgfExprFun,
+ .fun = papp->fun->fun);
+ size_t n_args = gu_seq_length(papp->args);
+ for (size_t i = 0; i < n_args; i++) {
+ PgfPArg* parg = gu_seq_index(papp->args, PgfPArg, i);
+ gu_assert(!parg->hypos || !parg->hypos->len);
+ PgfExpr earg = pgf_cat_to_expr(parg->ccat, choice, pool);
+ expr = gu_new_variant_i(pool, PGF_EXPR_APP,
+ PgfExprApp,
+ .fun = expr, .arg = earg);
+ }
+ return expr;
+ }
+ case PGF_PRODUCTION_COERCE: {
+ PgfProductionCoerce* pcoerce = pi.data;
+ return pgf_cat_to_expr(pcoerce->coerce, choice, pool);
+ }
+ default:
+ gu_impossible();
+ }
+ return gu_null_variant;
+}
+
+
+static PgfExpr
+pgf_cat_to_expr(PgfCCat* cat, GuChoice* choice, GuPool* pool)
+{
+ if (cat->fid != PGF_FID_SYNTHETIC) {
+ // XXX: What should the PgfMetaId be?
+ return gu_new_variant_i(pool, PGF_EXPR_META,
+ PgfExprMeta,
+ .id = 0);
+ }
+ size_t n_prods = gu_seq_length(cat->prods);
+ int i = gu_choice_next(choice, n_prods);
+ if (i == -1) {
+ return gu_null_variant;
+ }
+ PgfProduction prod = gu_seq_get(cat->prods, PgfProduction, i);
+ return pgf_production_to_expr(prod, choice, pool);
+}
+
+
+static PgfExpr
+pgf_parse_result_next(PgfParseResult* pr, GuPool* pool)
+{
+ if (pr->choice == NULL) {
+ return gu_null_variant;
+ }
+ size_t n_results = gu_buf_length(pr->completed);
+ GuChoiceMark mark = gu_choice_mark(pr->choice);
+ int i = gu_choice_next(pr->choice, n_results);
+ if (i == -1) {
+ return gu_null_variant;
+ }
+ PgfCCat* cat = gu_buf_get(pr->completed, PgfCCat*, i);
+ PgfExpr ret = pgf_cat_to_expr(cat, pr->choice, pool);
+ gu_choice_reset(pr->choice, mark);
+ if (!gu_choice_advance(pr->choice)) {
+ pr->choice = NULL;
+ };
+ return ret;
+}
+
+static void
+pgf_parse_result_enum_next(GuEnum* self, void* to, GuPool* pool)
+{
+ PgfParseResult* pr = gu_container(self, PgfParseResult, en);
+ *(PgfExpr*)to = pgf_parse_result_next(pr, pool);
+}
+
+PgfExprEnum*
+pgf_parse_result(PgfParse* parse, GuPool* pool)
+{
+ return &gu_new_i(pool, PgfParseResult,
+ .completed = parse->completed,
+ .choice = gu_new_choice(pool),
+ .en.next = pgf_parse_result_enum_next)->en;
+}
+
+
+
+// TODO: s/CId/Cat, add the cid to Cat, make Cat the key to CncCat
+PgfParse*
+pgf_parser_parse(PgfParser* parser, PgfCId cat, size_t lin_idx, GuPool* pool)
+{
+ PgfParse* parse = pgf_new_parse(parser, pool);
+ GuPool* tmp_pool = gu_new_pool();
+ PgfParsing* parsing = pgf_new_parsing(parse, pool, tmp_pool);
+ PgfCncCat* cnccat =
+ gu_map_get(parser->concr->cnccats, &cat, PgfCncCat*);
+ if (!cnccat) {
+ // error ...
+ gu_impossible();
+ }
+ gu_assert(lin_idx < cnccat->n_lins);
+ size_t n_ccats = gu_list_length(cnccat->cats);
+ for (size_t i = 0; i < n_ccats; i++) {
+ PgfCCat* ccat = gu_list_index(cnccat->cats, i);
+ if (ccat != NULL) {
+ pgf_parsing_predict(parsing, NULL, ccat, lin_idx);
+ }
+ }
+ gu_pool_free(tmp_pool);
+ return parse;
+}
+
+PgfParser*
+pgf_new_parser(PgfConcr* concr, GuPool* pool)
+{
+ gu_require(concr != NULL);
+ PgfParser* parser = gu_new(PgfParser, pool);
+ parser->concr = concr;
+ return parser;
+}
diff --git a/src/runtime/c/pgf/parser.h b/src/runtime/c/pgf/parser.h
new file mode 100644
index 000000000..127bed5dc
--- /dev/null
+++ b/src/runtime/c/pgf/parser.h
@@ -0,0 +1,127 @@
+#ifndef PGF_PARSER_H_
+#define PGF_PARSER_H_
+
+#include <gu/enum.h>
+#include <pgf/data.h>
+#include <pgf/expr.h>
+
+/// Parsing
+/** @file
+ *
+ * @todo Querying the parser for expected continuations
+ *
+ * @todo Literals and custom categories
+ *
+ * @todo HOAS, dependent types...
+ */
+
+typedef struct PgfParse PgfParse;
+
+/** @name Creating a new parser
+ *
+ * A #PgfParser object can parse sentences of a single concrete category into
+ * abstract syntax trees (#PgfExpr). The parser is created with
+ * #pgf_new_parser.
+ *
+ * @{
+ */
+
+/// A parser for a single concrete category
+typedef struct PgfParser PgfParser;
+
+
+/// Create a new parser
+PgfParser*
+pgf_new_parser(PgfConcr* concr, GuPool* pool);
+/**<
+ * @param concr The concrete category whose sentences are to be parsed
+ *
+ * @pool
+ *
+ * @return A newly created parser for the concrete category \p concr
+ */
+
+/** @}
+ *
+ * @name Parsing a sentence
+ *
+ * The progress of parsing is controlled by the client code. Firstly, the
+ * parsing of a sentence is initiated with #pgf_parser_parse. This returns an
+ * initial #PgfParse object, which represents the state of the parsing. A new
+ * parse state is obtained by feeding a token with #pgf_parse_token. The old
+ * parse state is unaffected by this, so backtracking - and even branching -
+ * can be accomplished by retaining the earlier #PgfParse objects.
+ *
+ * @{
+ */
+
+/// Begin parsing
+PgfParse*
+pgf_parser_parse(PgfParser* parser, PgfCId cat, size_t lin_idx, GuPool* pool);
+/**<
+ * @param parser The parser to use
+ *
+ * @param cat The identifier of the abstract category to parse
+ *
+ * @param lin_idx The index of the field of the concrete category to parse
+ *
+ * @pool
+ *
+ * @return An initial parsing state.
+*/
+
+
+/// Feed a token to the parser
+PgfParse*
+pgf_parse_token(PgfParse* parse, PgfToken tok, GuPool* pool);
+/**<
+ * @param parse The current parse state
+ *
+ * @param tok The token to feed
+ *
+ * @pool
+ *
+ * @return A new parse state obtained by feeding \p tok as an input to \p
+ * parse, or \c NULL if the token was unexpected.
+ *
+ * @note The new parse state partially depends on the old one, so it doesn't
+ * make sense to use a \p pool argument with a longer lifetime than that of
+ * the pool used to create \parse.
+ */
+
+
+/** @}
+ * @name Retrieving abstract syntax trees
+ *
+ * After the desired tokens have been fed to the parser, the resulting parse
+ * state can be queried for completed results. The #pgf_parse_result function
+ * returns an enumeration (#GuEnum) of possible abstract syntax trees whose
+ * linearization is the sequence of tokens fed so far.
+ *
+ * @{
+ */
+
+
+/// An enumeration of #PgfExpr elements.
+typedef GuEnum PgfExprEnum;
+
+
+/// Retrieve the current parses from the parse state.
+PgfExprEnum*
+pgf_parse_result(PgfParse* parse, GuPool* pool);
+/**<
+ * @param parse A parse state
+ *
+ * @pool
+ *
+ * @return An enumeration of #PgfExpr elements representing the abstract
+ * syntax trees that would linearize to the sequence of tokens fed to produce
+ * \p parse. The enumeration may yield zero, one or more abstract syntax
+ * trees, depending on whether the parse was unsuccesful, unambiguously
+ * succesful, or ambiguously successful.
+ */
+
+
+/** @} */
+
+#endif // PGF_PARSER_H_
diff --git a/src/runtime/c/pgf/pgf.h b/src/runtime/c/pgf/pgf.h
new file mode 100644
index 000000000..8ee26aadd
--- /dev/null
+++ b/src/runtime/c/pgf/pgf.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libpgf.
+ *
+ * Libpgf is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libpgf is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libpgf. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file
+ *
+ * The public libpgf API.
+ */
+
+#ifndef PGF_H_
+#define PGF_H_
+
+#include <gu/exn.h>
+#include <gu/mem.h>
+#include <gu/in.h>
+#include <gu/string.h>
+
+
+typedef GuString PgfCId;
+extern GU_DECLARE_TYPE(PgfCId, typedef);
+
+
+/// A single lexical token
+typedef GuString PgfToken;
+
+/// @name PGF Grammar objects
+/// @{
+
+typedef struct PgfPGF PgfPGF;
+
+/**< A representation of a PGF grammar.
+ */
+
+
+PgfPGF*
+pgf_read(GuIn* in, GuPool* pool, GuExn* err);
+
+/**< Read a grammar from a PGF file.
+ *
+ * @param from PGF input stream.
+ * The stream must be positioned in the beginning of a binary
+ * PGF representation. After a succesful invocation, the stream is
+ * still open and positioned at the end of the representation.
+ *
+ * @param[out] err_out Raised error.
+ * If non-\c NULL, \c *err_out should be \c NULL. Then, upon
+ * failure, \c *err_out is set to point to a newly allocated
+ * error object, which the caller must free with #g_exn_free
+ * or #g_exn_propagate.
+ *
+ * @return A new PGF object, or \c NULL upon failure. The returned
+ * object must later be freed with #pgf_free.
+ *
+ */
+
+
+#include <gu/type.h>
+GU_DECLARE_TYPE(PgfPGF, struct);
+
+/// @}
+
+
+#endif // PGF_H_
diff --git a/src/runtime/c/pgf/reader.c b/src/runtime/c/pgf/reader.c
new file mode 100644
index 000000000..6aa07e6b7
--- /dev/null
+++ b/src/runtime/c/pgf/reader.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright 2010 University of Helsinki.
+ *
+ * This file is part of libpgf.
+ *
+ * Libpgf is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Libpgf is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libpgf. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "data.h"
+#include "expr.h"
+#include <gu/defs.h>
+#include <gu/map.h>
+#include <gu/seq.h>
+#include <gu/assert.h>
+#include <gu/intern.h>
+#include <gu/in.h>
+#include <gu/bits.h>
+#include <gu/exn.h>
+#include <gu/utf8.h>
+
+#define GU_LOG_ENABLE
+#include <gu/log.h>
+
+typedef struct PgfIdContext PgfIdContext;
+
+//
+// PgfReader
+//
+
+typedef struct PgfReader PgfReader;
+
+struct PgfReader {
+ GuIn* in;
+ GuExn* err;
+ GuPool* opool;
+ GuPool* pool;
+ GuSymTable* symtab;
+ PgfSequences* curr_sequences;
+ PgfCncFuns* curr_cncfuns;
+ GuMap* curr_ccats;
+ GuMap* ccat_locs;
+ GuMap* curr_lindefs;
+ GuMap* curr_coercions;
+ GuTypeMap* read_to_map;
+ GuTypeMap* read_new_map;
+ void* curr_key;
+ GuPool* curr_pool;
+};
+
+typedef struct PgfReadTagExn PgfReadTagExn;
+
+struct PgfReadTagExn {
+ GuType* type;
+ int tag;
+};
+
+static GU_DEFINE_TYPE(PgfReadTagExn, abstract, _);
+
+static GU_DEFINE_TYPE(PgfReadExn, abstract, _);
+
+static uint8_t
+pgf_read_u8(PgfReader* rdr)
+{
+ uint8_t u = gu_in_u8(rdr->in, rdr->err);
+ gu_debug("u8: %u", u);
+ return u;
+}
+
+static uint32_t
+pgf_read_uint(PgfReader* rdr)
+{
+ uint32_t u = 0;
+ int shift = 0;
+ uint8_t b = 0;
+ do {
+ b = pgf_read_u8(rdr);
+ gu_return_on_exn(rdr->err, 0);
+ u |= (b & ~0x80) << shift;
+ shift += 7;
+ } while (b & 0x80);
+ gu_debug("uint: %u", u);
+ return u;
+}
+
+static int32_t
+pgf_read_int(PgfReader* rdr)
+{
+ uint32_t u = pgf_read_uint(rdr);
+ return gu_decode_2c32(u, rdr->err);
+}
+
+static GuLength
+pgf_read_len(PgfReader* rdr)
+{
+ int32_t len = pgf_read_int(rdr);
+ // It's crucial that we return 0 on failure, so the
+ // caller can proceed without checking for error
+ // immediately.
+ gu_return_on_exn(rdr->err, 0);
+ if (len < 0) {
+ gu_raise_i(rdr->err, PgfReadTagExn,
+ .type = gu_type(GuLength), .tag = len);
+ return 0;
+ }
+ return (GuLength) len;
+}
+
+typedef const struct PgfReadToFn PgfReadToFn;
+
+struct PgfReadToFn {
+ void (*fn)(GuType* type, PgfReader* rdr, void* to);
+};
+
+static void
+pgf_read_to(PgfReader* rdr, GuType* type, void* to) {
+ PgfReadToFn* fn = gu_type_map_get(rdr->read_to_map, type);
+ fn->fn(type, rdr, to);
+}
+
+typedef const struct PgfReadNewFn PgfReadNewFn;
+struct PgfReadNewFn {
+ void* (*fn)(GuType* type, PgfReader* rdr, GuPool* pool,
+ size_t* size_out);
+};
+
+static void*
+pgf_read_new(PgfReader* rdr, GuType* type, GuPool* pool, size_t* size_out)
+{
+ size_t size = 0;
+ PgfReadNewFn* fn = gu_type_map_get(rdr->read_new_map, type);
+ return fn->fn(type, rdr, pool, size_out ? size_out : &size);
+}
+
+static void*
+pgf_read_new_type(GuType* type, PgfReader* rdr, GuPool* pool,
+ size_t* size_out)
+{
+ GuTypeRepr* repr = gu_type_repr(type);
+ void* to = gu_malloc_aligned(pool, repr->size, repr->align);
+ pgf_read_to(rdr, type, to);
+ *size_out = repr->size;
+ return to;
+}
+
+static void*
+pgf_read_struct(GuStructRepr* stype, PgfReader* rdr, void* to,
+ GuPool* pool, size_t* size_out)
+{
+ GuTypeRepr* repr = gu_type_cast((GuType*)stype, repr);
+ size_t size = repr->size;
+ GuLength length = 0;
+ bool have_length = false;
+ uint8_t* p = NULL;
+ uint8_t* bto = to;
+ gu_enter("-> struct %s", stype->name);
+
+ for (int i = 0; i < stype->members.len; i++) {
+ const GuMember* m = &stype->members.elems[i];
+ gu_enter("-> %s.%s", stype->name, m->name);
+ if (m->is_flex) {
+ gu_assert(have_length && p == NULL && pool != NULL);
+ size_t m_size = gu_type_size(m->type);
+ size = gu_flex_size(size, m->offset,
+ length, m_size);
+ p = gu_malloc_aligned(pool, size, repr->align);
+ for (size_t j = 0; j < length; j++) {
+ pgf_read_to(rdr, m->type,
+ &p[m->offset + j * m_size]);
+ gu_return_on_exn(rdr->err, NULL);
+ }
+ } else {
+ pgf_read_to(rdr, m->type, &bto[m->offset]);
+ gu_return_on_exn(rdr->err, NULL);
+ }
+ if (m->type == gu_type(GuLength)) {
+ gu_assert(!have_length);
+ have_length = true;
+ length = gu_member(GuLength, to, m->offset);
+ }
+ gu_exit("<- %s.%s", stype->name, m->name);
+ }
+ if (p) {
+ memcpy(p, to, repr->size);
+ }
+ if (size_out) {
+ *size_out = size;
+ }
+ gu_exit("<- struct %s", stype->name);
+ return p;
+}
+
+static void
+pgf_read_to_struct(GuType* type, PgfReader* rdr, void* to)
+{
+ GuStructRepr* stype = gu_type_cast(type, struct);
+ pgf_read_struct(stype, rdr, to, NULL, NULL);
+}
+
+static void*
+pgf_read_new_struct(GuType* type, PgfReader* rdr,
+ GuPool* pool, size_t* size_out)
+{
+ GuStructRepr* stype = gu_type_cast(type, struct);
+ if (gu_struct_has_flex(stype)) {
+ GuPool* tmp_pool = gu_new_pool();
+ void* to = gu_type_malloc(type, tmp_pool);
+ void* p = pgf_read_struct(stype, rdr, to, pool, size_out);
+ gu_pool_free(tmp_pool);
+ gu_assert(p);
+ return p;
+ } else {
+ void* to = gu_type_malloc(type, pool);
+ pgf_read_struct(stype, rdr, to, NULL, NULL);
+ return to;
+ }
+}
+
+
+static void
+pgf_read_to_pointer(GuType* type, PgfReader* rdr, void* to)
+{
+ GuPointerType* ptype = (GuPointerType*) type;
+ GuType* pointed = ptype->pointed_type;
+ gu_require(gu_type_has_kind(pointed, gu_kind(struct)) ||
+ gu_type_has_kind(pointed, gu_kind(abstract)));
+ GuStruct** sto = to;
+ *sto = pgf_read_new(rdr, pointed, rdr->opool, NULL);
+}
+
+static void
+pgf_read_to_GuVariant(GuType* type, PgfReader* rdr, void* to)
+{
+ GuVariantType* vtype = (GuVariantType*) type;
+ GuVariant* vto = to;
+
+ uint8_t btag = pgf_read_u8(rdr);
+ gu_return_on_exn(rdr->err,);
+ if (btag >= vtype->ctors.len) {
+ gu_raise_i(rdr->err, PgfReadTagExn,
+ .type = type, .tag = btag);
+ return;
+ }
+ GuConstructor* ctor = &vtype->ctors.elems[btag];
+ gu_enter("-> variant %s", ctor->c_name);
+ GuPool* tmp_pool = gu_new_pool();
+ GuTypeRepr* repr = gu_type_repr(ctor->type);
+ size_t size = repr->size;
+ void* init = pgf_read_new(rdr, ctor->type, tmp_pool, &size);
+ *vto = gu_make_variant(btag, size, repr->align, init, rdr->opool);
+ gu_pool_free(tmp_pool);
+ gu_exit("<- variant %s", ctor->c_name);
+}
+
+static void
+pgf_read_to_enum(GuType* type, PgfReader* rdr, void* to)
+{
+ // For now, assume that enum values are encoded in a single octet
+ GuEnumType* etype = (GuEnumType*) type;
+ uint8_t tag = pgf_read_u8(rdr);
+ gu_return_on_exn(rdr->err,);
+ if (tag >= etype->constants.len) {
+ gu_raise_i(rdr->err, PgfReadTagExn,
+ .type = type, .tag = tag);
+ return;
+ }
+ GuEnumConstant* econ = &etype->constants.elems[tag];
+ size_t size = gu_type_size(type);
+ if (size == sizeof(int8_t)) {
+ *((int8_t*) to) = econ->value;
+ } else if (size == sizeof(int16_t)) {
+ *((int16_t*) to) = econ->value;
+ } else if (size == sizeof(int32_t)) {
+ *((int32_t*) to) = econ->value;
+ } else if (size == sizeof(int64_t)) {
+ *((int64_t*) to) = econ->value;
+ } else {
+ gu_impossible();
+ }
+}
+
+static void
+pgf_read_to_void(GuType* info, PgfReader* rdr, void* to)
+{
+ (void) (info && rdr && to);
+}
+
+
+static void
+pgf_read_to_int(GuType* type, PgfReader* rdr, void* to)
+{
+ (void) type;
+ *(int*) to = pgf_read_int(rdr);
+}
+
+static void
+pgf_read_to_uint16_t(GuType* type, PgfReader* rdr, void* to)
+{
+ (void) type;
+ *(uint16_t*) to = gu_in_u16be(rdr->in, rdr->err);
+}
+
+static void
+pgf_read_to_GuLength(GuType* type, PgfReader* rdr, void* to)
+{
+ (void) type;
+ *(GuLength*) to = pgf_read_len(rdr);
+}
+
+static void
+pgf_read_to_double(GuType* type, PgfReader* rdr, void* to)
+{
+ (void) type;
+ *(double*) to = gu_in_f64be(rdr->in, rdr->err);
+}
+
+static void
+pgf_read_to_alias(GuType* type, PgfReader* rdr, void* to)
+{
+ GuTypeAlias* atype = gu_type_cast(type, alias);
+ pgf_read_to(rdr, atype->type, to);
+}
+
+static void
+pgf_read_into_map(GuMapType* mtype, PgfReader* rdr, GuMap* map, GuPool* pool)
+{
+ /* The parameter pool is the temporary pool used to store the
+ map. But the actual values need to be more persistent so we
+ store them in rdr->opool. */
+ (void) pool;
+ GuPool* tmp_pool = gu_new_pool();
+ void* key = NULL;
+ GuLength len = pgf_read_len(rdr);
+ gu_return_on_exn(rdr->err, );
+ if (mtype->hasher) {
+ key = gu_type_malloc(mtype->key_type, tmp_pool);
+ }
+ for (size_t i = 0; i < len; i++) {
+ if (mtype->hasher) {
+ pgf_read_to(rdr, mtype->key_type, key);
+ } else {
+ key = pgf_read_new(rdr, mtype->key_type,
+ rdr->opool, NULL);
+ }
+ gu_return_on_exn(rdr->err, );
+ rdr->curr_key = key;
+ /* If an old value already exists, read into
+ it. This allows us to create the value
+ object and point into it before we read the
+ content. */
+ void* valp = gu_map_insert(map, key);
+ pgf_read_to(rdr, mtype->value_type, valp);
+ gu_return_on_exn(rdr->err, );
+ }
+ gu_pool_free(tmp_pool);
+}
+
+static void*
+pgf_read_new_GuMap(GuType* type, PgfReader* rdr, GuPool* pool, size_t* size_out)
+{
+ (void) size_out;
+ GuMapType* mtype = (GuMapType*) type;
+ GuMap* map = gu_map_type_make(mtype, pool);
+ pgf_read_into_map(mtype, rdr, map, pool);
+ gu_return_on_exn(rdr->err, NULL);
+ return map;
+}
+
+static void
+pgf_read_to_GuString(GuType* type, PgfReader* rdr, void* to)
+{
+ (void) (type);
+ gu_enter("-> GuString");
+ GuString* sp = to;
+
+ GuPool* tmp_pool = gu_new_pool();
+ GuStringBuf* sbuf = gu_string_buf(tmp_pool);
+ GuWriter* wtr = gu_string_buf_writer(sbuf);
+
+ GuLength len = pgf_read_len(rdr);
+
+ for (size_t i = 0; i < len; i++) {
+ GuUCS ucs = gu_in_utf8(rdr->in, rdr->err);
+ gu_ucs_write(ucs, wtr, rdr->err);
+ }
+ GuString str = gu_string_buf_freeze(sbuf, tmp_pool);
+ GuSymbol sym = gu_symtable_intern(rdr->symtab, str);
+ gu_pool_free(tmp_pool);
+
+ gu_exit("<- GuString");
+ *sp = sym;
+}
+
+static void
+pgf_read_to_PgfCId(GuType* type, PgfReader* rdr, void* to)
+{
+ (void) (type);
+ gu_enter("-> PgfCId");
+ PgfCId* sp = to;
+
+ GuPool* tmp_pool = gu_new_pool();
+ GuStringBuf* sbuf = gu_string_buf(tmp_pool);
+ GuWriter* wtr = gu_string_buf_writer(sbuf);
+
+ GuLength len = pgf_read_len(rdr);
+
+ for (size_t i = 0; i < len; i++) {
+ // CIds are in latin-1
+ GuUCS ucs = gu_in_u8(rdr->in, rdr->err);
+ gu_ucs_write(ucs, wtr, rdr->err);
+ }
+ GuString str = gu_string_buf_freeze(sbuf, tmp_pool);
+ GuSymbol sym = gu_symtable_intern(rdr->symtab, str);
+ gu_pool_free(tmp_pool);
+
+ gu_exit("<- PgfCId");
+ *sp = sym;
+}
+
+static void
+pgf_read_to_PgfCCatId(GuType* type, PgfReader* rdr, void* to)
+{
+ (void) (type);
+ PgfCCat** pto = to;
+ int fid = pgf_read_int(rdr);
+ gu_return_on_exn(rdr->err,);
+ PgfCCat* ccat = gu_map_get(rdr->curr_ccats, &fid, PgfCCat*);
+ if (ccat) {
+ *pto = ccat;
+ return;
+ }
+ GuBuf* locs = gu_map_get(rdr->ccat_locs, &fid, GuBuf*);
+ if (!locs) {
+ locs = gu_new_buf(PgfCCat**, rdr->pool);
+ gu_map_put(rdr->ccat_locs, &fid, GuBuf*, locs);
+ }
+ gu_buf_push(locs, PgfCCat**, pto);
+}
+
+static void
+pgf_read_to_PgfCCat(GuType* type, PgfReader* rdr, void* to)
+{
+ (void) type;
+ gu_enter("->");
+ PgfCCat* cat = to;
+ cat->cnccat = NULL;
+ pgf_read_to(rdr, gu_type(PgfProductionSeq), &cat->prods);
+ int* fidp = rdr->curr_key;
+ cat->fid = *fidp;
+ GuBuf* locs_buf = gu_map_get(rdr->ccat_locs, fidp, GuBuf*);
+ if (locs_buf) {
+ size_t len = gu_buf_length(locs_buf);
+ PgfCCat*** locs = gu_buf_data(locs_buf);
+ for (size_t n = 0; n < len; n++) {
+ *(locs[n]) = cat;
+ }
+ }
+ gu_exit("<-");
+}
+
+// This is only needed because new_struct would otherwise override.
+// TODO: get rid of new_struct and all the FAM mess
+static void*
+pgf_read_new_PgfCCat(GuType* type, PgfReader* rdr, GuPool* pool,
+ size_t* size_out)
+{
+ PgfCCat* ccat = gu_new(PgfCCat, pool);
+ pgf_read_to_PgfCCat(type, rdr, ccat);
+ *size_out = sizeof(PgfCCat);
+ return ccat;
+}
+
+static void*
+pgf_read_new_GuList(GuType* type, PgfReader* rdr, GuPool* pool, size_t* size_out)
+{
+ GuListType* ltype = gu_type_cast(type, GuList);
+ GuLength length = pgf_read_len(rdr);
+ gu_return_on_exn(rdr->err, NULL);
+ void* list = gu_list_type_alloc(ltype, length, pool);
+ for (size_t i = 0; i < length; i++) {
+ void* elem = gu_list_type_index(ltype, list, i);
+ pgf_read_to(rdr, ltype->elem_type, elem);
+ gu_return_on_exn(rdr->err, NULL);
+ }
+ *size_out = gu_flex_size(ltype->size, ltype->elems_offset,
+ length,
+ gu_type_size(ltype->elem_type));
+ return list;
+}
+
+static void
+pgf_read_to_GuSeq(GuType* type, PgfReader* rdr, void* to)
+{
+ gu_enter("->");
+ GuSeqType* stype = gu_type_cast(type, GuSeq);
+ GuLength length = pgf_read_len(rdr);
+ GuTypeRepr* repr = gu_type_repr(stype->elem_type);
+ gu_return_on_exn(rdr->err, );
+ GuSeq seq = gu_make_seq(repr->size, length, rdr->opool);
+ uint8_t* data = gu_seq_data(seq);
+ for (size_t i = 0; i < length; i++) {
+ void* elem = &data[i * repr->size];
+ pgf_read_to(rdr, stype->elem_type, elem);
+ gu_return_on_exn(rdr->err, );
+ }
+ GuSeq* sto = to;
+ *sto = seq;
+ gu_exit("<-");
+}
+
+static void
+pgf_read_to_maybe_seq(GuType* type, PgfReader* rdr, void* to)
+{
+ GuSeq* sto = to;
+ uint8_t tag = pgf_read_u8(rdr);
+ gu_return_on_exn(rdr->err,);
+ switch (tag) {
+ case 0:
+ *sto = gu_null_seq;
+ break;
+ case 1:
+ pgf_read_to_GuSeq(type, rdr, to);
+ break;
+ default:
+ gu_raise_i(rdr->err, PgfReadTagExn,
+ .type = type, .tag = tag);
+ break;
+ }
+}
+
+
+static void*
+pgf_read_new_idarray(GuType* type, PgfReader* rdr, GuPool* pool,
+ size_t* size_out)
+{
+ (void) type;
+ void* list = pgf_read_new_GuList(type, rdr, rdr->curr_pool, size_out);
+ if (type == gu_type(PgfSequences)) {
+ rdr->curr_sequences = list;
+ } else if (type == gu_type(PgfCncFuns)) {
+ rdr->curr_cncfuns = list;
+ } else {
+ gu_impossible();
+ }
+ return list;
+}
+
+static void
+pgf_read_to_PgfSeqId(GuType* type, PgfReader* rdr, void* to)
+{
+ (void) type;
+ int32_t id = pgf_read_int(rdr);
+ gu_return_on_exn(rdr->err,);
+ if (id < 0 || id >= gu_list_length(rdr->curr_sequences)) {
+ gu_raise(rdr->err, PgfReadExn);
+ return;
+ }
+ *(PgfSeqId*) to = gu_list_elems(rdr->curr_sequences)[id];
+}
+
+
+static void
+pgf_read_to_PgfFunId(GuType* type, PgfReader* rdr, void* to)
+{
+ (void) type;
+ int32_t id = pgf_read_int(rdr);
+ gu_return_on_exn(rdr->err,);
+ if (id < 0 || id >= gu_list_length(rdr->curr_cncfuns)) {
+ gu_raise(rdr->err, PgfReadExn);
+ return;
+ }
+ *(PgfFunId*) to = gu_list_elems(rdr->curr_cncfuns)[id];
+}
+
+static GU_DEFINE_TYPE(PgfLinDefs, GuIntMap, gu_ptr_type(PgfFunIds),
+ &gu_null_struct);
+typedef PgfCCat PgfCCatData;
+static GU_DEFINE_TYPE(PgfCCatData, typedef, gu_type(PgfCCat));
+
+static GU_DEFINE_TYPE(PgfCCatMap, GuIntMap, gu_ptr_type(PgfCCat),
+ &gu_null_struct);
+
+static GU_DEFINE_TYPE(PgfCncCatMap, GuStringMap, gu_ptr_type(PgfCncCat),
+ &gu_null_struct);
+
+typedef struct {
+ GuMapItor fn;
+ GuBuf* seq;
+} PgfCCatCbCtx;
+
+static PgfCncCat*
+pgf_ccat_set_cnccat(PgfCCat* ccat, GuBuf* newly_set)
+{
+ if (!ccat->cnccat) {
+ size_t n_prods = gu_seq_length(ccat->prods);
+ for (size_t i = 0; i < n_prods; i++) {
+ PgfProduction prod =
+ gu_seq_get(ccat->prods, PgfProduction, i);
+ GuVariantInfo i = gu_variant_open(prod);
+ switch (i.tag) {
+ case PGF_PRODUCTION_COERCE: {
+ PgfProductionCoerce* pcoerce = i.data;
+ PgfCncCat* cnccat =
+ pgf_ccat_set_cnccat(pcoerce->coerce,
+ newly_set);
+ if (!ccat->cnccat) {
+ ccat->cnccat = cnccat;
+ } else if (ccat->cnccat != cnccat) {
+ // XXX: real error
+ gu_impossible();
+ }
+ break;
+ }
+ case PGF_PRODUCTION_APPLY:
+ // Shouldn't happen with current PGF.
+ // XXX: real error
+ gu_impossible();
+ break;
+ default:
+ gu_impossible();
+ }
+ }
+ gu_buf_push(newly_set, PgfCCat*, ccat);
+ }
+ return ccat->cnccat;
+}
+
+
+static void
+pgf_read_ccat_cb(GuMapItor* fn, const void* key, void* value, GuExn* err)
+{
+ (void) (key && err);
+ PgfCCatCbCtx* ctx = (PgfCCatCbCtx*) fn;
+ PgfCCat** ccatp = value;
+ pgf_ccat_set_cnccat(*ccatp, ctx->seq);
+}
+
+static void*
+pgf_read_new_PgfConcr(GuType* type, PgfReader* rdr, GuPool* pool,
+ size_t* size_out)
+{
+ (void) (type && size_out);
+ /* We allocate indices from a temporary pool. The actual data
+ * is allocated from rdr->opool. Once everything is resolved
+ * and indices aren't needed, the temporary pool can be
+ * freed. */
+ GuPool* tmp_pool = gu_new_pool();
+ rdr->curr_pool = tmp_pool;
+ PgfConcr* concr = gu_new(PgfConcr, pool);;
+ concr->cflags =
+ pgf_read_new(rdr, gu_type(PgfFlags), pool, NULL);
+ concr->printnames =
+ pgf_read_new(rdr, gu_type(PgfPrintNames), pool, NULL);
+ rdr->curr_sequences =
+ pgf_read_new(rdr, gu_type(PgfSequences), pool, NULL);
+ rdr->curr_cncfuns =
+ pgf_read_new(rdr, gu_type(PgfCncFuns), pool, NULL);
+ GuMapType* lindefs_t = gu_type_cast(gu_type(PgfLinDefs), GuMap);
+ rdr->curr_lindefs = gu_map_type_make(lindefs_t, tmp_pool);
+ pgf_read_into_map(lindefs_t, rdr, rdr->curr_lindefs, rdr->opool);
+ GuMapType* ccats_t = gu_type_cast(gu_type(PgfCCatMap), GuMap);
+ rdr->curr_ccats =
+ gu_new_int_map(PgfCCat*, &gu_null_struct, tmp_pool);
+ rdr->ccat_locs =
+ gu_new_int_map(GuBuf*, &gu_null_struct, tmp_pool);
+ pgf_read_into_map(ccats_t, rdr, rdr->curr_ccats, rdr->opool);
+ concr->cnccats = pgf_read_new(rdr, gu_type(PgfCncCatMap),
+ rdr->opool, NULL);
+
+ GuBuf* extra_ccats = gu_new_buf(PgfCCat*, tmp_pool);
+ PgfCCatCbCtx ctx = { { pgf_read_ccat_cb }, extra_ccats };
+ gu_map_iter(rdr->curr_ccats, &ctx.fn, NULL);
+ concr->extra_ccats = gu_buf_freeze(extra_ccats, rdr->opool);
+ (void) pgf_read_int(rdr); // totalcats
+ gu_pool_free(tmp_pool);
+ return concr;
+}
+
+static bool
+pgf_ccat_n_lins(PgfCCat* cat, int* n_lins) {
+ if (gu_seq_is_null(cat->prods)) {
+ return true;
+ }
+ size_t n_prods = gu_seq_length(cat->prods);
+ for (size_t j = 0; j < n_prods; j++) {
+ PgfProduction prod =
+ gu_seq_get(cat->prods, PgfProduction, j);
+ GuVariantInfo i = gu_variant_open(prod);
+ switch (i.tag) {
+ case PGF_PRODUCTION_APPLY: {
+ PgfProductionApply* papp = i.data;
+ if (*n_lins == -1) {
+ *n_lins = (int) papp->fun->n_lins;
+ } else if (*n_lins != (int) papp->fun->n_lins) {
+ // Inconsistent n_lins for different productions!
+ return false;
+ }
+ break;
+ }
+ case PGF_PRODUCTION_COERCE: {
+ PgfProductionCoerce* pcoerce = i.data;
+ bool succ = pgf_ccat_n_lins(pcoerce->coerce, n_lins);
+ if (!succ) {
+ return false;
+ }
+ break;
+ }
+ default:
+ gu_impossible();
+ }
+ }
+ return true;
+}
+
+static void*
+pgf_read_new_PgfCncCat(GuType* type, PgfReader* rdr, GuPool* pool,
+ size_t* size_out)
+{
+ PgfCId cid = *(PgfCId*) rdr->curr_key;
+ gu_enter("-> cid");
+ (void) (type && size_out);
+ PgfCncCat* cnccat = gu_new(PgfCncCat, pool);
+ cnccat->cid = cid;
+ int first = pgf_read_int(rdr);
+ int last = pgf_read_int(rdr);
+ int len = last + 1 - first;
+ PgfCCatIds* cats = gu_new_list(PgfCCatIds, pool, len);
+ int n_lins = -1;
+ for (int i = 0; i < len; i++) {
+ int n = first + i;
+ PgfCCat* ccat = gu_map_get(rdr->curr_ccats, &n, PgfCCat*);
+ /* ccat can be NULL if the PGF is optimized and the
+ * category has been erased as useless */
+ gu_list_index(cats, i) = ccat;
+ if (ccat != NULL) {
+ // TODO: error if overlap
+ ccat->cnccat = cnccat;
+ if (!pgf_ccat_n_lins(ccat, &n_lins)) {
+ gu_raise(rdr->err, PgfReadExn);
+ goto fail;
+ }
+
+ }
+ gu_debug("range[%d] = %d", i, ccat ? ccat->fid : -1);
+ }
+ cnccat->n_lins = n_lins == -1 ? 0 : (size_t) n_lins;
+ cnccat->cats = cats;
+ cnccat->lindefs = gu_map_get(rdr->curr_lindefs, &first, PgfFunIds*);
+ cnccat->labels = pgf_read_new(rdr, gu_type(GuStringL),
+ pool, NULL);
+ gu_exit("<-");
+ return cnccat;
+fail:
+ gu_exit("<- fail");
+ return NULL;
+}
+
+#define PGF_READ_TO_FN(k_, fn_) \
+ { gu_kind(k_), (void*) &(PgfReadToFn){ fn_ } }
+
+#define PGF_READ_TO(k_) \
+ PGF_READ_TO_FN(k_, pgf_read_to_##k_)
+
+
+static GuTypeTable
+pgf_read_to_table = GU_TYPETABLE(
+ GU_SLIST_0,
+ PGF_READ_TO(struct),
+ PGF_READ_TO(GuVariant),
+ PGF_READ_TO(enum),
+ PGF_READ_TO(void),
+ PGF_READ_TO(int),
+ PGF_READ_TO(uint16_t),
+ PGF_READ_TO(GuLength),
+ PGF_READ_TO(PgfCId),
+ PGF_READ_TO(GuString),
+ PGF_READ_TO(double),
+ PGF_READ_TO(pointer),
+ PGF_READ_TO_FN(PgfEquationsM, pgf_read_to_maybe_seq),
+ PGF_READ_TO(GuSeq),
+ PGF_READ_TO(PgfCCatId),
+ PGF_READ_TO(PgfCCat),
+ PGF_READ_TO(PgfSeqId),
+ PGF_READ_TO(PgfFunId),
+ PGF_READ_TO(alias));
+
+#define PGF_READ_NEW_FN(k_, fn_) \
+ { gu_kind(k_), (void*) &(PgfReadNewFn){ fn_ } }
+
+#define PGF_READ_NEW(k_) \
+ PGF_READ_NEW_FN(k_, pgf_read_new_##k_)
+
+static GuTypeTable
+pgf_read_new_table = GU_TYPETABLE(
+ GU_SLIST_0,
+ PGF_READ_NEW(type),
+ PGF_READ_NEW(struct),
+ PGF_READ_NEW(GuMap),
+ PGF_READ_NEW(GuList),
+ PGF_READ_NEW(PgfCCat),
+ PGF_READ_NEW(PgfCncCat),
+ PGF_READ_NEW(PgfConcr),
+ PGF_READ_NEW_FN(PgfSequences, pgf_read_new_idarray),
+ PGF_READ_NEW_FN(PgfCncFuns, pgf_read_new_idarray)
+ );
+
+static PgfReader*
+pgf_new_reader(GuIn* in, GuPool* opool, GuPool* pool, GuExn* err)
+{
+ PgfReader* rdr = gu_new(PgfReader, pool);
+ rdr->opool = opool;
+ rdr->symtab = gu_new_symtable(opool, pool);
+ rdr->err = err;
+ rdr->in = in;
+ rdr->curr_sequences = NULL;
+ rdr->curr_cncfuns = NULL;
+ rdr->read_to_map = gu_new_type_map(&pgf_read_to_table, pool);
+ rdr->read_new_map = gu_new_type_map(&pgf_read_new_table, pool);
+ rdr->pool = pool;
+ return rdr;
+}
+
+
+PgfPGF*
+pgf_read(GuIn* in, GuPool* pool, GuExn* err)
+{
+ GuPool* tmp_pool = gu_new_pool();
+ PgfReader* rdr = pgf_new_reader(in, pool, tmp_pool, err);
+ PgfPGF* pgf = pgf_read_new(rdr, gu_type(PgfPGF), pool, NULL);
+ gu_pool_free(tmp_pool);
+ gu_return_on_exn(err, NULL);
+ return pgf;
+}
diff --git a/src/runtime/c/pgf/type.h b/src/runtime/c/pgf/type.h
deleted file mode 100644
index 7de9dea20..000000000
--- a/src/runtime/c/pgf/type.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef PGF_TYPE_H
-#define PGF_TYPE_H
-
-typedef struct _Hypo {
- BindType bt;
- CId var;
- Type ty;
-} *Hypo;
-
-typedef struct _Context {
- int size;
- struct _Hypo hypos[];
-} *Context;
-
-struct _Type {
- Context hypos;
- CId cat;
- int nArgs;
- Expr args[];
-};
-
-#endif
diff --git a/src/runtime/c/pgf/unloader.c b/src/runtime/c/pgf/unloader.c
deleted file mode 100644
index 6a1b0d41d..000000000
--- a/src/runtime/c/pgf/unloader.c
+++ /dev/null
@@ -1,248 +0,0 @@
-#include "../pgf.h"
-#include "data.h"
-#include "panic.h"
-#include <stdlib.h>
-
-static void freeCId(CId id) {
- free(id);
-}
-
-static void freeCIdList(CIdList ids) {
- int i;
- for (i = 0; i < ids->count; i++) {
- freeCId(ids->names[i]);
- }
- free(ids);
-}
-
-static void freeString(String str) {
- free(str);
-}
-
-static void freeLiteral(Literal lit) {
- switch (lit->tag) {
- case LIT_STR:
- freeString (((LiteralStr) lit)->val);
- break;
- }
- free(lit);
-}
-
-static void freeFlags(Flags flags) {
- int i;
- for (i = 0; i < flags->count; i++) {
- freeCId(flags->values[i].name);
- freeLiteral(flags->values[i].value);
- }
- free(flags);
-}
-
-static void freeContext(Context ctxt);
-static void freeType(Type ty);
-
-static void freeExpr(Expr e0) {
-
- switch (e0->tag) {
- case TAG_ABS:
- {
- ExprAbs e = (ExprAbs) e0;
- freeCId(e->var);
- freeExpr(e->body);
- }
- break;
- case TAG_APP:
- {
- ExprApp e = (ExprApp) e0;
- freeExpr(e->left);
- freeExpr(e->right);
- }
- break;
- case TAG_LIT:
- {
- ExprLit e = (ExprLit) e0;
- freeLiteral(e->lit);
- }
- break;
- case TAG_MET:
- {
- ExprMeta e = (ExprMeta) e0;
- }
- break;
- case TAG_FUN:
- {
- ExprFun e = (ExprFun) e0;
- freeCId(e->fun);
- }
- break;
- case TAG_VAR:
- {
- ExprVar e = (ExprVar) e0;
- }
- break;
- case TAG_TYP:
- {
- ExprTyped e = (ExprTyped) e0;
- freeExpr(e->e);
- freeType(e->ty);
- }
- break;
- case TAG_IMP:
- {
- ExprImplArg e = (ExprImplArg) e0;
- freeExpr(e->e);
- }
- break;
- default:
- __pgf_panic("Unknown expression tag");
- }
-
- free(e0);
-}
-
-static void freeType(Type ty) {
- freeContext(ty->hypos);
- freeCId(ty->cat);
-
- int i;
- for (i = 0; i < ty->nArgs; i++) {
- freeExpr(ty->args[i]);
- }
-
- free(ty);
-}
-
-static void freeHypo(Hypo hypo) {
- freeCId(hypo->var);
- freeType(hypo->ty);
-}
-
-static void freeContext(Context ctxt) {
- int i;
- for (i = 0; i < ctxt->size; i++) {
- freeHypo(&ctxt->hypos[i]);
- }
- free(ctxt);
-}
-
-static void freePatt(Patt p0) {
- switch (p0->tag) {
- case TAG_PAPP:
- {
- int i;
- PattApp p = (PattApp) p0;
-
- freeCId(p->fun);
- for (i = 0; i < p->args.count; i++) {
- freePatt(p->args.pats[i]);
- }
- }
- break;
- case TAG_PVAR:
- {
- PattVar p = (PattVar) p0;
- freeCId(p->var);
- }
- break;
- case TAG_PAT:
- {
- PattAt p = (PattAt) p0;
- freeCId(p->var);
- freePatt(p->pat);
- }
- break;
- case TAG_PWILD:
- {
- PattWild p = (PattWild) p0;
- }
- break;
- case TAG_PLIT:
- {
- PattLit p = (PattLit) p0;
- freeLiteral(p->lit);
- }
- break;
- case TAG_PIMP:
- {
- PattImplArg p = (PattImplArg) p0;
- freePatt(p->pat);
- }
- break;
- case TAG_PTILDE:
- {
- PattTilde p = (PattTilde) p0;
- freeExpr(p->e);
- }
- break;
- default:
- __pgf_panic("Unknown pattern tag");
- }
-
- free(p0);
-}
-
-static void freePatts(Patts pats) {
- int i;
- for (i = 0; i < pats->count; i++) {
- freePatt(pats->pats[i]);
- }
- free(pats);
-}
-
-static void freeEquations(Equations equs) {
- int i;
- for (i = 0; i < equs->count; i++) {
- freePatts(equs->equs[i].lhs);
- freeExpr(equs->equs[i].rhs);
- }
- free(equs);
-}
-
-static void freeAbsFun(AbsFun fun) {
- freeCId(fun->name);
- freeType(fun->ty);
- freeEquations(fun->equs);
-}
-
-static void freeAbsFuns(AbsFuns funs) {
- int i;
- for (i = 0; i < funs->count; i++) {
- freeAbsFun(&funs->lst[i]);
- }
- free(funs);
-}
-
-static void freeAbsCat(AbsCat cat) {
- freeCId(cat->name);
- freeContext(cat->hypos);
- freeCIdList(cat->funs);
-}
-
-static void freeAbsCats(AbsCats cats) {
- int i;
- for (i = 0; i < cats->count; i++) {
- freeAbsCat(&cats->lst[i]);
- }
- free(cats);
-}
-
-static void freeAbstract(Abstract abstr) {
- freeCId(abstr->name);
- freeFlags(abstr->flags);
- freeAbsFuns(abstr->funs);
- freeAbsCats(abstr->cats);
-}
-
-static void freeConcrete(Concrete concr) {
-// freeCId(concr->name);
-// freeFlags(concr->flags);
-}
-
-void freePGF(PGF pgf) {
- int i;
-
- freeFlags(pgf->flags);
- freeAbstract(&pgf->abstract);
- for (i = 0; i < pgf->nConcr; i++)
- freeConcrete(&pgf->concretes[i]);
- free(pgf);
-}
diff --git a/src/runtime/c/utils/pgf-translate.c b/src/runtime/c/utils/pgf-translate.c
new file mode 100644
index 000000000..c5cdc31e2
--- /dev/null
+++ b/src/runtime/c/utils/pgf-translate.c
@@ -0,0 +1,179 @@
+#include <gu/variant.h>
+#include <gu/map.h>
+#include <gu/dump.h>
+#include <gu/log.h>
+#include <gu/enum.h>
+#include <gu/file.h>
+#include <pgf/pgf.h>
+#include <pgf/data.h>
+#include <pgf/parser.h>
+#include <pgf/linearize.h>
+#include <pgf/expr.h>
+#include <pgf/edsl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+int main(int argc, char* argv[]) {
+ // Set the character locale, so we can produce proper output.
+ setlocale(LC_CTYPE, "");
+
+ // Create the pool that is used to allocate everything
+ GuPool* pool = gu_new_pool();
+ int status = EXIT_SUCCESS;
+ if (argc != 5) {
+ fprintf(stderr, "usage: %s pgf cat from_lang to_lang\n", argv[0]);
+ status = EXIT_FAILURE;
+ goto fail;
+ }
+ char* filename = argv[1];
+
+ // Transform C strings to libgu strings
+ GuString cat = gu_str_string(argv[2], pool);
+ GuString from_lang = gu_str_string(argv[3], pool);
+ GuString to_lang = gu_str_string(argv[4], pool);
+
+ FILE* infile = fopen(filename, "r");
+ if (infile == NULL) {
+ fprintf(stderr, "couldn't open %s\n", filename);
+ status = EXIT_FAILURE;
+ goto fail;
+ }
+
+ // Create an input stream from the input file
+ GuIn* in = gu_file_in(infile, pool);
+
+ // Create an exception frame that catches all errors.
+ GuExn* err = gu_new_exn(NULL, gu_kind(type), pool);
+
+ // Read the PGF grammar.
+ PgfPGF* pgf = pgf_read(in, pool, err);
+
+ // If an error occured, it shows in the exception frame
+ if (!gu_ok(err)) {
+ fprintf(stderr, "Reading PGF failed\n");
+ status = EXIT_FAILURE;
+ goto fail_read;
+ }
+
+ // Look up the source and destination concrete categories
+ PgfConcr* from_concr =
+ gu_map_get(pgf->concretes, &from_lang, PgfConcr*);
+ PgfConcr* to_concr =
+ gu_map_get(pgf->concretes, &to_lang, PgfConcr*);
+ if (!from_concr || !to_concr) {
+ fprintf(stderr, "Unknown language");
+ status = EXIT_FAILURE;
+ goto fail_concr;
+ }
+
+ // Create the parser for the source category
+ PgfParser* parser = pgf_new_parser(from_concr, pool);
+
+ // Create a linearizer for the destination category
+ PgfLzr* lzr = pgf_new_lzr(to_concr, pool);
+
+ // Arbitrarily choose linearization index 0. Usually the initial
+ // categories we are interested in only have one field.
+ int lin_idx = 0;
+
+ // Create an output stream for stdout
+ GuOut* out = gu_file_out(stdout, pool);
+
+ // Locale-encoding writers are currently unsupported
+ // GuWriter* wtr = gu_locale_writer(out, pool);
+ // Use a writer with hard-coded utf-8 encoding for now.
+ GuWriter* wtr = gu_new_utf8_writer(out, pool);
+
+ // The interactive translation loop.
+ // XXX: This currently reads stdin directly, so it doesn't support
+ // encodings properly. TODO: use a locale reader for input
+ while (true) {
+ fprintf(stdout, "> ");
+ fflush(stdout);
+ char buf[4096];
+ char* line = fgets(buf, sizeof(buf), stdin);
+ if (line == NULL) {
+ if (ferror(stdin)) {
+ fprintf(stderr, "Input error\n");
+ status = EXIT_FAILURE;
+ }
+ break;
+ } else if (line[0] == '\0') {
+ // End nicely on empty input
+ break;
+ }
+ // We create a temporary pool for translating a single
+ // sentence, so our memory usage doesn't increase over time.
+ GuPool* ppool = gu_new_pool();
+
+ // Begin parsing a sentence of the specified category
+ PgfParse* parse =
+ pgf_parser_parse(parser, cat, lin_idx, pool);
+ if (parse == NULL) {
+ fprintf(stderr, "Couldn't begin parsing");
+ status = EXIT_FAILURE;
+ break;
+ }
+
+ // naive tokenization
+ char* tok = strtok(line, " \n");
+ while (tok) {
+ GuString tok_s = gu_str_string(tok, pool);
+ gu_debug("parsing token \"%s\"", tok);
+ // feed the token to get a new parse state
+ parse = pgf_parse_token(parse, tok_s, ppool);
+ if (!parse) {
+ fprintf(stderr,
+ "Unexpected token: \"%s\"\n", tok);
+ goto fail_parse;
+ }
+ tok = strtok(NULL, " \n");
+ }
+
+ // Now begin enumerating the resulting syntax trees
+ GuEnum* result = pgf_parse_result(parse, ppool);
+
+ while (true) {
+ PgfExpr expr = gu_next(result, PgfExpr, ppool);
+ // The enumerator will return a null variant at the
+ // end of the results.
+ if (gu_variant_is_null(expr)) {
+ break;
+ }
+ gu_putc(' ', wtr, err);
+ // Write out the abstract syntax tree
+ pgf_expr_print(expr, wtr, err);
+ gu_putc('\n', wtr, err);
+
+ // Enumerate the concrete syntax trees corresponding
+ // to the abstract tree.
+ GuEnum* cts = pgf_lzr_concretize(lzr, expr, ppool);
+ while (true) {
+ PgfCncTree ctree =
+ gu_next(cts, PgfCncTree, ppool);
+ if (gu_variant_is_null(ctree)) {
+ break;
+ }
+ gu_puts(" ", wtr, err);
+ // Linearize the concrete tree as a simple
+ // sequence of strings.
+ pgf_lzr_linearize_simple(lzr, ctree, lin_idx,
+ wtr, err);
+ gu_putc('\n', wtr, err);
+ gu_writer_flush(wtr, err);
+ }
+ }
+ fail_parse:
+ // Free all resources allocated during parsing and linearization
+ gu_pool_free(ppool);
+ }
+fail_concr:
+fail_read:
+ fclose(infile);
+fail:
+ gu_pool_free(pool);
+ return status;
+}
+
diff --git a/src/runtime/c/utils/pgf2yaml.c b/src/runtime/c/utils/pgf2yaml.c
new file mode 100644
index 000000000..32029aa75
--- /dev/null
+++ b/src/runtime/c/utils/pgf2yaml.c
@@ -0,0 +1,29 @@
+#include <pgf/pgf.h>
+
+#include <gu/dump.h>
+#include <gu/file.h>
+#include <gu/utf8.h>
+
+int main(void) {
+ GuPool* pool = gu_new_pool();
+ GuExn* err = gu_exn(NULL, type, pool);
+ GuIn* in = gu_file_in(stdin, pool);
+ PgfPGF* pgf = pgf_read(in, pool, err);
+ int status = 0;
+ if (!gu_ok(err)) {
+ fprintf(stderr, "Reading PGF failed\n");
+ status = 1;
+ goto fail_read;
+ }
+ GuOut* out = gu_file_out(stdout, pool);
+ GuOut* bout = gu_out_buffered(out, pool);
+ // GuWriter* wtr = gu_locale_writer(bout, pool);
+ GuWriter* wtr = gu_new_utf8_writer(bout, pool);
+ GuDump* ctx = gu_new_dump(wtr, NULL, err, pool);
+ gu_dump(gu_type(PgfPGF), pgf, ctx);
+ gu_writer_flush(wtr, err);
+fail_read:
+ gu_pool_free(pool);
+ return status;
+}
+