Skip to content

Commit

Permalink
Updates to the ASTNode class.
Browse files Browse the repository at this point in the history
Added some checks for re-definition for values and children.
Removed the requirement that values not be instances of ASTNodes.
  • Loading branch information
chriswailes committed Jun 23, 2014
1 parent 3a042b3 commit c22a84d
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 27 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -485,7 +485,7 @@ class BinOp < Expression
end
```

The assignment functions that are generated for the children and values perform type checking to make sure that the AST is well-formed. The type of a child must be a subclass of the {RLTK::ASTNode} class, whereas the type of a value must **NOT** be a subclass of the {RLTK::ASTNode} class. While child and value objects are stored as instance variables it is unsafe to assign to these variables directly, and it is strongly recommended to always use the accessor functions.
The assignment functions that are generated for the children and values perform type checking to make sure that the AST is well-formed. The type of a child must be a subclass of the {RLTK::ASTNode} class, whereas the type of a value can be any Ruby class. While child and value objects are stored as instance variables it is unsafe to assign to these variables directly, and it is strongly recommended to always use the accessor functions.

When instantiating a subclass of {RLTK::ASTNode} the arguments to the constructor should be the node's values (in order of definition) followed by the node's children (in order of definition). If a constructor is given fewer arguments then the number of values and children the remaining arguments are assumed to be `nil`. Example:

Expand Down
3 changes: 0 additions & 3 deletions TODO.md
Expand Up @@ -40,9 +40,6 @@ Whole Project
Ruby Only
---------

* Allow ASTNode values to be subclasses of ASTNode (for reference nodes)
* Emit a warning when re-defining a value or child element of an AST node

* Double check parse table production method against LALR/LR table production
* Check lookahead pruning method to see if generating LALR(1) or LR(1) parser
* If we are generating LR(1) parser, correct documentation
Expand Down
28 changes: 19 additions & 9 deletions lib/rltk/ast.rb
Expand Up @@ -36,6 +36,19 @@ class ASTNode

class << self

# Check to make sure a name isn't re-defining a value or child.
#
# @raise [ArgumentError] Raised if the name is already used for an existing value or child
def check_odr(name)
if @child_names.include? name
raise ArgumentError, "Class #{self} or one of its superclasses already defines a child named #{name}"
end

if @value_names.include?(name)
raise ArgumentError, "Class #{self} or one of its superclasses already defines a value named #{name}"
end
end

# Installs instance class varialbes into a class.
#
# @return [void]
Expand Down Expand Up @@ -74,6 +87,8 @@ def inherited(klass)
#
# @return [void]
def child(name, type)
check_odr(name)

if type.is_a?(Array) and type.length == 1
t = type.first

Expand Down Expand Up @@ -151,14 +166,15 @@ def define_accessor(name, type, set_parent = false)

# Defined a value for this AST class and its subclasses.
# The name of the value will be used to define accessor
# methods that include type checking. The type of this
# value must NOT be a subclass of the ASTNode class.
# methods that include type checking.
#
# @param [String, Symbol] name Name of value
# @param [Class] type Type of value; Must NOT be a subclass of ASTNode
# @param [Class] type Type of value
#
# @return [void]
def value(name, type)
check_odr(name)

if type.is_a?(Array) and type.length == 1
t = type.first

Expand All @@ -169,12 +185,6 @@ def value(name, type)
raise 'Child and Value types must be a class name or an array with a single class name element.'
end

# Check to make sure that type is NOT a subclass of
# ASTNode.
if t.subclass_of?(ASTNode)
raise "A value's type specification must NOT be a subclass of ASTNode."
end

@value_names << name
@value_types << type
define_accessor(name, type)
Expand Down
40 changes: 26 additions & 14 deletions test/tc_ast.rb
Expand Up @@ -116,20 +116,6 @@ def test_copy
assert_equal(@tree5, new_tree)
end

def test_value
node = VNode.new

assert_equal(node.values, [nil, nil])

node.values = (expected_values = [42, 1984])

assert_equal(node.values, expected_values)

node.values = (expected_values = {:a => 1984, :b => 42})

assert_equal(node.values(Hash), expected_values)
end

def test_dump
tree0_string = @tree0.dump

Expand Down Expand Up @@ -236,8 +222,34 @@ def test_notes
assert_nil(node[:a])
end

def test_one_definition_rule
asserter = self

Class.new(ANode) do
asserter.assert_raises(ArgumentError) { child :left, ANode }
end

Class.new(ENode) do
asserter.assert_raises(ArgumentError) { value :str, String }
end
end

def test_root
assert_same(@tree0, @tree0.root)
assert_same(@tree0, @leaf0.root)
end

def test_value
node = VNode.new

assert_equal(node.values, [nil, nil])

node.values = (expected_values = [42, 1984])

assert_equal(node.values, expected_values)

node.values = (expected_values = {:a => 1984, :b => 42})

assert_equal(node.values(Hash), expected_values)
end
end

0 comments on commit c22a84d

Please sign in to comment.