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.
- checksums.yaml +7 -0
- data/README.md +75 -0
- data/VERSION +1 -0
- data/bin/tla-resolver.rb +7 -0
- data/lib/cli/cli.rb +90 -0
- data/lib/parser/exception.rb +6 -0
- data/lib/parser/lvalue.rb +63 -0
- data/lib/parser/parser.rb +129 -0
- data/lib/parser/parser_nodes.rb +1063 -0
- data/lib/parser/parser_sexp.treetop +442 -0
- data/lib/semantics/context.rb +355 -0
- data/lib/semantics/exception.rb +13 -0
- data/lib/semantics/resolver.rb +327 -0
- data/lib/semantics/symbol_table.rb +139 -0
- data/lib/tla-parser-s.rb +15 -0
- data/lib/utils/logger.rb +80 -0
- data/lib/utils/syntax_node.rb +73 -0
- data/lib/utils/version.rb +13 -0
- data/spec/fixtures/callables1.tla +64 -0
- data/spec/fixtures/directives.tla +7 -0
- data/spec/fixtures/resolver1/comments.tla +1 -0
- data/spec/fixtures/resolver1/directives.cfg +6 -0
- data/spec/fixtures/resolver1/directives.tla +12 -0
- data/spec/fixtures/resolver1/empty.tla +0 -0
- data/spec/fixtures/resolver1/macro1.tla +3 -0
- data/spec/fixtures/resolver1/macro2.tla +3 -0
- data/spec/fixtures/resolver1/op1.tla +3 -0
- data/spec/fixtures/resolver1/op2.tla +3 -0
- data/spec/fixtures/resolver1/proc1.tla +4 -0
- data/spec/fixtures/resolver1/proc2.tla +7 -0
- data/spec/fixtures/resolver1/proc3.tla +4 -0
- data/spec/fixtures/resolver1/proc4.tla +4 -0
- data/spec/fixtures/resolver1/proc4_hide.tla +4 -0
- data/spec/fixtures/resolver1/proc5.tla +4 -0
- data/spec/fixtures/resolver1/proc6.tla +4 -0
- data/spec/fixtures/resolver1/proc7.tla +4 -0
- data/spec/fixtures/resolver1/stmt_assert.tla +8 -0
- data/spec/fixtures/resolver1/stmt_assign.tla +6 -0
- data/spec/fixtures/resolver1/stmt_assign2.tla +6 -0
- data/spec/fixtures/resolver1/stmt_cond.tla +13 -0
- data/spec/fixtures/resolver1/stmt_either.tla +12 -0
- data/spec/fixtures/resolver1/stmt_print.tla +5 -0
- data/spec/fixtures/resolver1/var4.tla +1 -0
- data/spec/fixtures/resolver1/var5.tla +1 -0
- data/spec/fixtures/resolver1/var_choose_except.tla +2 -0
- data/spec/fixtures/resolver1/var_quantify.tla +4 -0
- data/spec/fixtures/resolver1/var_rec_except.tla +4 -0
- data/spec/fixtures/resolver1/var_rec_expr.tla +3 -0
- data/spec/fixtures/resolver1/var_record.tla +2 -0
- data/spec/fixtures/resolver1/var_seq.tla +1 -0
- data/spec/fixtures/resolver1/var_set.tla +1 -0
- data/spec/fixtures/resolver1/var_set_expr_map.tla +1 -0
- data/spec/fixtures/resolver1/var_x.tla +1 -0
- data/spec/fixtures/resolver1/variables.tla +4 -0
- data/spec/parser/parser_fixtures_spec.rb +117 -0
- data/spec/parser/parser_spec.rb +1649 -0
- data/spec/semantics/context_spec.rb +392 -0
- data/spec/semantics/resolver_spec.rb +364 -0
- data/spec/semantics/symbol_table_spec.rb +144 -0
- data/spec/spec_helper.rb +5 -0
- data/tla-parser-s.gemspec +41 -0
- 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
|
data/bin/tla-resolver.rb
ADDED
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,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
|