Skip to content

Add simple tutorial on common APIs #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ $ stree write program.rb
program.rb 1ms
```

For a simple tutorial on how to use SyntaxTree's API, read [USAGE.md](USAGE.md)

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
Expand Down
65 changes: 65 additions & 0 deletions USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# SyntaxTree Usage

## Utilizing SyntaxTree's API

Aside from providing a formatted view of AST nodes, SyntaxTree also provides access to information on each AST node.

This quick tutorial will show you some common APIs in SyntaxTree. Keep in mind this is only a fraction of methods available to use and you can find more in the [official documentation](https://ruby-syntax-tree.github.io/syntax_tree/).

Alright! Let's attain the `:+` operator in the following Ruby source code:

```ruby
require "syntax_tree"
tree = SyntaxTree.parse("puts 1+1")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can use the name of the node we get here:

Suggested change
tree = SyntaxTree.parse("puts 1+1")
program = SyntaxTree.parse("puts 1+1")

This will help in the following example to explain where statements is coming from.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could also do a pp program and show the output in this doc so readers can follow along which nodes we're accessing? Or just show the output like you did in other code snippets:

# => (program (statements (command (ident "puts") (args ((binary (int "1") :+ (int "1")))))))

```

Everything with a block of code inside of it has a list of statements represented by a `SyntaxTree::Statements` node.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here for example it's confusing if you don't know what kind of node is tree.


```ruby
statements = tree.statements
# => (statements (command (ident "puts") (args ((binary (int "1") :+ (int "1"))))))
```

Let's extract the first (and only) statement of our source code that is a `SyntaxTree::Command` node, representing the `puts` method call.

```ruby
puts_command = statements.body.first
# => (command (ident "puts") (args ((binary (int "1") :+ (int "1")))))
```

Using `#child_nodes` we can get an array of child nodes for any particular `SyntaxTree::Node`. In this case, the command node's child nodes are the method name and the arguments.

We are only interested in the arguments, so we can use the instance method `#arguments` to access the `Syntax::Args` node directly.

```ruby
puts_command.child_nodes
# => [(ident "puts"), (args ((binary (int "1") :+ (int "1"))))]

args = puts_command.arguments
# => (args ((binary (int "1") :+ (int "1"))))
```
Comment on lines +30 to +40
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should say this the other way around. The important info here is that you can access #arguments. #child_nodes is more of a helper method.

[(ident "puts"), (args ((binary (int "1") :+ (int "1"))))] ```">
Here, we are only interested in the arguments, so we can use the instance method `#arguments` to access the `Syntax::Args` node directly.

```ruby
args = puts_command.arguments
# => (args ((binary (int "1") :+ (int "1"))))
```

Note that we can also get an array of child nodes for any particular `SyntaxTree::Node` using `#child_nodes`. In this case, the command node's child nodes would be the method name and the arguments.

```ruby
puts_command.child_nodes
# => [(ident "puts"), (args ((binary (int "1") :+ (int "1"))))]
```


The `#parts` method returns an array of arguments. In this case, we want the first argument to access the `SyntaxTree::Binary` node.

```ruby
binary = args.parts.first
# => [(binary (int "1") :+ (int "1"))]
```

A `SyntaxTree::Binary` node represents an expression with two operands and an operator in between, and we can get the operator using the instance method `#operator`.

```ruby
binary.operator
# => :+
```

Lastly, each node has a `SyntaxTree::Location` object, providing information on the row and column of the node.

```ruby
binary.location
# => #
```

## Visiting Nodes

SyntaxTree allows you to perform additional operations on nodes through the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern).