mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-09 04:59:31 -06:00
initial import of the C runtime
This commit is contained in:
5
src/runtime/c/AUTHORS
Normal file
5
src/runtime/c/AUTHORS
Normal file
@@ -0,0 +1,5 @@
|
||||
Libpgf was written by:
|
||||
|
||||
Lauri Alanko <lealanko@ling.helsinki.fi>
|
||||
|
||||
Based on the original PGF implementation by Krasimir Angelov.
|
||||
165
src/runtime/c/COPYING.LESSER
Normal file
165
src/runtime/c/COPYING.LESSER
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
1632
src/runtime/c/Doxyfile
Normal file
1632
src/runtime/c/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
188
src/runtime/c/DoxygenLayout.xml
Normal file
188
src/runtime/c/DoxygenLayout.xml
Normal file
@@ -0,0 +1,188 @@
|
||||
<doxygenlayout version="1.0">
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title=""/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type="modules" visible="yes" title="" intro=""/>
|
||||
<tab type="namespaces" visible="yes" title="">
|
||||
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="namespacemembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="classes" visible="yes" title="">
|
||||
<tab type="classlist" visible="yes" title="" intro=""/>
|
||||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="hierarchy" visible="yes" title="" intro=""/>
|
||||
<tab type="classmembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="files" visible="yes" title="">
|
||||
<tab type="filelist" visible="yes" title="" intro=""/>
|
||||
<tab type="globals" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="dirs" visible="yes" title="" intro=""/>
|
||||
<tab type="examples" visible="yes" title="" intro=""/>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
<class>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<inheritancegraph visible="$CLASS_GRAPH"/>
|
||||
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
|
||||
<allmemberslink visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes title=""/>
|
||||
<publicslots title=""/>
|
||||
<signals title=""/>
|
||||
<publicmethods title=""/>
|
||||
<publicstaticmethods title=""/>
|
||||
<publicattributes title=""/>
|
||||
<publicstaticattributes title=""/>
|
||||
<protectedtypes title=""/>
|
||||
<protectedslots title=""/>
|
||||
<protectedmethods title=""/>
|
||||
<protectedstaticmethods title=""/>
|
||||
<protectedattributes title=""/>
|
||||
<protectedstaticattributes title=""/>
|
||||
<packagetypes title=""/>
|
||||
<packagemethods title=""/>
|
||||
<packagestaticmethods title=""/>
|
||||
<packageattributes title=""/>
|
||||
<packagestaticattributes title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
<privatetypes title=""/>
|
||||
<privateslots title=""/>
|
||||
<privatemethods title=""/>
|
||||
<privatestaticmethods title=""/>
|
||||
<privateattributes title=""/>
|
||||
<privatestaticattributes title=""/>
|
||||
<friends title=""/>
|
||||
<related title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<constructors title=""/>
|
||||
<functions title=""/>
|
||||
<related title=""/>
|
||||
<variables title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
</memberdef>
|
||||
<usedfiles visible="$SHOW_USED_FILES"/>
|
||||
<authorsection visible="yes"/>
|
||||
</class>
|
||||
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</namespace>
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<includegraph visible="$INCLUDE_GRAPH"/>
|
||||
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<membergroups visible="yes"/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<enums title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<typedefs title=""/>
|
||||
<functions title=""/>
|
||||
<defines title=""/>
|
||||
<variables title=""/>
|
||||
</memberdecl>
|
||||
<memberdef>
|
||||
<enums title=""/>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<functions title=""/>
|
||||
<defines title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection/>
|
||||
</file>
|
||||
|
||||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="$GROUP_GRAPHS"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<membergroups visible="yes"/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<typedefs title=""/>
|
||||
<defines title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
</memberdecl>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<functions title=""/>
|
||||
<defines title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
@@ -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
|
||||
112
src/runtime/c/Makefile.am
Normal file
112
src/runtime/c/Makefile.am
Normal file
@@ -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
|
||||
119
src/runtime/c/README
Normal file
119
src/runtime/c/README
Normal file
@@ -0,0 +1,119 @@
|
||||
|
||||
This is a preview release of libpgf, a native-code library for parsing
|
||||
and linearization of the PGF grammars produced by the Grammatical
|
||||
Framework <http://www.grammaticalframework.org/>.
|
||||
|
||||
This release is not yet ready for production use: essential
|
||||
functionality is still missing, the API is still likely to change, and
|
||||
the documentation is incomplete. This release is primarily meant for
|
||||
developers who are interested in using libpgf, and who wish to
|
||||
contribute to its design.
|
||||
|
||||
|
||||
PREREQUISITES
|
||||
-------------
|
||||
|
||||
This is a self-contained library: only a C99-conformant C compiler is
|
||||
needed. The code is mostly portable C, although it makes some very
|
||||
general assumptions about the architecture (mostly regarding the
|
||||
representation of addresses) that should hold on modern systems. Still,
|
||||
the code has only been tested on Linux-x86(-64) so far. Reports of
|
||||
porting problems on other platforms are appreciated.
|
||||
|
||||
Although the code "only" requires C99-conformance, it seems that many
|
||||
compilers fail at it subtly. In particular:
|
||||
|
||||
- Clang does not currently support "extern inline" properly.
|
||||
|
||||
- Sun C 5.9 apparently has a bug in its treatment of sizeof on compound
|
||||
array literals.
|
||||
|
||||
As a consequence, these compilers cannot be used in the current state of
|
||||
the code. Modern versions of GCC, on the other hand, seem to work fine.
|
||||
|
||||
|
||||
INSTALLING
|
||||
----------
|
||||
|
||||
This is a standard GNU Autotools package. Read the attached INSTALL file
|
||||
for generic installation instructions. There are currently no
|
||||
interesting special configuration options.
|
||||
|
||||
Pkg-config configuration files for the library are also provided.
|
||||
|
||||
|
||||
STATUS
|
||||
------
|
||||
|
||||
Currently only very basic PGF functionality is supported, enough to
|
||||
translate sentences of the Phrasebook grammar in the GF distribution.
|
||||
Among missing features are:
|
||||
|
||||
- querying a parser for tokens expected next
|
||||
- literals and custom categories
|
||||
- higher-order abstract syntax variables
|
||||
- type checking and inference
|
||||
- generation of random syntax trees
|
||||
|
||||
Most of these will eventually get added.
|
||||
|
||||
|
||||
PROGRAMS
|
||||
--------
|
||||
|
||||
There are two small programs included. These are mainly for testing
|
||||
purposes and for demonstrating how to use the library.
|
||||
|
||||
The pgf2yaml program simply reads a PGF file from the standard input and
|
||||
dumps it to the standard output in YAML <http://yaml.org/> format.
|
||||
|
||||
The pgf-translate program translates sentences of one language in a PGF
|
||||
grammar into another. It is invoked:
|
||||
|
||||
pgf-translate PGF CAT FROM_LANG TO_LANG
|
||||
|
||||
Where PGF is a PGF file, CAT is the name of the category whose sentences
|
||||
are to be translated, and FROM_LANG and TO_LANG are names of concrete
|
||||
grammars within the PGF file.
|
||||
|
||||
The program prompts for a line containing a full sentence of the
|
||||
specified category in the source language, and displays the syntax trees
|
||||
and destination language linearizations of all possible parses of that
|
||||
sentence.
|
||||
|
||||
|
||||
LIBGU
|
||||
-----
|
||||
|
||||
Along with libpgf proper, this distribution includes libgu, a
|
||||
general-purpose utility library that libpgf is based on. Libgu is usable
|
||||
independently of libpgf, and may eventually be split into a separate
|
||||
package. Do give it a try if you are looking for a library to make C
|
||||
programming less painful.
|
||||
|
||||
|
||||
DOCUMENTATION
|
||||
-------------
|
||||
|
||||
Documentation is still fragmentary, but some of the most important
|
||||
headers have documentation comments. If you have Doxygen
|
||||
<http://doxygen.org/> installed, "make doxygen-doc" will generate HTML
|
||||
documentation for the library.
|
||||
|
||||
The sources in utils/pgf-translate.c have some comments which may also
|
||||
clarify how to use the library.
|
||||
|
||||
|
||||
FEEDBACK
|
||||
--------
|
||||
|
||||
Please report bugs to the Grammatical Framework bug tracker
|
||||
<https://code.google.com/p/grammatical-framework/issues/>.
|
||||
|
||||
For general questions, comments and suggestions on libpgf, write to the
|
||||
GF mailing list at <mailto:gf-dev@googlegroups.com> or
|
||||
<https://groups.google.com/group/gf-dev>.
|
||||
|
||||
For questions and comments that are related to the core libgu library,
|
||||
but not to PGF, please write directly to the author at
|
||||
<mailto:lealanko@ling.helsinki.fi>.
|
||||
66
src/runtime/c/configure.ac
Normal file
66
src/runtime/c/configure.ac
Normal file
@@ -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
|
||||
169
src/runtime/c/doxygen.am
Normal file
169
src/runtime/c/doxygen.am
Normal file
@@ -0,0 +1,169 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html
|
||||
# ===========================================================================
|
||||
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2009 Oren Ben-Kiki <oren@ben-kiki.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
## --------------------------------- ##
|
||||
## Format-independent Doxygen rules. ##
|
||||
## --------------------------------- ##
|
||||
|
||||
if DX_COND_doc
|
||||
|
||||
## ------------------------------- ##
|
||||
## Rules specific for HTML output. ##
|
||||
## ------------------------------- ##
|
||||
|
||||
if DX_COND_html
|
||||
|
||||
DX_CLEAN_HTML = @DX_DOCDIR@/html
|
||||
|
||||
endif DX_COND_html
|
||||
|
||||
## ------------------------------ ##
|
||||
## Rules specific for CHM output. ##
|
||||
## ------------------------------ ##
|
||||
|
||||
if DX_COND_chm
|
||||
|
||||
DX_CLEAN_CHM = @DX_DOCDIR@/chm
|
||||
|
||||
if DX_COND_chi
|
||||
|
||||
DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi
|
||||
|
||||
endif DX_COND_chi
|
||||
|
||||
endif DX_COND_chm
|
||||
|
||||
## ------------------------------ ##
|
||||
## Rules specific for MAN output. ##
|
||||
## ------------------------------ ##
|
||||
|
||||
if DX_COND_man
|
||||
|
||||
DX_CLEAN_MAN = @DX_DOCDIR@/man
|
||||
|
||||
endif DX_COND_man
|
||||
|
||||
## ------------------------------ ##
|
||||
## Rules specific for RTF output. ##
|
||||
## ------------------------------ ##
|
||||
|
||||
if DX_COND_rtf
|
||||
|
||||
DX_CLEAN_RTF = @DX_DOCDIR@/rtf
|
||||
|
||||
endif DX_COND_rtf
|
||||
|
||||
## ------------------------------ ##
|
||||
## Rules specific for XML output. ##
|
||||
## ------------------------------ ##
|
||||
|
||||
if DX_COND_xml
|
||||
|
||||
DX_CLEAN_XML = @DX_DOCDIR@/xml
|
||||
|
||||
endif DX_COND_xml
|
||||
|
||||
## ----------------------------- ##
|
||||
## Rules specific for PS output. ##
|
||||
## ----------------------------- ##
|
||||
|
||||
if DX_COND_ps
|
||||
|
||||
DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps
|
||||
|
||||
DX_PS_GOAL = doxygen-ps
|
||||
|
||||
doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps
|
||||
|
||||
@DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag
|
||||
cd @DX_DOCDIR@/latex; \
|
||||
rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
|
||||
$(DX_LATEX) refman.tex; \
|
||||
$(MAKEINDEX_PATH) refman.idx; \
|
||||
$(DX_LATEX) refman.tex; \
|
||||
countdown=5; \
|
||||
while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
|
||||
refman.log > /dev/null 2>&1 \
|
||||
&& test $$countdown -gt 0; do \
|
||||
$(DX_LATEX) refman.tex; \
|
||||
countdown=`expr $$countdown - 1`; \
|
||||
done; \
|
||||
$(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi
|
||||
|
||||
endif DX_COND_ps
|
||||
|
||||
## ------------------------------ ##
|
||||
## Rules specific for PDF output. ##
|
||||
## ------------------------------ ##
|
||||
|
||||
if DX_COND_pdf
|
||||
|
||||
DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf
|
||||
|
||||
DX_PDF_GOAL = doxygen-pdf
|
||||
|
||||
doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf
|
||||
|
||||
@DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag
|
||||
cd @DX_DOCDIR@/latex; \
|
||||
rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
|
||||
$(DX_PDFLATEX) refman.tex; \
|
||||
$(DX_MAKEINDEX) refman.idx; \
|
||||
$(DX_PDFLATEX) refman.tex; \
|
||||
countdown=5; \
|
||||
while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
|
||||
refman.log > /dev/null 2>&1 \
|
||||
&& test $$countdown -gt 0; do \
|
||||
$(DX_PDFLATEX) refman.tex; \
|
||||
countdown=`expr $$countdown - 1`; \
|
||||
done; \
|
||||
mv refman.pdf ../@PACKAGE@.pdf
|
||||
|
||||
endif DX_COND_pdf
|
||||
|
||||
## ------------------------------------------------- ##
|
||||
## Rules specific for LaTeX (shared for PS and PDF). ##
|
||||
## ------------------------------------------------- ##
|
||||
|
||||
if DX_COND_latex
|
||||
|
||||
DX_CLEAN_LATEX = @DX_DOCDIR@/latex
|
||||
|
||||
endif DX_COND_latex
|
||||
|
||||
.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL)
|
||||
|
||||
.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
|
||||
|
||||
doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag
|
||||
|
||||
doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
|
||||
|
||||
@DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS)
|
||||
rm -rf @DX_DOCDIR@
|
||||
$(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG)
|
||||
|
||||
DX_CLEANFILES = \
|
||||
@DX_DOCDIR@/@PACKAGE@.tag \
|
||||
-r \
|
||||
$(DX_CLEAN_HTML) \
|
||||
$(DX_CLEAN_CHM) \
|
||||
$(DX_CLEAN_CHI) \
|
||||
$(DX_CLEAN_MAN) \
|
||||
$(DX_CLEAN_RTF) \
|
||||
$(DX_CLEAN_XML) \
|
||||
$(DX_CLEAN_PS) \
|
||||
$(DX_CLEAN_PDF) \
|
||||
$(DX_CLEAN_LATEX)
|
||||
|
||||
endif DX_COND_doc
|
||||
@@ -1,203 +0,0 @@
|
||||
#include "gfcc-term.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void *buffer = NULL;
|
||||
static size_t current;
|
||||
|
||||
extern void term_alloc_pool(size_t size) {
|
||||
if (buffer == NULL)
|
||||
buffer = malloc(size);
|
||||
current = 0;
|
||||
}
|
||||
|
||||
extern void term_free_pool() {
|
||||
if (buffer != NULL)
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
|
||||
extern void *term_alloc(size_t size) {
|
||||
void *off = buffer + current;
|
||||
current += size;
|
||||
return off;
|
||||
}
|
||||
|
||||
static inline Term *create_term(TermType type, int n) {
|
||||
Term *t = (Term*)term_alloc(sizeof(Term) + n * sizeof(Term *));
|
||||
t->type = type;
|
||||
t->value.size = n; /* FIXME: hack! */
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Term *term_array(int n, ...) {
|
||||
Term *t = create_term(TERM_ARRAY, n);
|
||||
va_list ap;
|
||||
int i;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
term_set_child(t, i, va_arg(ap, Term *));
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Term *term_seq(int n, ...) {
|
||||
Term *t = create_term(TERM_SEQUENCE, n);
|
||||
va_list ap;
|
||||
int i;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
term_set_child(t, i, va_arg(ap, Term *));
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Term *term_variants(int n, ...) {
|
||||
Term *t = create_term(TERM_VARIANTS, n);
|
||||
va_list ap;
|
||||
int i;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
term_set_child(t, i, va_arg(ap, Term *));
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Term *term_glue(int n, ...) {
|
||||
Term *t = create_term(TERM_GLUE, n);
|
||||
va_list ap;
|
||||
int i;
|
||||
|
||||
va_start(ap, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
term_set_child(t, i, va_arg(ap, Term *));
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Term *term_rp(Term *t1, Term *t2) {
|
||||
Term *t = create_term(TERM_RECORD_PARAM, 2);
|
||||
term_set_child(t, 0, t1);
|
||||
term_set_child(t, 1, t2);
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Term *term_suffix(const char *pref, Term *suf) {
|
||||
Term *t = create_term(TERM_SUFFIX_TABLE, 2);
|
||||
term_set_child(t,0,term_str(pref));
|
||||
term_set_child(t,1,suf);
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Term *term_str(const char *s) {
|
||||
Term *t = create_term(TERM_STRING, 0);
|
||||
t->value.string_value = s;
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Term *term_int(int i) {
|
||||
Term *t = create_term(TERM_INTEGER,0);
|
||||
t->value.integer_value = i;
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Term *term_meta() {
|
||||
return create_term(TERM_META, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern Term *term_sel_int(Term *t, int i) {
|
||||
switch (t->type) {
|
||||
case TERM_ARRAY:
|
||||
return term_get_child(t,i);
|
||||
case TERM_SUFFIX_TABLE:
|
||||
return term_glue(2,
|
||||
term_get_child(t,0),
|
||||
term_sel_int(term_get_child(t,1),i));
|
||||
case TERM_META:
|
||||
return t;
|
||||
default:
|
||||
fprintf(stderr,"Error: term_sel_int %d %d\n", t->type, i);
|
||||
exit(1);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
extern Term *term_sel(Term *t1, Term *t2) {
|
||||
switch (t2->type) {
|
||||
case TERM_INTEGER:
|
||||
return term_sel_int(t1, t2->value.integer_value);
|
||||
case TERM_RECORD_PARAM:
|
||||
return term_sel(t1,term_get_child(t2,0));
|
||||
case TERM_META:
|
||||
return term_sel_int(t1,0);
|
||||
default:
|
||||
fprintf(stderr,"Error: term_sel %d %d\n", t1->type, t2->type);
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void term_print_sep(FILE *stream, Term *t, const char *sep) {
|
||||
int n = t->value.size;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
term_print(stream, term_get_child(t,i));
|
||||
if (i < n-1) {
|
||||
fputs(sep, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern void term_print(FILE *stream, Term *t) {
|
||||
switch (t->type) {
|
||||
case TERM_ARRAY:
|
||||
term_print(stream, term_get_child(t,0));
|
||||
break;
|
||||
case TERM_SEQUENCE:
|
||||
term_print_sep(stream, t, " ");
|
||||
break;
|
||||
case TERM_VARIANTS:
|
||||
term_print_sep(stream, t, "/");
|
||||
break;
|
||||
case TERM_GLUE:
|
||||
term_print_sep(stream, t, "");
|
||||
break;
|
||||
case TERM_RECORD_PARAM:
|
||||
term_print(stream, term_get_child(t,0));
|
||||
break;
|
||||
case TERM_SUFFIX_TABLE:
|
||||
term_print(stream, term_get_child(t,0));
|
||||
term_print(stream, term_get_child(t,1));
|
||||
break;
|
||||
case TERM_META:
|
||||
fputs("?", stream);
|
||||
break;
|
||||
case TERM_STRING:
|
||||
fputs(t->value.string_value, stream);
|
||||
break;
|
||||
case TERM_INTEGER:
|
||||
fprintf(stream, "%d", t->value.integer_value);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"Error: term_print %d\n", t->type);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
#ifndef GFCC_TERM_H
|
||||
#define GFCC_TERM_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum {
|
||||
/* size = variable */
|
||||
TERM_ARRAY,
|
||||
TERM_SEQUENCE,
|
||||
TERM_VARIANTS,
|
||||
TERM_GLUE,
|
||||
/* size = 2 */
|
||||
TERM_RECORD_PARAM,
|
||||
TERM_SUFFIX_TABLE,
|
||||
/* size = 0 */
|
||||
TERM_META,
|
||||
TERM_STRING,
|
||||
TERM_INTEGER
|
||||
} TermType;
|
||||
|
||||
struct Term_ {
|
||||
TermType type;
|
||||
union {
|
||||
const char *string_value;
|
||||
int integer_value;
|
||||
int size;
|
||||
} value;
|
||||
struct Term_ *args[0];
|
||||
};
|
||||
|
||||
typedef struct Term_ Term;
|
||||
|
||||
|
||||
|
||||
static inline Term *term_get_child(Term *t, int n) {
|
||||
return t->args[n];
|
||||
}
|
||||
|
||||
static inline void term_set_child(Term *t, int n, Term *c) {
|
||||
t->args[n] = c;
|
||||
}
|
||||
|
||||
extern void term_alloc_pool(size_t size);
|
||||
extern void term_free_pool();
|
||||
extern void *term_alloc(size_t size);
|
||||
|
||||
|
||||
extern Term *term_array(int n, ...);
|
||||
extern Term *term_seq(int n, ...);
|
||||
extern Term *term_variants(int n, ...);
|
||||
extern Term *term_glue(int n, ...);
|
||||
|
||||
extern Term *term_rp(Term *t1, Term *t2);
|
||||
extern Term *term_suffix(const char *pref, Term *suf);
|
||||
extern Term *term_str(const char *s);
|
||||
extern Term *term_int(int i);
|
||||
extern Term *term_meta();
|
||||
|
||||
extern Term *term_sel_int(Term *t, int i);
|
||||
extern Term *term_sel(Term *t1, Term *t2);
|
||||
|
||||
|
||||
extern void term_print(FILE *stream, Term *t);
|
||||
|
||||
#endif
|
||||
@@ -1,61 +0,0 @@
|
||||
#include "gfcc-tree.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
extern int arity(Tree *t) {
|
||||
switch (t->type) {
|
||||
case ATOM_STRING:
|
||||
case ATOM_INTEGER:
|
||||
case ATOM_DOUBLE:
|
||||
case ATOM_META:
|
||||
return 0;
|
||||
default:
|
||||
return t->value.size;
|
||||
}
|
||||
}
|
||||
|
||||
static Tree *create_tree(atom_type c, int n) {
|
||||
Tree *t = (Tree *)malloc(sizeof(Tree) + n * sizeof(Tree *));
|
||||
t->type = c;
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Tree *tree_string(const char *s) {
|
||||
Tree *t = create_tree(ATOM_STRING, 0);
|
||||
t->value.string_value = s;
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Tree *tree_integer(int i) {
|
||||
Tree *t = create_tree(ATOM_INTEGER, 0);
|
||||
t->value.integer_value = i;
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Tree *tree_double(double d) {
|
||||
Tree *t = create_tree(ATOM_DOUBLE, 0);
|
||||
t->value.double_value = d;
|
||||
return t;
|
||||
}
|
||||
|
||||
extern Tree *tree_meta() {
|
||||
return create_tree(ATOM_META, 0);
|
||||
}
|
||||
|
||||
extern Tree *tree_fun(atom_type f, int n) {
|
||||
Tree *t = create_tree(f, n);
|
||||
t->value.size = n;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
extern void tree_free(Tree *t) {
|
||||
int n = arity(t);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
tree_free(tree_get_child(t,i));
|
||||
}
|
||||
free(t);
|
||||
}
|
||||
@@ -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
|
||||
53
src/runtime/c/gu/assert.c
Normal file
53
src/runtime/c/gu/assert.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <gu/assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
const char*
|
||||
gu_assert_mode_descs[] = {
|
||||
[GU_ASSERT_PRECOND] = "precondition failed",
|
||||
[GU_ASSERT_POSTCOND] = "postcondition failed",
|
||||
[GU_ASSERT_ASSERTION] = "assertion failed",
|
||||
[GU_ASSERT_NEVER] = "control should not reach here",
|
||||
};
|
||||
|
||||
void
|
||||
gu_abort_v_(GuAssertMode mode,
|
||||
const char* file, const char* func, int line,
|
||||
const char* msg_fmt, va_list args)
|
||||
{
|
||||
const char* desc = gu_assert_mode_descs[mode];
|
||||
(void) fprintf(stderr, "%s (%s:%d): %s\n", func, file, line, desc);
|
||||
if (msg_fmt != NULL) {
|
||||
(void) fputc('\t', stderr);
|
||||
(void) vfprintf(stderr, msg_fmt, args);
|
||||
(void) fputc('\n', stderr);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
void
|
||||
gu_abort_(GuAssertMode mode,
|
||||
const char* file, const char* func, int line,
|
||||
const char* msg_fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, msg_fmt);
|
||||
gu_abort_v_(mode, file, func, line, msg_fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_fatal(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fputs("Fatal error", stderr);
|
||||
if (fmt) {
|
||||
fputs(": ", stderr);
|
||||
(void) vfprintf(stderr, fmt, args);
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
abort();
|
||||
}
|
||||
61
src/runtime/c/gu/assert.h
Normal file
61
src/runtime/c/gu/assert.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef GU_ASSERT_H_
|
||||
#define GU_ASSERT_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
|
||||
typedef enum {
|
||||
GU_ASSERT_PRECOND,
|
||||
GU_ASSERT_ASSERTION,
|
||||
GU_ASSERT_POSTCOND,
|
||||
GU_ASSERT_NEVER
|
||||
} GuAssertMode;
|
||||
|
||||
void
|
||||
gu_abort_v_(GuAssertMode mode,
|
||||
const char* file, const char* func, int line,
|
||||
const char* msg_fmt, va_list args);
|
||||
|
||||
void
|
||||
gu_abort_(GuAssertMode mode,
|
||||
const char* file, const char* func, int line,
|
||||
const char* msg_fmt, ...);
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define gu_assertion_(mode_, expr_, ...) \
|
||||
GU_BEGIN \
|
||||
if (!(expr_)) { \
|
||||
gu_abort_(mode_, __FILE__, __func__, __LINE__, __VA_ARGS__); \
|
||||
} \
|
||||
GU_END
|
||||
#else
|
||||
// this should prevent unused variable warnings when a variable is only used
|
||||
// in an assertion
|
||||
#define gu_assertion_(mode_, expr_, ...) \
|
||||
GU_BEGIN \
|
||||
(void) (sizeof (expr_)); \
|
||||
GU_END
|
||||
#endif
|
||||
|
||||
|
||||
#define gu_require(expr) \
|
||||
gu_assertion_(GU_ASSERT_PRECOND, expr, "%s", #expr)
|
||||
|
||||
#define gu_assert_msg(expr, ...) \
|
||||
gu_assertion_(GU_ASSERT_ASSERTION, expr, __VA_ARGS__)
|
||||
|
||||
#define gu_assert(expr) \
|
||||
gu_assert_msg(expr, "%s", #expr)
|
||||
|
||||
#define gu_ensure(expr) \
|
||||
gu_assertion_(GU_ASSERT_POSTCOND, expr, "%s", #expr)
|
||||
|
||||
#define gu_impossible_msg(...) \
|
||||
gu_assertion_(GU_ASSERT_ASSERTION, false, __VA_ARGS__)
|
||||
|
||||
#define gu_impossible() \
|
||||
gu_impossible_msg(NULL)
|
||||
|
||||
void
|
||||
gu_fatal(const char* fmt, ...);
|
||||
|
||||
#endif /* GU_ASSERT_H_ */
|
||||
53
src/runtime/c/gu/bits.c
Normal file
53
src/runtime/c/gu/bits.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <gu/bits.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
unsigned gu_ceil2e(unsigned u)
|
||||
{
|
||||
u--;
|
||||
u |= u >> 1;
|
||||
u |= u >> 2;
|
||||
u |= u >> 4;
|
||||
u |= u >> 8;
|
||||
#if UINT_MAX > UINT16_MAX
|
||||
u |= u >> 16;
|
||||
#endif
|
||||
#if UINT_MAX > UINT32_MAX
|
||||
u |= u >> 32;
|
||||
#endif
|
||||
u++;
|
||||
return u;
|
||||
}
|
||||
|
||||
GU_DEFINE_TYPE(GuIntDecodeExn, abstract, _);
|
||||
|
||||
double
|
||||
gu_decode_double(uint64_t u)
|
||||
{
|
||||
bool sign = u >> 63;
|
||||
unsigned rawexp = u >> 52 & 0x7ff;
|
||||
uint64_t mantissa = u & 0xfffffffffffff;
|
||||
double ret;
|
||||
|
||||
if (rawexp == 0x7ff) {
|
||||
if (mantissa == 0) {
|
||||
ret = INFINITY;
|
||||
} else {
|
||||
// At least glibc supports specifying the
|
||||
// mantissa like this.
|
||||
int len = snprintf(NULL, 0, "0x%" PRIx64, mantissa);
|
||||
char buf[len + 1];
|
||||
snprintf(buf, len + 1, "0x%" PRIx64, mantissa);
|
||||
ret = nan(buf);
|
||||
}
|
||||
} else {
|
||||
uint64_t m = rawexp ? 1ULL << 52 | mantissa : mantissa << 1;
|
||||
ret = ldexp((double) m, rawexp - 1075);
|
||||
}
|
||||
return sign ? copysign(ret, -1.0) : ret;
|
||||
}
|
||||
151
src/runtime/c/gu/bits.h
Normal file
151
src/runtime/c/gu/bits.h
Normal file
@@ -0,0 +1,151 @@
|
||||
#ifndef GU_BITS_H_
|
||||
#define GU_BITS_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
|
||||
#define GU_WORD_BITS (sizeof(GuWord) * CHAR_BIT)
|
||||
|
||||
|
||||
/*
|
||||
* Based on the Bit Twiddling Hacks collection by Sean Eron Anderson
|
||||
* <http://graphics.stanford.edu/~seander/bithacks.html>
|
||||
*/
|
||||
|
||||
unsigned gu_ceil2e(unsigned i);
|
||||
|
||||
static inline int
|
||||
gu_sign(int i) {
|
||||
return (i > 0) - (i < 0);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
gu_ceildiv(size_t size, size_t div)
|
||||
{
|
||||
return (size + div - 1) / div;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
gu_aligned(uintptr_t addr, size_t alignment)
|
||||
{
|
||||
gu_require(alignment == gu_ceil2e(alignment));
|
||||
return (addr & (alignment - 1)) == 0;
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
gu_align_forward(uintptr_t addr, size_t alignment) {
|
||||
gu_require(alignment == gu_ceil2e(alignment));
|
||||
uintptr_t mask = alignment - 1;
|
||||
return (addr + mask) & ~mask;
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
gu_align_backward(uintptr_t addr, size_t alignment) {
|
||||
gu_require(alignment == gu_ceil2e(alignment));
|
||||
return addr & ~(alignment - 1);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
gu_bits_test(const GuWord* bitmap, int idx) {
|
||||
return !!(bitmap[idx / GU_WORD_BITS] & 1 << (idx % GU_WORD_BITS));
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_bits_set(GuWord* bitmap, int idx) {
|
||||
bitmap[idx / GU_WORD_BITS] |= ((GuWord) 1) << (idx % GU_WORD_BITS);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_bits_clear(GuWord* bitmap, int idx) {
|
||||
bitmap[idx / GU_WORD_BITS] &= ~(((GuWord) 1) << (idx % GU_WORD_BITS));
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
gu_bits_size(size_t n_bits) {
|
||||
return gu_ceildiv(n_bits, GU_WORD_BITS) * sizeof(GuWord);
|
||||
}
|
||||
|
||||
static inline void*
|
||||
gu_word_ptr(GuWord w)
|
||||
{
|
||||
return (void*) w;
|
||||
}
|
||||
|
||||
static inline GuWord
|
||||
gu_ptr_word(void* p)
|
||||
{
|
||||
return (GuWord) p;
|
||||
}
|
||||
|
||||
#define GuOpaque() struct { GuWord w_; }
|
||||
|
||||
typedef GuWord GuTagged;
|
||||
|
||||
#define GU_TAG_MAX (sizeof(GuWord) - 1)
|
||||
|
||||
static inline size_t
|
||||
gu_tagged_tag(GuTagged t) {
|
||||
return (int) (t & (sizeof(GuWord) - 1));
|
||||
}
|
||||
|
||||
static inline void*
|
||||
gu_tagged_ptr(GuTagged w) {
|
||||
return (void*) gu_align_backward(w, sizeof(GuWord));
|
||||
}
|
||||
|
||||
static inline GuTagged
|
||||
gu_tagged(void* ptr, size_t tag) {
|
||||
gu_require(tag < sizeof(GuWord));
|
||||
uintptr_t u = (uintptr_t) ptr;
|
||||
gu_require(gu_align_backward(u, sizeof(GuWord)) == u);
|
||||
return (GuWord) { u | tag };
|
||||
}
|
||||
|
||||
#include <gu/exn.h>
|
||||
#include <gu/type.h>
|
||||
|
||||
extern GU_DECLARE_TYPE(GuIntDecodeExn, abstract);
|
||||
|
||||
#define GU_DECODE_2C_(u_, t_, umax_, posmax_, tmin_, err_) \
|
||||
(((u_) <= (posmax_)) \
|
||||
? (t_) (u_) \
|
||||
: (tmin_) + ((t_) ((umax_) - (u_))) < 0 \
|
||||
? -1 - ((t_) ((umax_) - (u_))) \
|
||||
: (gu_raise(err_, GuIntDecodeExn), -1))
|
||||
|
||||
|
||||
static inline int8_t
|
||||
gu_decode_2c8(uint8_t u, GuExn* err)
|
||||
{
|
||||
return GU_DECODE_2C_(u, int8_t, UINT8_C(0xff),
|
||||
INT8_C(0x7f), INT8_MIN, err);
|
||||
}
|
||||
|
||||
static inline int16_t
|
||||
gu_decode_2c16(uint16_t u, GuExn* err)
|
||||
{
|
||||
return GU_DECODE_2C_(u, int16_t, UINT16_C(0xffff),
|
||||
INT16_C(0x7fff), INT16_MIN, err);
|
||||
}
|
||||
|
||||
static inline int32_t
|
||||
gu_decode_2c32(uint32_t u, GuExn* err)
|
||||
{
|
||||
return GU_DECODE_2C_(u, int32_t, UINT32_C(0xffffffff),
|
||||
INT32_C(0x7fffffff), INT32_MIN, err);
|
||||
}
|
||||
|
||||
static inline int64_t
|
||||
gu_decode_2c64(uint64_t u, GuExn* err)
|
||||
{
|
||||
return GU_DECODE_2C_(u, int64_t, UINT64_C(0xffffffffffffffff),
|
||||
INT64_C(0x7fffffffffffffff), INT64_MIN, err);
|
||||
}
|
||||
|
||||
double
|
||||
gu_decode_double(uint64_t u);
|
||||
|
||||
|
||||
|
||||
#endif // GU_BITS_H_
|
||||
73
src/runtime/c/gu/choice.c
Normal file
73
src/runtime/c/gu/choice.c
Normal file
@@ -0,0 +1,73 @@
|
||||
#include <gu/choice.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/log.h>
|
||||
|
||||
struct GuChoice {
|
||||
GuBuf* path;
|
||||
size_t path_idx;
|
||||
};
|
||||
|
||||
GuChoice*
|
||||
gu_new_choice(GuPool* pool)
|
||||
{
|
||||
GuChoice* ch = gu_new(GuChoice, pool);
|
||||
ch->path = gu_new_buf(uint8_t, pool);
|
||||
ch->path_idx = 0;
|
||||
return ch;
|
||||
}
|
||||
|
||||
GuChoiceMark
|
||||
gu_choice_mark(GuChoice* ch)
|
||||
{
|
||||
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
|
||||
gu_debug("%p@%d: mark", ch, ch->path_idx);
|
||||
return (GuChoiceMark){ch->path_idx};
|
||||
}
|
||||
|
||||
void
|
||||
gu_choice_reset(GuChoice* ch, GuChoiceMark mark)
|
||||
{
|
||||
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
|
||||
gu_debug("%p@%d: reset %d", ch, ch->path_idx, mark.path_idx);
|
||||
gu_require(mark.path_idx <= ch->path_idx );
|
||||
ch->path_idx = mark.path_idx;
|
||||
}
|
||||
|
||||
int
|
||||
gu_choice_next(GuChoice* ch, int n_choices)
|
||||
{
|
||||
gu_assert(n_choices >= 0);
|
||||
gu_require(n_choices <= UINT8_MAX);
|
||||
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
|
||||
if (n_choices == 0) {
|
||||
return -1;
|
||||
}
|
||||
int i = 0;
|
||||
if (gu_buf_length(ch->path) > ch->path_idx) {
|
||||
i = (int) gu_buf_get(ch->path, uint8_t, ch->path_idx);
|
||||
gu_assert(i <= n_choices);
|
||||
} else {
|
||||
gu_buf_push(ch->path, uint8_t, n_choices);
|
||||
i = n_choices;
|
||||
}
|
||||
int ret = (i == 0) ? -1 : n_choices - i;
|
||||
gu_debug("%p@%d: %d", ch, ch->path_idx, ret);
|
||||
ch->path_idx++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
gu_choice_advance(GuChoice* ch)
|
||||
{
|
||||
gu_assert(ch->path_idx <= gu_buf_length(ch->path));
|
||||
|
||||
while (gu_buf_length(ch->path) > ch->path_idx) {
|
||||
uint8_t last = gu_buf_pop(ch->path, uint8_t);
|
||||
if (last > 1) {
|
||||
gu_buf_push(ch->path, uint8_t, last-1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
37
src/runtime/c/gu/choice.h
Normal file
37
src/runtime/c/gu/choice.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef GU_CHOICE_H_
|
||||
#define GU_CHOICE_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
|
||||
typedef struct GuChoice GuChoice;
|
||||
|
||||
typedef struct GuChoiceMark GuChoiceMark;
|
||||
|
||||
GuChoice*
|
||||
gu_new_choice(GuPool* pool);
|
||||
|
||||
int
|
||||
gu_choice_next(GuChoice* ch, int n_choices);
|
||||
|
||||
GuChoiceMark
|
||||
gu_choice_mark(GuChoice* ch);
|
||||
|
||||
void
|
||||
gu_choice_reset(GuChoice* ch, GuChoiceMark mark);
|
||||
|
||||
bool
|
||||
gu_choice_advance(GuChoice* ch);
|
||||
|
||||
|
||||
// private
|
||||
|
||||
struct GuChoiceMark {
|
||||
size_t path_idx;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // GU_CHOICE_H_
|
||||
4
src/runtime/c/gu/defs.c
Normal file
4
src/runtime/c/gu/defs.c
Normal file
@@ -0,0 +1,4 @@
|
||||
#include <gu/defs.h>
|
||||
|
||||
void* const gu_null = NULL;
|
||||
GuStruct* const gu_null_struct = NULL;
|
||||
217
src/runtime/c/gu/defs.h
Normal file
217
src/runtime/c/gu/defs.h
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Miscellaneous macros.
|
||||
*/
|
||||
|
||||
#ifndef GU_DEFS_H_
|
||||
#define GU_DEFS_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <gu/sysdeps.h>
|
||||
|
||||
#define gu_container(mem_p, container_type, member) \
|
||||
((container_type*)(((uint8_t*) (mem_p)) - offsetof(container_type, member)))
|
||||
/**< Find the address of a containing structure.
|
||||
*
|
||||
* If @c s has type @c t*, where @c t is a struct or union type with a
|
||||
* member @m, then <tt>GU_CONTAINER_P(&s->m, t, m) == s</tt>.
|
||||
*
|
||||
* @param mem_p Pointer to the member of a structure.
|
||||
* @param container_type The type of the containing structure.
|
||||
* @param member The name of the member of @a container_type
|
||||
* @return The address of the containing structure.
|
||||
*
|
||||
* @hideinitializer */
|
||||
|
||||
|
||||
#define gu_member_p(struct_p_, offset_) \
|
||||
((void*)&((uint8_t*)(struct_p_))[offset_])
|
||||
|
||||
#define gu_member(t_, struct_p_, offset_) \
|
||||
(*(t_*)gu_member_p(struct_p_, offset_))
|
||||
|
||||
#ifdef GU_ALIGNOF
|
||||
# define gu_alignof GU_ALIGNOF
|
||||
# define GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
|
||||
#else
|
||||
# define gu_alignof(t_) \
|
||||
((size_t)(offsetof(struct { char c_; t_ e_; }, e_)))
|
||||
# ifdef GU_CAN_HAVE_FAM_IN_MEMBER
|
||||
# define GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define GU_PLIT(type, expr) \
|
||||
((type[1]){ expr })
|
||||
|
||||
#define GU_LVALUE(type, expr) \
|
||||
(*((type[1]){ expr }))
|
||||
|
||||
#define GU_COMMA ,
|
||||
|
||||
#define GU_ARRAY_LEN(t,a) (sizeof((const t[])a) / sizeof(t))
|
||||
|
||||
#define GU_ID(...) __VA_ARGS__
|
||||
|
||||
// This trick is by Laurent Deniau <laurent.deniau@cern.ch>
|
||||
#define GU_N_ARGS(...) \
|
||||
GU_N_ARGS_(__VA_ARGS__, \
|
||||
31,30,29,28,27,26,25,24, \
|
||||
23,22,21,20,19,18,17,16, \
|
||||
15,14,13,12,11,10,9,8, \
|
||||
7,6,5,4,3,2,1,0)
|
||||
#define GU_N_ARGS_(...) GU_N_ARGS__(__VA_ARGS__)
|
||||
#define GU_N_ARGS__(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p, \
|
||||
q,r,s,t,u,v,w,x,y,z,aa,ab,ac,ad,ae,N,...) \
|
||||
N
|
||||
|
||||
#define GU_ARG1(a1, ...) a1
|
||||
#define GU_ARG2(a1, a2, ...) a2
|
||||
|
||||
#define GU_BEGIN do {
|
||||
#define GU_END } while (false)
|
||||
|
||||
#define GU_NOP GU_BEGIN (void) 0; GU_END
|
||||
|
||||
/**< @hideinitializer */
|
||||
|
||||
//
|
||||
// Assert
|
||||
//
|
||||
|
||||
#define GU_MAX(a_, b_) ((a_) > (b_) ? (a_) : (b_))
|
||||
#define GU_MIN(a_, b_) ((a_) < (b_) ? (a_) : (b_))
|
||||
|
||||
static inline int
|
||||
gu_max(int a, int b) {
|
||||
return GU_MAX(a, b);
|
||||
}
|
||||
|
||||
static inline int
|
||||
gu_min(int a, int b) {
|
||||
return GU_MIN(a, b);
|
||||
}
|
||||
|
||||
#ifdef GU_ALIGNOF_WORKS_ON_FAM_STRUCTS
|
||||
#define gu_flex_alignof gu_alignof
|
||||
#else
|
||||
#define gu_flex_alignof(t) 0
|
||||
#endif
|
||||
|
||||
static inline size_t
|
||||
gu_flex_size(size_t ssize, size_t offset, int n_elems, size_t e_size)
|
||||
{
|
||||
return GU_MAX(ssize, offset + n_elems * e_size);
|
||||
}
|
||||
|
||||
#define GU_FLEX_SIZE(type, flex_member, n_elems) \
|
||||
gu_flex_size(sizeof(type), offsetof(type, flex_member), \
|
||||
n_elems, sizeof(((type*)NULL)->flex_member[0]))
|
||||
|
||||
|
||||
// The following are directly from gmacros.h in GLib
|
||||
|
||||
#define GU_PASTE_ARGS(id1_,id2_) \
|
||||
id1_ ## id2_
|
||||
|
||||
#define GU_PASTE(id1_, id2_) \
|
||||
GU_PASTE_ARGS(id1_, id2_)
|
||||
|
||||
#define GU_STATIC_ASSERT(expr_) \
|
||||
typedef struct { \
|
||||
char static_assert[(expr_) ? 1 : -1]; \
|
||||
} GU_PASTE(GuStaticAssert_, __LINE__)
|
||||
|
||||
|
||||
#define GU_ENSURE_TYPE(T, EXPR) \
|
||||
((void)(sizeof(*(T*)NULL=(EXPR))),(EXPR))
|
||||
|
||||
#define GU_END_DECLS \
|
||||
extern void gu_dummy_(void)
|
||||
|
||||
extern void* const gu_null;
|
||||
|
||||
// Dummy struct used for generic struct pointers
|
||||
typedef struct GuStruct GuStruct;
|
||||
|
||||
extern GuStruct* const gu_null_struct;
|
||||
|
||||
typedef uintptr_t GuWord;
|
||||
|
||||
#define GU_WORD_MAX UINTPTR_MAX
|
||||
|
||||
// TODO: use max_align_t once C1X is supported
|
||||
typedef union {
|
||||
char c;
|
||||
short s;
|
||||
int i;
|
||||
long l;
|
||||
long long ll;
|
||||
intmax_t im;
|
||||
float f;
|
||||
double d;
|
||||
long double ld;
|
||||
void* p;
|
||||
void (*fp)();
|
||||
} GuMaxAlign;
|
||||
|
||||
#define gu_alloca(N) \
|
||||
(((union { GuMaxAlign align_; uint8_t buf_[N]; }){{0}}).buf_)
|
||||
|
||||
|
||||
// For Doxygen
|
||||
#define GU_PRIVATE /** @private */
|
||||
|
||||
#ifdef GU_GNUC
|
||||
# define GU_LIKELY(EXPR) __builtin_expect(EXPR, 1)
|
||||
# define GU_UNLIKELY(EXPR) __builtin_expect(EXPR, 0)
|
||||
# define GU_IS_CONSTANT(EXPR) __builtin_constant_p(EXPR)
|
||||
#else
|
||||
# define GU_LIKELY(EXPR) (EXPR)
|
||||
# define GU_UNLIKELY(EXPR) (EXPR)
|
||||
# ifdef GU_OPTIMIZE_SIZE
|
||||
# define GU_IS_CONSTANT(EXPR) false
|
||||
# else
|
||||
# define GU_IS_CONSTANT(EXPR) true
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Splint annotations
|
||||
#define GU_ONLY GU_SPLINT(only)
|
||||
#define GU_NULL GU_SPLINT(null)
|
||||
#define GU_NOTNULL GU_SPLINT(notnull)
|
||||
#define GU_RETURNED GU_SPLINT(returned)
|
||||
#define GU_ABSTRACT GU_SPLINT(abstract)
|
||||
#define GU_IMMUTABLE GU_SPLINT(immutable)
|
||||
#define GU_NOTREACHED GU_SPLINT(notreached)
|
||||
#define GU_UNUSED GU_SPLINT(unused) GU_GNUC_ATTR(unused)
|
||||
#define GU_OUT GU_SPLINT(out)
|
||||
#define GU_IN GU_SPLINT(in)
|
||||
#define GU_NORETURN GU_SPLINT(noreturn) GU_GNUC_ATTR(noreturn)
|
||||
#define GU_MODIFIES(x) GU_SPLINT(modifies x)
|
||||
|
||||
#endif // GU_DEFS_H_
|
||||
411
src/runtime/c/gu/dump.c
Normal file
411
src/runtime/c/gu/dump.c
Normal file
@@ -0,0 +1,411 @@
|
||||
#include <gu/dump.h>
|
||||
#include <gu/list.h>
|
||||
#include <gu/variant.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/str.h>
|
||||
#include <gu/file.h>
|
||||
|
||||
GuDump*
|
||||
gu_new_dump(GuWriter* wtr, GuTypeTable* dumpers, GuExn* err, GuPool* pool)
|
||||
{
|
||||
GuDump* ctx = gu_new(GuDump, pool);
|
||||
ctx->pool = pool;
|
||||
if (dumpers == NULL) {
|
||||
dumpers = &gu_dump_table;
|
||||
}
|
||||
ctx->dumpers = gu_new_type_map(dumpers, pool);
|
||||
ctx->yaml = gu_new_yaml(wtr, err, pool);
|
||||
ctx->data = gu_new_addr_map(void, void*, &gu_null, pool);
|
||||
ctx->print_address = false;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void
|
||||
gu_dump(GuType* type, const void* value, GuDump* ctx)
|
||||
{
|
||||
GuDumpFn* dumper = gu_type_map_get(ctx->dumpers, type);
|
||||
if (ctx->print_address) {
|
||||
GuPool* pool = gu_new_pool();
|
||||
GuString s = gu_format_string(pool, "%p", value);
|
||||
gu_yaml_comment(ctx->yaml, s);
|
||||
gu_pool_free(pool);
|
||||
}
|
||||
(*dumper)(dumper, type, value, ctx);
|
||||
}
|
||||
|
||||
void
|
||||
gu_dump_stderr(GuType* type, const void* value, GuExn* err)
|
||||
{
|
||||
GuPool* pool = gu_new_pool();
|
||||
GuOut* out = gu_file_out(stderr, pool);
|
||||
#if 0
|
||||
GuWriter* wtr = gu_locale_writer(out, pool);
|
||||
#else
|
||||
GuWriter* wtr = gu_new_utf8_writer(out, pool);
|
||||
#endif
|
||||
GuDump* ctx = gu_new_dump(wtr, NULL, err, pool);
|
||||
gu_dump(type, value, ctx);
|
||||
gu_pool_free(pool);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_scalar(GuDump* ctx, const char* fmt, ...)
|
||||
{
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
GuString s = gu_format_string_v(fmt, args, tmp_pool);
|
||||
va_end(args);
|
||||
gu_yaml_scalar(ctx->yaml, s);
|
||||
gu_pool_free(tmp_pool);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_str_scalar(GuDump* ctx, const char* str)
|
||||
{
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
GuString s = gu_str_string(str, tmp_pool);
|
||||
gu_yaml_scalar(ctx->yaml, s);
|
||||
gu_pool_free(tmp_pool);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_null(GuDump* ctx)
|
||||
{
|
||||
gu_yaml_tag_secondary(ctx->yaml, "null");
|
||||
gu_yaml_scalar(ctx->yaml, gu_empty_string);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_int(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const int* ip = p;
|
||||
gu_dump_scalar(ctx, "%d", *ip);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_uint16(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const uint16_t* ip = p;
|
||||
gu_dump_scalar(ctx, "%" PRIu16, *ip);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_size(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) (dumper && type);
|
||||
const size_t* zp = p;
|
||||
gu_dump_scalar(ctx, "%zu", *zp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gu_dump_double(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const double* dp = p;
|
||||
gu_dump_scalar(ctx, "%lf", *dp);
|
||||
}
|
||||
|
||||
static const char gu_dump_length_key[] = "gu_dump_length_key";
|
||||
|
||||
static void
|
||||
gu_dump_length(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const GuLength* ip = p;
|
||||
gu_dump_scalar(ctx, "%d", *ip);
|
||||
GuLength* lenp = gu_map_get(ctx->data, gu_dump_length_key, void*);
|
||||
if (lenp != NULL) {
|
||||
*lenp = *ip;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_str(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const GuStr* sp = p;
|
||||
gu_dump_str_scalar(ctx, *sp);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_string(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
const GuString* sp = p;
|
||||
gu_yaml_scalar(ctx->yaml, *sp);
|
||||
}
|
||||
|
||||
|
||||
// For _non-shared_ pointers.
|
||||
static void
|
||||
gu_dump_pointer(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuPointerType* ptype = (GuPointerType*) type;
|
||||
void* const* pp = p;
|
||||
if (*pp == NULL) {
|
||||
gu_dump_null(ctx);
|
||||
} else {
|
||||
gu_dump(ptype->pointed_type, *pp, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GuMapItor itor;
|
||||
GuMapType* mtype;
|
||||
GuDump* ctx;
|
||||
} GuDumpMapFn;
|
||||
|
||||
static void
|
||||
gu_dump_map_itor(GuMapItor* self, const void* key, void* value, GuExn* err)
|
||||
{
|
||||
(void) err;
|
||||
GuDumpMapFn* clo = (GuDumpMapFn*) self;
|
||||
gu_dump(clo->mtype->key_type, key, clo->ctx);
|
||||
gu_dump(clo->mtype->value_type, value, clo->ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_map(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuMapType* mtype = (GuMapType*) type;
|
||||
GuMap* map = (GuMap*) p;
|
||||
gu_yaml_begin_mapping(ctx->yaml);
|
||||
GuDumpMapFn clo = { { gu_dump_map_itor }, mtype, ctx };
|
||||
gu_map_iter(map, &clo.itor, NULL);
|
||||
gu_yaml_end(ctx->yaml);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_dump_struct(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuStructRepr* srepr = (GuStructRepr*) type;
|
||||
gu_yaml_begin_mapping(ctx->yaml);
|
||||
const uint8_t* data = p;
|
||||
GuLength* old_lenp = gu_map_get(ctx->data, gu_dump_length_key, void*);
|
||||
GuLength len = (GuLength)-1;
|
||||
gu_map_put(ctx->data, gu_dump_length_key, void*, &len);
|
||||
|
||||
for (int i = 0; i < srepr->members.len; i++) {
|
||||
const GuMember* member = &srepr->members.elems[i];
|
||||
gu_dump_str_scalar(ctx, member->name);
|
||||
const uint8_t* memp = &data[member->offset];
|
||||
if (member->is_flex) {
|
||||
// Flexible array member
|
||||
gu_assert(len != (GuLength)-1);
|
||||
size_t mem_s = gu_type_size(member->type);
|
||||
gu_yaml_begin_sequence(ctx->yaml);
|
||||
for (GuLength i = 0; i < len; i++) {
|
||||
gu_dump(member->type, &memp[i * mem_s], ctx);
|
||||
}
|
||||
gu_yaml_end(ctx->yaml);
|
||||
} else {
|
||||
gu_dump(member->type, memp, ctx);
|
||||
}
|
||||
}
|
||||
gu_yaml_end(ctx->yaml);
|
||||
if (old_lenp) {
|
||||
gu_map_set(ctx->data, gu_dump_length_key, void*, old_lenp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_alias(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuTypeAlias* alias = gu_type_cast(type, alias);
|
||||
|
||||
gu_dump(alias->type, p, ctx);
|
||||
}
|
||||
|
||||
static const char gu_dump_reference_key[] = "reference";
|
||||
|
||||
static bool
|
||||
gu_dump_anchor(GuDump* ctx, const void* p)
|
||||
{
|
||||
GuMap* map = gu_map_get(ctx->data, gu_dump_reference_key, void*);
|
||||
if (map == NULL) {
|
||||
map = gu_new_addr_map(void, GuYamlAnchor,
|
||||
&gu_yaml_null_anchor, ctx->pool);
|
||||
gu_map_put(ctx->data, gu_dump_reference_key, void*, map);
|
||||
}
|
||||
GuYamlAnchor a = gu_map_get(map, p, GuYamlAnchor);
|
||||
if (a == gu_yaml_null_anchor) {
|
||||
a = gu_yaml_anchor(ctx->yaml);
|
||||
gu_map_put(map, p, GuYamlAnchor, a);
|
||||
return true;
|
||||
} else {
|
||||
gu_yaml_alias(ctx->yaml, a);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_referenced(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuTypeAlias* alias = gu_type_cast(type, alias);
|
||||
bool created = gu_dump_anchor(ctx, p);
|
||||
if (created) {
|
||||
gu_dump(alias->type, p, ctx);
|
||||
} else {
|
||||
// gu_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_reference(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
(void) type;
|
||||
void* const* pp = p;
|
||||
bool created = gu_dump_anchor(ctx, *pp);
|
||||
if (created) {
|
||||
// gu_assert(false);
|
||||
GuPointerType* ptype = (GuPointerType*) type;
|
||||
gu_dump(ptype->pointed_type, *pp, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_shared(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
void* const* pp = p;
|
||||
if (*pp == NULL) {
|
||||
gu_dump_null(ctx);
|
||||
} else {
|
||||
bool created = gu_dump_anchor(ctx, *pp);
|
||||
if (created) {
|
||||
GuPointerType* ptype = (GuPointerType*) type;
|
||||
gu_dump(ptype->pointed_type, *pp, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_list(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuListType* ltype = (GuListType*) type;
|
||||
const uint8_t* up = p;
|
||||
int len = * (const int*) p;
|
||||
size_t elem_size = gu_type_size(ltype->elem_type);
|
||||
gu_yaml_begin_sequence(ctx->yaml);
|
||||
for (int i = 0; i < len; i++) {
|
||||
ptrdiff_t offset = ltype->elems_offset + i * elem_size;
|
||||
gu_dump(ltype->elem_type, &up[offset], ctx);
|
||||
}
|
||||
gu_yaml_end(ctx->yaml);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_variant(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuVariantType* vtype = gu_type_cast(type, GuVariant);
|
||||
const GuVariant* vp = p;
|
||||
int tag = gu_variant_tag(*vp);
|
||||
for (int i = 0; i < vtype->ctors.len; i++) {
|
||||
GuConstructor* ctor = &vtype->ctors.elems[i];
|
||||
if (ctor->c_tag == tag) {
|
||||
gu_yaml_begin_mapping(ctx->yaml);
|
||||
gu_dump_str_scalar(ctx, ctor->c_name);
|
||||
void* data = gu_variant_data(*vp);
|
||||
gu_dump(ctor->type, data, ctx);
|
||||
gu_yaml_end(ctx->yaml);
|
||||
return;
|
||||
}
|
||||
}
|
||||
gu_assert(false);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_dump_enum(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuEnumType* etype = gu_type_cast(type, enum);
|
||||
GuEnumConstant* cp = gu_enum_value(etype, p);
|
||||
gu_assert(cp != NULL);
|
||||
gu_dump_str_scalar(ctx, cp->name);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_dump_seq(GuDumpFn* dumper, GuType* type, const void* p,
|
||||
GuDump* ctx)
|
||||
{
|
||||
(void) dumper;
|
||||
GuSeqType* dtype = gu_type_cast(type, GuSeq);
|
||||
size_t elem_size = gu_type_size(dtype->elem_type);
|
||||
const GuSeq* seqp = p;
|
||||
GuSeq seq = *seqp;
|
||||
if (gu_seq_is_null(seq)) {
|
||||
gu_dump_null(ctx);
|
||||
return;
|
||||
}
|
||||
size_t len = gu_seq_length(seq);
|
||||
const uint8_t* data = gu_seq_data(seq);
|
||||
gu_yaml_begin_sequence(ctx->yaml);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
const void* elemp = &data[i * elem_size];
|
||||
gu_dump(dtype->elem_type, elemp, ctx);
|
||||
}
|
||||
gu_yaml_end(ctx->yaml);
|
||||
}
|
||||
|
||||
|
||||
GuTypeTable
|
||||
gu_dump_table = GU_TYPETABLE(
|
||||
GU_SLIST_0,
|
||||
{ gu_kind(int), gu_fn(gu_dump_int) },
|
||||
{ gu_kind(uint16_t), gu_fn(gu_dump_uint16) },
|
||||
{ gu_kind(size_t), gu_fn(gu_dump_size) },
|
||||
{ gu_kind(GuStr), gu_fn(gu_dump_str) },
|
||||
{ gu_kind(GuString), gu_fn(gu_dump_string) },
|
||||
{ gu_kind(struct), gu_fn(gu_dump_struct) },
|
||||
{ gu_kind(pointer), gu_fn(gu_dump_pointer) },
|
||||
{ gu_kind(GuMap), gu_fn(gu_dump_map) },
|
||||
{ gu_kind(alias), gu_fn(gu_dump_alias) },
|
||||
{ gu_kind(reference), gu_fn(gu_dump_reference) },
|
||||
{ gu_kind(referenced), gu_fn(gu_dump_referenced) },
|
||||
{ gu_kind(shared), gu_fn(gu_dump_shared) },
|
||||
{ gu_kind(GuList), gu_fn(gu_dump_list) },
|
||||
{ gu_kind(GuSeq), gu_fn(gu_dump_seq) },
|
||||
{ gu_kind(GuLength), gu_fn(gu_dump_length) },
|
||||
{ gu_kind(GuVariant), gu_fn(gu_dump_variant) },
|
||||
{ gu_kind(double), gu_fn(gu_dump_double) },
|
||||
{ gu_kind(enum), gu_fn(gu_dump_enum) },
|
||||
);
|
||||
34
src/runtime/c/gu/dump.h
Normal file
34
src/runtime/c/gu/dump.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef GU_DUMP_H_
|
||||
#define GU_DUMP_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/yaml.h>
|
||||
#include <gu/type.h>
|
||||
#include <gu/map.h>
|
||||
|
||||
typedef struct GuDump GuDump;
|
||||
|
||||
struct GuDump {
|
||||
GuPool* pool;
|
||||
GuYaml* yaml;
|
||||
GuMap* data;
|
||||
GuTypeMap* dumpers;
|
||||
bool print_address;
|
||||
};
|
||||
|
||||
typedef void (*GuDumpFn)(GuFn* self, GuType* type, const void* value, GuDump* ctx);
|
||||
|
||||
GuDump*
|
||||
gu_new_dump(GuWriter* wtr, GuTypeTable* dumpers, GuExn* err, GuPool* pool);
|
||||
|
||||
void
|
||||
gu_dump(GuType* type, const void* value, GuDump* ctx);
|
||||
|
||||
void
|
||||
gu_dump_stderr(GuType* type, const void* value, GuExn* err);
|
||||
|
||||
extern GuTypeTable
|
||||
gu_dump_table;
|
||||
|
||||
|
||||
#endif // GU_DUMP_H_
|
||||
7
src/runtime/c/gu/enum.c
Normal file
7
src/runtime/c/gu/enum.c
Normal file
@@ -0,0 +1,7 @@
|
||||
#include <gu/enum.h>
|
||||
|
||||
void
|
||||
gu_enum_next(GuEnum* en, void* to, GuPool* pool)
|
||||
{
|
||||
en->next(en, to, pool);
|
||||
}
|
||||
35
src/runtime/c/gu/enum.h
Normal file
35
src/runtime/c/gu/enum.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef GU_ENUM_H_
|
||||
#define GU_ENUM_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
|
||||
typedef struct GuEnum GuEnum;
|
||||
|
||||
struct GuEnum {
|
||||
void (*next)(GuEnum* self, void* to, GuPool* pool);
|
||||
};
|
||||
|
||||
void
|
||||
gu_enum_next(GuEnum* en, void* to, GuPool* pool);
|
||||
|
||||
#ifdef GU_GNUC
|
||||
|
||||
#define gu_next(ENUM, T, POOL) \
|
||||
({ \
|
||||
T gu_next_tmp_; \
|
||||
gu_enum_next((ENUM), &gu_next_tmp_, (POOL)); \
|
||||
gu_next_tmp_; \
|
||||
})
|
||||
#else
|
||||
static inline void*
|
||||
gu_enum_next_(GuEnum* en, void* to, GuPool* pool)
|
||||
{
|
||||
gu_enum_next(en, to, pool);
|
||||
return to;
|
||||
}
|
||||
#define gu_next(ENUM, T, POOL) \
|
||||
(*(T*)gu_enum_next_((ENUM), &(T){0}, (POOL)))
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* GU_ENUM_H_ */
|
||||
72
src/runtime/c/gu/exn.c
Normal file
72
src/runtime/c/gu/exn.c
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <gu/exn.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
|
||||
GuExn*
|
||||
gu_new_exn(GuExn* parent, GuKind* catch, GuPool* pool)
|
||||
{
|
||||
return gu_new_s(pool, GuExn,
|
||||
.state = GU_EXN_OK,
|
||||
.parent = parent,
|
||||
.catch = catch,
|
||||
.caught = NULL,
|
||||
.data.pool = pool,
|
||||
.data.data = NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gu_exn_block(GuExn* err)
|
||||
{
|
||||
if (err && err->state == GU_EXN_RAISED) {
|
||||
err->state = GU_EXN_BLOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gu_exn_unblock(GuExn* err)
|
||||
{
|
||||
if (err && err->state == GU_EXN_BLOCKED) {
|
||||
err->state = GU_EXN_RAISED;
|
||||
}
|
||||
}
|
||||
|
||||
GuExnData*
|
||||
gu_exn_raise_debug_(GuExn* base, GuType* type,
|
||||
const char* filename, const char* func, int lineno)
|
||||
{
|
||||
gu_require(type);
|
||||
|
||||
// TODO: log the error, once there's a system for dumping
|
||||
// error objects.
|
||||
|
||||
GuExn* err = base;
|
||||
|
||||
while (err && !(err->catch && gu_type_has_kind(type, err->catch))) {
|
||||
err->state = GU_EXN_RAISED;
|
||||
err = err->parent;
|
||||
}
|
||||
if (!err) {
|
||||
gu_abort_(GU_ASSERT_ASSERTION, filename, func, lineno,
|
||||
"Unexpected error raised");
|
||||
}
|
||||
GuExnState old_state = err->state;
|
||||
err->state = GU_EXN_RAISED;
|
||||
if (old_state == GU_EXN_OK) {
|
||||
err->caught = type;
|
||||
if (err->data.pool) {
|
||||
return &err->data;
|
||||
}
|
||||
}
|
||||
// Exceptian had already been raised, possibly blocked, or no
|
||||
// exception value is required.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GuExnData*
|
||||
gu_exn_raise_(GuExn* base, GuType* type)
|
||||
{
|
||||
return gu_exn_raise_debug_(base, type, NULL, NULL, -1);
|
||||
}
|
||||
|
||||
|
||||
GU_DEFINE_TYPE(GuErrno, signed, _);
|
||||
195
src/runtime/c/gu/exn.h
Normal file
195
src/runtime/c/gu/exn.h
Normal file
@@ -0,0 +1,195 @@
|
||||
#ifndef GU_EXN_H_
|
||||
#define GU_EXN_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/type.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* @defgroup GuExn Exceptions
|
||||
* Defined in <gu/exn.h>.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/// An exception frame.
|
||||
typedef struct GuExn GuExn;
|
||||
|
||||
/// @private
|
||||
typedef enum {
|
||||
GU_EXN_RAISED,
|
||||
GU_EXN_OK,
|
||||
GU_EXN_BLOCKED
|
||||
} GuExnState;
|
||||
|
||||
typedef struct GuExnData GuExnData;
|
||||
|
||||
/// A structure for storing exception values.
|
||||
struct GuExnData
|
||||
/**
|
||||
* When an exception is raised, if there is an associated value, it
|
||||
* must be allocated from a pool that still exists when control
|
||||
* returns to the handler of that exception. This structure is used to
|
||||
* communicate the exception from the raiser to the handler: the
|
||||
* handler sets #pool when setting up the exception frame, and the
|
||||
* raiser uses that pool to allocate the value and stores that in
|
||||
* #data. When control returns to the handler, it reads the value from
|
||||
* there.
|
||||
*/
|
||||
{
|
||||
|
||||
/// The pool that the exception value should be allocated from.
|
||||
GuPool* const pool;
|
||||
|
||||
/// The exception value.
|
||||
const void* data;
|
||||
};
|
||||
|
||||
struct GuExn {
|
||||
/// @privatesection
|
||||
GuExnState state;
|
||||
GuExn* parent;
|
||||
GuKind* catch;
|
||||
GuType* caught;
|
||||
GuExnData data;
|
||||
};
|
||||
|
||||
|
||||
/// @name Creating exception frames
|
||||
//@{
|
||||
|
||||
|
||||
/// Allocate a new local exception frame.
|
||||
#define gu_exn(parent_, catch_, pool_) &(GuExn){ \
|
||||
.state = GU_EXN_OK, \
|
||||
.parent = parent_, \
|
||||
.catch = gu_kind(catch_), \
|
||||
.caught = NULL, \
|
||||
.data.pool = pool_, \
|
||||
.data.data = NULL \
|
||||
}
|
||||
|
||||
|
||||
/// Allocate a new exception frame.
|
||||
GuExn*
|
||||
gu_new_exn(GuExn* parent, GuKind* catch_kind, GuPool* pool);
|
||||
|
||||
|
||||
|
||||
static inline bool
|
||||
gu_exn_is_raised(GuExn* err) {
|
||||
return err && (err->state == GU_EXN_RAISED);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_exn_clear(GuExn* err) {
|
||||
err->caught = NULL;
|
||||
err->state = GU_EXN_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GuType*
|
||||
gu_exn_caught(GuExn* err);
|
||||
|
||||
const void*
|
||||
gu_exn_caught_data(GuExn* err);
|
||||
|
||||
/// Temporarily block a raised exception.
|
||||
void
|
||||
gu_exn_block(GuExn* err);
|
||||
|
||||
/// Show again a blocked exception.
|
||||
void
|
||||
gu_exn_unblock(GuExn* err);
|
||||
|
||||
//@private
|
||||
GuExnData*
|
||||
gu_exn_raise_(GuExn* err, GuType* type);
|
||||
|
||||
//@private
|
||||
GuExnData*
|
||||
gu_exn_raise_debug_(GuExn* err, GuType* type,
|
||||
const char* filename, const char* func, int lineno);
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define gu_exn_raise(err_, type_) \
|
||||
gu_exn_raise_(err_, type_)
|
||||
#else
|
||||
#define gu_exn_raise(err_, type_) \
|
||||
gu_exn_raise_debug_(err_, type_, \
|
||||
__FILE__, __func__, __LINE__)
|
||||
#endif
|
||||
|
||||
/// Raise an exception.
|
||||
#define gu_raise(exn, T) \
|
||||
gu_exn_raise(exn, gu_type(T))
|
||||
/**<
|
||||
* @param exn The current exception frame.
|
||||
*
|
||||
* @param T The C type of the exception to raise.
|
||||
*
|
||||
* @return A #GuExnData object that can be used to store the exception value, or
|
||||
* \c NULL if no value is required.
|
||||
*
|
||||
* @note The associated #GuType object for type \p T must be visible.
|
||||
*/
|
||||
|
||||
#define gu_raise_new(error_, t_, pool_, expr_) \
|
||||
GU_BEGIN \
|
||||
GuExnData* gu_raise_err_ = gu_raise(error_, t_); \
|
||||
if (gu_raise_err_) { \
|
||||
GuPool* pool_ = gu_raise_err_->pool; \
|
||||
gu_raise_err_->data = expr_; \
|
||||
} \
|
||||
GU_END
|
||||
|
||||
#define gu_raise_i(error_, t_, ...) \
|
||||
gu_raise_new(error_, t_, gu_raise_pool_, gu_new_i(gu_raise_pool_, t_, __VA_ARGS__))
|
||||
|
||||
|
||||
/// Check the status of the current exception frame
|
||||
static inline bool
|
||||
gu_ok(GuExn* exn) {
|
||||
return !GU_UNLIKELY(gu_exn_is_raised(exn));
|
||||
}
|
||||
/**<
|
||||
* @return \c false if an exception has been raised in the frame \p exn
|
||||
* and it has not been blocked, \c true otherwise.
|
||||
*/
|
||||
|
||||
|
||||
/// Return from current function if an exception has been raised.
|
||||
#define gu_return_on_exn(exn_, retval_) \
|
||||
GU_BEGIN \
|
||||
if (gu_exn_is_raised(exn_)) return retval_; \
|
||||
GU_END
|
||||
/**<
|
||||
* @showinitializer
|
||||
*/
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
typedef int GuErrno;
|
||||
|
||||
extern GU_DECLARE_TYPE(GuErrno, signed);
|
||||
|
||||
|
||||
|
||||
#define gu_raise_errno(error_) \
|
||||
gu_raise_i(error_, GuErrno, errno)
|
||||
|
||||
#if 0
|
||||
|
||||
typedef void (*GuExnPrintFn)(GuFn* clo, void* err, FILE* out);
|
||||
|
||||
extern GuTypeTable gu_exn_default_printer;
|
||||
|
||||
void
|
||||
gu_exn_print(GuExn* err, FILE* out, GuTypeMap printer_map);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif // GU_EXN_H_
|
||||
73
src/runtime/c/gu/file.c
Normal file
73
src/runtime/c/gu/file.c
Normal file
@@ -0,0 +1,73 @@
|
||||
#include <gu/file.h>
|
||||
|
||||
typedef struct GuFileOutStream GuFileOutStream;
|
||||
|
||||
struct GuFileOutStream {
|
||||
GuOutStream stream;
|
||||
FILE* file;
|
||||
};
|
||||
|
||||
static size_t
|
||||
gu_file_output(GuOutStream* stream, const uint8_t* buf, size_t len, GuExn* err)
|
||||
{
|
||||
GuFileOutStream* fos = gu_container(stream, GuFileOutStream, stream);
|
||||
errno = 0;
|
||||
size_t wrote = fwrite(buf, 1, len, fos->file);
|
||||
if (wrote < len) {
|
||||
if (ferror(fos->file)) {
|
||||
gu_raise_errno(err);
|
||||
}
|
||||
}
|
||||
return wrote;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_file_flush(GuOutStream* stream, GuExn* err)
|
||||
{
|
||||
GuFileOutStream* fos = gu_container(stream, GuFileOutStream, stream);
|
||||
errno = 0;
|
||||
if (fflush(fos->file) != 0) {
|
||||
gu_raise_errno(err);
|
||||
}
|
||||
}
|
||||
|
||||
GuOut*
|
||||
gu_file_out(FILE* file, GuPool* pool)
|
||||
{
|
||||
GuFileOutStream* fos = gu_new_i(pool, GuFileOutStream,
|
||||
.stream.output = gu_file_output,
|
||||
.stream.flush = gu_file_flush,
|
||||
.file = file);
|
||||
return gu_new_out(&fos->stream, pool);
|
||||
}
|
||||
|
||||
|
||||
typedef struct GuFileInStream GuFileInStream;
|
||||
|
||||
struct GuFileInStream {
|
||||
GuInStream stream;
|
||||
FILE* file;
|
||||
};
|
||||
|
||||
static size_t
|
||||
gu_file_input(GuInStream* stream, uint8_t* buf, size_t sz, GuExn* err)
|
||||
{
|
||||
GuFileInStream* fis = gu_container(stream, GuFileInStream, stream);
|
||||
errno = 0;
|
||||
size_t got = fread(buf, 1, sz, fis->file);
|
||||
if (got == 0) {
|
||||
if (ferror(fis->file)) {
|
||||
gu_raise_errno(err);
|
||||
}
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
GuIn*
|
||||
gu_file_in(FILE* file, GuPool* pool)
|
||||
{
|
||||
GuFileInStream* fis = gu_new_s(pool, GuFileInStream,
|
||||
.stream.input = gu_file_input,
|
||||
.file = file);
|
||||
return gu_new_in(&fis->stream, pool);
|
||||
}
|
||||
14
src/runtime/c/gu/file.h
Normal file
14
src/runtime/c/gu/file.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef GU_FILE_H_
|
||||
#define GU_FILE_H_
|
||||
|
||||
#include <gu/in.h>
|
||||
#include <gu/out.h>
|
||||
#include <stdio.h>
|
||||
|
||||
GuOut*
|
||||
gu_file_out(FILE* file, GuPool* pool);
|
||||
|
||||
GuIn*
|
||||
gu_file_in(FILE* file, GuPool* pool);
|
||||
|
||||
#endif // GU_FILE_H_
|
||||
1
src/runtime/c/gu/fun.c
Normal file
1
src/runtime/c/gu/fun.c
Normal file
@@ -0,0 +1 @@
|
||||
#include <gu/fun.h>
|
||||
65
src/runtime/c/gu/fun.h
Normal file
65
src/runtime/c/gu/fun.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef GU_FUN_H_
|
||||
#define GU_FUN_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
|
||||
typedef void (*GuFn)();
|
||||
typedef void (*GuFn0)(GuFn* clo);
|
||||
typedef void (*GuFn1)(GuFn* clo, void* arg1);
|
||||
typedef void (*GuFn2)(GuFn* clo, void* arg1, void* arg2);
|
||||
|
||||
#define gu_fn(fn_) (&(GuFn){ fn_ })
|
||||
|
||||
static inline void
|
||||
gu_apply0(GuFn* fn) {
|
||||
(*fn)(fn);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_apply1(GuFn* fn, void* arg1) {
|
||||
(*fn)(fn, arg1);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_apply2(GuFn* fn, void* arg1, void* arg2) {
|
||||
(*fn)(fn, arg1, arg2);
|
||||
}
|
||||
|
||||
#define gu_apply(fn_, ...) \
|
||||
((fn_)->fn((fn_), __VA_ARGS__))
|
||||
|
||||
typedef struct GuClo0 GuClo0;
|
||||
|
||||
struct GuClo0 {
|
||||
GuFn fn;
|
||||
};
|
||||
|
||||
typedef struct GuClo1 GuClo1;
|
||||
|
||||
struct GuClo1 {
|
||||
GuFn fn;
|
||||
void *env1;
|
||||
};
|
||||
|
||||
typedef struct GuClo2 GuClo2;
|
||||
struct GuClo2 {
|
||||
GuFn fn;
|
||||
void *env1;
|
||||
void *env2;
|
||||
};
|
||||
|
||||
typedef struct GuClo3 GuClo3;
|
||||
struct GuClo3 {
|
||||
GuFn fn;
|
||||
void *env1;
|
||||
void *env2;
|
||||
void *env3;
|
||||
};
|
||||
|
||||
typedef const struct GuEquality GuEquality;
|
||||
|
||||
struct GuEquality {
|
||||
bool (*is_equal)(GuEquality* self, const void* a, const void* b);
|
||||
};
|
||||
|
||||
#endif // GU_FUN_H_
|
||||
77
src/runtime/c/gu/hash.c
Normal file
77
src/runtime/c/gu/hash.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <gu/hash.h>
|
||||
|
||||
GuHash
|
||||
gu_hash_bytes(GuHash h, const uint8_t* buf, size_t len)
|
||||
{
|
||||
for (size_t n = 0; n < len; n++) {
|
||||
h = gu_hash_byte(h, buf[n]);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_int_eq_fn(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
const int* ip1 = p1;
|
||||
const int* ip2 = p2;
|
||||
return *ip1 == *ip2;
|
||||
}
|
||||
|
||||
static GuHash
|
||||
gu_int_hash_fn(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
return (GuHash) *(const int*) p;
|
||||
}
|
||||
|
||||
GuHasher gu_int_hasher[1] = {
|
||||
{
|
||||
{ gu_int_eq_fn },
|
||||
gu_int_hash_fn
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
gu_addr_eq_fn(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
return (p1 == p2);
|
||||
}
|
||||
|
||||
static GuHash
|
||||
gu_addr_hash_fn(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
return (GuHash) (uintptr_t) p;
|
||||
}
|
||||
|
||||
GuHasher gu_addr_hasher[1] = {
|
||||
{
|
||||
{ gu_addr_eq_fn },
|
||||
gu_addr_hash_fn
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
gu_word_eq_fn(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
const GuWord* wp1 = p1;
|
||||
const GuWord* wp2 = p2;
|
||||
return (*wp1 == *wp2);
|
||||
}
|
||||
|
||||
static GuHash
|
||||
gu_word_hash_fn(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
return (GuHash) (uintptr_t) p;
|
||||
}
|
||||
|
||||
GuHasher gu_word_hasher[1] = {
|
||||
{
|
||||
{ gu_word_eq_fn },
|
||||
gu_word_hash_fn
|
||||
}
|
||||
};
|
||||
40
src/runtime/c/gu/hash.h
Normal file
40
src/runtime/c/gu/hash.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef GU_HASH_H_
|
||||
#define GU_HASH_H_
|
||||
|
||||
#include <gu/fun.h>
|
||||
|
||||
typedef GuWord GuHash;
|
||||
|
||||
static inline GuHash
|
||||
gu_hash_ptr(void* ptr)
|
||||
{
|
||||
return (GuHash) ptr;
|
||||
}
|
||||
|
||||
|
||||
static inline GuHash
|
||||
gu_hash_byte(GuHash h, uint8_t u)
|
||||
{
|
||||
// Paul Larson's simple byte hash
|
||||
return h * 101 + u;
|
||||
}
|
||||
|
||||
|
||||
GuHash
|
||||
gu_hash_bytes(GuHash h, const uint8_t* buf, size_t len);
|
||||
|
||||
typedef const struct GuHasher GuHasher;
|
||||
|
||||
struct GuHasher {
|
||||
GuEquality eq;
|
||||
GuHash (*hash)(GuHasher* self, const void* p);
|
||||
};
|
||||
|
||||
|
||||
extern GuHasher gu_int_hasher[1];
|
||||
|
||||
extern GuHasher gu_addr_hasher[1];
|
||||
|
||||
extern GuHasher gu_word_hasher[1];
|
||||
|
||||
#endif // GU_HASH_H_
|
||||
421
src/runtime/c/gu/in.c
Normal file
421
src/runtime/c/gu/in.c
Normal file
@@ -0,0 +1,421 @@
|
||||
#include <gu/in.h>
|
||||
#include <gu/bits.h>
|
||||
#include <math.h>
|
||||
|
||||
GU_DEFINE_TYPE(GuEOF, abstract, _);
|
||||
|
||||
|
||||
static bool
|
||||
gu_in_is_buffering(GuIn* in)
|
||||
{
|
||||
return (in->buf_end != NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_in_end_buffering(GuIn* in, GuExn* err)
|
||||
{
|
||||
if (!gu_in_is_buffering(in)) {
|
||||
return;
|
||||
}
|
||||
if (in->stream->end_buffer) {
|
||||
size_t len = ((ptrdiff_t) in->buf_size) + in->buf_curr;
|
||||
in->stream->end_buffer(in->stream, len, err);
|
||||
}
|
||||
in->buf_curr = 0;
|
||||
in->buf_size = 0;
|
||||
in->buf_end = NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_in_begin_buffering(GuIn* in, GuExn* err)
|
||||
{
|
||||
if (gu_in_is_buffering(in)) {
|
||||
if (in->buf_curr < 0) {
|
||||
return true;
|
||||
} else {
|
||||
gu_in_end_buffering(in, err);
|
||||
if (!gu_ok(err)) return false;
|
||||
}
|
||||
}
|
||||
if (!in->stream->begin_buffer) {
|
||||
return false;
|
||||
}
|
||||
size_t sz = 0;
|
||||
const uint8_t* new_buf =
|
||||
in->stream->begin_buffer(in->stream, &sz, err);
|
||||
if (new_buf) {
|
||||
in->buf_end = &new_buf[sz];
|
||||
in->buf_curr = -(ptrdiff_t) sz;
|
||||
in->buf_size = sz;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_in_input(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
|
||||
{
|
||||
if (sz == 0) {
|
||||
return 0;
|
||||
}
|
||||
gu_in_end_buffering(in, err);
|
||||
if (!gu_ok(err)) {
|
||||
return 0;
|
||||
}
|
||||
GuInStream* stream = in->stream;
|
||||
if (stream->input) {
|
||||
return stream->input(stream, dst, sz, err);
|
||||
}
|
||||
gu_raise(err, GuEOF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_in_some(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
|
||||
{
|
||||
gu_require(sz <= PTRDIFF_MAX);
|
||||
if (!gu_in_begin_buffering(in, err)) {
|
||||
if (!gu_ok(err)) return 0;
|
||||
return gu_in_input(in, dst, sz, err);
|
||||
}
|
||||
size_t real_sz = GU_MIN(sz, (size_t)(-in->buf_curr));
|
||||
memcpy(dst, &in->buf_end[in->buf_curr], real_sz);
|
||||
in->buf_curr += real_sz;
|
||||
return real_sz;
|
||||
}
|
||||
|
||||
void
|
||||
gu_in_bytes_(GuIn* in, uint8_t* dst, size_t sz, GuExn* err)
|
||||
{
|
||||
size_t avail_sz = GU_MIN(sz, (size_t)(-in->buf_curr));
|
||||
memcpy(dst, &in->buf_end[in->buf_curr], avail_sz);
|
||||
in->buf_curr += avail_sz;
|
||||
if (avail_sz < sz) {
|
||||
gu_in_input(in, &dst[avail_sz], sz - avail_sz, err);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t*
|
||||
gu_in_begin_span(GuIn* in, size_t *sz_out, GuExn* err)
|
||||
{
|
||||
if (!gu_in_begin_buffering(in, err)) {
|
||||
return NULL;
|
||||
}
|
||||
*sz_out = (size_t) -in->buf_curr;
|
||||
return &in->buf_end[in->buf_curr];
|
||||
}
|
||||
|
||||
void
|
||||
gu_in_end_span(GuIn* in, size_t consumed)
|
||||
{
|
||||
gu_require(consumed <= (size_t) -in->buf_curr);
|
||||
in->buf_curr += (ptrdiff_t) consumed;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
gu_in_u8_(GuIn* in, GuExn* err)
|
||||
{
|
||||
if (gu_in_begin_buffering(in, err) && in->buf_curr < 0) {
|
||||
return in->buf_end[in->buf_curr++];
|
||||
}
|
||||
uint8_t u = 0;
|
||||
size_t r = gu_in_input(in, &u, 1, err);
|
||||
if (r < 1) {
|
||||
gu_raise(err, GuEOF);
|
||||
return 0;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
gu_in_be(GuIn* in, GuExn* err, int n)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
gu_in_bytes(in, buf, n, err);
|
||||
uint64_t u = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
u = u << 8 | buf[i];
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
gu_in_le(GuIn* in, GuExn* err, int n)
|
||||
{
|
||||
uint8_t buf[8];
|
||||
gu_in_bytes(in, buf, n, err);
|
||||
uint64_t u = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
u = u << 8 | buf[i];
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
int8_t
|
||||
gu_in_s8(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c8(gu_in_u8(in, err), err);
|
||||
}
|
||||
|
||||
|
||||
uint16_t
|
||||
gu_in_u16le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_le(in, err, 2);
|
||||
}
|
||||
|
||||
int16_t
|
||||
gu_in_s16le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c16(gu_in_u16le(in, err), err);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
gu_in_u16be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_be(in, err, 2);
|
||||
}
|
||||
|
||||
int16_t
|
||||
gu_in_s16be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c16(gu_in_u16be(in, err), err);
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
gu_in_u32le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_le(in, err, 4);
|
||||
}
|
||||
|
||||
int32_t
|
||||
gu_in_s32le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c32(gu_in_u32le(in, err), err);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
gu_in_u32be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_be(in, err, 4);
|
||||
}
|
||||
|
||||
int32_t
|
||||
gu_in_s32be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c32(gu_in_u32be(in, err), err);
|
||||
}
|
||||
|
||||
|
||||
uint64_t
|
||||
gu_in_u64le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_le(in, err, 8);
|
||||
}
|
||||
|
||||
int64_t
|
||||
gu_in_s64le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c64(gu_in_u64le(in, err), err);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
gu_in_u64be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_in_be(in, err, 8);
|
||||
}
|
||||
|
||||
int64_t
|
||||
gu_in_s64be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_2c64(gu_in_u64be(in, err), err);
|
||||
}
|
||||
|
||||
double
|
||||
gu_in_f64le(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_double(gu_in_u64le(in, err));
|
||||
}
|
||||
|
||||
double
|
||||
gu_in_f64be(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_decode_double(gu_in_u64le(in, err));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_in_fini(GuFinalizer* fin)
|
||||
{
|
||||
GuIn* in = gu_container(fin, GuIn, fini);
|
||||
GuPool* pool = gu_local_pool();
|
||||
GuExn* err = gu_exn(NULL, type, pool);
|
||||
gu_in_end_buffering(in, err);
|
||||
gu_pool_free(pool);
|
||||
}
|
||||
|
||||
GuIn
|
||||
gu_init_in(GuInStream* stream)
|
||||
{
|
||||
return (GuIn) {
|
||||
.buf_end = NULL,
|
||||
.buf_curr = 0,
|
||||
.buf_size = 0,
|
||||
.stream = stream,
|
||||
.fini.fn = gu_in_fini
|
||||
};
|
||||
}
|
||||
|
||||
GuIn*
|
||||
gu_new_in(GuInStream* stream, GuPool* pool)
|
||||
{
|
||||
GuIn* in = gu_new(GuIn, pool);
|
||||
*in = gu_init_in(stream);
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
typedef struct GuProxyInStream GuProxyInStream;
|
||||
|
||||
struct GuProxyInStream {
|
||||
GuInStream stream;
|
||||
GuIn* real_in;
|
||||
};
|
||||
|
||||
static const uint8_t*
|
||||
gu_proxy_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
|
||||
{
|
||||
GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
|
||||
return gu_in_begin_span(pis->real_in, sz_out, err);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_proxy_in_end_buffer(GuInStream* self, size_t sz, GuExn* err)
|
||||
{
|
||||
GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
|
||||
gu_in_end_span(pis->real_in, sz);
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_proxy_in_input(GuInStream* self, uint8_t* dst, size_t sz, GuExn* err)
|
||||
{
|
||||
GuProxyInStream* pis = gu_container(self, GuProxyInStream, stream);
|
||||
return gu_in_some(pis->real_in, dst, sz, err);
|
||||
}
|
||||
|
||||
GuInStream*
|
||||
gu_in_proxy_stream(GuIn* in, GuPool* pool)
|
||||
{
|
||||
return &gu_new_s(
|
||||
pool, GuProxyInStream,
|
||||
.stream.begin_buffer = gu_proxy_in_begin_buffer,
|
||||
.stream.end_buffer = gu_proxy_in_end_buffer,
|
||||
.stream.input = gu_proxy_in_input,
|
||||
.real_in = in)->stream;
|
||||
}
|
||||
|
||||
enum {
|
||||
GU_BUFFERED_IN_BUF_SIZE = 4096
|
||||
};
|
||||
|
||||
typedef struct GuBufferedInStream GuBufferedInStream;
|
||||
|
||||
struct GuBufferedInStream {
|
||||
GuInStream stream;
|
||||
size_t alloc;
|
||||
size_t have;
|
||||
size_t curr;
|
||||
GuIn* in;
|
||||
uint8_t buf[];
|
||||
};
|
||||
|
||||
static const uint8_t*
|
||||
gu_buffered_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
|
||||
{
|
||||
GuBufferedInStream* bis =
|
||||
gu_container(self, GuBufferedInStream, stream);
|
||||
if (bis->curr == bis->have) {
|
||||
bis->curr = 0;
|
||||
bis->have = gu_in_some(bis->in, bis->buf, bis->alloc, err);
|
||||
if (!gu_ok(err)) return NULL;
|
||||
}
|
||||
*sz_out = bis->have - bis->curr;
|
||||
return &bis->buf[bis->curr];
|
||||
}
|
||||
|
||||
static void
|
||||
gu_buffered_in_end_buffer(GuInStream* self, size_t consumed, GuExn* err)
|
||||
{
|
||||
GuBufferedInStream* bis =
|
||||
gu_container(self, GuBufferedInStream, stream);
|
||||
gu_require(consumed < bis->have - bis->curr);
|
||||
bis->curr += consumed;
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_buffered_in_input(GuInStream* self, uint8_t* dst, size_t sz, GuExn* err)
|
||||
{
|
||||
GuBufferedInStream* bis =
|
||||
gu_container(self, GuBufferedInStream, stream);
|
||||
return gu_in_some(bis->in, dst, sz, err);
|
||||
}
|
||||
|
||||
GuIn*
|
||||
gu_buffered_in(GuIn* in, size_t buf_sz, GuPool* pool)
|
||||
{
|
||||
GuBufferedInStream* bis = gu_new_flex(pool, GuBufferedInStream,
|
||||
buf, buf_sz);
|
||||
bis->stream = (GuInStream) {
|
||||
.begin_buffer = gu_buffered_in_begin_buffer,
|
||||
.end_buffer = gu_buffered_in_end_buffer,
|
||||
.input = gu_buffered_in_input
|
||||
};
|
||||
bis->have = bis->curr = 0;
|
||||
bis->alloc = buf_sz;
|
||||
return gu_new_in(&bis->stream, pool);
|
||||
}
|
||||
|
||||
typedef struct GuDataIn GuDataIn;
|
||||
|
||||
struct GuDataIn {
|
||||
GuInStream stream;
|
||||
const uint8_t* data;
|
||||
size_t sz;
|
||||
};
|
||||
|
||||
static const uint8_t*
|
||||
gu_data_in_begin_buffer(GuInStream* self, size_t* sz_out, GuExn* err)
|
||||
{
|
||||
(void) err;
|
||||
GuDataIn* di = gu_container(self, GuDataIn, stream);
|
||||
const uint8_t* buf = di->data;
|
||||
if (buf) {
|
||||
*sz_out = di->sz;
|
||||
di->data = NULL;
|
||||
di->sz = 0;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
GuIn*
|
||||
gu_data_in(const uint8_t* data, size_t sz, GuPool* pool)
|
||||
{
|
||||
GuDataIn* di = gu_new_s(pool, GuDataIn,
|
||||
.stream.begin_buffer = gu_data_in_begin_buffer,
|
||||
.data = data,
|
||||
.sz = sz);
|
||||
return gu_new_in(&di->stream, pool);
|
||||
}
|
||||
|
||||
extern inline uint8_t
|
||||
gu_in_u8(GuIn* restrict in, GuExn* err);
|
||||
|
||||
extern inline void
|
||||
gu_in_bytes(GuIn* in, uint8_t* buf, size_t sz, GuExn* err);
|
||||
|
||||
extern inline int
|
||||
gu_in_peek_u8(GuIn* restrict in);
|
||||
|
||||
extern inline void
|
||||
gu_in_consume(GuIn* restrict in, size_t sz);
|
||||
146
src/runtime/c/gu/in.h
Normal file
146
src/runtime/c/gu/in.h
Normal file
@@ -0,0 +1,146 @@
|
||||
#ifndef GU_IN_H_
|
||||
#define GU_IN_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/exn.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
typedef struct GuInStream GuInStream;
|
||||
|
||||
struct GuInStream {
|
||||
const uint8_t* (*begin_buffer)(GuInStream* self, size_t* sz_out,
|
||||
GuExn* err);
|
||||
void (*end_buffer)(GuInStream* self, size_t consumed, GuExn* err);
|
||||
size_t (*input)(GuInStream* self, uint8_t* buf, size_t max_sz,
|
||||
GuExn* err);
|
||||
};
|
||||
|
||||
typedef struct GuIn GuIn;
|
||||
|
||||
struct GuIn {
|
||||
const uint8_t* restrict buf_end;
|
||||
ptrdiff_t buf_curr;
|
||||
size_t buf_size;
|
||||
GuInStream* stream;
|
||||
GuFinalizer fini;
|
||||
};
|
||||
|
||||
|
||||
GuIn
|
||||
gu_init_in(GuInStream* stream);
|
||||
|
||||
GuIn*
|
||||
gu_new_in(GuInStream* stream, GuPool* pool);
|
||||
|
||||
GuInStream*
|
||||
gu_in_proxy_stream(GuIn* in, GuPool* pool);
|
||||
|
||||
const uint8_t*
|
||||
gu_in_begin_span(GuIn* in, size_t *sz_out, GuExn* err);
|
||||
|
||||
void
|
||||
gu_in_end_span(GuIn* in, size_t consumed);
|
||||
|
||||
size_t
|
||||
gu_in_some(GuIn* in, uint8_t* buf, size_t max_len, GuExn* err);
|
||||
|
||||
inline void
|
||||
gu_in_bytes(GuIn* in, uint8_t* buf, size_t sz, GuExn* err)
|
||||
{
|
||||
gu_require(sz < PTRDIFF_MAX);
|
||||
ptrdiff_t curr = in->buf_curr;
|
||||
ptrdiff_t new_curr = curr + (ptrdiff_t) sz;
|
||||
if (GU_UNLIKELY(new_curr > 0)) {
|
||||
extern void gu_in_bytes_(GuIn* in, uint8_t* buf, size_t sz,
|
||||
GuExn* err);
|
||||
gu_in_bytes_(in, buf, sz, err);
|
||||
return;
|
||||
}
|
||||
memcpy(buf, &in->buf_end[curr], sz);
|
||||
in->buf_curr = new_curr;
|
||||
}
|
||||
|
||||
inline int
|
||||
gu_in_peek_u8(GuIn* restrict in)
|
||||
{
|
||||
if (GU_UNLIKELY(in->buf_curr == 0)) {
|
||||
return -1;
|
||||
}
|
||||
return in->buf_end[in->buf_curr];
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_in_consume(GuIn* restrict in, size_t sz)
|
||||
{
|
||||
gu_require((ptrdiff_t) sz + in->buf_curr <= 0);
|
||||
in->buf_curr += sz;
|
||||
}
|
||||
|
||||
|
||||
inline uint8_t
|
||||
gu_in_u8(GuIn* restrict in, GuExn* err)
|
||||
{
|
||||
if (GU_UNLIKELY(in->buf_curr == 0)) {
|
||||
extern uint8_t gu_in_u8_(GuIn* restrict in, GuExn* err);
|
||||
return gu_in_u8_(in, err);
|
||||
}
|
||||
return in->buf_end[in->buf_curr++];
|
||||
}
|
||||
|
||||
int8_t
|
||||
gu_in_s8(GuIn* in, GuExn* err);
|
||||
|
||||
uint16_t
|
||||
gu_in_u16le(GuIn* in, GuExn* err);
|
||||
|
||||
uint16_t
|
||||
gu_in_u16be(GuIn* in, GuExn* err);
|
||||
|
||||
int16_t
|
||||
gu_in_s16le(GuIn* in, GuExn* err);
|
||||
|
||||
int16_t
|
||||
gu_in_s16be(GuIn* in, GuExn* err);
|
||||
|
||||
uint32_t
|
||||
gu_in_u32le(GuIn* in, GuExn* err);
|
||||
|
||||
uint32_t
|
||||
gu_in_u32be(GuIn* in, GuExn* err);
|
||||
|
||||
int32_t
|
||||
gu_in_s32le(GuIn* in, GuExn* err);
|
||||
|
||||
int32_t
|
||||
gu_in_s32be(GuIn* in, GuExn* err);
|
||||
|
||||
uint64_t
|
||||
gu_in_u64le(GuIn* in, GuExn* err);
|
||||
|
||||
uint64_t
|
||||
gu_in_u64be(GuIn* in, GuExn* err);
|
||||
|
||||
int64_t
|
||||
gu_in_s64le(GuIn* in, GuExn* err);
|
||||
|
||||
int64_t
|
||||
gu_in_s64be(GuIn* in, GuExn* err);
|
||||
|
||||
double
|
||||
gu_in_f64le(GuIn* in, GuExn* err);
|
||||
|
||||
double
|
||||
gu_in_f64be(GuIn* in, GuExn* err);
|
||||
|
||||
GuIn*
|
||||
gu_buffered_in(GuIn* in, size_t sz, GuPool* pool);
|
||||
|
||||
GuIn*
|
||||
gu_data_in(const uint8_t* buf, size_t size, GuPool* pool);
|
||||
|
||||
|
||||
extern GU_DECLARE_TYPE(GuEOF, abstract);
|
||||
|
||||
#include <gu/type.h>
|
||||
|
||||
#endif // GU_IN_H_
|
||||
59
src/runtime/c/gu/intern.c
Normal file
59
src/runtime/c/gu/intern.c
Normal file
@@ -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;
|
||||
}
|
||||
24
src/runtime/c/gu/intern.h
Normal file
24
src/runtime/c/gu/intern.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef GU_INTERN_H_
|
||||
#define GU_INTERN_H_
|
||||
|
||||
#include <gu/map.h>
|
||||
#include <gu/str.h>
|
||||
#include <gu/string.h>
|
||||
|
||||
typedef struct GuIntern GuIntern;
|
||||
|
||||
GuIntern* gu_new_intern(GuPool* str_pool, GuPool* pool);
|
||||
const char* gu_intern_str(GuIntern* intern, const char* cstr);
|
||||
|
||||
|
||||
typedef struct GuSymTable GuSymTable;
|
||||
|
||||
typedef GuString GuSymbol;
|
||||
|
||||
GuSymTable*
|
||||
gu_new_symtable(GuPool* sym_pool, GuPool* pool);
|
||||
|
||||
GuSymbol
|
||||
gu_symtable_intern(GuSymTable* symtab, GuString string);
|
||||
|
||||
#endif /* GU_INTERN_H_ */
|
||||
59
src/runtime/c/gu/list.c
Normal file
59
src/runtime/c/gu/list.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <gu/list.h>
|
||||
#include <gu/assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static const int gu_list_empty = 0;
|
||||
|
||||
void* gu_list_alloc(GuPool* pool, size_t base_size, size_t elem_size,
|
||||
int n_elems, size_t alignment)
|
||||
{
|
||||
gu_assert(n_elems >= 0);
|
||||
if (n_elems == 0) {
|
||||
return (void*) &gu_list_empty;
|
||||
}
|
||||
// XXX: use gu_flex_size, use offset of elems
|
||||
void* p = gu_malloc_aligned(pool, base_size + elem_size * n_elems,
|
||||
alignment);
|
||||
*(int*) p = n_elems;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
GU_DEFINE_KIND(GuList, abstract);
|
||||
|
||||
// GU_DEFINE_TYPE(GuStrs, GuList, gu_type(GuStr));
|
||||
// GU_DEFINE_TYPE(GuStrsP, pointer, gu_type(GuStrs));
|
||||
|
||||
void*
|
||||
gu_list_type_alloc(GuListType* ltype, int n_elems, GuPool* pool)
|
||||
{
|
||||
return gu_list_alloc(pool, ltype->size,
|
||||
gu_type_size(ltype->elem_type),
|
||||
n_elems, ltype->align);
|
||||
}
|
||||
|
||||
void*
|
||||
gu_list_type_index(GuListType* ltype, void* list, int i)
|
||||
{
|
||||
uint8_t* p = list;
|
||||
return &p[ltype->elems_offset + i * gu_type_size(ltype->elem_type)];
|
||||
}
|
||||
140
src/runtime/c/gu/list.h
Normal file
140
src/runtime/c/gu/list.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Lists.
|
||||
*/
|
||||
|
||||
#ifndef GU_LIST_H_
|
||||
#define GU_LIST_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
|
||||
|
||||
#define GuList(t) \
|
||||
struct { \
|
||||
const int len; \
|
||||
t elems[]; \
|
||||
}
|
||||
|
||||
void* gu_list_alloc(GuPool* pool, size_t base_size, size_t elem_size,
|
||||
int n_elems, size_t alignment);
|
||||
|
||||
#define gu_new_list(t, pool, n) \
|
||||
((t*) gu_list_alloc(pool, \
|
||||
sizeof(t), \
|
||||
sizeof(((t*)NULL)->elems[0]), \
|
||||
(n), \
|
||||
gu_flex_alignof(t)))
|
||||
|
||||
static inline int
|
||||
gu_list_length(const void* list)
|
||||
{
|
||||
return *(const int*) list;
|
||||
}
|
||||
|
||||
#define gu_list_elems(lst) \
|
||||
((lst)->elems)
|
||||
|
||||
#define gu_list_index(lst, i) \
|
||||
(gu_list_elems(lst)[i])
|
||||
|
||||
typedef GuList(void*) GuPointers;
|
||||
//typedef GuList(uint8_t) GuBytes;
|
||||
|
||||
typedef GuList(int) GuInts;
|
||||
|
||||
|
||||
#define GuListN(t_, len_) \
|
||||
struct { \
|
||||
int len; \
|
||||
t elems[len_]; \
|
||||
}
|
||||
|
||||
#define gu_list_(qual_, t_, ...) \
|
||||
((qual_ GuList(t_) *) \
|
||||
((qual_ GuListN(t_, (sizeof((t_[]){__VA_ARGS__}) / sizeof(t_)))[]){ \
|
||||
__VA_ARGS__ \
|
||||
}))
|
||||
|
||||
#define gu_list(t_, ...) \
|
||||
gu_list_(, t_, __VA_ARGS__)
|
||||
|
||||
#define gu_clist(t_, ...) \
|
||||
gu_list_(const, t_, __VA_ARGS__)
|
||||
|
||||
#define GuSList(t) \
|
||||
const struct { \
|
||||
int len; \
|
||||
t* elems; \
|
||||
}
|
||||
|
||||
#define GU_SLIST_0 { .len = 0, .elems = NULL }
|
||||
|
||||
#define GU_SLIST(t, ...) \
|
||||
{ \
|
||||
.len = GU_ARRAY_LEN(t,GU_ID({__VA_ARGS__})), \
|
||||
.elems = ((t[]){__VA_ARGS__}) \
|
||||
}
|
||||
|
||||
|
||||
#include <gu/type.h>
|
||||
|
||||
//
|
||||
// list
|
||||
//
|
||||
|
||||
typedef const struct GuListType GuListType, GuType_GuList;
|
||||
|
||||
struct GuListType {
|
||||
GuType_abstract abstract_base;
|
||||
size_t size;
|
||||
size_t align;
|
||||
GuType* elem_type;
|
||||
ptrdiff_t elems_offset;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_GuList(k_, t_, elem_type_) { \
|
||||
.abstract_base = GU_TYPE_INIT_abstract(k_, t_, _), \
|
||||
.size = sizeof(t_), \
|
||||
.align = gu_alignof(t_), \
|
||||
.elem_type = elem_type_, \
|
||||
.elems_offset = offsetof(t_, elems) \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(GuList);
|
||||
|
||||
void*
|
||||
gu_list_type_alloc(GuListType* ltype, int n_elems, GuPool* pool);
|
||||
|
||||
void*
|
||||
gu_list_type_index(GuListType* ltype, void* list, int i);
|
||||
|
||||
#include <gu/str.h>
|
||||
|
||||
|
||||
typedef GuList(GuStr) GuStrs;
|
||||
typedef GuStrs* GuStrsP;
|
||||
|
||||
extern GU_DECLARE_TYPE(GuStrs, GuList);
|
||||
extern GU_DECLARE_TYPE(GuStrsP, pointer);
|
||||
|
||||
|
||||
#endif // GU_LIST_H_
|
||||
79
src/runtime/c/gu/log.c
Normal file
79
src/runtime/c/gu/log.c
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <gu/defs.h>
|
||||
#include <gu/log.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
static int gu_log_depth = 0;
|
||||
|
||||
static bool
|
||||
gu_log_match(const char* pat, size_t patlen, const char* str)
|
||||
{
|
||||
if (patlen > 0 && pat[patlen-1] == '*') {
|
||||
return strncmp(pat, str, patlen-1) == 0;
|
||||
} else if (strlen(str) == patlen) {
|
||||
return strncmp(pat, str, patlen) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_log_enabled(const char* func, const char* file)
|
||||
{
|
||||
const char* cfg = getenv("GU_LOG");
|
||||
if (cfg == NULL) {
|
||||
return false;
|
||||
}
|
||||
const char* p = cfg;
|
||||
while (true) {
|
||||
size_t len = strcspn(p, ",");
|
||||
if (gu_log_match(p, len, func)) {
|
||||
return true;
|
||||
}
|
||||
if (gu_log_match(p, len, file)) {
|
||||
return true;
|
||||
}
|
||||
if (p[len] == '\0') {
|
||||
break;
|
||||
}
|
||||
p = &p[len + 1];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_log_full_v(GuLogKind kind, const char* func, const char* file, int line,
|
||||
const char* fmt, va_list args)
|
||||
{
|
||||
(void) (kind && line);
|
||||
if (!gu_log_enabled(func, file)) {
|
||||
return;
|
||||
}
|
||||
if (kind == GU_LOG_KIND_EXIT) {
|
||||
gu_log_depth--;
|
||||
}
|
||||
if (fmt) {
|
||||
int indent = gu_min(32 + gu_log_depth, 48);
|
||||
fprintf(stderr, "%-*s: ", indent, func);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
}
|
||||
if (kind == GU_LOG_KIND_ENTER) {
|
||||
gu_log_depth++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gu_log_full(GuLogKind kind, const char* func, const char* file, int line,
|
||||
const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
gu_log_full_v(kind, func, file, line, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
65
src/runtime/c/gu/log.h
Normal file
65
src/runtime/c/gu/log.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef GU_LOG_H_
|
||||
#define GU_LOG_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef enum GuLogKind {
|
||||
GU_LOG_KIND_ENTER,
|
||||
GU_LOG_KIND_EXIT,
|
||||
GU_LOG_KIND_DEBUG,
|
||||
GU_LOG_KIND_ERROR
|
||||
} GuLogKind;
|
||||
|
||||
void
|
||||
gu_log_full(GuLogKind kind, const char* func, const char* file, int line,
|
||||
const char* fmt, ...);
|
||||
|
||||
|
||||
void
|
||||
gu_log_full_v(GuLogKind kind, const char* func, const char* file, int line,
|
||||
const char* fmt, va_list args);
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#define gu_logv(kind_, fmt_, args_) \
|
||||
gu_log_full_v(kind_, __func__, __FILE__, __LINE__, fmt_, args_)
|
||||
|
||||
#define gu_log(kind_, ...) \
|
||||
gu_log_full(kind_, __func__, __FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#else
|
||||
|
||||
static inline void
|
||||
gu_logv(GuLogKind kind, const char* fmt, va_list args)
|
||||
{
|
||||
(void) kind;
|
||||
(void) fmt;
|
||||
(void) args;
|
||||
}
|
||||
|
||||
static inline void
|
||||
gu_log(GuLogKind kind, const char* fmt, ...)
|
||||
{
|
||||
(void) kind;
|
||||
(void) fmt;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#define gu_enter(...) \
|
||||
gu_log(GU_LOG_KIND_ENTER, __VA_ARGS__)
|
||||
|
||||
#define gu_exit(...) \
|
||||
gu_log(GU_LOG_KIND_EXIT, __VA_ARGS__)
|
||||
|
||||
#define gu_debug(...) \
|
||||
gu_log(GU_LOG_KIND_DEBUG, __VA_ARGS__)
|
||||
|
||||
#define gu_debugv(kind_, fmt_, args_) \
|
||||
gu_logv(GU_LOG_KIND_DEBUG, fmt_, args_)
|
||||
|
||||
#endif // GU_LOG_H_
|
||||
353
src/runtime/c/gu/map.c
Normal file
353
src/runtime/c/gu/map.c
Normal file
@@ -0,0 +1,353 @@
|
||||
#include <gu/defs.h>
|
||||
#include <gu/mem.h>
|
||||
#include <gu/type.h>
|
||||
#include <gu/map.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/prime.h>
|
||||
#include <gu/log.h>
|
||||
|
||||
typedef enum {
|
||||
GU_MAP_GENERIC,
|
||||
GU_MAP_ADDR,
|
||||
GU_MAP_WORD
|
||||
} GuMapKind;
|
||||
|
||||
typedef struct GuMapData GuMapData;
|
||||
|
||||
struct GuMapData {
|
||||
uint8_t* keys;
|
||||
uint8_t* values;
|
||||
size_t n_occupied;
|
||||
size_t n_entries;
|
||||
size_t zero_idx;
|
||||
};
|
||||
|
||||
struct GuMap {
|
||||
GuMapKind const kind;
|
||||
GuHasher* const hasher;
|
||||
size_t const key_size;
|
||||
size_t const value_size;
|
||||
const void* default_value;
|
||||
GuMapData data;
|
||||
|
||||
GuFinalizer fin;
|
||||
};
|
||||
|
||||
static void
|
||||
gu_map_finalize(GuFinalizer* fin)
|
||||
{
|
||||
GuMap* map = gu_container(fin, GuMap, fin);
|
||||
gu_mem_buf_free(map->data.keys);
|
||||
if (map->value_size) {
|
||||
gu_mem_buf_free(map->data.values);
|
||||
}
|
||||
}
|
||||
|
||||
static const GuWord gu_map_empty_key = 0;
|
||||
|
||||
static bool
|
||||
gu_map_buf_is_zero(const uint8_t* p, size_t sz) {
|
||||
while (sz >= sizeof(GuWord)) {
|
||||
sz -= sizeof(GuWord);
|
||||
if (memcmp(&p[sz], &gu_map_empty_key, sizeof(GuWord)) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (memcmp(p, &gu_map_empty_key, sz) == 0);
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_map_entry_is_free(GuMap* map, GuMapData* data, size_t idx)
|
||||
{
|
||||
if (idx == data->zero_idx) {
|
||||
return false;
|
||||
} else if (map->kind == GU_MAP_ADDR) {
|
||||
const void* key = ((const void**)data->keys)[idx];
|
||||
return key == NULL;
|
||||
} else if (map->kind == GU_MAP_WORD) {
|
||||
GuWord key = ((GuWord*)data->keys)[idx];
|
||||
return key == 0;
|
||||
}
|
||||
gu_assert(map->kind == GU_MAP_GENERIC);
|
||||
const void* key = &data->keys[idx * map->key_size];
|
||||
return gu_map_buf_is_zero(key, map->key_size);
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_map_lookup(GuMap* map, const void* key, size_t* idx_out)
|
||||
{
|
||||
size_t n = map->data.n_entries;
|
||||
switch (map->kind) {
|
||||
case GU_MAP_GENERIC: {
|
||||
GuHasher* hasher = map->hasher;
|
||||
GuEquality* eq = (GuEquality*) hasher;
|
||||
GuHash hash = hasher->hash(hasher, key);
|
||||
size_t idx = hash % n;
|
||||
size_t offset = (hash % (n - 2)) + 1;
|
||||
size_t key_size = map->key_size;
|
||||
while (true) {
|
||||
void* entry_key = &map->data.keys[idx * key_size];
|
||||
if (gu_map_buf_is_zero(entry_key, key_size) &&
|
||||
map->data.zero_idx != idx) {
|
||||
*idx_out = idx;
|
||||
return false;
|
||||
} else if (eq->is_equal(eq, key, entry_key)) {
|
||||
*idx_out = idx;
|
||||
return true;
|
||||
}
|
||||
idx = (idx + offset) % n;
|
||||
}
|
||||
gu_impossible();
|
||||
break;
|
||||
}
|
||||
case GU_MAP_ADDR: {
|
||||
GuHash hash = (GuHash) key;
|
||||
size_t idx = hash % n;
|
||||
size_t offset = (hash % (n - 2)) + 1;
|
||||
while (true) {
|
||||
const void* entry_key =
|
||||
((const void**)map->data.keys)[idx];
|
||||
if (entry_key == NULL && map->data.zero_idx != idx) {
|
||||
*idx_out = idx;
|
||||
return false;
|
||||
} else if (entry_key == key) {
|
||||
*idx_out = idx;
|
||||
return true;
|
||||
}
|
||||
idx = (idx + offset) % n;
|
||||
}
|
||||
gu_impossible();
|
||||
break;
|
||||
}
|
||||
case GU_MAP_WORD: {
|
||||
GuWord w = *(const GuWord*)key;
|
||||
GuHash hash = (GuHash) w;
|
||||
size_t idx = hash % n;
|
||||
size_t offset = (hash % (n - 2)) + 1;
|
||||
while (true) {
|
||||
GuWord entry_key = ((GuWord*)map->data.keys)[idx];
|
||||
if (entry_key == 0 && map->data.zero_idx != idx) {
|
||||
*idx_out = idx;
|
||||
return false;
|
||||
} else if (entry_key == w) {
|
||||
*idx_out = idx;
|
||||
return true;
|
||||
}
|
||||
idx = (idx + offset) % n;
|
||||
}
|
||||
gu_impossible();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
gu_impossible();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_map_resize(GuMap* map)
|
||||
{
|
||||
GuMapData* data = &map->data;
|
||||
GuMapData old_data = *data;
|
||||
size_t req_entries =
|
||||
gu_twin_prime_sup(GU_MAX(11, map->data.n_occupied * 4 / 3 + 1));
|
||||
|
||||
size_t key_size = map->key_size;
|
||||
size_t key_alloc = 0;
|
||||
data->keys = gu_mem_buf_alloc(req_entries * key_size, &key_alloc);
|
||||
|
||||
size_t value_size = map->value_size;
|
||||
size_t value_alloc = 0;
|
||||
if (value_size) {
|
||||
data->values = gu_mem_buf_alloc(req_entries * value_size,
|
||||
&value_alloc);
|
||||
memset(data->values, 0, value_alloc);
|
||||
}
|
||||
|
||||
data->n_entries = gu_twin_prime_inf(value_size ?
|
||||
GU_MIN(key_alloc / key_size,
|
||||
value_alloc / value_size)
|
||||
: key_alloc / key_size);
|
||||
switch (map->kind) {
|
||||
case GU_MAP_GENERIC:
|
||||
case GU_MAP_WORD:
|
||||
memset(data->keys, 0, key_alloc);
|
||||
break;
|
||||
case GU_MAP_ADDR:
|
||||
for (size_t i = 0; i < data->n_entries; i++) {
|
||||
((const void**)data->keys)[i] = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
|
||||
gu_assert(data->n_entries > data->n_occupied);
|
||||
gu_debug("Resized to %d entries", data->n_entries);
|
||||
|
||||
data->n_occupied = 0;
|
||||
data->zero_idx = SIZE_MAX;
|
||||
|
||||
for (size_t i = 0; i < old_data.n_entries; i++) {
|
||||
if (gu_map_entry_is_free(map, &old_data, i)) {
|
||||
continue;
|
||||
}
|
||||
void* old_key = &old_data.keys[i * key_size];
|
||||
if (map->kind == GU_MAP_ADDR) {
|
||||
old_key = *(void**)old_key;
|
||||
}
|
||||
void* old_value = &old_data.values[i * value_size];
|
||||
|
||||
memcpy(gu_map_insert(map, old_key),
|
||||
old_value, map->value_size);
|
||||
}
|
||||
|
||||
gu_mem_buf_free(old_data.keys);
|
||||
if (value_size) {
|
||||
gu_mem_buf_free(old_data.values);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
gu_map_maybe_resize(GuMap* map)
|
||||
{
|
||||
if (map->data.n_entries <=
|
||||
map->data.n_occupied + (map->data.n_occupied / 4)) {
|
||||
gu_map_resize(map);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_map_find(GuMap* map, const void* key)
|
||||
{
|
||||
size_t idx;
|
||||
bool found = gu_map_lookup(map, key, &idx);
|
||||
if (found) {
|
||||
return &map->data.values[idx * map->value_size];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const void*
|
||||
gu_map_find_default(GuMap* map, const void* key)
|
||||
{
|
||||
void* p = gu_map_find(map, key);
|
||||
return p ? p : map->default_value;
|
||||
}
|
||||
|
||||
const void*
|
||||
gu_map_find_key(GuMap* map, const void* key)
|
||||
{
|
||||
size_t idx;
|
||||
bool found = gu_map_lookup(map, key, &idx);
|
||||
if (found) {
|
||||
return &map->data.keys[idx * map->key_size];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
gu_map_insert(GuMap* map, const void* key)
|
||||
{
|
||||
size_t idx;
|
||||
bool found = gu_map_lookup(map, key, &idx);
|
||||
if (!found) {
|
||||
if (gu_map_maybe_resize(map)) {
|
||||
found = gu_map_lookup(map, key, &idx);
|
||||
gu_assert(!found);
|
||||
}
|
||||
if (map->kind == GU_MAP_ADDR) {
|
||||
((const void**)map->data.keys)[idx] = key;
|
||||
} else {
|
||||
memcpy(&map->data.keys[idx * map->key_size],
|
||||
key, map->key_size);
|
||||
}
|
||||
if (map->default_value) {
|
||||
memcpy(&map->data.values[idx * map->value_size],
|
||||
map->default_value, map->value_size);
|
||||
}
|
||||
if (gu_map_entry_is_free(map, &map->data, idx)) {
|
||||
gu_assert(map->data.zero_idx == SIZE_MAX);
|
||||
map->data.zero_idx = idx;
|
||||
}
|
||||
map->data.n_occupied++;
|
||||
}
|
||||
return &map->data.values[idx * map->value_size];
|
||||
}
|
||||
|
||||
void
|
||||
gu_map_iter(GuMap* map, GuMapItor* itor, GuExn* err)
|
||||
{
|
||||
for (size_t i = 0; i < map->data.n_entries && gu_ok(err); i++) {
|
||||
if (gu_map_entry_is_free(map, &map->data, i)) {
|
||||
continue;
|
||||
}
|
||||
const void* key = &map->data.keys[i * map->key_size];
|
||||
void* value = &map->data.values[i * map->value_size];
|
||||
if (map->kind == GU_MAP_ADDR) {
|
||||
key = *(const void* const*) key;
|
||||
}
|
||||
itor->fn(itor, key, value, err);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t gu_map_no_values[1] = { 0 };
|
||||
|
||||
GuMap*
|
||||
gu_make_map(size_t key_size, GuHasher* hasher,
|
||||
size_t value_size, const void* default_value,
|
||||
GuPool* pool)
|
||||
{
|
||||
GuMapKind kind =
|
||||
((!hasher || hasher == gu_addr_hasher)
|
||||
? GU_MAP_ADDR
|
||||
: (key_size == sizeof(GuWord) && hasher == gu_word_hasher)
|
||||
? GU_MAP_WORD
|
||||
: GU_MAP_GENERIC);
|
||||
if (kind == GU_MAP_ADDR) {
|
||||
key_size = sizeof(GuWord);
|
||||
}
|
||||
GuMapData data = {
|
||||
.n_occupied = 0,
|
||||
.n_entries = 0,
|
||||
.keys = NULL,
|
||||
.values = value_size ? NULL : (uint8_t*) gu_map_no_values,
|
||||
.zero_idx = SIZE_MAX
|
||||
};
|
||||
GuMap* map = gu_new_i(
|
||||
pool, GuMap,
|
||||
.default_value = default_value,
|
||||
.hasher = hasher,
|
||||
.data = data,
|
||||
.key_size = key_size,
|
||||
.value_size = value_size,
|
||||
.fin.fn = gu_map_finalize,
|
||||
.kind = kind
|
||||
);
|
||||
gu_pool_finally(pool, &map->fin);
|
||||
gu_map_resize(map);
|
||||
return map;
|
||||
}
|
||||
|
||||
GuMap*
|
||||
gu_map_type_make(GuMapType* mtype, GuPool* pool)
|
||||
{
|
||||
size_t key_size = 0;
|
||||
if (mtype->hasher && mtype->hasher != gu_addr_hasher) {
|
||||
key_size = gu_type_size(mtype->key_type);
|
||||
}
|
||||
size_t value_size = gu_type_size(mtype->value_type);
|
||||
return gu_make_map(key_size, mtype->hasher,
|
||||
value_size, mtype->default_value, pool);
|
||||
}
|
||||
|
||||
GU_DEFINE_KIND(GuMap, abstract);
|
||||
// GU_DEFINE_KIND(GuIntMap, GuMap);
|
||||
|
||||
|
||||
121
src/runtime/c/gu/map.h
Normal file
121
src/runtime/c/gu/map.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#ifndef GU_MAP_H_
|
||||
#define GU_MAP_H_
|
||||
|
||||
#include <gu/hash.h>
|
||||
#include <gu/mem.h>
|
||||
#include <gu/exn.h>
|
||||
|
||||
typedef const struct GuMapItor GuMapItor;
|
||||
|
||||
struct GuMapItor {
|
||||
void (*fn)(GuMapItor* self, const void* key, void* value,
|
||||
GuExn *err);
|
||||
};
|
||||
|
||||
typedef struct GuMap GuMap;
|
||||
|
||||
GuMap*
|
||||
gu_make_map(size_t key_size, GuHasher* hasher,
|
||||
size_t value_size, const void* default_value,
|
||||
GuPool* pool);
|
||||
|
||||
#define gu_new_map(K, HASHER, V, DV, POOL) \
|
||||
(gu_make_map(sizeof(K), (HASHER), sizeof(V), (DV), (POOL)))
|
||||
|
||||
#define gu_new_set(K, HASHER, POOL) \
|
||||
(gu_make_map(sizeof(K), (HASHER), 0, NULL, (POOL)))
|
||||
|
||||
#define gu_new_addr_map(K, V, DV, POOL) \
|
||||
(gu_make_map(0, NULL, sizeof(V), (DV), (POOL)))
|
||||
|
||||
size_t
|
||||
gu_map_count(GuMap* map);
|
||||
|
||||
void*
|
||||
gu_map_find_full(GuMap* ht, void* key_inout);
|
||||
|
||||
const void*
|
||||
gu_map_find_default(GuMap* ht, const void* key);
|
||||
|
||||
#define gu_map_get(MAP, KEYP, V) \
|
||||
(*(V*)gu_map_find_default((MAP), (KEYP)))
|
||||
|
||||
void*
|
||||
gu_map_find(GuMap* ht, const void* key);
|
||||
|
||||
#define gu_map_set(MAP, KEYP, V, VAL) \
|
||||
GU_BEGIN \
|
||||
V* gu_map_set_p_ = gu_map_find((MAP), (KEYP)); \
|
||||
*gu_map_set_p_ = (VAL); \
|
||||
GU_END
|
||||
|
||||
const void*
|
||||
gu_map_find_key(GuMap* ht, const void* key);
|
||||
|
||||
static inline bool
|
||||
gu_map_has(GuMap* ht, const void* key)
|
||||
{
|
||||
return gu_map_find_key(ht, key) != NULL;
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
gu_map_insert(GuMap* ht, const void* key);
|
||||
|
||||
#define gu_map_put(MAP, KEYP, V, VAL) \
|
||||
GU_BEGIN \
|
||||
V* gu_map_put_p_ = gu_map_insert((MAP), (KEYP)); \
|
||||
*gu_map_put_p_ = (VAL); \
|
||||
GU_END
|
||||
|
||||
void
|
||||
gu_map_iter(GuMap* ht, GuMapItor* itor, GuExn* err);
|
||||
|
||||
|
||||
typedef GuMap GuIntMap;
|
||||
|
||||
#define gu_new_int_map(VAL_T, DEFAULT, POOL) \
|
||||
gu_new_map(int, gu_int_hasher, VAL_T, DEFAULT, POOL)
|
||||
|
||||
|
||||
#if defined(GU_TYPE_H_) && !defined(GU_MAP_H_TYPE_)
|
||||
#define GU_MAP_H_TYPE_
|
||||
|
||||
extern GU_DECLARE_KIND(GuMap);
|
||||
|
||||
typedef const struct GuMapType GuMapType, GuType_GuMap;
|
||||
|
||||
struct GuMapType {
|
||||
GuType_abstract abstract_base;
|
||||
GuHasher* hasher;
|
||||
GuType* key_type;
|
||||
GuType* value_type;
|
||||
const void* default_value;
|
||||
};
|
||||
|
||||
GuMap*
|
||||
gu_map_type_make(GuMapType* mtype, GuPool* pool);
|
||||
|
||||
#define gu_map_type_new(MAP_T, POOL) \
|
||||
gu_map_type_make(gu_type_cast(gu_type(MAP_T), GuMap), (POOL))
|
||||
|
||||
#define GU_TYPE_INIT_GuMap(k_, t_, kt_, h_, vt_, dv_) \
|
||||
{ \
|
||||
.abstract_base = GU_TYPE_INIT_abstract(k_, t_, _), \
|
||||
.hasher = h_, \
|
||||
.key_type = kt_, \
|
||||
.value_type = vt_, \
|
||||
.default_value = dv_ \
|
||||
}
|
||||
|
||||
#define gu_type__GuIntMap gu_type__GuMap
|
||||
|
||||
typedef GuType_GuMap GuType_GuIntMap;
|
||||
|
||||
#define GU_TYPE_INIT_GuIntMap(KIND, MAP_T, VAL_T, DEFAULT) \
|
||||
GU_TYPE_INIT_GuMap(KIND, MAP_T, gu_type(int), gu_int_hasher, \
|
||||
VAL_T, DEFAULT)
|
||||
|
||||
#endif
|
||||
|
||||
#endif // GU_MAP_H_
|
||||
346
src/runtime/c/gu/mem.c
Normal file
346
src/runtime/c/gu/mem.c
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/fun.h>
|
||||
#include <gu/bits.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/log.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef USE_VALGRIND
|
||||
#include <valgrind/valgrind.h>
|
||||
#define VG(X) X
|
||||
#else
|
||||
#define VG(X) GU_NOP
|
||||
#endif
|
||||
|
||||
static const size_t
|
||||
// Maximum request size for a chunk. The actual maximum chunk size
|
||||
// may be somewhat larger.
|
||||
gu_mem_chunk_max_size = 1024 * sizeof(void*),
|
||||
|
||||
// number of bytes to allocate in the pool when it is created
|
||||
gu_mem_pool_initial_size = 24 * sizeof(void*),
|
||||
|
||||
// Pool allocations larger than this will get their own chunk if
|
||||
// there's no room in the current one. Allocations smaller than this may trigger
|
||||
// the creation of a new chunk, in which case the remaining space in
|
||||
// the current chunk is left unused (internal fragmentation).
|
||||
gu_mem_max_shared_alloc = 64 * sizeof(void*),
|
||||
|
||||
// Should not be smaller than the granularity for malloc
|
||||
gu_mem_unit_size = 2 * sizeof(void*),
|
||||
|
||||
/* Malloc tuning: the additional memory used by malloc next to the
|
||||
allocated object */
|
||||
gu_malloc_overhead = sizeof(size_t);
|
||||
|
||||
static void*
|
||||
gu_mem_realloc(void* p, size_t size)
|
||||
{
|
||||
void* buf = realloc(p, size);
|
||||
if (size != 0 && buf == NULL) {
|
||||
gu_fatal("Memory allocation failed");
|
||||
}
|
||||
gu_debug("%p %zu -> %p", p, size, buf); // strictly illegal
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void*
|
||||
gu_mem_alloc(size_t size)
|
||||
{
|
||||
void* buf = malloc(size);
|
||||
if (buf == NULL) {
|
||||
gu_fatal("Memory allocation failed");
|
||||
}
|
||||
gu_debug("%zu -> %p", size, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_mem_free(void* p)
|
||||
{
|
||||
gu_debug("%p", p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_mem_padovan(size_t min)
|
||||
{
|
||||
// This could in principle be done faster with Q-matrices for
|
||||
// Padovan numbers, but not really worth it for our commonly
|
||||
// small numbers.
|
||||
if (min <= 5) {
|
||||
return min;
|
||||
}
|
||||
size_t a = 7, b = 9, c = 12;
|
||||
while (min > a) {
|
||||
if (b < a) {
|
||||
// overflow
|
||||
return SIZE_MAX;
|
||||
}
|
||||
size_t tmp = a + b;
|
||||
a = b;
|
||||
b = c;
|
||||
c = tmp;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_mem_buf_realloc(void* old_buf, size_t min_size, size_t* real_size_out)
|
||||
{
|
||||
size_t min_blocks = ((min_size + gu_malloc_overhead - 1) /
|
||||
gu_mem_unit_size) + 1;
|
||||
size_t blocks = gu_mem_padovan(min_blocks);
|
||||
size_t size = blocks * gu_mem_unit_size - gu_malloc_overhead;
|
||||
void* buf = gu_mem_realloc(old_buf, size);
|
||||
*real_size_out = buf ? size : 0;
|
||||
return buf;
|
||||
}
|
||||
void*
|
||||
gu_mem_buf_alloc(size_t min_size, size_t* real_size_out)
|
||||
{
|
||||
return gu_mem_buf_realloc(NULL, min_size, real_size_out);
|
||||
}
|
||||
|
||||
void
|
||||
gu_mem_buf_free(void* buf)
|
||||
{
|
||||
gu_mem_free(buf);
|
||||
}
|
||||
|
||||
|
||||
typedef struct GuMemChunk GuMemChunk;
|
||||
|
||||
struct GuMemChunk {
|
||||
GuMemChunk* next;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
typedef struct GuFinalizerNode GuFinalizerNode;
|
||||
|
||||
struct GuFinalizerNode {
|
||||
GuFinalizerNode* next;
|
||||
GuFinalizer* fin;
|
||||
};
|
||||
|
||||
enum GuPoolFlags {
|
||||
GU_POOL_LOCAL = 1 << 0
|
||||
};
|
||||
|
||||
struct GuPool {
|
||||
uint8_t* curr_buf; // actually GuMemChunk*
|
||||
GuMemChunk* chunks;
|
||||
GuFinalizerNode* finalizers;
|
||||
uint16_t flags;
|
||||
uint16_t left_edge;
|
||||
uint16_t right_edge;
|
||||
uint16_t curr_size;
|
||||
uint8_t init_buf[];
|
||||
};
|
||||
|
||||
static GuPool*
|
||||
gu_init_pool(uint8_t* buf, size_t sz)
|
||||
{
|
||||
gu_require(gu_aligned((uintptr_t) (void*) buf, gu_alignof(GuPool)));
|
||||
gu_require(sz >= sizeof(GuPool));
|
||||
GuPool* pool = (GuPool*) buf;
|
||||
pool->flags = 0;
|
||||
pool->curr_size = sz;
|
||||
pool->curr_buf = (uint8_t*) pool;
|
||||
pool->chunks = NULL;
|
||||
pool->finalizers = NULL;
|
||||
pool->left_edge = offsetof(GuPool, init_buf);
|
||||
pool->right_edge = sz;
|
||||
VG(VALGRIND_CREATE_MEMPOOL(pool, 0, false));
|
||||
return pool;
|
||||
}
|
||||
|
||||
GuPool*
|
||||
gu_local_pool_(uint8_t* buf, size_t sz)
|
||||
{
|
||||
GuPool* pool = gu_init_pool(buf, sz);
|
||||
pool->flags |= GU_POOL_LOCAL;
|
||||
gu_debug("%p", pool);
|
||||
return pool;
|
||||
}
|
||||
|
||||
GuPool*
|
||||
gu_new_pool(void)
|
||||
{
|
||||
size_t sz = GU_FLEX_SIZE(GuPool, init_buf, gu_mem_pool_initial_size);
|
||||
uint8_t* buf = gu_mem_buf_alloc(sz, &sz);
|
||||
GuPool* pool = gu_init_pool(buf, sz);
|
||||
gu_debug("%p", pool);
|
||||
return pool;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_pool_expand(GuPool* pool, size_t req)
|
||||
{
|
||||
size_t real_req = GU_MAX(req, GU_MIN(((size_t)pool->curr_size) + 1,
|
||||
gu_mem_chunk_max_size));
|
||||
gu_assert(real_req >= sizeof(GuMemChunk));
|
||||
size_t size = 0;
|
||||
GuMemChunk* chunk = gu_mem_buf_alloc(real_req, &size);
|
||||
chunk->next = pool->chunks;
|
||||
pool->chunks = chunk;
|
||||
pool->curr_buf = (uint8_t*) chunk;
|
||||
pool->left_edge = offsetof(GuMemChunk, data);
|
||||
pool->right_edge = pool->curr_size = size;
|
||||
// size should always fit in uint16_t
|
||||
gu_assert((size_t) pool->right_edge == size);
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_mem_advance(size_t old_pos, size_t pre_align, size_t pre_size,
|
||||
size_t align, size_t size)
|
||||
{
|
||||
size_t p = gu_align_forward(old_pos, pre_align);
|
||||
p += pre_size;
|
||||
p = gu_align_forward(p, align);
|
||||
p += size;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void*
|
||||
gu_pool_malloc_aligned(GuPool* pool, size_t pre_align, size_t pre_size,
|
||||
size_t align, size_t size)
|
||||
{
|
||||
gu_require(size <= gu_mem_max_shared_alloc);
|
||||
size_t pos = gu_mem_advance(pool->left_edge, pre_align, pre_size,
|
||||
align, size);
|
||||
if (pos > (size_t) pool->right_edge) {
|
||||
pos = gu_mem_advance(offsetof(GuMemChunk, data),
|
||||
pre_align, pre_size, align, size);
|
||||
gu_pool_expand(pool, pos);
|
||||
gu_assert(pos <= pool->right_edge);
|
||||
}
|
||||
pool->left_edge = pos;
|
||||
uint8_t* addr = &pool->curr_buf[pos - size];
|
||||
VG(VALGRIND_MEMPOOL_ALLOC(pool, addr - pre_size, size + pre_size ));
|
||||
return addr;
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_pool_avail(GuPool* pool)
|
||||
{
|
||||
return (size_t) pool->right_edge - (size_t) pool->left_edge;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_pool_malloc_unaligned(GuPool* pool, size_t size)
|
||||
{
|
||||
if (size > gu_pool_avail(pool)) {
|
||||
gu_pool_expand(pool, offsetof(GuMemChunk, data) + size);
|
||||
gu_assert(size <= gu_pool_avail(pool));
|
||||
}
|
||||
pool->right_edge -= size;
|
||||
void* addr = &pool->curr_buf[pool->right_edge];
|
||||
VG(VALGRIND_MEMPOOL_ALLOC(pool, addr, size));
|
||||
return addr;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_malloc_prefixed(GuPool* pool, size_t pre_align, size_t pre_size,
|
||||
size_t align, size_t size)
|
||||
{
|
||||
gu_enter("-> %p %zu %zu %zu %zu",
|
||||
pool, pre_align, pre_size, align, size);
|
||||
void* ret = NULL;
|
||||
if (pre_align == 0) {
|
||||
pre_align = gu_alignof(GuMaxAlign);
|
||||
}
|
||||
if (align == 0) {
|
||||
align = gu_alignof(GuMaxAlign);
|
||||
}
|
||||
size_t full_size = gu_mem_advance(offsetof(GuMemChunk, data),
|
||||
pre_align, pre_size, align, size);
|
||||
if (full_size > gu_mem_max_shared_alloc) {
|
||||
GuMemChunk* chunk = gu_mem_alloc(full_size);
|
||||
chunk->next = pool->chunks;
|
||||
pool->chunks = chunk;
|
||||
uint8_t* addr = &chunk->data[full_size - size
|
||||
- offsetof(GuMemChunk, data)];
|
||||
VG(VALGRIND_MEMPOOL_ALLOC(pool, addr - pre_size,
|
||||
pre_size + size));
|
||||
ret = addr;
|
||||
} else if (pre_align == 1 && align == 1) {
|
||||
uint8_t* buf = gu_pool_malloc_unaligned(pool, pre_size + size);
|
||||
ret = &buf[pre_size];
|
||||
} else {
|
||||
ret = gu_pool_malloc_aligned(pool, pre_align, pre_size,
|
||||
align, size);
|
||||
}
|
||||
gu_exit("<- %p", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_malloc_aligned(GuPool* pool, size_t size, size_t align)
|
||||
{
|
||||
if (align == 0) {
|
||||
align = GU_MIN(size, gu_alignof(GuMaxAlign));
|
||||
}
|
||||
void* ret = gu_malloc_prefixed(pool, 1, 0, align, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_pool_finally(GuPool* pool, GuFinalizer* finalizer)
|
||||
{
|
||||
GuFinalizerNode* node = gu_new(GuFinalizerNode, pool);
|
||||
node->next = pool->finalizers;
|
||||
node->fin = finalizer;
|
||||
pool->finalizers = node;
|
||||
}
|
||||
|
||||
void
|
||||
gu_pool_free(GuPool* pool)
|
||||
{
|
||||
gu_debug("%p", pool);
|
||||
GuFinalizerNode* node = pool->finalizers;
|
||||
while (node) {
|
||||
GuFinalizerNode* next = node->next;
|
||||
node->fin->fn(node->fin);
|
||||
node = next;
|
||||
}
|
||||
GuMemChunk* chunk = pool->chunks;
|
||||
while (chunk) {
|
||||
GuMemChunk* next = chunk->next;
|
||||
gu_mem_buf_free(chunk);
|
||||
chunk = next;
|
||||
}
|
||||
VG(VALGRIND_DESTROY_MEMPOOL(pool));
|
||||
if (!pool->flags & GU_POOL_LOCAL) {
|
||||
gu_mem_buf_free(pool);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern inline void* gu_malloc(GuPool* pool, size_t size);
|
||||
|
||||
extern inline void*
|
||||
gu_malloc_init_aligned(GuPool* pool, size_t size, size_t alignment,
|
||||
const void* init);
|
||||
|
||||
276
src/runtime/c/gu/mem.h
Normal file
276
src/runtime/c/gu/mem.h
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Memory allocation tools.
|
||||
*/
|
||||
|
||||
#ifndef GU_MEM_H_
|
||||
#define GU_MEM_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/fun.h>
|
||||
|
||||
/** @defgroup GuPool Memory pools */
|
||||
//@{
|
||||
|
||||
|
||||
/// A memory pool.
|
||||
typedef struct GuPool GuPool;
|
||||
|
||||
/// @name Creating a pool
|
||||
//@{
|
||||
|
||||
|
||||
/// Create a new memory pool.
|
||||
GU_ONLY GuPool*
|
||||
gu_new_pool(void);
|
||||
|
||||
/**<
|
||||
* @return A new memory pool.
|
||||
*/
|
||||
|
||||
|
||||
//@private
|
||||
GuPool*
|
||||
gu_local_pool_(uint8_t* init_buf, size_t sz);
|
||||
|
||||
//@private
|
||||
#define GU_LOCAL_POOL_INIT_SIZE (16 * sizeof(GuWord))
|
||||
|
||||
|
||||
/// Create a stack-allocated memory pool.
|
||||
#define gu_local_pool() \
|
||||
gu_local_pool_(gu_alloca(GU_LOCAL_POOL_INIT_SIZE), \
|
||||
GU_LOCAL_POOL_INIT_SIZE)
|
||||
/**<
|
||||
* @return A memory pool whose first chunk is allocated directly from
|
||||
* the stack. This makes its creation faster, and more suitable for
|
||||
* functions that usually allocate only a little memory from the pool
|
||||
* until it is freed.
|
||||
*
|
||||
* @note The pool created with #gu_local_pool \e must be freed with
|
||||
* #gu_pool_free before the end of the block where #gu_local_pool was
|
||||
* called.
|
||||
*
|
||||
* @note Because #gu_local_pool uses relatively much stack space, it
|
||||
* should not be used in the bodies of recursive functions.
|
||||
*/
|
||||
|
||||
|
||||
//@}
|
||||
/// @name Destroying a pool
|
||||
//@{
|
||||
|
||||
|
||||
/// Free a memory pool and all objects allocated from it.
|
||||
void
|
||||
gu_pool_free(GU_ONLY GuPool* pool);
|
||||
/**<
|
||||
* When the pool is freed, all finalizers registered by
|
||||
* #gu_pool_finally on \p pool are invoked in reverse order of
|
||||
* registration.
|
||||
*
|
||||
* @note After the pool is freed, all objects allocated from it become
|
||||
* invalid and may no longer be used. */
|
||||
|
||||
//@}
|
||||
/// @name Allocating from a pool
|
||||
//@{
|
||||
|
||||
|
||||
/// Allocate memory with a specified alignment.
|
||||
void*
|
||||
gu_malloc_aligned(GuPool* pool, size_t size, size_t alignment);
|
||||
|
||||
void*
|
||||
gu_malloc_prefixed(GuPool* pool, size_t pre_align, size_t pre_size,
|
||||
size_t align, size_t size);
|
||||
|
||||
/// Allocate memory from a pool.
|
||||
inline void*
|
||||
gu_malloc(GuPool* pool, size_t size) {
|
||||
return gu_malloc_aligned(pool, size, 0);
|
||||
}
|
||||
|
||||
#include <string.h>
|
||||
|
||||
//@private
|
||||
static inline void*
|
||||
gu_malloc_init_aligned(GuPool* pool, size_t size, size_t alignment,
|
||||
const void* init)
|
||||
{
|
||||
void* p = gu_malloc_aligned(pool, size, alignment);
|
||||
memcpy(p, init, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
//@private
|
||||
static inline void*
|
||||
gu_malloc_init(GuPool* pool, size_t size, const void* init)
|
||||
{
|
||||
return gu_malloc_init_aligned(pool, size, 0, init);
|
||||
}
|
||||
|
||||
|
||||
/** Allocate memory to store an array of objects of a given type. */
|
||||
|
||||
#define gu_new_n(type, n, pool) \
|
||||
((type*)gu_malloc_aligned((pool), \
|
||||
sizeof(type) * (n), \
|
||||
gu_alignof(type)))
|
||||
/**<
|
||||
* @param type The C type of the objects to allocate.
|
||||
*
|
||||
* @param n The number of objects to allocate.
|
||||
*
|
||||
* @param pool The memory pool to allocate from.
|
||||
*
|
||||
* @return A pointer to a heap-allocated array of \p n uninitialized
|
||||
* objects of type \p type.
|
||||
*/
|
||||
|
||||
|
||||
/** Allocate memory to store an object of a given type. */
|
||||
|
||||
#define gu_new(type, pool) \
|
||||
gu_new_n(type, 1, pool)
|
||||
/**<
|
||||
* @param type The C type of the object to allocate.
|
||||
*
|
||||
* @param pool The memory pool to allocate from.
|
||||
*
|
||||
* @return A pointer to a heap-allocated uninitialized object of type
|
||||
* \p type.
|
||||
*/
|
||||
|
||||
|
||||
#define gu_new_prefixed(pre_type, type, pool) \
|
||||
((type*)(gu_malloc_prefixed((pool), \
|
||||
gu_alignof(pre_type), sizeof(pre_type), \
|
||||
gu_alignof(type), sizeof(type))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef GU_HAVE_STATEMENT_EXPRESSIONS
|
||||
#define gu_new_i(pool, type, ...) \
|
||||
({ \
|
||||
type *gu_new_p_ = gu_new(type, pool); \
|
||||
memcpy((void*) gu_new_p_, &(type){ __VA_ARGS__ }, \
|
||||
sizeof(type)); \
|
||||
gu_new_p_; \
|
||||
})
|
||||
#else // GU_HAVE_STATEMENT_EXPRESSIONS
|
||||
#define gu_new_i(pool, type, ...) \
|
||||
((type*)gu_malloc_init_aligned((pool), sizeof(type), \
|
||||
gu_alignof(type), \
|
||||
&(type){ __VA_ARGS__ }))
|
||||
#endif // GU_HAVE_STATEMENT_EXPRESSIONS
|
||||
|
||||
/** @def gu_new_i(pool, type, ...)
|
||||
*
|
||||
* Allocate and initialize an object.
|
||||
*
|
||||
* @param pool The pool to allocate from.
|
||||
*
|
||||
* @param type The C type of the object to allocate.
|
||||
*
|
||||
* @param ... An initializer list for the object to allocate.
|
||||
*/
|
||||
|
||||
#define gu_new_s gu_new_i
|
||||
|
||||
// Alas, there's no portable way to get the alignment of flex structs.
|
||||
#define gu_new_flex(pool_, type_, flex_member_, n_elems_) \
|
||||
((type_ *)gu_malloc_aligned( \
|
||||
(pool_), \
|
||||
GU_FLEX_SIZE(type_, flex_member_, n_elems_), \
|
||||
gu_flex_alignof(type_)))
|
||||
|
||||
|
||||
//@}
|
||||
/// @name Finalizers
|
||||
//@{
|
||||
|
||||
|
||||
typedef struct GuFinalizer GuFinalizer;
|
||||
|
||||
struct GuFinalizer {
|
||||
void (*fn)(GuFinalizer* self);
|
||||
///< @param self A pointer to this finalizer.
|
||||
};
|
||||
|
||||
/// Register a finalizer.
|
||||
void gu_pool_finally(GuPool* pool, GuFinalizer* fini);
|
||||
|
||||
/**< Register \p fini to be called when \p pool is destroyed. The
|
||||
* finalizers are called in reverse order of registration.
|
||||
*/
|
||||
|
||||
|
||||
//@}
|
||||
//@}
|
||||
|
||||
/** @defgroup GuMemBuf Memory buffers
|
||||
*
|
||||
* Resizable blocks of heap-allocated memory. These operations differ
|
||||
* from standard \c malloc, \c realloc and \c free -functions in that
|
||||
* memory buffers are not allocated by exact size. Instead, a minimum
|
||||
* size is requested, and the returned buffer may be larger. This
|
||||
* gives the memory allocator more flexibility when the client code
|
||||
* can make use of larger buffers than requested.
|
||||
* */
|
||||
|
||||
//@{
|
||||
|
||||
|
||||
/// Allocate a new memory buffer.
|
||||
GU_ONLY void*
|
||||
gu_mem_buf_alloc(size_t min_size, size_t* real_size);
|
||||
/**<
|
||||
* @param min_size The minimum acceptable size for a returned memory block.
|
||||
*
|
||||
* @param[out] real_size The actual size of the returned memory
|
||||
* block. This is never less than \p min_size.
|
||||
*
|
||||
* @return A pointer to the memory buffer.
|
||||
*/
|
||||
|
||||
|
||||
/// Allocate a new memory buffer to replace an old one.
|
||||
GU_ONLY void*
|
||||
gu_mem_buf_realloc(
|
||||
GU_NULL GU_ONLY GU_RETURNED
|
||||
void* buf,
|
||||
size_t min_size,
|
||||
size_t* real_size_out);
|
||||
|
||||
|
||||
/// Free a memory buffer.
|
||||
void
|
||||
gu_mem_buf_free(GU_ONLY void* buf);
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
#endif // GU_MEM_H_
|
||||
302
src/runtime/c/gu/out.c
Normal file
302
src/runtime/c/gu/out.c
Normal file
@@ -0,0 +1,302 @@
|
||||
#include <gu/seq.h>
|
||||
#include <gu/out.h>
|
||||
|
||||
|
||||
|
||||
GuOut
|
||||
gu_init_out(GuOutStream* stream)
|
||||
{
|
||||
gu_require(stream != NULL);
|
||||
GuOut out = {
|
||||
.buf_end = NULL,
|
||||
.buf_curr = 0,
|
||||
.stream = stream,
|
||||
.fini.fn = NULL
|
||||
};
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_out_is_buffering(GuOut* out)
|
||||
{
|
||||
return !!out->buf_end;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_out_end_buf(GuOut* out, GuExn* err)
|
||||
{
|
||||
if (!gu_out_is_buffering(out)) {
|
||||
return;
|
||||
}
|
||||
GuOutStream* stream = out->stream;
|
||||
size_t curr_len = ((ptrdiff_t)out->buf_size) + out->buf_curr;
|
||||
stream->end_buf(stream, curr_len, err);
|
||||
out->buf_end = NULL;
|
||||
out->buf_size = out->buf_curr = 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_out_begin_buf(GuOut* out, size_t req, GuExn* err)
|
||||
{
|
||||
GuOutStream* stream = out->stream;
|
||||
if (gu_out_is_buffering(out)) {
|
||||
if (out->buf_curr < 0) {
|
||||
return true;
|
||||
} else {
|
||||
gu_out_end_buf(out, err);
|
||||
if (!gu_ok(err)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stream->begin_buf) {
|
||||
size_t sz = 0;
|
||||
uint8_t* buf = stream->begin_buf(stream, req, &sz, err);
|
||||
gu_assert(sz <= PTRDIFF_MAX);
|
||||
if (buf) {
|
||||
out->buf_end = &buf[sz];
|
||||
out->buf_curr = -(ptrdiff_t) sz;
|
||||
out->buf_size = sz;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gu_out_fini(GuFinalizer* self)
|
||||
{
|
||||
GuOut* out = gu_container(self, GuOut, fini);
|
||||
if (gu_out_is_buffering(out)) {
|
||||
GuPool* pool = gu_local_pool();
|
||||
GuExn* err = gu_new_exn(NULL, gu_kind(type), pool);
|
||||
gu_out_end_buf(out, err);
|
||||
gu_pool_free(pool);
|
||||
}
|
||||
}
|
||||
|
||||
GuOut*
|
||||
gu_new_out(GuOutStream* stream, GuPool* pool)
|
||||
{
|
||||
GuOut* out = gu_new(GuOut, pool);
|
||||
*out = gu_init_out(stream);
|
||||
out->fini.fn = gu_out_fini;
|
||||
gu_pool_finally(pool, &out->fini);
|
||||
return out;
|
||||
}
|
||||
|
||||
extern inline bool
|
||||
gu_out_try_buf_(GuOut* out, const uint8_t* src, size_t len);
|
||||
|
||||
|
||||
extern inline size_t
|
||||
gu_out_bytes(GuOut* out, const uint8_t* buf, size_t len, GuExn* err);
|
||||
|
||||
static size_t
|
||||
gu_out_output(GuOut* out, const uint8_t* src, size_t len, GuExn* err)
|
||||
{
|
||||
gu_out_end_buf(out, err);
|
||||
if (!gu_ok(err)) {
|
||||
return 0;
|
||||
}
|
||||
return out->stream->output(out->stream, src, len, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
gu_out_flush(GuOut* out, GuExn* err)
|
||||
{
|
||||
GuOutStream* stream = out->stream;
|
||||
if (out->buf_end) {
|
||||
gu_out_end_buf(out, err);
|
||||
if (!gu_ok(err)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (stream->flush) {
|
||||
stream->flush(stream, err);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
gu_out_begin_span(GuOut* out, size_t req, size_t* sz_out, GuExn* err)
|
||||
{
|
||||
if (!out->buf_end && !gu_out_begin_buf(out, req, err)) {
|
||||
return NULL;
|
||||
}
|
||||
*sz_out = -out->buf_curr;
|
||||
return &out->buf_end[out->buf_curr];
|
||||
}
|
||||
|
||||
void
|
||||
gu_out_end_span(GuOut* out, size_t sz)
|
||||
{
|
||||
ptrdiff_t new_curr = (ptrdiff_t) sz + out->buf_curr;
|
||||
gu_require(new_curr <= 0);
|
||||
out->buf_curr = new_curr;
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_out_bytes_(GuOut* restrict out, const uint8_t* restrict src, size_t len,
|
||||
GuExn* err)
|
||||
{
|
||||
if (!gu_ok(err)) {
|
||||
return 0;
|
||||
} else if (gu_out_try_buf_(out, src, len)) {
|
||||
return len;
|
||||
}
|
||||
if (gu_out_begin_buf(out, len, err)) {
|
||||
if (gu_out_try_buf_(out, src, len)) {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
return gu_out_output(out, src, len, err);
|
||||
}
|
||||
|
||||
|
||||
void gu_out_u8_(GuOut* restrict out, uint8_t u, GuExn* err)
|
||||
{
|
||||
if (gu_out_begin_buf(out, 1, err)) {
|
||||
if (gu_out_try_u8_(out, u)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
gu_out_output(out, &u, 1, err);
|
||||
}
|
||||
|
||||
|
||||
extern inline void
|
||||
gu_out_u8(GuOut* restrict out, uint8_t u, GuExn* err);
|
||||
|
||||
extern inline void
|
||||
gu_out_s8(GuOut* restrict out, int8_t i, GuExn* err);
|
||||
|
||||
extern inline bool
|
||||
gu_out_is_buffered(GuOut* out);
|
||||
|
||||
extern inline bool
|
||||
gu_out_try_u8_(GuOut* restrict out, uint8_t u);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct GuProxyOutStream GuProxyOutStream;
|
||||
|
||||
struct GuProxyOutStream {
|
||||
GuOutStream stream;
|
||||
GuOut* real_out;
|
||||
};
|
||||
|
||||
|
||||
static uint8_t*
|
||||
gu_proxy_out_buf_begin(GuOutStream* self, size_t req, size_t* sz_out,
|
||||
GuExn* err)
|
||||
{
|
||||
GuProxyOutStream* pos =
|
||||
gu_container(self, GuProxyOutStream, stream);
|
||||
return gu_out_begin_span(pos->real_out, req, sz_out, err);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_proxy_out_buf_end(GuOutStream* self, size_t sz, GuExn* err)
|
||||
{
|
||||
GuProxyOutStream* pos =
|
||||
gu_container(self, GuProxyOutStream, stream);
|
||||
gu_out_end_span(pos->real_out, sz);
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_proxy_out_output(GuOutStream* self, const uint8_t* src, size_t sz,
|
||||
GuExn* err)
|
||||
{
|
||||
GuProxyOutStream* pos =
|
||||
gu_container(self, GuProxyOutStream, stream);
|
||||
return gu_out_bytes(pos->real_out, src, sz, err);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_proxy_out_flush(GuOutStream* self, GuExn* err)
|
||||
{
|
||||
GuProxyOutStream* pos =
|
||||
gu_container(self, GuProxyOutStream, stream);
|
||||
gu_out_flush(pos->real_out, err);
|
||||
}
|
||||
|
||||
|
||||
GuOutStream*
|
||||
gu_out_proxy_stream(GuOut* out, GuPool* pool)
|
||||
{
|
||||
return &gu_new_s(pool, GuProxyOutStream,
|
||||
.stream.begin_buf = gu_proxy_out_buf_begin,
|
||||
.stream.end_buf = gu_proxy_out_buf_end,
|
||||
.stream.output = gu_proxy_out_output,
|
||||
.stream.flush = gu_proxy_out_flush,
|
||||
.real_out = out)->stream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct GuBufferedOutStream GuBufferedOutStream;
|
||||
|
||||
struct GuBufferedOutStream {
|
||||
GuProxyOutStream pstream;
|
||||
size_t sz;
|
||||
uint8_t buf[];
|
||||
};
|
||||
|
||||
static uint8_t*
|
||||
gu_buffered_out_buf_begin(GuOutStream* self, size_t req, size_t* sz_out,
|
||||
GuExn* err)
|
||||
{
|
||||
(void) (req && err);
|
||||
GuBufferedOutStream* b =
|
||||
gu_container(self, GuBufferedOutStream, pstream.stream);
|
||||
*sz_out = b->sz;
|
||||
return b->buf;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_buffered_out_buf_end(GuOutStream* self, size_t sz, GuExn* err)
|
||||
{
|
||||
GuBufferedOutStream* b =
|
||||
gu_container(self, GuBufferedOutStream, pstream.stream);
|
||||
gu_require(sz <= b->sz);
|
||||
gu_out_bytes(b->pstream.real_out, b->buf, sz, err);
|
||||
}
|
||||
|
||||
GuOut*
|
||||
gu_new_buffered_out(GuOut* out, size_t sz, GuPool* pool)
|
||||
{
|
||||
GuBufferedOutStream* b =
|
||||
gu_new_flex(pool, GuBufferedOutStream, buf, sz);
|
||||
b->pstream.stream = (GuOutStream) {
|
||||
.begin_buf = gu_buffered_out_buf_begin,
|
||||
.end_buf = gu_buffered_out_buf_end,
|
||||
.output = gu_proxy_out_output,
|
||||
.flush = gu_proxy_out_flush
|
||||
};
|
||||
b->pstream.real_out = out;
|
||||
b->sz = sz;
|
||||
return gu_new_out(&b->pstream.stream, pool);
|
||||
}
|
||||
|
||||
GuOut*
|
||||
gu_out_buffered(GuOut* out, GuPool* pool)
|
||||
{
|
||||
if (gu_out_is_buffered(out)) {
|
||||
return out;
|
||||
}
|
||||
return gu_new_buffered_out(out, 4096, pool);
|
||||
}
|
||||
|
||||
|
||||
166
src/runtime/c/gu/out.h
Normal file
166
src/runtime/c/gu/out.h
Normal file
@@ -0,0 +1,166 @@
|
||||
#ifndef GU_OUT_H_
|
||||
#define GU_OUT_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/exn.h>
|
||||
|
||||
typedef struct GuOut GuOut;
|
||||
|
||||
typedef struct GuOutStream GuOutStream;
|
||||
|
||||
struct GuOutStream {
|
||||
uint8_t* (*begin_buf)(GuOutStream* self, size_t req, size_t* sz_out,
|
||||
GuExn* err);
|
||||
void (*end_buf)(GuOutStream* self, size_t span, GuExn* err);
|
||||
size_t (*output)(GuOutStream* self, const uint8_t* buf, size_t size,
|
||||
GuExn* err);
|
||||
void (*flush)(GuOutStream* self, GuExn* err);
|
||||
};
|
||||
|
||||
|
||||
struct GuOut {
|
||||
uint8_t* restrict buf_end;
|
||||
ptrdiff_t buf_curr;
|
||||
size_t buf_size;
|
||||
GuOutStream* stream;
|
||||
GuFinalizer fini;
|
||||
};
|
||||
|
||||
|
||||
GuOut
|
||||
gu_init_out(GuOutStream* stream);
|
||||
|
||||
GuOut*
|
||||
gu_new_out(GuOutStream* stream, GuPool* pool);
|
||||
|
||||
inline bool
|
||||
gu_out_is_buffered(GuOut* out)
|
||||
{
|
||||
return !!out->stream->begin_buf;
|
||||
}
|
||||
|
||||
GuOutStream*
|
||||
gu_out_proxy_stream(GuOut* out, GuPool* pool);
|
||||
|
||||
GuOut*
|
||||
gu_new_buffered_out(GuOut* out, size_t buf_sz, GuPool* pool);
|
||||
|
||||
GuOut*
|
||||
gu_out_buffered(GuOut* out, GuPool* pool);
|
||||
|
||||
uint8_t*
|
||||
gu_out_begin_span(GuOut* out, size_t req, size_t* sz_out, GuExn* err);
|
||||
|
||||
uint8_t*
|
||||
gu_out_force_span(GuOut* out, size_t min, size_t max, size_t* sz_out,
|
||||
GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_end_span(GuOut* out, size_t sz);
|
||||
|
||||
size_t
|
||||
gu_out_bytes_(GuOut* restrict out, const uint8_t* restrict src,
|
||||
size_t len, GuExn* err);
|
||||
|
||||
inline bool
|
||||
gu_out_try_buf_(GuOut* restrict out, const uint8_t* restrict src, size_t len)
|
||||
{
|
||||
gu_require(len <= PTRDIFF_MAX);
|
||||
ptrdiff_t curr = out->buf_curr;
|
||||
ptrdiff_t new_curr = curr + (ptrdiff_t) len;
|
||||
if (GU_UNLIKELY(new_curr > 0)) {
|
||||
return false;
|
||||
}
|
||||
memcpy(&out->buf_end[curr], src, len);
|
||||
out->buf_curr = new_curr;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline size_t
|
||||
gu_out_bytes(GuOut* restrict out, const uint8_t* restrict src, size_t len,
|
||||
GuExn* err)
|
||||
{
|
||||
if (GU_LIKELY(gu_out_try_buf_(out, src, len))) {
|
||||
return len;
|
||||
}
|
||||
return gu_out_bytes_(out, src, len, err);
|
||||
}
|
||||
|
||||
void
|
||||
gu_out_flush(GuOut* out, GuExn* err);
|
||||
|
||||
inline bool
|
||||
gu_out_try_u8_(GuOut* restrict out, uint8_t u)
|
||||
{
|
||||
ptrdiff_t curr = out->buf_curr;
|
||||
ptrdiff_t new_curr = curr + 1;
|
||||
if (GU_UNLIKELY(new_curr > 0)) {
|
||||
return false;
|
||||
}
|
||||
out->buf_end[curr] = u;
|
||||
out->buf_curr = new_curr;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_out_u8(GuOut* restrict out, uint8_t u, GuExn* err)
|
||||
{
|
||||
if (GU_UNLIKELY(!gu_out_try_u8_(out, u))) {
|
||||
extern void gu_out_u8_(GuOut* restrict out, uint8_t u,
|
||||
GuExn* err);
|
||||
gu_out_u8_(out, u, err);
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_out_s8(GuOut* restrict out, int8_t i, GuExn* err)
|
||||
{
|
||||
gu_out_u8(out, (uint8_t) i, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
gu_out_u16le(GuOut* out, uint16_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_u16be(GuOut* out, uint16_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s16le(GuOut* out, int16_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s16be(GuOut* out, int16_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_u32le(GuOut* out, uint32_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_u32be(GuOut* out, uint32_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s32le(GuOut* out, int32_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s32be(GuOut* out, int32_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_u64le(GuOut* out, uint64_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_u64be(GuOut* out, uint64_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s64le(GuOut* out, int64_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_s64be(GuOut* out, int64_t u, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_f64le(GuOut* out, double d, GuExn* err);
|
||||
|
||||
void
|
||||
gu_out_f64be(GuOut* out, double d, GuExn* err);
|
||||
|
||||
#endif // GU_OUT_H_
|
||||
154
src/runtime/c/gu/prime.c
Normal file
154
src/runtime/c/gu/prime.c
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <gu/defs.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
static const uint32_t gu_prime_wheel_mask = 0UL
|
||||
| 1 << 1
|
||||
| 1 << 7
|
||||
| 1 << 11
|
||||
| 1 << 13
|
||||
| 1 << 17
|
||||
| 1 << 19
|
||||
| 1 << 23
|
||||
| 1 << 29;
|
||||
|
||||
static bool
|
||||
gu_prime_wheel(int i)
|
||||
{
|
||||
gu_assert(i >= 0 && i < 30);
|
||||
return !!(gu_prime_wheel_mask & (1 << i));
|
||||
}
|
||||
|
||||
static const uint32_t gu_small_prime_mask = 0UL
|
||||
| 1 << 2
|
||||
| 1 << 3
|
||||
| 1 << 5
|
||||
| 1 << 7
|
||||
| 1 << 11
|
||||
| 1 << 13
|
||||
| 1 << 17
|
||||
| 1 << 19
|
||||
| 1 << 23
|
||||
| 1 << 29
|
||||
| 1U << 31;
|
||||
|
||||
static bool
|
||||
gu_is_wheel_prime(int u)
|
||||
{
|
||||
gu_assert(u > 30 && u % 2 != 0 && u % 3 != 0 && u % 5 != 0);
|
||||
int d = 0;
|
||||
int i = 7;
|
||||
goto start;
|
||||
while (d * d <= u) {
|
||||
for (i = 1; i <= 29; i+=2) {
|
||||
start:
|
||||
if (gu_prime_wheel(i) && u % (d + i) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
d += 30;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
gu_prime_inf(int i)
|
||||
{
|
||||
if (i < 2) {
|
||||
return 0;
|
||||
} else if (i < 32) {
|
||||
while (!(gu_small_prime_mask & (1 << i))) {
|
||||
i--;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int d = (i - 1) | 1;
|
||||
int r = d % 30;
|
||||
|
||||
while (!gu_prime_wheel(r) || !gu_is_wheel_prime(d)) {
|
||||
d -= 2;
|
||||
r -= 2;
|
||||
if (r < 0) {
|
||||
r += 30;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
int
|
||||
gu_prime_sup(int i)
|
||||
{
|
||||
if (i <= 2) {
|
||||
return 2;
|
||||
} else if (i < 32) {
|
||||
while (!(gu_small_prime_mask & (1 << i))) {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int d = i | 1;
|
||||
int r = d % 30;
|
||||
|
||||
while (!gu_prime_wheel(r) || !gu_is_wheel_prime(d)) {
|
||||
d += 2;
|
||||
r += 2;
|
||||
if (r > 30) {
|
||||
r -= 30;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
bool
|
||||
gu_is_prime(int i)
|
||||
{
|
||||
if (i < 2) {
|
||||
return false;
|
||||
} else if (i < 30) {
|
||||
return !!(gu_small_prime_mask & (1 << i));
|
||||
} else if (!gu_prime_wheel(i % 30)) {
|
||||
return false;
|
||||
} else {
|
||||
return gu_is_wheel_prime(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
gu_is_twin_prime(int i)
|
||||
{
|
||||
return gu_is_prime(i) && gu_is_prime(i - 2);
|
||||
}
|
||||
|
||||
int
|
||||
gu_twin_prime_inf(int i)
|
||||
{
|
||||
while (true) {
|
||||
i = gu_prime_inf(i);
|
||||
if (i == 0) {
|
||||
return 0;
|
||||
} else if (gu_is_prime(i - 2)) {
|
||||
return i;
|
||||
}
|
||||
i = i - 4;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
gu_twin_prime_sup(int i)
|
||||
{
|
||||
if (i <= 5) {
|
||||
return 5;
|
||||
}
|
||||
i = i - 2;
|
||||
while (true) {
|
||||
i = gu_prime_sup(i);
|
||||
if (gu_is_prime(i + 2)) {
|
||||
return i + 2;
|
||||
}
|
||||
i = i + 4;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
16
src/runtime/c/gu/prime.h
Normal file
16
src/runtime/c/gu/prime.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef GU_PRIME_H_
|
||||
#define GU_PRIME_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
|
||||
bool gu_is_prime(int i);
|
||||
|
||||
bool gu_is_twin_prime(int i);
|
||||
|
||||
int gu_prime_inf(int i);
|
||||
int gu_twin_prime_inf(int i);
|
||||
|
||||
int gu_prime_sup(int i);
|
||||
int gu_twin_prime_sup(int i);
|
||||
|
||||
#endif // GU_PRIME_H_
|
||||
15
src/runtime/c/gu/read.c
Normal file
15
src/runtime/c/gu/read.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <gu/read.h>
|
||||
|
||||
extern inline GuUCS
|
||||
gu_read_ucs(GuReader* rdr, GuExn* err);
|
||||
|
||||
extern inline char
|
||||
gu_getc(GuReader* rdr, GuExn* err);
|
||||
|
||||
GuReader*
|
||||
gu_new_utf8_reader(GuIn* utf8_in, GuPool* pool)
|
||||
{
|
||||
GuReader* rdr = gu_new(GuReader, pool);
|
||||
rdr->in_ = gu_init_in(gu_in_proxy_stream(utf8_in, pool));
|
||||
return rdr;
|
||||
}
|
||||
31
src/runtime/c/gu/read.h
Normal file
31
src/runtime/c/gu/read.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef GU_READ_H_
|
||||
#define GU_READ_H_
|
||||
|
||||
#include <gu/in.h>
|
||||
#include <gu/ucs.h>
|
||||
#include <gu/utf8.h>
|
||||
|
||||
typedef struct GuReader GuReader;
|
||||
|
||||
struct GuReader {
|
||||
GuIn in_;
|
||||
};
|
||||
|
||||
inline GuUCS
|
||||
gu_read_ucs(GuReader* rdr, GuExn* err)
|
||||
{
|
||||
return gu_in_utf8(&rdr->in_, err);
|
||||
}
|
||||
|
||||
inline char
|
||||
gu_getc(GuReader* rdr, GuExn* err)
|
||||
{
|
||||
return gu_in_utf8_char(&rdr->in_, err);
|
||||
}
|
||||
|
||||
GuReader*
|
||||
gu_new_utf8_reader(GuIn* utf8_in, GuPool* pool);
|
||||
/**< @todo Implement. */
|
||||
|
||||
|
||||
#endif // GU_READ_H_
|
||||
245
src/runtime/c/gu/seq.c
Normal file
245
src/runtime/c/gu/seq.c
Normal file
@@ -0,0 +1,245 @@
|
||||
#include <gu/out.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/fun.h>
|
||||
#include <gu/assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
struct GuBuf {
|
||||
uint8_t* data;
|
||||
size_t elem_size;
|
||||
size_t avail_len;
|
||||
GuFinalizer fin;
|
||||
};
|
||||
|
||||
GuBuf*
|
||||
gu_seq_buf(GuSeq seq)
|
||||
{
|
||||
gu_require(gu_tagged_tag(seq.w_) == 0);
|
||||
return gu_word_ptr(seq.w_);
|
||||
}
|
||||
|
||||
GuSeq
|
||||
gu_buf_seq(GuBuf* buf)
|
||||
{
|
||||
return (GuSeq) { .w_ = gu_ptr_word(buf) };
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_buf_length(GuBuf* dyn)
|
||||
{
|
||||
return (size_t)(((GuWord*)(void*)dyn)[-1] >> 1);
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_buf_avail(GuBuf* buf)
|
||||
{
|
||||
return buf->avail_len;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_buf_set_length(GuBuf* dyn, size_t new_len)
|
||||
{
|
||||
((GuWord*)(void*)dyn)[-1] = ((GuWord) new_len) << 1 | 0x1;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_buf_fini(GuFinalizer* fin)
|
||||
{
|
||||
GuBuf* buf = gu_container(fin, GuBuf, fin);
|
||||
gu_mem_buf_free(buf->data);
|
||||
}
|
||||
|
||||
GuBuf*
|
||||
gu_make_buf(size_t elem_size, GuPool* pool)
|
||||
{
|
||||
GuBuf* buf = gu_new_prefixed(unsigned, GuBuf, pool);
|
||||
gu_buf_set_length(buf, 0);
|
||||
buf->elem_size = elem_size;
|
||||
buf->data = NULL;
|
||||
buf->avail_len = 0;
|
||||
buf->fin.fn = gu_buf_fini;
|
||||
gu_pool_finally(pool, &buf->fin);
|
||||
gu_buf_set_length(buf, 0);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const GuWord gu_empty_seq[2] = {0, 0};
|
||||
|
||||
GuSeq
|
||||
gu_make_seq(size_t elem_size, size_t length, GuPool* pool)
|
||||
{
|
||||
size_t size = elem_size * length;
|
||||
if (0 < length && length <= GU_TAG_MAX) {
|
||||
void* buf = gu_malloc(pool, size);
|
||||
return (GuSeq) { gu_tagged(buf, length) };
|
||||
} else if (size == 0) {
|
||||
return (GuSeq) { gu_tagged((void*)&gu_empty_seq[1], 0) };
|
||||
} else {
|
||||
void* buf = gu_malloc_prefixed(pool,
|
||||
gu_alignof(GuWord),
|
||||
sizeof(GuWord),
|
||||
0, size);
|
||||
((GuWord*) buf)[-1] = ((GuWord) length) << 1;
|
||||
return (GuSeq) { gu_tagged(buf, 0) };
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_buf_require(GuBuf* buf, size_t req_len)
|
||||
{
|
||||
if (req_len <= buf->avail_len) {
|
||||
return;
|
||||
}
|
||||
size_t req_size = buf->elem_size * req_len;
|
||||
size_t real_size;
|
||||
buf->data = gu_mem_buf_realloc(buf->data, req_size,
|
||||
&real_size);
|
||||
buf->avail_len = real_size / buf->elem_size;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_buf_data(GuBuf* buf)
|
||||
{
|
||||
return buf->data;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_buf_extend_n(GuBuf* buf, size_t n_elems)
|
||||
{
|
||||
size_t len = gu_buf_length(buf);
|
||||
size_t new_len = len + n_elems;
|
||||
gu_buf_require(buf, new_len);
|
||||
gu_buf_set_length(buf, new_len);
|
||||
return &buf->data[buf->elem_size * len];
|
||||
}
|
||||
|
||||
void*
|
||||
gu_buf_extend(GuBuf* buf)
|
||||
{
|
||||
return gu_buf_extend_n(buf, 1);
|
||||
}
|
||||
|
||||
void
|
||||
gu_buf_push_n(GuBuf* buf, const void* data, size_t n_elems)
|
||||
{
|
||||
|
||||
void* p = gu_buf_extend_n(buf, n_elems);
|
||||
memcpy(p, data, buf->elem_size * n_elems);
|
||||
}
|
||||
|
||||
const void*
|
||||
gu_buf_trim_n(GuBuf* buf, size_t n_elems)
|
||||
{
|
||||
gu_require(n_elems <= gu_buf_length(buf));
|
||||
size_t new_len = gu_buf_length(buf) - n_elems;
|
||||
gu_buf_set_length(buf, new_len);
|
||||
return &buf->data[buf->elem_size * new_len];
|
||||
}
|
||||
|
||||
const void*
|
||||
gu_buf_trim(GuBuf* buf)
|
||||
{
|
||||
return gu_buf_trim_n(buf, 1);
|
||||
}
|
||||
|
||||
void
|
||||
gu_buf_pop_n(GuBuf* buf, size_t n_elems, void* data_out)
|
||||
{
|
||||
const void* p = gu_buf_trim_n(buf, n_elems);
|
||||
memcpy(data_out, p, buf->elem_size * n_elems);
|
||||
}
|
||||
|
||||
GuSeq
|
||||
gu_buf_freeze(GuBuf* buf, GuPool* pool)
|
||||
{
|
||||
size_t len = gu_buf_length(buf);
|
||||
GuSeq seq = gu_make_seq(buf->elem_size, len, pool);
|
||||
void* bufdata = gu_buf_data(buf);
|
||||
void* seqdata = gu_seq_data(seq);
|
||||
memcpy(seqdata, bufdata, buf->elem_size * len);
|
||||
return seq;
|
||||
}
|
||||
|
||||
typedef struct GuBufOut GuBufOut;
|
||||
struct GuBufOut
|
||||
{
|
||||
GuOutStream stream;
|
||||
GuBuf* buf;
|
||||
};
|
||||
|
||||
static size_t
|
||||
gu_buf_out_output(GuOutStream* stream, const uint8_t* src, size_t sz,
|
||||
GuExn* err)
|
||||
{
|
||||
(void) err;
|
||||
GuBufOut* bout = gu_container(stream, GuBufOut, stream);
|
||||
GuBuf* buf = bout->buf;
|
||||
gu_assert(sz % buf->elem_size == 0);
|
||||
size_t len = sz / buf->elem_size;
|
||||
gu_buf_push_n(bout->buf, src, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static uint8_t*
|
||||
gu_buf_outbuf_begin(GuOutStream* stream, size_t req, size_t* sz_out, GuExn* err)
|
||||
{
|
||||
(void) req;
|
||||
(void) err;
|
||||
GuBufOut* bout = gu_container(stream, GuBufOut, stream);
|
||||
GuBuf* buf = bout->buf;
|
||||
size_t esz = buf->elem_size;
|
||||
size_t len = gu_buf_length(buf);
|
||||
gu_buf_require(buf, len + (req + esz - 1) / esz);
|
||||
size_t avail = buf->avail_len;
|
||||
gu_assert(len < avail);
|
||||
*sz_out = esz * (avail - len);
|
||||
return &buf->data[len * esz];
|
||||
}
|
||||
|
||||
static void
|
||||
gu_buf_outbuf_end(GuOutStream* stream, size_t sz, GuExn* err)
|
||||
{
|
||||
(void) err;
|
||||
GuBufOut* bout = gu_container(stream, GuBufOut, stream);
|
||||
GuBuf* buf = bout->buf;
|
||||
size_t len = gu_buf_length(buf);
|
||||
size_t elem_size = buf->elem_size;
|
||||
gu_require(sz % elem_size == 0);
|
||||
gu_require(sz < elem_size * (len - buf->avail_len));
|
||||
gu_buf_set_length(buf, len + (sz / elem_size));
|
||||
}
|
||||
|
||||
GuOut*
|
||||
gu_buf_out(GuBuf* buf, GuPool* pool)
|
||||
{
|
||||
GuBufOut* bout = gu_new_i(pool, GuBufOut,
|
||||
.stream.output = gu_buf_out_output,
|
||||
.stream.begin_buf = gu_buf_outbuf_begin,
|
||||
.stream.end_buf = gu_buf_outbuf_end,
|
||||
.buf = buf);
|
||||
return gu_new_out(&bout->stream, pool);
|
||||
}
|
||||
|
||||
const GuSeq
|
||||
gu_null_seq = GU_NULL_SEQ;
|
||||
|
||||
|
||||
#include <gu/type.h>
|
||||
|
||||
GU_DEFINE_KIND(GuSeq, GuOpaque);
|
||||
GU_DEFINE_KIND(GuBuf, abstract);
|
||||
|
||||
GU_DEFINE_TYPE(GuChars, GuSeq, gu_type(char));
|
||||
GU_DEFINE_TYPE(GuBytes, GuSeq, gu_type(uint8_t));
|
||||
|
||||
char*
|
||||
gu_chars_str(GuChars chars, GuPool* pool)
|
||||
{
|
||||
size_t len = gu_seq_length(chars);
|
||||
char* data = gu_seq_data(chars);
|
||||
char* str = gu_new_str(len, pool);
|
||||
memcpy(str, data, len);
|
||||
return str;
|
||||
}
|
||||
198
src/runtime/c/gu/seq.h
Normal file
198
src/runtime/c/gu/seq.h
Normal file
@@ -0,0 +1,198 @@
|
||||
#ifndef GU_SEQ_H_
|
||||
#define GU_SEQ_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/bits.h>
|
||||
|
||||
|
||||
typedef struct GuBuf GuBuf;
|
||||
|
||||
typedef GuOpaque() GuSeq;
|
||||
|
||||
GuSeq
|
||||
gu_make_seq(size_t elem_size, size_t len, GuPool* pool);
|
||||
|
||||
#define gu_new_seq(T, N, POOL) \
|
||||
gu_make_seq(sizeof(T), (N), (POOL))
|
||||
|
||||
static inline size_t
|
||||
gu_seq_length(GuSeq seq)
|
||||
{
|
||||
GuWord w = seq.w_;
|
||||
size_t tag = gu_tagged_tag(w);
|
||||
if (tag == 0) {
|
||||
GuWord* p = gu_tagged_ptr(w);
|
||||
return (size_t) (p[-1] >> 1);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
static inline void*
|
||||
gu_seq_data(GuSeq seq)
|
||||
{
|
||||
GuWord w = seq.w_;
|
||||
int tag = gu_tagged_tag(w);
|
||||
void* ptr = gu_tagged_ptr(w);
|
||||
if (tag == 0) {
|
||||
GuWord* p = ptr;
|
||||
if (p[-1] & 0x1) {
|
||||
return *(uint8_t**) ptr;
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
gu_seq_is_null(GuSeq seq)
|
||||
{
|
||||
return (gu_tagged_ptr(seq.w_)) == NULL;
|
||||
}
|
||||
|
||||
|
||||
#define gu_seq_index(SEQ, T, I) \
|
||||
(&((T*)gu_seq_data(SEQ))[I])
|
||||
|
||||
#define gu_seq_get(SEQ, T, I) \
|
||||
(*gu_seq_index(SEQ, T, I))
|
||||
|
||||
#define gu_seq_set(SEQ, T, I, V) \
|
||||
GU_BEGIN \
|
||||
(*gu_seq_index(SEQ, T, I) = (V)); \
|
||||
GU_END
|
||||
|
||||
|
||||
|
||||
|
||||
GuBuf*
|
||||
gu_seq_buf(GuSeq seq);
|
||||
|
||||
GuSeq
|
||||
gu_buf_seq(GuBuf* buf);
|
||||
|
||||
GuBuf*
|
||||
gu_make_buf(size_t elem_size, GuPool* pool);
|
||||
|
||||
#define gu_new_buf(T, POOL) \
|
||||
gu_make_buf(sizeof(T), (POOL))
|
||||
|
||||
size_t
|
||||
gu_buf_length(GuBuf* buf);
|
||||
|
||||
size_t
|
||||
gu_buf_avail(GuBuf* buf);
|
||||
|
||||
void*
|
||||
gu_buf_data(GuBuf* buf);
|
||||
|
||||
#define gu_buf_index(BUF, T, I) \
|
||||
(&((T*)gu_buf_data(BUF))[I])
|
||||
|
||||
#define gu_buf_get(BUF, T, I) \
|
||||
(*gu_buf_index(BUF, T, I))
|
||||
|
||||
#define gu_buf_set(BUF, T, I) \
|
||||
GU_BEGIN \
|
||||
(*gu_buf_index(BUF, T, I) = (V)); \
|
||||
GU_END
|
||||
|
||||
void
|
||||
gu_buf_push_n(GuBuf* buf, const void* elems, size_t n_elems);
|
||||
|
||||
void*
|
||||
gu_buf_extend_n(GuBuf* buf, size_t n_elems);
|
||||
|
||||
void*
|
||||
gu_buf_extend(GuBuf* buf);
|
||||
|
||||
#define gu_buf_push(BUF, T, VAL) \
|
||||
GU_BEGIN \
|
||||
((*(T*)gu_buf_extend(BUF)) = (VAL)); \
|
||||
GU_END
|
||||
|
||||
void
|
||||
gu_buf_pop_n(GuBuf* buf, size_t n_elems, void* data_out);
|
||||
|
||||
const void*
|
||||
gu_buf_trim_n(GuBuf* buf, size_t n_elems);
|
||||
|
||||
const void*
|
||||
gu_buf_trim(GuBuf* buf);
|
||||
|
||||
#define gu_buf_pop(BUF, T) \
|
||||
(*(T*)gu_buf_trim(BUF))
|
||||
|
||||
void
|
||||
gu_seq_resize_tail(GuSeq seq, ptrdiff_t change);
|
||||
|
||||
#if 0
|
||||
void
|
||||
gu_buf_resize_head(GuBuf* buf, ptrdiff_t change);
|
||||
|
||||
void
|
||||
gu_buf_unshift(GuBuf* buf, const void* data, size_t size);
|
||||
|
||||
void
|
||||
gu_buf_shift(GuBuf* buf, size_t size, void* data_out);
|
||||
#endif
|
||||
|
||||
GuSeq
|
||||
gu_buf_freeze(GuBuf* buf, GuPool* pool);
|
||||
|
||||
extern const GuSeq gu_null_seq;
|
||||
|
||||
#define GU_NULL_SEQ { .w_ = (GuWord)(void*)NULL }
|
||||
|
||||
typedef GuSeq GuChars;
|
||||
typedef GuSeq GuBytes;
|
||||
typedef GuBuf GuCharBuf;
|
||||
typedef GuBuf GuByteBuf;
|
||||
|
||||
char*
|
||||
gu_chars_str(GuChars chars, GuPool* pool);
|
||||
|
||||
#endif // GU_SEQ_H_
|
||||
|
||||
#if defined(GU_OUT_H_) && !defined(GU_SEQ_H_OUT_)
|
||||
#define GU_SEQ_H_OUT_
|
||||
|
||||
GuOut*
|
||||
gu_buf_out(GuBuf* buf, GuPool* pool);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(GU_TYPE_H_) && !defined(GU_SEQ_H_TYPE_)
|
||||
#define GU_SEQ_H_TYPE_
|
||||
|
||||
extern GU_DECLARE_KIND(GuSeq);
|
||||
extern GU_DECLARE_KIND(GuBuf);
|
||||
|
||||
struct GuSeqType {
|
||||
GuType_GuOpaque opaque_base;
|
||||
GuType* elem_type;
|
||||
};
|
||||
|
||||
typedef const struct GuSeqType GuSeqType, GuType_GuSeq;
|
||||
|
||||
#define GU_TYPE_INIT_GuSeq(k_, t_, elem_type_) { \
|
||||
.opaque_base = GU_TYPE_INIT_GuOpaque(k_, t_, _), \
|
||||
.elem_type = elem_type_, \
|
||||
}
|
||||
|
||||
typedef struct GuBufType GuBufType, GuType_GuBuf;
|
||||
|
||||
struct GuBufType {
|
||||
GuType_abstract abstract_base;
|
||||
GuType* elem_type;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_GuBuf(KIND, BUF_T, ELEM_T) { \
|
||||
.abstract_base = GU_TYPE_INIT_abstract(KIND, BUF_T, _), \
|
||||
.elem_type = ELEM_T \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_TYPE(GuChars, GuSeq);
|
||||
extern GU_DECLARE_TYPE(GuBytes, GuSeq);
|
||||
|
||||
#endif
|
||||
|
||||
85
src/runtime/c/gu/str.c
Normal file
85
src/runtime/c/gu/str.c
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <gu/assert.h>
|
||||
#include <gu/str.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
const char gu_empty_str[] = "";
|
||||
const char* const gu_null_str = NULL;
|
||||
|
||||
char*
|
||||
gu_new_str(size_t size, GuPool* pool)
|
||||
{
|
||||
char* str = gu_new_n(char, size + 1, pool);
|
||||
memset(str, '\0', size + 1);
|
||||
return str;
|
||||
}
|
||||
|
||||
char*
|
||||
gu_strdup(const char* cstr, GuPool* pool)
|
||||
{
|
||||
int len = strlen(cstr);
|
||||
char* str = gu_new_str(len, pool);
|
||||
memcpy(str, cstr, len);
|
||||
return str;
|
||||
}
|
||||
|
||||
bool
|
||||
gu_str_eq(GuStr s1, GuStr s2)
|
||||
{
|
||||
return (strcmp(s1, s2)) == 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_str_is_equal(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
const GuStr* sp1 = p1;
|
||||
const GuStr* sp2 = p2;
|
||||
return gu_str_eq(*sp1, *sp2);
|
||||
}
|
||||
|
||||
static GuHash
|
||||
gu_str_hasher_hash(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
GuHash h = 0;
|
||||
const GuStr* sp = p;
|
||||
for (const char* s = *sp; *s != '\0'; s++) {
|
||||
h = 101 * h + (unsigned char) *s;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
GuHasher gu_str_hasher[1] = {
|
||||
{
|
||||
.eq = { .is_equal = gu_str_is_equal },
|
||||
.hash = gu_str_hasher_hash
|
||||
}
|
||||
};
|
||||
|
||||
GU_DEFINE_TYPE(GuStr, repr, _);
|
||||
|
||||
char*
|
||||
gu_vasprintf(const char* fmt, va_list args, GuPool* pool)
|
||||
{
|
||||
va_list args2;
|
||||
va_copy(args2, args);
|
||||
int len = vsnprintf(NULL, 0, fmt, args2);
|
||||
gu_assert_msg(len >= 0, "Invalid format string: \"%s\"", fmt);
|
||||
va_end(args2);
|
||||
char* str = gu_new_str(len, pool);
|
||||
vsnprintf(str, len + 1, fmt, args);
|
||||
return str;
|
||||
}
|
||||
|
||||
char*
|
||||
gu_asprintf(GuPool* pool, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
char* str = gu_vasprintf(fmt, args, pool);
|
||||
va_end(args);
|
||||
return str;
|
||||
}
|
||||
29
src/runtime/c/gu/str.h
Normal file
29
src/runtime/c/gu/str.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef GU_STR_H_
|
||||
#define GU_STR_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/hash.h>
|
||||
|
||||
extern const char gu_empty_str[];
|
||||
extern const char* const gu_null_str;
|
||||
|
||||
typedef const char* GuStr;
|
||||
|
||||
char* gu_new_str(size_t size, GuPool* pool);
|
||||
|
||||
char* gu_strdup(const char* str, GuPool* pool);
|
||||
|
||||
bool
|
||||
gu_str_eq(GuStr s1, GuStr s2);
|
||||
|
||||
extern GuHasher gu_str_hasher[1];
|
||||
|
||||
#include <gu/type.h>
|
||||
|
||||
extern GU_DECLARE_TYPE(GuStr, repr);
|
||||
|
||||
char* gu_vasprintf(const char* fmt, va_list args, GuPool* pool);
|
||||
|
||||
char* gu_asprintf(GuPool* pool, const char* fmt, ...);
|
||||
|
||||
#endif // GU_STR_H_
|
||||
270
src/runtime/c/gu/string.c
Normal file
270
src/runtime/c/gu/string.c
Normal file
@@ -0,0 +1,270 @@
|
||||
#include <gu/type.h>
|
||||
#include <gu/out.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/map.h>
|
||||
#include <gu/string.h>
|
||||
#include <gu/utf8.h>
|
||||
#include <gu/assert.h>
|
||||
#include "config.h"
|
||||
|
||||
const GuString gu_empty_string = { 1 };
|
||||
|
||||
struct GuStringBuf {
|
||||
GuByteBuf* bbuf;
|
||||
GuWriter* wtr;
|
||||
};
|
||||
|
||||
GuStringBuf*
|
||||
gu_string_buf(GuPool* pool)
|
||||
{
|
||||
GuBuf* buf = gu_new_buf(uint8_t, pool);
|
||||
GuOut* out = gu_buf_out(buf, pool);
|
||||
GuWriter* wtr = gu_new_utf8_writer(out, pool);
|
||||
return gu_new_s(pool, GuStringBuf,
|
||||
.bbuf = buf,
|
||||
.wtr = wtr);
|
||||
}
|
||||
|
||||
GuWriter*
|
||||
gu_string_buf_writer(GuStringBuf* sb)
|
||||
{
|
||||
return sb->wtr;
|
||||
}
|
||||
|
||||
static GuString
|
||||
gu_utf8_string(const uint8_t* buf, size_t sz, GuPool* pool)
|
||||
{
|
||||
if (sz < GU_MIN(sizeof(GuWord), 128)) {
|
||||
GuWord w = 0;
|
||||
for (size_t n = 0; n < sz; n++) {
|
||||
w = w << 8 | buf[n];
|
||||
}
|
||||
w = w << 8 | (sz << 1) | 1;
|
||||
return (GuString) { w };
|
||||
}
|
||||
uint8_t* p = NULL;
|
||||
if (sz < 256) {
|
||||
p = gu_malloc_aligned(pool, 1 + sz, 2);
|
||||
p[0] = (uint8_t) sz;
|
||||
} else {
|
||||
uint8_t* p =
|
||||
gu_malloc_prefixed(pool, gu_alignof(size_t),
|
||||
sizeof(size_t), 1, 1 + sizeof(sz));
|
||||
((size_t*) p)[-1] = sz;
|
||||
p[0] = 0;
|
||||
}
|
||||
memcpy(&p[1], buf, sz);
|
||||
return (GuString) { (GuWord) (void*) p };
|
||||
}
|
||||
|
||||
|
||||
|
||||
GuString
|
||||
gu_string_buf_freeze(GuStringBuf* sb, GuPool* pool)
|
||||
{
|
||||
gu_writer_flush(sb->wtr, NULL);
|
||||
uint8_t* data = gu_buf_data(sb->bbuf);
|
||||
size_t len = gu_buf_length(sb->bbuf);
|
||||
return gu_utf8_string(data, len, pool);
|
||||
}
|
||||
|
||||
GuReader*
|
||||
gu_string_reader(GuString s, GuPool* pool)
|
||||
{
|
||||
GuWord w = s.w_;
|
||||
uint8_t* buf = NULL;
|
||||
size_t len = 0;
|
||||
if (w & 1) {
|
||||
len = (w & 0xff) >> 1;
|
||||
buf = gu_new_n(uint8_t, len, pool);
|
||||
for (int i = len - 1; i >= 0; i--) {
|
||||
w >>= 8;
|
||||
buf[i] = w & 0xff;
|
||||
}
|
||||
} else {
|
||||
uint8_t* p = (void*) w;
|
||||
len = (p[0] == 0) ? ((size_t*) p)[-1] : p[0];
|
||||
buf = &p[1];
|
||||
}
|
||||
GuIn* in = gu_data_in(buf, len, pool);
|
||||
GuReader* rdr = gu_new_utf8_reader(in, pool);
|
||||
return rdr;
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_string_is_long(GuString s)
|
||||
{
|
||||
return !(s.w_ & 1);
|
||||
}
|
||||
|
||||
bool
|
||||
gu_string_is_stable(GuString s)
|
||||
{
|
||||
return !gu_string_is_long(s);
|
||||
}
|
||||
|
||||
static size_t
|
||||
gu_string_long_length(GuString s)
|
||||
{
|
||||
gu_assert(gu_string_is_long(s));
|
||||
uint8_t* p = (void*) s.w_;
|
||||
uint8_t len = p[0];
|
||||
if (len > 0) {
|
||||
return len;
|
||||
}
|
||||
return ((size_t*) p)[-1];
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_string_length(GuString s)
|
||||
{
|
||||
if (gu_string_is_long(s)) {
|
||||
return gu_string_long_length(s);
|
||||
}
|
||||
return (s.w_ & 0xff) >> 1;
|
||||
}
|
||||
|
||||
static uint8_t*
|
||||
gu_string_long_data(GuString s)
|
||||
{
|
||||
gu_require(gu_string_is_long(s));
|
||||
uint8_t* p = (void*) s.w_;
|
||||
return &p[1];
|
||||
}
|
||||
|
||||
GuString
|
||||
gu_string_copy(GuString string, GuPool* pool)
|
||||
{
|
||||
if (gu_string_is_long(string)) {
|
||||
uint8_t* data = gu_string_long_data(string);
|
||||
size_t len = gu_string_long_length(string);
|
||||
return gu_utf8_string(data, len, pool);
|
||||
} else {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_string_write(GuString s, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
GuWord w = s.w_;
|
||||
uint8_t buf[sizeof(GuWord)];
|
||||
uint8_t* src;
|
||||
size_t sz;
|
||||
if (w & 1) {
|
||||
sz = (w & 0xff) >> 1;
|
||||
gu_assert(sz <= sizeof(GuWord));
|
||||
size_t i = sz;
|
||||
while (i > 0) {
|
||||
w >>= 8;
|
||||
buf[--i] = w & 0xff;
|
||||
}
|
||||
src = buf;
|
||||
} else {
|
||||
uint8_t* p = (void*) w;
|
||||
sz = (p[0] == 0) ? ((size_t*) p)[-1] : p[0];
|
||||
src = &p[1];
|
||||
}
|
||||
gu_utf8_write(src, sz, wtr, err);
|
||||
}
|
||||
|
||||
GuString
|
||||
gu_format_string_v(const char* fmt, va_list args, GuPool* pool)
|
||||
{
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
GuStringBuf* sb = gu_string_buf(tmp_pool);
|
||||
GuWriter* wtr = gu_string_buf_writer(sb);
|
||||
gu_vprintf(fmt, args, wtr, NULL);
|
||||
gu_writer_flush(wtr, NULL);
|
||||
GuString s = gu_string_buf_freeze(sb, pool);
|
||||
gu_pool_free(tmp_pool);
|
||||
return s;
|
||||
}
|
||||
|
||||
GuString
|
||||
gu_format_string(GuPool* pool, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
GuString s = gu_format_string_v(fmt, args, pool);
|
||||
va_end(args);
|
||||
return s;
|
||||
}
|
||||
|
||||
GuString
|
||||
gu_str_string(const char* str, GuPool* pool)
|
||||
{
|
||||
#ifdef GU_CHAR_ASCII
|
||||
return gu_utf8_string((const uint8_t*) str, strlen(str), pool);
|
||||
#else
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
GuStringBuf* sb = gu_string_buf(tmp_pool);
|
||||
GuWriter* wtr = gu_string_buf_writer(sb);
|
||||
gu_puts(str, wtr, NULL);
|
||||
gu_writer_flush(wtr, NULL);
|
||||
GuString s = gu_string_buf_freeze(sb, pool);
|
||||
gu_pool_free(tmp_pool);
|
||||
return s;
|
||||
#endif
|
||||
}
|
||||
|
||||
GuWord
|
||||
gu_string_hash(GuString s)
|
||||
{
|
||||
if (s.w_ & 1) {
|
||||
return s.w_;
|
||||
}
|
||||
size_t len = gu_string_length(s);
|
||||
uint8_t* data = gu_string_long_data(s);
|
||||
return gu_hash_bytes(0, data, len);
|
||||
}
|
||||
|
||||
bool
|
||||
gu_string_eq(GuString s1, GuString s2)
|
||||
{
|
||||
if (s1.w_ == s2.w_) {
|
||||
return true;
|
||||
} else if (gu_string_is_long(s1) && gu_string_is_long(s2)) {
|
||||
size_t len1 = gu_string_long_length(s1);
|
||||
size_t len2 = gu_string_long_length(s2);
|
||||
if (len1 != len2) {
|
||||
return false;
|
||||
}
|
||||
uint8_t* data1 = gu_string_long_data(s1);
|
||||
uint8_t* data2 = gu_string_long_data(s2);
|
||||
return (memcmp(data1, data2, len1) == 0);
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static GuHash
|
||||
gu_string_hasher_hash(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
const GuString* sp = p;
|
||||
return gu_string_hash(*sp);
|
||||
}
|
||||
|
||||
static bool
|
||||
gu_string_eq_fn(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
const GuString* sp1 = p1;
|
||||
const GuString* sp2 = p2;
|
||||
return gu_string_eq(*sp1, *sp2);
|
||||
}
|
||||
|
||||
GuHasher gu_string_hasher[1] = {
|
||||
{
|
||||
.eq = { gu_string_eq_fn },
|
||||
.hash = gu_string_hasher_hash
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
GU_DEFINE_TYPE(GuString, GuOpaque, _);
|
||||
GU_DEFINE_TYPE(GuStrings, GuSeq, gu_type(GuString));
|
||||
GU_DEFINE_KIND(GuStringMap, GuMap);
|
||||
125
src/runtime/c/gu/string.h
Normal file
125
src/runtime/c/gu/string.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2011 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GU_STRING_H_
|
||||
#define GU_STRING_H_
|
||||
|
||||
#include <gu/bits.h>
|
||||
#include <gu/read.h>
|
||||
#include <gu/write.h>
|
||||
|
||||
typedef GuOpaque() GuString;
|
||||
|
||||
extern const GuString gu_empty_string;
|
||||
|
||||
GuString
|
||||
gu_string_copy(GuString string, GuPool* pool);
|
||||
|
||||
void
|
||||
gu_string_write(GuString string, GuWriter* wtr, GuExn* err);
|
||||
|
||||
GuReader*
|
||||
gu_string_reader(GuString string, GuPool* pool);
|
||||
|
||||
bool
|
||||
gu_string_is_stable(GuString string);
|
||||
|
||||
GuString
|
||||
gu_ucs_string(const GuUCS* ubuf, size_t len, GuPool* pool);
|
||||
|
||||
typedef struct GuStringBuf GuStringBuf;
|
||||
|
||||
GuStringBuf*
|
||||
gu_string_buf(GuPool* pool);
|
||||
|
||||
GuWriter*
|
||||
gu_string_buf_writer(GuStringBuf* sb);
|
||||
|
||||
GuString
|
||||
gu_string_buf_freeze(GuStringBuf* sb, GuPool* pool);
|
||||
|
||||
GuString
|
||||
gu_format_string_v(const char* fmt, va_list args, GuPool* pool);
|
||||
|
||||
GuString
|
||||
gu_format_string(GuPool* pool, const char* fmt, ...);
|
||||
|
||||
GuString
|
||||
gu_str_string(const char* str, GuPool* pool);
|
||||
|
||||
#endif // GU_STRING_H_
|
||||
|
||||
#if defined(GU_HASH_H_) && !defined(GU_STRING_H_HASH_)
|
||||
#define GU_STRING_H_HASH_
|
||||
|
||||
uintptr_t
|
||||
gu_string_hash(GuString s);
|
||||
|
||||
extern GuHasher gu_string_hasher[1];
|
||||
|
||||
bool
|
||||
gu_string_eq(GuString s1, GuString s2);
|
||||
#endif
|
||||
|
||||
#ifdef GU_TYPE_H_
|
||||
# ifndef GU_STRING_H_TYPE_
|
||||
# define GU_STRING_H_TYPE_
|
||||
|
||||
extern GU_DECLARE_TYPE(GuString, GuOpaque);
|
||||
# endif
|
||||
|
||||
# if defined(GU_SEQ_H_) && !defined(GU_STRING_H_SEQ_TYPE_)
|
||||
# define GU_STRING_H_SEQ_TYPE_
|
||||
extern GU_DECLARE_TYPE(GuStrings, GuSeq);
|
||||
# endif
|
||||
|
||||
# if defined(GU_MAP_H_TYPE_) && !defined(GU_STRING_H_MAP_TYPE_)
|
||||
# define GU_STRING_H_MAP_TYPE_
|
||||
|
||||
extern GU_DECLARE_KIND(GuStringMap);
|
||||
typedef GuType_GuMap GuType_GuStringMap;
|
||||
|
||||
#define GU_TYPE_INIT_GuStringMap(KIND, MAP_T, VAL_T, DEFAULT) \
|
||||
GU_TYPE_INIT_GuMap(KIND, MAP_T, \
|
||||
gu_type(GuString), gu_string_hasher, \
|
||||
VAL_T, DEFAULT)
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(GU_SEQ_H_) && !defined(GU_STRING_H_SEQ_)
|
||||
#define GU_STRING_H_SEQ_
|
||||
|
||||
typedef GuSeq GuStrings;
|
||||
// typedef GuBuf GuStringBuf;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(GU_MAP_H_) && !defined(GU_STRING_H_MAP_)
|
||||
#define GU_STRING_H_MAP_
|
||||
|
||||
typedef GuMap GuStringMap;
|
||||
|
||||
#define gu_new_string_map(VAL_T, DEFAULT, POOL) \
|
||||
gu_new_map(GuString, gu_string_hasher, (VAL_T), (DEFAULT), (POOL))
|
||||
|
||||
#endif
|
||||
|
||||
30
src/runtime/c/gu/sysdeps.h
Normal file
30
src/runtime/c/gu/sysdeps.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef GU_SYSDEPS_H_
|
||||
#define GU_SYSDEPS_H_
|
||||
|
||||
#include <guconfig.h>
|
||||
|
||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
||||
# define GU_GNUC
|
||||
#endif
|
||||
|
||||
#ifdef GU_GNUC
|
||||
# define GU_ALIGNOF __alignof
|
||||
# define GU_HAVE_STATEMENT_EXPRESSIONS
|
||||
# define GU_GNUC_ATTR(x) __attribute__(( x ))
|
||||
# if defined(__OPTIMIZE_SIZE__)
|
||||
# define GU_OPTIMIZE_SIZE
|
||||
# elif defined(__OPTIMIZE__)
|
||||
# define GU_OPTIMIZE_SPEED
|
||||
# endif
|
||||
#else
|
||||
# define GU_GNUC_ATTR(x)
|
||||
#endif
|
||||
|
||||
#ifdef S_SPLINT_S
|
||||
# define GU_SPLINT(x) %{ x %}
|
||||
#else
|
||||
# define GU_SPLINT(x)
|
||||
#endif
|
||||
|
||||
|
||||
#endif // GU_SYSDEPS_H_
|
||||
229
src/runtime/c/gu/type.c
Normal file
229
src/runtime/c/gu/type.c
Normal file
@@ -0,0 +1,229 @@
|
||||
|
||||
#include <gu/type.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/map.h>
|
||||
|
||||
GuKind GU_TYPE_IDENT(type)[1] = {{ .super = NULL }};
|
||||
|
||||
GU_DEFINE_KIND(alias, type);
|
||||
GU_DEFINE_KIND(typedef, alias);
|
||||
GU_DEFINE_KIND(referenced, alias);
|
||||
|
||||
GU_DEFINE_KIND(repr, type);
|
||||
GU_DEFINE_KIND(GuOpaque, repr);
|
||||
|
||||
GU_DEFINE_KIND(abstract, type);
|
||||
|
||||
GU_DEFINE_KIND(struct, repr);
|
||||
|
||||
GU_DEFINE_KIND(pointer, repr);
|
||||
GU_DEFINE_KIND(reference, pointer);
|
||||
GU_DEFINE_KIND(shared, pointer);
|
||||
|
||||
GU_DEFINE_KIND(primitive, repr);
|
||||
|
||||
// sizeof(void) is illegal, so do this manually
|
||||
GuPrimType GU_TYPE_IDENT(void)[1] = {{
|
||||
.repr_base = {
|
||||
.type_base = {
|
||||
.kind_base = {
|
||||
.super = gu_kind(primitive),
|
||||
},
|
||||
},
|
||||
.size = 0,
|
||||
.align = 1,
|
||||
},
|
||||
.name = "void",
|
||||
}};
|
||||
|
||||
GU_DEFINE_KIND(integer, primitive);
|
||||
GU_DEFINE_TYPE(char, integer, _);
|
||||
|
||||
GU_DEFINE_KIND(signed, integer);
|
||||
GU_DEFINE_TYPE(int, signed, _);
|
||||
GU_DEFINE_TYPE(int8_t, signed, _);
|
||||
GU_DEFINE_TYPE(int16_t, signed, _);
|
||||
GU_DEFINE_TYPE(int32_t, signed, _);
|
||||
GU_DEFINE_TYPE(int64_t, signed, _);
|
||||
GU_DEFINE_TYPE(intptr_t, signed, _);
|
||||
GU_DEFINE_TYPE(intmax_t, signed, _);
|
||||
|
||||
GU_DEFINE_KIND(unsigned, integer);
|
||||
GU_DEFINE_TYPE(uint8_t, unsigned, _);
|
||||
GU_DEFINE_TYPE(uint16_t, unsigned, _);
|
||||
GU_DEFINE_TYPE(uint32_t, unsigned, _);
|
||||
GU_DEFINE_TYPE(uint64_t, unsigned, _);
|
||||
GU_DEFINE_TYPE(uintmax_t, unsigned, _);
|
||||
GU_DEFINE_TYPE(size_t, unsigned, _);
|
||||
|
||||
GU_DEFINE_TYPE(GuLength, unsigned, _);
|
||||
|
||||
GU_DEFINE_KIND(GuFloating, primitive);
|
||||
GU_DEFINE_TYPE(float, GuFloating, _);
|
||||
GU_DEFINE_TYPE(double, GuFloating, _);
|
||||
GU_DEFINE_TYPE(GuLongDouble, GuFloating, _);
|
||||
|
||||
|
||||
GU_DEFINE_KIND(enum, repr);
|
||||
|
||||
bool gu_type_has_kind(GuType* type, GuKind* kind)
|
||||
{
|
||||
GuKind* k = (GuKind*)type;
|
||||
while (k != NULL) {
|
||||
if (k == kind) {
|
||||
return true;
|
||||
}
|
||||
k = k->super;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
struct GuTypeMap {
|
||||
GuMap* map;
|
||||
};
|
||||
|
||||
static void
|
||||
gu_type_map_init(GuTypeMap* tmap, GuTypeTable* table)
|
||||
{
|
||||
for (int i = 0; i < table->parents.len; i++) {
|
||||
gu_type_map_init(tmap, table->parents.elems[i]);
|
||||
}
|
||||
for (int i = 0; i < table->entries.len; i++) {
|
||||
GuTypeTableEntry* e = &table->entries.elems[i];
|
||||
gu_map_put(tmap->map, e->kind, void*, e->val);
|
||||
}
|
||||
}
|
||||
|
||||
GuTypeMap*
|
||||
gu_new_type_map(GuTypeTable* table, GuPool* pool)
|
||||
{
|
||||
GuTypeMap* tmap =
|
||||
gu_new_i(pool, GuTypeMap,
|
||||
.map = gu_new_map(GuKind, NULL, void*, &gu_null, pool));
|
||||
gu_type_map_init(tmap, table);
|
||||
return tmap;
|
||||
}
|
||||
|
||||
bool
|
||||
gu_struct_has_flex(GuStructRepr* srepr)
|
||||
{
|
||||
for (int i = 0; i < srepr->members.len; i++) {
|
||||
if (srepr->members.elems[i].is_flex) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_type_map_get(GuTypeMap* tmap, GuType* type)
|
||||
{
|
||||
GuKind* kind = (GuKind*)type;
|
||||
while (kind != NULL) {
|
||||
void* val = gu_map_get(tmap->map, kind, void*);
|
||||
if (val != NULL) {
|
||||
return val;
|
||||
}
|
||||
kind = kind->super;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const void*
|
||||
gu_type_dyn_cast(GuType* type, GuKind* kind)
|
||||
{
|
||||
if (gu_type_has_kind(type, kind)) {
|
||||
return type;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const void*
|
||||
gu_type_check_cast(GuType* type, GuKind* kind)
|
||||
{
|
||||
gu_assert(gu_type_has_kind(type, kind));
|
||||
return type;
|
||||
}
|
||||
|
||||
GuTypeRepr*
|
||||
gu_type_repr(GuType* type)
|
||||
{
|
||||
GuTypeAlias* alias;
|
||||
while ((alias = gu_type_try_cast(type, alias))) {
|
||||
type = alias->type;
|
||||
}
|
||||
return gu_type_try_cast(type, repr);
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_type_size(GuType* type)
|
||||
{
|
||||
GuTypeRepr* repr = gu_type_repr(type);
|
||||
return repr ? repr->size : 0;
|
||||
}
|
||||
|
||||
GuEnumConstant*
|
||||
gu_enum_value(GuEnumType* etype, const void* enump)
|
||||
{
|
||||
size_t esize = etype->repr_base.size;
|
||||
#define CHECK_ENUM_TYPE(t_) do { \
|
||||
if (esize == sizeof(t_)) { \
|
||||
t_ c = *(const t_*)enump; \
|
||||
for (int i = 0; i < etype->constants.len; i++) { \
|
||||
GuEnumConstant* cp = &etype->constants.elems[i]; \
|
||||
t_ d = *(const t_*)cp->enum_value; \
|
||||
if (c == d) { \
|
||||
return cp; \
|
||||
} \
|
||||
} \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
CHECK_ENUM_TYPE(int);
|
||||
CHECK_ENUM_TYPE(char);
|
||||
CHECK_ENUM_TYPE(short);
|
||||
CHECK_ENUM_TYPE(long);
|
||||
CHECK_ENUM_TYPE(long long);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_type_malloc(GuType* type, GuPool* pool)
|
||||
{
|
||||
GuTypeRepr* repr = gu_type_repr(type);
|
||||
gu_assert(repr);
|
||||
return gu_malloc_aligned(pool, repr->size, repr->align);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
typedef const struct GuPtrConvFns GuPtrConvFns;
|
||||
|
||||
struct GuPtrConvFns {
|
||||
void* (*get)(const void* pp);
|
||||
void (*set)(void** pp, void* p);
|
||||
};
|
||||
|
||||
#define GU_TYPE_PTR_DEFINE_GETSET(name_, t_) \
|
||||
static void* gu_type_##name_##_ptr_get(const void* pp) { \
|
||||
return *(t_* const*) pp; \
|
||||
} \
|
||||
\
|
||||
static void gu_type_##name_##_ptr_set(void* pp, void* p) { \
|
||||
*(t_**) pp = p; \
|
||||
} \
|
||||
static GuPtrConvFns gu_ptr_conv_##name_ = { \
|
||||
.get = gu_type_##name_##_ptr_get, \
|
||||
.set = gu_type_##name_##_ptr_set \
|
||||
}
|
||||
|
||||
GU_TYPE_PTR_DEFINE_GETSET(void, void);
|
||||
GU_TYPE_PTR_DEFINE_GETSET(struct, GuStruct);
|
||||
GU_TYPE_PTR_DEFINE_GETSET(int, int);
|
||||
|
||||
|
||||
#endif
|
||||
454
src/runtime/c/gu/type.h
Normal file
454
src/runtime/c/gu/type.h
Normal file
@@ -0,0 +1,454 @@
|
||||
|
||||
#ifndef GU_TYPE_H_
|
||||
#define GU_TYPE_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
|
||||
//
|
||||
// kind
|
||||
//
|
||||
|
||||
typedef const struct GuKind GuKind;
|
||||
|
||||
struct GuKind {
|
||||
GuKind* super;
|
||||
};
|
||||
|
||||
// Use GU_PASTE here so k_ can be preprocessor-expanded
|
||||
#define GU_TYPE_IDENT(k_) GU_PASTE(gu_type__,k_)
|
||||
|
||||
#define gu_kind(k_) ((GuKind*)GU_TYPE_IDENT(k_))
|
||||
|
||||
#define GU_DECLARE_KIND(k_) \
|
||||
GuKind GU_TYPE_IDENT(k_)[1]
|
||||
|
||||
extern GU_DECLARE_KIND(kind);
|
||||
|
||||
#define GU_DEFINE_KIND(k_, super_k_) \
|
||||
GuKind GU_TYPE_IDENT(k_)[1] = {{ .super = gu_kind(super_k_) }}
|
||||
|
||||
//
|
||||
// type
|
||||
//
|
||||
|
||||
typedef const struct GuType GuType;
|
||||
|
||||
struct GuType {
|
||||
GuKind kind_base;
|
||||
};
|
||||
|
||||
typedef GuType GuType_type;
|
||||
|
||||
extern GU_DECLARE_KIND(type);
|
||||
|
||||
#define GU_TYPE_INIT_type(k_, t_, _) { .kind_base = { .super = gu_kind(k_) } }
|
||||
|
||||
#define gu_type(t_) ((GuType*)gu_kind(t_))
|
||||
|
||||
|
||||
#define GU_KIND_TYPE(k_) GU_PASTE(GuType_,k_)
|
||||
|
||||
// This cannot be used indirectly, since we don't want to pp-expand k_.
|
||||
// We must inline the body into other macros.
|
||||
#define GU_TYPE_INIT(k_, ...) \
|
||||
GU_TYPE_INIT_##k_(k_, __VA_ARGS__)
|
||||
|
||||
//#define GU_TYPE_LIT(k_, ...)
|
||||
// ((GuType*)(GuType_##k_[]){GU_TYPE_INIT(k_, __VA_ARGS__)})
|
||||
#define GU_TYPE_LIT(k_, ...) \
|
||||
((GuType*)&(GU_KIND_TYPE(k_)) GU_TYPE_INIT_##k_(k_, __VA_ARGS__))
|
||||
|
||||
#define GU_DECLARE_TYPE(t_, k_) \
|
||||
GU_KIND_TYPE(k_) GU_TYPE_IDENT(t_)[1]
|
||||
|
||||
//#define GU_DEFINE_TYPE(t_, k_, ...)
|
||||
// GuType_##k_ GU_TYPE_IDENT(t_) = GU_TYPE_INIT(k_, t_, __VA_ARGS__)
|
||||
#define GU_DEFINE_TYPE(t_, k_, ...) \
|
||||
GU_KIND_TYPE(k_) GU_TYPE_IDENT(t_)[1] = \
|
||||
{ GU_TYPE_INIT_##k_(k_, t_, __VA_ARGS__) }
|
||||
|
||||
#define GU_DEFINE_TYPE_ALIAS(t1_, t2_) \
|
||||
static GuType* const GU_TYPE_IDENT(t1_) = gu_type(t2_)
|
||||
|
||||
|
||||
//
|
||||
// abstract
|
||||
//
|
||||
|
||||
typedef GuType GuType_abstract;
|
||||
|
||||
#define GU_TYPE_INIT_abstract(k_, t_, _) \
|
||||
GU_TYPE_INIT_type(k_, t_, _)
|
||||
|
||||
extern GU_DECLARE_KIND(abstract);
|
||||
|
||||
|
||||
//
|
||||
// repr
|
||||
//
|
||||
|
||||
typedef struct GuTypeRepr GuTypeRepr, GuType_repr;
|
||||
|
||||
struct GuTypeRepr {
|
||||
GuType type_base;
|
||||
uint16_t size;
|
||||
uint16_t align;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_repr(k_, t_, _) { \
|
||||
.type_base = GU_TYPE_INIT_type(k_, t_, _), \
|
||||
.size = sizeof(t_), \
|
||||
.align = gu_alignof(t_) \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(repr);
|
||||
|
||||
|
||||
|
||||
//
|
||||
// GuOpaque
|
||||
//
|
||||
|
||||
typedef GuType_repr GuType_GuOpaque;
|
||||
|
||||
#define GU_TYPE_INIT_GuOpaque GU_TYPE_INIT_repr
|
||||
|
||||
extern GU_DECLARE_KIND(GuOpaque);
|
||||
|
||||
//
|
||||
// pointer
|
||||
//
|
||||
|
||||
typedef const struct GuPointerType GuPointerType, GuType_pointer;
|
||||
|
||||
struct GuPointerType {
|
||||
GuType_repr repr_base;
|
||||
GuType* pointed_type;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_pointer(k_, t_, pointed_) \
|
||||
{ \
|
||||
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
|
||||
.pointed_type = pointed_ \
|
||||
}
|
||||
|
||||
|
||||
extern GU_DECLARE_KIND(pointer);
|
||||
|
||||
#define gu_ptr_type(t_) \
|
||||
GU_TYPE_LIT(pointer, t_*, gu_type(t_))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// alias
|
||||
//
|
||||
|
||||
|
||||
typedef const struct GuTypeAlias GuTypeAlias, GuType_alias;
|
||||
|
||||
struct GuTypeAlias {
|
||||
GuType type_base;
|
||||
GuType* type;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_alias(k_, t_, type_) { \
|
||||
.type_base = GU_TYPE_INIT_type(k_, t_, _), \
|
||||
.type = type_ \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(alias);
|
||||
|
||||
//
|
||||
// typedef
|
||||
//
|
||||
|
||||
typedef const struct GuTypeDef GuTypeDef, GuType_typedef;
|
||||
|
||||
struct GuTypeDef {
|
||||
GuType_alias alias_base;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_typedef(k_, t_, type_) { \
|
||||
.alias_base = GU_TYPE_INIT_alias(k_, t_, type_), \
|
||||
.name = #t_, \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(typedef);
|
||||
|
||||
#define GU_DEFINE_TYPEDEF_X(t_, dk_, k_, ...) \
|
||||
GU_DEFINE_TYPE(t_, dk_, GU_TYPE_LIT(k_, t_, __VA_ARGS__))
|
||||
|
||||
#define GU_DEFINE_TYPEDEF(t_, ...) \
|
||||
GU_DEFINE_TYPEDEF_X(t_, typedef, __VA_ARGS__)
|
||||
|
||||
|
||||
|
||||
//
|
||||
// referenced
|
||||
//
|
||||
|
||||
extern GU_DECLARE_KIND(referenced);
|
||||
|
||||
typedef GuType_alias GuType_referenced;
|
||||
|
||||
#define GU_TYPE_INIT_referenced GU_TYPE_INIT_alias
|
||||
|
||||
|
||||
|
||||
#include <gu/list.h>
|
||||
|
||||
//
|
||||
// struct
|
||||
//
|
||||
|
||||
typedef const struct GuStructRepr GuStructRepr, GuType_struct;
|
||||
|
||||
typedef const struct GuMember GuMember;
|
||||
|
||||
struct GuMember {
|
||||
ptrdiff_t offset;
|
||||
const char* name;
|
||||
GuType* type;
|
||||
bool is_flex;
|
||||
};
|
||||
|
||||
struct GuStructRepr {
|
||||
GuType_repr repr_base;
|
||||
const char* name;
|
||||
GuSList(GuMember) members;
|
||||
};
|
||||
|
||||
extern GU_DECLARE_KIND(struct);
|
||||
|
||||
#define GU_MEMBER_AUX_(struct_, member_, type_, is_flex_) \
|
||||
{ \
|
||||
.offset = offsetof(struct_, member_), \
|
||||
.name = #member_, \
|
||||
.type = type_, \
|
||||
.is_flex = is_flex_, \
|
||||
}
|
||||
|
||||
#define GU_MEMBER_V(struct_, member_, type_) \
|
||||
GU_MEMBER_AUX_(struct_, member_, type_, false)
|
||||
|
||||
#define GU_MEMBER(s_, m_, t_) \
|
||||
GU_MEMBER_V(s_, m_, gu_type(t_))
|
||||
|
||||
#define GU_MEMBER_P(s_, m_, t_) \
|
||||
GU_MEMBER_V(s_, m_, gu_ptr_type(t_))
|
||||
|
||||
#define GU_MEMBER_S(s_, m_, t_) \
|
||||
GU_MEMBER_V(s_, m_, gu_shared_ptr_type(t_))
|
||||
|
||||
#define GU_FLEX_MEMBER_V(struct_, member_, type_) \
|
||||
GU_MEMBER_AUX_(struct_, member_, type_, true)
|
||||
|
||||
#define GU_FLEX_MEMBER(s_, m_, t_) \
|
||||
GU_FLEX_MEMBER_V(s_, m_, gu_type(t_))
|
||||
|
||||
#define GU_FLEX_MEMBER_P(s_, m_, t_) \
|
||||
GU_FLEX_MEMBER_V(s_, m_, gu_ptr_type(t_))
|
||||
|
||||
|
||||
#define GU_TYPE_INIT_struct(k_, t_, ...) { \
|
||||
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
|
||||
.name = #t_, \
|
||||
.members = GU_SLIST(GuMember, __VA_ARGS__) \
|
||||
}
|
||||
|
||||
bool
|
||||
gu_struct_has_flex(GuStructRepr* srepr);
|
||||
|
||||
|
||||
//
|
||||
// reference
|
||||
//
|
||||
|
||||
typedef GuType_pointer GuType_reference;
|
||||
|
||||
#define GU_TYPE_INIT_reference GU_TYPE_INIT_pointer
|
||||
|
||||
extern GU_DECLARE_KIND(reference);
|
||||
|
||||
|
||||
//
|
||||
// shared
|
||||
//
|
||||
|
||||
typedef GuType_pointer GuType_shared;
|
||||
|
||||
#define GU_TYPE_INIT_shared GU_TYPE_INIT_pointer
|
||||
|
||||
extern GU_DECLARE_KIND(shared);
|
||||
|
||||
#define gu_shared_ptr_type(t_) \
|
||||
GU_TYPE_LIT(shared, t_*, gu_type(t_))
|
||||
|
||||
//
|
||||
// primitives
|
||||
//
|
||||
|
||||
typedef const struct GuPrimType GuPrimType, GuType_primitive;
|
||||
|
||||
struct GuPrimType {
|
||||
GuType_repr repr_base;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_primitive(k_, t_, _) { \
|
||||
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
|
||||
.name = #t_ \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(primitive);
|
||||
extern GU_DECLARE_TYPE(void, primitive);
|
||||
|
||||
#define GU_TYPE_INIT_integer GU_TYPE_INIT_primitive
|
||||
typedef GuType_primitive GuType_integer;
|
||||
extern GU_DECLARE_KIND(integer);
|
||||
extern GU_DECLARE_TYPE(char, integer);
|
||||
|
||||
#define GU_TYPE_INIT_signed GU_TYPE_INIT_integer
|
||||
typedef GuType_integer GuType_signed;
|
||||
extern GU_DECLARE_KIND(signed);
|
||||
extern GU_DECLARE_TYPE(int, signed);
|
||||
extern GU_DECLARE_TYPE(int8_t, signed);
|
||||
extern GU_DECLARE_TYPE(int16_t, signed);
|
||||
extern GU_DECLARE_TYPE(int32_t, signed);
|
||||
extern GU_DECLARE_TYPE(int64_t, signed);
|
||||
extern GU_DECLARE_TYPE(intptr_t, signed);
|
||||
extern GU_DECLARE_TYPE(intmax_t, signed);
|
||||
|
||||
#define GU_TYPE_INIT_unsigned GU_TYPE_INIT_integer
|
||||
typedef GuType_integer GuType_unsigned;
|
||||
extern GU_DECLARE_KIND(unsigned);
|
||||
extern GU_DECLARE_TYPE(uint8_t, unsigned);
|
||||
extern GU_DECLARE_TYPE(uint16_t, unsigned);
|
||||
extern GU_DECLARE_TYPE(uint32_t, unsigned);
|
||||
extern GU_DECLARE_TYPE(uint64_t, unsigned);
|
||||
extern GU_DECLARE_TYPE(uintmax_t, unsigned);
|
||||
extern GU_DECLARE_TYPE(size_t, unsigned);
|
||||
|
||||
typedef size_t GuLength;
|
||||
extern GU_DECLARE_TYPE(GuLength, unsigned); // TODO: get rid
|
||||
|
||||
|
||||
#define GU_TYPE_INIT_GuFloating GU_TYPE_INIT_primitive
|
||||
typedef GuType_primitive GuType_GuFloating;
|
||||
extern GU_DECLARE_KIND(GuFloating);
|
||||
extern GU_DECLARE_TYPE(float, GuFloating);
|
||||
extern GU_DECLARE_TYPE(double, GuFloating);
|
||||
typedef long double GuLongDouble;
|
||||
extern GU_DECLARE_TYPE(GuLongDouble, GuFloating);
|
||||
|
||||
|
||||
|
||||
//
|
||||
// enum
|
||||
//
|
||||
|
||||
extern GU_DECLARE_KIND(enum);
|
||||
|
||||
typedef const struct GuEnumConstant GuEnumConstant;
|
||||
|
||||
struct GuEnumConstant {
|
||||
const char* name;
|
||||
int64_t value;
|
||||
const void* enum_value;
|
||||
};
|
||||
|
||||
typedef const struct GuEnumType GuEnumType, GuType_enum;
|
||||
|
||||
struct GuEnumType {
|
||||
GuType_repr repr_base;
|
||||
GuSList(GuEnumConstant) constants;
|
||||
};
|
||||
|
||||
#define GU_ENUM_C(t_, x) { \
|
||||
.name = #x, \
|
||||
.value = x, \
|
||||
.enum_value = (const t_[1]){ x } \
|
||||
}
|
||||
|
||||
#define GU_TYPE_INIT_enum(k_, t_, ...) { \
|
||||
.repr_base = GU_TYPE_INIT_repr(k_, t_, _), \
|
||||
.constants = GU_SLIST(GuEnumConstant, __VA_ARGS__) \
|
||||
}
|
||||
|
||||
GuEnumConstant*
|
||||
gu_enum_value(GuEnumType* etype, const void* enump);
|
||||
|
||||
|
||||
|
||||
|
||||
bool gu_type_has_kind(const GuType* type, const GuKind* kind);
|
||||
|
||||
|
||||
|
||||
|
||||
typedef const struct GuTypeTableEntry GuTypeTableEntry;
|
||||
|
||||
struct GuTypeTableEntry {
|
||||
GuKind* kind;
|
||||
void* val;
|
||||
};
|
||||
|
||||
typedef const struct GuTypeTable GuTypeTable;
|
||||
|
||||
struct GuTypeTable {
|
||||
GuSList(const GuTypeTable*) parents;
|
||||
GuSList(GuTypeTableEntry) entries;
|
||||
};
|
||||
|
||||
#define GU_TYPETABLE(parents_, ...) { \
|
||||
.parents = parents_, \
|
||||
.entries = GU_SLIST(GuTypeTableEntry, \
|
||||
__VA_ARGS__) \
|
||||
}
|
||||
|
||||
typedef struct GuTypeMap GuTypeMap;
|
||||
|
||||
GuTypeMap*
|
||||
gu_new_type_map(GuTypeTable* table, GuPool* pool);
|
||||
|
||||
void*
|
||||
gu_type_map_get(GuTypeMap* tmap, GuType* type);
|
||||
|
||||
size_t
|
||||
gu_type_size(GuType* type);
|
||||
|
||||
GuTypeRepr*
|
||||
gu_type_repr(GuType* type);
|
||||
|
||||
const void*
|
||||
gu_type_check_cast(GuType* t, GuKind* k);
|
||||
|
||||
const void*
|
||||
gu_type_dyn_cast(GuType* t, GuKind* k);
|
||||
|
||||
#define gu_type_try_cast(type_, k_) \
|
||||
((GU_KIND_TYPE(k_)*)gu_type_dyn_cast(type_, gu_kind(k_)))
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define gu_type_cast(type_, k_) \
|
||||
((GU_KIND_TYPE(k_)*)gu_type_check_cast(type_, gu_kind(k_)))
|
||||
#else
|
||||
#define gu_type_cast(type_, k_) \
|
||||
((GU_KIND_TYPE(k_)*)(type_))
|
||||
#endif
|
||||
|
||||
void* gu_type_malloc(GuType* type, GuPool* pool);
|
||||
|
||||
#if 0
|
||||
void* gu_type_ptr_get(GuType* type, const void* pp);
|
||||
void gu_type_ptr_set(GuType* type, void* pp, void* p);
|
||||
#endif
|
||||
|
||||
|
||||
#endif // GU_TYPE_H_
|
||||
135
src/runtime/c/gu/ucs.c
Normal file
135
src/runtime/c/gu/ucs.c
Normal file
@@ -0,0 +1,135 @@
|
||||
#include <gu/ucs.h>
|
||||
#include <gu/assert.h>
|
||||
#include <guconfig.h>
|
||||
|
||||
GU_DEFINE_TYPE(GuUCSExn, abstract, _);
|
||||
|
||||
|
||||
#ifdef GU_CHAR_ASCII
|
||||
|
||||
bool
|
||||
gu_char_is_valid(char c)
|
||||
{
|
||||
if (c < 0) {
|
||||
return false;
|
||||
} else if (c < 64) {
|
||||
return UINT64_C(0xffffffef00003f81) & (UINT64_C(1) << c);
|
||||
}
|
||||
#if CHAR_MAX > 127 // Let's avoid spurious warnings
|
||||
else if (c > 127) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return UINT64_C(0x7ffffffefffffffe) & (UINT64_C(1) << (c - 64));
|
||||
}
|
||||
|
||||
char
|
||||
gu_ucs_char(GuUCS uc, GuExn* err)
|
||||
{
|
||||
if (0 <= uc && uc <= 127) {
|
||||
char c = (char) uc;
|
||||
if (gu_char_is_valid(c)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
gu_raise(err, GuUCSExn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // defined(GU_CHAR_ASCII)
|
||||
|
||||
static const char gu_ucs_ascii[128] =
|
||||
"\0\0\0\0\0\0\0\a\b\t\n\v\f\r\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
" !\"#\0%&'()*+,-./0123456789:;<=>?"
|
||||
"\0ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
|
||||
"\0abcdefghijklmnopqrstuvwxyz{|}~\0";
|
||||
|
||||
const uint8_t gu_ucs_ascii_reverse_[CHAR_MAX] = {
|
||||
['\0'] = 0x00, ['\a'] = 0x07, ['\b'] = 0x08, ['\t'] = 0x09,
|
||||
['\n'] = 0x0a, ['\v'] = 0x0b, ['\f'] = 0x0c, ['\r'] = 0x0d,
|
||||
[' '] = 0x20, ['!'] = 0x21, ['"'] = 0x22, ['#'] = 0x23, ['%'] = 0x25,
|
||||
['&'] = 0x26, ['\''] = 0x27, ['('] = 0x28, [')'] = 0x29, ['*'] = 0x2a,
|
||||
['+'] = 0x2b, [','] = 0x2c, ['-'] = 0x2d, ['.'] = 0x2e, ['/'] = 0x2f,
|
||||
['0'] = 0x30, ['1'] = 0x31, ['2'] = 0x32, ['3'] = 0x33, ['4'] = 0x34,
|
||||
['5'] = 0x35, ['6'] = 0x36, ['7'] = 0x37, ['8'] = 0x38, ['9'] = 0x39,
|
||||
[':'] = 0x3a, [';'] = 0x3b, ['<'] = 0x3c, ['='] = 0x3d, ['>'] = 0x3e,
|
||||
['?'] = 0x3f, ['A'] = 0x41, ['B'] = 0x42, ['C'] = 0x43, ['D'] = 0x44,
|
||||
['E'] = 0x45, ['F'] = 0x46, ['G'] = 0x47, ['H'] = 0x48, ['I'] = 0x49,
|
||||
['J'] = 0x4a, ['K'] = 0x4b, ['L'] = 0x4c, ['M'] = 0x4d, ['N'] = 0x4e,
|
||||
['O'] = 0x4f, ['P'] = 0x50, ['Q'] = 0x51, ['R'] = 0x52, ['S'] = 0x53,
|
||||
['T'] = 0x54, ['U'] = 0x55, ['V'] = 0x56, ['W'] = 0x57, ['X'] = 0x58,
|
||||
['Y'] = 0x59, ['Z'] = 0x5a, ['['] = 0x5b, ['\\'] = 0x5c, [']'] = 0x5d,
|
||||
['^'] = 0x5e, ['_'] = 0x5f, ['a'] = 0x61, ['b'] = 0x62, ['c'] = 0x63,
|
||||
['d'] = 0x64, ['e'] = 0x65, ['f'] = 0x66, ['g'] = 0x67, ['h'] = 0x68,
|
||||
['i'] = 0x69, ['j'] = 0x6a, ['k'] = 0x6b, ['l'] = 0x6c, ['m'] = 0x6d,
|
||||
['n'] = 0x6e, ['o'] = 0x6f, ['p'] = 0x70, ['q'] = 0x71, ['r'] = 0x72,
|
||||
['s'] = 0x73, ['t'] = 0x74, ['u'] = 0x75, ['v'] = 0x76, ['w'] = 0x77,
|
||||
['x'] = 0x78, ['y'] = 0x79, ['z'] = 0x7a, ['{'] = 0x7b, ['|'] = 0x7c,
|
||||
['}'] = 0x7d, ['~'] = 0x7e
|
||||
};
|
||||
|
||||
|
||||
bool
|
||||
gu_char_is_valid(char c)
|
||||
{
|
||||
if (c > 0) {
|
||||
return (gu_ucs_ascii_reverse_[(int) c] > 0);
|
||||
}
|
||||
return (c == '\0');
|
||||
}
|
||||
|
||||
char
|
||||
gu_ucs_char(GuUCS uc, GuExn* err)
|
||||
{
|
||||
if (uc == 0) {
|
||||
return '\0';
|
||||
} else if (0 < uc && uc <= 127) {
|
||||
char c = gu_ucs_ascii[uc];
|
||||
if (c != '\0') {
|
||||
return (unsigned char) c;
|
||||
}
|
||||
}
|
||||
gu_raise(err, GuUCSExn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
size_t
|
||||
gu_str_to_ucs(const char* cbuf, size_t len, GuUCS* ubuf, GuExn* err)
|
||||
{
|
||||
size_t n = 0;
|
||||
while (n < len) {
|
||||
char c = cbuf[n];
|
||||
if (!gu_char_is_valid(c)) {
|
||||
gu_raise(err, GuUCSExn);
|
||||
return n;
|
||||
}
|
||||
ubuf[n] = gu_char_ucs(c);
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_ucs_to_str(const GuUCS* ubuf, size_t len, char* cbuf, GuExn* err)
|
||||
{
|
||||
size_t n = 0;
|
||||
while (n < len) {
|
||||
char c = gu_ucs_char(ubuf[n], err);
|
||||
if (!gu_ok(err)) {
|
||||
break;
|
||||
}
|
||||
cbuf[n] = c;
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
extern inline bool
|
||||
gu_ucs_valid(GuUCS ucs);
|
||||
|
||||
extern inline GuUCS
|
||||
gu_char_ucs(char c);
|
||||
53
src/runtime/c/gu/ucs.h
Normal file
53
src/runtime/c/gu/ucs.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef GU_UCS_H_
|
||||
#define GU_UCS_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/exn.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
|
||||
#if defined(__STDC_ISO_10646__) && WCHAR_MAX >= 0x10FFFF
|
||||
#include <wchar.h>
|
||||
#define GU_UCS_WCHAR
|
||||
typedef wchar_t GuUCS;
|
||||
#else
|
||||
typedef int32_t GuUCS;
|
||||
#endif
|
||||
|
||||
#define GU_UCS_MAX ((GuUCS)(0x10FFFF))
|
||||
|
||||
bool
|
||||
gu_char_is_valid(char c);
|
||||
|
||||
inline bool
|
||||
gu_ucs_valid(GuUCS ucs)
|
||||
{
|
||||
return ucs >= 0 && ucs <= GU_UCS_MAX;
|
||||
}
|
||||
|
||||
inline GuUCS
|
||||
gu_char_ucs(char c)
|
||||
{
|
||||
gu_require(gu_char_is_valid(c));
|
||||
#ifdef GU_CHAR_ASCII
|
||||
GuUCS u = (GuUCS) c;
|
||||
#else
|
||||
extern const uint8_t gu_ucs_ascii_reverse_[CHAR_MAX];
|
||||
GuUCS u = gu_ucs_ascii_reverse_[(unsigned char) c];
|
||||
#endif
|
||||
gu_ensure(u < 0x80);
|
||||
return u;
|
||||
}
|
||||
|
||||
char
|
||||
gu_ucs_char(GuUCS uc, GuExn* err);
|
||||
|
||||
size_t
|
||||
gu_str_to_ucs(const char* cbuf, size_t len, GuUCS* ubuf, GuExn* err);
|
||||
|
||||
size_t
|
||||
gu_ucs_to_str(const GuUCS* ubuf, size_t len, char* cbuf, GuExn* err);
|
||||
|
||||
extern GU_DECLARE_TYPE(GuUCSExn, abstract);
|
||||
|
||||
#endif // GU_ISO10646_H_
|
||||
220
src/runtime/c/gu/utf8.c
Normal file
220
src/runtime/c/gu/utf8.c
Normal file
@@ -0,0 +1,220 @@
|
||||
#include <gu/assert.h>
|
||||
#include <gu/utf8.h>
|
||||
#include <guconfig.h>
|
||||
|
||||
GuUCS
|
||||
gu_utf8_decode(const uint8_t** src_inout)
|
||||
{
|
||||
const uint8_t* src = *src_inout;
|
||||
uint8_t c = src[0];
|
||||
if (c < 0x80) {
|
||||
*src_inout = src + 1;
|
||||
return (GuUCS) c;
|
||||
}
|
||||
size_t len = (c < 0xe0 ? 1 :
|
||||
c < 0xf0 ? 2 :
|
||||
3);
|
||||
uint32_t mask = 0x07071f7f;
|
||||
uint32_t u = c & (mask >> (len * 8));
|
||||
for (size_t i = 1; i <= len; i++) {
|
||||
c = src[i];
|
||||
u = u << 6 | (c & 0x3f);
|
||||
}
|
||||
*src_inout = &src[len + 1];
|
||||
return (GuUCS) u;
|
||||
}
|
||||
|
||||
GuUCS
|
||||
gu_in_utf8_(GuIn* in, GuExn* err)
|
||||
{
|
||||
uint8_t c = gu_in_u8(in, err);
|
||||
if (!gu_ok(err)) {
|
||||
return 0;
|
||||
}
|
||||
int len = (c < 0x80 ? 0 :
|
||||
c < 0xc2 ? -1 :
|
||||
c < 0xe0 ? 1 :
|
||||
c < 0xf0 ? 2 :
|
||||
c < 0xf5 ? 3 :
|
||||
-1);
|
||||
if (len < 0) {
|
||||
goto fail;
|
||||
} else if (len == 0) {
|
||||
return c;
|
||||
}
|
||||
static const uint8_t mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 };
|
||||
uint32_t u = c & mask[len];
|
||||
uint8_t buf[3];
|
||||
// If reading the extra bytes causes EOF, it is an encoding
|
||||
// error, not a legitimate end of character stream.
|
||||
GuExn* tmp_err = gu_exn(err, GuEOF, NULL);
|
||||
gu_in_bytes(in, buf, len, tmp_err);
|
||||
if (tmp_err->caught) {
|
||||
goto fail;
|
||||
}
|
||||
if (!gu_ok(err)) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
c = buf[i];
|
||||
if ((c & 0xc0) != 0x80) {
|
||||
goto fail;
|
||||
}
|
||||
u = u << 6 | (c & 0x3f);
|
||||
}
|
||||
GuUCS ucs = (GuUCS) u;
|
||||
if (!gu_ucs_valid(ucs)) {
|
||||
goto fail;
|
||||
}
|
||||
return ucs;
|
||||
|
||||
fail:
|
||||
gu_raise(err, GuUCSExn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
gu_advance_utf8(GuUCS ucs, uint8_t* buf)
|
||||
{
|
||||
gu_require(gu_ucs_valid(ucs));
|
||||
if (ucs < 0x80) {
|
||||
buf[0] = (uint8_t) ucs;
|
||||
return 1;
|
||||
} else if (ucs < 0x800) {
|
||||
buf[0] = 0xc0 | (ucs >> 6);
|
||||
buf[1] = 0x80 | (ucs & 0x3f);
|
||||
return 2;
|
||||
} else if (ucs < 0x10000) {
|
||||
buf[0] = 0xe0 | (ucs >> 12);
|
||||
buf[1] = 0x80 | ((ucs >> 6) & 0x3f);
|
||||
buf[2] = 0x80 | (ucs & 0x3f);
|
||||
return 3;
|
||||
} else {
|
||||
buf[0] = 0xf0 | (ucs >> 18);
|
||||
buf[1] = 0x80 | ((ucs >> 12) & 0x3f);
|
||||
buf[2] = 0x80 | ((ucs >> 6) & 0x3f);
|
||||
buf[3] = 0x80 | (ucs & 0x3f);
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
char
|
||||
gu_in_utf8_char_(GuIn* in, GuExn* err)
|
||||
{
|
||||
return gu_ucs_char(gu_in_utf8(in, err), err);
|
||||
}
|
||||
|
||||
void
|
||||
gu_out_utf8_long_(GuUCS ucs, GuOut* out, GuExn* err)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
size_t sz = gu_advance_utf8(ucs, buf);
|
||||
switch (sz) {
|
||||
case 2:
|
||||
gu_out_bytes(out, buf, 2, err);
|
||||
break;
|
||||
case 3:
|
||||
gu_out_bytes(out, buf, 3, err);
|
||||
break;
|
||||
case 4:
|
||||
gu_out_bytes(out, buf, 4, err);
|
||||
break;
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
}
|
||||
|
||||
extern inline void
|
||||
gu_out_utf8(GuUCS ucs, GuOut* out, GuExn* err);
|
||||
|
||||
static size_t
|
||||
gu_utf32_out_utf8_buffered_(const GuUCS* src, size_t len, GuOut* out,
|
||||
GuExn* err)
|
||||
{
|
||||
size_t src_i = 0;
|
||||
while (src_i < len) {
|
||||
size_t dst_sz;
|
||||
uint8_t* dst = gu_out_begin_span(out, len - src_i, &dst_sz, err);
|
||||
if (!gu_ok(err)) {
|
||||
return src_i;
|
||||
}
|
||||
if (!dst) {
|
||||
gu_out_utf8(src[src_i], out, err);
|
||||
if (!gu_ok(err)) {
|
||||
return src_i;
|
||||
}
|
||||
src_i++;
|
||||
break;
|
||||
}
|
||||
size_t dst_i = 0;
|
||||
while (true) {
|
||||
size_t safe = (dst_sz - dst_i) / 4;
|
||||
size_t end = GU_MIN(len, src_i + safe);
|
||||
if (end == src_i) {
|
||||
break;
|
||||
}
|
||||
do {
|
||||
GuUCS ucs = src[src_i++];
|
||||
dst_i += gu_advance_utf8(ucs, &dst[dst_i]);
|
||||
} while (src_i < end);
|
||||
}
|
||||
gu_out_end_span(out, dst_i);
|
||||
}
|
||||
return src_i;
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_utf32_out_utf8(const GuUCS* src, size_t len, GuOut* out, GuExn* err)
|
||||
{
|
||||
if (gu_out_is_buffered(out)) {
|
||||
return gu_utf32_out_utf8_buffered_(src, len, out, err);
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
gu_out_utf8(src[i], out, err);
|
||||
if (!gu_ok(err)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
#ifndef GU_CHAR_ASCII
|
||||
|
||||
void gu_str_out_utf8_(const char* str, GuOut* out, GuExn* err)
|
||||
{
|
||||
size_t len = strlen(str);
|
||||
size_t sz = 0;
|
||||
uint8_t* buf = gu_out_begin_span(out, len, &sz, err);
|
||||
if (!gu_ok(err)) {
|
||||
return;
|
||||
}
|
||||
if (buf != NULL && sz < len) {
|
||||
gu_out_end_span(out, 0);
|
||||
buf = NULL;
|
||||
}
|
||||
GuPool* tmp_pool = buf ? NULL : gu_local_pool();
|
||||
buf = buf ? buf : gu_new_n(uint8_t, len, tmp_pool);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
GuUCS ucs = gu_char_ucs(str[i]);
|
||||
buf[i] = (uint8_t) ucs;
|
||||
}
|
||||
if (tmp_pool) {
|
||||
gu_out_bytes(out, buf, len, err);
|
||||
gu_pool_free(tmp_pool);
|
||||
} else {
|
||||
gu_out_end_span(out, len);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern inline void
|
||||
gu_str_out_utf8(const char* str, GuOut* out, GuExn* err);
|
||||
|
||||
extern inline GuUCS
|
||||
gu_in_utf8(GuIn* in, GuExn* err);
|
||||
|
||||
extern inline char
|
||||
gu_in_utf8_char(GuIn* in, GuExn* err);
|
||||
67
src/runtime/c/gu/utf8.h
Normal file
67
src/runtime/c/gu/utf8.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef GU_UTF8_H_
|
||||
#define GU_UTF8_H_
|
||||
|
||||
#include <gu/in.h>
|
||||
#include <gu/out.h>
|
||||
#include <gu/ucs.h>
|
||||
|
||||
inline GuUCS
|
||||
gu_in_utf8(GuIn* in, GuExn* err)
|
||||
{
|
||||
int i = gu_in_peek_u8(in);
|
||||
if (i >= 0 && i < 0x80) {
|
||||
gu_in_consume(in, 1);
|
||||
return (GuUCS) i;
|
||||
}
|
||||
extern GuUCS gu_in_utf8_(GuIn* in, GuExn* err);
|
||||
return gu_in_utf8_(in, err);
|
||||
}
|
||||
|
||||
|
||||
inline char
|
||||
gu_in_utf8_char(GuIn* in, GuExn* err)
|
||||
{
|
||||
#ifdef GU_CHAR_ASCII
|
||||
int i = gu_in_peek_u8(in);
|
||||
if (i >= 0 && i < 0x80) {
|
||||
gu_in_consume(in, 1);
|
||||
return (char) i;
|
||||
}
|
||||
#endif
|
||||
extern char gu_in_utf8_char_(GuIn* in, GuExn* err);
|
||||
return gu_in_utf8_char_(in, err);
|
||||
}
|
||||
|
||||
void
|
||||
gu_out_utf8_long_(GuUCS ucs, GuOut* out, GuExn* err);
|
||||
|
||||
inline void
|
||||
gu_out_utf8(GuUCS ucs, GuOut* out, GuExn* err)
|
||||
{
|
||||
gu_require(gu_ucs_valid(ucs));
|
||||
if (GU_LIKELY(ucs < 0x80)) {
|
||||
gu_out_u8(out, ucs, err);
|
||||
} else {
|
||||
gu_out_utf8_long_(ucs, out, err);
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
gu_utf32_out_utf8(const GuUCS* src, size_t len, GuOut* out, GuExn* err);
|
||||
|
||||
GuUCS
|
||||
gu_utf8_decode(const uint8_t** utf8);
|
||||
|
||||
inline void
|
||||
gu_str_out_utf8(const char* str, GuOut* out, GuExn* err)
|
||||
{
|
||||
#ifdef GU_CHAR_ASCII
|
||||
gu_out_bytes(out, (const uint8_t*) str, strlen(str), err);
|
||||
#else
|
||||
extern void
|
||||
gu_str_out_utf8_(const char* str, GuOut* out, GuExn* err);
|
||||
gu_str_out_utf8_(str, out, err);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // GU_UTF8_H_
|
||||
100
src/runtime/c/gu/variant.c
Normal file
100
src/runtime/c/gu/variant.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "variant.h"
|
||||
#include "bits.h"
|
||||
|
||||
enum {
|
||||
GU_VARIANT_ALIGNMENT = sizeof(uintptr_t)
|
||||
};
|
||||
|
||||
void*
|
||||
gu_alloc_variant(uint8_t tag, size_t size,
|
||||
size_t align, GuVariant* variant_out, GuPool* pool)
|
||||
{
|
||||
align = gu_max(align, GU_VARIANT_ALIGNMENT);
|
||||
if (((size_t)tag) > GU_VARIANT_ALIGNMENT - 2) {
|
||||
uint8_t* alloc = gu_malloc_aligned(pool, align + size, align);
|
||||
alloc[align - 1] = tag;
|
||||
void* p = &alloc[align];
|
||||
variant_out->p = (uintptr_t)p;
|
||||
return p;
|
||||
}
|
||||
void* p = gu_malloc_aligned(pool, size, align);
|
||||
variant_out->p = ((uintptr_t)p) | (tag + 1);
|
||||
return p;
|
||||
}
|
||||
|
||||
GuVariant
|
||||
gu_make_variant(uint8_t tag, size_t size, size_t align, const void* init,
|
||||
GuPool* pool)
|
||||
{
|
||||
GuVariant v;
|
||||
void* data = gu_alloc_variant(tag, size, align, &v, pool);
|
||||
memcpy(data, init, size);
|
||||
return v;
|
||||
}
|
||||
|
||||
int
|
||||
gu_variant_tag(GuVariant variant)
|
||||
{
|
||||
if (gu_variant_is_null(variant)) {
|
||||
return GU_VARIANT_NULL;
|
||||
}
|
||||
int u = variant.p % GU_VARIANT_ALIGNMENT;
|
||||
if (u == 0) {
|
||||
uint8_t* mem = (uint8_t*)variant.p;
|
||||
return mem[-1];
|
||||
}
|
||||
return u - 1;
|
||||
}
|
||||
|
||||
void*
|
||||
gu_variant_data(GuVariant variant)
|
||||
{
|
||||
if (gu_variant_is_null(variant)) {
|
||||
return NULL;
|
||||
}
|
||||
return (void*)gu_align_backward(variant.p, GU_VARIANT_ALIGNMENT);
|
||||
}
|
||||
|
||||
GuVariantInfo gu_variant_open(GuVariant variant)
|
||||
{
|
||||
GuVariantInfo info = {
|
||||
.tag = gu_variant_tag(variant),
|
||||
.data = gu_variant_data(variant)
|
||||
};
|
||||
return info;
|
||||
}
|
||||
|
||||
int
|
||||
gu_variant_intval(GuVariant variant)
|
||||
{
|
||||
int u = variant.p % GU_VARIANT_ALIGNMENT;
|
||||
if (u == 0) {
|
||||
int* mem = (int*)variant.p;
|
||||
return *mem;
|
||||
}
|
||||
return (variant.p / GU_VARIANT_ALIGNMENT);
|
||||
}
|
||||
|
||||
const GuVariant gu_null_variant = { (GuWord) NULL };
|
||||
|
||||
GU_DEFINE_KIND(GuVariant, repr);
|
||||
GU_DEFINE_KIND(GuVariantAsPtr, repr);
|
||||
167
src/runtime/c/gu/variant.h
Normal file
167
src/runtime/c/gu/variant.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libgu.
|
||||
*
|
||||
* Libgu is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libgu is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libgu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Lightweight tagged data.
|
||||
*/
|
||||
|
||||
#ifndef GU_VARIANT_H_
|
||||
#define GU_VARIANT_H_
|
||||
|
||||
#include <gu/defs.h>
|
||||
#include <gu/mem.h>
|
||||
#include <gu/type.h>
|
||||
|
||||
/** @name Variants
|
||||
* @{
|
||||
*/
|
||||
|
||||
typedef struct GuVariant GuVariant;
|
||||
|
||||
|
||||
void* gu_alloc_variant(uint8_t tag,
|
||||
size_t size, size_t align,
|
||||
GuVariant* variant_out, GuPool* pool);
|
||||
|
||||
GuVariant gu_make_variant(uint8_t tag,
|
||||
size_t size, size_t align,
|
||||
const void* init, GuPool* pool);
|
||||
|
||||
#define gu_new_variant(tag, type, variant_out, pool) \
|
||||
((type*)gu_alloc_variant(tag, sizeof(type), \
|
||||
gu_alignof(type), variant_out, pool))
|
||||
|
||||
/**<
|
||||
* @hideinitializer */
|
||||
|
||||
#define gu_new_variant_i(POOL, TAG, T, ...) \
|
||||
gu_make_variant(TAG, sizeof(T), gu_alignof(T), \
|
||||
&(T){ __VA_ARGS__ }, POOL)
|
||||
|
||||
|
||||
|
||||
#define gu_new_flex_variant(tag, type, flex_mem, n_elems, variant_out, pool) \
|
||||
((type*)gu_alloc_variant(tag, \
|
||||
GU_FLEX_SIZE(type, flex_mem, n_elems), \
|
||||
gu_flex_alignof(type), \
|
||||
variant_out, pool))
|
||||
/**<
|
||||
* @hideinitializer */
|
||||
|
||||
enum {
|
||||
GU_VARIANT_NULL = -1
|
||||
};
|
||||
|
||||
int gu_variant_tag(GuVariant variant);
|
||||
|
||||
void* gu_variant_data(GuVariant variant);
|
||||
|
||||
|
||||
typedef struct GuVariantInfo GuVariantInfo;
|
||||
|
||||
struct GuVariantInfo {
|
||||
int tag;
|
||||
void* data;
|
||||
};
|
||||
|
||||
GuVariantInfo gu_variant_open(GuVariant variant);
|
||||
|
||||
/** @privatesection */
|
||||
struct GuVariant {
|
||||
uintptr_t p;
|
||||
/**< @private */
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
static inline void*
|
||||
gu_variant_to_ptr(GuVariant variant)
|
||||
{
|
||||
return (void*)variant.p;
|
||||
}
|
||||
|
||||
static inline GuVariant
|
||||
gu_variant_from_ptr(const void* p)
|
||||
{
|
||||
GuVariant v = { (uintptr_t)p };
|
||||
return v;
|
||||
}
|
||||
|
||||
extern const GuVariant gu_null_variant;
|
||||
|
||||
static inline bool
|
||||
gu_variant_is_null(GuVariant v) {
|
||||
return ((void*)v.p == NULL);
|
||||
}
|
||||
|
||||
|
||||
// variant
|
||||
|
||||
typedef const struct GuConstructor GuConstructor;
|
||||
|
||||
struct GuConstructor {
|
||||
int c_tag;
|
||||
const char* c_name;
|
||||
const GuType* type;
|
||||
};
|
||||
|
||||
#define GU_CONSTRUCTOR_V(ctag, c_type) { \
|
||||
.c_tag = ctag, \
|
||||
.c_name = #ctag, \
|
||||
.type = c_type \
|
||||
}
|
||||
|
||||
#define GU_CONSTRUCTOR(ctag, t_) \
|
||||
GU_CONSTRUCTOR_V(ctag, gu_type(t_))
|
||||
|
||||
#define GU_CONSTRUCTOR_P(ctag, t_) \
|
||||
GU_CONSTRUCTOR_V(ctag, gu_ptr_type(t_))
|
||||
|
||||
#define GU_CONSTRUCTOR_S(ctag, t_, ...) \
|
||||
GU_CONSTRUCTOR_V(ctag, GU_TYPE_LIT(struct, t_, __VA_ARGS__))
|
||||
|
||||
#define GU_CONSTRUCTOR_S1(ctag, t_, mem1_, type1_) \
|
||||
GU_CONSTRUCTOR_S(ctag, t_, \
|
||||
GU_MEMBER(t_, mem1_, type1_))
|
||||
|
||||
#define GU_CONSTRUCTOR_S2(ctag, t_, mem1_, type1_, mem2_, type2_) \
|
||||
GU_CONSTRUCTOR_S(ctag, t_, \
|
||||
GU_MEMBER(t_, mem1_, type1_), \
|
||||
GU_MEMBER(t_, mem2_, type2_))
|
||||
|
||||
|
||||
|
||||
typedef GuSList(GuConstructor) GuConstructors;
|
||||
|
||||
typedef const struct GuVariantType GuVariantType, GuType_GuVariant;
|
||||
|
||||
struct GuVariantType {
|
||||
GuType_repr repr_base;
|
||||
GuConstructors ctors;
|
||||
};
|
||||
|
||||
#define GU_TYPE_INIT_GuVariant(k_, t_, ...) { \
|
||||
.repr_base = GU_TYPE_INIT_repr(k_, GuVariant, _), \
|
||||
.ctors = GU_SLIST(GuConstructor, __VA_ARGS__) \
|
||||
}
|
||||
|
||||
extern GU_DECLARE_KIND(GuVariant);
|
||||
|
||||
#endif // GU_VARIANT_H_
|
||||
174
src/runtime/c/gu/write.c
Normal file
174
src/runtime/c/gu/write.c
Normal file
@@ -0,0 +1,174 @@
|
||||
#include <gu/write.h>
|
||||
|
||||
|
||||
size_t
|
||||
gu_utf32_write(const GuUCS* src, size_t len, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
return gu_utf32_out_utf8(src, len, &wtr->out_, err);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_vprintf(const char* fmt, va_list args, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
char* str = gu_vasprintf(fmt, args, tmp_pool);
|
||||
gu_puts(str, wtr, err);
|
||||
gu_pool_free(tmp_pool);
|
||||
}
|
||||
|
||||
void
|
||||
gu_printf(GuWriter* wtr, GuExn* err, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
gu_vprintf(fmt, args, wtr, err);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
GuWriter*
|
||||
gu_new_utf8_writer(GuOut* utf8_out, GuPool* pool)
|
||||
{
|
||||
GuOutStream* stream = gu_out_proxy_stream(utf8_out, pool);
|
||||
GuWriter* wtr = gu_new(GuWriter, pool);
|
||||
wtr->out_ = gu_init_out(stream);
|
||||
return wtr;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
#ifdef GU_UCS_WCHAR
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
static const mbstate_t gu_init_mbstate; // implicitly initialized to zero
|
||||
#endif
|
||||
|
||||
typedef struct GuLocaleWriter GuLocaleWriter;
|
||||
|
||||
struct GuLocaleWriter {
|
||||
GuOutWriter owtr;
|
||||
#ifdef GU_UCS_WCHAR
|
||||
mbstate_t ps;
|
||||
size_t mb_cur_max;
|
||||
#endif
|
||||
};
|
||||
|
||||
size_t
|
||||
gu_locale_writer_write(GuWriter* wtr, const uint8_t* utf8_src, size_t sz,
|
||||
GuExn* err)
|
||||
{
|
||||
GuLocaleWriter* lwtr = (GuLocaleWriter*) wtr;
|
||||
size_t done = 0;
|
||||
static const size_t bufsize = 256;
|
||||
#ifdef GU_UCS_WCHAR
|
||||
size_t margin = lwtr->mb_cur_max;
|
||||
#else
|
||||
size_t margin = 1;
|
||||
#endif
|
||||
GuOut* out = lwtr->owtr.out;
|
||||
if (gu_out_is_buffered(out)) {
|
||||
while (done < sz) {
|
||||
size_t dst_sz;
|
||||
uint8_t* dst = gu_out_begin_span(out, &dst_sz);
|
||||
if (!dst) {
|
||||
break;
|
||||
}
|
||||
if (dst_sz <= margin) {
|
||||
gu_out_end_span(out, 0);
|
||||
break;
|
||||
}
|
||||
size_t end = dst_sz - margin;
|
||||
const uint8_t*
|
||||
size_t n = done;
|
||||
while (n < sz && dst_i <= end) {
|
||||
#ifdef GU_UCS_WCHAR
|
||||
GuUCS ucs = gu_
|
||||
wchar_t wc = src[n];
|
||||
size_t nb = wcrtomb((char*) p, wc, &lwtr->ps);
|
||||
#else
|
||||
*p = (uint8_t) gu_ucs_char(buf[n], err);
|
||||
size_t nb = 1;
|
||||
if (!gu_ok(err)) {
|
||||
gu_exn_clear(err);
|
||||
nb = (size_t) -1;
|
||||
}
|
||||
#endif
|
||||
if (nb == (size_t) -1) {
|
||||
*p++ = (uint8_t) '?';
|
||||
} else {
|
||||
p += nb;
|
||||
}
|
||||
|
||||
}
|
||||
for (
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
uint8_t cbuf[256];
|
||||
while (done < size && gu_ok(err)) {
|
||||
uint8_t* p = cbuf;
|
||||
uint8_t* edge = &cbuf[bufsize - margin];
|
||||
size_t n;
|
||||
for (n = done; p <= edge && n < size; n++) {
|
||||
#ifdef GU_UCS_WCHAR
|
||||
wchar_t wc = buf[n];
|
||||
size_t nb = wcrtomb((char*) p, wc, &lwtr->ps);
|
||||
#else
|
||||
*p = (uint8_t) gu_ucs_char(buf[n], err);
|
||||
size_t nb = 1;
|
||||
if (!gu_ok(err)) {
|
||||
gu_exn_clear(err);
|
||||
nb = (size_t) -1;
|
||||
}
|
||||
#endif
|
||||
if (nb == (size_t) -1) {
|
||||
*p++ = (uint8_t) '?';
|
||||
} else {
|
||||
p += nb;
|
||||
}
|
||||
}
|
||||
gu_out_bytes(lwtr->owtr.out, cbuf, p - cbuf, err);
|
||||
if (gu_ok(err)) {
|
||||
done = n;
|
||||
}
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
GuWriter*
|
||||
gu_locale_writer(GuOut* out, GuPool* pool)
|
||||
{
|
||||
GuLocaleWriter* lwtr = gu_new_s(
|
||||
pool, GuLocaleWriter,
|
||||
.wtr.out.output = gu_locale_writer_output,
|
||||
.wtr.out.flush = gu_locale_writer_flush,
|
||||
.out = out);
|
||||
#ifdef GU_UCS_WCHAR
|
||||
lwtr->ps = gu_init_mbstate;
|
||||
lwtr->mb_cur_max = MB_CUR_MAX;
|
||||
#endif
|
||||
return (GuWriter*) lwtr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern inline void
|
||||
gu_ucs_write(GuUCS ucs, GuWriter* wtr, GuExn* err);
|
||||
|
||||
extern inline void
|
||||
gu_writer_flush(GuWriter* wtr, GuExn* err);
|
||||
|
||||
extern inline void
|
||||
gu_putc(char c, GuWriter* wtr, GuExn* err);
|
||||
|
||||
extern inline void
|
||||
gu_puts(const char* str, GuWriter* wtr, GuExn* err);
|
||||
|
||||
extern inline size_t
|
||||
gu_utf8_write(const uint8_t* src, size_t sz, GuWriter* wtr, GuExn* err);
|
||||
|
||||
64
src/runtime/c/gu/write.h
Normal file
64
src/runtime/c/gu/write.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef GU_WRITE_H_
|
||||
#define GU_WRITE_H_
|
||||
|
||||
#include <gu/exn.h>
|
||||
#include <gu/ucs.h>
|
||||
#include <gu/out.h>
|
||||
#include <gu/utf8.h>
|
||||
|
||||
typedef struct GuWriter GuWriter;
|
||||
|
||||
struct GuWriter {
|
||||
GuOut out_;
|
||||
};
|
||||
|
||||
size_t
|
||||
gu_utf32_write(const GuUCS* buf, size_t size, GuWriter* wtr, GuExn* err);
|
||||
|
||||
inline void
|
||||
gu_writer_flush(GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
gu_out_flush(&wtr->out_, err);
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_ucs_write(GuUCS ucs, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
gu_out_utf8(ucs, &wtr->out_, err);
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_putc(char c, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
GuUCS ucs = gu_char_ucs(c);
|
||||
gu_out_u8(&wtr->out_, (uint8_t) ucs, err);
|
||||
}
|
||||
|
||||
inline void
|
||||
gu_puts(const char* str, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
gu_str_out_utf8(str, &wtr->out_, err);
|
||||
}
|
||||
|
||||
inline size_t
|
||||
gu_utf8_write(const uint8_t* src, size_t sz, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
return gu_out_bytes(&wtr->out_, src, sz, err);
|
||||
}
|
||||
|
||||
void
|
||||
gu_vprintf(const char* fmt, va_list args, GuWriter* wtr, GuExn* err);
|
||||
|
||||
void
|
||||
gu_printf(GuWriter* wtr, GuExn* err, const char* fmt, ...);
|
||||
|
||||
//GuWriter
|
||||
//gu_init_utf8_writer(GuOut* utf8_out);
|
||||
|
||||
GuWriter*
|
||||
gu_new_utf8_writer(GuOut* utf8_out, GuPool* pool);
|
||||
|
||||
GuWriter*
|
||||
gu_make_locale_writer(GuOut* locale_out, GuPool* pool);
|
||||
|
||||
#endif // GU_WRITE_H_
|
||||
339
src/runtime/c/gu/yaml.c
Normal file
339
src/runtime/c/gu/yaml.c
Normal file
@@ -0,0 +1,339 @@
|
||||
#include <gu/yaml.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/read.h>
|
||||
#include <gu/ucs.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
const GuYamlAnchor gu_yaml_null_anchor = 0;
|
||||
|
||||
typedef const struct GuYamlState GuYamlState;
|
||||
|
||||
struct GuYamlState {
|
||||
const char* prefix;
|
||||
const char* suffix;
|
||||
GuYamlState* next;
|
||||
};
|
||||
|
||||
static const struct {
|
||||
GuYamlState document, first_key, key, value, first_elem, elem;
|
||||
} gu_yaml_states = {
|
||||
.document = {
|
||||
.prefix = "---\n",
|
||||
.suffix = "\n...\n",
|
||||
.next = &gu_yaml_states.document,
|
||||
},
|
||||
.key = {
|
||||
.prefix = "? ",
|
||||
.next = &gu_yaml_states.value,
|
||||
},
|
||||
.value = {
|
||||
.prefix = ": ",
|
||||
.suffix = ",",
|
||||
.next = &gu_yaml_states.key,
|
||||
},
|
||||
.elem = {
|
||||
.suffix = ",",
|
||||
.next = &gu_yaml_states.elem,
|
||||
},
|
||||
};
|
||||
|
||||
typedef const struct GuYamlFrameClass GuYamlFrameClass;
|
||||
|
||||
struct GuYamlFrameClass {
|
||||
const char* open;
|
||||
GuYamlState* first;
|
||||
const char* close;
|
||||
};
|
||||
|
||||
static const struct {
|
||||
GuYamlFrameClass document, mapping, sequence;
|
||||
} gu_yaml_frame_classes = {
|
||||
.mapping = {
|
||||
.open = "{",
|
||||
.first = &gu_yaml_states.key,
|
||||
.close = "}",
|
||||
},
|
||||
.sequence = {
|
||||
.open = "[",
|
||||
.first = &gu_yaml_states.elem,
|
||||
.close = "]",
|
||||
},
|
||||
};
|
||||
|
||||
typedef struct GuYamlFrame GuYamlFrame;
|
||||
|
||||
struct GuYamlFrame {
|
||||
GuYamlFrameClass* klass;
|
||||
GuYamlState* next;
|
||||
};
|
||||
|
||||
typedef GuBuf GuYamlStack;
|
||||
|
||||
struct GuYaml {
|
||||
GuWriter* wtr;
|
||||
GuExn* err;
|
||||
GuPool* pool;
|
||||
GuYamlState* state;
|
||||
bool in_node;
|
||||
bool have_anchor;
|
||||
bool have_tag;
|
||||
int next_anchor;
|
||||
bool indent;
|
||||
int indent_level;
|
||||
bool indented;
|
||||
GuYamlStack* stack;
|
||||
};
|
||||
|
||||
|
||||
GuYaml*
|
||||
gu_new_yaml(GuWriter* wtr, GuExn* err, GuPool* pool)
|
||||
{
|
||||
GuYaml* yaml = gu_new(GuYaml, pool);
|
||||
yaml->wtr = wtr;
|
||||
yaml->pool = pool;
|
||||
yaml->err = err;
|
||||
yaml->state = &gu_yaml_states.document;
|
||||
yaml->in_node = false;
|
||||
yaml->have_anchor = false;
|
||||
yaml->have_tag = false;
|
||||
yaml->next_anchor = 1;
|
||||
yaml->stack = gu_new_buf(GuYamlFrame, pool);
|
||||
yaml->indent = true;
|
||||
yaml->indent_level = 0;
|
||||
yaml->indented = false;
|
||||
return yaml;
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_printf(GuYaml* yaml, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
gu_vprintf(fmt, args, yaml->wtr, yaml->err);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_putc(GuYaml* yaml, char c)
|
||||
{
|
||||
gu_putc(c, yaml->wtr, yaml->err);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_puts(GuYaml* yaml, const char* str)
|
||||
{
|
||||
gu_puts(str, yaml->wtr, yaml->err);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_begin_line(GuYaml* yaml)
|
||||
{
|
||||
if (yaml->indent && !yaml->indented) {
|
||||
for (int i = 0; i < yaml->indent_level; i++) {
|
||||
gu_yaml_putc(yaml, ' ');
|
||||
}
|
||||
yaml->indented = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_end_line(GuYaml* yaml)
|
||||
{
|
||||
if (yaml->indent) {
|
||||
gu_yaml_putc(yaml, '\n');
|
||||
}
|
||||
yaml->indented = false;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gu_yaml_begin_node(GuYaml* yaml)
|
||||
{
|
||||
gu_yaml_begin_line(yaml);
|
||||
if (!yaml->in_node) {
|
||||
if (yaml->state->prefix != NULL) {
|
||||
gu_yaml_puts(yaml, yaml->state->prefix);
|
||||
}
|
||||
yaml->in_node = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_end_node(GuYaml* yaml)
|
||||
{
|
||||
gu_assert(yaml->in_node);
|
||||
if (yaml->state->suffix != NULL) {
|
||||
gu_yaml_puts(yaml, yaml->state->suffix);
|
||||
}
|
||||
gu_yaml_end_line(yaml);
|
||||
yaml->in_node = false;
|
||||
yaml->have_anchor = false;
|
||||
yaml->have_tag = false;
|
||||
if (yaml->state != NULL) {
|
||||
yaml->state = yaml->state->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_begin(GuYaml* yaml, GuYamlFrameClass* klass)
|
||||
{
|
||||
gu_yaml_begin_node(yaml);
|
||||
gu_yaml_puts(yaml, klass->open);
|
||||
gu_buf_push(yaml->stack, GuYamlFrame,
|
||||
((GuYamlFrame) { .klass = klass, .next = yaml->state}));
|
||||
yaml->state = klass->first;
|
||||
yaml->in_node = yaml->have_anchor = yaml->have_tag = false;
|
||||
gu_yaml_end_line(yaml);
|
||||
yaml->indent_level++;
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_begin_mapping(GuYaml* yaml)
|
||||
{
|
||||
gu_yaml_begin(yaml, &gu_yaml_frame_classes.mapping);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_begin_sequence(GuYaml* yaml)
|
||||
{
|
||||
gu_yaml_begin(yaml, &gu_yaml_frame_classes.sequence);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_end(GuYaml* yaml)
|
||||
{
|
||||
gu_assert(!yaml->in_node);
|
||||
yaml->indent_level--;
|
||||
gu_yaml_begin_line(yaml);
|
||||
GuYamlFrame f = gu_buf_pop(yaml->stack, GuYamlFrame);
|
||||
gu_yaml_puts(yaml, f.klass->close);
|
||||
yaml->state = f.next;
|
||||
yaml->in_node = true;
|
||||
gu_yaml_end_node(yaml);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gu_yaml_scalar(GuYaml* yaml, GuString s)
|
||||
{
|
||||
gu_yaml_begin_node(yaml);
|
||||
gu_yaml_putc(yaml, '"');
|
||||
GuPool* tmp_pool = gu_local_pool();
|
||||
GuReader* rdr = gu_string_reader(s, tmp_pool);
|
||||
GuExn* err = gu_exn(yaml->err, GuEOF, NULL);
|
||||
|
||||
static const char esc[0x20] = {
|
||||
[0x00] = '0',
|
||||
[0x07] = 'a', 'b', 't', 'n', 'v', 'f', 'r',
|
||||
[0x1b] = 'e'
|
||||
};
|
||||
|
||||
while (true) {
|
||||
GuUCS u = gu_read_ucs(rdr, err);
|
||||
if (!gu_ok(err)) {
|
||||
break;
|
||||
}
|
||||
if (GU_LIKELY(u >= 0x20 && u < 0x7f)) {
|
||||
if (GU_UNLIKELY(u == 0x22 || u == 0x5c)) {
|
||||
gu_yaml_putc(yaml, '\\');
|
||||
}
|
||||
gu_ucs_write(u, yaml->wtr, yaml->err);
|
||||
} else if (GU_UNLIKELY(u < 0x20 && esc[u])) {
|
||||
gu_yaml_printf(yaml, "\\%c", esc[u]);
|
||||
} else if (GU_UNLIKELY(u <= 0x9f)) {
|
||||
gu_yaml_printf(yaml, "\\x%02x", (unsigned) u);
|
||||
} else if (GU_UNLIKELY((u >= 0xd800 && u <= 0xdfff) ||
|
||||
(u >= 0xfffe && u <= 0xffff))) {
|
||||
gu_yaml_printf(yaml, "\\u%04x", (unsigned) u);
|
||||
} else {
|
||||
gu_ucs_write(u, yaml->wtr, yaml->err);
|
||||
}
|
||||
}
|
||||
gu_pool_free(tmp_pool);
|
||||
gu_yaml_putc(yaml, '"');
|
||||
gu_yaml_end_node(yaml);
|
||||
}
|
||||
|
||||
static void
|
||||
gu_yaml_tag(GuYaml* yaml, const char* format, ...)
|
||||
{
|
||||
gu_yaml_begin_node(yaml);
|
||||
gu_assert(!yaml->have_tag);
|
||||
gu_yaml_putc(yaml, '!');
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
gu_vprintf(format, args, yaml->wtr, yaml->err);
|
||||
va_end(args);
|
||||
gu_yaml_putc(yaml, ' ');
|
||||
yaml->have_tag = true;
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_tag_primary(GuYaml* yaml, const char* tag)
|
||||
{
|
||||
// TODO: check tag validity
|
||||
gu_yaml_tag(yaml, "%s", tag);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_tag_secondary(GuYaml* yaml, const char* tag)
|
||||
{
|
||||
// TODO: check tag validity
|
||||
gu_yaml_tag(yaml, "!%s", tag);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_tag_named(GuYaml* yaml, const char* handle, const char* tag)
|
||||
{
|
||||
// TODO: check tag validity
|
||||
gu_yaml_tag(yaml, "%s!%s", handle, tag);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_tag_verbatim(GuYaml* yaml, const char* uri)
|
||||
{
|
||||
// XXX: uri escaping?
|
||||
gu_yaml_tag(yaml, "<%s>", uri);
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_tag_non_specific(GuYaml* yaml)
|
||||
{
|
||||
gu_yaml_tag(yaml, "");
|
||||
}
|
||||
|
||||
GuYamlAnchor
|
||||
gu_yaml_anchor(GuYaml* yaml)
|
||||
{
|
||||
gu_yaml_begin_node(yaml);
|
||||
gu_assert(!yaml->have_anchor);
|
||||
yaml->have_anchor = true;
|
||||
int anchor = yaml->next_anchor++;
|
||||
gu_yaml_printf(yaml, "&%d ", anchor);
|
||||
return anchor;
|
||||
}
|
||||
|
||||
void
|
||||
gu_yaml_alias(GuYaml* yaml, GuYamlAnchor anchor)
|
||||
{
|
||||
gu_yaml_begin_node(yaml);
|
||||
gu_assert(!yaml->have_anchor && !yaml->have_tag);
|
||||
gu_yaml_printf(yaml, "*%d ", anchor);
|
||||
gu_yaml_end_node(yaml);
|
||||
return;
|
||||
}
|
||||
|
||||
void gu_yaml_comment(GuYaml* yaml, GuString s)
|
||||
{
|
||||
gu_yaml_begin_line(yaml);
|
||||
gu_yaml_puts(yaml, "# ");
|
||||
// TODO: verify no newlines in comment
|
||||
gu_string_write(s, yaml->wtr, yaml->err);
|
||||
gu_yaml_puts(yaml, "\n");
|
||||
yaml->indented = false;
|
||||
}
|
||||
|
||||
38
src/runtime/c/gu/yaml.h
Normal file
38
src/runtime/c/gu/yaml.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef GU_YAML_H_
|
||||
#define GU_YAML_H_
|
||||
|
||||
#include <gu/mem.h>
|
||||
#include <gu/write.h>
|
||||
#include <gu/string.h>
|
||||
|
||||
typedef struct GuYaml GuYaml;
|
||||
|
||||
typedef int GuYamlAnchor;
|
||||
|
||||
extern const GuYamlAnchor gu_yaml_null_anchor;
|
||||
|
||||
GuYaml* gu_new_yaml(GuWriter* wtr, GuExn* err, GuPool* pool);
|
||||
|
||||
GuYamlAnchor gu_yaml_anchor(GuYaml* yaml);
|
||||
|
||||
void gu_yaml_tag_primary(GuYaml* yaml, const char* tag);
|
||||
void gu_yaml_tag_secondary(GuYaml* yaml, const char* tag);
|
||||
void gu_yaml_tag_named(GuYaml* yaml, const char* handle, const char* tag);
|
||||
void gu_yaml_tag_verbatim(GuYaml* yaml, const char* uri);
|
||||
void gu_yaml_tag_non_specific(GuYaml* yaml);
|
||||
void gu_yaml_comment(GuYaml* yaml, GuString comment);
|
||||
|
||||
|
||||
void gu_yaml_scalar(GuYaml* yaml, GuString scalar);
|
||||
|
||||
void gu_yaml_alias(GuYaml* yaml, GuYamlAnchor anchor);
|
||||
|
||||
void gu_yaml_begin_document(GuYaml* yaml);
|
||||
|
||||
void gu_yaml_begin_sequence(GuYaml* yaml);
|
||||
|
||||
void gu_yaml_begin_mapping(GuYaml* yaml);
|
||||
|
||||
void gu_yaml_end(GuYaml* yaml);
|
||||
|
||||
#endif // GU_YAML_H_
|
||||
2
src/runtime/c/guconfig.h.in
Normal file
2
src/runtime/c/guconfig.h.in
Normal file
@@ -0,0 +1,2 @@
|
||||
/* Define to 1 if character literals use ASCII encoding */
|
||||
#undef GU_CHAR_ASCII
|
||||
10
src/runtime/c/libgu.pc.in
Normal file
10
src/runtime/c/libgu.pc.in
Normal file
@@ -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
|
||||
11
src/runtime/c/libpgf.pc.in
Normal file
11
src/runtime/c/libpgf.pc.in
Normal file
@@ -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}
|
||||
533
src/runtime/c/m4/ax_prog_doxygen.m4
Normal file
533
src/runtime/c/m4/ax_prog_doxygen.m4
Normal file
@@ -0,0 +1,533 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# DX_INIT_DOXYGEN(PROJECT-NAME, DOXYFILE-PATH, [OUTPUT-DIR])
|
||||
# DX_DOXYGEN_FEATURE(ON|OFF)
|
||||
# DX_DOT_FEATURE(ON|OFF)
|
||||
# DX_HTML_FEATURE(ON|OFF)
|
||||
# DX_CHM_FEATURE(ON|OFF)
|
||||
# DX_CHI_FEATURE(ON|OFF)
|
||||
# DX_MAN_FEATURE(ON|OFF)
|
||||
# DX_RTF_FEATURE(ON|OFF)
|
||||
# DX_XML_FEATURE(ON|OFF)
|
||||
# DX_PDF_FEATURE(ON|OFF)
|
||||
# DX_PS_FEATURE(ON|OFF)
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# The DX_*_FEATURE macros control the default setting for the given
|
||||
# Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for
|
||||
# generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML
|
||||
# help (for MS users), 'CHI' for generating a seperate .chi file by the
|
||||
# .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate
|
||||
# output formats. The environment variable DOXYGEN_PAPER_SIZE may be
|
||||
# specified to override the default 'a4wide' paper size.
|
||||
#
|
||||
# By default, HTML, PDF and PS documentation is generated as this seems to
|
||||
# be the most popular and portable combination. MAN pages created by
|
||||
# Doxygen are usually problematic, though by picking an appropriate subset
|
||||
# and doing some massaging they might be better than nothing. CHM and RTF
|
||||
# are specific for MS (note that you can't generate both HTML and CHM at
|
||||
# the same time). The XML is rather useless unless you apply specialized
|
||||
# post-processing to it.
|
||||
#
|
||||
# The macros mainly control the default state of the feature. The use can
|
||||
# override the default by specifying --enable or --disable. The macros
|
||||
# ensure that contradictory flags are not given (e.g.,
|
||||
# --enable-doxygen-html and --enable-doxygen-chm,
|
||||
# --enable-doxygen-anything with --disable-doxygen, etc.) Finally, each
|
||||
# feature will be automatically disabled (with a warning) if the required
|
||||
# programs are missing.
|
||||
#
|
||||
# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN
|
||||
# with the following parameters: a one-word name for the project for use
|
||||
# as a filename base etc., an optional configuration file name (the
|
||||
# default is 'Doxyfile', the same as Doxygen's default), and an optional
|
||||
# output directory name (the default is 'doxygen-doc').
|
||||
#
|
||||
# Automake Support
|
||||
#
|
||||
# The following is a template aminclude.am file for use with Automake.
|
||||
# Make targets and variables values are controlled by the various
|
||||
# DX_COND_* conditionals set by autoconf.
|
||||
#
|
||||
# The provided targets are:
|
||||
#
|
||||
# doxygen-doc: Generate all doxygen documentation.
|
||||
#
|
||||
# doxygen-run: Run doxygen, which will generate some of the
|
||||
# documentation (HTML, CHM, CHI, MAN, RTF, XML)
|
||||
# but will not do the post processing required
|
||||
# for the rest of it (PS, PDF, and some MAN).
|
||||
#
|
||||
# doxygen-man: Rename some doxygen generated man pages.
|
||||
#
|
||||
# doxygen-ps: Generate doxygen PostScript documentation.
|
||||
#
|
||||
# doxygen-pdf: Generate doxygen PDF documentation.
|
||||
#
|
||||
# Note that by default these are not integrated into the automake targets.
|
||||
# If doxygen is used to generate man pages, you can achieve this
|
||||
# integration by setting man3_MANS to the list of man pages generated and
|
||||
# then adding the dependency:
|
||||
#
|
||||
# $(man3_MANS): doxygen-doc
|
||||
#
|
||||
# This will cause make to run doxygen and generate all the documentation.
|
||||
#
|
||||
# The following variable is intended for use in Makefile.am:
|
||||
#
|
||||
# DX_CLEANFILES = everything to clean.
|
||||
#
|
||||
# Then add this variable to MOSTLYCLEANFILES.
|
||||
#
|
||||
# ----- begin aminclude.am -------------------------------------
|
||||
#
|
||||
# ## --------------------------------- ##
|
||||
# ## Format-independent Doxygen rules. ##
|
||||
# ## --------------------------------- ##
|
||||
#
|
||||
# if DX_COND_doc
|
||||
#
|
||||
# ## ------------------------------- ##
|
||||
# ## Rules specific for HTML output. ##
|
||||
# ## ------------------------------- ##
|
||||
#
|
||||
# if DX_COND_html
|
||||
#
|
||||
# DX_CLEAN_HTML = @DX_DOCDIR@/html
|
||||
#
|
||||
# endif DX_COND_html
|
||||
#
|
||||
# ## ------------------------------ ##
|
||||
# ## Rules specific for CHM output. ##
|
||||
# ## ------------------------------ ##
|
||||
#
|
||||
# if DX_COND_chm
|
||||
#
|
||||
# DX_CLEAN_CHM = @DX_DOCDIR@/chm
|
||||
#
|
||||
# if DX_COND_chi
|
||||
#
|
||||
# DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi
|
||||
#
|
||||
# endif DX_COND_chi
|
||||
#
|
||||
# endif DX_COND_chm
|
||||
#
|
||||
# ## ------------------------------ ##
|
||||
# ## Rules specific for MAN output. ##
|
||||
# ## ------------------------------ ##
|
||||
#
|
||||
# if DX_COND_man
|
||||
#
|
||||
# DX_CLEAN_MAN = @DX_DOCDIR@/man
|
||||
#
|
||||
# endif DX_COND_man
|
||||
#
|
||||
# ## ------------------------------ ##
|
||||
# ## Rules specific for RTF output. ##
|
||||
# ## ------------------------------ ##
|
||||
#
|
||||
# if DX_COND_rtf
|
||||
#
|
||||
# DX_CLEAN_RTF = @DX_DOCDIR@/rtf
|
||||
#
|
||||
# endif DX_COND_rtf
|
||||
#
|
||||
# ## ------------------------------ ##
|
||||
# ## Rules specific for XML output. ##
|
||||
# ## ------------------------------ ##
|
||||
#
|
||||
# if DX_COND_xml
|
||||
#
|
||||
# DX_CLEAN_XML = @DX_DOCDIR@/xml
|
||||
#
|
||||
# endif DX_COND_xml
|
||||
#
|
||||
# ## ----------------------------- ##
|
||||
# ## Rules specific for PS output. ##
|
||||
# ## ----------------------------- ##
|
||||
#
|
||||
# if DX_COND_ps
|
||||
#
|
||||
# DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps
|
||||
#
|
||||
# DX_PS_GOAL = doxygen-ps
|
||||
#
|
||||
# doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps
|
||||
#
|
||||
# @DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag
|
||||
# cd @DX_DOCDIR@/latex; \
|
||||
# rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
|
||||
# $(DX_LATEX) refman.tex; \
|
||||
# $(MAKEINDEX_PATH) refman.idx; \
|
||||
# $(DX_LATEX) refman.tex; \
|
||||
# countdown=5; \
|
||||
# while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
|
||||
# refman.log > /dev/null 2>&1 \
|
||||
# && test $$countdown -gt 0; do \
|
||||
# $(DX_LATEX) refman.tex; \
|
||||
# countdown=`expr $$countdown - 1`; \
|
||||
# done; \
|
||||
# $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi
|
||||
#
|
||||
# endif DX_COND_ps
|
||||
#
|
||||
# ## ------------------------------ ##
|
||||
# ## Rules specific for PDF output. ##
|
||||
# ## ------------------------------ ##
|
||||
#
|
||||
# if DX_COND_pdf
|
||||
#
|
||||
# DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf
|
||||
#
|
||||
# DX_PDF_GOAL = doxygen-pdf
|
||||
#
|
||||
# doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf
|
||||
#
|
||||
# @DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag
|
||||
# cd @DX_DOCDIR@/latex; \
|
||||
# rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
|
||||
# $(DX_PDFLATEX) refman.tex; \
|
||||
# $(DX_MAKEINDEX) refman.idx; \
|
||||
# $(DX_PDFLATEX) refman.tex; \
|
||||
# countdown=5; \
|
||||
# while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
|
||||
# refman.log > /dev/null 2>&1 \
|
||||
# && test $$countdown -gt 0; do \
|
||||
# $(DX_PDFLATEX) refman.tex; \
|
||||
# countdown=`expr $$countdown - 1`; \
|
||||
# done; \
|
||||
# mv refman.pdf ../@PACKAGE@.pdf
|
||||
#
|
||||
# endif DX_COND_pdf
|
||||
#
|
||||
# ## ------------------------------------------------- ##
|
||||
# ## Rules specific for LaTeX (shared for PS and PDF). ##
|
||||
# ## ------------------------------------------------- ##
|
||||
#
|
||||
# if DX_COND_latex
|
||||
#
|
||||
# DX_CLEAN_LATEX = @DX_DOCDIR@/latex
|
||||
#
|
||||
# endif DX_COND_latex
|
||||
#
|
||||
# .PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL)
|
||||
#
|
||||
# .INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
|
||||
#
|
||||
# doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag
|
||||
#
|
||||
# doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
|
||||
#
|
||||
# @DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS)
|
||||
# rm -rf @DX_DOCDIR@
|
||||
# $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG)
|
||||
#
|
||||
# DX_CLEANFILES = \
|
||||
# @DX_DOCDIR@/@PACKAGE@.tag \
|
||||
# -r \
|
||||
# $(DX_CLEAN_HTML) \
|
||||
# $(DX_CLEAN_CHM) \
|
||||
# $(DX_CLEAN_CHI) \
|
||||
# $(DX_CLEAN_MAN) \
|
||||
# $(DX_CLEAN_RTF) \
|
||||
# $(DX_CLEAN_XML) \
|
||||
# $(DX_CLEAN_PS) \
|
||||
# $(DX_CLEAN_PDF) \
|
||||
# $(DX_CLEAN_LATEX)
|
||||
#
|
||||
# endif DX_COND_doc
|
||||
#
|
||||
# ----- end aminclude.am ---------------------------------------
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2009 Oren Ben-Kiki <oren@ben-kiki.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 10
|
||||
|
||||
## ----------##
|
||||
## Defaults. ##
|
||||
## ----------##
|
||||
|
||||
DX_ENV=""
|
||||
AC_DEFUN([DX_FEATURE_doc], ON)
|
||||
AC_DEFUN([DX_FEATURE_dot], ON)
|
||||
AC_DEFUN([DX_FEATURE_man], OFF)
|
||||
AC_DEFUN([DX_FEATURE_html], ON)
|
||||
AC_DEFUN([DX_FEATURE_chm], OFF)
|
||||
AC_DEFUN([DX_FEATURE_chi], OFF)
|
||||
AC_DEFUN([DX_FEATURE_rtf], OFF)
|
||||
AC_DEFUN([DX_FEATURE_xml], OFF)
|
||||
AC_DEFUN([DX_FEATURE_pdf], ON)
|
||||
AC_DEFUN([DX_FEATURE_ps], ON)
|
||||
|
||||
## --------------- ##
|
||||
## Private macros. ##
|
||||
## --------------- ##
|
||||
|
||||
# DX_ENV_APPEND(VARIABLE, VALUE)
|
||||
# ------------------------------
|
||||
# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen.
|
||||
AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])])
|
||||
|
||||
# DX_DIRNAME_EXPR
|
||||
# ---------------
|
||||
# Expand into a shell expression prints the directory part of a path.
|
||||
AC_DEFUN([DX_DIRNAME_EXPR],
|
||||
[[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']])
|
||||
|
||||
# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF)
|
||||
# -------------------------------------
|
||||
# Expands according to the M4 (static) status of the feature.
|
||||
AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])])
|
||||
|
||||
# DX_REQUIRE_PROG(VARIABLE, PROGRAM)
|
||||
# ----------------------------------
|
||||
# Require the specified program to be found for the DX_CURRENT_FEATURE to work.
|
||||
AC_DEFUN([DX_REQUIRE_PROG], [
|
||||
AC_PATH_TOOL([$1], [$2])
|
||||
if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then
|
||||
AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION])
|
||||
AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0)
|
||||
fi
|
||||
])
|
||||
|
||||
# DX_TEST_FEATURE(FEATURE)
|
||||
# ------------------------
|
||||
# Expand to a shell expression testing whether the feature is active.
|
||||
AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1])
|
||||
|
||||
# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE)
|
||||
# -------------------------------------------------
|
||||
# Verify that a required features has the right state before trying to turn on
|
||||
# the DX_CURRENT_FEATURE.
|
||||
AC_DEFUN([DX_CHECK_DEPEND], [
|
||||
test "$DX_FLAG_$1" = "$2" \
|
||||
|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1,
|
||||
requires, contradicts) doxygen-DX_CURRENT_FEATURE])
|
||||
])
|
||||
|
||||
# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE)
|
||||
# ----------------------------------------------------------
|
||||
# Turn off the DX_CURRENT_FEATURE if the required feature is off.
|
||||
AC_DEFUN([DX_CLEAR_DEPEND], [
|
||||
test "$DX_FLAG_$1" = "$2" || AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0)
|
||||
])
|
||||
|
||||
# DX_FEATURE_ARG(FEATURE, DESCRIPTION,
|
||||
# CHECK_DEPEND, CLEAR_DEPEND,
|
||||
# REQUIRE, DO-IF-ON, DO-IF-OFF)
|
||||
# --------------------------------------------
|
||||
# Parse the command-line option controlling a feature. CHECK_DEPEND is called
|
||||
# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND),
|
||||
# otherwise CLEAR_DEPEND is called to turn off the default state if a required
|
||||
# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional
|
||||
# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and
|
||||
# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature.
|
||||
AC_DEFUN([DX_ARG_ABLE], [
|
||||
AC_DEFUN([DX_CURRENT_FEATURE], [$1])
|
||||
AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2])
|
||||
AC_ARG_ENABLE(doxygen-$1,
|
||||
[AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1],
|
||||
[--enable-doxygen-$1]),
|
||||
DX_IF_FEATURE([$1], [don't $2], [$2]))],
|
||||
[
|
||||
case "$enableval" in
|
||||
#(
|
||||
y|Y|yes|Yes|YES)
|
||||
AC_SUBST([DX_FLAG_$1], 1)
|
||||
$3
|
||||
;; #(
|
||||
n|N|no|No|NO)
|
||||
AC_SUBST([DX_FLAG_$1], 0)
|
||||
;; #(
|
||||
*)
|
||||
AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1])
|
||||
;;
|
||||
esac
|
||||
], [
|
||||
AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)])
|
||||
$4
|
||||
])
|
||||
if DX_TEST_FEATURE([$1]); then
|
||||
$5
|
||||
:
|
||||
fi
|
||||
if DX_TEST_FEATURE([$1]); then
|
||||
AM_CONDITIONAL(DX_COND_$1, :)
|
||||
$6
|
||||
:
|
||||
else
|
||||
AM_CONDITIONAL(DX_COND_$1, false)
|
||||
$7
|
||||
:
|
||||
fi
|
||||
])
|
||||
|
||||
## -------------- ##
|
||||
## Public macros. ##
|
||||
## -------------- ##
|
||||
|
||||
# DX_XXX_FEATURE(DEFAULT_STATE)
|
||||
# -----------------------------
|
||||
AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])])
|
||||
AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])])
|
||||
AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])])
|
||||
AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])])
|
||||
AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])])
|
||||
AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])])
|
||||
AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])])
|
||||
AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])])
|
||||
AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])])
|
||||
AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])])
|
||||
|
||||
# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR])
|
||||
# ---------------------------------------------------------
|
||||
# PROJECT also serves as the base name for the documentation files.
|
||||
# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc".
|
||||
AC_DEFUN([DX_INIT_DOXYGEN], [
|
||||
|
||||
# Files:
|
||||
AC_SUBST([DX_PROJECT], [$1])
|
||||
AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])])
|
||||
AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])])
|
||||
|
||||
# Environment variables used inside doxygen.cfg:
|
||||
DX_ENV_APPEND(SRCDIR, $srcdir)
|
||||
DX_ENV_APPEND(PROJECT, $DX_PROJECT)
|
||||
DX_ENV_APPEND(DOCDIR, $DX_DOCDIR)
|
||||
DX_ENV_APPEND(VERSION, $PACKAGE_VERSION)
|
||||
|
||||
# Doxygen itself:
|
||||
DX_ARG_ABLE(doc, [generate any doxygen documentation],
|
||||
[],
|
||||
[],
|
||||
[DX_REQUIRE_PROG([DX_DOXYGEN], doxygen)
|
||||
DX_REQUIRE_PROG([DX_PERL], perl)],
|
||||
[DX_ENV_APPEND(PERL_PATH, $DX_PERL)])
|
||||
|
||||
# Dot for graphics:
|
||||
DX_ARG_ABLE(dot, [generate graphics for doxygen documentation],
|
||||
[DX_CHECK_DEPEND(doc, 1)],
|
||||
[DX_CLEAR_DEPEND(doc, 1)],
|
||||
[DX_REQUIRE_PROG([DX_DOT], dot)],
|
||||
[DX_ENV_APPEND(HAVE_DOT, YES)
|
||||
DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])],
|
||||
[DX_ENV_APPEND(HAVE_DOT, NO)])
|
||||
|
||||
# Man pages generation:
|
||||
DX_ARG_ABLE(man, [generate doxygen manual pages],
|
||||
[DX_CHECK_DEPEND(doc, 1)],
|
||||
[DX_CLEAR_DEPEND(doc, 1)],
|
||||
[],
|
||||
[DX_ENV_APPEND(GENERATE_MAN, YES)],
|
||||
[DX_ENV_APPEND(GENERATE_MAN, NO)])
|
||||
|
||||
# RTF file generation:
|
||||
DX_ARG_ABLE(rtf, [generate doxygen RTF documentation],
|
||||
[DX_CHECK_DEPEND(doc, 1)],
|
||||
[DX_CLEAR_DEPEND(doc, 1)],
|
||||
[],
|
||||
[DX_ENV_APPEND(GENERATE_RTF, YES)],
|
||||
[DX_ENV_APPEND(GENERATE_RTF, NO)])
|
||||
|
||||
# XML file generation:
|
||||
DX_ARG_ABLE(xml, [generate doxygen XML documentation],
|
||||
[DX_CHECK_DEPEND(doc, 1)],
|
||||
[DX_CLEAR_DEPEND(doc, 1)],
|
||||
[],
|
||||
[DX_ENV_APPEND(GENERATE_XML, YES)],
|
||||
[DX_ENV_APPEND(GENERATE_XML, NO)])
|
||||
|
||||
# (Compressed) HTML help generation:
|
||||
DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation],
|
||||
[DX_CHECK_DEPEND(doc, 1)],
|
||||
[DX_CLEAR_DEPEND(doc, 1)],
|
||||
[DX_REQUIRE_PROG([DX_HHC], hhc)],
|
||||
[DX_ENV_APPEND(HHC_PATH, $DX_HHC)
|
||||
DX_ENV_APPEND(GENERATE_HTML, YES)
|
||||
DX_ENV_APPEND(GENERATE_HTMLHELP, YES)],
|
||||
[DX_ENV_APPEND(GENERATE_HTMLHELP, NO)])
|
||||
|
||||
# Seperate CHI file generation.
|
||||
DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file],
|
||||
[DX_CHECK_DEPEND(chm, 1)],
|
||||
[DX_CLEAR_DEPEND(chm, 1)],
|
||||
[],
|
||||
[DX_ENV_APPEND(GENERATE_CHI, YES)],
|
||||
[DX_ENV_APPEND(GENERATE_CHI, NO)])
|
||||
|
||||
# Plain HTML pages generation:
|
||||
DX_ARG_ABLE(html, [generate doxygen plain HTML documentation],
|
||||
[DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)],
|
||||
[DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)],
|
||||
[],
|
||||
[DX_ENV_APPEND(GENERATE_HTML, YES)],
|
||||
[DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)])
|
||||
|
||||
# PostScript file generation:
|
||||
DX_ARG_ABLE(ps, [generate doxygen PostScript documentation],
|
||||
[DX_CHECK_DEPEND(doc, 1)],
|
||||
[DX_CLEAR_DEPEND(doc, 1)],
|
||||
[DX_REQUIRE_PROG([DX_LATEX], latex)
|
||||
DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
|
||||
DX_REQUIRE_PROG([DX_DVIPS], dvips)
|
||||
DX_REQUIRE_PROG([DX_EGREP], egrep)])
|
||||
|
||||
# PDF file generation:
|
||||
DX_ARG_ABLE(pdf, [generate doxygen PDF documentation],
|
||||
[DX_CHECK_DEPEND(doc, 1)],
|
||||
[DX_CLEAR_DEPEND(doc, 1)],
|
||||
[DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex)
|
||||
DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
|
||||
DX_REQUIRE_PROG([DX_EGREP], egrep)])
|
||||
|
||||
# LaTeX generation for PS and/or PDF:
|
||||
if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then
|
||||
AM_CONDITIONAL(DX_COND_latex, :)
|
||||
DX_ENV_APPEND(GENERATE_LATEX, YES)
|
||||
else
|
||||
AM_CONDITIONAL(DX_COND_latex, false)
|
||||
DX_ENV_APPEND(GENERATE_LATEX, NO)
|
||||
fi
|
||||
|
||||
# Paper size for PS and/or PDF:
|
||||
AC_ARG_VAR(DOXYGEN_PAPER_SIZE,
|
||||
[a4wide (default), a4, letter, legal or executive])
|
||||
case "$DOXYGEN_PAPER_SIZE" in
|
||||
#(
|
||||
"")
|
||||
AC_SUBST(DOXYGEN_PAPER_SIZE, "")
|
||||
;; #(
|
||||
a4wide|a4|letter|legal|executive)
|
||||
DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE)
|
||||
;; #(
|
||||
*)
|
||||
AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE'])
|
||||
;;
|
||||
esac
|
||||
|
||||
#For debugging:
|
||||
#echo DX_FLAG_doc=$DX_FLAG_doc
|
||||
#echo DX_FLAG_dot=$DX_FLAG_dot
|
||||
#echo DX_FLAG_man=$DX_FLAG_man
|
||||
#echo DX_FLAG_html=$DX_FLAG_html
|
||||
#echo DX_FLAG_chm=$DX_FLAG_chm
|
||||
#echo DX_FLAG_chi=$DX_FLAG_chi
|
||||
#echo DX_FLAG_rtf=$DX_FLAG_rtf
|
||||
#echo DX_FLAG_xml=$DX_FLAG_xml
|
||||
#echo DX_FLAG_pdf=$DX_FLAG_pdf
|
||||
#echo DX_FLAG_ps=$DX_FLAG_ps
|
||||
#echo DX_ENV=$DX_ENV
|
||||
])
|
||||
89
src/runtime/c/m4/c_ext.m4
Normal file
89
src/runtime/c/m4/c_ext.m4
Normal file
@@ -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
|
||||
])
|
||||
@@ -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
|
||||
251
src/runtime/c/pgf/data.c
Normal file
251
src/runtime/c/pgf/data.c
Normal file
@@ -0,0 +1,251 @@
|
||||
#include "data.h"
|
||||
#include "expr.h"
|
||||
#include <gu/type.h>
|
||||
#include <gu/variant.h>
|
||||
#include <gu/assert.h>
|
||||
|
||||
|
||||
PgfCCat pgf_ccat_string = { NULL, GU_NULL_SEQ, -1 };
|
||||
PgfCCat pgf_ccat_int = { NULL, GU_NULL_SEQ, -2 };
|
||||
PgfCCat pgf_ccat_float = { NULL, GU_NULL_SEQ, -3 };
|
||||
PgfCCat pgf_ccat_var = { NULL, GU_NULL_SEQ, -4 };
|
||||
|
||||
PgfCCatId
|
||||
pgf_literal_cat(PgfLiteral lit)
|
||||
{
|
||||
switch (gu_variant_tag(lit)) {
|
||||
case PGF_LITERAL_STR:
|
||||
return &pgf_ccat_string;
|
||||
case PGF_LITERAL_INT:
|
||||
return &pgf_ccat_int;
|
||||
case PGF_LITERAL_FLT:
|
||||
return &pgf_ccat_float;
|
||||
default:
|
||||
gu_impossible();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
pgf_tokens_equal(PgfTokens t1, PgfTokens t2)
|
||||
{
|
||||
size_t len1 = gu_seq_length(t1);
|
||||
size_t len2 = gu_seq_length(t2);
|
||||
if (len1 != len2) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < len1; i++) {
|
||||
GuString s1 = gu_seq_get(t1, PgfToken, i);
|
||||
GuString s2 = gu_seq_get(t2, PgfToken, i);
|
||||
if (!gu_string_eq(s1, s2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GU_DEFINE_TYPE(PgfTokens, GuSeq, gu_type(GuString));
|
||||
|
||||
GU_DEFINE_TYPE(PgfCId, typedef, gu_type(GuString));
|
||||
|
||||
GU_DEFINE_TYPE(GuStringL, GuList, gu_type(GuString));
|
||||
|
||||
|
||||
#define gu_type__PgfCIdMap gu_type__GuStringMap
|
||||
typedef GuType_GuStringMap GuType_PgfCIdMap;
|
||||
#define GU_TYPE_INIT_PgfCIdMap GU_TYPE_INIT_GuStringMap
|
||||
|
||||
GU_DEFINE_TYPE(PgfCCat, struct,
|
||||
GU_MEMBER_S(PgfCCat, cnccat, PgfCncCat),
|
||||
GU_MEMBER(PgfCCat, prods, PgfProductionSeq));
|
||||
|
||||
GU_DEFINE_TYPE(PgfCCatId, shared, gu_type(PgfCCat));
|
||||
|
||||
GU_DEFINE_TYPE(PgfCCatIds, GuList, gu_type(PgfCCatId));
|
||||
|
||||
GU_DEFINE_TYPE(PgfCCatSeq, GuSeq, gu_type(PgfCCatId));
|
||||
|
||||
GU_DEFINE_TYPE(PgfAlternative, struct,
|
||||
GU_MEMBER(PgfAlternative, form, PgfTokens),
|
||||
GU_MEMBER_P(PgfAlternative, prefixes, GuStringL));
|
||||
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfSymbol, GuVariant,
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_SYMBOL_CAT, PgfSymbolCat,
|
||||
GU_MEMBER(PgfSymbolCat, d, int),
|
||||
GU_MEMBER(PgfSymbolCat, r, int)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_SYMBOL_LIT, PgfSymbolLit,
|
||||
GU_MEMBER(PgfSymbolLit, d, int),
|
||||
GU_MEMBER(PgfSymbolLit, r, int)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_SYMBOL_VAR, PgfSymbolVar,
|
||||
GU_MEMBER(PgfSymbolVar, d, int),
|
||||
GU_MEMBER(PgfSymbolVar, r, int)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_SYMBOL_KS, PgfSymbolKS,
|
||||
GU_MEMBER(PgfSymbolKS, tokens, PgfTokens)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_SYMBOL_KP, PgfSymbolKP,
|
||||
GU_MEMBER(PgfSymbolKP, default_form, PgfTokens),
|
||||
GU_MEMBER(PgfSymbolKP, n_forms, GuLength),
|
||||
GU_FLEX_MEMBER(PgfSymbolKP, forms, PgfAlternative)));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfCncCat, struct,
|
||||
GU_MEMBER(PgfCncCat, cid, PgfCId),
|
||||
GU_MEMBER_P(PgfCncCat, cats, PgfCCatIds),
|
||||
GU_MEMBER(PgfCncCat, n_lins, size_t),
|
||||
GU_MEMBER_P(PgfCncCat, lindefs, PgfFunIds),
|
||||
GU_MEMBER_P(PgfCncCat, labels, GuStringL));
|
||||
|
||||
// GU_DEFINE_TYPE(PgfSequence, GuList, gu_ptr_type(PgfSymbol));
|
||||
// GU_DEFINE_TYPE(PgfSequence, GuList, gu_type(PgfSymbol));
|
||||
GU_DEFINE_TYPE(PgfSequence, GuSeq, gu_type(PgfSymbol));
|
||||
|
||||
GU_DEFINE_TYPE(PgfFlags, GuStringMap, gu_type(PgfLiteral), &gu_null_variant);
|
||||
|
||||
typedef PgfFlags* PgfFlagsP;
|
||||
|
||||
GU_DEFINE_TYPE(PgfFlagsP, pointer, gu_type(PgfFlags));
|
||||
|
||||
GU_DEFINE_TYPE(PgfSequences, GuList, gu_type(PgfSequence));
|
||||
|
||||
GU_DEFINE_TYPE(PgfSeqId, typedef, gu_type(PgfSequence));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfCncFun, struct,
|
||||
GU_MEMBER(PgfCncFun, fun, PgfCId),
|
||||
GU_MEMBER(PgfCncFun, n_lins, GuLength),
|
||||
GU_FLEX_MEMBER(PgfCncFun, lins, PgfSeqId));
|
||||
|
||||
GU_DEFINE_TYPE(PgfCncFuns, GuList,
|
||||
GU_TYPE_LIT(referenced, _, gu_ptr_type(PgfCncFun)));
|
||||
|
||||
GU_DEFINE_TYPE(PgfFunId, shared, gu_type(PgfCncFun));
|
||||
|
||||
GU_DEFINE_TYPE(PgfFunIds, GuList, gu_type(PgfFunId));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfPArg, struct,
|
||||
GU_MEMBER_P(PgfPArg, hypos, PgfCCatIds),
|
||||
GU_MEMBER(PgfPArg, ccat, PgfCCatId));
|
||||
|
||||
GU_DEFINE_TYPE(PgfPArgs, GuSeq, gu_type(PgfPArg));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfProduction, GuVariant,
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_PRODUCTION_APPLY, PgfProductionApply,
|
||||
GU_MEMBER(PgfProductionApply, fun, PgfFunId),
|
||||
GU_MEMBER(PgfProductionApply, args, PgfPArgs)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_PRODUCTION_COERCE, PgfProductionCoerce,
|
||||
GU_MEMBER(PgfProductionCoerce, coerce, PgfCCatId)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_PRODUCTION_CONST, PgfProductionConst,
|
||||
GU_MEMBER(PgfProductionConst, expr, PgfExpr),
|
||||
GU_MEMBER(PgfProductionConst, n_toks, GuLength),
|
||||
GU_FLEX_MEMBER(PgfProductionConst, toks, GuString)));
|
||||
|
||||
GU_DEFINE_TYPE(PgfProductions, GuList, gu_type(PgfProduction));
|
||||
GU_DEFINE_TYPE(PgfProductionSeq, GuSeq, gu_type(PgfProduction));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfPatt, GuVariant,
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_PATT_APP, PgfPattApp,
|
||||
GU_MEMBER(PgfPattApp, ctor, PgfCId),
|
||||
GU_MEMBER(PgfPattApp, n_args, GuLength),
|
||||
GU_MEMBER(PgfPattApp, args, PgfPatt)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_PATT_LIT, PgfPattLit,
|
||||
GU_MEMBER(PgfPattLit, lit, PgfLiteral)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_PATT_VAR, PgfPattVar,
|
||||
GU_MEMBER(PgfPattVar, var, PgfCId)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_PATT_AS, PgfPattAs,
|
||||
GU_MEMBER(PgfPattAs, var, PgfCId),
|
||||
GU_MEMBER(PgfPattAs, patt, PgfPatt)),
|
||||
GU_CONSTRUCTOR(
|
||||
PGF_PATT_WILD, void),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_PATT_IMPL_ARG, PgfPattImplArg,
|
||||
GU_MEMBER(PgfPattImplArg, patt, PgfPatt)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_PATT_TILDE, PgfPattTilde,
|
||||
GU_MEMBER(PgfPattTilde, expr, PgfExpr)));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfEquation, struct,
|
||||
GU_MEMBER(PgfEquation, body, PgfExpr),
|
||||
GU_MEMBER(PgfEquation, n_patts, GuLength),
|
||||
GU_MEMBER(PgfEquation, patts, PgfPatt));
|
||||
|
||||
// Distinct type so we can give it special treatment in the reader
|
||||
GU_DEFINE_TYPE(PgfEquationsM, GuSeq, gu_type(PgfEquation));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfFunDecl, struct,
|
||||
GU_MEMBER_P(PgfFunDecl, type, PgfType),
|
||||
GU_MEMBER(PgfFunDecl, arity, int),
|
||||
GU_MEMBER(PgfFunDecl, defns, PgfEquationsM),
|
||||
GU_MEMBER(PgfFunDecl, prob, double));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfCatFun, struct,
|
||||
GU_MEMBER(PgfCatFun, prob, double),
|
||||
GU_MEMBER(PgfCatFun, fun, PgfCId));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfCat, struct,
|
||||
GU_MEMBER(PgfCat, context, PgfHypos),
|
||||
GU_MEMBER(PgfCat, n_functions, GuLength),
|
||||
GU_FLEX_MEMBER(PgfCat, functions, PgfCatFun));
|
||||
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfAbstr, struct,
|
||||
GU_MEMBER(PgfAbstr, aflags, PgfFlagsP),
|
||||
GU_MEMBER_V(PgfAbstr, funs,
|
||||
GU_TYPE_LIT(pointer, PgfCIdMap*,
|
||||
GU_TYPE_LIT(PgfCIdMap, _,
|
||||
gu_ptr_type(PgfFunDecl),
|
||||
&gu_null_struct))),
|
||||
GU_MEMBER_V(PgfAbstr, cats,
|
||||
GU_TYPE_LIT(pointer, PgfCIdMap*,
|
||||
GU_TYPE_LIT(PgfCIdMap, _,
|
||||
gu_ptr_type(PgfCat),
|
||||
&gu_null_struct))));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfPrintNames, PgfCIdMap, gu_type(GuString), NULL);
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfConcr, struct,
|
||||
GU_MEMBER(PgfConcr, cflags, PgfFlagsP),
|
||||
GU_MEMBER_P(PgfConcr, printnames, PgfPrintNames),
|
||||
GU_MEMBER_V(PgfConcr, cnccats,
|
||||
GU_TYPE_LIT(pointer, PgfCIdMap*,
|
||||
GU_TYPE_LIT(PgfCIdMap, _,
|
||||
gu_ptr_type(PgfCncCat),
|
||||
&gu_null_struct))));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfPGF, struct,
|
||||
GU_MEMBER(PgfPGF, major_version, uint16_t),
|
||||
GU_MEMBER(PgfPGF, minor_version, uint16_t),
|
||||
GU_MEMBER(PgfPGF, gflags, PgfFlagsP),
|
||||
GU_MEMBER(PgfPGF, absname, PgfCId),
|
||||
GU_MEMBER(PgfPGF, abstract, PgfAbstr),
|
||||
GU_MEMBER_V(PgfPGF, concretes,
|
||||
GU_TYPE_LIT(pointer, PgfCIdMap*,
|
||||
GU_TYPE_LIT(PgfCIdMap, _,
|
||||
gu_ptr_type(PgfConcr),
|
||||
&gu_null_struct))));
|
||||
|
||||
@@ -1,76 +1,330 @@
|
||||
#ifndef PGF_DATA_H
|
||||
#define PGF_DATA_H
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libpgf.
|
||||
*
|
||||
* Libpgf is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libpgf is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libpgf. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
typedef int BindType;
|
||||
#ifndef PGF_DATA_H_
|
||||
#define PGF_DATA_H_
|
||||
|
||||
#include "expr.h"
|
||||
#include "type.h"
|
||||
#include <gu/list.h>
|
||||
#include <gu/variant.h>
|
||||
#include <gu/map.h>
|
||||
#include <gu/string.h>
|
||||
#include <gu/type.h>
|
||||
#include <gu/seq.h>
|
||||
#include <pgf/pgf.h>
|
||||
#include <pgf/expr.h>
|
||||
|
||||
struct _String {
|
||||
int len;
|
||||
unsigned int chars[];
|
||||
typedef struct PgfCCat PgfCCat;
|
||||
typedef PgfCCat* PgfCCatId;
|
||||
extern GU_DECLARE_TYPE(PgfCCat, struct);
|
||||
extern GU_DECLARE_TYPE(PgfCCatId, shared);
|
||||
typedef GuList(PgfCCatId) PgfCCatIds;
|
||||
extern GU_DECLARE_TYPE(PgfCCatIds, GuList);
|
||||
typedef GuSeq PgfCCatSeq;
|
||||
extern GU_DECLARE_TYPE(PgfCCatSeq, GuSeq);
|
||||
|
||||
typedef struct PgfAbstr PgfAbstr;
|
||||
typedef struct PgfFunDecl PgfFunDecl;
|
||||
typedef struct PgfConcr PgfConcr;
|
||||
|
||||
typedef int PgfLength;
|
||||
typedef struct GuVariant PgfSymbol;
|
||||
typedef struct PgfAlternative PgfAlternative;
|
||||
typedef struct PgfCncFun PgfCncFun;
|
||||
|
||||
|
||||
typedef GuSeq PgfSequence; // -> PgfSymbol
|
||||
|
||||
typedef PgfCncFun* PgfFunId; // key to PgfCncFuns
|
||||
extern GU_DECLARE_TYPE(PgfFunId, shared);
|
||||
typedef GuList(PgfCncFun*) PgfCncFuns;
|
||||
extern GU_DECLARE_TYPE(PgfCncFuns, GuList);
|
||||
typedef GuList(PgfFunId) PgfFunIds;
|
||||
extern GU_DECLARE_TYPE(PgfFunIds, GuList);
|
||||
// typedef GuStringMap PgfCIdMap; // PgfCId -> ?
|
||||
#define PgfCIdMap GuStringMap
|
||||
typedef PgfCIdMap PgfFlags; // PgfCId -> PgfLiteral
|
||||
extern GU_DECLARE_TYPE(PgfFlags, GuMap);
|
||||
|
||||
extern GU_DECLARE_TYPE(PgfType, struct);
|
||||
typedef GuVariant PgfProduction;
|
||||
typedef GuList(PgfProduction) PgfProductions;
|
||||
extern GU_DECLARE_TYPE(PgfProductions, GuList);
|
||||
typedef GuSeq PgfProductionSeq;
|
||||
extern GU_DECLARE_TYPE(PgfProductionSeq, GuSeq);
|
||||
|
||||
typedef struct PgfCatFun PgfCatFun;
|
||||
typedef struct PgfCncCat PgfCncCat;
|
||||
extern GU_DECLARE_TYPE(PgfCncCat, struct);
|
||||
typedef GuVariant PgfPatt;
|
||||
|
||||
typedef GuList(GuString) GuStringL;
|
||||
extern GU_DECLARE_TYPE(GuStringL, GuList);
|
||||
typedef GuSeq PgfTokens; // -> PgfToken
|
||||
extern GU_DECLARE_TYPE(PgfTokens, GuSeq);
|
||||
|
||||
bool
|
||||
pgf_tokens_equal(PgfTokens t1, PgfTokens t2);
|
||||
|
||||
|
||||
|
||||
typedef PgfExpr PgfTree;
|
||||
|
||||
typedef struct PgfEquation PgfEquation;
|
||||
typedef GuSeq PgfEquations;
|
||||
typedef PgfEquations PgfEquationsM; // can be null
|
||||
extern GU_DECLARE_TYPE(PgfEquationsM, GuSeq);
|
||||
typedef struct PgfCat PgfCat;
|
||||
|
||||
typedef PgfSequence PgfSeqId; // shared reference
|
||||
|
||||
extern GU_DECLARE_TYPE(PgfSeqId, typedef);
|
||||
|
||||
typedef GuList(PgfSequence) PgfSequences;
|
||||
|
||||
extern GU_DECLARE_TYPE(PgfSequences, GuList);
|
||||
|
||||
|
||||
|
||||
|
||||
struct PgfAbstr {
|
||||
PgfFlags* aflags;
|
||||
PgfCIdMap* funs; // |-> PgfFunDecl*
|
||||
PgfCIdMap* cats; // |-> PgfCat*
|
||||
};
|
||||
|
||||
struct _CId {
|
||||
int len;
|
||||
char chars[];
|
||||
struct PgfPGF {
|
||||
uint16_t major_version;
|
||||
uint16_t minor_version;
|
||||
PgfFlags* gflags;
|
||||
PgfCId absname;
|
||||
PgfAbstr abstract;
|
||||
PgfCIdMap* concretes; // |-> PgfConcr*
|
||||
GuPool* pool;
|
||||
};
|
||||
|
||||
typedef struct _CIdList {
|
||||
int count;
|
||||
CId names[];
|
||||
} *CIdList;
|
||||
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_ */
|
||||
|
||||
20
src/runtime/c/pgf/edsl.h
Normal file
20
src/runtime/c/pgf/edsl.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef PGF_EDSL_H_
|
||||
#define PGF_EDSL_H_
|
||||
|
||||
#include <pgf/expr.h>
|
||||
|
||||
#define APP(f, a) \
|
||||
gu_new_variant_i(PGF_EDSL_POOL, PGF_EXPR_APP, PgfExprApp, f, a)
|
||||
#define APP2(f, a1, a2) APP(APP(f, a1), a2)
|
||||
#define APP3(f, a1, a2, a3) APP2(APP(f, a1), a2, a3)
|
||||
|
||||
#define VAR(s) \
|
||||
gu_new_variant_i(PGF_EDSL_POOL, PGF_EXPR_FUN, PgfExprFun, gu_cstring(#s))
|
||||
|
||||
#define APPV(s, a) APP(VAR(s), a)
|
||||
#define APPV2(s, a1, a2) APP2(VAR(s), a1, a2)
|
||||
#define APPV3(s, a1, a2, a3) APP3(VAR(s), a1, a2)
|
||||
|
||||
|
||||
|
||||
#endif // PGF_EDSL_H_
|
||||
334
src/runtime/c/pgf/expr.c
Normal file
334
src/runtime/c/pgf/expr.c
Normal file
@@ -0,0 +1,334 @@
|
||||
#include "expr.h"
|
||||
#include <gu/intern.h>
|
||||
#include <gu/assert.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
PgfExpr
|
||||
pgf_expr_unwrap(PgfExpr expr)
|
||||
{
|
||||
while (true) {
|
||||
GuVariantInfo i = gu_variant_open(expr);
|
||||
switch (i.tag) {
|
||||
case PGF_EXPR_IMPL_ARG: {
|
||||
PgfExprImplArg* eimpl = i.data;
|
||||
expr = eimpl->expr;
|
||||
break;
|
||||
}
|
||||
case PGF_EXPR_TYPED: {
|
||||
PgfExprTyped* etyped = i.data;
|
||||
expr = etyped->expr;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pgf_expr_arity(PgfExpr expr)
|
||||
{
|
||||
int n = 0;
|
||||
while (true) {
|
||||
PgfExpr e = pgf_expr_unwrap(expr);
|
||||
GuVariantInfo i = gu_variant_open(e);
|
||||
switch (i.tag) {
|
||||
case PGF_EXPR_APP: {
|
||||
PgfExprApp* app = i.data;
|
||||
expr = app->fun;
|
||||
n = n + 1;
|
||||
break;
|
||||
}
|
||||
case PGF_EXPR_FUN:
|
||||
return n;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PgfApplication*
|
||||
pgf_expr_unapply(PgfExpr expr, GuPool* pool)
|
||||
{
|
||||
int arity = pgf_expr_arity(expr);
|
||||
if (arity < 0) {
|
||||
return NULL;
|
||||
}
|
||||
PgfApplication* appl = gu_new_flex(pool, PgfApplication, args, arity);
|
||||
appl->n_args = arity;
|
||||
for (int n = arity - 1; n >= 0; n--) {
|
||||
PgfExpr e = pgf_expr_unwrap(expr);
|
||||
gu_assert(gu_variant_tag(e) == PGF_EXPR_APP);
|
||||
PgfExprApp* app = gu_variant_data(e);
|
||||
appl->args[n] = app->arg;
|
||||
expr = app->fun;
|
||||
}
|
||||
PgfExpr e = pgf_expr_unwrap(expr);
|
||||
gu_assert(gu_variant_tag(e) == PGF_EXPR_FUN);
|
||||
PgfExprFun* fun = gu_variant_data(e);
|
||||
appl->fun = fun->fun;
|
||||
return appl;
|
||||
}
|
||||
|
||||
GU_DEFINE_TYPE(PgfBindType, enum,
|
||||
GU_ENUM_C(PgfBindType, PGF_BIND_TYPE_EXPLICIT),
|
||||
GU_ENUM_C(PgfBindType, PGF_BIND_TYPE_IMPLICIT));
|
||||
|
||||
GU_DEFINE_TYPE(PgfLiteral, GuVariant,
|
||||
GU_CONSTRUCTOR_S(PGF_LITERAL_STR, PgfLiteralStr,
|
||||
GU_MEMBER(PgfLiteralStr, val, GuString)),
|
||||
GU_CONSTRUCTOR_S(PGF_LITERAL_INT, PgfLiteralInt,
|
||||
GU_MEMBER(PgfLiteralInt, val, int)),
|
||||
GU_CONSTRUCTOR_S(PGF_LITERAL_FLT, PgfLiteralFlt,
|
||||
GU_MEMBER(PgfLiteralFlt, val, double)));
|
||||
|
||||
GU_DECLARE_TYPE(PgfType, struct);
|
||||
|
||||
GU_DEFINE_TYPE(PgfHypo, struct,
|
||||
GU_MEMBER(PgfHypo, bindtype, PgfBindType),
|
||||
GU_MEMBER(PgfHypo, cid, PgfCId),
|
||||
GU_MEMBER_P(PgfHypo, type, PgfType));
|
||||
|
||||
GU_DEFINE_TYPE(PgfHypos, GuSeq, gu_type(PgfHypo));
|
||||
|
||||
GU_DEFINE_TYPE(PgfType, struct,
|
||||
GU_MEMBER(PgfType, hypos, PgfHypos),
|
||||
GU_MEMBER(PgfType, cid, PgfCId),
|
||||
GU_MEMBER(PgfType, n_exprs, GuLength),
|
||||
GU_FLEX_MEMBER(PgfType, exprs, PgfExpr));
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfExpr, GuVariant,
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_EXPR_ABS, PgfExprAbs,
|
||||
GU_MEMBER(PgfExprAbs, bind_type, PgfBindType),
|
||||
GU_MEMBER(PgfExprAbs, id, GuStr),
|
||||
GU_MEMBER(PgfExprAbs, body, PgfExpr)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_EXPR_APP, PgfExprApp,
|
||||
GU_MEMBER(PgfExprApp, fun, PgfExpr),
|
||||
GU_MEMBER(PgfExprApp, arg, PgfExpr)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_EXPR_LIT, PgfExprLit,
|
||||
GU_MEMBER(PgfExprLit, lit, PgfLiteral)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_EXPR_META, PgfExprMeta,
|
||||
GU_MEMBER(PgfExprMeta, id, int)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_EXPR_FUN, PgfExprFun,
|
||||
GU_MEMBER(PgfExprFun, fun, GuStr)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_EXPR_VAR, PgfExprVar,
|
||||
GU_MEMBER(PgfExprVar, var, int)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_EXPR_TYPED, PgfExprTyped,
|
||||
GU_MEMBER(PgfExprTyped, expr, PgfExpr),
|
||||
GU_MEMBER_P(PgfExprTyped, type, PgfType)),
|
||||
GU_CONSTRUCTOR_S(
|
||||
PGF_EXPR_IMPL_ARG, PgfExprImplArg,
|
||||
GU_MEMBER(PgfExprImplArg, expr, PgfExpr)));
|
||||
|
||||
|
||||
typedef struct PgfExprParser PgfExprParser;
|
||||
|
||||
struct PgfExprParser {
|
||||
GuReader* rdr;
|
||||
GuIntern* intern;
|
||||
GuExn* err;
|
||||
GuPool* expr_pool;
|
||||
const char* lookahead;
|
||||
int next_char;
|
||||
};
|
||||
|
||||
|
||||
static const char pgf_expr_lpar[] = "(";
|
||||
static const char pgf_expr_rpar[] = ")";
|
||||
static const char pgf_expr_semic[] = ";";
|
||||
|
||||
static char
|
||||
pgf_expr_parser_next(PgfExprParser* parser)
|
||||
{
|
||||
if (parser->next_char >= 0) {
|
||||
char ret = (char) parser->next_char;
|
||||
parser->next_char = -1;
|
||||
return ret;
|
||||
}
|
||||
return gu_getc(parser->rdr, parser->err);
|
||||
}
|
||||
|
||||
static const char*
|
||||
pgf_expr_parser_lookahead(PgfExprParser* parser)
|
||||
{
|
||||
if (parser->lookahead != NULL) {
|
||||
return parser->lookahead;
|
||||
}
|
||||
const char* str = NULL;
|
||||
char c;
|
||||
do {
|
||||
c = pgf_expr_parser_next(parser);
|
||||
if (!gu_ok(parser->err)) {
|
||||
return NULL;
|
||||
}
|
||||
} while (isspace(c));
|
||||
switch (c) {
|
||||
case '(':
|
||||
str = pgf_expr_lpar;
|
||||
break;
|
||||
case ')':
|
||||
str = pgf_expr_rpar;
|
||||
break;
|
||||
case ';':
|
||||
str = pgf_expr_semic;
|
||||
break;
|
||||
default:
|
||||
if (isalpha(c)) {
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
GuCharBuf* chars = gu_new_buf(char, tmp_pool);
|
||||
while (isalnum(c) || c == '_') {
|
||||
gu_buf_push(chars, char, c);
|
||||
c = pgf_expr_parser_next(parser);
|
||||
if (!gu_ok(parser->err)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
parser->next_char = (unsigned char) c;
|
||||
char* tmp_str = gu_chars_str(gu_buf_seq(chars),
|
||||
tmp_pool);
|
||||
str = gu_intern_str(parser->intern, tmp_str);
|
||||
gu_pool_free(tmp_pool);
|
||||
}
|
||||
}
|
||||
parser->lookahead = str;
|
||||
return str;
|
||||
}
|
||||
|
||||
static bool
|
||||
pgf_expr_parser_token_is_id(const char* str)
|
||||
{
|
||||
if (str == NULL || !str[0]) {
|
||||
return false;
|
||||
}
|
||||
char c = str[0];
|
||||
return (isalpha(c) || c == '_');
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_expr_parser_consume(PgfExprParser* parser)
|
||||
{
|
||||
pgf_expr_parser_lookahead(parser);
|
||||
parser->lookahead = NULL;
|
||||
}
|
||||
|
||||
static PgfExpr
|
||||
pgf_expr_parser_expr(PgfExprParser* parser);
|
||||
|
||||
static PgfExpr
|
||||
pgf_expr_parser_term(PgfExprParser* parser)
|
||||
{
|
||||
const char* la = pgf_expr_parser_lookahead(parser);
|
||||
|
||||
if (la == pgf_expr_lpar) {
|
||||
pgf_expr_parser_consume(parser);
|
||||
PgfExpr expr = pgf_expr_parser_expr(parser);
|
||||
la = pgf_expr_parser_lookahead(parser);
|
||||
if (la == pgf_expr_rpar) {
|
||||
pgf_expr_parser_consume(parser);
|
||||
return expr;
|
||||
}
|
||||
} else if (pgf_expr_parser_token_is_id(la)) {
|
||||
pgf_expr_parser_consume(parser);
|
||||
GuString s = gu_str_string(la, parser->expr_pool);
|
||||
return gu_new_variant_i(parser->expr_pool,
|
||||
PGF_EXPR_FUN,
|
||||
PgfExprFun,
|
||||
s);
|
||||
}
|
||||
return gu_null_variant;
|
||||
}
|
||||
|
||||
static PgfExpr
|
||||
pgf_expr_parser_expr(PgfExprParser* parser)
|
||||
{
|
||||
PgfExpr expr = pgf_expr_parser_term(parser);
|
||||
if (gu_variant_is_null(expr))
|
||||
{
|
||||
return expr;
|
||||
}
|
||||
while (true) {
|
||||
PgfExpr arg = pgf_expr_parser_term(parser);
|
||||
if (gu_variant_is_null(arg)) {
|
||||
return expr;
|
||||
}
|
||||
expr = gu_new_variant_i(parser->expr_pool,
|
||||
PGF_EXPR_APP,
|
||||
PgfExprApp,
|
||||
expr, arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
PgfExpr
|
||||
pgf_read_expr(GuReader* rdr, GuPool* pool, GuExn* err)
|
||||
{
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
PgfExprParser* parser = gu_new(PgfExprParser, tmp_pool);
|
||||
parser->rdr = rdr;
|
||||
parser->intern = gu_new_intern(pool, tmp_pool);
|
||||
parser->expr_pool = pool;
|
||||
parser->err = err;
|
||||
parser->lookahead = NULL;
|
||||
parser->next_char = -1;
|
||||
PgfExpr expr = pgf_expr_parser_expr(parser);
|
||||
const char* la = pgf_expr_parser_lookahead(parser);
|
||||
if (la == pgf_expr_semic) {
|
||||
pgf_expr_parser_consume(parser);
|
||||
} else {
|
||||
expr = gu_null_variant;
|
||||
}
|
||||
gu_pool_free(tmp_pool);
|
||||
return expr;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_expr_print_with_paren(PgfExpr expr, bool need_paren,
|
||||
GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
GuVariantInfo ei = gu_variant_open(expr);
|
||||
switch (ei.tag) {
|
||||
case PGF_EXPR_FUN: {
|
||||
PgfExprFun* fun = ei.data;
|
||||
gu_string_write(fun->fun, wtr, err);
|
||||
break;
|
||||
}
|
||||
case PGF_EXPR_APP: {
|
||||
PgfExprApp* app = ei.data;
|
||||
if (need_paren) {
|
||||
gu_puts("(", wtr, err);
|
||||
}
|
||||
pgf_expr_print_with_paren(app->fun, false, wtr, err);
|
||||
gu_puts(" ", wtr, err);
|
||||
pgf_expr_print_with_paren(app->arg, true, wtr, err);
|
||||
if (need_paren) {
|
||||
gu_puts(")", wtr, err);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PGF_EXPR_ABS:
|
||||
case PGF_EXPR_LIT:
|
||||
case PGF_EXPR_META:
|
||||
case PGF_EXPR_VAR:
|
||||
case PGF_EXPR_TYPED:
|
||||
case PGF_EXPR_IMPL_ARG:
|
||||
gu_impossible();
|
||||
break;
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pgf_expr_print(PgfExpr expr, GuWriter* wtr, GuExn* err) {
|
||||
pgf_expr_print_with_paren(expr, false, wtr, err);
|
||||
}
|
||||
@@ -1,144 +1,152 @@
|
||||
#ifndef PGF_EXPR_H
|
||||
#define PGF_EXPR_H
|
||||
#ifndef EXPR_H_
|
||||
#define EXPR_H_
|
||||
|
||||
#define LIT_STR 0
|
||||
#define LIT_INT 1
|
||||
#define LIT_FLOAT 2
|
||||
#include <gu/read.h>
|
||||
#include <gu/write.h>
|
||||
#include <gu/variant.h>
|
||||
#include <gu/seq.h>
|
||||
#include <pgf/pgf.h>
|
||||
|
||||
struct _Literal {
|
||||
int tag;
|
||||
/// Abstract syntax trees
|
||||
/// @file
|
||||
|
||||
/// An abstract syntax tree
|
||||
typedef GuVariant PgfExpr;
|
||||
|
||||
GU_DECLARE_TYPE(PgfExpr, GuVariant);
|
||||
|
||||
typedef GuList(PgfExpr) PgfExprs;
|
||||
|
||||
typedef struct PgfHypo PgfHypo;
|
||||
typedef struct PgfType PgfType;
|
||||
|
||||
typedef int PgfMetaId;
|
||||
|
||||
typedef enum {
|
||||
PGF_BIND_TYPE_EXPLICIT,
|
||||
PGF_BIND_TYPE_IMPLICIT
|
||||
} PgfBindType;
|
||||
|
||||
// PgfLiteral
|
||||
|
||||
typedef GuVariant PgfLiteral;
|
||||
|
||||
|
||||
typedef enum {
|
||||
PGF_LITERAL_STR,
|
||||
PGF_LITERAL_INT,
|
||||
PGF_LITERAL_FLT,
|
||||
PGF_LITERAL_NUM_TAGS
|
||||
} PgfLiteralTag;
|
||||
|
||||
typedef struct {
|
||||
GuStr val;
|
||||
} PgfLiteralStr;
|
||||
|
||||
typedef struct {
|
||||
int val;
|
||||
} PgfLiteralInt;
|
||||
|
||||
typedef struct {
|
||||
double val;
|
||||
} PgfLiteralFlt;
|
||||
|
||||
|
||||
|
||||
struct PgfHypo {
|
||||
PgfBindType bindtype;
|
||||
|
||||
PgfCId cid;
|
||||
/**< Locally scoped name for the parameter if dependent types
|
||||
* are used. "_" for normal parameters. */
|
||||
|
||||
PgfType* type;
|
||||
};
|
||||
|
||||
typedef 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_ */
|
||||
|
||||
613
src/runtime/c/pgf/linearize.c
Normal file
613
src/runtime/c/pgf/linearize.c
Normal file
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libpgf.
|
||||
*
|
||||
* Libpgf is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libpgf is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libpgf. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "data.h"
|
||||
#include "linearize.h"
|
||||
#include <gu/map.h>
|
||||
#include <gu/fun.h>
|
||||
#include <gu/log.h>
|
||||
#include <gu/choice.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/string.h>
|
||||
#include <gu/assert.h>
|
||||
#include <pgf/expr.h>
|
||||
|
||||
typedef GuStringMap PgfLinInfer;
|
||||
typedef GuSeq PgfProdSeq;
|
||||
|
||||
static GU_DEFINE_TYPE(PgfProdSeq, GuSeq, gu_type(PgfProduction));
|
||||
|
||||
typedef struct PgfLinInferEntry PgfLinInferEntry;
|
||||
|
||||
struct PgfLinInferEntry {
|
||||
PgfCCat* cat;
|
||||
PgfCncFun* fun;
|
||||
};
|
||||
|
||||
static GU_DEFINE_TYPE(
|
||||
PgfLinInferEntry, struct,
|
||||
GU_MEMBER_P(PgfLinInferEntry, cat, PgfCCat)
|
||||
// ,GU_MEMBER(PgfLinInferEntry, fun, ...)
|
||||
);
|
||||
|
||||
typedef GuBuf PgfLinInfers;
|
||||
static GU_DEFINE_TYPE(PgfLinInfers, GuBuf, gu_type(PgfLinInferEntry));
|
||||
|
||||
typedef GuIntMap PgfCncProds;
|
||||
static GU_DEFINE_TYPE(PgfCncProds, GuIntMap, gu_type(PgfProdSeq),
|
||||
&gu_null_seq);
|
||||
|
||||
typedef GuStringMap PgfLinProds;
|
||||
static GU_DEFINE_TYPE(PgfLinProds, GuStringMap, gu_ptr_type(PgfCncProds),
|
||||
&gu_null_struct);
|
||||
|
||||
|
||||
static GuHash
|
||||
pgf_lzr_cats_hash_fn(GuHasher* self, const void* p)
|
||||
{
|
||||
(void) self;
|
||||
PgfCCatIds* cats = *(PgfCCatIds* const*)p;
|
||||
size_t len = gu_list_length(cats);
|
||||
uintptr_t h = 0;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
h = 101 * h + (uintptr_t) gu_list_index(cats, i);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static bool
|
||||
pgf_lzr_cats_eq_fn(GuEquality* self, const void* p1, const void* p2)
|
||||
{
|
||||
(void) self;
|
||||
PgfCCatIds* cats1 = *(PgfCCatIds* const*) p1;
|
||||
PgfCCatIds* cats2 = *(PgfCCatIds* const*) p2;
|
||||
int len = gu_list_length(cats1);
|
||||
if (gu_list_length(cats2) != len) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
PgfCCat* cat1 = gu_list_index(cats1, i);
|
||||
PgfCCat* cat2 = gu_list_index(cats2, i);
|
||||
if (cat1 != cat2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static GuHasher
|
||||
pgf_lzr_cats_hasher[1] = {
|
||||
{
|
||||
.eq = { pgf_lzr_cats_eq_fn },
|
||||
.hash = pgf_lzr_cats_hash_fn
|
||||
}
|
||||
};
|
||||
|
||||
typedef GuMap PgfInferMap;
|
||||
static GU_DEFINE_TYPE(PgfInferMap, GuMap,
|
||||
gu_ptr_type(PgfCCatIds), pgf_lzr_cats_hasher,
|
||||
gu_ptr_type(PgfLinInfers), &gu_null_struct);
|
||||
|
||||
typedef GuStringMap PgfFunIndices;
|
||||
static GU_DEFINE_TYPE(PgfFunIndices, GuStringMap, gu_ptr_type(PgfInferMap),
|
||||
&gu_null_struct);
|
||||
|
||||
typedef GuBuf PgfCCatBuf;
|
||||
static GU_DEFINE_TYPE(PgfCCatBuf, GuBuf, gu_ptr_type(PgfCCat));
|
||||
|
||||
typedef GuMap PgfCoerceIdx;
|
||||
static GU_DEFINE_TYPE(PgfCoerceIdx, GuMap,
|
||||
gu_type(PgfCCat), NULL,
|
||||
gu_ptr_type(PgfCCatBuf), &gu_null_struct);
|
||||
|
||||
struct PgfLzr {
|
||||
PgfConcr* cnc;
|
||||
GuPool* pool;
|
||||
PgfFunIndices* fun_indices;
|
||||
PgfCoerceIdx* coerce_idx;
|
||||
};
|
||||
|
||||
GU_DEFINE_TYPE(
|
||||
PgfLzr, struct,
|
||||
GU_MEMBER_P(PgfLzr, cnc, PgfConcr),
|
||||
GU_MEMBER_P(PgfLzr, fun_indices, PgfFunIndices),
|
||||
GU_MEMBER_P(PgfLzr, coerce_idx, PgfCoerceIdx));
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
pgf_lzr_add_infer_entry(PgfLzr* lzr,
|
||||
PgfInferMap* infer_table,
|
||||
PgfCCat* cat,
|
||||
PgfProductionApply* papply)
|
||||
{
|
||||
PgfPArgs args = papply->args;
|
||||
size_t n_args = gu_seq_length(args);
|
||||
PgfCCatIds* arg_cats = gu_new_list(PgfCCatIds, lzr->pool, n_args);
|
||||
for (size_t i = 0; i < n_args; i++) {
|
||||
// XXX: What about the hypos in the args?
|
||||
gu_list_index(arg_cats, i) = gu_seq_get(args, PgfPArg, i).ccat;
|
||||
}
|
||||
gu_debug("%d,%d,%d -> %d, %s",
|
||||
n_args > 0 ? gu_list_index(arg_cats, 0)->fid : -1,
|
||||
n_args > 1 ? gu_list_index(arg_cats, 1)->fid : -1,
|
||||
n_args > 2 ? gu_list_index(arg_cats, 2)->fid : -1,
|
||||
cat->fid, papply->fun->fun);
|
||||
PgfLinInfers* entries =
|
||||
gu_map_get(infer_table, &arg_cats, PgfLinInfers*);
|
||||
if (!entries) {
|
||||
entries = gu_new_buf(PgfLinInferEntry, lzr->pool);
|
||||
gu_map_put(infer_table, &arg_cats, PgfLinInfers*, entries);
|
||||
} else {
|
||||
// XXX: arg_cats is duplicate, we ought to free it
|
||||
// Display warning?
|
||||
}
|
||||
|
||||
PgfLinInferEntry entry = {
|
||||
.cat = cat,
|
||||
.fun = papply->fun
|
||||
};
|
||||
gu_buf_push(entries, PgfLinInferEntry, entry);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pgf_lzr_index(PgfLzr* lzr, PgfCCat* cat, PgfProduction prod)
|
||||
{
|
||||
void* data = gu_variant_data(prod);
|
||||
switch (gu_variant_tag(prod)) {
|
||||
case PGF_PRODUCTION_APPLY: {
|
||||
PgfProductionApply* papply = data;
|
||||
PgfInferMap* infer =
|
||||
gu_map_get(lzr->fun_indices, &papply->fun->fun,
|
||||
PgfInferMap*);
|
||||
gu_debug("index: %s -> %d", papply->fun->fun, cat->fid);
|
||||
if (!infer) {
|
||||
infer = gu_map_type_new(PgfInferMap, lzr->pool);
|
||||
gu_map_put(lzr->fun_indices,
|
||||
&papply->fun->fun, PgfInferMap*, infer);
|
||||
}
|
||||
pgf_lzr_add_infer_entry(lzr, infer, cat, papply);
|
||||
break;
|
||||
}
|
||||
case PGF_PRODUCTION_COERCE: {
|
||||
PgfProductionCoerce* pcoerce = data;
|
||||
PgfCCatBuf* cats = gu_map_get(lzr->coerce_idx, pcoerce->coerce,
|
||||
PgfCCatBuf*);
|
||||
if (!cats) {
|
||||
cats = gu_new_buf(PgfCCat*, lzr->pool);
|
||||
gu_map_put(lzr->coerce_idx,
|
||||
pcoerce->coerce, PgfCCatBuf*, cats);
|
||||
}
|
||||
gu_debug("coerce_idx: %d -> %d", pcoerce->coerce->fid, cat->fid);
|
||||
gu_buf_push(cats, PgfCCat*, cat);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Display warning?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_lzr_index_ccat(PgfLzr* lzr, PgfCCat* cat)
|
||||
{
|
||||
gu_debug("ccat: %d", cat->fid);
|
||||
if (gu_seq_is_null(cat->prods)) {
|
||||
return;
|
||||
}
|
||||
size_t n_prods = gu_seq_length(cat->prods);
|
||||
for (size_t i = 0; i < n_prods; i++) {
|
||||
PgfProduction prod = gu_seq_get(cat->prods, PgfProduction, i);
|
||||
pgf_lzr_index(lzr, cat, prod);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GuMapItor fn;
|
||||
PgfLzr* lzr;
|
||||
} PgfLzrIndexFn;
|
||||
|
||||
static void
|
||||
pgf_lzr_index_cnccat_cb(GuMapItor* fn, const void* key, void* value,
|
||||
GuExn* err)
|
||||
{
|
||||
(void) (key && err);
|
||||
PgfLzrIndexFn* clo = (PgfLzrIndexFn*) fn;
|
||||
PgfCncCat** cnccatp = value;
|
||||
PgfCncCat* cnccat = *cnccatp;
|
||||
gu_enter("-> cnccat: %s", cnccat->cid);
|
||||
int n_ccats = gu_list_length(cnccat->cats);
|
||||
for (int i = 0; i < n_ccats; i++) {
|
||||
PgfCCat* cat = gu_list_index(cnccat->cats, i);
|
||||
if (cat) {
|
||||
pgf_lzr_index_ccat(clo->lzr, cat);
|
||||
}
|
||||
}
|
||||
gu_exit("<-");
|
||||
}
|
||||
|
||||
|
||||
PgfLzr*
|
||||
pgf_new_lzr(PgfConcr* cnc, GuPool* pool)
|
||||
{
|
||||
PgfLzr* lzr = gu_new(PgfLzr, pool);
|
||||
lzr->cnc = cnc;
|
||||
lzr->pool = pool;
|
||||
lzr->fun_indices = gu_map_type_new(PgfFunIndices, pool);
|
||||
lzr->coerce_idx = gu_map_type_new(PgfCoerceIdx, pool);
|
||||
PgfLzrIndexFn clo = { { pgf_lzr_index_cnccat_cb }, lzr };
|
||||
gu_map_iter(cnc->cnccats, &clo.fn, NULL);
|
||||
size_t n_extras = gu_seq_length(cnc->extra_ccats);
|
||||
for (size_t i = 0; i < n_extras; i++) {
|
||||
PgfCCat* cat = gu_seq_get(cnc->extra_ccats, PgfCCat*, i);
|
||||
pgf_lzr_index_ccat(lzr, cat);
|
||||
}
|
||||
// TODO: prune productions with zero linearizations
|
||||
return lzr;
|
||||
}
|
||||
|
||||
typedef struct PgfLzn PgfLzn;
|
||||
|
||||
struct PgfLzn {
|
||||
PgfLzr* lzr;
|
||||
GuChoice* ch;
|
||||
PgfExpr expr;
|
||||
GuEnum en;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// PgfCncTree
|
||||
//
|
||||
|
||||
typedef enum {
|
||||
PGF_CNC_TREE_APP,
|
||||
PGF_CNC_TREE_LIT,
|
||||
} PgfCncTreeTag;
|
||||
|
||||
typedef struct PgfCncTreeApp PgfCncTreeApp;
|
||||
struct PgfCncTreeApp {
|
||||
PgfCncFun* fun;
|
||||
GuLength n_args;
|
||||
PgfCncTree args[];
|
||||
};
|
||||
|
||||
typedef struct PgfCncTreeLit PgfCncTreeLit;
|
||||
struct PgfCncTreeLit {
|
||||
PgfLiteral lit;
|
||||
};
|
||||
|
||||
|
||||
static PgfCCat*
|
||||
pgf_lzn_pick_supercat(PgfLzn* lzn, PgfCCat* cat)
|
||||
{
|
||||
gu_enter("->");
|
||||
while (true) {
|
||||
PgfCCatBuf* supers =
|
||||
gu_map_get(lzn->lzr->coerce_idx, cat, PgfCCatBuf*);
|
||||
if (!supers) {
|
||||
break;
|
||||
}
|
||||
gu_debug("n_supers: %d", gu_buf_length(supers));
|
||||
int ch = gu_choice_next(lzn->ch, gu_buf_length(supers) + 1);
|
||||
gu_debug("choice: %d", ch);
|
||||
if (ch == 0) {
|
||||
break;
|
||||
}
|
||||
cat = gu_buf_get(supers, PgfCCat*, ch - 1);
|
||||
}
|
||||
gu_exit("<- %d", cat->fid);
|
||||
return cat;
|
||||
}
|
||||
|
||||
static PgfCCat*
|
||||
pgf_lzn_infer(PgfLzn* lzn, PgfExpr expr, GuPool* pool, PgfCncTree* ctree_out);
|
||||
|
||||
static PgfCCat*
|
||||
pgf_lzn_infer_apply_try(PgfLzn* lzn, PgfApplication* appl,
|
||||
PgfInferMap* infer, GuChoiceMark* marks,
|
||||
PgfCCatIds* arg_cats, int* ip, int n_args,
|
||||
GuPool* pool, PgfCncTreeApp* app_out)
|
||||
{
|
||||
gu_enter("f: %s, *ip: %d, n_args: %d", appl->fun, *ip, n_args);
|
||||
PgfCCat* ret = NULL;
|
||||
while (*ip < n_args) {
|
||||
PgfCncTree* arg_treep =
|
||||
(app_out == NULL ? NULL : &app_out->args[*ip]);
|
||||
PgfCCat* arg_i =
|
||||
pgf_lzn_infer(lzn, appl->args[*ip], pool, arg_treep);
|
||||
if (arg_i == NULL) {
|
||||
goto finish;
|
||||
}
|
||||
arg_i = pgf_lzn_pick_supercat(lzn, arg_i);
|
||||
gu_list_index(arg_cats, *ip) = arg_i;
|
||||
marks[++*ip] = gu_choice_mark(lzn->ch);
|
||||
}
|
||||
PgfLinInfers* entries = gu_map_get(infer, &arg_cats, PgfLinInfers*);
|
||||
if (!entries) {
|
||||
goto finish;
|
||||
}
|
||||
size_t n_entries = gu_buf_length(entries);
|
||||
int e = gu_choice_next(lzn->ch, n_entries);
|
||||
gu_debug("entry %d of %d", e, n_entries);
|
||||
if (e < 0) {
|
||||
goto finish;
|
||||
}
|
||||
PgfLinInferEntry* entry = gu_buf_index(entries, PgfLinInferEntry, e);
|
||||
if (app_out != NULL) {
|
||||
app_out->fun = entry->fun;
|
||||
}
|
||||
ret = entry->cat;
|
||||
finish:
|
||||
gu_exit("fid: %d", ret ? ret->fid : -1);
|
||||
return ret;
|
||||
}
|
||||
typedef GuList(GuChoiceMark) PgfChoiceMarks;
|
||||
|
||||
static PgfCCat*
|
||||
pgf_lzn_infer_application(PgfLzn* lzn, PgfApplication* appl,
|
||||
GuPool* pool, PgfCncTree* ctree_out)
|
||||
{
|
||||
PgfInferMap* infer =
|
||||
gu_map_get(lzn->lzr->fun_indices, &appl->fun, PgfInferMap*);
|
||||
gu_enter("-> f: %s, n_args: %d", appl->fun, appl->n_args);
|
||||
if (infer == NULL) {
|
||||
gu_exit("<- couldn't find f");
|
||||
return NULL;
|
||||
}
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
PgfCCat* ret = NULL;
|
||||
int n = appl->n_args;
|
||||
PgfCCatIds* arg_cats = gu_new_list(PgfCCatIds, tmp_pool, n);
|
||||
|
||||
PgfCncTreeApp* appt = NULL;
|
||||
if (ctree_out) {
|
||||
appt = gu_new_flex_variant(PGF_CNC_TREE_APP, PgfCncTreeApp,
|
||||
args, n, ctree_out, pool);
|
||||
appt->n_args = n;
|
||||
}
|
||||
|
||||
PgfChoiceMarks* marksl = gu_new_list(PgfChoiceMarks, tmp_pool, n + 1);
|
||||
GuChoiceMark* marks = gu_list_elems(marksl);
|
||||
int i = 0;
|
||||
marks[i] = gu_choice_mark(lzn->ch);
|
||||
while (true) {
|
||||
ret = pgf_lzn_infer_apply_try(lzn, appl, infer,
|
||||
marks, arg_cats,
|
||||
&i, n, pool, appt);
|
||||
if (ret != NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
--i;
|
||||
if (i < 0) {
|
||||
goto finish;
|
||||
}
|
||||
gu_choice_reset(lzn->ch, marks[i]);
|
||||
} while (!gu_choice_advance(lzn->ch));
|
||||
}
|
||||
finish:
|
||||
gu_pool_free(tmp_pool);
|
||||
gu_exit("<- fid: %d", ret ? ret->fid : -1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PgfCCat*
|
||||
pgf_lzn_infer(PgfLzn* lzn, PgfExpr expr, GuPool* pool, PgfCncTree* ctree_out)
|
||||
{
|
||||
PgfCCat* ret = NULL;
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
PgfApplication* appl = pgf_expr_unapply(expr, tmp_pool);
|
||||
if (appl != NULL) {
|
||||
ret = pgf_lzn_infer_application(lzn, appl, pool, ctree_out);
|
||||
} else {
|
||||
GuVariantInfo i = gu_variant_open(pgf_expr_unwrap(expr));
|
||||
switch (i.tag) {
|
||||
case PGF_EXPR_LIT: {
|
||||
PgfExprLit* elit = i.data;
|
||||
if (pool != NULL) {
|
||||
*ctree_out = gu_new_variant_i(
|
||||
pool, PGF_CNC_TREE_LIT,
|
||||
PgfCncTreeLit,
|
||||
.lit = elit->lit);
|
||||
}
|
||||
ret = pgf_literal_cat(elit->lit);
|
||||
}
|
||||
default:
|
||||
// XXX: should we do something here?
|
||||
break;
|
||||
}
|
||||
}
|
||||
gu_pool_free(tmp_pool);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static PgfCncTree
|
||||
pgf_lzn_next(PgfLzn* lzn, GuPool* pool)
|
||||
{
|
||||
// XXX: rewrite this whole mess
|
||||
PgfCncTree ctree = gu_null_variant;
|
||||
if (gu_variant_is_null(lzn->expr)) {
|
||||
return ctree;
|
||||
}
|
||||
PgfCCat* cat = NULL;
|
||||
GuChoiceMark mark = gu_choice_mark(lzn->ch);
|
||||
do {
|
||||
cat = pgf_lzn_infer(lzn, lzn->expr, NULL, NULL);
|
||||
gu_choice_reset(lzn->ch, mark);
|
||||
} while (!cat && gu_choice_advance(lzn->ch));
|
||||
|
||||
if (cat) {
|
||||
PgfCCat* cat2 = pgf_lzn_infer(lzn, lzn->expr, pool, &ctree);
|
||||
gu_assert(cat == cat2);
|
||||
gu_debug("fid: %d", cat->fid);
|
||||
gu_choice_reset(lzn->ch, mark);
|
||||
if (!gu_choice_advance(lzn->ch)) {
|
||||
lzn->expr = gu_null_variant;
|
||||
}
|
||||
}
|
||||
return ctree;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_cnc_tree_enum_next(GuEnum* self, void* to, GuPool* pool)
|
||||
{
|
||||
PgfLzn* lzn = gu_container(self, PgfLzn, en);
|
||||
PgfCncTree* toc = to;
|
||||
*toc = pgf_lzn_next(lzn, pool);
|
||||
}
|
||||
|
||||
PgfCncTreeEnum*
|
||||
pgf_lzr_concretize(PgfLzr* lzr, PgfExpr expr, GuPool* pool)
|
||||
{
|
||||
PgfLzn* lzn = gu_new(PgfLzn, pool);
|
||||
lzn->lzr = lzr;
|
||||
lzn->expr = expr;
|
||||
lzn->ch = gu_new_choice(pool);
|
||||
lzn->en.next = pgf_cnc_tree_enum_next;
|
||||
return &lzn->en;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pgf_cnc_tree_dimension(PgfCncTree ctree)
|
||||
{
|
||||
GuVariantInfo cti = gu_variant_open(ctree);
|
||||
switch (cti.tag) {
|
||||
case PGF_CNC_TREE_LIT:
|
||||
return 1;
|
||||
case PGF_CNC_TREE_APP: {
|
||||
PgfCncTreeApp* fapp = cti.data;
|
||||
return fapp->fun->n_lins;
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pgf_lzr_linearize(PgfLzr* lzr, PgfCncTree ctree, size_t lin_idx, PgfLinFuncs** fnsp)
|
||||
{
|
||||
PgfLinFuncs* fns = *fnsp;
|
||||
GuVariantInfo cti = gu_variant_open(ctree);
|
||||
|
||||
switch (cti.tag) {
|
||||
case PGF_CNC_TREE_LIT: {
|
||||
gu_require(lin_idx == 0);
|
||||
PgfCncTreeLit* flit = cti.data;
|
||||
if (fns->expr_literal) {
|
||||
fns->expr_literal(fnsp, flit->lit);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PGF_CNC_TREE_APP: {
|
||||
PgfCncTreeApp* fapp = cti.data;
|
||||
PgfCncFun* fun = fapp->fun;
|
||||
if (fns->expr_apply) {
|
||||
fns->expr_apply(fnsp, fun->fun, fapp->n_args);
|
||||
}
|
||||
gu_require(lin_idx < fun->n_lins);
|
||||
PgfSequence seq = fun->lins[lin_idx];
|
||||
size_t nsyms = gu_seq_length(seq);
|
||||
PgfSymbol* syms = gu_seq_data(seq);
|
||||
for (size_t i = 0; i < nsyms; i++) {
|
||||
PgfSymbol sym = syms[i];
|
||||
GuVariantInfo sym_i = gu_variant_open(sym);
|
||||
switch (sym_i.tag) {
|
||||
case PGF_SYMBOL_CAT:
|
||||
case PGF_SYMBOL_VAR:
|
||||
case PGF_SYMBOL_LIT: {
|
||||
PgfSymbolIdx* sidx = sym_i.data;
|
||||
gu_assert((unsigned) sidx->d < fapp->n_args);
|
||||
PgfCncTree argf = fapp->args[sidx->d];
|
||||
pgf_lzr_linearize(lzr, argf, sidx->r, fnsp);
|
||||
break;
|
||||
}
|
||||
case PGF_SYMBOL_KS: {
|
||||
PgfSymbolKS* ks = sym_i.data;
|
||||
if (fns->symbol_tokens) {
|
||||
fns->symbol_tokens(fnsp, ks->tokens);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PGF_SYMBOL_KP: {
|
||||
// TODO: correct prefix-dependencies
|
||||
PgfSymbolKP* kp = sym_i.data;
|
||||
if (fns->symbol_tokens) {
|
||||
fns->symbol_tokens(fnsp,
|
||||
kp->default_form);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
}
|
||||
break;
|
||||
} // case PGF_CNC_TREE_APP
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct PgfSimpleLin PgfSimpleLin;
|
||||
|
||||
struct PgfSimpleLin {
|
||||
PgfLinFuncs* funcs;
|
||||
GuWriter* wtr;
|
||||
GuExn* err;
|
||||
};
|
||||
|
||||
static void
|
||||
pgf_file_lzn_symbol_tokens(PgfLinFuncs** funcs, PgfTokens toks)
|
||||
{
|
||||
PgfSimpleLin* flin = gu_container(funcs, PgfSimpleLin, funcs);
|
||||
if (!gu_ok(flin->err)) {
|
||||
return;
|
||||
}
|
||||
size_t len = gu_seq_length(toks);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
PgfToken tok = gu_seq_get(toks, PgfToken, i);
|
||||
gu_string_write(tok, flin->wtr, flin->err);
|
||||
gu_putc(' ', flin->wtr, flin->err);
|
||||
}
|
||||
}
|
||||
|
||||
static PgfLinFuncs pgf_file_lin_funcs = {
|
||||
.symbol_tokens = pgf_file_lzn_symbol_tokens
|
||||
};
|
||||
|
||||
void
|
||||
pgf_lzr_linearize_simple(PgfLzr* lzr, PgfCncTree ctree,
|
||||
size_t lin_idx, GuWriter* wtr, GuExn* err)
|
||||
{
|
||||
PgfSimpleLin flin = {
|
||||
.funcs = &pgf_file_lin_funcs,
|
||||
.wtr = wtr,
|
||||
.err = err
|
||||
};
|
||||
pgf_lzr_linearize(lzr, ctree, lin_idx, &flin.funcs);
|
||||
}
|
||||
156
src/runtime/c/pgf/linearize.h
Normal file
156
src/runtime/c/pgf/linearize.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2010-2011 University of Helsinki.
|
||||
*
|
||||
* This file is part of libpgf.
|
||||
*
|
||||
* Libpgf is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libpgf is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libpgf. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <gu/type.h>
|
||||
#include <gu/dump.h>
|
||||
#include <gu/enum.h>
|
||||
#include <pgf/data.h>
|
||||
|
||||
/// Linearization of abstract syntax trees.
|
||||
/// @file
|
||||
|
||||
/** @name Linearizers
|
||||
*
|
||||
* Linearization begins by choosing a concrete category (#PgfConcr) for some
|
||||
* grammar, and creating a new linearizer (#PgfLzr) which can then be used to
|
||||
* linearize abstract syntax trees (#PgfExpr) of that grammar into the given
|
||||
* concrete category.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/// A linearizer.
|
||||
typedef struct PgfLzr PgfLzr;
|
||||
/**<
|
||||
*
|
||||
* A #PgfLzr object transforms abstract syntax trees of a PGF grammar
|
||||
* into sequences of token events for a single concrete category of
|
||||
* that grammar.
|
||||
*
|
||||
*/
|
||||
GU_DECLARE_TYPE(PgfLzr, struct);
|
||||
|
||||
|
||||
/// Create a new linearizer.
|
||||
PgfLzr*
|
||||
pgf_new_lzr(PgfConcr* cnc, GuPool* pool);
|
||||
/**<
|
||||
* @param cnc The concrete category to linearize to.
|
||||
*
|
||||
* @pool
|
||||
*
|
||||
* @return A new linearizer.
|
||||
*/
|
||||
|
||||
/** @}
|
||||
*
|
||||
* @name Enumerating concrete syntax trees
|
||||
*
|
||||
* Because of the \c variants construct in GF, there may be several
|
||||
* possible concrete syntax trees that correspond to a given abstract
|
||||
* syntax tree. These can be enumerated with #pgf_lzr_concretize and
|
||||
* #pgf_cnc_trees_next.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/// A concrete syntax tree
|
||||
typedef GuVariant PgfCncTree;
|
||||
|
||||
/// An enumeration of #PgfCncTree trees.
|
||||
typedef GuEnum PgfCncTreeEnum;
|
||||
|
||||
/// Begin enumerating concrete syntax variants.
|
||||
PgfCncTreeEnum*
|
||||
pgf_lzr_concretize(PgfLzr* lzr, PgfExpr expr, GuPool* pool);
|
||||
|
||||
/** @}
|
||||
*
|
||||
* @name Linearizing concrete syntax trees
|
||||
*
|
||||
* An individual concrete syntax tree has several different
|
||||
* linearizations, corresponding to the various fields and cases of
|
||||
* corresponding GF values. The number of these linearizations, called
|
||||
* the \e dimension of the tree, can be retrieved with
|
||||
* #pgf_cnc_tree_dimension.
|
||||
*
|
||||
* A single linearization of a concrete syntax tree is performed by
|
||||
* #pgf_lzr_linearize. The linearization is realized as a sequence of
|
||||
* events that are notified by calling the functions of a #PgfLinFuncs
|
||||
* structure that the client provides.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/// Callback functions for linearization.
|
||||
typedef struct PgfLinFuncs PgfLinFuncs;
|
||||
|
||||
struct PgfLinFuncs
|
||||
{
|
||||
/// Output tokens
|
||||
void (*symbol_tokens)(PgfLinFuncs** self, PgfTokens toks);
|
||||
|
||||
void (*symbol_expr)(PgfLinFuncs** self,
|
||||
int argno, PgfExpr expr, int lin_idx);
|
||||
|
||||
/// Begin application
|
||||
void (*expr_apply)(PgfLinFuncs** self, PgfCId cid, int n_args);
|
||||
|
||||
/// Output literal
|
||||
void (*expr_literal)(PgfLinFuncs** self, PgfLiteral lit);
|
||||
|
||||
void (*abort)(PgfLinFuncs** self);
|
||||
void (*finish)(PgfLinFuncs** self);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Linearize a concrete syntax tree.
|
||||
void
|
||||
pgf_lzr_linearize(PgfLzr* lzr, PgfCncTree ctree, size_t lin_idx,
|
||||
PgfLinFuncs** fnsp);
|
||||
|
||||
|
||||
/// Linearize a concrete syntax tree as space-separated tokens.
|
||||
void
|
||||
pgf_lzr_linearize_simple(PgfLzr* lzr, PgfCncTree ctree,
|
||||
size_t lin_idx, GuWriter* wtr, GuExn* err);
|
||||
|
||||
|
||||
/// Return the dimension of a concrete syntax tree.
|
||||
int
|
||||
pgf_cnc_tree_dimension(PgfCncTree ctree);
|
||||
/**<
|
||||
* @param ctree A concrete syntax tree.
|
||||
*
|
||||
* @return The dimension of the tree, i.e. the number of different
|
||||
* linearizations the tree has.
|
||||
*/
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
extern GuTypeTable
|
||||
pgf_linearize_dump_table;
|
||||
|
||||
@@ -1,396 +0,0 @@
|
||||
#include "../pgf.h"
|
||||
#include "data.h"
|
||||
#include "panic.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static int readTag(FILE *f) {
|
||||
return getc(f);
|
||||
}
|
||||
|
||||
static int readInt16(FILE *f) {
|
||||
int x = getc(f);
|
||||
int y = getc(f);
|
||||
return ((x << 8) | y);
|
||||
}
|
||||
|
||||
static int readInt(FILE *f) {
|
||||
unsigned int x = (unsigned int) getc(f);
|
||||
if (x <= 0x7f)
|
||||
return (int) x;
|
||||
else {
|
||||
unsigned int y = (unsigned int) readInt(f);
|
||||
return (int) ((y << 7) | (x & 0x7f)) ;
|
||||
}
|
||||
}
|
||||
|
||||
static double readFloat(FILE *f) {
|
||||
double d;
|
||||
fread(&d, sizeof(d), 1, f);
|
||||
return d;
|
||||
}
|
||||
|
||||
static String readString(FILE *f) {
|
||||
int len = readInt(f);
|
||||
String str = (String) malloc(sizeof(struct _CId)+len*sizeof(unsigned int));
|
||||
str->len = len;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
int c = fgetc(f);
|
||||
str->chars[i] = c;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static CId readCId(FILE *f) {
|
||||
int len = readInt(f);
|
||||
CId cid = (CId) malloc(sizeof(struct _CId)+len*sizeof(char));
|
||||
cid->len = len;
|
||||
fread(&cid->chars, sizeof(char), len, f);
|
||||
return cid;
|
||||
}
|
||||
|
||||
static CIdList readCIdList(FILE *f) {
|
||||
int i;
|
||||
int count = readInt(f);
|
||||
CIdList list = (CIdList) malloc(sizeof(struct _CIdList)+count*sizeof(CId));
|
||||
|
||||
list->count = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
list->names[i] = readCId(f);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static Literal readLiteral(FILE *f) {
|
||||
int tag = readTag(f);
|
||||
switch (tag) {
|
||||
case LIT_STR:
|
||||
{
|
||||
LiteralStr lit = (LiteralStr) malloc(sizeof(struct _LiteralStr));
|
||||
lit->_.tag = tag;
|
||||
lit->val = readString(f);
|
||||
return ((Literal) lit);
|
||||
}
|
||||
case LIT_INT:
|
||||
{
|
||||
LiteralInt lit = (LiteralInt) malloc(sizeof(struct _LiteralInt));
|
||||
lit->_.tag = tag;
|
||||
lit->val = readInt(f);
|
||||
return ((Literal) lit);
|
||||
}
|
||||
case LIT_FLOAT:
|
||||
{
|
||||
LiteralFloat lit = (LiteralFloat) malloc(sizeof(struct _LiteralFloat));
|
||||
lit->_.tag = tag;
|
||||
lit->val = readFloat(f);
|
||||
return ((Literal) lit);
|
||||
}
|
||||
default:
|
||||
__pgf_panic("Unknown literal tag");
|
||||
}
|
||||
}
|
||||
|
||||
static Flags readFlags(FILE *f) {
|
||||
int i;
|
||||
int count = readInt(f);
|
||||
Flags flags = (Flags) malloc(sizeof(struct _Flags)+count*sizeof(struct _Flag));
|
||||
|
||||
flags->count = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
flags->values[i].name = readCId(f);
|
||||
flags->values[i].value = readLiteral(f);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static Context readContext(FILE *f);
|
||||
static Type readType(FILE *f);
|
||||
|
||||
static Expr readExpr(FILE *f) {
|
||||
int tag = readTag(f);
|
||||
|
||||
switch (tag) {
|
||||
case TAG_ABS:
|
||||
{
|
||||
ExprAbs e = (ExprAbs) malloc(sizeof(struct _ExprAbs));
|
||||
e->_.tag = tag;
|
||||
e->bt = readTag(f);
|
||||
e->var = readCId(f);
|
||||
e->body = readExpr(f);
|
||||
return ((Expr) e);
|
||||
}
|
||||
case TAG_APP:
|
||||
{
|
||||
ExprApp e = (ExprApp) malloc(sizeof(struct _ExprApp));
|
||||
e->_.tag = tag;
|
||||
e->left = readExpr(f);
|
||||
e->right = readExpr(f);
|
||||
return ((Expr) e);
|
||||
}
|
||||
case TAG_LIT:
|
||||
{
|
||||
ExprLit e = (ExprLit) malloc(sizeof(struct _ExprLit));
|
||||
e->_.tag = tag;
|
||||
e->lit = readLiteral(f);
|
||||
return ((Expr) e);
|
||||
}
|
||||
case TAG_MET:
|
||||
{
|
||||
ExprMeta e = (ExprMeta) malloc(sizeof(struct _ExprMeta));
|
||||
e->_.tag = tag;
|
||||
e->id = readInt(f);
|
||||
return ((Expr) e);
|
||||
}
|
||||
case TAG_FUN:
|
||||
{
|
||||
ExprFun e = (ExprFun) malloc(sizeof(struct _ExprFun));
|
||||
e->_.tag = tag;
|
||||
e->fun = readCId(f);
|
||||
return ((Expr) e);
|
||||
}
|
||||
case TAG_VAR:
|
||||
{
|
||||
ExprVar e = (ExprVar) malloc(sizeof(struct _ExprVar));
|
||||
e->_.tag = tag;
|
||||
e->index = readInt(f);
|
||||
return ((Expr) e);
|
||||
}
|
||||
case TAG_TYP:
|
||||
{
|
||||
ExprTyped e = (ExprTyped) malloc(sizeof(struct _ExprTyped));
|
||||
e->_.tag = tag;
|
||||
e->e = readExpr(f);
|
||||
e->ty = readType(f);
|
||||
return ((Expr) e);
|
||||
}
|
||||
case TAG_IMP:
|
||||
{
|
||||
ExprImplArg e = (ExprImplArg) malloc(sizeof(struct _ExprImplArg));
|
||||
e->_.tag = tag;
|
||||
e->e = readExpr(f);
|
||||
return ((Expr) e);
|
||||
}
|
||||
default:
|
||||
__pgf_panic("Unknown expression tag");
|
||||
}
|
||||
}
|
||||
|
||||
static Type readType(FILE *f) {
|
||||
Context hypos = readContext(f);
|
||||
CId cat = readCId(f);
|
||||
|
||||
int i;
|
||||
int count = readInt(f);
|
||||
Type ty = (Type) malloc(sizeof(struct _Type)+count*sizeof(Expr));
|
||||
|
||||
ty->hypos = hypos;
|
||||
ty->cat = cat;
|
||||
ty->nArgs = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
ty->args[i] = readExpr(f);
|
||||
}
|
||||
|
||||
return ty;
|
||||
}
|
||||
|
||||
static void readHypo(FILE *f, Hypo h) {
|
||||
h->bt = readTag(f);
|
||||
h->var = readCId(f);
|
||||
h->ty = readType(f);
|
||||
}
|
||||
|
||||
static Context readContext(FILE *f) {
|
||||
int i;
|
||||
int size = readInt(f);
|
||||
Context ctxt = (Context) malloc(sizeof(struct _Context)+size*sizeof(struct _Hypo));
|
||||
|
||||
ctxt->size = size;
|
||||
for (i = 0; i < size; i++) {
|
||||
readHypo(f, &ctxt->hypos[i]);
|
||||
}
|
||||
|
||||
return ctxt;
|
||||
}
|
||||
|
||||
static Patt readPatt(FILE *f) {
|
||||
int tag = readTag(f);
|
||||
|
||||
switch (tag) {
|
||||
case TAG_PAPP:
|
||||
{
|
||||
CId fun = readCId(f);
|
||||
|
||||
int i;
|
||||
int count = readInt(f);
|
||||
PattApp p = (PattApp) malloc(sizeof(struct _PattApp)+count*sizeof(Patt));
|
||||
|
||||
p->_.tag = tag;
|
||||
p->fun = fun;
|
||||
p->args.count = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
p->args.pats[i] = readPatt(f);
|
||||
}
|
||||
|
||||
return ((Patt) p);
|
||||
}
|
||||
case TAG_PVAR:
|
||||
{
|
||||
PattVar p = (PattVar) malloc(sizeof(struct _PattVar));
|
||||
p->_.tag = tag;
|
||||
p->var = readCId(f);
|
||||
return ((Patt) p);
|
||||
}
|
||||
case TAG_PAT:
|
||||
{
|
||||
PattAt p = (PattAt) malloc(sizeof(struct _PattAt));
|
||||
p->_.tag = tag;
|
||||
p->var = readCId(f);
|
||||
p->pat = readPatt(f);
|
||||
return ((Patt) p);
|
||||
}
|
||||
case TAG_PWILD:
|
||||
{
|
||||
PattWild p = (PattWild) malloc(sizeof(struct _PattWild));
|
||||
p->_.tag = tag;
|
||||
return ((Patt) p);
|
||||
}
|
||||
case TAG_PLIT:
|
||||
{
|
||||
PattLit p = (PattLit) malloc(sizeof(struct _PattLit));
|
||||
p->_.tag = tag;
|
||||
p->lit = readLiteral(f);
|
||||
return ((Patt) p);
|
||||
}
|
||||
case TAG_PIMP:
|
||||
{
|
||||
PattImplArg p = (PattImplArg) malloc(sizeof(struct _PattImplArg));
|
||||
p->_.tag = tag;
|
||||
p->pat = readPatt(f);
|
||||
return ((Patt) p);
|
||||
}
|
||||
case TAG_PTILDE:
|
||||
{
|
||||
PattTilde p = (PattTilde) malloc(sizeof(struct _PattTilde));
|
||||
p->_.tag = tag;
|
||||
p->e = readExpr(f);
|
||||
return ((Patt) p);
|
||||
}
|
||||
default:
|
||||
__pgf_panic("Unknown pattern tag");
|
||||
}
|
||||
}
|
||||
|
||||
static Patts readPatts(FILE *f) {
|
||||
int i;
|
||||
int count = readInt(f);
|
||||
Patts pats = (Patts) malloc(sizeof(struct _Patts)+count*sizeof(Patt));
|
||||
|
||||
pats->count = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
pats->pats[i] = readPatt(f);
|
||||
}
|
||||
|
||||
return pats;
|
||||
}
|
||||
|
||||
static Equations readEquations(FILE *f) {
|
||||
int i;
|
||||
int count = readInt(f);
|
||||
Equations equs = (Equations) malloc(sizeof(struct _Equations)+count*sizeof(struct _Equation));
|
||||
|
||||
equs->count = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
equs->equs[i].lhs = readPatts(f);
|
||||
equs->equs[i].rhs = readExpr(f);
|
||||
}
|
||||
|
||||
return equs;
|
||||
}
|
||||
|
||||
static void readAbsFun(FILE *f, AbsFun fun) {
|
||||
fun->name = readCId(f);
|
||||
fun->ty = readType(f);
|
||||
fun->arrity = readInt(f);
|
||||
if (readTag(f) != 0)
|
||||
fun->equs = readEquations(f);
|
||||
else
|
||||
fun->equs = NULL;
|
||||
}
|
||||
|
||||
static AbsFuns readAbsFuns(FILE *f) {
|
||||
int i;
|
||||
int count = readInt(f);
|
||||
AbsFuns funs = (AbsFuns) malloc(sizeof(struct _AbsFuns)+count*sizeof(struct _AbsFun));
|
||||
|
||||
funs->count = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
readAbsFun(f, &funs->lst[i]);
|
||||
}
|
||||
|
||||
return funs;
|
||||
}
|
||||
|
||||
static void readAbsCat(FILE *f, AbsCat cat) {
|
||||
cat->name = readCId(f);
|
||||
cat->hypos = readContext(f);
|
||||
cat->funs = readCIdList(f);
|
||||
}
|
||||
|
||||
static AbsCats readAbsCats(FILE *f) {
|
||||
int i;
|
||||
int count = readInt(f);
|
||||
AbsCats cats = (AbsCats) malloc(sizeof(struct _AbsCats)+count*sizeof(struct _AbsCat));
|
||||
|
||||
cats->count = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
readAbsCat(f, &cats->lst[i]);
|
||||
}
|
||||
|
||||
return cats;
|
||||
}
|
||||
|
||||
static void readAbstr(FILE *f, Abstract abstr) {
|
||||
abstr->name = readCId(f);
|
||||
abstr->flags = readFlags(f);
|
||||
abstr->funs = readAbsFuns(f);
|
||||
abstr->cats = readAbsCats(f);
|
||||
}
|
||||
|
||||
static void readConcr(FILE *f, Concrete concr) {
|
||||
concr->name = readCId(f);
|
||||
concr->flags = readFlags(f);
|
||||
}
|
||||
|
||||
PGF readPGF(char *filename) {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if (f == NULL)
|
||||
return NULL;
|
||||
|
||||
int maj_ver = readInt16(f);
|
||||
int min_ver = readInt16(f);
|
||||
|
||||
Flags flags = readFlags(f);
|
||||
|
||||
struct _Abstract abstr;
|
||||
readAbstr(f, &abstr);
|
||||
|
||||
int nConcr = readInt(f);
|
||||
PGF pgf = (PGF) malloc(sizeof(struct _PGF)+sizeof(Concrete)*nConcr);
|
||||
|
||||
pgf->flags = flags;
|
||||
pgf->abstract = abstr;
|
||||
pgf->nConcr = nConcr;
|
||||
|
||||
int i;
|
||||
// for (i = 0; i < nConcr; i++) {
|
||||
// readConcr(f, &pgf->concretes[i]);
|
||||
// }
|
||||
|
||||
fclose(f);
|
||||
return pgf;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#include "panic.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void __pgf_panic(char *msg) {
|
||||
printf("%s\n",msg);
|
||||
fflush(stdout);
|
||||
exit(1);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef PGF_PANIC_H
|
||||
#define PGF_PANIC_H
|
||||
|
||||
void __pgf_panic(char *msg);
|
||||
|
||||
#endif
|
||||
697
src/runtime/c/pgf/parser.c
Normal file
697
src/runtime/c/pgf/parser.c
Normal file
@@ -0,0 +1,697 @@
|
||||
#include <pgf/parser.h>
|
||||
#include <gu/choice.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/log.h>
|
||||
|
||||
typedef struct PgfItem PgfItem;
|
||||
|
||||
enum {
|
||||
PGF_FID_SYNTHETIC = -999
|
||||
};
|
||||
|
||||
typedef GuBuf PgfItemBuf;
|
||||
typedef GuList(PgfItemBuf*) PgfItemBufs;
|
||||
|
||||
|
||||
|
||||
// GuString -> PgfItemBuf*
|
||||
typedef GuMap PgfTransitions;
|
||||
|
||||
typedef GuBuf PgfCCatBuf;
|
||||
|
||||
struct PgfParser {
|
||||
PgfConcr* concr;
|
||||
};
|
||||
|
||||
struct PgfParse {
|
||||
PgfParser* parser;
|
||||
PgfTransitions* transitions;
|
||||
PgfCCatBuf* completed;
|
||||
};
|
||||
|
||||
typedef struct PgfParseResult PgfParseResult;
|
||||
|
||||
struct PgfParseResult {
|
||||
PgfCCatBuf* completed;
|
||||
GuChoice* choice;
|
||||
PgfExprEnum en;
|
||||
};
|
||||
|
||||
typedef struct PgfItemBase PgfItemBase;
|
||||
|
||||
struct PgfItemBase {
|
||||
PgfItemBuf* conts;
|
||||
PgfCCat* ccat;
|
||||
PgfProduction prod;
|
||||
unsigned short lin_idx;
|
||||
};
|
||||
|
||||
struct PgfItem {
|
||||
PgfItemBase* base;
|
||||
PgfPArgs args;
|
||||
PgfSymbol curr_sym;
|
||||
uint16_t seq_idx;
|
||||
uint8_t tok_idx;
|
||||
uint8_t alt;
|
||||
};
|
||||
|
||||
typedef GuMap PgfContsMap;
|
||||
|
||||
|
||||
static GU_DEFINE_TYPE(PgfItemBuf, abstract, _);
|
||||
static GU_DEFINE_TYPE(PgfItemBufs, abstract, _);
|
||||
static GU_DEFINE_TYPE(PgfContsMap, GuMap,
|
||||
gu_type(PgfCCat), NULL,
|
||||
gu_ptr_type(PgfItemBufs), &gu_null_struct);
|
||||
|
||||
static GU_DEFINE_TYPE(PgfGenCatMap, GuMap,
|
||||
gu_type(PgfItemBuf), NULL,
|
||||
gu_ptr_type(PgfCCat), &gu_null_struct);
|
||||
|
||||
static GU_DEFINE_TYPE(PgfTransitions, GuStringMap,
|
||||
gu_ptr_type(PgfItemBuf), &gu_null_struct);
|
||||
|
||||
typedef GuMap PgfGenCatMap;
|
||||
|
||||
typedef struct PgfParsing PgfParsing;
|
||||
|
||||
struct PgfParsing {
|
||||
PgfParse* parse;
|
||||
GuPool* pool;
|
||||
PgfContsMap* conts_map;
|
||||
PgfGenCatMap* generated_cats;
|
||||
};
|
||||
|
||||
static void
|
||||
pgf_parsing_add_transition(PgfParsing* parsing, PgfToken tok, PgfItem* item)
|
||||
{
|
||||
gu_debug("%s -> %p", tok, item);
|
||||
PgfTransitions* tmap = parsing->parse->transitions;
|
||||
PgfItemBuf* items = gu_map_get(tmap, &tok, PgfItemBuf*);
|
||||
if (!items) {
|
||||
items = gu_new_buf(PgfItem*, parsing->pool);
|
||||
gu_map_put(tmap, &tok, PgfItemBuf*, items);
|
||||
}
|
||||
gu_buf_push(items, PgfItem*, item);
|
||||
}
|
||||
|
||||
static PgfItemBufs*
|
||||
pgf_parsing_get_contss(PgfParsing* parsing, PgfCCat* cat)
|
||||
{
|
||||
PgfItemBufs* contss = gu_map_get(parsing->conts_map, cat, PgfItemBufs*);
|
||||
if (!contss) {
|
||||
size_t n_lins = cat->cnccat->n_lins;
|
||||
contss = gu_new_list(PgfItemBufs, parsing->pool, n_lins);
|
||||
for (size_t i = 0; i < n_lins; i++) {
|
||||
gu_list_index(contss, i) = NULL;
|
||||
}
|
||||
gu_map_put(parsing->conts_map, cat, PgfItemBufs*, contss);
|
||||
}
|
||||
return contss;
|
||||
}
|
||||
|
||||
|
||||
static PgfItemBuf*
|
||||
pgf_parsing_get_conts(PgfParsing* parsing, PgfCCat* cat, size_t lin_idx)
|
||||
{
|
||||
gu_require(lin_idx < cat->cnccat->n_lins);
|
||||
PgfItemBufs* contss = pgf_parsing_get_contss(parsing, cat);
|
||||
PgfItemBuf* conts = gu_list_index(contss, lin_idx);
|
||||
if (!conts) {
|
||||
conts = gu_new_buf(PgfItem*, parsing->pool);
|
||||
gu_list_index(contss, lin_idx) = conts;
|
||||
}
|
||||
return conts;
|
||||
}
|
||||
|
||||
static PgfCCat*
|
||||
pgf_parsing_create_completed(PgfParsing* parsing, PgfItemBuf* conts,
|
||||
PgfCncCat* cnccat)
|
||||
{
|
||||
PgfCCat* cat = gu_new(PgfCCat, parsing->pool);
|
||||
cat->cnccat = cnccat;
|
||||
cat->fid = PGF_FID_SYNTHETIC;
|
||||
cat->prods = gu_buf_seq(gu_new_buf(PgfProduction, parsing->pool));
|
||||
gu_map_put(parsing->generated_cats, conts, PgfCCat*, cat);
|
||||
return cat;
|
||||
}
|
||||
|
||||
static PgfCCat*
|
||||
pgf_parsing_get_completed(PgfParsing* parsing, PgfItemBuf* conts)
|
||||
{
|
||||
return gu_map_get(parsing->generated_cats, conts, PgfCCat*);
|
||||
}
|
||||
|
||||
static PgfSymbol
|
||||
pgf_item_base_symbol(PgfItemBase* ibase, size_t seq_idx, GuPool* pool)
|
||||
{
|
||||
GuVariantInfo i = gu_variant_open(ibase->prod);
|
||||
switch (i.tag) {
|
||||
case PGF_PRODUCTION_APPLY: {
|
||||
PgfProductionApply* papp = i.data;
|
||||
PgfCncFun* fun = papp->fun;
|
||||
gu_assert(ibase->lin_idx < fun->n_lins);
|
||||
PgfSequence seq = fun->lins[ibase->lin_idx];
|
||||
gu_assert(seq_idx <= gu_seq_length(seq));
|
||||
if (seq_idx == gu_seq_length(seq)) {
|
||||
return gu_null_variant;
|
||||
} else {
|
||||
return gu_seq_get(seq, PgfSymbol, seq_idx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PGF_PRODUCTION_COERCE: {
|
||||
gu_assert(seq_idx <= 1);
|
||||
if (seq_idx == 1) {
|
||||
return gu_null_variant;
|
||||
} else {
|
||||
return gu_new_variant_i(pool, PGF_SYMBOL_CAT,
|
||||
PgfSymbolCat,
|
||||
.d = 0, .r = ibase->lin_idx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
return gu_null_variant;
|
||||
}
|
||||
|
||||
static PgfItem*
|
||||
pgf_new_item(PgfItemBase* base, GuPool* pool)
|
||||
{
|
||||
PgfItem* item = gu_new(PgfItem, pool);
|
||||
GuVariantInfo pi = gu_variant_open(base->prod);
|
||||
switch (pi.tag) {
|
||||
case PGF_PRODUCTION_APPLY: {
|
||||
PgfProductionApply* papp = pi.data;
|
||||
item->args = papp->args;
|
||||
break;
|
||||
}
|
||||
case PGF_PRODUCTION_COERCE: {
|
||||
PgfProductionCoerce* pcoerce = pi.data;
|
||||
item->args = gu_new_seq(PgfPArg, 1, pool);
|
||||
PgfPArg* parg = gu_seq_index(item->args, PgfPArg, 0);
|
||||
parg->hypos = NULL;
|
||||
parg->ccat = pcoerce->coerce;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
item->base = base;
|
||||
item->curr_sym = pgf_item_base_symbol(item->base, 0, pool);
|
||||
item->seq_idx = 0;
|
||||
item->tok_idx = 0;
|
||||
item->alt = 0;
|
||||
return item;
|
||||
}
|
||||
|
||||
static PgfItem*
|
||||
pgf_item_copy(PgfItem* item, GuPool* pool)
|
||||
{
|
||||
PgfItem* copy = gu_new(PgfItem, pool);
|
||||
memcpy(copy, item, sizeof(PgfItem));
|
||||
return copy;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_item_advance(PgfItem* item, GuPool* pool)
|
||||
{
|
||||
item->seq_idx++;
|
||||
item->curr_sym = pgf_item_base_symbol(item->base, item->seq_idx, pool);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_parsing_item(PgfParsing* parsing, PgfItem* item);
|
||||
|
||||
static void
|
||||
pgf_parsing_combine(PgfParsing* parsing, PgfItem* cont, PgfCCat* cat)
|
||||
{
|
||||
if (cont == NULL) {
|
||||
gu_buf_push(parsing->parse->completed, PgfCCat*, cat);
|
||||
return;
|
||||
}
|
||||
PgfItem* item = pgf_item_copy(cont, parsing->pool);
|
||||
size_t nargs = gu_seq_length(cont->args);
|
||||
item->args = gu_new_seq(PgfPArg, nargs, parsing->pool);
|
||||
memcpy(gu_seq_data(item->args), gu_seq_data(cont->args),
|
||||
nargs * sizeof(PgfPArg));
|
||||
gu_assert(gu_variant_tag(item->curr_sym) == PGF_SYMBOL_CAT);
|
||||
PgfSymbolCat* pcat = gu_variant_data(cont->curr_sym);
|
||||
gu_seq_set(item->args, PgfPArg, pcat->d,
|
||||
((PgfPArg) { .hypos = NULL, .ccat = cat }));
|
||||
pgf_item_advance(item, parsing->pool);
|
||||
pgf_parsing_item(parsing, item);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_parsing_production(PgfParsing* parsing, PgfCCat* cat, size_t lin_idx,
|
||||
PgfProduction prod, PgfItemBuf* conts)
|
||||
{
|
||||
PgfItemBase* base = gu_new(PgfItemBase, parsing->pool);
|
||||
base->ccat = cat;
|
||||
base->lin_idx = lin_idx;
|
||||
base->prod = prod;
|
||||
base->conts = conts;
|
||||
PgfItem* item = pgf_new_item(base, parsing->pool);
|
||||
pgf_parsing_item(parsing, item);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_parsing_complete(PgfParsing* parsing, PgfItem* item)
|
||||
{
|
||||
GuVariantInfo i = gu_variant_open(item->base->prod);
|
||||
PgfProduction prod = gu_null_variant;
|
||||
switch (i.tag) {
|
||||
case PGF_PRODUCTION_APPLY: {
|
||||
PgfProductionApply* papp = i.data;
|
||||
PgfProductionApply* new_papp =
|
||||
gu_new_variant(PGF_PRODUCTION_APPLY,
|
||||
PgfProductionApply,
|
||||
&prod, parsing->pool);
|
||||
new_papp->fun = papp->fun;
|
||||
new_papp->args = item->args;
|
||||
break;
|
||||
}
|
||||
case PGF_PRODUCTION_COERCE: {
|
||||
PgfProductionCoerce* new_pcoerce =
|
||||
gu_new_variant(PGF_PRODUCTION_COERCE,
|
||||
PgfProductionCoerce,
|
||||
&prod, parsing->pool);
|
||||
PgfPArg* parg = gu_seq_index(item->args, PgfPArg, 0);
|
||||
gu_assert(!parg->hypos || !parg->hypos->len);
|
||||
new_pcoerce->coerce = parg->ccat;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
PgfItemBuf* conts = item->base->conts;
|
||||
PgfCCat* cat = pgf_parsing_get_completed(parsing, conts);
|
||||
if (cat != NULL) {
|
||||
// The category has already been created. If it has also been
|
||||
// predicted already, then process a new item for this production.
|
||||
PgfItemBufs* contss = pgf_parsing_get_contss(parsing, cat);
|
||||
size_t n_contss = gu_list_length(contss);
|
||||
for (size_t i = 0; i < n_contss; i++) {
|
||||
PgfItemBuf* conts2 = gu_list_index(contss, i);
|
||||
/* If there are continuations for
|
||||
* linearization index i, then (cat, i) has
|
||||
* already been predicted. Add the new
|
||||
* production immediately to the agenda,
|
||||
* i.e. process it. */
|
||||
if (conts2) {
|
||||
pgf_parsing_production(parsing, cat, i,
|
||||
prod, conts2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cat = pgf_parsing_create_completed(parsing, conts,
|
||||
item->base->ccat->cnccat);
|
||||
size_t n_conts = gu_buf_length(conts);
|
||||
for (size_t i = 0; i < n_conts; i++) {
|
||||
PgfItem* cont = gu_buf_get(conts, PgfItem*, i);
|
||||
pgf_parsing_combine(parsing, cont, cat);
|
||||
}
|
||||
}
|
||||
GuBuf* prodbuf = gu_seq_buf(cat->prods);
|
||||
gu_buf_push(prodbuf, PgfProduction, prod);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pgf_parsing_predict(PgfParsing* parsing, PgfItem* item,
|
||||
PgfCCat* cat, size_t lin_idx)
|
||||
{
|
||||
gu_enter("-> cat: %d", cat->fid);
|
||||
if (gu_seq_is_null(cat->prods)) {
|
||||
// Empty category
|
||||
return;
|
||||
}
|
||||
PgfItemBuf* conts = pgf_parsing_get_conts(parsing, cat, lin_idx);
|
||||
gu_buf_push(conts, PgfItem*, item);
|
||||
if (gu_buf_length(conts) == 1) {
|
||||
/* First time we encounter this linearization
|
||||
* of this category at the current position,
|
||||
* so predict it. */
|
||||
PgfProductionSeq prods = cat->prods;
|
||||
size_t n_prods = gu_seq_length(prods);
|
||||
for (size_t i = 0; i < n_prods; i++) {
|
||||
PgfProduction prod =
|
||||
gu_seq_get(prods, PgfProduction, i);
|
||||
pgf_parsing_production(parsing, cat, lin_idx,
|
||||
prod, conts);
|
||||
}
|
||||
} else {
|
||||
/* If it has already been completed, combine. */
|
||||
PgfCCat* completed =
|
||||
pgf_parsing_get_completed(parsing, conts);
|
||||
if (completed) {
|
||||
pgf_parsing_combine(parsing, item, completed);
|
||||
}
|
||||
}
|
||||
gu_exit(NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_parsing_symbol(PgfParsing* parsing, PgfItem* item, PgfSymbol sym) {
|
||||
switch (gu_variant_tag(sym)) {
|
||||
case PGF_SYMBOL_CAT: {
|
||||
PgfSymbolCat* scat = gu_variant_data(sym);
|
||||
PgfPArg* parg = gu_seq_index(item->args, PgfPArg, scat->d);
|
||||
gu_assert(!parg->hypos || !parg->hypos->len);
|
||||
pgf_parsing_predict(parsing, item, parg->ccat, scat->r);
|
||||
break;
|
||||
}
|
||||
case PGF_SYMBOL_KS: {
|
||||
PgfSymbolKS* sks = gu_variant_data(sym);
|
||||
gu_assert(item->tok_idx < gu_seq_length(sks->tokens));
|
||||
PgfToken tok =
|
||||
gu_seq_get(sks->tokens, PgfToken, item->tok_idx);
|
||||
pgf_parsing_add_transition(parsing, tok, item);
|
||||
break;
|
||||
}
|
||||
case PGF_SYMBOL_KP: {
|
||||
PgfSymbolKP* skp = gu_variant_data(sym);
|
||||
size_t idx = item->tok_idx;
|
||||
uint8_t alt = item->alt;
|
||||
gu_assert(idx < gu_seq_length(skp->default_form));
|
||||
if (idx == 0) {
|
||||
PgfToken tok = gu_seq_get(skp->default_form, PgfToken, 0);
|
||||
pgf_parsing_add_transition(parsing, tok, item);
|
||||
for (size_t i = 0; i < skp->n_forms; i++) {
|
||||
PgfTokens toks = skp->forms[i].form;
|
||||
PgfTokens toks2 = skp->default_form;
|
||||
// XXX: do nubbing properly
|
||||
bool skip = pgf_tokens_equal(toks, toks2);
|
||||
for (size_t j = 0; j < i; j++) {
|
||||
PgfTokens toks2 = skp->forms[j].form;
|
||||
skip |= pgf_tokens_equal(toks, toks2);
|
||||
}
|
||||
if (skip) {
|
||||
continue;
|
||||
}
|
||||
PgfToken tok = gu_seq_get(toks, PgfToken, 0);
|
||||
pgf_parsing_add_transition(parsing, tok, item);
|
||||
}
|
||||
} else if (alt == 0) {
|
||||
PgfToken tok =
|
||||
gu_seq_get(skp->default_form, PgfToken, idx);
|
||||
pgf_parsing_add_transition(parsing, tok, item);
|
||||
} else {
|
||||
gu_assert(alt <= skp->n_forms);
|
||||
PgfToken tok = gu_seq_get(skp->forms[alt - 1].form,
|
||||
PgfToken, idx);
|
||||
pgf_parsing_add_transition(parsing, tok, item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PGF_SYMBOL_LIT:
|
||||
// XXX TODO proper support
|
||||
break;
|
||||
case PGF_SYMBOL_VAR:
|
||||
// XXX TODO proper support
|
||||
break;
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_parsing_item(PgfParsing* parsing, PgfItem* item)
|
||||
{
|
||||
GuVariantInfo i = gu_variant_open(item->base->prod);
|
||||
switch (i.tag) {
|
||||
case PGF_PRODUCTION_APPLY: {
|
||||
PgfProductionApply* papp = i.data;
|
||||
PgfCncFun* fun = papp->fun;
|
||||
PgfSequence seq = fun->lins[item->base->lin_idx];
|
||||
if (item->seq_idx == gu_seq_length(seq)) {
|
||||
pgf_parsing_complete(parsing, item);
|
||||
} else {
|
||||
PgfSymbol sym =
|
||||
gu_seq_get(seq, PgfSymbol, item->seq_idx);
|
||||
pgf_parsing_symbol(parsing, item, sym);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PGF_PRODUCTION_COERCE: {
|
||||
PgfProductionCoerce* pcoerce = i.data;
|
||||
switch (item->seq_idx) {
|
||||
case 0:
|
||||
pgf_parsing_predict(parsing, item,
|
||||
pcoerce->coerce,
|
||||
item->base->lin_idx);
|
||||
break;
|
||||
case 1:
|
||||
pgf_parsing_complete(parsing, item);
|
||||
break;
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
pgf_parsing_scan_toks(PgfParsing* parsing, PgfItem* old_item,
|
||||
PgfToken tok, int alt, PgfTokens toks)
|
||||
{
|
||||
gu_assert(old_item->tok_idx < gu_seq_length(toks));
|
||||
if (!gu_string_eq(gu_seq_get(toks, PgfToken, old_item->tok_idx),
|
||||
tok)) {
|
||||
return false;
|
||||
}
|
||||
PgfItem* item = pgf_item_copy(old_item, parsing->pool);
|
||||
item->tok_idx++;
|
||||
item->alt = alt;
|
||||
if (item->tok_idx == gu_seq_length(toks)) {
|
||||
item->tok_idx = 0;
|
||||
pgf_item_advance(item, parsing->pool);
|
||||
}
|
||||
pgf_parsing_item(parsing, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_parsing_scan(PgfParsing* parsing, PgfItem* item, PgfToken tok)
|
||||
{
|
||||
bool succ = false;
|
||||
GuVariantInfo i = gu_variant_open(item->curr_sym);
|
||||
switch (i.tag) {
|
||||
case PGF_SYMBOL_KS: {
|
||||
PgfSymbolKS* ks = i.data;
|
||||
succ = pgf_parsing_scan_toks(parsing, item, tok, 0,
|
||||
ks->tokens);
|
||||
break;
|
||||
}
|
||||
case PGF_SYMBOL_KP: {
|
||||
PgfSymbolKP* kp = i.data;
|
||||
size_t alt = item->alt;
|
||||
if (item->tok_idx == 0) {
|
||||
succ = pgf_parsing_scan_toks(parsing, item, tok, 0,
|
||||
kp->default_form);
|
||||
for (size_t i = 0; i < kp->n_forms; i++) {
|
||||
// XXX: do nubbing properly
|
||||
PgfTokens toks = kp->forms[i].form;
|
||||
PgfTokens toks2 = kp->default_form;
|
||||
bool skip = pgf_tokens_equal(toks, toks2);
|
||||
for (size_t j = 0; j < i; j++) {
|
||||
PgfTokens toks2 = kp->forms[j].form;
|
||||
skip |= pgf_tokens_equal(toks, toks2);
|
||||
}
|
||||
if (!skip) {
|
||||
succ |= pgf_parsing_scan_toks(
|
||||
parsing, item, tok, i + 1,
|
||||
kp->forms[i].form);
|
||||
}
|
||||
}
|
||||
} else if (alt == 0) {
|
||||
succ = pgf_parsing_scan_toks(parsing, item, tok, 0,
|
||||
kp->default_form);
|
||||
} else {
|
||||
gu_assert(alt <= kp->n_forms);
|
||||
succ = pgf_parsing_scan_toks(parsing, item, tok,
|
||||
alt, kp->forms[alt - 1].form);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
gu_assert(succ);
|
||||
}
|
||||
|
||||
|
||||
static PgfParsing*
|
||||
pgf_new_parsing(PgfParse* parse, GuPool* parse_pool, GuPool* out_pool)
|
||||
{
|
||||
PgfParsing* parsing = gu_new(PgfParsing, out_pool);
|
||||
parsing->parse = parse;
|
||||
parsing->generated_cats = gu_map_type_new(PgfGenCatMap, out_pool);
|
||||
parsing->conts_map = gu_map_type_new(PgfContsMap, out_pool);
|
||||
parsing->pool = parse_pool;
|
||||
return parsing;
|
||||
}
|
||||
|
||||
static PgfParse*
|
||||
pgf_new_parse(PgfParser* parser, GuPool* pool)
|
||||
{
|
||||
PgfParse* parse = gu_new(PgfParse, pool);
|
||||
parse->parser = parser;
|
||||
parse->transitions = gu_map_type_new(PgfTransitions, pool);
|
||||
parse->completed = gu_new_buf(PgfCCat*, pool);
|
||||
return parse;
|
||||
}
|
||||
|
||||
PgfParse*
|
||||
pgf_parse_token(PgfParse* parse, PgfToken tok, GuPool* pool)
|
||||
{
|
||||
PgfItemBuf* agenda =
|
||||
gu_map_get(parse->transitions, &tok, PgfItemBuf*);
|
||||
if (!agenda) {
|
||||
return NULL;
|
||||
}
|
||||
PgfParse* next_parse = pgf_new_parse(parse->parser, pool);
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
PgfParsing* parsing = pgf_new_parsing(next_parse, pool, tmp_pool);
|
||||
size_t n_items = gu_buf_length(agenda);
|
||||
for (size_t i = 0; i < n_items; i++) {
|
||||
PgfItem* item = gu_buf_get(agenda, PgfItem*, i);
|
||||
pgf_parsing_scan(parsing, item, tok);
|
||||
}
|
||||
gu_pool_free(tmp_pool);
|
||||
return next_parse;
|
||||
}
|
||||
|
||||
static PgfExpr
|
||||
pgf_cat_to_expr(PgfCCat* cat, GuChoice* choice, GuPool* pool);
|
||||
|
||||
static PgfExpr
|
||||
pgf_production_to_expr(PgfProduction prod, GuChoice* choice, GuPool* pool)
|
||||
{
|
||||
GuVariantInfo pi = gu_variant_open(prod);
|
||||
switch (pi.tag) {
|
||||
case PGF_PRODUCTION_APPLY: {
|
||||
PgfProductionApply* papp = pi.data;
|
||||
PgfExpr expr = gu_new_variant_i(pool, PGF_EXPR_FUN,
|
||||
PgfExprFun,
|
||||
.fun = papp->fun->fun);
|
||||
size_t n_args = gu_seq_length(papp->args);
|
||||
for (size_t i = 0; i < n_args; i++) {
|
||||
PgfPArg* parg = gu_seq_index(papp->args, PgfPArg, i);
|
||||
gu_assert(!parg->hypos || !parg->hypos->len);
|
||||
PgfExpr earg = pgf_cat_to_expr(parg->ccat, choice, pool);
|
||||
expr = gu_new_variant_i(pool, PGF_EXPR_APP,
|
||||
PgfExprApp,
|
||||
.fun = expr, .arg = earg);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
case PGF_PRODUCTION_COERCE: {
|
||||
PgfProductionCoerce* pcoerce = pi.data;
|
||||
return pgf_cat_to_expr(pcoerce->coerce, choice, pool);
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
return gu_null_variant;
|
||||
}
|
||||
|
||||
|
||||
static PgfExpr
|
||||
pgf_cat_to_expr(PgfCCat* cat, GuChoice* choice, GuPool* pool)
|
||||
{
|
||||
if (cat->fid != PGF_FID_SYNTHETIC) {
|
||||
// XXX: What should the PgfMetaId be?
|
||||
return gu_new_variant_i(pool, PGF_EXPR_META,
|
||||
PgfExprMeta,
|
||||
.id = 0);
|
||||
}
|
||||
size_t n_prods = gu_seq_length(cat->prods);
|
||||
int i = gu_choice_next(choice, n_prods);
|
||||
if (i == -1) {
|
||||
return gu_null_variant;
|
||||
}
|
||||
PgfProduction prod = gu_seq_get(cat->prods, PgfProduction, i);
|
||||
return pgf_production_to_expr(prod, choice, pool);
|
||||
}
|
||||
|
||||
|
||||
static PgfExpr
|
||||
pgf_parse_result_next(PgfParseResult* pr, GuPool* pool)
|
||||
{
|
||||
if (pr->choice == NULL) {
|
||||
return gu_null_variant;
|
||||
}
|
||||
size_t n_results = gu_buf_length(pr->completed);
|
||||
GuChoiceMark mark = gu_choice_mark(pr->choice);
|
||||
int i = gu_choice_next(pr->choice, n_results);
|
||||
if (i == -1) {
|
||||
return gu_null_variant;
|
||||
}
|
||||
PgfCCat* cat = gu_buf_get(pr->completed, PgfCCat*, i);
|
||||
PgfExpr ret = pgf_cat_to_expr(cat, pr->choice, pool);
|
||||
gu_choice_reset(pr->choice, mark);
|
||||
if (!gu_choice_advance(pr->choice)) {
|
||||
pr->choice = NULL;
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_parse_result_enum_next(GuEnum* self, void* to, GuPool* pool)
|
||||
{
|
||||
PgfParseResult* pr = gu_container(self, PgfParseResult, en);
|
||||
*(PgfExpr*)to = pgf_parse_result_next(pr, pool);
|
||||
}
|
||||
|
||||
PgfExprEnum*
|
||||
pgf_parse_result(PgfParse* parse, GuPool* pool)
|
||||
{
|
||||
return &gu_new_i(pool, PgfParseResult,
|
||||
.completed = parse->completed,
|
||||
.choice = gu_new_choice(pool),
|
||||
.en.next = pgf_parse_result_enum_next)->en;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: s/CId/Cat, add the cid to Cat, make Cat the key to CncCat
|
||||
PgfParse*
|
||||
pgf_parser_parse(PgfParser* parser, PgfCId cat, size_t lin_idx, GuPool* pool)
|
||||
{
|
||||
PgfParse* parse = pgf_new_parse(parser, pool);
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
PgfParsing* parsing = pgf_new_parsing(parse, pool, tmp_pool);
|
||||
PgfCncCat* cnccat =
|
||||
gu_map_get(parser->concr->cnccats, &cat, PgfCncCat*);
|
||||
if (!cnccat) {
|
||||
// error ...
|
||||
gu_impossible();
|
||||
}
|
||||
gu_assert(lin_idx < cnccat->n_lins);
|
||||
size_t n_ccats = gu_list_length(cnccat->cats);
|
||||
for (size_t i = 0; i < n_ccats; i++) {
|
||||
PgfCCat* ccat = gu_list_index(cnccat->cats, i);
|
||||
if (ccat != NULL) {
|
||||
pgf_parsing_predict(parsing, NULL, ccat, lin_idx);
|
||||
}
|
||||
}
|
||||
gu_pool_free(tmp_pool);
|
||||
return parse;
|
||||
}
|
||||
|
||||
PgfParser*
|
||||
pgf_new_parser(PgfConcr* concr, GuPool* pool)
|
||||
{
|
||||
gu_require(concr != NULL);
|
||||
PgfParser* parser = gu_new(PgfParser, pool);
|
||||
parser->concr = concr;
|
||||
return parser;
|
||||
}
|
||||
127
src/runtime/c/pgf/parser.h
Normal file
127
src/runtime/c/pgf/parser.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#ifndef PGF_PARSER_H_
|
||||
#define PGF_PARSER_H_
|
||||
|
||||
#include <gu/enum.h>
|
||||
#include <pgf/data.h>
|
||||
#include <pgf/expr.h>
|
||||
|
||||
/// Parsing
|
||||
/** @file
|
||||
*
|
||||
* @todo Querying the parser for expected continuations
|
||||
*
|
||||
* @todo Literals and custom categories
|
||||
*
|
||||
* @todo HOAS, dependent types...
|
||||
*/
|
||||
|
||||
typedef struct PgfParse PgfParse;
|
||||
|
||||
/** @name Creating a new parser
|
||||
*
|
||||
* A #PgfParser object can parse sentences of a single concrete category into
|
||||
* abstract syntax trees (#PgfExpr). The parser is created with
|
||||
* #pgf_new_parser.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/// A parser for a single concrete category
|
||||
typedef struct PgfParser PgfParser;
|
||||
|
||||
|
||||
/// Create a new parser
|
||||
PgfParser*
|
||||
pgf_new_parser(PgfConcr* concr, GuPool* pool);
|
||||
/**<
|
||||
* @param concr The concrete category whose sentences are to be parsed
|
||||
*
|
||||
* @pool
|
||||
*
|
||||
* @return A newly created parser for the concrete category \p concr
|
||||
*/
|
||||
|
||||
/** @}
|
||||
*
|
||||
* @name Parsing a sentence
|
||||
*
|
||||
* The progress of parsing is controlled by the client code. Firstly, the
|
||||
* parsing of a sentence is initiated with #pgf_parser_parse. This returns an
|
||||
* initial #PgfParse object, which represents the state of the parsing. A new
|
||||
* parse state is obtained by feeding a token with #pgf_parse_token. The old
|
||||
* parse state is unaffected by this, so backtracking - and even branching -
|
||||
* can be accomplished by retaining the earlier #PgfParse objects.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/// Begin parsing
|
||||
PgfParse*
|
||||
pgf_parser_parse(PgfParser* parser, PgfCId cat, size_t lin_idx, GuPool* pool);
|
||||
/**<
|
||||
* @param parser The parser to use
|
||||
*
|
||||
* @param cat The identifier of the abstract category to parse
|
||||
*
|
||||
* @param lin_idx The index of the field of the concrete category to parse
|
||||
*
|
||||
* @pool
|
||||
*
|
||||
* @return An initial parsing state.
|
||||
*/
|
||||
|
||||
|
||||
/// Feed a token to the parser
|
||||
PgfParse*
|
||||
pgf_parse_token(PgfParse* parse, PgfToken tok, GuPool* pool);
|
||||
/**<
|
||||
* @param parse The current parse state
|
||||
*
|
||||
* @param tok The token to feed
|
||||
*
|
||||
* @pool
|
||||
*
|
||||
* @return A new parse state obtained by feeding \p tok as an input to \p
|
||||
* parse, or \c NULL if the token was unexpected.
|
||||
*
|
||||
* @note The new parse state partially depends on the old one, so it doesn't
|
||||
* make sense to use a \p pool argument with a longer lifetime than that of
|
||||
* the pool used to create \parse.
|
||||
*/
|
||||
|
||||
|
||||
/** @}
|
||||
* @name Retrieving abstract syntax trees
|
||||
*
|
||||
* After the desired tokens have been fed to the parser, the resulting parse
|
||||
* state can be queried for completed results. The #pgf_parse_result function
|
||||
* returns an enumeration (#GuEnum) of possible abstract syntax trees whose
|
||||
* linearization is the sequence of tokens fed so far.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/// An enumeration of #PgfExpr elements.
|
||||
typedef GuEnum PgfExprEnum;
|
||||
|
||||
|
||||
/// Retrieve the current parses from the parse state.
|
||||
PgfExprEnum*
|
||||
pgf_parse_result(PgfParse* parse, GuPool* pool);
|
||||
/**<
|
||||
* @param parse A parse state
|
||||
*
|
||||
* @pool
|
||||
*
|
||||
* @return An enumeration of #PgfExpr elements representing the abstract
|
||||
* syntax trees that would linearize to the sequence of tokens fed to produce
|
||||
* \p parse. The enumeration may yield zero, one or more abstract syntax
|
||||
* trees, depending on whether the parse was unsuccesful, unambiguously
|
||||
* succesful, or ambiguously successful.
|
||||
*/
|
||||
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif // PGF_PARSER_H_
|
||||
78
src/runtime/c/pgf/pgf.h
Normal file
78
src/runtime/c/pgf/pgf.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libpgf.
|
||||
*
|
||||
* Libpgf is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libpgf is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libpgf. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
*
|
||||
* The public libpgf API.
|
||||
*/
|
||||
|
||||
#ifndef PGF_H_
|
||||
#define PGF_H_
|
||||
|
||||
#include <gu/exn.h>
|
||||
#include <gu/mem.h>
|
||||
#include <gu/in.h>
|
||||
#include <gu/string.h>
|
||||
|
||||
|
||||
typedef GuString PgfCId;
|
||||
extern GU_DECLARE_TYPE(PgfCId, typedef);
|
||||
|
||||
|
||||
/// A single lexical token
|
||||
typedef GuString PgfToken;
|
||||
|
||||
/// @name PGF Grammar objects
|
||||
/// @{
|
||||
|
||||
typedef struct PgfPGF PgfPGF;
|
||||
|
||||
/**< A representation of a PGF grammar.
|
||||
*/
|
||||
|
||||
|
||||
PgfPGF*
|
||||
pgf_read(GuIn* in, GuPool* pool, GuExn* err);
|
||||
|
||||
/**< Read a grammar from a PGF file.
|
||||
*
|
||||
* @param from PGF input stream.
|
||||
* The stream must be positioned in the beginning of a binary
|
||||
* PGF representation. After a succesful invocation, the stream is
|
||||
* still open and positioned at the end of the representation.
|
||||
*
|
||||
* @param[out] err_out Raised error.
|
||||
* If non-\c NULL, \c *err_out should be \c NULL. Then, upon
|
||||
* failure, \c *err_out is set to point to a newly allocated
|
||||
* error object, which the caller must free with #g_exn_free
|
||||
* or #g_exn_propagate.
|
||||
*
|
||||
* @return A new PGF object, or \c NULL upon failure. The returned
|
||||
* object must later be freed with #pgf_free.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <gu/type.h>
|
||||
GU_DECLARE_TYPE(PgfPGF, struct);
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
#endif // PGF_H_
|
||||
843
src/runtime/c/pgf/reader.c
Normal file
843
src/runtime/c/pgf/reader.c
Normal file
@@ -0,0 +1,843 @@
|
||||
/*
|
||||
* Copyright 2010 University of Helsinki.
|
||||
*
|
||||
* This file is part of libpgf.
|
||||
*
|
||||
* Libpgf is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Libpgf is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with libpgf. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "data.h"
|
||||
#include "expr.h"
|
||||
#include <gu/defs.h>
|
||||
#include <gu/map.h>
|
||||
#include <gu/seq.h>
|
||||
#include <gu/assert.h>
|
||||
#include <gu/intern.h>
|
||||
#include <gu/in.h>
|
||||
#include <gu/bits.h>
|
||||
#include <gu/exn.h>
|
||||
#include <gu/utf8.h>
|
||||
|
||||
#define GU_LOG_ENABLE
|
||||
#include <gu/log.h>
|
||||
|
||||
typedef struct PgfIdContext PgfIdContext;
|
||||
|
||||
//
|
||||
// PgfReader
|
||||
//
|
||||
|
||||
typedef struct PgfReader PgfReader;
|
||||
|
||||
struct PgfReader {
|
||||
GuIn* in;
|
||||
GuExn* err;
|
||||
GuPool* opool;
|
||||
GuPool* pool;
|
||||
GuSymTable* symtab;
|
||||
PgfSequences* curr_sequences;
|
||||
PgfCncFuns* curr_cncfuns;
|
||||
GuMap* curr_ccats;
|
||||
GuMap* ccat_locs;
|
||||
GuMap* curr_lindefs;
|
||||
GuMap* curr_coercions;
|
||||
GuTypeMap* read_to_map;
|
||||
GuTypeMap* read_new_map;
|
||||
void* curr_key;
|
||||
GuPool* curr_pool;
|
||||
};
|
||||
|
||||
typedef struct PgfReadTagExn PgfReadTagExn;
|
||||
|
||||
struct PgfReadTagExn {
|
||||
GuType* type;
|
||||
int tag;
|
||||
};
|
||||
|
||||
static GU_DEFINE_TYPE(PgfReadTagExn, abstract, _);
|
||||
|
||||
static GU_DEFINE_TYPE(PgfReadExn, abstract, _);
|
||||
|
||||
static uint8_t
|
||||
pgf_read_u8(PgfReader* rdr)
|
||||
{
|
||||
uint8_t u = gu_in_u8(rdr->in, rdr->err);
|
||||
gu_debug("u8: %u", u);
|
||||
return u;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
pgf_read_uint(PgfReader* rdr)
|
||||
{
|
||||
uint32_t u = 0;
|
||||
int shift = 0;
|
||||
uint8_t b = 0;
|
||||
do {
|
||||
b = pgf_read_u8(rdr);
|
||||
gu_return_on_exn(rdr->err, 0);
|
||||
u |= (b & ~0x80) << shift;
|
||||
shift += 7;
|
||||
} while (b & 0x80);
|
||||
gu_debug("uint: %u", u);
|
||||
return u;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
pgf_read_int(PgfReader* rdr)
|
||||
{
|
||||
uint32_t u = pgf_read_uint(rdr);
|
||||
return gu_decode_2c32(u, rdr->err);
|
||||
}
|
||||
|
||||
static GuLength
|
||||
pgf_read_len(PgfReader* rdr)
|
||||
{
|
||||
int32_t len = pgf_read_int(rdr);
|
||||
// It's crucial that we return 0 on failure, so the
|
||||
// caller can proceed without checking for error
|
||||
// immediately.
|
||||
gu_return_on_exn(rdr->err, 0);
|
||||
if (len < 0) {
|
||||
gu_raise_i(rdr->err, PgfReadTagExn,
|
||||
.type = gu_type(GuLength), .tag = len);
|
||||
return 0;
|
||||
}
|
||||
return (GuLength) len;
|
||||
}
|
||||
|
||||
typedef const struct PgfReadToFn PgfReadToFn;
|
||||
|
||||
struct PgfReadToFn {
|
||||
void (*fn)(GuType* type, PgfReader* rdr, void* to);
|
||||
};
|
||||
|
||||
static void
|
||||
pgf_read_to(PgfReader* rdr, GuType* type, void* to) {
|
||||
PgfReadToFn* fn = gu_type_map_get(rdr->read_to_map, type);
|
||||
fn->fn(type, rdr, to);
|
||||
}
|
||||
|
||||
typedef const struct PgfReadNewFn PgfReadNewFn;
|
||||
struct PgfReadNewFn {
|
||||
void* (*fn)(GuType* type, PgfReader* rdr, GuPool* pool,
|
||||
size_t* size_out);
|
||||
};
|
||||
|
||||
static void*
|
||||
pgf_read_new(PgfReader* rdr, GuType* type, GuPool* pool, size_t* size_out)
|
||||
{
|
||||
size_t size = 0;
|
||||
PgfReadNewFn* fn = gu_type_map_get(rdr->read_new_map, type);
|
||||
return fn->fn(type, rdr, pool, size_out ? size_out : &size);
|
||||
}
|
||||
|
||||
static void*
|
||||
pgf_read_new_type(GuType* type, PgfReader* rdr, GuPool* pool,
|
||||
size_t* size_out)
|
||||
{
|
||||
GuTypeRepr* repr = gu_type_repr(type);
|
||||
void* to = gu_malloc_aligned(pool, repr->size, repr->align);
|
||||
pgf_read_to(rdr, type, to);
|
||||
*size_out = repr->size;
|
||||
return to;
|
||||
}
|
||||
|
||||
static void*
|
||||
pgf_read_struct(GuStructRepr* stype, PgfReader* rdr, void* to,
|
||||
GuPool* pool, size_t* size_out)
|
||||
{
|
||||
GuTypeRepr* repr = gu_type_cast((GuType*)stype, repr);
|
||||
size_t size = repr->size;
|
||||
GuLength length = 0;
|
||||
bool have_length = false;
|
||||
uint8_t* p = NULL;
|
||||
uint8_t* bto = to;
|
||||
gu_enter("-> struct %s", stype->name);
|
||||
|
||||
for (int i = 0; i < stype->members.len; i++) {
|
||||
const GuMember* m = &stype->members.elems[i];
|
||||
gu_enter("-> %s.%s", stype->name, m->name);
|
||||
if (m->is_flex) {
|
||||
gu_assert(have_length && p == NULL && pool != NULL);
|
||||
size_t m_size = gu_type_size(m->type);
|
||||
size = gu_flex_size(size, m->offset,
|
||||
length, m_size);
|
||||
p = gu_malloc_aligned(pool, size, repr->align);
|
||||
for (size_t j = 0; j < length; j++) {
|
||||
pgf_read_to(rdr, m->type,
|
||||
&p[m->offset + j * m_size]);
|
||||
gu_return_on_exn(rdr->err, NULL);
|
||||
}
|
||||
} else {
|
||||
pgf_read_to(rdr, m->type, &bto[m->offset]);
|
||||
gu_return_on_exn(rdr->err, NULL);
|
||||
}
|
||||
if (m->type == gu_type(GuLength)) {
|
||||
gu_assert(!have_length);
|
||||
have_length = true;
|
||||
length = gu_member(GuLength, to, m->offset);
|
||||
}
|
||||
gu_exit("<- %s.%s", stype->name, m->name);
|
||||
}
|
||||
if (p) {
|
||||
memcpy(p, to, repr->size);
|
||||
}
|
||||
if (size_out) {
|
||||
*size_out = size;
|
||||
}
|
||||
gu_exit("<- struct %s", stype->name);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_struct(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
GuStructRepr* stype = gu_type_cast(type, struct);
|
||||
pgf_read_struct(stype, rdr, to, NULL, NULL);
|
||||
}
|
||||
|
||||
static void*
|
||||
pgf_read_new_struct(GuType* type, PgfReader* rdr,
|
||||
GuPool* pool, size_t* size_out)
|
||||
{
|
||||
GuStructRepr* stype = gu_type_cast(type, struct);
|
||||
if (gu_struct_has_flex(stype)) {
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
void* to = gu_type_malloc(type, tmp_pool);
|
||||
void* p = pgf_read_struct(stype, rdr, to, pool, size_out);
|
||||
gu_pool_free(tmp_pool);
|
||||
gu_assert(p);
|
||||
return p;
|
||||
} else {
|
||||
void* to = gu_type_malloc(type, pool);
|
||||
pgf_read_struct(stype, rdr, to, NULL, NULL);
|
||||
return to;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pgf_read_to_pointer(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
GuPointerType* ptype = (GuPointerType*) type;
|
||||
GuType* pointed = ptype->pointed_type;
|
||||
gu_require(gu_type_has_kind(pointed, gu_kind(struct)) ||
|
||||
gu_type_has_kind(pointed, gu_kind(abstract)));
|
||||
GuStruct** sto = to;
|
||||
*sto = pgf_read_new(rdr, pointed, rdr->opool, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_GuVariant(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
GuVariantType* vtype = (GuVariantType*) type;
|
||||
GuVariant* vto = to;
|
||||
|
||||
uint8_t btag = pgf_read_u8(rdr);
|
||||
gu_return_on_exn(rdr->err,);
|
||||
if (btag >= vtype->ctors.len) {
|
||||
gu_raise_i(rdr->err, PgfReadTagExn,
|
||||
.type = type, .tag = btag);
|
||||
return;
|
||||
}
|
||||
GuConstructor* ctor = &vtype->ctors.elems[btag];
|
||||
gu_enter("-> variant %s", ctor->c_name);
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
GuTypeRepr* repr = gu_type_repr(ctor->type);
|
||||
size_t size = repr->size;
|
||||
void* init = pgf_read_new(rdr, ctor->type, tmp_pool, &size);
|
||||
*vto = gu_make_variant(btag, size, repr->align, init, rdr->opool);
|
||||
gu_pool_free(tmp_pool);
|
||||
gu_exit("<- variant %s", ctor->c_name);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_enum(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
// For now, assume that enum values are encoded in a single octet
|
||||
GuEnumType* etype = (GuEnumType*) type;
|
||||
uint8_t tag = pgf_read_u8(rdr);
|
||||
gu_return_on_exn(rdr->err,);
|
||||
if (tag >= etype->constants.len) {
|
||||
gu_raise_i(rdr->err, PgfReadTagExn,
|
||||
.type = type, .tag = tag);
|
||||
return;
|
||||
}
|
||||
GuEnumConstant* econ = &etype->constants.elems[tag];
|
||||
size_t size = gu_type_size(type);
|
||||
if (size == sizeof(int8_t)) {
|
||||
*((int8_t*) to) = econ->value;
|
||||
} else if (size == sizeof(int16_t)) {
|
||||
*((int16_t*) to) = econ->value;
|
||||
} else if (size == sizeof(int32_t)) {
|
||||
*((int32_t*) to) = econ->value;
|
||||
} else if (size == sizeof(int64_t)) {
|
||||
*((int64_t*) to) = econ->value;
|
||||
} else {
|
||||
gu_impossible();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_void(GuType* info, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) (info && rdr && to);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pgf_read_to_int(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) type;
|
||||
*(int*) to = pgf_read_int(rdr);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_uint16_t(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) type;
|
||||
*(uint16_t*) to = gu_in_u16be(rdr->in, rdr->err);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_GuLength(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) type;
|
||||
*(GuLength*) to = pgf_read_len(rdr);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_double(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) type;
|
||||
*(double*) to = gu_in_f64be(rdr->in, rdr->err);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_alias(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
GuTypeAlias* atype = gu_type_cast(type, alias);
|
||||
pgf_read_to(rdr, atype->type, to);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_into_map(GuMapType* mtype, PgfReader* rdr, GuMap* map, GuPool* pool)
|
||||
{
|
||||
/* The parameter pool is the temporary pool used to store the
|
||||
map. But the actual values need to be more persistent so we
|
||||
store them in rdr->opool. */
|
||||
(void) pool;
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
void* key = NULL;
|
||||
GuLength len = pgf_read_len(rdr);
|
||||
gu_return_on_exn(rdr->err, );
|
||||
if (mtype->hasher) {
|
||||
key = gu_type_malloc(mtype->key_type, tmp_pool);
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (mtype->hasher) {
|
||||
pgf_read_to(rdr, mtype->key_type, key);
|
||||
} else {
|
||||
key = pgf_read_new(rdr, mtype->key_type,
|
||||
rdr->opool, NULL);
|
||||
}
|
||||
gu_return_on_exn(rdr->err, );
|
||||
rdr->curr_key = key;
|
||||
/* If an old value already exists, read into
|
||||
it. This allows us to create the value
|
||||
object and point into it before we read the
|
||||
content. */
|
||||
void* valp = gu_map_insert(map, key);
|
||||
pgf_read_to(rdr, mtype->value_type, valp);
|
||||
gu_return_on_exn(rdr->err, );
|
||||
}
|
||||
gu_pool_free(tmp_pool);
|
||||
}
|
||||
|
||||
static void*
|
||||
pgf_read_new_GuMap(GuType* type, PgfReader* rdr, GuPool* pool, size_t* size_out)
|
||||
{
|
||||
(void) size_out;
|
||||
GuMapType* mtype = (GuMapType*) type;
|
||||
GuMap* map = gu_map_type_make(mtype, pool);
|
||||
pgf_read_into_map(mtype, rdr, map, pool);
|
||||
gu_return_on_exn(rdr->err, NULL);
|
||||
return map;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_GuString(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) (type);
|
||||
gu_enter("-> GuString");
|
||||
GuString* sp = to;
|
||||
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
GuStringBuf* sbuf = gu_string_buf(tmp_pool);
|
||||
GuWriter* wtr = gu_string_buf_writer(sbuf);
|
||||
|
||||
GuLength len = pgf_read_len(rdr);
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
GuUCS ucs = gu_in_utf8(rdr->in, rdr->err);
|
||||
gu_ucs_write(ucs, wtr, rdr->err);
|
||||
}
|
||||
GuString str = gu_string_buf_freeze(sbuf, tmp_pool);
|
||||
GuSymbol sym = gu_symtable_intern(rdr->symtab, str);
|
||||
gu_pool_free(tmp_pool);
|
||||
|
||||
gu_exit("<- GuString");
|
||||
*sp = sym;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_PgfCId(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) (type);
|
||||
gu_enter("-> PgfCId");
|
||||
PgfCId* sp = to;
|
||||
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
GuStringBuf* sbuf = gu_string_buf(tmp_pool);
|
||||
GuWriter* wtr = gu_string_buf_writer(sbuf);
|
||||
|
||||
GuLength len = pgf_read_len(rdr);
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
// CIds are in latin-1
|
||||
GuUCS ucs = gu_in_u8(rdr->in, rdr->err);
|
||||
gu_ucs_write(ucs, wtr, rdr->err);
|
||||
}
|
||||
GuString str = gu_string_buf_freeze(sbuf, tmp_pool);
|
||||
GuSymbol sym = gu_symtable_intern(rdr->symtab, str);
|
||||
gu_pool_free(tmp_pool);
|
||||
|
||||
gu_exit("<- PgfCId");
|
||||
*sp = sym;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_PgfCCatId(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) (type);
|
||||
PgfCCat** pto = to;
|
||||
int fid = pgf_read_int(rdr);
|
||||
gu_return_on_exn(rdr->err,);
|
||||
PgfCCat* ccat = gu_map_get(rdr->curr_ccats, &fid, PgfCCat*);
|
||||
if (ccat) {
|
||||
*pto = ccat;
|
||||
return;
|
||||
}
|
||||
GuBuf* locs = gu_map_get(rdr->ccat_locs, &fid, GuBuf*);
|
||||
if (!locs) {
|
||||
locs = gu_new_buf(PgfCCat**, rdr->pool);
|
||||
gu_map_put(rdr->ccat_locs, &fid, GuBuf*, locs);
|
||||
}
|
||||
gu_buf_push(locs, PgfCCat**, pto);
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_PgfCCat(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) type;
|
||||
gu_enter("->");
|
||||
PgfCCat* cat = to;
|
||||
cat->cnccat = NULL;
|
||||
pgf_read_to(rdr, gu_type(PgfProductionSeq), &cat->prods);
|
||||
int* fidp = rdr->curr_key;
|
||||
cat->fid = *fidp;
|
||||
GuBuf* locs_buf = gu_map_get(rdr->ccat_locs, fidp, GuBuf*);
|
||||
if (locs_buf) {
|
||||
size_t len = gu_buf_length(locs_buf);
|
||||
PgfCCat*** locs = gu_buf_data(locs_buf);
|
||||
for (size_t n = 0; n < len; n++) {
|
||||
*(locs[n]) = cat;
|
||||
}
|
||||
}
|
||||
gu_exit("<-");
|
||||
}
|
||||
|
||||
// This is only needed because new_struct would otherwise override.
|
||||
// TODO: get rid of new_struct and all the FAM mess
|
||||
static void*
|
||||
pgf_read_new_PgfCCat(GuType* type, PgfReader* rdr, GuPool* pool,
|
||||
size_t* size_out)
|
||||
{
|
||||
PgfCCat* ccat = gu_new(PgfCCat, pool);
|
||||
pgf_read_to_PgfCCat(type, rdr, ccat);
|
||||
*size_out = sizeof(PgfCCat);
|
||||
return ccat;
|
||||
}
|
||||
|
||||
static void*
|
||||
pgf_read_new_GuList(GuType* type, PgfReader* rdr, GuPool* pool, size_t* size_out)
|
||||
{
|
||||
GuListType* ltype = gu_type_cast(type, GuList);
|
||||
GuLength length = pgf_read_len(rdr);
|
||||
gu_return_on_exn(rdr->err, NULL);
|
||||
void* list = gu_list_type_alloc(ltype, length, pool);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
void* elem = gu_list_type_index(ltype, list, i);
|
||||
pgf_read_to(rdr, ltype->elem_type, elem);
|
||||
gu_return_on_exn(rdr->err, NULL);
|
||||
}
|
||||
*size_out = gu_flex_size(ltype->size, ltype->elems_offset,
|
||||
length,
|
||||
gu_type_size(ltype->elem_type));
|
||||
return list;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_GuSeq(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
gu_enter("->");
|
||||
GuSeqType* stype = gu_type_cast(type, GuSeq);
|
||||
GuLength length = pgf_read_len(rdr);
|
||||
GuTypeRepr* repr = gu_type_repr(stype->elem_type);
|
||||
gu_return_on_exn(rdr->err, );
|
||||
GuSeq seq = gu_make_seq(repr->size, length, rdr->opool);
|
||||
uint8_t* data = gu_seq_data(seq);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
void* elem = &data[i * repr->size];
|
||||
pgf_read_to(rdr, stype->elem_type, elem);
|
||||
gu_return_on_exn(rdr->err, );
|
||||
}
|
||||
GuSeq* sto = to;
|
||||
*sto = seq;
|
||||
gu_exit("<-");
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_maybe_seq(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
GuSeq* sto = to;
|
||||
uint8_t tag = pgf_read_u8(rdr);
|
||||
gu_return_on_exn(rdr->err,);
|
||||
switch (tag) {
|
||||
case 0:
|
||||
*sto = gu_null_seq;
|
||||
break;
|
||||
case 1:
|
||||
pgf_read_to_GuSeq(type, rdr, to);
|
||||
break;
|
||||
default:
|
||||
gu_raise_i(rdr->err, PgfReadTagExn,
|
||||
.type = type, .tag = tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void*
|
||||
pgf_read_new_idarray(GuType* type, PgfReader* rdr, GuPool* pool,
|
||||
size_t* size_out)
|
||||
{
|
||||
(void) type;
|
||||
void* list = pgf_read_new_GuList(type, rdr, rdr->curr_pool, size_out);
|
||||
if (type == gu_type(PgfSequences)) {
|
||||
rdr->curr_sequences = list;
|
||||
} else if (type == gu_type(PgfCncFuns)) {
|
||||
rdr->curr_cncfuns = list;
|
||||
} else {
|
||||
gu_impossible();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static void
|
||||
pgf_read_to_PgfSeqId(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) type;
|
||||
int32_t id = pgf_read_int(rdr);
|
||||
gu_return_on_exn(rdr->err,);
|
||||
if (id < 0 || id >= gu_list_length(rdr->curr_sequences)) {
|
||||
gu_raise(rdr->err, PgfReadExn);
|
||||
return;
|
||||
}
|
||||
*(PgfSeqId*) to = gu_list_elems(rdr->curr_sequences)[id];
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pgf_read_to_PgfFunId(GuType* type, PgfReader* rdr, void* to)
|
||||
{
|
||||
(void) type;
|
||||
int32_t id = pgf_read_int(rdr);
|
||||
gu_return_on_exn(rdr->err,);
|
||||
if (id < 0 || id >= gu_list_length(rdr->curr_cncfuns)) {
|
||||
gu_raise(rdr->err, PgfReadExn);
|
||||
return;
|
||||
}
|
||||
*(PgfFunId*) to = gu_list_elems(rdr->curr_cncfuns)[id];
|
||||
}
|
||||
|
||||
static GU_DEFINE_TYPE(PgfLinDefs, GuIntMap, gu_ptr_type(PgfFunIds),
|
||||
&gu_null_struct);
|
||||
typedef PgfCCat PgfCCatData;
|
||||
static GU_DEFINE_TYPE(PgfCCatData, typedef, gu_type(PgfCCat));
|
||||
|
||||
static GU_DEFINE_TYPE(PgfCCatMap, GuIntMap, gu_ptr_type(PgfCCat),
|
||||
&gu_null_struct);
|
||||
|
||||
static GU_DEFINE_TYPE(PgfCncCatMap, GuStringMap, gu_ptr_type(PgfCncCat),
|
||||
&gu_null_struct);
|
||||
|
||||
typedef struct {
|
||||
GuMapItor fn;
|
||||
GuBuf* seq;
|
||||
} PgfCCatCbCtx;
|
||||
|
||||
static PgfCncCat*
|
||||
pgf_ccat_set_cnccat(PgfCCat* ccat, GuBuf* newly_set)
|
||||
{
|
||||
if (!ccat->cnccat) {
|
||||
size_t n_prods = gu_seq_length(ccat->prods);
|
||||
for (size_t i = 0; i < n_prods; i++) {
|
||||
PgfProduction prod =
|
||||
gu_seq_get(ccat->prods, PgfProduction, i);
|
||||
GuVariantInfo i = gu_variant_open(prod);
|
||||
switch (i.tag) {
|
||||
case PGF_PRODUCTION_COERCE: {
|
||||
PgfProductionCoerce* pcoerce = i.data;
|
||||
PgfCncCat* cnccat =
|
||||
pgf_ccat_set_cnccat(pcoerce->coerce,
|
||||
newly_set);
|
||||
if (!ccat->cnccat) {
|
||||
ccat->cnccat = cnccat;
|
||||
} else if (ccat->cnccat != cnccat) {
|
||||
// XXX: real error
|
||||
gu_impossible();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PGF_PRODUCTION_APPLY:
|
||||
// Shouldn't happen with current PGF.
|
||||
// XXX: real error
|
||||
gu_impossible();
|
||||
break;
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
}
|
||||
gu_buf_push(newly_set, PgfCCat*, ccat);
|
||||
}
|
||||
return ccat->cnccat;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pgf_read_ccat_cb(GuMapItor* fn, const void* key, void* value, GuExn* err)
|
||||
{
|
||||
(void) (key && err);
|
||||
PgfCCatCbCtx* ctx = (PgfCCatCbCtx*) fn;
|
||||
PgfCCat** ccatp = value;
|
||||
pgf_ccat_set_cnccat(*ccatp, ctx->seq);
|
||||
}
|
||||
|
||||
static void*
|
||||
pgf_read_new_PgfConcr(GuType* type, PgfReader* rdr, GuPool* pool,
|
||||
size_t* size_out)
|
||||
{
|
||||
(void) (type && size_out);
|
||||
/* We allocate indices from a temporary pool. The actual data
|
||||
* is allocated from rdr->opool. Once everything is resolved
|
||||
* and indices aren't needed, the temporary pool can be
|
||||
* freed. */
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
rdr->curr_pool = tmp_pool;
|
||||
PgfConcr* concr = gu_new(PgfConcr, pool);;
|
||||
concr->cflags =
|
||||
pgf_read_new(rdr, gu_type(PgfFlags), pool, NULL);
|
||||
concr->printnames =
|
||||
pgf_read_new(rdr, gu_type(PgfPrintNames), pool, NULL);
|
||||
rdr->curr_sequences =
|
||||
pgf_read_new(rdr, gu_type(PgfSequences), pool, NULL);
|
||||
rdr->curr_cncfuns =
|
||||
pgf_read_new(rdr, gu_type(PgfCncFuns), pool, NULL);
|
||||
GuMapType* lindefs_t = gu_type_cast(gu_type(PgfLinDefs), GuMap);
|
||||
rdr->curr_lindefs = gu_map_type_make(lindefs_t, tmp_pool);
|
||||
pgf_read_into_map(lindefs_t, rdr, rdr->curr_lindefs, rdr->opool);
|
||||
GuMapType* ccats_t = gu_type_cast(gu_type(PgfCCatMap), GuMap);
|
||||
rdr->curr_ccats =
|
||||
gu_new_int_map(PgfCCat*, &gu_null_struct, tmp_pool);
|
||||
rdr->ccat_locs =
|
||||
gu_new_int_map(GuBuf*, &gu_null_struct, tmp_pool);
|
||||
pgf_read_into_map(ccats_t, rdr, rdr->curr_ccats, rdr->opool);
|
||||
concr->cnccats = pgf_read_new(rdr, gu_type(PgfCncCatMap),
|
||||
rdr->opool, NULL);
|
||||
|
||||
GuBuf* extra_ccats = gu_new_buf(PgfCCat*, tmp_pool);
|
||||
PgfCCatCbCtx ctx = { { pgf_read_ccat_cb }, extra_ccats };
|
||||
gu_map_iter(rdr->curr_ccats, &ctx.fn, NULL);
|
||||
concr->extra_ccats = gu_buf_freeze(extra_ccats, rdr->opool);
|
||||
(void) pgf_read_int(rdr); // totalcats
|
||||
gu_pool_free(tmp_pool);
|
||||
return concr;
|
||||
}
|
||||
|
||||
static bool
|
||||
pgf_ccat_n_lins(PgfCCat* cat, int* n_lins) {
|
||||
if (gu_seq_is_null(cat->prods)) {
|
||||
return true;
|
||||
}
|
||||
size_t n_prods = gu_seq_length(cat->prods);
|
||||
for (size_t j = 0; j < n_prods; j++) {
|
||||
PgfProduction prod =
|
||||
gu_seq_get(cat->prods, PgfProduction, j);
|
||||
GuVariantInfo i = gu_variant_open(prod);
|
||||
switch (i.tag) {
|
||||
case PGF_PRODUCTION_APPLY: {
|
||||
PgfProductionApply* papp = i.data;
|
||||
if (*n_lins == -1) {
|
||||
*n_lins = (int) papp->fun->n_lins;
|
||||
} else if (*n_lins != (int) papp->fun->n_lins) {
|
||||
// Inconsistent n_lins for different productions!
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PGF_PRODUCTION_COERCE: {
|
||||
PgfProductionCoerce* pcoerce = i.data;
|
||||
bool succ = pgf_ccat_n_lins(pcoerce->coerce, n_lins);
|
||||
if (!succ) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gu_impossible();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void*
|
||||
pgf_read_new_PgfCncCat(GuType* type, PgfReader* rdr, GuPool* pool,
|
||||
size_t* size_out)
|
||||
{
|
||||
PgfCId cid = *(PgfCId*) rdr->curr_key;
|
||||
gu_enter("-> cid");
|
||||
(void) (type && size_out);
|
||||
PgfCncCat* cnccat = gu_new(PgfCncCat, pool);
|
||||
cnccat->cid = cid;
|
||||
int first = pgf_read_int(rdr);
|
||||
int last = pgf_read_int(rdr);
|
||||
int len = last + 1 - first;
|
||||
PgfCCatIds* cats = gu_new_list(PgfCCatIds, pool, len);
|
||||
int n_lins = -1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
int n = first + i;
|
||||
PgfCCat* ccat = gu_map_get(rdr->curr_ccats, &n, PgfCCat*);
|
||||
/* ccat can be NULL if the PGF is optimized and the
|
||||
* category has been erased as useless */
|
||||
gu_list_index(cats, i) = ccat;
|
||||
if (ccat != NULL) {
|
||||
// TODO: error if overlap
|
||||
ccat->cnccat = cnccat;
|
||||
if (!pgf_ccat_n_lins(ccat, &n_lins)) {
|
||||
gu_raise(rdr->err, PgfReadExn);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
}
|
||||
gu_debug("range[%d] = %d", i, ccat ? ccat->fid : -1);
|
||||
}
|
||||
cnccat->n_lins = n_lins == -1 ? 0 : (size_t) n_lins;
|
||||
cnccat->cats = cats;
|
||||
cnccat->lindefs = gu_map_get(rdr->curr_lindefs, &first, PgfFunIds*);
|
||||
cnccat->labels = pgf_read_new(rdr, gu_type(GuStringL),
|
||||
pool, NULL);
|
||||
gu_exit("<-");
|
||||
return cnccat;
|
||||
fail:
|
||||
gu_exit("<- fail");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define PGF_READ_TO_FN(k_, fn_) \
|
||||
{ gu_kind(k_), (void*) &(PgfReadToFn){ fn_ } }
|
||||
|
||||
#define PGF_READ_TO(k_) \
|
||||
PGF_READ_TO_FN(k_, pgf_read_to_##k_)
|
||||
|
||||
|
||||
static GuTypeTable
|
||||
pgf_read_to_table = GU_TYPETABLE(
|
||||
GU_SLIST_0,
|
||||
PGF_READ_TO(struct),
|
||||
PGF_READ_TO(GuVariant),
|
||||
PGF_READ_TO(enum),
|
||||
PGF_READ_TO(void),
|
||||
PGF_READ_TO(int),
|
||||
PGF_READ_TO(uint16_t),
|
||||
PGF_READ_TO(GuLength),
|
||||
PGF_READ_TO(PgfCId),
|
||||
PGF_READ_TO(GuString),
|
||||
PGF_READ_TO(double),
|
||||
PGF_READ_TO(pointer),
|
||||
PGF_READ_TO_FN(PgfEquationsM, pgf_read_to_maybe_seq),
|
||||
PGF_READ_TO(GuSeq),
|
||||
PGF_READ_TO(PgfCCatId),
|
||||
PGF_READ_TO(PgfCCat),
|
||||
PGF_READ_TO(PgfSeqId),
|
||||
PGF_READ_TO(PgfFunId),
|
||||
PGF_READ_TO(alias));
|
||||
|
||||
#define PGF_READ_NEW_FN(k_, fn_) \
|
||||
{ gu_kind(k_), (void*) &(PgfReadNewFn){ fn_ } }
|
||||
|
||||
#define PGF_READ_NEW(k_) \
|
||||
PGF_READ_NEW_FN(k_, pgf_read_new_##k_)
|
||||
|
||||
static GuTypeTable
|
||||
pgf_read_new_table = GU_TYPETABLE(
|
||||
GU_SLIST_0,
|
||||
PGF_READ_NEW(type),
|
||||
PGF_READ_NEW(struct),
|
||||
PGF_READ_NEW(GuMap),
|
||||
PGF_READ_NEW(GuList),
|
||||
PGF_READ_NEW(PgfCCat),
|
||||
PGF_READ_NEW(PgfCncCat),
|
||||
PGF_READ_NEW(PgfConcr),
|
||||
PGF_READ_NEW_FN(PgfSequences, pgf_read_new_idarray),
|
||||
PGF_READ_NEW_FN(PgfCncFuns, pgf_read_new_idarray)
|
||||
);
|
||||
|
||||
static PgfReader*
|
||||
pgf_new_reader(GuIn* in, GuPool* opool, GuPool* pool, GuExn* err)
|
||||
{
|
||||
PgfReader* rdr = gu_new(PgfReader, pool);
|
||||
rdr->opool = opool;
|
||||
rdr->symtab = gu_new_symtable(opool, pool);
|
||||
rdr->err = err;
|
||||
rdr->in = in;
|
||||
rdr->curr_sequences = NULL;
|
||||
rdr->curr_cncfuns = NULL;
|
||||
rdr->read_to_map = gu_new_type_map(&pgf_read_to_table, pool);
|
||||
rdr->read_new_map = gu_new_type_map(&pgf_read_new_table, pool);
|
||||
rdr->pool = pool;
|
||||
return rdr;
|
||||
}
|
||||
|
||||
|
||||
PgfPGF*
|
||||
pgf_read(GuIn* in, GuPool* pool, GuExn* err)
|
||||
{
|
||||
GuPool* tmp_pool = gu_new_pool();
|
||||
PgfReader* rdr = pgf_new_reader(in, pool, tmp_pool, err);
|
||||
PgfPGF* pgf = pgf_read_new(rdr, gu_type(PgfPGF), pool, NULL);
|
||||
gu_pool_free(tmp_pool);
|
||||
gu_return_on_exn(err, NULL);
|
||||
return pgf;
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,248 +0,0 @@
|
||||
#include "../pgf.h"
|
||||
#include "data.h"
|
||||
#include "panic.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
static void freeCId(CId id) {
|
||||
free(id);
|
||||
}
|
||||
|
||||
static void freeCIdList(CIdList ids) {
|
||||
int i;
|
||||
for (i = 0; i < ids->count; i++) {
|
||||
freeCId(ids->names[i]);
|
||||
}
|
||||
free(ids);
|
||||
}
|
||||
|
||||
static void freeString(String str) {
|
||||
free(str);
|
||||
}
|
||||
|
||||
static void freeLiteral(Literal lit) {
|
||||
switch (lit->tag) {
|
||||
case LIT_STR:
|
||||
freeString (((LiteralStr) lit)->val);
|
||||
break;
|
||||
}
|
||||
free(lit);
|
||||
}
|
||||
|
||||
static void freeFlags(Flags flags) {
|
||||
int i;
|
||||
for (i = 0; i < flags->count; i++) {
|
||||
freeCId(flags->values[i].name);
|
||||
freeLiteral(flags->values[i].value);
|
||||
}
|
||||
free(flags);
|
||||
}
|
||||
|
||||
static void freeContext(Context ctxt);
|
||||
static void freeType(Type ty);
|
||||
|
||||
static void freeExpr(Expr e0) {
|
||||
|
||||
switch (e0->tag) {
|
||||
case TAG_ABS:
|
||||
{
|
||||
ExprAbs e = (ExprAbs) e0;
|
||||
freeCId(e->var);
|
||||
freeExpr(e->body);
|
||||
}
|
||||
break;
|
||||
case TAG_APP:
|
||||
{
|
||||
ExprApp e = (ExprApp) e0;
|
||||
freeExpr(e->left);
|
||||
freeExpr(e->right);
|
||||
}
|
||||
break;
|
||||
case TAG_LIT:
|
||||
{
|
||||
ExprLit e = (ExprLit) e0;
|
||||
freeLiteral(e->lit);
|
||||
}
|
||||
break;
|
||||
case TAG_MET:
|
||||
{
|
||||
ExprMeta e = (ExprMeta) e0;
|
||||
}
|
||||
break;
|
||||
case TAG_FUN:
|
||||
{
|
||||
ExprFun e = (ExprFun) e0;
|
||||
freeCId(e->fun);
|
||||
}
|
||||
break;
|
||||
case TAG_VAR:
|
||||
{
|
||||
ExprVar e = (ExprVar) e0;
|
||||
}
|
||||
break;
|
||||
case TAG_TYP:
|
||||
{
|
||||
ExprTyped e = (ExprTyped) e0;
|
||||
freeExpr(e->e);
|
||||
freeType(e->ty);
|
||||
}
|
||||
break;
|
||||
case TAG_IMP:
|
||||
{
|
||||
ExprImplArg e = (ExprImplArg) e0;
|
||||
freeExpr(e->e);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
__pgf_panic("Unknown expression tag");
|
||||
}
|
||||
|
||||
free(e0);
|
||||
}
|
||||
|
||||
static void freeType(Type ty) {
|
||||
freeContext(ty->hypos);
|
||||
freeCId(ty->cat);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ty->nArgs; i++) {
|
||||
freeExpr(ty->args[i]);
|
||||
}
|
||||
|
||||
free(ty);
|
||||
}
|
||||
|
||||
static void freeHypo(Hypo hypo) {
|
||||
freeCId(hypo->var);
|
||||
freeType(hypo->ty);
|
||||
}
|
||||
|
||||
static void freeContext(Context ctxt) {
|
||||
int i;
|
||||
for (i = 0; i < ctxt->size; i++) {
|
||||
freeHypo(&ctxt->hypos[i]);
|
||||
}
|
||||
free(ctxt);
|
||||
}
|
||||
|
||||
static void freePatt(Patt p0) {
|
||||
switch (p0->tag) {
|
||||
case TAG_PAPP:
|
||||
{
|
||||
int i;
|
||||
PattApp p = (PattApp) p0;
|
||||
|
||||
freeCId(p->fun);
|
||||
for (i = 0; i < p->args.count; i++) {
|
||||
freePatt(p->args.pats[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TAG_PVAR:
|
||||
{
|
||||
PattVar p = (PattVar) p0;
|
||||
freeCId(p->var);
|
||||
}
|
||||
break;
|
||||
case TAG_PAT:
|
||||
{
|
||||
PattAt p = (PattAt) p0;
|
||||
freeCId(p->var);
|
||||
freePatt(p->pat);
|
||||
}
|
||||
break;
|
||||
case TAG_PWILD:
|
||||
{
|
||||
PattWild p = (PattWild) p0;
|
||||
}
|
||||
break;
|
||||
case TAG_PLIT:
|
||||
{
|
||||
PattLit p = (PattLit) p0;
|
||||
freeLiteral(p->lit);
|
||||
}
|
||||
break;
|
||||
case TAG_PIMP:
|
||||
{
|
||||
PattImplArg p = (PattImplArg) p0;
|
||||
freePatt(p->pat);
|
||||
}
|
||||
break;
|
||||
case TAG_PTILDE:
|
||||
{
|
||||
PattTilde p = (PattTilde) p0;
|
||||
freeExpr(p->e);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
__pgf_panic("Unknown pattern tag");
|
||||
}
|
||||
|
||||
free(p0);
|
||||
}
|
||||
|
||||
static void freePatts(Patts pats) {
|
||||
int i;
|
||||
for (i = 0; i < pats->count; i++) {
|
||||
freePatt(pats->pats[i]);
|
||||
}
|
||||
free(pats);
|
||||
}
|
||||
|
||||
static void freeEquations(Equations equs) {
|
||||
int i;
|
||||
for (i = 0; i < equs->count; i++) {
|
||||
freePatts(equs->equs[i].lhs);
|
||||
freeExpr(equs->equs[i].rhs);
|
||||
}
|
||||
free(equs);
|
||||
}
|
||||
|
||||
static void freeAbsFun(AbsFun fun) {
|
||||
freeCId(fun->name);
|
||||
freeType(fun->ty);
|
||||
freeEquations(fun->equs);
|
||||
}
|
||||
|
||||
static void freeAbsFuns(AbsFuns funs) {
|
||||
int i;
|
||||
for (i = 0; i < funs->count; i++) {
|
||||
freeAbsFun(&funs->lst[i]);
|
||||
}
|
||||
free(funs);
|
||||
}
|
||||
|
||||
static void freeAbsCat(AbsCat cat) {
|
||||
freeCId(cat->name);
|
||||
freeContext(cat->hypos);
|
||||
freeCIdList(cat->funs);
|
||||
}
|
||||
|
||||
static void freeAbsCats(AbsCats cats) {
|
||||
int i;
|
||||
for (i = 0; i < cats->count; i++) {
|
||||
freeAbsCat(&cats->lst[i]);
|
||||
}
|
||||
free(cats);
|
||||
}
|
||||
|
||||
static void freeAbstract(Abstract abstr) {
|
||||
freeCId(abstr->name);
|
||||
freeFlags(abstr->flags);
|
||||
freeAbsFuns(abstr->funs);
|
||||
freeAbsCats(abstr->cats);
|
||||
}
|
||||
|
||||
static void freeConcrete(Concrete concr) {
|
||||
// freeCId(concr->name);
|
||||
// freeFlags(concr->flags);
|
||||
}
|
||||
|
||||
void freePGF(PGF pgf) {
|
||||
int i;
|
||||
|
||||
freeFlags(pgf->flags);
|
||||
freeAbstract(&pgf->abstract);
|
||||
for (i = 0; i < pgf->nConcr; i++)
|
||||
freeConcrete(&pgf->concretes[i]);
|
||||
free(pgf);
|
||||
}
|
||||
179
src/runtime/c/utils/pgf-translate.c
Normal file
179
src/runtime/c/utils/pgf-translate.c
Normal file
@@ -0,0 +1,179 @@
|
||||
#include <gu/variant.h>
|
||||
#include <gu/map.h>
|
||||
#include <gu/dump.h>
|
||||
#include <gu/log.h>
|
||||
#include <gu/enum.h>
|
||||
#include <gu/file.h>
|
||||
#include <pgf/pgf.h>
|
||||
#include <pgf/data.h>
|
||||
#include <pgf/parser.h>
|
||||
#include <pgf/linearize.h>
|
||||
#include <pgf/expr.h>
|
||||
#include <pgf/edsl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// Set the character locale, so we can produce proper output.
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
// Create the pool that is used to allocate everything
|
||||
GuPool* pool = gu_new_pool();
|
||||
int status = EXIT_SUCCESS;
|
||||
if (argc != 5) {
|
||||
fprintf(stderr, "usage: %s pgf cat from_lang to_lang\n", argv[0]);
|
||||
status = EXIT_FAILURE;
|
||||
goto fail;
|
||||
}
|
||||
char* filename = argv[1];
|
||||
|
||||
// Transform C strings to libgu strings
|
||||
GuString cat = gu_str_string(argv[2], pool);
|
||||
GuString from_lang = gu_str_string(argv[3], pool);
|
||||
GuString to_lang = gu_str_string(argv[4], pool);
|
||||
|
||||
FILE* infile = fopen(filename, "r");
|
||||
if (infile == NULL) {
|
||||
fprintf(stderr, "couldn't open %s\n", filename);
|
||||
status = EXIT_FAILURE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Create an input stream from the input file
|
||||
GuIn* in = gu_file_in(infile, pool);
|
||||
|
||||
// Create an exception frame that catches all errors.
|
||||
GuExn* err = gu_new_exn(NULL, gu_kind(type), pool);
|
||||
|
||||
// Read the PGF grammar.
|
||||
PgfPGF* pgf = pgf_read(in, pool, err);
|
||||
|
||||
// If an error occured, it shows in the exception frame
|
||||
if (!gu_ok(err)) {
|
||||
fprintf(stderr, "Reading PGF failed\n");
|
||||
status = EXIT_FAILURE;
|
||||
goto fail_read;
|
||||
}
|
||||
|
||||
// Look up the source and destination concrete categories
|
||||
PgfConcr* from_concr =
|
||||
gu_map_get(pgf->concretes, &from_lang, PgfConcr*);
|
||||
PgfConcr* to_concr =
|
||||
gu_map_get(pgf->concretes, &to_lang, PgfConcr*);
|
||||
if (!from_concr || !to_concr) {
|
||||
fprintf(stderr, "Unknown language");
|
||||
status = EXIT_FAILURE;
|
||||
goto fail_concr;
|
||||
}
|
||||
|
||||
// Create the parser for the source category
|
||||
PgfParser* parser = pgf_new_parser(from_concr, pool);
|
||||
|
||||
// Create a linearizer for the destination category
|
||||
PgfLzr* lzr = pgf_new_lzr(to_concr, pool);
|
||||
|
||||
// Arbitrarily choose linearization index 0. Usually the initial
|
||||
// categories we are interested in only have one field.
|
||||
int lin_idx = 0;
|
||||
|
||||
// Create an output stream for stdout
|
||||
GuOut* out = gu_file_out(stdout, pool);
|
||||
|
||||
// Locale-encoding writers are currently unsupported
|
||||
// GuWriter* wtr = gu_locale_writer(out, pool);
|
||||
// Use a writer with hard-coded utf-8 encoding for now.
|
||||
GuWriter* wtr = gu_new_utf8_writer(out, pool);
|
||||
|
||||
// The interactive translation loop.
|
||||
// XXX: This currently reads stdin directly, so it doesn't support
|
||||
// encodings properly. TODO: use a locale reader for input
|
||||
while (true) {
|
||||
fprintf(stdout, "> ");
|
||||
fflush(stdout);
|
||||
char buf[4096];
|
||||
char* line = fgets(buf, sizeof(buf), stdin);
|
||||
if (line == NULL) {
|
||||
if (ferror(stdin)) {
|
||||
fprintf(stderr, "Input error\n");
|
||||
status = EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
} else if (line[0] == '\0') {
|
||||
// End nicely on empty input
|
||||
break;
|
||||
}
|
||||
// We create a temporary pool for translating a single
|
||||
// sentence, so our memory usage doesn't increase over time.
|
||||
GuPool* ppool = gu_new_pool();
|
||||
|
||||
// Begin parsing a sentence of the specified category
|
||||
PgfParse* parse =
|
||||
pgf_parser_parse(parser, cat, lin_idx, pool);
|
||||
if (parse == NULL) {
|
||||
fprintf(stderr, "Couldn't begin parsing");
|
||||
status = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
// naive tokenization
|
||||
char* tok = strtok(line, " \n");
|
||||
while (tok) {
|
||||
GuString tok_s = gu_str_string(tok, pool);
|
||||
gu_debug("parsing token \"%s\"", tok);
|
||||
// feed the token to get a new parse state
|
||||
parse = pgf_parse_token(parse, tok_s, ppool);
|
||||
if (!parse) {
|
||||
fprintf(stderr,
|
||||
"Unexpected token: \"%s\"\n", tok);
|
||||
goto fail_parse;
|
||||
}
|
||||
tok = strtok(NULL, " \n");
|
||||
}
|
||||
|
||||
// Now begin enumerating the resulting syntax trees
|
||||
GuEnum* result = pgf_parse_result(parse, ppool);
|
||||
|
||||
while (true) {
|
||||
PgfExpr expr = gu_next(result, PgfExpr, ppool);
|
||||
// The enumerator will return a null variant at the
|
||||
// end of the results.
|
||||
if (gu_variant_is_null(expr)) {
|
||||
break;
|
||||
}
|
||||
gu_putc(' ', wtr, err);
|
||||
// Write out the abstract syntax tree
|
||||
pgf_expr_print(expr, wtr, err);
|
||||
gu_putc('\n', wtr, err);
|
||||
|
||||
// Enumerate the concrete syntax trees corresponding
|
||||
// to the abstract tree.
|
||||
GuEnum* cts = pgf_lzr_concretize(lzr, expr, ppool);
|
||||
while (true) {
|
||||
PgfCncTree ctree =
|
||||
gu_next(cts, PgfCncTree, ppool);
|
||||
if (gu_variant_is_null(ctree)) {
|
||||
break;
|
||||
}
|
||||
gu_puts(" ", wtr, err);
|
||||
// Linearize the concrete tree as a simple
|
||||
// sequence of strings.
|
||||
pgf_lzr_linearize_simple(lzr, ctree, lin_idx,
|
||||
wtr, err);
|
||||
gu_putc('\n', wtr, err);
|
||||
gu_writer_flush(wtr, err);
|
||||
}
|
||||
}
|
||||
fail_parse:
|
||||
// Free all resources allocated during parsing and linearization
|
||||
gu_pool_free(ppool);
|
||||
}
|
||||
fail_concr:
|
||||
fail_read:
|
||||
fclose(infile);
|
||||
fail:
|
||||
gu_pool_free(pool);
|
||||
return status;
|
||||
}
|
||||
|
||||
29
src/runtime/c/utils/pgf2yaml.c
Normal file
29
src/runtime/c/utils/pgf2yaml.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <pgf/pgf.h>
|
||||
|
||||
#include <gu/dump.h>
|
||||
#include <gu/file.h>
|
||||
#include <gu/utf8.h>
|
||||
|
||||
int main(void) {
|
||||
GuPool* pool = gu_new_pool();
|
||||
GuExn* err = gu_exn(NULL, type, pool);
|
||||
GuIn* in = gu_file_in(stdin, pool);
|
||||
PgfPGF* pgf = pgf_read(in, pool, err);
|
||||
int status = 0;
|
||||
if (!gu_ok(err)) {
|
||||
fprintf(stderr, "Reading PGF failed\n");
|
||||
status = 1;
|
||||
goto fail_read;
|
||||
}
|
||||
GuOut* out = gu_file_out(stdout, pool);
|
||||
GuOut* bout = gu_out_buffered(out, pool);
|
||||
// GuWriter* wtr = gu_locale_writer(bout, pool);
|
||||
GuWriter* wtr = gu_new_utf8_writer(bout, pool);
|
||||
GuDump* ctx = gu_new_dump(wtr, NULL, err, pool);
|
||||
gu_dump(gu_type(PgfPGF), pgf, ctx);
|
||||
gu_writer_flush(wtr, err);
|
||||
fail_read:
|
||||
gu_pool_free(pool);
|
||||
return status;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user