View Source LixParser (LixParser v0.1.0)

Abstract

Lix is a simple Lisp dialect which is parsed by LixParser.parse into an AST.

This AST can then be interpreted as seen fit. An example of a useful interpretation can be found in LixEval which defines a minimum of special forms (def, fn, defn, let and use)

The syntax of Lix is defined by the following doctests

Syntax

Literals

Numbers

  iex(1)> parse("42")
  {:ok, {:lit, 42}}

As long as we get an :ok result we can use parse! too

  iex(2)> parse!("43")
  {:lit, 43}

There are floats too

  iex(3)> parse!("-44.0")
  {:lit, -44.0}

Atoms

  iex(4)> parse!(":atom")
  {:lit, :atom}

Strings

  iex(5)> parse!(~s{"hello"})
  {:lit, "hello"}

Identifiers

As no semantic checks can be made by the parser, the ast is augemented by the position in case of all other constructs.

This will allow an interpreter, evaluator or semantic checker to point to the correct position in the source.

  iex(6)> parse!("variable")
  {:id, {1, 1}, ~W[variable]}

The name of the identifier is a list because we can have compound names, as follows

  iex(7)> parse!("  alpha.beta")
  {:id, {3, 1}, ~W[alpha beta]}

And we can have more of this of course

  iex(8)> parse!("\n\t  a.b.c")
  {:id, {4, 2}, ~W[a b c]}

Note that positioning does not know about tabwidth in this version of Minipeg and thus just assumes tabwidth 1

Operators are just identifiers too

  iex(9)> parse!("+")
  {:id, {1, 1}, ["+"]}

They can consist of up to three symbols

  iex(10)> parse!("@@-")
  {:id, {1, 1}, ["@@-"]}

Compound Expressions

Literals and Identifiers are the only scalar expressions in Lix.

They can be combined into lists ((...)), quoted lists ([...]) and maps ({...}) as follows

Lists (s-expressions)

  iex(11)> parse!("(+ 2 4)")
  {:list, {1, 1}, [{:id, {2, 1}, ["+"]}, {:lit, 2}, {:lit, 4}]}

Quoted lists

  iex(12)> parse!(~s{[(:a) "b"]})
  {:quoted, {1, 1}, [{:list, {2, 1}, [lit: :a]}, {:lit, "b"}]}

Maps

  iex(13)> parse!("{:a 1 b 2}")
  {:map, {1, 1}, [{:lit, :a}, {:lit, 1}, {:id, {7, 1}, ~W[b]}, {:lit, 2}]}

Error Handling

Few, experienced with parsing, would argue that error handling is the most difficult aspect of good parsing.

In this early version of the LixParser error handling is very rudimentary, which is just an euphemisim for outright terrible. Hopefully this will get better along with better support from the underlying PEG library which is Minipeg.

Bad number

This should not parse, but does

  iex(14)> parse("42.")
  {:ok, {:lit, 42}}

  iex(15)> parse(".42")
  {:error, "no alternative could be parsed in  <binary>:1,1"}

Missing closing parens

  iex(16)> parse("(")
  {:error, "no alternative could be parsed in  <binary>:1,1"}

Odd number of elements in a map

  iex(17)> parse("{:a 1 :b}")
  {:error, "no alternative could be parsed in  <binary>:1,1"}

Summary

Types

@type ast_leaf_t() :: {:id, position_t(), binaries()} | {:literal, scalar_t()}
@type ast_t() :: ast_leaf_t() | {:list | :map | :quoted, position_t(), [ast_t()]}
@type binaries() :: [binary()]
@type position_t() :: {pos_integer(), pos_integer()}
@type result_t() :: {:error, binary()} | {:ok, ast_t()}
@type scalar_t() :: atom() | binary() | number()

Functions