tla-parser-s 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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