bel_parser 1.0.0.alpha.3 → 1.0.0.alpha.4
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 +4 -4
- data/VERSION +1 -1
- data/bin/bel2_validator +1 -3
- data/lib/bel_parser/expression/term_validator.rb +1 -1
- data/lib/bel_parser/language/expression_validator.rb +42 -12
- data/lib/bel_parser/language/semantics/signature_mapping.rb +78 -0
- data/lib/bel_parser/language/semantics.rb +35 -2
- data/lib/bel_parser/language/{semantics/semantic_ast.rb → semantics_ast.rb} +3 -3
- data/lib/bel_parser/language/semantics_function.rb +13 -0
- data/lib/bel_parser/language/{semantics/semantic_match.rb → semantics_match.rb} +0 -0
- data/lib/bel_parser/language/semantics_result.rb +23 -0
- data/lib/bel_parser/language/semantics_warning.rb +17 -0
- data/lib/bel_parser/language/syntax/invalid_function.rb +11 -17
- data/lib/bel_parser/language/syntax/undefined_namespace.rb +23 -16
- data/lib/bel_parser/language/syntax_error.rb +1 -4
- data/lib/bel_parser/language/syntax_warning.rb +1 -1
- data/lib/bel_parser/parsers/ast/node.rb +10 -0
- metadata +7 -4
- data/lib/bel_parser/language/semantics/analyzer.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ef8ebaf115a02e0b19b7af93039277e7e31ec98
|
4
|
+
data.tar.gz: 9a34c875fa35ea3926b2c285b7ea031320e39194
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c355a19969807bc4d582a62a66f84cdac544efbd2a2a117d18c53c21dcb2dd0ae9043ce18d801f6475e0e40af1d2886518c3249ec77b3951d8e4b178586ed07
|
7
|
+
data.tar.gz: 0c7e304b87dea0e1b27009d93190a1f862f04e8cc4283f482e3117b55441467f160da8e0925bc05d3b7c4a12119cf6ec71786c2d11084c5a58f4e8bb1147a550
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.0.alpha.
|
1
|
+
1.0.0.alpha.4
|
data/bin/bel2_validator
CHANGED
@@ -13,7 +13,5 @@ namespaces = Hash[ARGV[1..-1].map { |ns| ns.split('=') }]
|
|
13
13
|
require 'bel_parser'
|
14
14
|
require 'bel_parser/expression/term_validator'
|
15
15
|
BELParser::Expression::TermValidator.new(ARGV.first, namespaces).each($stdin) do |res|
|
16
|
-
res.
|
17
|
-
puts " #{res}"
|
18
|
-
end
|
16
|
+
puts res.map { |r| "#{r}\n" }.join.each_line.map { |l| " #{l}" }.join
|
19
17
|
end
|
@@ -44,7 +44,7 @@ if __FILE__ == $PROGRAM_NAME
|
|
44
44
|
namespaces = Hash[ARGV[1..-1].map { |ns| ns.split('=') }]
|
45
45
|
BELParser::Expression::TermValidator.new(ARGV.first, namespaces).each($stdin) do |res|
|
46
46
|
res.each do |res|
|
47
|
-
puts " #{
|
47
|
+
puts res.each_line.map { |l| " #{l}" }.join
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
@@ -5,27 +5,57 @@ module BELParser
|
|
5
5
|
module Language
|
6
6
|
class ExpressionValidator
|
7
7
|
def initialize(spec, namespaces)
|
8
|
-
@spec
|
9
|
-
@namespaces
|
10
|
-
@syntax_functions
|
8
|
+
@spec = spec
|
9
|
+
@namespaces = namespaces
|
10
|
+
@syntax_functions = Syntax.syntax_functions
|
11
|
+
@semantics_functions = Semantics.semantics_functions
|
11
12
|
end
|
12
13
|
|
13
|
-
def validate(
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
def validate(term_ast)
|
15
|
+
results = syntax(term_ast)
|
16
|
+
if results.empty?
|
17
|
+
results << Syntax::Valid.new(term_ast, @spec)
|
18
|
+
results.concat(semantics(term_ast))
|
19
|
+
end
|
20
|
+
results
|
17
21
|
end
|
18
22
|
|
19
23
|
private
|
20
24
|
|
21
|
-
def syntax(
|
22
|
-
|
23
|
-
|
25
|
+
def syntax(term_ast)
|
26
|
+
syntax_results = syntax_term(term_ast).compact
|
27
|
+
end
|
28
|
+
|
29
|
+
def semantics(term_ast)
|
30
|
+
semantics_term(term_ast)
|
31
|
+
end
|
32
|
+
|
33
|
+
def syntax_term(term_ast)
|
34
|
+
syntax_results = @syntax_functions.flat_map do |syntax_func|
|
35
|
+
syntax_func.map(term_ast, @spec, @namespaces)
|
24
36
|
end
|
37
|
+
|
38
|
+
term_ast.arguments
|
39
|
+
.select(&:has_term?)
|
40
|
+
.map(&:child)
|
41
|
+
.each do |child_term|
|
42
|
+
syntax_results.concat(syntax_term(child_term))
|
43
|
+
end
|
44
|
+
syntax_results
|
25
45
|
end
|
26
46
|
|
27
|
-
def
|
28
|
-
|
47
|
+
def semantics_term(term_ast)
|
48
|
+
semantics_results = @semantics_functions.flat_map do |semantics_func|
|
49
|
+
semantics_func.map(term_ast, @spec, @namespaces)
|
50
|
+
end
|
51
|
+
|
52
|
+
term_ast.arguments
|
53
|
+
.select(&:has_term?)
|
54
|
+
.map(&:child)
|
55
|
+
.each do |child_term|
|
56
|
+
semantics_results.concat(semantics_term(child_term))
|
57
|
+
end
|
58
|
+
semantics_results
|
29
59
|
end
|
30
60
|
end
|
31
61
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'bel_parser/parsers/ast/node'
|
2
|
+
|
3
|
+
module BELParser
|
4
|
+
module Language
|
5
|
+
module Semantics
|
6
|
+
class SignatureMapping
|
7
|
+
include SemanticsFunction
|
8
|
+
|
9
|
+
private_class_method :new
|
10
|
+
|
11
|
+
# Map {BELParser::Parsers::AST::Term term} to BEL signatures defined
|
12
|
+
# by a {BELParser::Language::Specification}. The mapping includes both
|
13
|
+
# successful and failed signature matches.
|
14
|
+
def self.map(term_ast, spec, namespaces)
|
15
|
+
unless term_ast.is_a?(BELParser::Parsers::AST::Term)
|
16
|
+
raise(
|
17
|
+
ArgumentError,
|
18
|
+
"term_ast: expected BELParser::Parsers::AST::Term")
|
19
|
+
end
|
20
|
+
|
21
|
+
function_name = term_ast.function.identifier.string_literal
|
22
|
+
function = spec.function(function_name.to_sym)
|
23
|
+
match = BELParser::Language::Semantics.method(:match)
|
24
|
+
|
25
|
+
successes, failures = function.signatures
|
26
|
+
.map { |sig| [sig, match.call(term_ast, sig.semantic_ast, spec)] }
|
27
|
+
.partition { |(sig, results)| results.all?(&:success?) }
|
28
|
+
|
29
|
+
if successes.empty?
|
30
|
+
SignatureMappingWarning.new(term_ast, spec, failures)
|
31
|
+
else
|
32
|
+
SignatureMappingSuccess.new(term_ast, spec, successes, failures)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class SignatureMappingSuccess < SemanticsResult
|
38
|
+
attr_reader :success_signatures
|
39
|
+
attr_reader :failure_signatures
|
40
|
+
|
41
|
+
def initialize(expression_node, spec, successes, failures)
|
42
|
+
super(expression_node, spec)
|
43
|
+
@success_signatures = successes
|
44
|
+
@failure_signatures = failures
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
sig_list = success_signatures
|
49
|
+
.map { |(sig, results)| sig.string_form }
|
50
|
+
.join("\n ")
|
51
|
+
<<-MSG.gsub(/ {12}/, '').gsub(/\n$/, '')
|
52
|
+
Term matches function signatures:
|
53
|
+
#{sig_list}
|
54
|
+
MSG
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class SignatureMappingWarning < SemanticsWarning
|
59
|
+
attr_reader :failure_signatures
|
60
|
+
|
61
|
+
def initialize(expression_node, spec, failure_signatures)
|
62
|
+
super(expression_node, spec)
|
63
|
+
@failure_signatures = failure_signatures
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
sig_list = failure_signatures
|
68
|
+
.map { |(sig, results)| sig.string_form }
|
69
|
+
.join("\n ")
|
70
|
+
<<-MSG.gsub(/ {12}/, '').gsub(/\n$/, '')
|
71
|
+
Term did not conform to function signatures:
|
72
|
+
#{sig_list}
|
73
|
+
MSG
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -1,2 +1,35 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative '
|
1
|
+
require_relative 'semantics_match'
|
2
|
+
require_relative 'semantics_ast'
|
3
|
+
require_relative 'semantics_result'
|
4
|
+
require_relative 'semantics_warning'
|
5
|
+
require_relative 'semantics_function'
|
6
|
+
|
7
|
+
module BELParser
|
8
|
+
module Language
|
9
|
+
module Semantics
|
10
|
+
def self.semantics_functions
|
11
|
+
self.constants.collect do |symbol|
|
12
|
+
const = self.const_get(symbol)
|
13
|
+
const if
|
14
|
+
const.respond_to?(:include?) &&
|
15
|
+
const.include?(SemanticsFunction)
|
16
|
+
end.compact
|
17
|
+
end
|
18
|
+
|
19
|
+
class Valid < SemanticsResult
|
20
|
+
def msg
|
21
|
+
'Semantics are valid.'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Require all semantics functions.
|
29
|
+
Dir[
|
30
|
+
File.join(
|
31
|
+
File.dirname(File.expand_path(__FILE__)),
|
32
|
+
'semantics', '*.rb')
|
33
|
+
].each do |path|
|
34
|
+
require_relative "semantics/#{File.basename(path)}"
|
35
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module BELParser
|
2
|
+
module Language
|
3
|
+
module Semantics
|
4
|
+
module SemanticsFunction
|
5
|
+
# @abstract Include {SemanticsFunction} and override {#map} to check
|
6
|
+
# term semantics (e.g. signatures).
|
7
|
+
def self.map(term_ast, spec, namespaces)
|
8
|
+
raise NotImplementedError, "#{__method__} is not implemented."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module BELParser
|
2
|
+
module Language
|
3
|
+
module Semantics
|
4
|
+
class SemanticsResult
|
5
|
+
attr_reader :expression_node, :specification
|
6
|
+
|
7
|
+
def initialize(expression_node, specification)
|
8
|
+
@expression_node = expression_node
|
9
|
+
@specification = specification
|
10
|
+
end
|
11
|
+
|
12
|
+
# @abstract Subclass and override {#msg} to provide the message.
|
13
|
+
def msg
|
14
|
+
raise NotImplementedError, "#{__method__} is not implemented."
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"Info: #{msg}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'semantics_result'
|
2
|
+
|
3
|
+
module BELParser
|
4
|
+
module Language
|
5
|
+
module Semantics
|
6
|
+
class SemanticsWarning < SemanticsResult
|
7
|
+
def initialize(expression_node, specification)
|
8
|
+
super(expression_node, specification)
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"Warning: #{msg}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -10,33 +10,27 @@ module BELParser
|
|
10
10
|
|
11
11
|
private_class_method :new
|
12
12
|
|
13
|
-
def self.map(
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
function_name = node.identifier.string_literal
|
18
|
-
unless spec.function(function_name.to_sym)
|
19
|
-
errors << InvalidFunctionSyntaxError.new(
|
20
|
-
expression_ast, spec, node, spec.functions)
|
21
|
-
end
|
13
|
+
def self.map(term_node, spec, namespaces)
|
14
|
+
function_name = term_node.function.identifier.string_literal
|
15
|
+
unless spec.function(function_name.to_sym)
|
16
|
+
InvalidFunctionSyntaxError.new(term_node, spec, function_name)
|
22
17
|
end
|
23
|
-
errors
|
24
18
|
end
|
25
19
|
end
|
26
20
|
|
27
21
|
# InvalidFunctionSyntaxError indicates a function name was invalid.
|
28
22
|
class InvalidFunctionSyntaxError < SyntaxError
|
29
|
-
# Gets the
|
30
|
-
|
23
|
+
# Gets the function literal that was invalid according to a
|
24
|
+
# BEL specification.
|
25
|
+
attr_reader :function
|
31
26
|
|
32
|
-
def initialize(
|
33
|
-
super(
|
34
|
-
@
|
27
|
+
def initialize(term_node, spec, function)
|
28
|
+
super(term_node, spec)
|
29
|
+
@function = function
|
35
30
|
end
|
36
31
|
|
37
32
|
def msg
|
38
|
-
|
39
|
-
%Q{Invalid function "#{invalid_function}".}
|
33
|
+
%Q{Invalid function "#{function}".}
|
40
34
|
end
|
41
35
|
end
|
42
36
|
end
|
@@ -10,36 +10,43 @@ module BELParser
|
|
10
10
|
|
11
11
|
private_class_method :new
|
12
12
|
|
13
|
-
def self.map(
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
def self.map(term_node, spec, namespaces)
|
14
|
+
syntax_results = []
|
15
|
+
term_node.arguments
|
16
|
+
.select(&:has_parameter?)
|
17
|
+
.map(&:child)
|
18
|
+
.each do |child_parameter|
|
19
|
+
prefix_identifier = child_parameter.prefix.identifier
|
20
|
+
next if prefix_identifier.nil?
|
21
|
+
|
22
|
+
namespace_prefix = prefix_identifier.string_literal
|
23
|
+
unless namespaces[namespace_prefix]
|
24
|
+
syntax_results << UndefinedNamespaceError.new(term_node, node, namespaces)
|
25
|
+
end
|
23
26
|
end
|
24
|
-
|
25
|
-
errors
|
27
|
+
syntax_results
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
29
31
|
# UndefinedNamespaceError indicates a parameter prefix is referencing
|
30
32
|
# an undefined namespace.
|
31
33
|
class UndefinedNamespaceError < SyntaxError
|
34
|
+
# Gets the invalid prefix.
|
35
|
+
attr_reader :invalid_prefix
|
32
36
|
# Gets the defined namespaces.
|
33
37
|
attr_reader :defined_namespaces
|
34
38
|
|
35
|
-
def initialize(
|
36
|
-
super(
|
39
|
+
def initialize(term_node, spec, invalid_prefix, defined_namespaces)
|
40
|
+
super(term_node, spec, error_node)
|
41
|
+
@invalid_prefix = invalid_prefix
|
37
42
|
@defined_namespaces = defined_namespaces.dup
|
38
43
|
end
|
39
44
|
|
40
45
|
def msg
|
41
|
-
|
42
|
-
|
46
|
+
<<-MSG.gsub(/ {10}/, '')
|
47
|
+
Undefined namespace "#{invalid_prefix}".
|
48
|
+
Defined namespaces are: #{defined_namespaces.keys.join(', ')}
|
49
|
+
MSG
|
43
50
|
end
|
44
51
|
end
|
45
52
|
end
|
@@ -4,11 +4,8 @@ module BELParser
|
|
4
4
|
module Language
|
5
5
|
module Syntax
|
6
6
|
class SyntaxError < SyntaxResult
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(expression_node, specification, target_node)
|
7
|
+
def initialize(expression_node, specification)
|
10
8
|
super(expression_node, specification)
|
11
|
-
@target_node = target_node
|
12
9
|
end
|
13
10
|
|
14
11
|
# @abstract Subclass and override {#msg} to provide the message.
|
@@ -352,6 +352,16 @@ module BELParser
|
|
352
352
|
def initialize(children = [], properties = {})
|
353
353
|
super(Parameter.ast_type, children, properties)
|
354
354
|
end
|
355
|
+
|
356
|
+
# Get the prefix for the parameter.
|
357
|
+
def prefix
|
358
|
+
children[0]
|
359
|
+
end
|
360
|
+
|
361
|
+
# Get the value for the parameter.
|
362
|
+
def value
|
363
|
+
children[1]
|
364
|
+
end
|
355
365
|
end
|
356
366
|
|
357
367
|
# AST node representing a relationship.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bel_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.alpha.
|
4
|
+
version: 1.0.0.alpha.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anthony Bargnesi
|
@@ -36,9 +36,12 @@ files:
|
|
36
36
|
- lib/bel_parser/language/quoting.rb
|
37
37
|
- lib/bel_parser/language/relationship.rb
|
38
38
|
- lib/bel_parser/language/semantics.rb
|
39
|
-
- lib/bel_parser/language/semantics/
|
40
|
-
- lib/bel_parser/language/
|
41
|
-
- lib/bel_parser/language/
|
39
|
+
- lib/bel_parser/language/semantics/signature_mapping.rb
|
40
|
+
- lib/bel_parser/language/semantics_ast.rb
|
41
|
+
- lib/bel_parser/language/semantics_function.rb
|
42
|
+
- lib/bel_parser/language/semantics_match.rb
|
43
|
+
- lib/bel_parser/language/semantics_result.rb
|
44
|
+
- lib/bel_parser/language/semantics_warning.rb
|
42
45
|
- lib/bel_parser/language/signature.rb
|
43
46
|
- lib/bel_parser/language/specification.rb
|
44
47
|
- lib/bel_parser/language/syntax.rb
|
@@ -1,59 +0,0 @@
|
|
1
|
-
require 'bel_parser/parsers/ast/node'
|
2
|
-
|
3
|
-
module BELParser
|
4
|
-
module Language
|
5
|
-
module Semantics
|
6
|
-
# Recursively apply function semantics to each term.
|
7
|
-
# AST Term Property = :function_semantics
|
8
|
-
def self.apply_function_semantics(term, spec)
|
9
|
-
unless term.is_a?(BELParser::Parsers::AST::Term)
|
10
|
-
raise ArgumentError, "term_ast: expected BELParser::Parsers::AST::Term"
|
11
|
-
end
|
12
|
-
|
13
|
-
function_name = term.function.identifier.string_literal
|
14
|
-
function = spec.function(function_name.to_sym)
|
15
|
-
|
16
|
-
valid_signature =
|
17
|
-
function.signatures.select do |sig|
|
18
|
-
match(term, sig.semantic_ast, spec).all?(&:success?)
|
19
|
-
end.max
|
20
|
-
if valid_signature
|
21
|
-
term.function_semantics = valid_signature
|
22
|
-
term.arguments.select(&:has_term?).map(&:child).each do |term|
|
23
|
-
apply_function_semantics(term, spec)
|
24
|
-
end
|
25
|
-
else
|
26
|
-
nullify_function_semantics(term)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.nullify_function_semantics(term)
|
31
|
-
unless term.is_a?(BELParser::Parsers::AST::Term)
|
32
|
-
raise ArgumentError, "term_ast: expected BELParser::Parsers::AST::Term"
|
33
|
-
end
|
34
|
-
|
35
|
-
term.function_semantics = nil
|
36
|
-
term.arguments.select(&:has_term?).map(&:child).each do |term|
|
37
|
-
nullify_function_semantics(term)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.check_term(term, spec)
|
42
|
-
unless term.is_a?(BELParser::Parsers::AST::Term)
|
43
|
-
raise ArgumentError, "term_ast: expected BELParser::Parsers::AST::Term"
|
44
|
-
end
|
45
|
-
|
46
|
-
apply_function_semantics(term, spec)
|
47
|
-
|
48
|
-
semantics = []
|
49
|
-
terms = [term]
|
50
|
-
while !terms.empty? do
|
51
|
-
term = terms.shift
|
52
|
-
semantics << term.function_semantics
|
53
|
-
terms.concat(term.arguments.select(&:has_term?).map(&:child))
|
54
|
-
end
|
55
|
-
semantics
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|