acorn
Version, currently master branch1 version
- master branchlatestApr 20, 2017
github.com/rmosolgo/acorn
🚧 State Machine Compiler for Crystal
13 stars
0 dependents
License: MIT
Installation
# Add this to your shard.yml
dependencies:
acorn:
github: rmosolgo/acorn
branch: mastermaster is a branch, not a release, so this tracks it rather than pinning a version.
Then run:
shards installshard.yml
- Crystal
0.21.0- License
- MIT
- Author
- Robert Mosolgo <rdmosolgo@gmail.com>
Dependencies
This version declares no dependencies.
README
# Acorn [](https://travis-ci.org/rmosolgo/acorn)
🚧 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`:
```yaml
dependencies:
acorn:
github: "rmosolgo/acorn"
```
## Usage
- Define the grammar in a build file:
```ruby
# ./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:
```ruby
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 | <code>a|b</code>
~~Grouping~~ | <code>(ab)|c</code>
~~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:
```ruby
# 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:
```ruby
# 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
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This branch
- Branch
master- Seen
- Apr 20, 2017
- Crystal
0.21.0- Indexed
- yes
Dependents
No indexed shard depends on this one yet.
Repository
github.com/rmosolgo/acorn
Metadata
- Created
- Aug 12, 2026
- Updated
- Aug 12, 2026
- Synced
- Aug 12, 2026
- Versions
- 1