From a369d3bdac5306339dcef95e0dd3bde13af2f9a8 Mon Sep 17 00:00:00 2001 From: "kr.angelov" Date: Fri, 20 Jan 2012 13:41:10 +0000 Subject: [PATCH] initial import of the C runtime --- src/runtime/c/AUTHORS | 5 + src/runtime/c/COPYING.LESSER | 165 +++ src/runtime/c/Doxyfile | 1632 +++++++++++++++++++++++++++ src/runtime/c/DoxygenLayout.xml | 188 +++ src/runtime/c/Makefile | 19 - src/runtime/c/Makefile.am | 112 ++ src/runtime/c/README | 119 ++ src/runtime/c/configure.ac | 66 ++ src/runtime/c/doxygen.am | 169 +++ src/runtime/c/gfcc-term.c | 203 ---- src/runtime/c/gfcc-term.h | 65 -- src/runtime/c/gfcc-tree.c | 61 - src/runtime/c/gfcc-tree.h | 49 - src/runtime/c/gu/assert.c | 53 + src/runtime/c/gu/assert.h | 61 + src/runtime/c/gu/bits.c | 53 + src/runtime/c/gu/bits.h | 151 +++ src/runtime/c/gu/choice.c | 73 ++ src/runtime/c/gu/choice.h | 37 + src/runtime/c/gu/defs.c | 4 + src/runtime/c/gu/defs.h | 217 ++++ src/runtime/c/gu/dump.c | 411 +++++++ src/runtime/c/gu/dump.h | 34 + src/runtime/c/gu/enum.c | 7 + src/runtime/c/gu/enum.h | 35 + src/runtime/c/gu/exn.c | 72 ++ src/runtime/c/gu/exn.h | 195 ++++ src/runtime/c/gu/file.c | 73 ++ src/runtime/c/gu/file.h | 14 + src/runtime/c/gu/fun.c | 1 + src/runtime/c/gu/fun.h | 65 ++ src/runtime/c/gu/hash.c | 77 ++ src/runtime/c/gu/hash.h | 40 + src/runtime/c/gu/in.c | 421 +++++++ src/runtime/c/gu/in.h | 146 +++ src/runtime/c/gu/intern.c | 59 + src/runtime/c/gu/intern.h | 24 + src/runtime/c/gu/list.c | 59 + src/runtime/c/gu/list.h | 140 +++ src/runtime/c/gu/log.c | 79 ++ src/runtime/c/gu/log.h | 65 ++ src/runtime/c/gu/map.c | 353 ++++++ src/runtime/c/gu/map.h | 121 ++ src/runtime/c/gu/mem.c | 346 ++++++ src/runtime/c/gu/mem.h | 276 +++++ src/runtime/c/gu/out.c | 302 +++++ src/runtime/c/gu/out.h | 166 +++ src/runtime/c/gu/prime.c | 154 +++ src/runtime/c/gu/prime.h | 16 + src/runtime/c/gu/read.c | 15 + src/runtime/c/gu/read.h | 31 + src/runtime/c/gu/seq.c | 245 ++++ src/runtime/c/gu/seq.h | 198 ++++ src/runtime/c/gu/str.c | 85 ++ src/runtime/c/gu/str.h | 29 + src/runtime/c/gu/string.c | 270 +++++ src/runtime/c/gu/string.h | 125 ++ src/runtime/c/gu/sysdeps.h | 30 + src/runtime/c/gu/type.c | 229 ++++ src/runtime/c/gu/type.h | 454 ++++++++ src/runtime/c/gu/ucs.c | 135 +++ src/runtime/c/gu/ucs.h | 53 + src/runtime/c/gu/utf8.c | 220 ++++ src/runtime/c/gu/utf8.h | 67 ++ src/runtime/c/gu/variant.c | 100 ++ src/runtime/c/gu/variant.h | 167 +++ src/runtime/c/gu/write.c | 174 +++ src/runtime/c/gu/write.h | 64 ++ src/runtime/c/gu/yaml.c | 339 ++++++ src/runtime/c/gu/yaml.h | 38 + src/runtime/c/guconfig.h.in | 2 + src/runtime/c/libgu.pc.in | 10 + src/runtime/c/libpgf.pc.in | 11 + src/runtime/c/m4/ax_prog_doxygen.m4 | 533 +++++++++ src/runtime/c/m4/c_ext.m4 | 89 ++ src/runtime/c/pgf.h | 14 - src/runtime/c/pgf/data.c | 251 ++++ src/runtime/c/pgf/data.h | 386 +++++-- src/runtime/c/pgf/edsl.h | 20 + src/runtime/c/pgf/expr.c | 334 ++++++ src/runtime/c/pgf/expr.h | 248 ++-- src/runtime/c/pgf/linearize.c | 613 ++++++++++ src/runtime/c/pgf/linearize.h | 156 +++ src/runtime/c/pgf/loader.c | 396 ------- src/runtime/c/pgf/panic.c | 8 - src/runtime/c/pgf/panic.h | 6 - src/runtime/c/pgf/parser.c | 697 ++++++++++++ src/runtime/c/pgf/parser.h | 127 +++ src/runtime/c/pgf/pgf.h | 78 ++ src/runtime/c/pgf/reader.c | 843 ++++++++++++++ src/runtime/c/pgf/type.h | 22 - src/runtime/c/pgf/unloader.c | 248 ---- src/runtime/c/utils/pgf-translate.c | 179 +++ src/runtime/c/utils/pgf2yaml.c | 29 + 94 files changed, 14344 insertions(+), 1277 deletions(-) create mode 100644 src/runtime/c/AUTHORS create mode 100644 src/runtime/c/COPYING.LESSER create mode 100644 src/runtime/c/Doxyfile create mode 100644 src/runtime/c/DoxygenLayout.xml delete mode 100644 src/runtime/c/Makefile create mode 100644 src/runtime/c/Makefile.am create mode 100644 src/runtime/c/README create mode 100644 src/runtime/c/configure.ac create mode 100644 src/runtime/c/doxygen.am delete mode 100644 src/runtime/c/gfcc-term.c delete mode 100644 src/runtime/c/gfcc-term.h delete mode 100644 src/runtime/c/gfcc-tree.c delete mode 100644 src/runtime/c/gfcc-tree.h create mode 100644 src/runtime/c/gu/assert.c create mode 100644 src/runtime/c/gu/assert.h create mode 100644 src/runtime/c/gu/bits.c create mode 100644 src/runtime/c/gu/bits.h create mode 100644 src/runtime/c/gu/choice.c create mode 100644 src/runtime/c/gu/choice.h create mode 100644 src/runtime/c/gu/defs.c create mode 100644 src/runtime/c/gu/defs.h create mode 100644 src/runtime/c/gu/dump.c create mode 100644 src/runtime/c/gu/dump.h create mode 100644 src/runtime/c/gu/enum.c create mode 100644 src/runtime/c/gu/enum.h create mode 100644 src/runtime/c/gu/exn.c create mode 100644 src/runtime/c/gu/exn.h create mode 100644 src/runtime/c/gu/file.c create mode 100644 src/runtime/c/gu/file.h create mode 100644 src/runtime/c/gu/fun.c create mode 100644 src/runtime/c/gu/fun.h create mode 100644 src/runtime/c/gu/hash.c create mode 100644 src/runtime/c/gu/hash.h create mode 100644 src/runtime/c/gu/in.c create mode 100644 src/runtime/c/gu/in.h create mode 100644 src/runtime/c/gu/intern.c create mode 100644 src/runtime/c/gu/intern.h create mode 100644 src/runtime/c/gu/list.c create mode 100644 src/runtime/c/gu/list.h create mode 100644 src/runtime/c/gu/log.c create mode 100644 src/runtime/c/gu/log.h create mode 100644 src/runtime/c/gu/map.c create mode 100644 src/runtime/c/gu/map.h create mode 100644 src/runtime/c/gu/mem.c create mode 100644 src/runtime/c/gu/mem.h create mode 100644 src/runtime/c/gu/out.c create mode 100644 src/runtime/c/gu/out.h create mode 100644 src/runtime/c/gu/prime.c create mode 100644 src/runtime/c/gu/prime.h create mode 100644 src/runtime/c/gu/read.c create mode 100644 src/runtime/c/gu/read.h create mode 100644 src/runtime/c/gu/seq.c create mode 100644 src/runtime/c/gu/seq.h create mode 100644 src/runtime/c/gu/str.c create mode 100644 src/runtime/c/gu/str.h create mode 100644 src/runtime/c/gu/string.c create mode 100644 src/runtime/c/gu/string.h create mode 100644 src/runtime/c/gu/sysdeps.h create mode 100644 src/runtime/c/gu/type.c create mode 100644 src/runtime/c/gu/type.h create mode 100644 src/runtime/c/gu/ucs.c create mode 100644 src/runtime/c/gu/ucs.h create mode 100644 src/runtime/c/gu/utf8.c create mode 100644 src/runtime/c/gu/utf8.h create mode 100644 src/runtime/c/gu/variant.c create mode 100644 src/runtime/c/gu/variant.h create mode 100644 src/runtime/c/gu/write.c create mode 100644 src/runtime/c/gu/write.h create mode 100644 src/runtime/c/gu/yaml.c create mode 100644 src/runtime/c/gu/yaml.h create mode 100644 src/runtime/c/guconfig.h.in create mode 100644 src/runtime/c/libgu.pc.in create mode 100644 src/runtime/c/libpgf.pc.in create mode 100644 src/runtime/c/m4/ax_prog_doxygen.m4 create mode 100644 src/runtime/c/m4/c_ext.m4 delete mode 100644 src/runtime/c/pgf.h create mode 100644 src/runtime/c/pgf/data.c create mode 100644 src/runtime/c/pgf/edsl.h create mode 100644 src/runtime/c/pgf/expr.c create mode 100644 src/runtime/c/pgf/linearize.c create mode 100644 src/runtime/c/pgf/linearize.h delete mode 100644 src/runtime/c/pgf/loader.c delete mode 100644 src/runtime/c/pgf/panic.c delete mode 100644 src/runtime/c/pgf/panic.h create mode 100644 src/runtime/c/pgf/parser.c create mode 100644 src/runtime/c/pgf/parser.h create mode 100644 src/runtime/c/pgf/pgf.h create mode 100644 src/runtime/c/pgf/reader.c delete mode 100644 src/runtime/c/pgf/type.h delete mode 100644 src/runtime/c/pgf/unloader.c create mode 100644 src/runtime/c/utils/pgf-translate.c create mode 100644 src/runtime/c/utils/pgf2yaml.c 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 + +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. + 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 , where is the value of +# the FILE_VERSION_FILTER tag, and 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 , where +# is the value of the INPUT_FILTER tag, and 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 +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 . + +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 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 + 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 +. + +For general questions, comments and suggestions on libpgf, write to the +GF mailing list at or +. + +For questions and comments that are related to the core libgu library, +but not to PGF, please write directly to the author at +. 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 +# +# 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 -#include -#include - -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 - -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 - - -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 +#include +#include +#include + +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 + +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 + +#include +#include +#include +#include +#include +#include + +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 +#include + + +#define GU_WORD_BITS (sizeof(GuWord) * CHAR_BIT) + + +/* + * Based on the Bit Twiddling Hacks collection by Sean Eron Anderson + * + */ + +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 +#include + +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 +#include +#include +#include + +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 + +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 + +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 . + */ + +/** @file + * + * Miscellaneous macros. + */ + +#ifndef GU_DEFS_H_ +#define GU_DEFS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#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 GU_CONTAINER_P(&s->m, t, m) == s. + * + * @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 +#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 +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include + +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 + +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 + +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 +#include + + +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 +#include + +/** @file + * + * @defgroup GuExn Exceptions + * Defined in . + * @{ + */ + +/// 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 + +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 + +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 +#include +#include + +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 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 + +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 + +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 + +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 +#include +#include + +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 +#include +#include + +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 + +#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 +#include +#include + +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 . + */ + +#include +#include +#include + +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 . + */ + +/** @file + * + * Lists. + */ + +#ifndef GU_LIST_H_ +#define GU_LIST_H_ + +#include + + +#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 + +// +// 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 + + +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 +#include + +#include +#include +#include +#include + +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 + +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 +#include +#include +#include +#include +#include +#include + +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 +#include +#include + +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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_VALGRIND +#include +#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 . + */ + +/** @file + * + * Memory allocation tools. + */ + +#ifndef GU_MEM_H_ +#define GU_MEM_H_ + +#include +#include + +/** @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 + +//@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 +#include + + + +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 +#include +#include + +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 +#include + +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 + +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 + +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 +#include +#include + +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 +#include +#include +#include +#include + + +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_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 +#include + + +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 +#include +#include +#include +#include +#include + +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 +#include + +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 + +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 +#include +#include +#include +#include +#include +#include +#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 . + */ + +#ifndef GU_STRING_H_ +#define GU_STRING_H_ + +#include +#include +#include + +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 + +#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 +#include +#include + +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 + +// +// 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 + +// +// 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 +#include +#include + +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 +#include +#include + + +#if defined(__STDC_ISO_10646__) && WCHAR_MAX >= 0x10FFFF +#include +#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 +#include +#include + +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 +#include +#include + +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 . + */ + +#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 . + */ + +/** @file + * + * Lightweight tagged data. + */ + +#ifndef GU_VARIANT_H_ +#define GU_VARIANT_H_ + +#include +#include +#include + +/** @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 + + +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 +#include +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 +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include + + +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 +#include +#include + +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 +# +# 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 +#include +#include + + +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 . + */ -typedef int BindType; +#ifndef PGF_DATA_H_ +#define PGF_DATA_H_ -#include "expr.h" -#include "type.h" +#include +#include +#include +#include +#include +#include +#include +#include -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; +extern GU_DECLARE_TYPE(PgfPGF, struct); -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[]; +struct PgfFunDecl { + PgfType* type; + int arity; // Only for computational defs? + PgfEquationsM defns; // maybe null + double prob; }; -#endif +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; +}; + +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 + +#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 +#include +#include + + +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 +#include +#include +#include +#include -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 struct _LiteralStr { - struct _Literal _; - String val; -} *LiteralStr; +typedef GuSeq PgfHypos; +extern GU_DECLARE_TYPE(PgfHypos, GuSeq); -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; +struct PgfType { + PgfHypos hypos; + PgfCId cid; /// XXX: resolve to PgfCat*? + int n_exprs; + PgfExpr exprs[]; }; -typedef struct _ExprAbs { - struct _Expr _; - BindType bt; - CId var; - Expr body; -} *ExprAbs; + +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 _ExprApp { - struct _Expr _; - Expr left, right; -} *ExprApp; +typedef struct { + PgfBindType bind_type; + PgfCId id; // + PgfExpr body; +} PgfExprAbs; + +typedef struct { + PgfExpr fun; + PgfExpr arg; +} PgfExprApp; -typedef struct _ExprLit { - struct _Expr _; - Literal lit; -} *ExprLit; +typedef struct { + PgfLiteral lit; +} PgfExprLit; -typedef struct _ExprMeta { - struct _Expr _; - int id; -} *ExprMeta; +typedef struct { + PgfMetaId id; +} PgfExprMeta; -typedef struct _ExprFun { - struct _Expr _; - CId fun; -} *ExprFun; +typedef struct { + PgfCId fun; +} PgfExprFun; -typedef struct _ExprVar { - struct _Expr _; - int index; -} *ExprVar; +typedef struct { + int var; +} PgfExprVar; -typedef struct _ExprTyped { - struct _Expr _; - Expr e; - Type ty; -} *ExprTyped; +/**< A variable. The value is a de Bruijn index to the environment, + * beginning from the innermost variable. */ -typedef struct _ExprImplArg { - struct _Expr _; - Expr e; -} *ExprImplArg; +typedef struct { + PgfExpr expr; + PgfType* type; +} PgfExprTyped; -#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 { + PgfExpr expr; +} PgfExprImplArg; -typedef struct _Patt { - int tag; -} *Patt; +int +pgf_expr_arity(PgfExpr expr); -typedef struct _Patts { - int count; - Patt pats[]; -} *Patts; +PgfExpr +pgf_expr_unwrap(PgfExpr expr); -typedef struct _PattApp { - struct _Patt _; - CId fun; - struct _Patts args; -} *PattApp; +typedef struct PgfApplication PgfApplication; -typedef struct _PattVar { - struct _Patt _; - CId var; -} *PattVar; +struct PgfApplication { + PgfCId fun; + int n_args; + PgfExpr args[]; +}; -typedef struct _PattAt { - struct _Patt _; - CId var; - Patt pat; -} *PattAt; +PgfApplication* +pgf_expr_unapply(PgfExpr expr, GuPool* pool); -typedef struct _PattWild { - struct _Patt _; -} *PattWild; -typedef struct _PattLit { - struct _Patt _; - Literal lit; -} *PattLit; +PgfExpr +pgf_read_expr(GuReader* rdr, GuPool* pool, GuExn* err); -typedef struct _PattImplArg { - struct _Patt _; - Patt pat; -} *PattImplArg; +void +pgf_expr_print(PgfExpr expr, GuWriter* wtr, GuExn* err); -typedef struct _PattTilde { - struct _Patt _; - Expr e; -} *PattTilde; - -typedef struct _Equations { - int count; - struct _Equation { - Patts lhs; - Expr rhs; - } equs[]; -} *Equations; - -#endif +#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 . + */ + +#include "data.h" +#include "linearize.h" +#include +#include +#include +#include +#include +#include +#include +#include + +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 . + */ + +#include +#include +#include +#include + +/// 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 -#include - -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 - -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 +#include +#include +#include +#include + +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 +#include +#include + +/// 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 . + */ + +/** @file + * + * The public libpgf API. + */ + +#ifndef PGF_H_ +#define PGF_H_ + +#include +#include +#include +#include + + +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_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 . + */ + +#include "data.h" +#include "expr.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GU_LOG_ENABLE +#include + +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 - -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + +#include +#include +#include + +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; +} +