pax_global_header00006660000000000000000000000064133051644350014516gustar00rootroot0000000000000052 comment=870afb94aabca1fb8005f54f423c2777854dd49a postgresql-numeral-1.0/000077500000000000000000000000001330516443500152225ustar00rootroot00000000000000postgresql-numeral-1.0/.gitignore000066400000000000000000000001351330516443500172110ustar00rootroot00000000000000numeral--*.sql regression.diffs regression.out results/ tags *.o *.so *.tab.c *.tab.h *.yy.c postgresql-numeral-1.0/.travis.yml000066400000000000000000000032501330516443500173330ustar00rootroot00000000000000# run the testsuite on travis-ci.com --- # versions to run on env: - PG_SUPPORTED_VERSIONS=9.4 - PG_SUPPORTED_VERSIONS=9.5 - PG_SUPPORTED_VERSIONS=9.6 - PG_SUPPORTED_VERSIONS=10 language: C dist: trusty sudo: required before_install: # apt.postgresql.org is already configured, we just need to add devel - | DIST=trusty-pgdg if [ "$PG_SUPPORTED_VERSIONS" = "10" ]; then # update pgdg-source.list sudo sed -i -e "s/pgdg.*/pgdg-testing main $PG_SUPPORTED_VERSIONS/" /etc/apt/sources.list.d/pgdg*.list DIST=trusty-pgdg-testing fi - sudo apt-get -qq update install: - export DEBIAN_FRONTEND=noninteractive # suppress warnings about deprecated PostgreSQL versions # trusty's pg_buildext doesn't cope with PG version numbers >= 10, so upgrade that to -pgdg - sudo apt-get install bison debhelper devscripts fakeroot flex postgresql-server-dev-$PG_SUPPORTED_VERSIONS postgresql-server-dev-all/$DIST # install PostgreSQL $PG_SUPPORTED_VERSIONS if not there yet - | if [ ! -x /usr/lib/postgresql/$PG_SUPPORTED_VERSIONS/bin/postgres ]; then sudo apt-get install postgresql-common # upgrade pg-common first ... sudo /etc/init.d/postgresql stop # ... so we can stop postgresql again before installing the server sudo apt-get install postgresql-$PG_SUPPORTED_VERSIONS fi # stop the travis-provided cluster - sudo /etc/init.d/postgresql stop - pg_lsclusters - dpkg -l postgresql\* | cat script: - pg_buildext updatecontrol - dpkg-buildpackage -us -uc -rfakeroot - for deb in ../*.deb; do echo "$deb:"; dpkg-deb --info $deb; dpkg-deb --contents $deb; done - sudo debi - pg_buildext -i '--locale=C.UTF-8' installcheck postgresql-numeral-1.0/.vimrc000066400000000000000000000000161330516443500163400ustar00rootroot00000000000000set ts=4 sw=4 postgresql-numeral-1.0/Makefile000066400000000000000000000022611330516443500166630ustar00rootroot00000000000000MODULE_big = numeral OBJS = numeral.o \ numeralfuncs.o numerallexer.yy.o numeralparser.tab.o \ zahlfuncs.o zahllexer.yy.o zahlparser.tab.o \ romanfuncs.o romanlexer.yy.o romanparser.tab.o EXTENSION = numeral DATA_built = numeral--1.sql REGRESS = extension numeral zahl roman operator EXTRA_CLEAN = *.yy.* *.tab.* PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) INCLUDEDIR_SERVER = $(shell $(PG_CONFIG) --includedir-server) FLOAT8PASSBYVAL = $(shell grep FLOAT8PASSBYVAL $(INCLUDEDIR_SERVER)/pg_config.h | grep -o true) ifeq ($(FLOAT8PASSBYVAL),true) PASSEDBYVALUE = passedbyvalue, endif numeral.o: numeral.c numeral.h %.yy.c: %.l flex -o $@ $< numerallexer.yy.o: numeral.h numeralparser.tab.c # actually numeralparser.tab.h zahllexer.yy.o: numeral.h zahlparser.tab.c # actually zahlparser.tab.h romanlexer.yy.o: numeral.h romanparser.tab.c # actually romanparser.tab.h %.tab.c: %.y bison -d $< numeralparser.tab.o: numeral.h zahlparser.tab.o: numeral.h romanparser.tab.o: numeral.h # extension sql %.sql: %.sql.in set -e; \ for type in numeral zahl roman; do \ sed -e "s/@TYPE@/$$type/g" -e "s/@PASSEDBYVALUE@/$(PASSEDBYVALUE)/" < $<; \ done > $@ postgresql-numeral-1.0/NEWS.md000066400000000000000000000000531330516443500163160ustar00rootroot00000000000000v1.0: ------------------ * Initial release postgresql-numeral-1.0/README.md000066400000000000000000000036011330516443500165010ustar00rootroot00000000000000postgresql-numeral ================== Christoph Berg **postgresql-numeral** provides numeric data types for PostgreSQL that use numerals (words instead of digits) for input and output. Data types: * *numeral*: English numerals (one, two, three, four, ...), short scale (10⁹ = billion) * *zahl*: German numerals (eins, zwei, drei, vier, ...), long scale (10⁹ = Milliarde) * *roman*: Roman numerals (I, II, III, IV, ...) Requires PostgreSQL >= 9.4 and Bison 3. [![Build Status](https://travis-ci.org/ChristophBerg/postgresql-numeral.svg?branch=master)](https://travis-ci.org/ChristophBerg/postgresql-numeral) Examples -------- ``` # SELECT 'thirty'::numeral + 'twelve'::numeral as sum; sum ─────────── forty-two # SELECT 'siebzehn'::zahl * 'dreiundzwanzig' AS "Produkt"; Produkt ────────────────────────── dreihunderteinundneunzig # SELECT 'MCMLV'::roman + 'II'::roman * 'XXX' AS futurum; futurum ───────── MMXV ``` Implementation -------------- The data types are internally binary compatible to *bigint*. Casts to and from bigint are defined (to bigint as *implicit*). The module does not implement any operators but instead reuses the existing bigint operators. Effectively, the data types behave like bigint, just with different input/output functions. License ------- Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. postgresql-numeral-1.0/debian/000077500000000000000000000000001330516443500164445ustar00rootroot00000000000000postgresql-numeral-1.0/debian/changelog000066400000000000000000000002341330516443500203150ustar00rootroot00000000000000postgresql-numeral (1.0-1) unstable; urgency=medium * Initial release. -- Christoph Berg Mon, 04 Jun 2018 09:16:54 +0200 postgresql-numeral-1.0/debian/compat000066400000000000000000000000021330516443500176420ustar00rootroot000000000000009 postgresql-numeral-1.0/debian/control000066400000000000000000000016401330516443500200500ustar00rootroot00000000000000Source: postgresql-numeral Section: database Priority: optional Maintainer: Christoph Berg Build-Depends: bison, debhelper (>> 9), flex, postgresql-server-dev-all (>= 153~), Standards-Version: 4.1.4 Vcs-Git: https://github.com/ChristophBerg/postgresql-numeral.git Vcs-Browser: https://github.com/ChristophBerg/postgresql-numeral Homepage: https://github.com/ChristophBerg/postgresql-numeral Package: postgresql-10-numeral Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, postgresql-10 Description: numeral datatypes for PostgreSQL This module provides numeric data types for PostgreSQL that use numerals (words instead of digits) for input and output. . * numeral: English numerals (one, two, three, four, ...), short scale (10^9 = trillion) * zahl: German numerals (eins, zwei, drei, vier, ...), long scale (10^9 = Milliarde) * roman: Roman numerals (I, II, III, IV, ...) postgresql-numeral-1.0/debian/control.in000066400000000000000000000016551330516443500204630ustar00rootroot00000000000000Source: postgresql-numeral Section: database Priority: optional Maintainer: Christoph Berg Build-Depends: bison, debhelper (>> 9), flex, postgresql-server-dev-all (>= 153~), Standards-Version: 4.1.4 Vcs-Git: https://github.com/ChristophBerg/postgresql-numeral.git Vcs-Browser: https://github.com/ChristophBerg/postgresql-numeral Homepage: https://github.com/ChristophBerg/postgresql-numeral Package: postgresql-PGVERSION-numeral Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, postgresql-PGVERSION Description: numeral datatypes for PostgreSQL This module provides numeric data types for PostgreSQL that use numerals (words instead of digits) for input and output. . * numeral: English numerals (one, two, three, four, ...), short scale (10^9 = trillion) * zahl: German numerals (eins, zwei, drei, vier, ...), long scale (10^9 = Milliarde) * roman: Roman numerals (I, II, III, IV, ...) postgresql-numeral-1.0/debian/copyright000066400000000000000000000013421330516443500203770ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Files: * Copyright: (C) 2017 Christoph Berg License: GPL-2+ License: GPL-2+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . This program 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 General Public License for more details. . The full text of the GPL is distributed as in /usr/share/common-licenses/GPL on Debian systems. postgresql-numeral-1.0/debian/pgversions000066400000000000000000000000051330516443500205610ustar00rootroot000000000000009.4+ postgresql-numeral-1.0/debian/rules000077500000000000000000000006321330516443500175250ustar00rootroot00000000000000#!/usr/bin/make -f include /usr/share/postgresql-common/pgxs_debian_control.mk override_dh_auto_build: +pg_buildext build build-%v override_dh_auto_test: # nothing to do here, see debian/tests/* instead override_dh_auto_install: +pg_buildext install build-%v postgresql-%v-numeral override_dh_installdocs: dh_installdocs --all README.* override_dh_auto_clean: +pg_buildext clean build-%v %: dh $@ postgresql-numeral-1.0/debian/source/000077500000000000000000000000001330516443500177445ustar00rootroot00000000000000postgresql-numeral-1.0/debian/source/format000066400000000000000000000000141330516443500211520ustar00rootroot000000000000003.0 (quilt) postgresql-numeral-1.0/debian/source/lintian-overrides000066400000000000000000000001751330516443500233300ustar00rootroot00000000000000# don't bug people uploading from @work source: changelog-should-mention-nmu source: source-nmu-has-incorrect-version-number postgresql-numeral-1.0/debian/tests/000077500000000000000000000000001330516443500176065ustar00rootroot00000000000000postgresql-numeral-1.0/debian/tests/control000066400000000000000000000001251330516443500212070ustar00rootroot00000000000000Depends: @, postgresql-server-dev-all Tests: installcheck Restrictions: allow-stderr postgresql-numeral-1.0/debian/tests/installcheck000077500000000000000000000000711330516443500221760ustar00rootroot00000000000000#!/bin/sh pg_buildext -i '--locale=C.UTF-8' installcheck postgresql-numeral-1.0/debian/watch000066400000000000000000000001271330516443500174750ustar00rootroot00000000000000version=4 https://github.com/ChristophBerg/postgresql-numeral/releases .*/v(.*).tar.gz postgresql-numeral-1.0/do000077500000000000000000000012161330516443500155520ustar00rootroot00000000000000#!/bin/bash set -eux export PGDATABASE=postgres for PGVERSION in ${*:-10 9.6 9.5 9.4}; do echo echo "### $PGVERSION ###" PG_CONFIG=/usr/lib/postgresql/$PGVERSION/bin/pg_config export PGCLUSTER="$PGVERSION/main" export PGPORT="54${PGVERSION/./}" [ "$PGVERSION" = "9.6" ] && unset PGPORT # default version make clean make PG_CONFIG=$PG_CONFIG PROFILE="-Werror" sudo make install PG_CONFIG=$PG_CONFIG psql -c "DROP EXTENSION IF EXISTS numeral CASCADE" #psql -c "CREATE EXTENSION IF NOT EXISTS numeral" if ! make installcheck REGRESS_OPTS="--use-existing --dbname=postgres" PG_CONFIG=$PG_CONFIG; then cat regression.diffs exit 1 fi done postgresql-numeral-1.0/expected/000077500000000000000000000000001330516443500170235ustar00rootroot00000000000000postgresql-numeral-1.0/expected/extension.out000066400000000000000000000001461330516443500215710ustar00rootroot00000000000000SET client_min_messages = warning; CREATE EXTENSION IF NOT EXISTS numeral; RESET client_min_messages; postgresql-numeral-1.0/expected/numeral.out000066400000000000000000000131571330516443500212260ustar00rootroot00000000000000SELECT '0'::numeral; numeral --------- null (1 row) SELECT '1'::numeral; numeral --------- one (1 row) SELECT '-2'::numeral; numeral ----------- minus two (1 row) SELECT 'zero'::numeral; numeral --------- null (1 row) SELECT 'one'::numeral; numeral --------- one (1 row) SELECT 'minus one'::numeral; numeral ----------- minus one (1 row) SELECT 'two'::numeral; numeral --------- two (1 row) SELECT 'three'::numeral; numeral --------- three (1 row) SELECT 'four'::numeral; numeral --------- four (1 row) SELECT 'five'::numeral; numeral --------- five (1 row) SELECT 'six'::numeral; numeral --------- six (1 row) SELECT 'seven'::numeral; numeral --------- seven (1 row) SELECT 'eight'::numeral; numeral --------- eight (1 row) SELECT 'nine'::numeral; numeral --------- nine (1 row) SELECT 'ten'::numeral; numeral --------- ten (1 row) SELECT 'eleven'::numeral; numeral --------- eleven (1 row) SELECT 'twelve'::numeral; numeral --------- twelve (1 row) SELECT 'thirteen'::numeral; numeral ---------- thirteen (1 row) SELECT 'fourteen'::numeral; numeral ---------- fourteen (1 row) SELECT 'fifteen'::numeral; numeral --------- fifteen (1 row) SELECT 'sixteen'::numeral; numeral --------- sixteen (1 row) SELECT 'seventeen'::numeral; numeral ----------- seventeen (1 row) SELECT 'eighteen'::numeral; numeral ---------- eighteen (1 row) SELECT 'nineteen'::numeral; numeral ---------- nineteen (1 row) SELECT 'twenty'::numeral; numeral --------- twenty (1 row) SELECT 'twenty-one'::numeral; numeral ------------ twenty-one (1 row) SELECT 'thirty'::numeral; numeral --------- thirty (1 row) SELECT 'thirty-two'::numeral; numeral ------------ thirty-two (1 row) SELECT 'forty'::numeral; numeral --------- forty (1 row) SELECT 'fifty'::numeral; numeral --------- fifty (1 row) SELECT 'sixty'::numeral; numeral --------- sixty (1 row) SELECT 'seventy'::numeral; numeral --------- seventy (1 row) SELECT 'eighty'::numeral; numeral --------- eighty (1 row) SELECT 'ninety'::numeral; numeral --------- ninety (1 row) SELECT 'hundred'::numeral; numeral ------------- one hundred (1 row) SELECT 'one hundred'::numeral; numeral ------------- one hundred (1 row) SELECT 'hundred one'::numeral; numeral ----------------- one hundred one (1 row) SELECT 'one hundred one'::numeral; numeral ----------------- one hundred one (1 row) SELECT 'one hundred twenty-one'::numeral; numeral ------------------------ one hundred twenty-one (1 row) SELECT 'two hundred'::numeral; numeral ------------- two hundred (1 row) SELECT 'two hundred one'::numeral; numeral ----------------- two hundred one (1 row) SELECT 'two hundred thirty-two'::numeral; numeral ------------------------ two hundred thirty-two (1 row) SELECT 'thousand'::numeral; numeral -------------- one thousand (1 row) SELECT 'one thousand'::numeral; numeral -------------- one thousand (1 row) SELECT 'one thousand one'::numeral; numeral ------------------ one thousand one (1 row) SELECT 'thousand one hundred'::numeral; numeral -------------------------- one thousand one hundred (1 row) SELECT 'one thousand one hundred'::numeral; numeral -------------------------- one thousand one hundred (1 row) SELECT 'eleven hundred'::numeral; numeral -------------------------- one thousand one hundred (1 row) SELECT 'one thousand one hundred one'::numeral; numeral ------------------------------ one thousand one hundred one (1 row) SELECT 'hundred thousand'::numeral; numeral ---------------------- one hundred thousand (1 row) SELECT 'one hundred thousand'::numeral; numeral ---------------------- one hundred thousand (1 row) SELECT 'one hundred one thousand'::numeral; numeral -------------------------- one hundred one thousand (1 row) SELECT 'two hundred thousand'::numeral; numeral ---------------------- two hundred thousand (1 row) SELECT 'one million'::numeral; numeral ------------- one million (1 row) SELECT 'one million one'::numeral; numeral ----------------- one million one (1 row) SELECT 'one million one hundred'::numeral; numeral ------------------------- one million one hundred (1 row) SELECT 'one million one thousand'::numeral; numeral -------------------------- one million one thousand (1 row) SELECT 'one million one hundred thousand'::numeral; numeral ---------------------------------- one million one hundred thousand (1 row) SELECT 'two million'::numeral; numeral ------------- two million (1 row) SELECT 'hundred million'::numeral; numeral --------------------- one hundred million (1 row) SELECT 'one billion'::numeral; numeral ------------- one billion (1 row) SELECT 'one billion one million'::numeral; numeral ------------------------- one billion one million (1 row) SELECT 'one billion one million one thousand'::numeral; numeral -------------------------------------- one billion one million one thousand (1 row) SELECT 'billion'::numeral; numeral ------------- one billion (1 row) SELECT 'trillion'::numeral; numeral -------------- one trillion (1 row) SELECT 'quadrillion'::numeral; numeral ----------------- one quadrillion (1 row) SELECT 'quintillion'::numeral; numeral ----------------- one quintillion (1 row) postgresql-numeral-1.0/expected/operator.out000066400000000000000000000023041330516443500214060ustar00rootroot00000000000000SELECT 'vier'::zahl::bigint; int8 ------ 4 (1 row) SELECT 'hundert'::zahl = 'einhundert'; ?column? ---------- t (1 row) SELECT 'drei'::zahl <> 'vier'; ?column? ---------- t (1 row) SELECT 'zehn'::zahl < 'elf'; ?column? ---------- t (1 row) SELECT 'acht'::zahl > 'sieben'; ?column? ---------- t (1 row) SELECT 'neun'::zahl <= 'neun'; ?column? ---------- t (1 row) SELECT 'eins'::zahl >= 'null'; ?column? ---------- t (1 row) SELECT 'zehn'::zahl % 'drei'; ?column? ---------- eins (1 row) SELECT 'acht'::zahl + 'vier'; ?column? ---------- zwölf (1 row) SELECT 'neun'::zahl - 'vier'; ?column? ---------- fünf (1 row) SELECT 'fünf'::zahl * 'vier'; ?column? ---------- zwanzig (1 row) SELECT 'zwölf'::zahl / 'drei'; ?column? ---------- vier (1 row) SELECT 'zehn'::zahl & 'sechs'; ?column? ---------- zwei (1 row) SELECT 'zwei'::zahl | 'vier'; ?column? ---------- sechs (1 row) SELECT @ 'minus drei'::zahl; ?column? ---------- drei (1 row) SELECT - 'vier'::zahl; ?column? ------------ minus vier (1 row) SELECT ~ 'fünfzehn'::zahl; ?column? ---------------- minus sechzehn (1 row) SELECT + 'sechs'::zahl; ?column? ---------- sechs (1 row) postgresql-numeral-1.0/expected/roman.out000066400000000000000000000044571330516443500207020ustar00rootroot00000000000000SELECT '0'::roman; roman ------- nulla (1 row) SELECT '1'::roman; roman ------- I (1 row) SELECT '-2'::roman; roman ---------- minus II (1 row) SELECT 'nulla'::roman; roman ------- nulla (1 row) SELECT 'minus nulla'::roman; roman ------- nulla (1 row) SELECT 'I'::roman, 'i'::roman; roman | roman -------+------- I | I (1 row) SELECT 'V'::roman, 'v'::roman; roman | roman -------+------- V | V (1 row) SELECT 'X'::roman, 'x'::roman; roman | roman -------+------- X | X (1 row) SELECT 'L'::roman, 'l'::roman; roman | roman -------+------- L | L (1 row) SELECT 'C'::roman, 'c'::roman; roman | roman -------+------- C | C (1 row) SELECT 'D'::roman, 'd'::roman; roman | roman -------+------- D | D (1 row) SELECT 'M'::roman, 'm'::roman; roman | roman -------+------- M | M (1 row) SELECT 'minus I'::roman; roman --------- minus I (1 row) SELECT 'minus 1'::roman; roman --------- minus I (1 row) SELECT 'II'::roman; roman ------- II (1 row) SELECT 'III'::roman; roman ------- III (1 row) SELECT 'IV'::roman; roman ------- IV (1 row) SELECT 'V'::roman; roman ------- V (1 row) SELECT 'VI'::roman; roman ------- VI (1 row) SELECT 'IX'::roman; roman ------- IX (1 row) SELECT 'XI'::roman; roman ------- XI (1 row) SELECT 'XX'::roman; roman ------- XX (1 row) SELECT 'IL'::roman; roman ------- XLIX (1 row) SELECT 'LI'::roman; roman ------- LI (1 row) SELECT 'VL'::roman; roman ------- XLV (1 row) SELECT 'LV'::roman; roman ------- LV (1 row) SELECT 'XL'::roman; roman ------- XL (1 row) SELECT 'LX'::roman; roman ------- LX (1 row) SELECT 'XC'::roman; roman ------- XC (1 row) SELECT 'CX'::roman; roman ------- CX (1 row) SELECT 'CC'::roman; roman ------- CC (1 row) SELECT 'CD'::roman; roman ------- CD (1 row) SELECT 'DC'::roman; roman ------- DC (1 row) SELECT 'CM'::roman; roman ------- CM (1 row) SELECT 'MC'::roman; roman ------- MC (1 row) SELECT 'MM'::roman; roman ------- MM (1 row) SELECT '10000'::roman; roman ------------ MMMMMMMMMM (1 row) SELECT '-10000'::roman; roman ------------------ minus MMMMMMMMMM (1 row) SELECT '10001'::roman; roman ------- 10001 (1 row) SELECT '-10001'::roman; roman ------------- minus 10001 (1 row) postgresql-numeral-1.0/expected/zahl.out000066400000000000000000000123671330516443500205230ustar00rootroot00000000000000SELECT '0'::zahl; zahl ------ null (1 row) SELECT '1'::zahl; zahl ------ eins (1 row) SELECT '-2'::zahl; zahl ------------ minus zwei (1 row) SELECT 'null'::zahl; zahl ------ null (1 row) SELECT 'eins'::zahl; zahl ------ eins (1 row) SELECT 'minus eins'::zahl; zahl ------------ minus eins (1 row) SELECT 'zwei'::zahl; zahl ------ zwei (1 row) SELECT 'drei'::zahl; zahl ------ drei (1 row) SELECT 'vier'::zahl; zahl ------ vier (1 row) SELECT 'fünf'::zahl; zahl ------ fünf (1 row) SELECT 'sechs'::zahl; zahl ------- sechs (1 row) SELECT 'sieben'::zahl; zahl -------- sieben (1 row) SELECT 'acht'::zahl; zahl ------ acht (1 row) SELECT 'neun'::zahl; zahl ------ neun (1 row) SELECT 'zehn'::zahl; zahl ------ zehn (1 row) SELECT 'elf'::zahl; zahl ------ elf (1 row) SELECT 'zwölf'::zahl; zahl ------- zwölf (1 row) SELECT 'dreizehn'::zahl; zahl ---------- dreizehn (1 row) SELECT 'vierzehn'::zahl; zahl ---------- vierzehn (1 row) SELECT 'fünfzehn'::zahl; zahl ---------- fünfzehn (1 row) SELECT 'sechzehn'::zahl; zahl ---------- sechzehn (1 row) SELECT 'siebzehn'::zahl; zahl ---------- siebzehn (1 row) SELECT 'achtzehn'::zahl; zahl ---------- achtzehn (1 row) SELECT 'neunzehn'::zahl; zahl ---------- neunzehn (1 row) SELECT 'zwanzig'::zahl; zahl --------- zwanzig (1 row) SELECT 'einundzwanzig'::zahl; zahl --------------- einundzwanzig (1 row) SELECT 'dreißig'::zahl; zahl --------- dreißig (1 row) SELECT 'zweiunddreißig'::zahl; zahl ---------------- zweiunddreißig (1 row) SELECT 'vierzig'::zahl; zahl --------- vierzig (1 row) SELECT 'fünfzig'::zahl; zahl --------- fünfzig (1 row) SELECT 'sechzig'::zahl; zahl --------- sechzig (1 row) SELECT 'siebzig'::zahl; zahl --------- siebzig (1 row) SELECT 'achtzig'::zahl; zahl --------- achtzig (1 row) SELECT 'neunzig'::zahl; zahl --------- neunzig (1 row) SELECT 'hundert'::zahl; zahl ------------ einhundert (1 row) SELECT 'einhundert'::zahl; zahl ------------ einhundert (1 row) SELECT 'hunderteins'::zahl; zahl ---------------- einhunderteins (1 row) SELECT 'einhunderteins'::zahl; zahl ---------------- einhunderteins (1 row) SELECT 'einhunderteinundzwanzig'::zahl; zahl ------------------------- einhunderteinundzwanzig (1 row) SELECT 'zweihundert'::zahl; zahl ------------- zweihundert (1 row) SELECT 'zweihunderteins'::zahl; zahl ----------------- zweihunderteins (1 row) SELECT 'zweihundertzweiunddreißig'::zahl; zahl --------------------------- zweihundertzweiunddreißig (1 row) SELECT 'tausend'::zahl; zahl ------------ eintausend (1 row) SELECT 'eintausend'::zahl; zahl ------------ eintausend (1 row) SELECT 'eintausendeins'::zahl; zahl ---------------- eintausendeins (1 row) SELECT 'tausendeinhundert'::zahl; zahl ---------------------- eintausendeinhundert (1 row) SELECT 'eintausendeinhundert'::zahl; zahl ---------------------- eintausendeinhundert (1 row) SELECT 'elfhundert'::zahl; zahl ---------------------- eintausendeinhundert (1 row) SELECT 'eintausendeinhunderteins'::zahl; zahl -------------------------- eintausendeinhunderteins (1 row) SELECT 'hunderttausend'::zahl; zahl ------------------- einhunderttausend (1 row) SELECT 'einhunderttausend'::zahl; zahl ------------------- einhunderttausend (1 row) SELECT 'einhunderteintausend'::zahl; zahl ---------------------- einhunderteintausend (1 row) SELECT 'zweihunderttausend'::zahl; zahl -------------------- zweihunderttausend (1 row) SELECT 'eine Million'::zahl; zahl -------------- eine Million (1 row) SELECT 'eine Million eins'::zahl; zahl ------------------- eine Million eins (1 row) SELECT 'eine Million einhundert'::zahl; zahl ------------------------- eine Million einhundert (1 row) SELECT 'eine Million eintausend'::zahl; zahl ------------------------- eine Million eintausend (1 row) SELECT 'eine Million einhunderttausend'::zahl; zahl -------------------------------- eine Million einhunderttausend (1 row) SELECT 'zwei Millionen'::zahl; zahl ---------------- zwei Millionen (1 row) SELECT 'hundert Millionen'::zahl; zahl ---------------------- einhundert Millionen (1 row) SELECT 'eine Milliarde'::zahl; zahl ---------------- eine Milliarde (1 row) SELECT 'eine Milliarde eine Million'::zahl; zahl ----------------------------- eine Milliarde eine Million (1 row) SELECT 'eine Milliarde eine Million eintausend'::zahl; zahl ---------------------------------------- eine Milliarde eine Million eintausend (1 row) SELECT 'Billion'::zahl; zahl -------------- eine Billion (1 row) SELECT 'Billiarde'::zahl; zahl ---------------- eine Billiarde (1 row) SELECT 'Trillion'::zahl; zahl --------------- eine Trillion (1 row) postgresql-numeral-1.0/numeral--1.sql.in000066400000000000000000000116511330516443500202320ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ -- type definition CREATE TYPE @TYPE@; CREATE OR REPLACE FUNCTION @TYPE@_in(cstring) RETURNS @TYPE@ AS '$libdir/numeral' LANGUAGE C IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION @TYPE@_out(@TYPE@) RETURNS cstring AS '$libdir/numeral' LANGUAGE C IMMUTABLE STRICT; CREATE TYPE @TYPE@ ( internallength = 8, input = @TYPE@_in, output = @TYPE@_out, @PASSEDBYVALUE@ alignment = double, category = 'N' ); /* @TYPE@ is as good as bigint, make casts */ CREATE CAST (@TYPE@ AS bigint) WITHOUT FUNCTION AS IMPLICIT; CREATE CAST (bigint AS @TYPE@) WITHOUT FUNCTION; /* steal bigint's operators */ CREATE FUNCTION @TYPE@eq(@TYPE@, @TYPE@) RETURNS boolean AS 'int8eq' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR = ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@eq, commutator = =, negator = <>, restrict = eqsel, join = eqjoinsel, hashes, merges ); CREATE FUNCTION @TYPE@ne(@TYPE@, @TYPE@) RETURNS boolean AS 'int8ne' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR <> ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@ne, commutator = <>, negator = =, restrict = neqsel, join = neqjoinsel ); CREATE FUNCTION @TYPE@lt(@TYPE@, @TYPE@) RETURNS boolean AS 'int8lt' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR < ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@lt, commutator = >, negator = >=, restrict = scalarltsel, join = scalarltjoinsel ); CREATE FUNCTION @TYPE@gt(@TYPE@, @TYPE@) RETURNS boolean AS 'int8gt' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR > ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@gt, commutator = <, negator = <=, restrict = scalargtsel, join = scalargtjoinsel ); CREATE FUNCTION @TYPE@le(@TYPE@, @TYPE@) RETURNS boolean AS 'int8le' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR <= ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@le, commutator = >=, negator = >, restrict = scalarltsel, join = scalarltjoinsel ); CREATE FUNCTION @TYPE@ge(@TYPE@, @TYPE@) RETURNS boolean AS 'int8ge' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR >= ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@ge, commutator = <=, negator = <, restrict = scalargtsel, join = scalargtjoinsel ); CREATE FUNCTION @TYPE@mod(@TYPE@, @TYPE@) RETURNS @TYPE@ AS 'int8mod' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR % ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@mod ); CREATE FUNCTION @TYPE@pl(@TYPE@, @TYPE@) RETURNS @TYPE@ AS 'int8pl' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR + ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@pl, commutator = + ); CREATE FUNCTION @TYPE@mi(@TYPE@, @TYPE@) RETURNS @TYPE@ AS 'int8mi' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR - ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@mi ); CREATE FUNCTION @TYPE@mul(@TYPE@, @TYPE@) RETURNS @TYPE@ AS 'int8mul' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR * ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@mul, commutator = * ); CREATE FUNCTION @TYPE@div(@TYPE@, @TYPE@) RETURNS @TYPE@ AS 'int8div' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR / ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@div ); CREATE FUNCTION @TYPE@and(@TYPE@, @TYPE@) RETURNS @TYPE@ AS 'int8and' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR & ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@and, commutator = & ); CREATE FUNCTION @TYPE@or(@TYPE@, @TYPE@) RETURNS @TYPE@ AS 'int8or' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR | ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@or, commutator = | ); CREATE FUNCTION @TYPE@xor(@TYPE@, @TYPE@) RETURNS @TYPE@ AS 'int8xor' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR # ( leftarg = @TYPE@, rightarg = @TYPE@, procedure = @TYPE@xor, commutator = # ); CREATE FUNCTION @TYPE@abs(@TYPE@) RETURNS @TYPE@ AS 'int8abs' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR @ ( rightarg = @TYPE@, procedure = @TYPE@abs ); CREATE FUNCTION @TYPE@um(@TYPE@) RETURNS @TYPE@ AS 'int8um' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR - ( rightarg = @TYPE@, procedure = @TYPE@um ); CREATE FUNCTION @TYPE@not(@TYPE@) RETURNS @TYPE@ AS 'int8not' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR ~ ( rightarg = @TYPE@, procedure = @TYPE@not ); CREATE FUNCTION @TYPE@up(@TYPE@) RETURNS @TYPE@ AS 'int8up' LANGUAGE internal IMMUTABLE STRICT; CREATE OPERATOR + ( rightarg = @TYPE@, procedure = @TYPE@up ); postgresql-numeral-1.0/numeral.c000066400000000000000000000012141330516443500170270ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ #include "postgres.h" #include "fmgr.h" /* module initialization */ PG_MODULE_MAGIC; void _PG_init(void); void _PG_init(void) { } postgresql-numeral-1.0/numeral.control000066400000000000000000000001211330516443500202610ustar00rootroot00000000000000default_version = '1' comment = 'numeral datatypes extension' relocatable = true postgresql-numeral-1.0/numeral.h000066400000000000000000000005561330516443500170440ustar00rootroot00000000000000/* type defs */ typedef long long Numeral; typedef long long Zahl; typedef long long Roman; /* prototypes */ const char *numeral_cstring (Numeral numeral); int numeral_parse (char *s, Numeral *numeral); const char *zahl_cstring (Zahl zahl); int zahl_parse (char *s, Zahl *zahl); const char *roman_cstring (Roman roman); int roman_parse (char *s, Roman *roman); postgresql-numeral-1.0/numeralfuncs.c000066400000000000000000000073361330516443500201010ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ #include "postgres.h" #include "fmgr.h" #include "numeral.h" /* input and output */ char *yyerrstr; /* copy of error catched by yynumeralerror() */ void yynumeralerror (char *s); void yynumeralerror (char *s) { /* store error for later use in number_in */ yyerrstr = pstrdup(s); } PG_FUNCTION_INFO_V1(numeral_in); Datum numeral_in (PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); Numeral numeral; if (numeral_parse(str, &numeral) > 0) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type numeral: \"%s\", %s", str, yyerrstr))); PG_RETURN_INT64(numeral); } PG_FUNCTION_INFO_V1(numeral_out); Datum numeral_out(PG_FUNCTION_ARGS) { Numeral numeral = PG_GETARG_INT64(0); PG_RETURN_CSTRING(numeral_cstring(numeral)); } /* format German numerals */ static const char *numeral_one[] = { "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", }; static const char *numeral_ten[] = { "", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety", }; static const char * numeral_x (Numeral numeral) { Assert (numeral >= 0 && numeral < 20); return numeral_one[numeral]; } static const char * numeral_xx (Numeral numeral) { if (numeral < 20) { return numeral_x(numeral); /* returns empty for 0 */ } if (numeral % 10 == 0) return numeral_ten[numeral / 10]; return psprintf("%s-%s", numeral_ten[numeral / 10], numeral_x(numeral % 10)); } static const char * numeral_xxx (Numeral numeral) { if (numeral < 100) return numeral_xx(numeral); if (numeral % 100 == 0) return psprintf("%s hundred", numeral_x(numeral / 100)); return psprintf("%s hundred %s", numeral_x(numeral / 100), numeral_xx(numeral % 100)); } static const char * numeral_xxxxxx (Numeral numeral) { if (numeral < 1000) return numeral_xxx(numeral); if (numeral % 1000 == 0) return psprintf("%s thousand", numeral_xxx(numeral / 1000)); return psprintf("%s thousand %s", numeral_xxx(numeral / 1000), numeral_xxx(numeral % 1000)); } struct zillions { long long value; const char *name; } static zillions[] = { { 1000000000000000000, "quintillion" }, { 1000000000000000, "quadrillion" }, { 1000000000000, "trillion" }, { 1000000000, "billion" }, { 1000000, "million" }, { 0 }, }; static const char * numeral_zillion (Numeral numeral) { struct zillions *z; char *result = palloc0(1000); for (z = zillions; z->value; z++) if (numeral >= z->value) { int n = numeral / z->value; if (*result) strlcat (result, " ", 1000); strlcat (result, numeral_xxx(n), 1000); strlcat (result, " ", 1000); strlcat (result, z->name, 1000); numeral = numeral % z->value; } if (numeral > 0) { if (*result) strlcat (result, " ", 1000); strlcat (result, numeral_xxxxxx(numeral), 1000); } return result; } const char * numeral_cstring (Numeral numeral) { if (numeral < 0) { return psprintf("minus %s", numeral_cstring(-numeral)); } else if (numeral == 0) { return "null"; } return numeral_zillion(numeral); } postgresql-numeral-1.0/numerallexer.l000066400000000000000000000050071330516443500201040ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ %{ #include "numeral.h" #include "numeralparser.tab.h" %} %option prefix="yynumeral" %option noyywrap %option nounput %option noinput INT_R [\-+]?[0-9]+ %% {INT_R} { yynumerallval = atoll(yytext); return INT; } minus { return MINUS; } zero { return ZERO; } and { return AND; } - { return DASH; } one { yynumerallval = 1; return ONE; } two { yynumerallval = 2; return ONE; } three { yynumerallval = 3; return ONE; } four { yynumerallval = 4; return ONE; } five { yynumerallval = 5; return ONE; } six { yynumerallval = 6; return ONE; } seven { yynumerallval = 7; return ONE; } eight { yynumerallval = 8; return ONE; } nine { yynumerallval = 9; return ONE; } ten { yynumerallval = 10; return ONE; } eleven { yynumerallval = 11; return ONE; } twelve { yynumerallval = 12; return ONE; } thirteen { yynumerallval = 13; return ONE; } fourteen { yynumerallval = 14; return ONE; } fifteen { yynumerallval = 15; return ONE; } sixteen { yynumerallval = 16; return ONE; } seventeen { yynumerallval = 17; return ONE; } eighteen { yynumerallval = 18; return ONE; } nineteen { yynumerallval = 19; return ONE; } twenty { yynumerallval = 20; return TEN; } thirty { yynumerallval = 30; return TEN; } forty { yynumerallval = 40; return TEN; } fifty { yynumerallval = 50; return TEN; } sixty { yynumerallval = 60; return TEN; } seventy { yynumerallval = 70; return TEN; } eighty { yynumerallval = 80; return TEN; } ninety { yynumerallval = 90; return TEN; } hundred { yynumerallval = 100; return HUNDRED; } thousand { yynumerallval = 1000; return THOUSAND; } millions? { yynumerallval = 1000000; return ZILLION; } billions? { yynumerallval = 1000000000; return ZILLION; } trillions? { yynumerallval = 1000000000000; return ZILLION; } quadrillions? { yynumerallval = 1000000000000000; return ZILLION; } quintillions? { yynumerallval = 1000000000000000000; return ZILLION; } [ \t\n]* /* eat whitespace */ . return ERR; postgresql-numeral-1.0/numeralparser.y000066400000000000000000000045311330516443500202770ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ %{ #include /* bzero */ #include "numeral.h" /* flex/bison prototypes */ int yynumerallex (void); struct yynumeral_buffer_state *yynumeral_scan_string(char *str); void yynumeral_delete_buffer(struct yynumeral_buffer_state *buffer); void yynumeralerror (char const *s); static Numeral *numeral_parse_result; /* parsing result gets stored here */ %} %define parse.error verbose %define api.prefix {yynumeral} %define api.value.type {Numeral} %token INT %token MINUS %token ZERO %token AND %token DASH %token ONE %token TEN %token HUNDRED %token THOUSAND %token ZILLION %token ERR %% input: /* parser entry */ INT { *numeral_parse_result = $1; } | ZERO { *numeral_parse_result = 0; } | expr { *numeral_parse_result = $1; } | MINUS expr { *numeral_parse_result = -$2; } ; expr: /* general number */ xxxxxx | prefix_xxx ZILLION maybe_and expr_tail { $$ = $1 * $2 + $4; } ; expr_tail: /* tail of general number */ %empty { $$ = 0; } | xxxxxx | prefix_xxx ZILLION maybe_and expr_tail { $$ = $1 * $2 + $4; } xxxxxx: /* 6-digit number */ xxx | prefix_xxx THOUSAND maybe_and suffix_xxx { $$ = $1 * $2 + $4; } ; prefix_xxx: /* N-thousand */ %empty { $$ = 1; } | xxx ; suffix_xxx: /* thousand-and-N */ %empty { $$ = 0; } | xxx ; xxx: /* 3-digit number */ xx | prefix_x HUNDRED maybe_and suffix_xx { $$ = $1 * $2 + $4; } ; xx: /* 2-digit number */ ONE | TEN | TEN DASH ONE { $$ = $1 + $3; } ; prefix_x: /* N-hundred */ %empty { $$ = 1; } | ONE ; suffix_xx: /* hundred-and-N */ %empty { $$ = 0; } | xx ; maybe_and: %empty | AND ; %% /* parse a given string and return the result via the second argument */ int numeral_parse (char *s, Numeral *numeral) { struct yynumeral_buffer_state *buf; int ret; numeral_parse_result = numeral; buf = yynumeral_scan_string(s); ret = yynumeralparse(); yynumeral_delete_buffer(buf); return ret; } postgresql-numeral-1.0/romanfuncs.c000066400000000000000000000036231330516443500175450ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ #include "postgres.h" #include "fmgr.h" #include "numeral.h" /* input and output */ char *yyerrstr; /* copy of error catched by yyromanerror() */ void yyromanerror (char *s); void yyromanerror (char *s) { /* store error for later use in number_in */ yyerrstr = pstrdup(s); } PG_FUNCTION_INFO_V1(roman_in); Datum roman_in (PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); Roman roman; if (roman_parse(str, &roman) > 0) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type roman: \"%s\", %s", str, yyerrstr))); PG_RETURN_INT64(roman); } PG_FUNCTION_INFO_V1(roman_out); Datum roman_out(PG_FUNCTION_ARGS) { Roman roman = PG_GETARG_INT64(0); PG_RETURN_CSTRING(roman_cstring(roman)); } /* format roman numerals */ static char * romanize (Roman roman) { int val[] = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }; char *rom[] = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" }; char res[1000] = ""; int i; for (i = 0; i < 13; i++) { while (val[i] <= roman) { strlcat(res, rom[i], 1000); roman -= val[i]; } } return pstrdup(res); } const char * roman_cstring (Roman roman) { if (roman < 0) { return psprintf("minus %s", roman_cstring(-roman)); } else if (roman == 0) { return "nulla"; } else if (roman > 10000) { return psprintf("%lld", roman); } return romanize(roman); } postgresql-numeral-1.0/romanlexer.l000066400000000000000000000021451330516443500175550ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ %{ #include "numeral.h" #include "romanparser.tab.h" %} %option prefix="yyroman" %option noyywrap %option nounput %option noinput INT_R [\-+]?[0-9]+ %% {INT_R} { yyromanlval = atoll(yytext); return INT; } nulla { yyromanlval = 0; return ZERO; } minus { yyromanlval = -1; return MINUS; } [iI] { yyromanlval = 1; return I; } [vV] { yyromanlval = 5; return V; } [xX] { yyromanlval = 10; return X; } [lL] { yyromanlval = 50; return L; } [cC] { yyromanlval = 100; return C; } [dD] { yyromanlval = 500; return D; } [mM] { yyromanlval = 1000; return M; } [ \t\n]* /* eat whitespace */ . return ERR; postgresql-numeral-1.0/romanparser.y000066400000000000000000000050531330516443500177500ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ %{ #include /* bzero */ #include "numeral.h" /* flex/bison prototypes */ int yyromanlex (void); struct yyroman_buffer_state *yyroman_scan_string(char *str); void yyroman_delete_buffer(struct yyroman_buffer_state *buffer); void yyromanerror (char const *s); static Roman *numeral_parse_result; /* parsing result gets stored here */ %} %define parse.error verbose %define api.prefix {yyroman} %define api.value.type {Roman} %token INT %token ZERO %token MINUS %token I %token V %token X %token L %token C %token D %token M %token ERR %% input: /* parser entry */ maybe_minus INT { *numeral_parse_result = $1 * $2; } | maybe_minus ZERO { *numeral_parse_result = 0; } | maybe_minus expr { *numeral_parse_result = $1 * $2; } ; maybe_minus: %empty { $$ = 1; } | MINUS; expr: max_c M max_m { $$ = $2 - $1 + $3; } | max_c D max_c { $$ = $2 - $1 + $3; } | max_x C max_c { $$ = $2 - $1 + $3; } | max_x L max_x { $$ = $2 - $1 + $3; } | max_i X max_x { $$ = $2 - $1 + $3; } | max_i V max_i { $$ = $2 - $1 + $3; } | I max_i { $$ = $1 + $2; } ; max_m: %empty { $$ = 0; } | max_c M max_m { $$ = $2 - $1 + $3; } | max_c D max_c { $$ = $2 - $1 + $3; } | max_x C max_c { $$ = $2 - $1 + $3; } | max_x L max_x { $$ = $2 - $1 + $3; } | max_i X max_x { $$ = $2 - $1 + $3; } | max_i V max_i { $$ = $2 - $1 + $3; } | I max_i { $$ = $1 + $2; } ; max_c: %empty { $$ = 0; } | max_x C max_c { $$ = $2 - $1 + $3; } | max_x L max_x { $$ = $2 - $1 + $3; } | max_i X max_x { $$ = $2 - $1 + $3; } | max_i V max_i { $$ = $2 - $1 + $3; } | I max_i { $$ = $1 + $2; } ; max_x: %empty { $$ = 0; } | max_i X max_x { $$ = $2 - $1 + $3; } | max_i V max_i { $$ = $2 - $1 + $3; } | I max_i { $$ = $1 + $2; } ; max_i: %empty { $$ = 0; } | I max_i { $$ = $1 + $2; } ; %% /* parse a given string and return the result via the second argument */ int roman_parse (char *s, Roman *roman) { struct yyroman_buffer_state *buf; int ret; numeral_parse_result = roman; buf = yyroman_scan_string(s); ret = yyromanparse(); yyroman_delete_buffer(buf); return ret; } postgresql-numeral-1.0/sql/000077500000000000000000000000001330516443500160215ustar00rootroot00000000000000postgresql-numeral-1.0/sql/extension.sql000066400000000000000000000001461330516443500205570ustar00rootroot00000000000000SET client_min_messages = warning; CREATE EXTENSION IF NOT EXISTS numeral; RESET client_min_messages; postgresql-numeral-1.0/sql/numeral.sql000066400000000000000000000040571330516443500202130ustar00rootroot00000000000000SELECT '0'::numeral; SELECT '1'::numeral; SELECT '-2'::numeral; SELECT 'zero'::numeral; SELECT 'one'::numeral; SELECT 'minus one'::numeral; SELECT 'two'::numeral; SELECT 'three'::numeral; SELECT 'four'::numeral; SELECT 'five'::numeral; SELECT 'six'::numeral; SELECT 'seven'::numeral; SELECT 'eight'::numeral; SELECT 'nine'::numeral; SELECT 'ten'::numeral; SELECT 'eleven'::numeral; SELECT 'twelve'::numeral; SELECT 'thirteen'::numeral; SELECT 'fourteen'::numeral; SELECT 'fifteen'::numeral; SELECT 'sixteen'::numeral; SELECT 'seventeen'::numeral; SELECT 'eighteen'::numeral; SELECT 'nineteen'::numeral; SELECT 'twenty'::numeral; SELECT 'twenty-one'::numeral; SELECT 'thirty'::numeral; SELECT 'thirty-two'::numeral; SELECT 'forty'::numeral; SELECT 'fifty'::numeral; SELECT 'sixty'::numeral; SELECT 'seventy'::numeral; SELECT 'eighty'::numeral; SELECT 'ninety'::numeral; SELECT 'hundred'::numeral; SELECT 'one hundred'::numeral; SELECT 'hundred one'::numeral; SELECT 'one hundred one'::numeral; SELECT 'one hundred twenty-one'::numeral; SELECT 'two hundred'::numeral; SELECT 'two hundred one'::numeral; SELECT 'two hundred thirty-two'::numeral; SELECT 'thousand'::numeral; SELECT 'one thousand'::numeral; SELECT 'one thousand one'::numeral; SELECT 'thousand one hundred'::numeral; SELECT 'one thousand one hundred'::numeral; SELECT 'eleven hundred'::numeral; SELECT 'one thousand one hundred one'::numeral; SELECT 'hundred thousand'::numeral; SELECT 'one hundred thousand'::numeral; SELECT 'one hundred one thousand'::numeral; SELECT 'two hundred thousand'::numeral; SELECT 'one million'::numeral; SELECT 'one million one'::numeral; SELECT 'one million one hundred'::numeral; SELECT 'one million one thousand'::numeral; SELECT 'one million one hundred thousand'::numeral; SELECT 'two million'::numeral; SELECT 'hundred million'::numeral; SELECT 'one billion'::numeral; SELECT 'one billion one million'::numeral; SELECT 'one billion one million one thousand'::numeral; SELECT 'billion'::numeral; SELECT 'trillion'::numeral; SELECT 'quadrillion'::numeral; SELECT 'quintillion'::numeral; postgresql-numeral-1.0/sql/operator.sql000066400000000000000000000010401330516443500203700ustar00rootroot00000000000000SELECT 'vier'::zahl::bigint; SELECT 'hundert'::zahl = 'einhundert'; SELECT 'drei'::zahl <> 'vier'; SELECT 'zehn'::zahl < 'elf'; SELECT 'acht'::zahl > 'sieben'; SELECT 'neun'::zahl <= 'neun'; SELECT 'eins'::zahl >= 'null'; SELECT 'zehn'::zahl % 'drei'; SELECT 'acht'::zahl + 'vier'; SELECT 'neun'::zahl - 'vier'; SELECT 'fünf'::zahl * 'vier'; SELECT 'zwölf'::zahl / 'drei'; SELECT 'zehn'::zahl & 'sechs'; SELECT 'zwei'::zahl | 'vier'; SELECT @ 'minus drei'::zahl; SELECT - 'vier'::zahl; SELECT ~ 'fünfzehn'::zahl; SELECT + 'sechs'::zahl; postgresql-numeral-1.0/sql/roman.sql000066400000000000000000000016241330516443500176610ustar00rootroot00000000000000SELECT '0'::roman; SELECT '1'::roman; SELECT '-2'::roman; SELECT 'nulla'::roman; SELECT 'minus nulla'::roman; SELECT 'I'::roman, 'i'::roman; SELECT 'V'::roman, 'v'::roman; SELECT 'X'::roman, 'x'::roman; SELECT 'L'::roman, 'l'::roman; SELECT 'C'::roman, 'c'::roman; SELECT 'D'::roman, 'd'::roman; SELECT 'M'::roman, 'm'::roman; SELECT 'minus I'::roman; SELECT 'minus 1'::roman; SELECT 'II'::roman; SELECT 'III'::roman; SELECT 'IV'::roman; SELECT 'V'::roman; SELECT 'VI'::roman; SELECT 'IX'::roman; SELECT 'XI'::roman; SELECT 'XX'::roman; SELECT 'IL'::roman; SELECT 'LI'::roman; SELECT 'VL'::roman; SELECT 'LV'::roman; SELECT 'XL'::roman; SELECT 'LX'::roman; SELECT 'XC'::roman; SELECT 'CX'::roman; SELECT 'CC'::roman; SELECT 'CD'::roman; SELECT 'DC'::roman; SELECT 'CM'::roman; SELECT 'MC'::roman; SELECT 'MM'::roman; SELECT '10000'::roman; SELECT '-10000'::roman; SELECT '10001'::roman; SELECT '-10001'::roman; postgresql-numeral-1.0/sql/zahl.sql000066400000000000000000000035231330516443500175030ustar00rootroot00000000000000SELECT '0'::zahl; SELECT '1'::zahl; SELECT '-2'::zahl; SELECT 'null'::zahl; SELECT 'eins'::zahl; SELECT 'minus eins'::zahl; SELECT 'zwei'::zahl; SELECT 'drei'::zahl; SELECT 'vier'::zahl; SELECT 'fünf'::zahl; SELECT 'sechs'::zahl; SELECT 'sieben'::zahl; SELECT 'acht'::zahl; SELECT 'neun'::zahl; SELECT 'zehn'::zahl; SELECT 'elf'::zahl; SELECT 'zwölf'::zahl; SELECT 'dreizehn'::zahl; SELECT 'vierzehn'::zahl; SELECT 'fünfzehn'::zahl; SELECT 'sechzehn'::zahl; SELECT 'siebzehn'::zahl; SELECT 'achtzehn'::zahl; SELECT 'neunzehn'::zahl; SELECT 'zwanzig'::zahl; SELECT 'einundzwanzig'::zahl; SELECT 'dreißig'::zahl; SELECT 'zweiunddreißig'::zahl; SELECT 'vierzig'::zahl; SELECT 'fünfzig'::zahl; SELECT 'sechzig'::zahl; SELECT 'siebzig'::zahl; SELECT 'achtzig'::zahl; SELECT 'neunzig'::zahl; SELECT 'hundert'::zahl; SELECT 'einhundert'::zahl; SELECT 'hunderteins'::zahl; SELECT 'einhunderteins'::zahl; SELECT 'einhunderteinundzwanzig'::zahl; SELECT 'zweihundert'::zahl; SELECT 'zweihunderteins'::zahl; SELECT 'zweihundertzweiunddreißig'::zahl; SELECT 'tausend'::zahl; SELECT 'eintausend'::zahl; SELECT 'eintausendeins'::zahl; SELECT 'tausendeinhundert'::zahl; SELECT 'eintausendeinhundert'::zahl; SELECT 'elfhundert'::zahl; SELECT 'eintausendeinhunderteins'::zahl; SELECT 'hunderttausend'::zahl; SELECT 'einhunderttausend'::zahl; SELECT 'einhunderteintausend'::zahl; SELECT 'zweihunderttausend'::zahl; SELECT 'eine Million'::zahl; SELECT 'eine Million eins'::zahl; SELECT 'eine Million einhundert'::zahl; SELECT 'eine Million eintausend'::zahl; SELECT 'eine Million einhunderttausend'::zahl; SELECT 'zwei Millionen'::zahl; SELECT 'hundert Millionen'::zahl; SELECT 'eine Milliarde'::zahl; SELECT 'eine Milliarde eine Million'::zahl; SELECT 'eine Milliarde eine Million eintausend'::zahl; SELECT 'Billion'::zahl; SELECT 'Billiarde'::zahl; SELECT 'Trillion'::zahl; postgresql-numeral-1.0/zahlfuncs.c000066400000000000000000000072271330516443500173730ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ #include "postgres.h" #include "fmgr.h" #include "numeral.h" /* input and output */ char *yyerrstr; /* copy of error catched by yyzahlerror() */ void yyzahlerror (char *s); void yyzahlerror (char *s) { /* store error for later use in number_in */ yyerrstr = pstrdup(s); } PG_FUNCTION_INFO_V1(zahl_in); Datum zahl_in (PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); Zahl zahl; if (zahl_parse(str, &zahl) > 0) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type zahl: \"%s\", %s", str, yyerrstr))); PG_RETURN_INT64(zahl); } PG_FUNCTION_INFO_V1(zahl_out); Datum zahl_out(PG_FUNCTION_ARGS) { Zahl zahl = PG_GETARG_INT64(0); PG_RETURN_CSTRING(zahl_cstring(zahl)); } /* format German numerals */ static const char *zahl_one[] = { "", "eins", /* einhunderteins */ "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun", "zehn", "elf", "zwölf", "dreizehn", "vierzehn", "fünfzehn", "sechzehn", "siebzehn", "achtzehn", "neunzehn", }; static const char *zahl_ten[] = { "", "zehn", "zwanzig", "dreißig", "vierzig", "fünfzig", "sechzig", "siebzig", "achtzig", "neunzig", }; static const char * zahl_x (Zahl zahl, const char *eins) { Assert (zahl >= 0 && zahl < 20); if (zahl == 1) return eins; return zahl_one[zahl]; } static const char * zahl_xx (Zahl zahl, const char *eins) { if (zahl < 20) { return zahl_x(zahl, eins); /* returns empty for 0 */ } if (zahl % 10 == 0) return zahl_ten[zahl / 10]; return psprintf("%sund%s", zahl_x(zahl % 10, "ein"), zahl_ten[zahl / 10]); } static const char * zahl_xxx (Zahl zahl, const char *eins) { if (zahl < 100) return zahl_xx(zahl, eins); return psprintf("%shundert%s", zahl_x(zahl / 100, "ein"), zahl_xx(zahl % 100, eins)); } static const char * zahl_xxxxxx (Zahl zahl) { if (zahl < 1000) return zahl_xxx(zahl, "eins"); return psprintf("%stausend%s", zahl_xxx(zahl / 1000, "ein"), zahl_xxx(zahl % 1000, "eins")); } struct zillions { long long value; const char *name1; const char *name2; } static zillions[] = { { 1000000000000000000, "Trillion", "Trillionen" }, { 1000000000000000, "Billiarde", "Billiarden" }, { 1000000000000, "Billion", "Billionen" }, { 1000000000, "Milliarde", "Milliarden" }, { 1000000, "Million", "Millionen" }, { 0 }, }; static const char * zahl_zillion (Zahl zahl) { struct zillions *z; char *result = palloc0(1000); for (z = zillions; z->value; z++) if (zahl >= z->value) { int n = zahl / z->value; if (*result) strlcat (result, " ", 1000); strlcat (result, zahl_xxx(n, "eine"), 1000); strlcat (result, " ", 1000); if (n == 1) strlcat (result, z->name1, 1000); else strlcat (result, z->name2, 1000); zahl = zahl % z->value; } if (zahl > 0) { if (*result) strlcat (result, " ", 1000); strlcat (result, zahl_xxxxxx(zahl), 1000); } return result; } const char * zahl_cstring (Zahl zahl) { if (zahl < 0) { return psprintf("minus %s", zahl_cstring(-zahl)); } else if (zahl == 0) { return "null"; } return zahl_zillion(zahl); } postgresql-numeral-1.0/zahllexer.l000066400000000000000000000046251330516443500174040ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ %{ #include "numeral.h" #include "zahlparser.tab.h" %} %option prefix="yyzahl" %option noyywrap %option nounput %option noinput INT_R [\-+]?[0-9]+ %% {INT_R} { yyzahllval = atoll(yytext); return INT; } minus { return MINUS; } null { return ZERO; } und { return AND; } ein[es]? { yyzahllval = 1; return ONE; } zwei { yyzahllval = 2; return ONE; } drei { yyzahllval = 3; return ONE; } vier { yyzahllval = 4; return ONE; } fünf { yyzahllval = 5; return ONE; } sechs { yyzahllval = 6; return ONE; } sieben { yyzahllval = 7; return ONE; } acht { yyzahllval = 8; return ONE; } neun { yyzahllval = 9; return ONE; } zehn { yyzahllval = 10; return ONE; } elf { yyzahllval = 11; return ONE; } zwölf { yyzahllval = 12; return ONE; } dreizehn { yyzahllval = 13; return ONE; } vierzehn { yyzahllval = 14; return ONE; } fünfzehn { yyzahllval = 15; return ONE; } sechzehn { yyzahllval = 16; return ONE; } siebzehn { yyzahllval = 17; return ONE; } achtzehn { yyzahllval = 18; return ONE; } neunzehn { yyzahllval = 19; return ONE; } zwanzig { yyzahllval = 20; return TEN; } dreißig { yyzahllval = 30; return TEN; } vierzig { yyzahllval = 40; return TEN; } fünfzig { yyzahllval = 50; return TEN; } sechzig { yyzahllval = 60; return TEN; } siebzig { yyzahllval = 70; return TEN; } achtzig { yyzahllval = 80; return TEN; } neunzig { yyzahllval = 90; return TEN; } hundert { yyzahllval = 100; return HUNDRED; } tausend { yyzahllval = 1000; return THOUSAND; } [mM]illion(en)? { yyzahllval = 1000000; return ZILLION; } [mM]illiarden? { yyzahllval = 1000000000; return ZILLION; } [bB]illion(en)? { yyzahllval = 1000000000000; return ZILLION; } [bB]illiarden? { yyzahllval = 1000000000000000; return ZILLION; } [tT]rillion(en)? { yyzahllval = 1000000000000000000; return ZILLION; } /* Trilliarde doesn't fit into long long */ [ \t\n]* /* eat whitespace */ . return ERR; postgresql-numeral-1.0/zahlparser.y000066400000000000000000000043241330516443500175720ustar00rootroot00000000000000/* Copyright (C) 2017 Christoph Berg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. */ %{ #include /* bzero */ #include "numeral.h" /* flex/bison prototypes */ int yyzahllex (void); struct yyzahl_buffer_state *yyzahl_scan_string(char *str); void yyzahl_delete_buffer(struct yyzahl_buffer_state *buffer); void yyzahlerror (char const *s); static Zahl *numeral_parse_result; /* parsing result gets stored here */ %} %define parse.error verbose %define api.prefix {yyzahl} %define api.value.type {Zahl} %token INT %token MINUS %token ZERO %token AND %token ONE %token TEN %token HUNDRED %token THOUSAND %token ZILLION %token ERR %% input: /* parser entry */ INT { *numeral_parse_result = $1; } | ZERO { *numeral_parse_result = 0; } | expr { *numeral_parse_result = $1; } | MINUS expr { *numeral_parse_result = -$2; } ; expr: /* general number */ xxxxxx | prefix_xxx ZILLION expr_tail { $$ = $1 * $2 + $3; } ; expr_tail: /* tail of general number */ %empty { $$ = 0; } | xxxxxx | prefix_xxx ZILLION expr_tail { $$ = $1 * $2 + $3; } xxxxxx: /* 6-digit number */ xxx | prefix_xxx THOUSAND suffix_xxx { $$ = $1 * $2 + $3; } ; prefix_xxx: /* N-thousand */ %empty { $$ = 1; } | xxx ; suffix_xxx: /* thousand-and-N */ %empty { $$ = 0; } | xxx ; xxx: /* 3-digit number */ xx | prefix_x HUNDRED suffix_xx { $$ = $1 * $2 + $3; } ; xx: /* 2-digit number */ ONE | TEN | ONE AND TEN { $$ = $1 + $3; } ; prefix_x: /* N-hundred */ %empty { $$ = 1; } | ONE ; suffix_xx: /* hundred-and-N */ %empty { $$ = 0; } | xx ; %% /* parse a given string and return the result via the second argument */ int zahl_parse (char *s, Zahl *zahl) { struct yyzahl_buffer_state *buf; int ret; numeral_parse_result = zahl; buf = yyzahl_scan_string(s); ret = yyzahlparse(); yyzahl_delete_buffer(buf); return ret; }