initial import of the C runtime

This commit is contained in:
kr.angelov
2012-01-20 13:41:10 +00:00
parent 2fd04e0797
commit a369d3bdac
94 changed files with 14344 additions and 1277 deletions

5
src/runtime/c/AUTHORS Normal file
View File

@@ -0,0 +1,5 @@
Libpgf was written by:
Lauri Alanko <lealanko@ling.helsinki.fi>
Based on the original PGF implementation by Krasimir Angelov.

View 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

File diff suppressed because it is too large Load Diff

View 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>

View File

@@ -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
View 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
View 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>.

View 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
View 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

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1 @@
#include <gu/fun.h>

65
src/runtime/c/gu/fun.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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_

View 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
View 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

View 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}

View 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
View 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
])

View File

@@ -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
View 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))));

View File

@@ -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
View 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
View 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);
}

View File

@@ -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_ */

View 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);
}

View 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;

View File

@@ -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;
}

View File

@@ -1,8 +0,0 @@
#include "panic.h"
#include <stdio.h>
void __pgf_panic(char *msg) {
printf("%s\n",msg);
fflush(stdout);
exit(1);
}

View File

@@ -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
View 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
View 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
View 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
View 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;
}

View File

@@ -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

View File

@@ -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);
}

View 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;
}

View 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;
}