Reference

Texomy syntax at a glance.

A Texomy specification describes what your text means — types and their fields — and how those types appear on the surface. The compiler turns it into a deterministic parser.

The mental model

A specification describes a semantic model and the rules that recognize it in text. A type describes what a value is; a field describes the value's role in another concept;$patterns describes how that concept appears on the surface. The result is typed JSON, not just regex captures.

Object-oriented data modeling

Texomy applies familiar object-oriented modeling concepts to semantic parsing. It uses the data-modeling part of OO: types, inheritance, composition, and named properties. It is a parsing language rather than an object runtime, so these concepts describe semantic values and their recognition rules rather than methods or mutable object behaviour.

Types and inheritance

Every ordinary declaration defines a type. Nesting an ordinary declaration creates a child type. The two declarations below are equivalent: both define Currency::USD andCurrency::EUR. A reference to Currency accepts either child, while the result retains the concrete type that matched.

Currency:
  USD: [ 'USD', '\$' ]
  EUR: [ 'EUR', '€' ]

# Equivalent fully qualified declarations:
# Currency::USD: [ 'USD', '\$' ]
# Currency::EUR: [ 'EUR', '€' ]

Number:
  Integer: '\d+'
  Decimal: '${Number::Integer}\.${Number::Integer}'

Use a parent when it represents a real shared category or when consumers need to accept its variants polymorphically. Use a direct type when there is only one meaningful concept.

A child type may add its own pattern while inheriting fields from its parent. HereDate::Iso and Date::Slash are ordinary child types ofDate.

Date:
  $fields:
    year: { $type: Number::Integer, $match: '\d{4}' }
    month: { $type: Number::Integer, $match: '\d{2}' }
    day: { $type: Number::Integer, $match: '\d{2}' }
  Iso: '#{year}-#{month}-#{day}'
  Slash: '#{month}/#{day}/#{year}'

Nested types

$types defines types whose recognition belongs to one containing type family. They are visible in that type and its descendants, and are compiled into the containing pattern rather than run as independent top-level matchers. They still keep their type identity when captured through a field.

Comparison:
  $types:
    Operator: [ '>=', '<=', '=', '>', '<' ]
  $fields:
    left: Token
    operator: Operator
    right: Token
  $patterns: '#{left} #{operator} #{right}'

Use a normal declaration for a concept whose own text or format has a stable meaning outside this grammar. Use $types when the containing type supplies the boundary or the meaning that makes a recognizer safe.

Fields

$fields declares semantic roles inside a structured type. The type says what a value is; the field name says what role it plays here. Use a direct type name when the complete type is accepted, or $type + $match when this role accepts a narrower surface form.

Integer: '\d+'

Payment:
  $fields:
    money: Money
    fiscalYear:
      $type: Integer
      $match: '\d{4}'
  $patterns: '#{money} for #{fiscalYear}'

Do not encode a role into a new type when the value category is unchanged. A year or quarter number is still an Integer; year orquarter is its field role. A name, identifier, amount, or status can still deserve its own type when that category has stable meaning beyond its immediate context.

Use [] on the type side for collection fields.

Line:
  $fields:
    words: Word[]
  $patterns: '#{words}( #{words})*'

Patterns

$patterns is where a type's surface form meets structure. It accepts one pattern or a list of alternatives. A field-level $match narrows the form accepted for that one role.

TransactionId: 'TX-${Number::Integer}'   # compose a type without a field

Payment: '#{money:Money} due on #{date:Date}'  # inline typed fields

HtmlTag:
  $types:
    Content: '.*?'
  $fields:
    name: Identifier
    content: Content
  $patterns: '<#{name}>#{content}</#{=name}>'

A type may declare multiple patterns as alternatives. Any of them will match.

Country:
  $fields:
    code: CountryCode
    name: CountryName
  $patterns:
    - '#{code}'
    - '#{name}'

Recognition order

Declaration and pattern order controls recognition order. Put a composite or more specific recognizer before a broad type that could consume part of the same text. Once Texomy recognizes a span, later patterns compose that semantic node rather than matching its raw text again.

Cross-references and paths

Use :: to reference a child type. A short child name resolves relative to its type hierarchy; use a fully qualified path when the short name is ambiguous.

Number:
  Decimal: '${Integer}\.${Integer}'
  Integer: '\d+'

Latency:
  $fields:
    ms: Number::Integer     # specifically the Integer child
  $patterns: '#{ms}ms'

Imports

A file can import other specifications by relative path. Split reusable concepts into files when they are shared by several domain specifications.

$imports:
  - types/money
  - types/date

Payment:
  $fields:
    money: Money
    date: Date
  $patterns: '#{money} due on #{date}'

Flags

$flags controls how patterns match at scope. They are inherited from the file scope down into each type; a type may opt out of an inherited flag by prefixing it with -.

$flags: ['FLEXIBLE_WHITESPACE', 'WORD_BOUNDARY']

Number:
  $flags: '-WORD_BOUNDARY'   # opt out for this type; digits are composed elsewhere
  Decimal: '\d+\.\d+'
  Integer: '\d+'

Call:
  $flags: OPTIONAL_WHITESPACE
  $fields: { name: Identifier, arguments: 'Identifier[]' }
  $patterns: '#{name}\((#{arguments}(, #{arguments})*)?\)'

Common flags:

Put a flag at the widest scope it correctly applies to. Do not repeat it on every child.

Composition rules of thumb


Try it

The fastest way to learn the syntax is to open an example in Studio and change it.