Phrases

In elliptical, natural language is modeled using Phrases. This allows you to model language in an extensible, composable, dynamic way. At its most basic level, a Component is just an object that has a single function called describe.

const Exclamation = {
  describe() {
    return <literal text='elliptical rocks!' />
  }
}

describe returns an element.

Parsing Components

Once we have a Component, we can use it in elements of our own.

const parse = compile(<Exclamation />)
parse('') // => 'elliptical rocks!'

Props

We can also set props on our elements. The element itself is exposed as the first argument to describe. Here we access props using ES2015 argument destructuring.

const Exclamation = {
  describe({props}) {
    return <literal text={`${props.app} rocks!`} />
  }
}
const parse = compile(<Exclamation app='Lacona' />)
parse('') // => 'Lacona rocks!'

Children

describe is also passed its children as an Array. We can use this to build "Higher-order Phrases" - that is, Components that take elements and augment them.

const Exclamation = {
  describe({children}) {
    return (
      <sequence>
        {children[0]}
        <literal text=' rocks!' />
      </sequence>
    )
  }
}

const grammar = <Exclamation><literal text='Batman' /></Exclamation>
const parse = compile(grammar)
parse('') // => 'Batman rocks!'

defaultProps

We don't want consumers of our Phrase to need to specify every prop every time, so we can use the defaultProps helper. It is an object that is always merged onto the provided props using _.defaults.

const Exclamation = {
  defaultProps: {app: 'Google Chrome'},
  describe({props}) {
    return <literal text={`${props.app} rocks!`} />
  }
}

const parse = compile(<Exclamation />)
parse('') // => 'Google Chrome rocks!'

Result Helpers

Every Phrase can have two functions, mapResult and filterResult, which can be used to modify phrase behavior based upon their result in a simple, imperative way.

These these methods are only called if the phrase is complete - that is, if any Words with placeholder: true are in the words. Therefore, they do not need to worry about errors because of partial results.

If both are declared, mapResult runs before filterResult.

These functions do not allow for any functionality that <map> and <filter> do not already provide, but they are a simpler, more declarative approach. Additionally, elliptical addons can make use of them easily.

mapResult

mapResult: (result: Any, element: Element) => Any

The return value of this call will set as the result from this phrase. This should be used sparingly, only when the element structure returns data in the wrong format.

If you need to do validation on incomplete outputs, use <map>.

Unlike <map>, mapResult cannot return an iterable and cannot be limited.

filterResult

filterResult: (result: Any, element: Element) => Boolean

If this function returns false, this branch will not continue parsing. This should be used agressively to ensure that invalid outputs never make it past the phrase.

If you need to do validation on incomplete outputs, use <filter>.