tla-parser-s 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +75 -0
  3. data/VERSION +1 -0
  4. data/bin/tla-resolver.rb +7 -0
  5. data/lib/cli/cli.rb +90 -0
  6. data/lib/parser/exception.rb +6 -0
  7. data/lib/parser/lvalue.rb +63 -0
  8. data/lib/parser/parser.rb +129 -0
  9. data/lib/parser/parser_nodes.rb +1063 -0
  10. data/lib/parser/parser_sexp.treetop +442 -0
  11. data/lib/semantics/context.rb +355 -0
  12. data/lib/semantics/exception.rb +13 -0
  13. data/lib/semantics/resolver.rb +327 -0
  14. data/lib/semantics/symbol_table.rb +139 -0
  15. data/lib/tla-parser-s.rb +15 -0
  16. data/lib/utils/logger.rb +80 -0
  17. data/lib/utils/syntax_node.rb +73 -0
  18. data/lib/utils/version.rb +13 -0
  19. data/spec/fixtures/callables1.tla +64 -0
  20. data/spec/fixtures/directives.tla +7 -0
  21. data/spec/fixtures/resolver1/comments.tla +1 -0
  22. data/spec/fixtures/resolver1/directives.cfg +6 -0
  23. data/spec/fixtures/resolver1/directives.tla +12 -0
  24. data/spec/fixtures/resolver1/empty.tla +0 -0
  25. data/spec/fixtures/resolver1/macro1.tla +3 -0
  26. data/spec/fixtures/resolver1/macro2.tla +3 -0
  27. data/spec/fixtures/resolver1/op1.tla +3 -0
  28. data/spec/fixtures/resolver1/op2.tla +3 -0
  29. data/spec/fixtures/resolver1/proc1.tla +4 -0
  30. data/spec/fixtures/resolver1/proc2.tla +7 -0
  31. data/spec/fixtures/resolver1/proc3.tla +4 -0
  32. data/spec/fixtures/resolver1/proc4.tla +4 -0
  33. data/spec/fixtures/resolver1/proc4_hide.tla +4 -0
  34. data/spec/fixtures/resolver1/proc5.tla +4 -0
  35. data/spec/fixtures/resolver1/proc6.tla +4 -0
  36. data/spec/fixtures/resolver1/proc7.tla +4 -0
  37. data/spec/fixtures/resolver1/stmt_assert.tla +8 -0
  38. data/spec/fixtures/resolver1/stmt_assign.tla +6 -0
  39. data/spec/fixtures/resolver1/stmt_assign2.tla +6 -0
  40. data/spec/fixtures/resolver1/stmt_cond.tla +13 -0
  41. data/spec/fixtures/resolver1/stmt_either.tla +12 -0
  42. data/spec/fixtures/resolver1/stmt_print.tla +5 -0
  43. data/spec/fixtures/resolver1/var4.tla +1 -0
  44. data/spec/fixtures/resolver1/var5.tla +1 -0
  45. data/spec/fixtures/resolver1/var_choose_except.tla +2 -0
  46. data/spec/fixtures/resolver1/var_quantify.tla +4 -0
  47. data/spec/fixtures/resolver1/var_rec_except.tla +4 -0
  48. data/spec/fixtures/resolver1/var_rec_expr.tla +3 -0
  49. data/spec/fixtures/resolver1/var_record.tla +2 -0
  50. data/spec/fixtures/resolver1/var_seq.tla +1 -0
  51. data/spec/fixtures/resolver1/var_set.tla +1 -0
  52. data/spec/fixtures/resolver1/var_set_expr_map.tla +1 -0
  53. data/spec/fixtures/resolver1/var_x.tla +1 -0
  54. data/spec/fixtures/resolver1/variables.tla +4 -0
  55. data/spec/parser/parser_fixtures_spec.rb +117 -0
  56. data/spec/parser/parser_spec.rb +1649 -0
  57. data/spec/semantics/context_spec.rb +392 -0
  58. data/spec/semantics/resolver_spec.rb +364 -0
  59. data/spec/semantics/symbol_table_spec.rb +144 -0
  60. data/spec/spec_helper.rb +5 -0
  61. data/tla-parser-s.gemspec +41 -0
  62. metadata +153 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7a18b7ddd5c763aaf63218b27d64c38c71ccf5fb
