Project

ruco-cpp

0.0
Low commit activity in last 3 years
No release in over a year
Generates an LL(1) parser for a grammar described in a .ruco file
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

Runtime

 Project Readme

Ruco

Ruco generates the boilerplate code for Coco/R. It has a very simple DSL that generates C++ code and the ATG file.

Installation

Add this line to your application's Gemfile:

gem 'ruco-cpp'

And then execute:

$ bundle

Or install it yourself as:

$ gem install ruco-cpp

Usage

A very simple ruco file looks like this:

# mygrammar.ruco

token "Type", :pascal_case 		# There is a token named Type and has PascalCase
token "Name", :camel_case		# There is a token named Name and has camelCase
token "Integer", :integer		# There is a token named Integer and is an integer

grammar "Statement" do 			# This is equivalent to a production in Coco/R
	one Type
	one Name
end

grammar "Format" do 

	one group {					# Group things together using the group method
		one "format" 
		one "{"
		many Statement 			# One or more Statements
		one "}"
	}
end

maybemany Format 				# Zero or more Formats

Code Generation

Generate the code by doing:

$ ruco mygrammar.ruco MyGrammar

This will write the following files in the current directory:

  • MyGrammar.atg - the grammar for coco/R
  • Parser.cpp - parser code generated by coco/R
  • Scanner.cpp - scanner code generated by coco/R
  • parse_MyGrammar.cpp - code for parsing MyGrammar
  • picojson.hpp
  • MyGrammar.hpp - data structures for MyGrammar
  • Parser.h
  • Scanner.h
  • parse_MyGrammar.hpp
  • Makefile - creates one if it does not find one in the current directory. This is a simple Makefile that builds all files in the current directory

Using the generated code

It is up to use the functions in parse_MyGrammar.hpp. There are two functions:

namespace MyGrammar
{
	/**
	 * Parses a source file into the data structure of MyGrammar
	 */
	MyGrammarPtr Parse(std::string sourceFile);

	/**
	 * Transforms the data structure of MyGrammar to an abstract syntax tree in JSON format
	 */
	picojson::value Jsonify(MyGrammarPtr parseResult);
}

Simply call Parse() on a source file that follows your grammar and it will return a std::shared_ptr containing the AST. If you wish to see or process the AST with another tool, you can export it by calling Jsonify() on the parse result.

The following example main.cpp takes a file called test.lang and outputs the AST of the file in JSON format to the console.

#include "parse_MyGrammar.hpp"

int main()
{
	auto s = MyGrammar::Parse("test.lang");
	auto json = MyGrammar::Jsonify(s);
	
	std::wcout << json.serialize() << std::endl;

	return EXIT_SUCCESS;
}

Building the code

As shown above ruco will generate a Makefile for you. However the code will not compile unless you have a main() function. You need to write this code yourself.

Feel free to ignore the Makefile if you wish to use the code generated as a library instead.

Reference

Ruco files are made of statements that define elements of your language.

  • token - A string of characters that meet a particular criteria
  • variation - A set of grammars or tokens that can appear in the same place (are variations of an element)
  • grammar - A production that contains a sub-set of your language

Within each grammar you need to declare elements of your language. The file itself is a grammar.

  • group - a set of elements that come one after another
  • either - a set of elements that can appear in the same place
  • one - one of this element
  • many - one or more of this element
  • maybe - zero or one of this element
  • maybemany - zero or more of this element

More details are shown below:

token

A token takes a string and a type of token it is. Tokens available are:

  • :camelcase
  • :pascalcase
  • :integer
  • :string

Example:

token "VariableName", :camelcase

variation

A variation is an element that is the base production of another set of elements.

Example:

grammar "IntegerLiteral" do
	one Number
end

grammar "MemberName" do
	one MemberIdentifier
end

variation "Expression", MemberName, IntegerLiteral

grammar

A grammar declares a subset of the language.

Example:

grammar "ArrayDef" do
	one "["
	one Expression
	one "]"
end

Getting Started

Here is a simple tutorial to get you started using ruco!

First of all, make a new directory and enter it.

mkdir tutorial
cd tutorial

Then, make a Gemfile and specify that you want to use ruco-cpp

source "https://rubygems.org"

gem "ruco-cpp"

Install ruco-cpp this way

bundle

Now let us make a simple grammar that can parse an addition operation like 1+1

In order to do so, we need to specify that there is a number, followed by a plus sign, followed by a number.

We then need to create a new file lets call it example.ruco and write the following in it:

token "NumberLiteral", :number

grammar "Binary" do
	one NumberLiteral
	one "+"
	one NumberLiteral
end

one Binary

Also we will need a test case. So let us make a file called test.example with the following:

5 + 6

Excellent. Now in the command line, we run ruco like so:

bundle exec ruco example.ruco

This will produce all the files needed to run this example, and generates an API to access the AST.

For now, let us simply compile it:

make

After this, simply run the test application that was created like this:

./example_parse

You should see some JSON in the output. The following is pretty printed, but you should receive a similar output anyway.

{
  "_col": 1,
  "_line": 1,
  "_type": "Example",
  "binary": {
    "_col": 1,
    "_line": 1,
    "_type": "Binary",
    "numberliterals": [
      {
        "_col": 1,
        "_line": 1,
        "_token": "5",
        "_type": "NumberLiteral"
      },
      {
        "_col": 3,
        "_line": 1,
        "_token": "6",
        "_type": "NumberLiteral"
      }
    ]
  }
}

This is the AST of the test file test.example we created earlier. Now you can make use of this AST to do whatever processing you want for your compiler!

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake rspec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/davidsiaw/ruco. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.