bel_parser 1.0.0.alpha.3 → 1.0.0.alpha.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|