4
+ data.tar.gz: 9f343d38938ddcf5cbdfd9abb2a7fcf624f30dee
5
+ SHA512:
6
+ metadata.gz: 2fd17951f4589debafa7dc2bd69bdf9f02ea6d80e64c93d07d667df66340cb8d41b590ef78353c1509d1dd9db5355e81f6281022f231dd1cfba75fc3c549b3b5
7
+ data.tar.gz: 2a4f66ab14732a613469b71a576713db8e3a1df4fae94dc9528b69f830b637ed70f143349abe99ea0aa5ecc67b3d34b4239aca7e656d04a42b2d6973776114af
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ <link href="../site.css" rel="stylesheet"></link>
2
+ [Up](../index.php) [Readme](README.html) [Releases](RELEASES.html) [Todo](TODO.html)
3
+
4
+
5
+ # tla-parser-s - TLA+ language parser (for tla-sbuilder) - $Release:0.1.0$
6
+
7
+ A Ruby library for parsing
8
+ [TLA+ language](http://research.microsoft.com/en-us/um/people/lamport/tla/book.html).
9
+
10
+ ## <a id="USAGE">Usage</a>
11
+
12
+ ### Command line interface
13
+
14
+ Run
15
+
16
+ bin/tla-resolver.rb resolve v_pets --dir-globs demo/*
17
+
18
+ to parse files in `demo/*`, and to output
19
+
20
+ demo/state_pet.tla
21
+
22
+ because `v_pets` in defined in module `state_pet.tla`
23
+
24
+ Run
25
+
26
+ bin/tla-resolver.rb resolve enter_pet --dir-globs demo/*
27
+
28
+
29
+ to parse files in `demo/*`, and to output
30
+
31
+
32
+ demo/transaction_enter_pet.tla
33
+ demo/state_pet.tla
34
+
35
+ because `enter_pet` macro defined in `demo/transaction_enter_pet.tla`
36
+ uses also variable definition `v_pets` defined in `demo/state_pet.tla`.
37
+
38
+
39
+ ### Api usage
40
+
41
+
42
+
43
+ require 'tla-parser-s' # TLA+ parser && resolver
44
+
45
+ # create new context resolver
46
+ resolver = TlaParserS::Resolver.new( options )
47
+
48
+ # Add Globals some constant symbols to resolver (to avoid unresolve warnings)
49
+ GLOBALS = %w( TRUE FALSE Cardinality)
50
+ resolver.initContext( GLOBALS )
51
+
52
+ # parse snippets in dir '"demo/**./*"'
53
+ snippet_files = Dir.glob( "demo/**./*" )
54
+ resolver.initSnippets( snippet_files ) do |status, entry|
55
+ if ! status
56
+ warn "Error parsing #{entry} - continue"
57
+ end
58
+ # continue
59
+ true
60
+ end
61
+
62
+ # resolve depencies
63
+ depencies = resolver.resolveModules( getEntryPoints ) do |type,arr|
64
+ case type
65
+ when "start"
66
+ when "resolved"
67
+ when "unresolved"
68
+ else
69
+ raise "Unkown type #{type}"
70
+ end
71
+ end
72
+
73
+ # dendencies of 'getEntryPoints' array
74
+ depencies
75
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/cli/cli.rb"
4
+
5
+ TlaParserS::Cli.start
6
+
7
+
data/lib/cli/cli.rb ADDED
@@ -0,0 +1,90 @@
1
+ require 'thor'
2
+
3
+ require_relative '../tla-parser-s.rb'
4
+
5
+ module TlaParserS
6
+
7
+ class Cli < Thor
8
+
9
+ include TlaParserS::Utils::MyLogger # mix logger
10
+ PROGNAME = "main" # logger progname
11
+
12
+ VERSION_BANNER = <<-EOS
13
+ #{File.basename $0} - #{TlaParserS::version}
14
+ EOS
15
+
16
+ # ------------------------------------------------------------------
17
+ # make two thor tasks share options?
18
+ # http://stackoverflow.com/questions/14346285/how-to-make-two-thor-tasks-share-options
19
+
20
+ class << self
21
+ def add_shared_option(name, options = {})
22
+ @shared_options = {} if @shared_options.nil?
23
+ @shared_options[name] = options
24
+ end
25
+
26
+ def shared_options(*option_names)
27
+ option_names.each do |option_name|
28
+ opt = @shared_options[option_name]
29
+ raise "Tried to access shared option '#{option_name}' but it was not previously defined" if opt.nil?
30
+ yield option_name, opt if block_given?
31
+ option option_name, opt
32
+ end
33
+ end
34
+ end
35
+
36
+ # ------------------------------------------------------------------
37
+ # constructore
38
+
39
+ def initialize(*args)
40
+ super
41
+ @logger = getLogger( PROGNAME, options )
42
+ @logger.info( "#{__method__} initialized" )
43
+ end
44
+
45
+ # ------------------------------------------------------------------
46
+ # class options
47
+
48
+ class_option :log, :aliases => "-l", :type =>:string, :default => nil,
49
+ :enum => [ "DEBUG", "INFO", "WARN", "ERROR" ],
50
+ :desc => "Set log level "
51
+
52
+ class_option :log_file, :type =>:string, :default => TlaParserS::Utils::MyLogger::LOGFILE,
53
+ :desc => "Set file for log output"
54
+
55
+ # ------------------------------------------------------------------
56
+ # make default task to be version
57
+ default_task :version
58
+
59
+ desc :version, "Output version"
60
+ def version()
61
+ puts VERSION_BANNER
62
+ end
63
+
64
+ # ------------------------------------------------------------------
65
+ # resolver
66
+
67
+ desc "resolve ENTRYPOINTS", "Load modules in 'dir-globs' and resolve modules for [ENTRYPOINTS]"
68
+
69
+ option :dir_globs, :aliases => '-g', :type =>:array, :default => [],
70
+ :desc => "Array of directory globs to load TLA+ modules"
71
+
72
+ option :report_unresolved, :aliases => '-u', :type =>:boolean, :default => true,
73
+ :desc => "Output unresolved symbols to stderr"
74
+
75
+ def resolve( *entrypoints )
76
+ entries = []
77
+ # iterate globs and choose dirtory entries
78
+ options[:dir_globs].each { |glob| entries += Dir.glob( glob ).select { |path| File.file?(path ) } }
79
+ @logger.info( "#{__method__} entries=#{entries}" )
80
+ result = TlaParserS::Resolver.new( options ).
81
+ initSnippets( entries.map { |fileName| File.new(fileName )} ).
82
+ resolveModules( entrypoints )
83
+ @logger.info( "#{__method__} result=#{result}" )
84
+ puts result
85
+ end
86
+
87
+
88
+ end
89
+ end
90
+
@@ -0,0 +1,6 @@
1
+ module TlaParserS
2
+
3
+ class ParseException < Exception
4
+ end
5
+
6
+ end
@@ -0,0 +1,63 @@
1
+ module TlaParserS
2
+
3
+
4
+ module LValue
5
+
6
+ # default implmentation for 'recurse_lvalue' when no block: create expression
7
+ #
8
+ def default_lvalue( memo, node )
9
+ case node.node_type
10
+ when "LValue"
11
+ # when 'Identifier', "LValue"
12
+ memo = node.lvalue
13
+ # when 'RecordField'
14
+ # memo = memo ? "#{memo}.#{node.lvalue}" : node.lvalue
15
+ # when 'RecordFieldName'
16
+ # memo = memo ? "#{memo}[#{node.lvalue}]" : node.lvalue
17
+ when 'UnitExpression'
18
+ # no output - just recurs
19
+ when 'FieldByName'
20
+ memo = "#{memo}.#{node.lvalue}"
21
+ when 'FieldByValue'
22
+ memo = "#{memo}[#{node.lvalue}]"
23
+ else
24
+ raise "Lvalue recursion, parser error unkown node_type #{node_type} #{node.inspect}"
25
+ end
26
+ memo
27
+ end # recurs
28
+
29
+ # @return [String] lvalue
30
+ def lvalue
31
+ # n = recursive_select( Sexp::Root ).first
32
+ # return nil unless n
33
+ recursive_select( Sexp::Identifier ).first.node_value
34
+ end
35
+
36
+
37
+ # Recurse down 'RecordField' structure and create string for
38
+ # lvalue or yield block with 'ret' and 'node'.
39
+ #
40
+ # @param ret [Object|String] to create in recursion
41
+ # @return [String] like A, A.b, A[c], A[d].e
42
+ def recurse_lvalue( ret=nil, &blk )
43
+ if ( blk )
44
+ ret = yield( ret, self )
45
+ else
46
+ ret = default_lvalue( ret, self )
47
+ end
48
+ ret = lvalue_down.recurse_lvalue( ret, &blk ) if lvalue_down
49
+ ret
50
+
51
+ end
52
+
53
+ # Eac lvalue should implement something like this:
54
+ # # @return [nil|RecordField] down next level in hierarchy
55
+ # def lvalue_down
56
+ # down = recursive_select( Sexp::RecordField ).first
57
+ # return nil unless down
58
+ # down.recursive_select( Sexp::RecordField ).first
59
+ # end
60
+
61
+
62
+ end
63
+ end
@@ -0,0 +1,129 @@
1
+ # coding: utf-8
2
+ require 'treetop'
3
+
4
+ # see http://thingsaaronmade.com/blog/a-quick-intro-to-writing-a-parser-using-treetop.html
5
+
6
+ module TlaParserS
7
+
8
+ require_relative "parser_nodes"
9
+
10
+ class Parser
11
+
12
+
13
+ # ------------------------------------------------------------------
14
+ # attributes
15
+
16
+ # Load the Treetop grammar from the 'sexp_parser' file, and
17
+ # create a new instance of that parser as a class variable
18
+ # so we don't have to re-create it every time we need to
19
+ # parse a string
20
+ @@parser = nil
21
+
22
+
23
+ # ------------------------------------------------------------------
24
+ # Logger
25
+
26
+ PROGNAME = "parser" # progname for logger
27
+ include TlaParserS::Utils::MyLogger # mix logger
28
+
29
+ # ------------------------------------------------------------------
30
+ # version
31
+
32
+ # @return version [String] string from file 'VERSION'
33
+ def self.version
34
+ TlaParserS::version
35
+ end
36
+
37
+
38
+
39
+ # ------------------------------------------------------------------
40
+ # constructore
41
+ def initialize( options = {} )
42
+ @logger = getLogger( PROGNAME, options )
43
+ @logger.info( "#{__method__} initialized" )
44
+
45
+ self.loadParsers
46
+ end
47
+
48
+ # @return [SexpParser] cached parser from 'parser_sexp.treetop'
49
+ def loadParsers
50
+ # cached
51
+ return @@parser unless @@parser.nil?
52
+ base_path = File.expand_path(File.dirname(__FILE__))
53
+ parser_path = File.join( base_path, 'parser_sexp.treetop')
54
+ @logger.info( "#{__method__} loading parser from parser path #{parser_path}" )
55
+ Treetop.load( parser_path)
56
+ @@parser = SexpParser.new
57
+ @@parser
58
+ end
59
+
60
+
61
+
62
+ # ------------------------------------------------------------------
63
+ # parse
64
+
65
+
66
+ # @param start [symbol] treep grammar non-terminal to start iwth
67
+ def parse( data, start = nil )
68
+ return nil if data.nil?
69
+
70
+
71
+ # to pass a file
72
+ if data.respond_to? :read
73
+ data = data.read
74
+ # elsif ! data.respond_to? :lines
75
+ # data.lines = data.to_a
76
+ end
77
+
78
+
79
+ # Pass the data over to the parser instance
80
+ parser = getTheParser
81
+ tree = parser.parse(data, :root=>start )
82
+
83
+ # If the AST is nil then there was an error during parsing
84
+ # we need to report a simple error message to help the user
85
+ if(tree.nil?)
86
+ # adopted from http://whitequark.org/blog/2011/09/08/treetop-typical-errors/
87
+ parser.failure_reason =~ /(Expected .*) after/m
88
+ expected = $1.nil? ? parser.failure_reason : $1
89
+ msg= <<-EOS
90
+ Parse error:
91
+ Line : #{parser.failure_line}, offset : #{parser.index}
92
+ #{expected.gsub( "\n", '$NEWLINE')}
93
+ #{data.lines.to_a[ parser.failure_line()-1 ]}
94
+ #{'~' * (parser.failure_column-1)}^
95
+ EOS
96
+ # puts msg
97
+ @logger.error( "#{__method__} #{msg}, for data #{data}" )
98
+ raise ParseException.new msg
99
+ end
100
+
101
+ self.class.clean_tree( tree )
102
+ return tree
103
+ end # parse
104
+
105
+ # @return [SexpParser] static parser to use
106
+ def getTheParser
107
+ @@parser
108
+ end
109
+
110
+ # ------------------------------------------------------------------
111
+ # priv
112
+
113
+ private
114
+
115
+ # we have created a tree from our input, but it has a lot of
116
+ # extraneous nodes that we don’t need or care about. Let’s put in
117
+ # a system for cleaning up the tree:
118
+ def self.clean_tree(root_node)
119
+ return if(root_node.elements.nil?)
120
+ # comment the following line if parse loosees nodes
121
+ root_node.elements.delete_if{|node| node.class.name == "Treetop::Runtime::SyntaxNode" }
122
+ root_node.elements.each {|node| self.clean_tree(node) }
123
+ end
124
+
125
+
126
+
127
+
128
+ end # class
129
+ end # module
@@ -0,0 +1,1063 @@
1
+ require_relative 'lvalue'
2
+
3
+ module Sexp
4
+
5
+ # ------------------------------------------------------------------
6
+ # Abstract
7
+
8
+ class Root <Treetop::Runtime::SyntaxNode
9
+ end
10
+
11
+ class NonTerminal <Root
12
+ end
13
+
14
+
15
+ class RootContainer < Root
16
+
17
+ # @return [String] of from sub-elements
18
+ def value
19
+ self.elements.map {|x| x.value }
20
+ end
21
+
22
+ end
23
+
24
+ class Snippet < RootContainer
25
+ end
26
+
27
+ class Snippets < RootContainer
28
+
29
+ def name
30
+ node_type
31
+ end
32
+
33
+ # @return [Directive:array] of parses' tree nodes
34
+ def directives
35
+ recursive_select( Sexp::Directive )
36
+ end
37
+
38
+ def directive_definitions
39
+ directives.map { |d| d.directives }.flatten
40
+ end
41
+
42
+ # @return [Callable:array] of 'Callable' tree nodes
43
+ def callables
44
+ recursive_select( Sexp::Callable )
45
+ end
46
+
47
+ # @return [array] of 'Define' tree nodes
48
+ def defines
49
+ recursive_select( Sexp::Define )
50
+ end
51
+
52
+ # Snippets is main level parse target, and it adds to symbol table
53
+ # base context entries for 'Define' nodes
54
+ #
55
+ # @return [Array] of hash with ':node_type', :value properties
56
+ #
57
+ def symbol_definitions
58
+ defines.map { |c| { :node_type => c.node_type, :value => c.name, :tree => c } }
59
+ end
60
+
61
+ end
62
+
63
+ # ------------------------------------------------------------------
64
+ # Node lists
65
+
66
+ class IdentifierList <Root
67
+
68
+ def identifier_nodes
69
+ recursive_select( Sexp::Identifier)
70
+ end
71
+ def identifiers
72
+ identifier_nodes.map{ |r| r.value }
73
+ end
74
+ def node_value
75
+ identifiers
76
+ end
77
+
78
+ end
79
+
80
+ class Parameters <IdentifierList
81
+ end
82
+
83
+
84
+
85
+ class ExpressionList <Root
86
+ end
87
+
88
+ # ------------------------------------------------------------------
89
+ # expression
90
+
91
+ # Expression as a binary tree
92
+ class AbstactExpression < Root
93
+
94
+ # @return [String|Integer|etc] from sub-classes with applicable value
95
+ def node_value
96
+ expression_val
97
+ end
98
+
99
+ # @return [AbstractExpression] sub expressions in a binary tree
100
+ def expressions
101
+ # expressions = recursive_select( Sexp::AbstactExpression )
102
+ expressions = elements && elements.select { |e| e.is_a?( Sexp::AbstactExpression ) }
103
+ # raise "Parser error - compound expression should build binary tree" if expressions && expressios.length >2
104
+
105
+ end
106
+
107
+ def has_rhs
108
+ expressions && expressions.length > 1
109
+ end
110
+
111
+ def lhs_node
112
+ expressions && expressions[0]
113
+ end
114
+
115
+ def rhs_node
116
+ expressions[1]
117
+ end
118
+
119
+ # override in sub classes to make `traverse` method to yield
120
+ # `node_value` to block. Block may use `node_value`, or use
121
+ # `node_type` to determine, how to access AST properties on `node`
122
+ #
123
+ # @return [String] non nil value to yield in pre-order
124
+ def expression_prefix
125
+ nil
126
+ end
127
+
128
+ # @return [String] non nil value to yield in in-order
129
+ def expression_val
130
+ nil
131
+ end
132
+
133
+ # @return [String] non nil value to yield in post-order
134
+ def expression_postfix
135
+ nil
136
+ end
137
+
138
+ # walk down binary tree for the expresssion, and yield
139
+ # 'expression_prefix', 'expxression_val', and 'expression_postfix'
140
+ #
141
+ # @param [String] variable contructed in `blk` during `traversal`
142
+ # @returns [String] memo constructured during the traversal
143
+ def traverse( memo="", &blk )
144
+
145
+ memo = yield memo, node_type, self, expression_prefix if blk && expression_prefix
146
+ memo = lhs_node.traverse( memo, &blk ) if lhs_node
147
+ memo = yield memo, node_type, self, expression_val if blk && expression_val
148
+ memo = rhs_node.traverse( memo, &blk ) if has_rhs
149
+ memo = yield memo, node_type, self, expression_postfix if blk && expression_postfix
150
+ memo
151
+
152
+ end
153
+
154
+ end
155
+
156
+ class InfixExpression < AbstactExpression
157
+
158
+ # during traverse show operator in between 'lhs' and 'rhs'
159
+ def expression_val
160
+ operator
161
+ end
162
+
163
+ #
164
+ # @return [String] operator to use (nil if no rhs, else rhs.operator)
165
+ def operator
166
+ return nil if !has_rhs
167
+ rhs_operators = expressions[1].recursive_select( Sexp::Operator )
168
+ return rhs_operators && rhs_operators.any? && rhs_operators.first.node_value
169
+ end
170
+
171
+ end
172
+
173
+ class PrimaryExpression < InfixExpression
174
+
175
+ # AbstractExpression.traverse: postfix expression does not recurse
176
+ # to UnitExpressions, instead yield block iterates using 'attribute_accessors'
177
+
178
+ def expressions
179
+ # expressions = recursive_select( Sexp::AbstactExpression )
180
+ super.select { |e| !e.is_a?(Sexp::UnitExpression) }
181
+ end
182
+
183
+ # AbstactExpression.travers yield if non-nil
184
+ def expression_postfix
185
+ return "has-attribute-accessors" if has_attribute_accessors
186
+ end
187
+
188
+ # Access record by field name or by value
189
+ # @return [AbstactExpression:Array] to acccess record fields
190
+ def attribute_accessors
191
+ deffi = elements.select{ |e| e.is_a?( Sexp::UnitExpression )}.first
192
+ return unless deffi && !deffi.text_value.empty?
193
+ deffi = deffi.recursive_select( Sexp::AbstactExpression )
194
+ return deffi
195
+ end
196
+
197
+
198
+ # @return [boolean] true if expression defines record accessor
199
+ def has_attribute_accessors
200
+ attribute_accessors && attribute_accessors.any?
201
+ end
202
+
203
+ end
204
+
205
+
206
+ class MultitiveExpression < InfixExpression
207
+ end
208
+
209
+ class LValue < Root
210
+
211
+ # AbstractExpression.travers: no automatic traversal
212
+ def expressions
213
+ nil
214
+ end
215
+
216
+ # @return [AbstactExpression] for the LValue
217
+ def expression
218
+ recursive_select( Sexp::AbstactExpression).first
219
+ end
220
+
221
+ # implements lvalue
222
+ include TlaParserS::LValue
223
+
224
+ # Implement traverse down for 'recurse_lvalue'. In my case recurse
225
+ # 'Sexp::UnitExpression', which define record access by value or by name
226
+ #
227
+ # @return [nil|RecordField] down next level in hierarchy
228
+ def lvalue_down
229
+ down = recursive_select( Sexp::UnitExpression ).first
230
+ # down = recursive_select( Sexp::RecordField ).first
231
+ # return nil unless down
232
+ # down.recursive_select( Sexp::RecordField ).first
233
+ end
234
+
235
+ end
236
+
237
+
238
+ class UnitExpression < AbstactExpression
239
+
240
+ # implements lvalue
241
+ include TlaParserS::LValue
242
+
243
+ # Implement traversal for 'recurse_lvalue'
244
+ # @return [nil|AbstractExpression] down next level in hierarchy
245
+
246
+ def lvalue_down
247
+ down = recursive_select( Sexp::AbstactExpression ).first
248
+ end
249
+
250
+
251
+ end
252
+
253
+ class OperatorExpression < AbstactExpression
254
+
255
+ # @return [String] operator name as expression value -->
256
+ # `AbstactExpression` traverse yields in-order
257
+ def expression_val
258
+ operator_name
259
+ end
260
+
261
+ def operator_name
262
+ recursive_select( Sexp::Identifier ).first.node_value
263
+ end
264
+ # @return [Expression:Array] list or arguments
265
+ def arguments
266
+ recursive_select( Sexp::Expression )
267
+ end
268
+
269
+ def record_field_node
270
+ recursive_select( Sexp::RecordField ).first
271
+ end
272
+ def record_field
273
+ node = record_field_node
274
+ return nil unless node
275
+ node.node_value
276
+ end
277
+
278
+
279
+ # @return [Array] empty array to quit recursion in 'AbstactExpression'
280
+ def expressions
281
+ []
282
+ end
283
+ end
284
+
285
+ class PrefixExpression < AbstactExpression
286
+
287
+ # during traverse: show operator before 'expression_val'
288
+ def expression_prefix
289
+ operator
290
+ end
291
+
292
+ def operator_node
293
+ recursive_select( Sexp::Operator ).first
294
+ end
295
+ def operator
296
+ operator_node.text_value
297
+ end
298
+
299
+ end
300
+
301
+
302
+ class SimpleExpression < AbstactExpression
303
+
304
+ # do not automatically recurse during 'traverse'
305
+ def lhs_node
306
+ nil
307
+ end
308
+
309
+
310
+ # def expression_value
311
+ # text_value
312
+ # end
313
+ def expression_val
314
+ text_value
315
+ end
316
+
317
+ end
318
+
319
+ class ChooseExpression < SimpleExpression
320
+
321
+ # @return [BoundInExpression] root node for x \in S
322
+ def binds_node
323
+ recursive_select( Sexp::BoundInExpression ).first
324
+ end
325
+
326
+ # Choose defines 'choose_expression' in left hand size
327
+ # in binary tree modeling expression.
328
+ #
329
+ # @return [AbstractExpression] synxtax tree node for the quentified expression
330
+ def choose_expression
331
+ expressions.first
332
+ end
333
+
334
+
335
+ # For documentation purposes symbol table context needs a name
336
+ # (For procedures, and macros name is ovbious. For a set
337
+ # expression we define name set a string "Set+<generator set>"
338
+ #
339
+ # @return [String] name identifying context in symbol table
340
+ def name
341
+ "Choose-in-#{binds_node.bind_set.text_value}"
342
+ end
343
+
344
+ # @return [Hash:Array] see 'symbol_definition'
345
+ def symbol_definitions
346
+ [symbol_definition]
347
+ end
348
+
349
+ # @return [Hash] of symbol_definition {:node_type,:value,:tree}
350
+ # for the bind variabe in choose
351
+ def symbol_definition
352
+ { :node_type => node_type, :value => binds_node.bind_var.expression_val, :tree=>binds_node }
353
+ end
354
+
355
+
356
+ end
357
+
358
+
359
+ # Parse goal
360
+ class Expression < AbstactExpression
361
+ end
362
+
363
+ class QuantifyExpression < PrefixExpression
364
+
365
+ # do not automatically recurse during 'traverse'
366
+ def lhs_node
367
+ nil
368
+ end
369
+
370
+ def binds_nodes
371
+ recursive_select( Sexp::BindsInExpression ).first.recursive_select( Sexp::BoundInExpression )
372
+ end
373
+
374
+ # Quantification defines 'quantified_expression' in left hand size
375
+ # in binary tree modeling expression.
376
+ #
377
+ # @return [AbstractExpression] synxtax tree node for the quentified expression
378
+ def quantified_expression
379
+ expressions.first
380
+ end
381
+
382
+ # For documentation purposes symbol table context needs a name
383
+ # (For procedures, and macros name is ovbious. For a set
384
+ # expression we define name set a string "Set+<generator set>"
385
+ #
386
+ # @return [String] name identifying context in symbol table
387
+ def name
388
+ "Quantification-??"
389
+ end
390
+
391
+ # Some variables in 'constructor_expression' (most likely) refer
392
+ # to variable defined in set constructor generate. Return name of
393
+ # this variables.
394
+ #
395
+ # @return [Array] of one hash with ':node_type',':value' properties
396
+ def symbol_definitions
397
+ binds_nodes.map do |binds_node|
398
+ { :node_type => node_type, :value => binds_node.bind_var.expression_val, :tree=>binds_node }
399
+ end
400
+ end
401
+
402
+ end
403
+
404
+ class BoundInExpression < Root
405
+ def bind_var
406
+ elements.select{ |e| e.is_a?( Sexp::Identifier ) }.first
407
+ end
408
+ def bind_set
409
+ elements.select{ |e| e.is_a?( Sexp::Expression ) }.first
410
+ end
411
+
412
+ end
413
+
414
+ class BindsInExpression < Root
415
+ end
416
+
417
+ class SetExpressionDef < Root
418
+ end
419
+
420
+
421
+ class ParenthesisExpression < AbstactExpression
422
+ def expression_prefix
423
+ "("
424
+ end
425
+ def expression_postfix
426
+ ")"
427
+ end
428
+ end
429
+
430
+ class RecordExcept < AbstactExpression
431
+ def expressions
432
+ nil
433
+ end
434
+ def lhs_node
435
+ nil
436
+ end
437
+ def rhs_node
438
+ nil
439
+ end
440
+ def expression_val
441
+ "exprsssion"
442
+ end
443
+ def record_identifier
444
+ recursive_select( Sexp::RecordExceptIdentifier ).first.recursive_select( Sexp::Identifier ).first.expression_val
445
+ end
446
+ def record_field_definitions
447
+ # rigth recursion results to empty RecordExceptField -node
448
+ recursive_select( Sexp::RecordExceptField ).select{ |f| f.elements && f.elements.any? }
449
+ end
450
+ end
451
+
452
+ class RecordExceptField < Root
453
+ def lvalue_expression
454
+ recursive_select( Sexp::LValue ).first
455
+ end
456
+ def rvalue_expression
457
+ # first is expression for lvalue
458
+ recursive_select( Sexp::Expression ).last
459
+ end
460
+ end
461
+
462
+ class RecordExceptIdentifier < Root
463
+ end
464
+
465
+
466
+ class RecordDefinition < AbstactExpression
467
+ # output during 'AbstactExpression.traverse'
468
+ def expression_val
469
+ "[]"
470
+ end
471
+
472
+ # @return [RecordElement:Array] of record field definitions
473
+ def record_fields
474
+ ret = recursive_select( Sexp::RecordElement )
475
+ ret
476
+ end
477
+
478
+
479
+ end
480
+
481
+ class RecordField < AbstactExpression
482
+
483
+ # implements lvalue
484
+ include TlaParserS::LValue
485
+
486
+ # Implement traversal for 'recurse_lvalue'
487
+ # @return [nil|RecordField] down next level in hierarchy
488
+ def lvalue_down
489
+ down = recursive_select( Sexp::RecordField ).first
490
+ return nil unless down
491
+ down.recursive_select( Sexp::RecordField ).first
492
+ end
493
+
494
+
495
+ # # @return [String] identifier name for lvalue
496
+ # def lvalue
497
+ # # default recursion creates string for expression lvalue
498
+ # recurse_lvalue
499
+ # end
500
+
501
+ # AbstactExpression.traverse calls once: collect rec.id.field,
502
+ # expression value is the lvalue
503
+ def expression_val
504
+ # return A, A.b, A.b[c]
505
+ recurse_lvalue
506
+ end
507
+
508
+
509
+ end
510
+ class RecordFieldName < RecordField
511
+
512
+ # implements lvalue
513
+ include TlaParserS::LValue
514
+
515
+ # # @return [String] for record value return '[record_field]'
516
+ # def record_field_value
517
+ # "[#{record_name}]"
518
+ # end
519
+
520
+
521
+ end
522
+
523
+ class SequenceExpression < AbstactExpression
524
+ # AbstactExpression.travers
525
+ def expression_val
526
+ "<<>>"
527
+ end
528
+
529
+ # @return [Expression:Array] of sequence expressions
530
+ def tuples
531
+ recursive_select( Sexp::Expression )
532
+ end
533
+ end
534
+
535
+ class AbstractSetExpression < AbstactExpression
536
+ # AbstactExpression.traverse quit traversing the expression
537
+ def lhs_node
538
+ nil
539
+ end
540
+
541
+ # AbstactExpression.traverse calls in yield
542
+ def expression_val
543
+ true
544
+ end
545
+
546
+ # Return set generator ( x \in Set ) for set constructor
547
+ #
548
+ # @return [BoundInExpression|nil] syntax tree node defining
549
+ # generator variable and set
550
+ def binds_node
551
+ ret = recursive_select( Sexp::BoundInExpression ).first
552
+ return nil unless ret
553
+ ret = ret.recursive_select( Sexp::BoundInExpression ).first
554
+ # puts ret
555
+ ret
556
+ end
557
+
558
+ def set_expression
559
+ deffi = recursive_select( Sexp::SetExpressionDef )
560
+ return nil unless deffi && deffi.any?
561
+ ret = deffi.first.elements[0]
562
+ # # ret = deffi.first.recursive_select( Sexp::SetExpression ).first
563
+ # puts "set_expression=#{ret.inspect}"
564
+ ret
565
+ end
566
+
567
+ # For documentation purposes symbol table context needs a name
568
+ # (For procedures, and macros name is ovbious. For a set
569
+ # expression we define name set a string "Set+<generator set>"
570
+ #
571
+ # @return [String] name identifying context in symbol table
572
+ def name
573
+ "Set-#{binds_node.bind_set.text_value}"
574
+ end
575
+ # Some variables in 'set_expression' (most likely) refer to
576
+ # variable defined in set constructor generate. Return name of
577
+ # this variables.
578
+ #
579
+ # @return [Array] of one hash with ':node_type',':value' properties
580
+ def symbol_definitions
581
+ return [] unless binds_node
582
+ [ { :node_type => node_type, :value => binds_node.bind_var.expression_val, :tree=>binds_node } ]
583
+ end
584
+
585
+ end
586
+
587
+ class SetExpressionMap < AbstractSetExpression
588
+ end
589
+
590
+ class SetExpression < AbstractSetExpression
591
+ end
592
+
593
+ class FieldBy < AbstactExpression
594
+ # AbstractExpression.traverse - do not recurse will evaluate separetetely
595
+ def lhs_node
596
+ nil
597
+ end
598
+ def rhs_node
599
+ nil
600
+ end
601
+
602
+
603
+ end
604
+
605
+ class FieldByName < FieldBy
606
+
607
+ # @return [AbstactExpression] defining the name
608
+ def field_name_expression
609
+ recursive_select( Sexp::AbstactExpression ).first
610
+ end
611
+
612
+ def expression_val
613
+ field_name_expression && field_name_expression.expression_val
614
+ end
615
+
616
+ # implements lvalue
617
+ include TlaParserS::LValue
618
+ def lvalue_down
619
+ down = recursive_select( Sexp::UnitExpression ).first
620
+ # down = recursive_select( Sexp::RecordField ).first
621
+ # return nil unless down
622
+ # down.recursive_select( Sexp::RecordField ).first
623
+ end
624
+
625
+
626
+ end
627
+
628
+ class FieldByValue < FieldBy
629
+
630
+ def expression_val
631
+ field_value_expression && field_value_expression.expression_val
632
+ end
633
+
634
+ def field_value_expression
635
+ recursive_select( Sexp::AbstactExpression ).first
636
+ end
637
+
638
+
639
+ # # @return [AbstactExpression] defining the value
640
+ # def val_expression
641
+ # recursive_select( Sexp::AbstactExpression ).first
642
+ # end
643
+
644
+ # implements lvalue
645
+ include TlaParserS::LValue
646
+ def lvalue_down
647
+ down = recursive_select( Sexp::UnitExpression ).first
648
+ # down = recursive_select( Sexp::RecordField ).first
649
+ # return nil unless down
650
+ # down.recursive_select( Sexp::RecordField ).first
651
+ end
652
+
653
+ end
654
+
655
+
656
+
657
+ class UnaryExpression < PrefixExpression
658
+
659
+ end
660
+
661
+ class AdditiveExpression < InfixExpression
662
+ end
663
+
664
+ class Identifier < SimpleExpression
665
+
666
+ # implements lvalue
667
+ include TlaParserS::LValue
668
+
669
+ # No need to recurse down from identifier
670
+ def lvalue_down
671
+ nil
672
+ end
673
+
674
+ # @return [String] identifier name for lvalue
675
+ def lvalue
676
+ node_value
677
+ end
678
+
679
+ end
680
+
681
+ class Self < Identifier
682
+ end
683
+
684
+ class OriginalValue < SimpleExpression
685
+
686
+ # implements lvalue
687
+ include TlaParserS::LValue
688
+
689
+ # No need to recurse down from identifier
690
+ def lvalue_down
691
+ nil
692
+ end
693
+
694
+
695
+ # @return [String] identifier name for lvalue
696
+ def lvalue
697
+ node_value
698
+ end
699
+
700
+ end
701
+
702
+
703
+
704
+ class StringValue < SimpleExpression
705
+ end
706
+
707
+ class IntegerValue < SimpleExpression
708
+ def expression_val
709
+ retun 0 if text_value.nil?
710
+ text_value.to_i
711
+ end
712
+ end
713
+
714
+ # ------------------------------------------------------------------
715
+ # statements
716
+
717
+ # Tree node with 'Label' and Unlabeled statement
718
+ class Statement <Root
719
+
720
+
721
+ # ------------------------------------------------------------------
722
+ # Parsed node structure: Label && Unlabeled statement
723
+
724
+ # Locate 'Label' as direct child. Compound statements may contain
725
+ # statement, which are also labeled, and we cannot just do
726
+ # 'recursive_select'
727
+ def find_labelnode
728
+
729
+ # return labelnode = recursive_select( Sexp::Label ).first
730
+ if !elements.nil? then
731
+ elements.each do |e|
732
+ return e if e.is_a?( Sexp::Label )
733
+ end
734
+ end
735
+ # Label node not found
736
+ nil
737
+ end
738
+
739
+ # @return [Label:SyntaxTree] label assigned to the statement (nil if no label)
740
+ def statement_label
741
+ labelnode = find_labelnode
742
+ return labelnode.label if labelnode
743
+ return nil
744
+ end
745
+
746
+ # @return [UnlabeledStatement:TreeNode] TLA -language statement
747
+ def get_statement
748
+ ret = recursive_select( Sexp::UnlabeledStatement ).first
749
+ ret
750
+ end
751
+
752
+ # tree structure (for compund statments)
753
+ def statements
754
+ nil
755
+ end
756
+
757
+
758
+ # ------------------------------------------------------------------
759
+ # traverse
760
+
761
+ # @return [string] the value for traserval
762
+ def statement_val
763
+ nil
764
+ end
765
+
766
+ def traverse( memo="", &blk )
767
+ # visit label as 'Statement' node
768
+ memo = yield( memo, node_type, self ) if statement_label
769
+
770
+ # visit the real thing 'as object sekf'
771
+ uls = get_statement
772
+ # raise "No unlabeled_statement statement found #{self} from \n '#{inspect}' " unless uls
773
+
774
+ memo = yield( memo, uls.node_type, uls ) if uls
775
+
776
+ statements.each{ |s| memo = s.traverse( memo, &blk ) } if statements
777
+
778
+ memo
779
+ end
780
+
781
+ def node_value
782
+ statement_val
783
+ end
784
+
785
+ end
786
+
787
+
788
+ class UnlabeledStatement < Statement
789
+ def statement_label
790
+ labelnode = parent.statement_label if parent && parent.respond_to?(:statement_label)
791
+ end
792
+
793
+ end
794
+
795
+ class CompoundStatement < UnlabeledStatement
796
+
797
+ def statements
798
+ stmts=recursive_select(Sexp::StatementList).first.recursive_select(Sexp::Statement)
799
+ stmts
800
+
801
+ end
802
+
803
+ end
804
+
805
+ class StatementList < Root
806
+ end
807
+
808
+ class Goto < UnlabeledStatement
809
+
810
+ def goto_label
811
+ recursive_select( Sexp::Identifier).map{ |r| r.node_value }.first
812
+ end
813
+
814
+ end
815
+
816
+ class Print < UnlabeledStatement
817
+
818
+ # @return [Expression] to print
819
+ def print_expression
820
+ recursive_select( Sexp::Expression).first
821
+ end
822
+
823
+ end
824
+
825
+ class Assignment < UnlabeledStatement
826
+
827
+ # @return [LValue] where to assign to
828
+ def lvalue
829
+ recursive_select( Sexp::LValue).first
830
+ end
831
+
832
+ # @return [Expression] expression to assign
833
+ def rvalue
834
+ recursive_select( Sexp::Expression).first
835
+ end
836
+
837
+ end
838
+
839
+
840
+ class Return < UnlabeledStatement
841
+ end
842
+
843
+ class Conditional < UnlabeledStatement
844
+
845
+ def condition
846
+ recursive_select( Sexp::Expression).first
847
+ end
848
+
849
+ def true_or_else
850
+ recursive_select( Sexp::Statement)
851
+ end
852
+
853
+ def if_true
854
+ true_or_else.first
855
+ end
856
+
857
+ def if_not_true
858
+ return true_or_else[1] if true_or_else.length == 2
859
+ end
860
+
861
+ end
862
+
863
+
864
+ class Either < UnlabeledStatement
865
+
866
+ # Return array of choices
867
+ def choices
868
+ recursive_select( Sexp::Statement)
869
+ end
870
+
871
+ end
872
+
873
+
874
+ class Assert < UnlabeledStatement
875
+
876
+ def assertion
877
+ recursive_select( Sexp::Expression).first
878
+ end
879
+
880
+ end
881
+
882
+ class Called < UnlabeledStatement
883
+
884
+ def called
885
+ recursive_select( Sexp::Identifier).map{ |r| r.node_value }.first
886
+ end
887
+
888
+ def statement_val
889
+ "call #{called}"
890
+ end
891
+
892
+ # @return [Array] actual parameters parsed as 'Expression' nodes
893
+ def actual_parameters
894
+ recursive_select( Sexp::Expression)
895
+ end
896
+
897
+
898
+ end
899
+
900
+ class Call < Called
901
+ end
902
+
903
+ class MacroCall < Called
904
+ end
905
+
906
+
907
+ class Skip < UnlabeledStatement
908
+ def statement_val
909
+ text_value
910
+ end
911
+ end
912
+
913
+ # ---------------------------------
914
+ # optional statement label
915
+
916
+ class Label < Root
917
+
918
+ def label
919
+ recursive_select(Sexp::Identifier).first.node_value
920
+ end
921
+
922
+ def node_value
923
+ label if respond_to?( :label )
924
+ end
925
+
926
+ end
927
+
928
+ # ------------------------------------------------------------------
929
+ # directives (assumption, invariants)
930
+ class Directive < Root
931
+
932
+ # @return [String:Array] of directive names
933
+ def directives
934
+ recursive_select( Sexp::Identifier ).map { |i| i.node_value }
935
+ end
936
+
937
+
938
+ end
939
+
940
+ class Invariant < Directive
941
+ end
942
+
943
+ class Assumption < Directive
944
+ end
945
+
946
+ # ------------------------------------------------------------------
947
+ # callable (procedures, macros, processes?)
948
+
949
+ class Define < Root
950
+ def name
951
+ recursive_select(Sexp::Identifier).first.node_value
952
+ end
953
+
954
+ end
955
+
956
+ class Callable < Define
957
+
958
+ def parameters_node
959
+ tree_nodes = recursive_select(Sexp::Parameters)
960
+ return tree_nodes.first if tree_nodes
961
+ end
962
+ def parameters
963
+ node = parameters_node
964
+ return node.value if node
965
+ end
966
+
967
+ def body_node
968
+ ret = recursive_select(Sexp::Statement).first
969
+ ret
970
+ end
971
+ def body
972
+ node = body_node
973
+ node.value if node
974
+ end
975
+
976
+ def node_value
977
+ {
978
+ :name => "name",
979
+ :parameters => parameters,
980
+ :body => body,
981
+ }
982
+ end
983
+
984
+ # Symbols defined in this node
985
+ #
986
+ # @return [Hash:Array] symbol hash with ':node_type', :value,
987
+ # :tree properties
988
+ def symbol_definitions
989
+ parameter_def = parameters
990
+ return [] unless parameter_def
991
+ parameter_def[:value]
992
+ end
993
+ end
994
+
995
+ class Macro < Callable
996
+ end
997
+
998
+ class Procedure < Callable
999
+ end
1000
+
1001
+ class OperatorDef < Callable
1002
+
1003
+ # For operator no body:
1004
+ def body_node
1005
+ tree_nodes = recursive_select(Sexp::Expression)
1006
+ return tree_nodes.first if tree_nodes
1007
+ end
1008
+ end
1009
+
1010
+ # ------------------------------------------------------------------
1011
+ # Variable
1012
+
1013
+ class VariableDef < Define
1014
+
1015
+ #
1016
+ def name
1017
+ recursive_select(Sexp::Identifier).first.node_value
1018
+ end
1019
+
1020
+ # @return [Expression] tree node for init expression
1021
+ def init
1022
+ tree_nodes = recursive_select(Sexp::Expression).first
1023
+ end
1024
+
1025
+ # Return entries for symbol table. In this case, add just variable
1026
+ # definition.
1027
+ # @return [Array] of 1 hash with :node_type, :value -properties
1028
+ def symbol_definitions
1029
+ [ { :node_type => node_type, :value => name } ]
1030
+ end
1031
+
1032
+ end
1033
+
1034
+ class RecordElement < Root
1035
+ def element_name
1036
+ recursive_select( Sexp::Identifier ).first
1037
+ end
1038
+ def element_expression
1039
+ recursive_select( Sexp::Expression ).first
1040
+ end
1041
+ end
1042
+
1043
+
1044
+ # ------------------------------------------------------------------
1045
+ # simple
1046
+
1047
+ class ReservedWord < Root
1048
+ end
1049
+
1050
+ class Operator < Root
1051
+ end
1052
+
1053
+
1054
+
1055
+ # class Body < RootContainer
1056
+ # end
1057
+
1058
+ class VariableDeclaration < RootContainer
1059
+ end
1060
+
1061
+
1062
+
1063
+ end