pax_global_header00006660000000000000000000000064137101040270014505gustar00rootroot0000000000000052 comment=fd70635a24078d7d6205fd28a235f58705093291 postgresql-numeral-1.3/000077500000000000000000000000001371010402700152145ustar00rootroot00000000000000postgresql-numeral-1.3/.gitignore000066400000000000000000000001351371010402700172030ustar00rootroot00000000000000numeral--*.sql regression.diffs regression.out results/ tags *.o *.so *.tab.c *.tab.h *.yy.c postgresql-numeral-1.3/.travis.yml000066400000000000000000000013631371010402700173300ustar00rootroot00000000000000# 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 - PG_SUPPORTED_VERSIONS=11 - PG_SUPPORTED_VERSIONS=12 - PG_SUPPORTED_VERSIONS=13 language: C dist: bionic before_install: # extra apt.pg.o.sh options added in version 204, travis currently has 199 (2019-11-27) - sudo apt-get -qq update - sudo apt-get -y install postgresql-common bison flex install: - sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -p -v $PG_SUPPORTED_VERSIONS -i script: - make PROFILE="-Werror" - sudo make install - pg_virtualenv make installcheck - if test -s regression.diffs; then cat regression.diffs; fi postgresql-numeral-1.3/.vimrc000066400000000000000000000000161371010402700163320ustar00rootroot00000000000000set ts=4 sw=4 postgresql-numeral-1.3/Makefile000066400000000000000000000023461371010402700166610ustar00rootroot00000000000000MODULE_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 is derived from SIZEOF_VOID_P SIZEOF_VOID_P = $(shell grep '^.define SIZEOF_VOID_P' $(INCLUDEDIR_SERVER)/pg_config.h | cut -d ' ' -f 3) ifeq ($(SIZEOF_VOID_P),8) 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.3/NEWS.md000066400000000000000000000000531371010402700163100ustar00rootroot00000000000000v1.0: ------------------ * Initial release postgresql-numeral-1.3/README.md000066400000000000000000000036141371010402700164770ustar00rootroot00000000000000postgresql-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 (currently up to 13) and Bison 3. [![Build Status](https://travis-ci.org/df7cb/postgresql-numeral.svg?branch=master)](https://travis-ci.org/df7cb/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, 2020 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.3/debian/000077500000000000000000000000001371010402700164365ustar00rootroot00000000000000postgresql-numeral-1.3/debian/changelog000066400000000000000000000025141371010402700203120ustar00rootroot00000000000000postgresql-numeral (1.3-1) unstable; urgency=medium * Fix compatibility with gcc 10. -- Christoph Berg Tue, 28 Jul 2020 22:10:46 +0200 postgresql-numeral (1.2-1) unstable; urgency=medium * Fix passbyvalue logic on PG13/32-bit. -- Christoph Berg Thu, 21 May 2020 11:08:32 +0200 postgresql-numeral (1.1-1) unstable; urgency=medium [ Debian Janitor ] * Use secure copyright file specification URI. * Bump debhelper from deprecated 9 to 12. * Set debhelper-compat version in Build-Depends. * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, Repository-Browse. * Refer to specific version of license GPL-2+. * Update standards version to 4.5.0, no changes needed. [ Christoph Berg ] * Fix zero numeral. * Support PostgreSQL 13. -- Christoph Berg Wed, 20 May 2020 23:40:45 +0200 postgresql-numeral (1.0-3) unstable; urgency=medium * Upload for PostgreSQL 12. -- Christoph Berg Wed, 30 Oct 2019 13:58:04 +0100 postgresql-numeral (1.0-2) unstable; urgency=medium * Upload for PostgreSQL 11. -- Christoph Berg Sat, 13 Oct 2018 11:52:55 +0200 postgresql-numeral (1.0-1) unstable; urgency=medium * Initial release. -- Christoph Berg Mon, 04 Jun 2018 09:16:54 +0200 postgresql-numeral-1.3/debian/control000066400000000000000000000016161371010402700200450ustar00rootroot00000000000000Source: postgresql-numeral Section: database Priority: optional Maintainer: Christoph Berg Build-Depends: bison, debhelper-compat (= 12), flex, postgresql-server-dev-all (>= 153~), Standards-Version: 4.5.0 Vcs-Git: https://github.com/df7cb/postgresql-numeral.git Vcs-Browser: https://github.com/df7cb/postgresql-numeral Homepage: https://github.com/df7cb/postgresql-numeral Package: postgresql-12-numeral Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, postgresql-12 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.3/debian/control.in000066400000000000000000000016341371010402700204520ustar00rootroot00000000000000Source: postgresql-numeral Section: database Priority: optional Maintainer: Christoph Berg Build-Depends: bison, debhelper-compat (= 12), flex, postgresql-server-dev-all (>= 153~), Standards-Version: 4.5.0 Vcs-Git: https://github.com/df7cb/postgresql-numeral.git Vcs-Browser: https://github.com/df7cb/postgresql-numeral Homepage: https://github.com/df7cb/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.3/debian/copyright000066400000000000000000000013531371010402700203730ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Files: * Copyright: (C) 2017, 2020 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-2 on Debian systems. postgresql-numeral-1.3/debian/pgversions000066400000000000000000000000051371010402700205530ustar00rootroot000000000000009.4+ postgresql-numeral-1.3/debian/rules000077500000000000000000000006321371010402700175170ustar00rootroot00000000000000#!/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.3/debian/source/000077500000000000000000000000001371010402700177365ustar00rootroot00000000000000postgresql-numeral-1.3/debian/source/format000066400000000000000000000000141371010402700211440ustar00rootroot000000000000003.0 (quilt) postgresql-numeral-1.3/debian/source/lintian-overrides000066400000000000000000000001751371010402700233220ustar00rootroot00000000000000# don't bug people uploading from @work source: changelog-should-mention-nmu source: source-nmu-has-incorrect-version-number postgresql-numeral-1.3/debian/tests/000077500000000000000000000000001371010402700176005ustar00rootroot00000000000000postgresql-numeral-1.3/debian/tests/control000066400000000000000000000001251371010402700212010ustar00rootroot00000000000000Depends: @, postgresql-server-dev-all Tests: installcheck Restrictions: allow-stderr postgresql-numeral-1.3/debian/tests/installcheck000077500000000000000000000000711371010402700221700ustar00rootroot00000000000000#!/bin/sh pg_buildext -i '--locale=C.UTF-8' installcheck postgresql-numeral-1.3/debian/upstream/000077500000000000000000000000001371010402700202765ustar00rootroot00000000000000postgresql-numeral-1.3/debian/upstream/metadata000066400000000000000000000003771371010402700220100ustar00rootroot00000000000000Bug-Database: https://github.com/df7cb/postgresql-numeral/issues Bug-Submit: https://github.com/df7cb/postgresql-numeral/issues/new Repository: https://github.com/df7cb/postgresql-numeral.git Repository-Browse: https://github.com/df7cb/postgresql-numeral postgresql-numeral-1.3/debian/watch000066400000000000000000000001171371010402700174660ustar00rootroot00000000000000version=4 https://github.com/df7cb/postgresql-numeral/releases .*/v(.*).tar.gz postgresql-numeral-1.3/do000077500000000000000000000011431371010402700155430ustar00rootroot00000000000000#!/bin/bash set -eux export PGDATABASE=postgres for PGVERSION in ${*:-13 12 11 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" = "12" ] && 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" if ! make installcheck REGRESS_OPTS="--use-existing --dbname=postgres" PG_CONFIG=$PG_CONFIG; then cat regression.diffs exit 1 fi done postgresql-numeral-1.3/expected/000077500000000000000000000000001371010402700170155ustar00rootroot00000000000000postgresql-numeral-1.3/expected/extension.out000066400000000000000000000001461371010402700215630ustar00rootroot00000000000000SET client_min_messages = warning; CREATE EXTENSION IF NOT EXISTS numeral; RESET client_min_messages; postgresql-numeral-1.3/expected/numeral.out000066400000000000000000000131571371010402700212200ustar00rootroot00000000000000SELECT '0'::numeral; numeral --------- zero (1 row) SELECT '1'::numeral; numeral --------- one (1 row) SELECT '-2'::numeral; numeral ----------- minus two (1 row) SELECT 'zero'::numeral; numeral --------- zero (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.3/expected/operator.out000066400000000000000000000023041371010402700214000ustar00rootroot00000000000000SELECT '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.3/expected/roman.out000066400000000000000000000044571371010402700206740ustar00rootroot00000000000000SELECT '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.3/expected/zahl.out000066400000000000000000000123671371010402700205150ustar00rootroot00000000000000SELECT '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.3/numeral--1.sql.in000066400000000000000000000116511371010402700202240ustar00rootroot00000000000000/* 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.3/numeral.c000066400000000000000000000012141371010402700170210ustar00rootroot00000000000000/* 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.3/numeral.control000066400000000000000000000001211371010402700202530ustar00rootroot00000000000000default_version = '1' comment = 'numeral datatypes extension' relocatable = true postgresql-numeral-1.3/numeral.h000066400000000000000000000005561371010402700170360ustar00rootroot00000000000000/* 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.3/numeralfuncs.c000066400000000000000000000073451371010402700200730ustar00rootroot00000000000000/* Copyright (C) 2017, 2020 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 English 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 "zero"; } return numeral_zillion(numeral); } postgresql-numeral-1.3/numerallexer.l000066400000000000000000000050071371010402700200760ustar00rootroot00000000000000/* 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.3/numeralparser.y000066400000000000000000000045311371010402700202710ustar00rootroot00000000000000/* 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.3/romanfuncs.c000066400000000000000000000036371371010402700175440ustar00rootroot00000000000000/* Copyright (C) 2017-2020 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 */ extern 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.3/romanlexer.l000066400000000000000000000021451371010402700175470ustar00rootroot00000000000000/* 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.3/romanparser.y000066400000000000000000000050531371010402700177420ustar00rootroot00000000000000/* 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.3/sql/000077500000000000000000000000001371010402700160135ustar00rootroot00000000000000postgresql-numeral-1.3/sql/extension.sql000066400000000000000000000001461371010402700205510ustar00rootroot00000000000000SET client_min_messages = warning; CREATE EXTENSION IF NOT EXISTS numeral; RESET client_min_messages; postgresql-numeral-1.3/sql/numeral.sql000066400000000000000000000040571371010402700202050ustar00rootroot00000000000000SELECT '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.3/sql/operator.sql000066400000000000000000000010401371010402700203620ustar00rootroot00000000000000SELECT '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.3/sql/roman.sql000066400000000000000000000016241371010402700176530ustar00rootroot00000000000000SELECT '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.3/sql/zahl.sql000066400000000000000000000035231371010402700174750ustar00rootroot00000000000000SELECT '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.3/zahlfuncs.c000066400000000000000000000072431371010402700173630ustar00rootroot00000000000000/* Copyright (C) 2017-2020 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 */ extern 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.3/zahllexer.l000066400000000000000000000046251371010402700173760ustar00rootroot00000000000000/* 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.3/zahlparser.y000066400000000000000000000043241371010402700175640ustar00rootroot00000000000000/* 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; }