
NAME
Contact - Parse free-form contact text into vCard (RFC 6350) and jCard (RFC 7095)
SYNOPSIS
use Contact;
my $text = q:to/END/;
John Doe
123 Main St.
Springfield
IL 62704
Tel: 555-867-5309
Email: john.doe@example.com
END
my $card = Contact::Grammar.parse($text, actions => Contact::Actions.new).made;
my $jcard = Contact::jCard.new(:$card);
my $vcard = Contact::vCard.new(:$card);
say $card.fn; # John Doe
say $card.street; # 123 Main St.
say "{.locality}, {.region} {.postal-code}" given $card; # Springfield, IL 62704
print $vcard; # vCard (RFC 6350) text
say $jcard.to-json; # jCard (RFC 7095) JSON
DESCRIPTION
Contact parses free-form plain-text contact records and produces structured data conforming to vCard 4.0 (RFC 6350) and jCard (RFC 7095).
The input is a plain-text block. The full name is always the first line; all other fields are optional and may appear in any order.
US format:
John Doe ← name (required, always first)
PO Box 999 ← po-box (optional)
Apt 4B ← ext-address (optional — Apt/Suite/Unit/…)
123 Main St. ← street
Springfield ← locality
IL 62704 ← region + postal-code
United States ← country (optional)
Tel: 555-867-5309 ← tel (optional — Tel/Telephone/Phone)
Email: john.doe@example.com ← email (optional — Email/E-mail)
UK format (trailing commas stripped automatically):
Jane Smith
PO Box 42 ← po-box (optional)
Sleepy Cottage ← ext-address (optional — named house, no digits)
123 High Street ← street
Henley-on-Thames ← locality
Oxon ← region (optional)
RG9 2XX ← postal-code
UK ← country (optional)
The single Contact::Grammar auto-detects locale from the address structure. Label and country synonyms are defined per locale in their respective modules.
Boss grammar that assembles locale sub-grammars. Strips trailing commas and chomps the input before parsing. Throws X::Contact::CannotParse if the input cannot be parsed.
my $card = Contact::Grammar.parse($text, actions => Contact::Actions.new).made;
Locale address grammars are registered in @locale-adrs:
Role defining the seven RFC 6350 §6.3.1 address components: po-box, ext-address, street, locality, region, postal-code, country. The components method returns them as an ordered list with absent fields as empty strings, ready for embedding in a jCard adr array.
Locale classes (Contact::Address::en_US::Address, Contact::Address::en_UK::Address) implement this role, mapping their local attribute names (e.g. town, postcode) to the RFC methods.
Module providing Contact::Name::Grammar and Contact::Name. The grammar parses a free-form name line into prefix, given, additional, family, and suffix components. The class implements RFC 6350 N and FN fields via components and fn.
The primary parsed result, populated automatically from grammar captures via Actionable. Attributes: version (default "4.0"), name, adr, tel, email. Name sub-fields (fn, given, family, etc.) and address sub-fields (street, locality, etc.) are delegated directly from $.name and $.adr.
Wraps a Card and serialises to jCard format (RFC 7095):
my $jcard = Contact::jCard.new(:$card);
say $jcard.to-json;
Wraps a Card and stringifies to vCard 4.0 format (RFC 6350):
my $vcard = Contact::vCard.new(:$card);
print $vcard; # coerces via method Str
Also parses a vCard string back to a Card (locale-agnostic, via Contact::vCard::Grammar):
my $card = Contact::vCard.parse($vcard-string);
This enables round-trip use and LLM DSL workflows: prompt the LLM to extract contact data as vCard 4.0, then parse the result directly. The Contact::vCard::Grammar and Contact::vCard::Actions from Contact::vCard can also be used directly in the slangify.org playground.
Synonyms
Label synonyms live in Contact.rakumod; address and name synonyms live in their locale modules. All matches are case-insensitive.
Contact (tel/email labels):
constant %syns = (
tel => <Tel Telephone Phone>,
email => <Email E-mail>,
);
Contact::Address::en_US (apt prefixes, country names):
my constant @apt-syns = <Apt Apartment Suite Ste Unit Flat Fl>;
my constant @country-syns = ('United States of America', 'United States', 'USA', 'US', 'America');
Contact::Address::en_UK (country names):
my constant @country-syns = ('England', 'Scotland', 'Wales', 'Northern Ireland',
'United Kingdom', 'UK', 'Great Britain', 'GB', 'Britain', ...);
Contact::Name (honorifics, suffixes):
my constant @prefix-syns = <Mr Mrs Ms Miss Dr Prof Rev>;
my constant @suffix-syns = <Jr Sr II III IV Esq PhD MD JD>;
Exceptions
X::Contact::CannotParse is thrown when the input cannot be parsed. The $.text attribute carries the offending input.
Standards
AUTHOR
librasteve librasteve@furnival.net
COPYRIGHT AND LICENSE
Copyright 2026 librasteve
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.