• Stars
    star
    13
  • Rank 1,512,713 (Top 30 %)
  • Language
    Crystal
  • License
    GNU Lesser Genera...
  • Created over 7 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🚧 State Machine Compiler for Crystal

Acorn Build Status

🚧 Under Construction πŸ‘·

A state machine compiler with no runtime dependency. Define a grammar using a subset of regular expression notation, then compile it into a blazing-fast state machine. Acorn supports lexers or custom string-based state machines.

Installation

Add this to your application's shard.yml:

dependencies:
  acorn:
    github: "rmosolgo/acorn"

Usage

  • Define the grammar in a build file:

    # ./build/my_lexer.cr
    require "acorn"
    
    class MyLexer < Acorn::Lexer
      # optional, rename the generated class:
      # name("Namespace::MyLexer")
      token :letter "a-z"
      token :number "0-9"
      generate "./src/my_lexer.cr"
    end
  • Generate the lexer:

    crystal run ./build/my_lexer.cr
    
  • Use the compiled lexer:

    require "./src/my_lexer.cr"
    MyLexer.scan(input) # => Array(Tuple(Symbol, String))

Regular Expressions

Tokens are defined with a small regular expression language:

Feature Example
Character a, 1, ❀️
Sequence ab, 123
Alternation a|b
Grouping (ab)|c
Any character .
One of [abc]
Not one of [^abc]
Escape \[, \.
Unicode character range a-z, 0-9
Zero-or-more a*
One-or-more a+
Zero-or-one a?
Specific number a{3}
Between numbers a{3,4}
At least a{3,}

Build Step

An Acorn module is a Crystal program that generates code. To get a lexer, you have to run the Acorn module. Then, your main program should use the generated code.

For example, if you define a lexer:

# build/my_lexer.cr
class MyLexer < Acorn::Lexer
  # ...
  generate("./app/my_lexer.cr")
end

You should run the file with Crystal to generate the specified file:

crystal run build/my_lexer.cr

Then, your main program should require the generated file:

# my_app.cr
require "app/my_lexer"
MyLexer.scan(input) # => Array(Tuple(Symbol, String))

The generated code has no dependency on Acorn, so you only need this library during development.

Tokens

Acorn returns an of array tokens. Each token is a tuple with:

  • Symbol: the name of this token in the lexer definition
  • String: the segment of input which matched the pattern
  • {Int32, Int32}: line number and column number where the token began
  • {Int32, Int32}: line number and column number where the token ended

Line numbers and column numbers are 1-indexed, so the first character in the input is 1:1.

Custom Machines

Acorn lexers are actually a special case of state machine. You can specify a custom machine, too.

  • class MyMachine < Acorn::Machine to bring in the macros
  • alias Accumulator = ... to specify the data that will be modified during the process
    • It will be initialized with .new
    • It will be returned from .scan
  • use action :name, "pattern" { |acc, str, ts, te| ... } to define patterns
    • acc is an instance of your Accumulator
    • str is the original input
    • ts is the index in str where this token began
    • te is the index in str where this token ended (if the match is one character long, ts == te)
  • optionally, name("MyNamespace::MachineName") to rename the generated Crystal class

Development

  • rebuild fixtures with crystal run spec/prepare.cr
  • crystal spec

Goals & Non-Goals

Goals:

  • Great runtime performance
  • Linear time complexity
  • Reasonable code size (.cr file and memory usage)
  • No runtime dependency
  • Plain-Crystal API (source files are .cr, not a special format)
  • Easy to write a lexer, possible to write something else

Non-goals:

  • Great compile-time performance (choose simplicity over performance)
  • Fancy regexp features

TODO

  • Finish regexp language ((...), ., [^...])
    • A move on a is also a move on :any, how is that handled?
  • Better line/col awareness:
    • Add line/col to tokens
    • Add line/col to errors

License

LGPLv3

More Repositories

1

graphql-ruby

Ruby implementation of GraphQL
Ruby
5,367
star
2

graphiql-rails

Mount the GraphiQL query editor in a Rails app
JavaScript
447
star
3

graphql-ruby-demo

Use graphql-ruby to expose a Rails app
Ruby
221
star
4

react-rails-hot-loader

Live-reload React.js components with Ruby on Rails & react-rails
Ruby
88
star
5

prop-types

Turn JSON into React.js PropType specification
Ruby
65
star
6

language-graphql

GraphQL support for Atom text editor
CoffeeScript
57
star
7

graphql-libgraphqlparser-ruby

libgraphqlparser + graphql gem
Ruby
51
star
8

graphql-batch-example

Before & after example with shopify/graphql-batch
Ruby
33
star
9

graphql-ruby-chatroom-example

Example GraphQL-Ruby + Rails app showing subscriptions and persisted queries
Ruby
33
star
10

lingo

parser generator
Crystal
27
star
11

css_modules

Local CSS rules for sass, Rails views and JS
Ruby
23
star
12

react-badges

Make custom badges, built with reactjs
HTML
22
star
13

graphql-ruby-stream-defer-demo

Ruby
20
star
14

graphql-parallel

~~One level of asychronous field resolution for GraphQL in Ruby~~ defunct πŸ”₯
Ruby
12
star
15

crythtal

A Lisp in Crystal
Crystal
8
star
16

mruby-examples

Just a guy tryna learn mruby
C
8
star
17

jsx-ruby

Transform JSX from Ruby. Includes a Tilt template.
Ruby
7
star
18

wishlist-app

The wishlist app I wish existed
Ruby
7
star
19

batfire

Firebase bindings for batman.js
CoffeeScript
6
star
20

ripper_events

A project for learning about Ruby's built-in parser, Ripper
HTML
5
star
21

batmanjs-mvc-cookbook

A Softcover.io project for batman.js recipes
TeX
5
star
22

bramble

Map-Reduce for Rails, ActiveJob and Redis
Ruby
5
star
23

batman-rails-flo

Live reloading batman.js with Ruby on Rails
Ruby
5
star
24

graphql_defer_example

Demo of GraphQL-Ruby + Rails + `@defer` directive
Ruby
4
star
25

bootstrap-teaser

Paragraph preview/teaser for Twitter Bootstrap/jQuery
JavaScript
4
star
26

graphql-ruby-doc-en

Example repo for translating GraphQL-Ruby documentation
CSS
4
star
27

skyblue_rails

Use SkyBlue Sass framework with Ruby on Rails
CSS
4
star
28

graphql-ruby-client

πŸ’€ Moved to graphql-ruby
4
star
29

cancanpreload

Test app with graphql-preload and CanCan auth
Ruby
3
star
30

aiddata-bootswatch

Bootstrap in AidData Blue
JavaScript
3
star
31

rmosolgo.github.io

HTML
3
star
32

linked-list-as-ruby-ext

Immutable linked list for Ruby as a C extension
C
3
star
33

rails-graphql-async-demo

A Rails App with GraphQL-Ruby and Async for parallel HTTP and database queries
Ruby
3
star
34

aiddata-codes

Sector Codes API
JavaScript
2
star
35

aiddata-loan-calculator

Calculate the grant elements for loans with limited info
Ruby
2
star
36

batmanjs-blog

Batman.js blog with Firebase backend
CoffeeScript
2
star
37

aiddata-fs

RESTful file storage for development project records
JavaScript
2
star
38

graphql-ruby-urql-stream-example

Replicating the Urql stream example with GraphQL-Ruby
Ruby
2
star
39

usda-ers-food-dollar

Draw data from the USDA ERS API
Ruby
1
star
40

one-way

Playing around with the idea of Flux in Ruby
Ruby
1
star
41

rufo-autoformat

Autoformat Ruby code with Rufo in Atom
JavaScript
1
star
42

countrysaurus

standardize country names in a CSV
JavaScript
1
star
43

literate-sinatra

Template for literate sinatra app on PG or Mongo
JavaScript
1
star
44

gulp-batman-templates

Gulp plugin to preload HTML in batman.js, a la davemo/grunt-batman-templates
JavaScript
1
star
45

data-structures-crystal

πŸ“š Trying some basic data structures in Crystal
Crystal
1
star
46

graphql-pro-operation-store-example

An example app using GraphQL-Pro's persisted queries
Ruby
1
star
47

graphql-pro-dependabot-test

Ruby
1
star
48

batman-tutorial

wip
Ruby
1
star
49

batmanjs-leaflet-example

Example Batman.js App with custom view for wrapping leaflet.js
JavaScript
1
star
50

batmanjs-tools

CoffeeScript
1
star
51

datavore-bars

CoffeeScript
1
star
52

batman-react-trial

messing around with batman.js and react
JavaScript
1
star
53

batman-rails-example-crepes

A Rails/Batman app to help you run your crepe shop! (Or help you learn Batman :)
Ruby
1
star
54

batmanjs-refresh

Refresh HTML and CSS in your batman.js app
CoffeeScript
1
star
55

aiddata_shirt_summer_13

AidData summer t-shirt submission, 2013
1
star
56

batmanjs-starter

Boilerplate for Firebase-driven batman.js app
CoffeeScript
1
star
57

ripper-preview

A website to see how Ripper parses Ruby code: https://ripper-preview.herokuapp.com/
Ruby
1
star