2022-03-08 17:03:14 +03:00
|
|
|
(* This file is part of the Catala compiler, a specification language for tax
|
|
|
|
and social benefits computation rules. Copyright (C) 2020 Inria, contributor:
|
|
|
|
Nicolas Chataing <nicolas.chataing@ens.fr>
|
2020-11-23 11:22:47 +03:00
|
|
|
|
2022-03-08 17:03:14 +03:00
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
|
|
use this file except in compliance with the License. You may obtain a copy of
|
|
|
|
the License at
|
2020-11-23 11:22:47 +03:00
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
2022-03-08 17:03:14 +03:00
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
License for the specific language governing permissions and limitations under
|
2020-11-23 11:22:47 +03:00
|
|
|
the License. *)
|
|
|
|
|
2022-03-08 17:03:14 +03:00
|
|
|
(** Scope dependencies computations using {{:http://ocamlgraph.lri.fr/}
|
|
|
|
OCamlgraph} *)
|
2020-12-14 19:00:42 +03:00
|
|
|
|
2021-01-21 23:33:04 +03:00
|
|
|
open Utils
|
2020-11-23 11:22:47 +03:00
|
|
|
|
2021-01-13 21:07:35 +03:00
|
|
|
(** {1 Scope variables dependency graph} *)
|
|
|
|
|
|
|
|
(** {2 Graph declaration} *)
|
2020-11-23 11:22:47 +03:00
|
|
|
|
2020-12-14 19:00:42 +03:00
|
|
|
(** Vertices: scope variables or subscopes.
|
2020-11-23 11:22:47 +03:00
|
|
|
|
2020-12-14 19:00:42 +03:00
|
|
|
The vertices of the scope dependency graph are either :
|
2020-11-23 11:22:47 +03:00
|
|
|
|
2020-12-14 19:00:42 +03:00
|
|
|
- the variables of the scope ;
|
|
|
|
- the subscopes of the scope.
|
2020-11-23 11:22:47 +03:00
|
|
|
|
2020-12-14 19:00:42 +03:00
|
|
|
Indeed, during interpretation, subscopes are executed atomically. *)
|
2020-11-23 11:22:47 +03:00
|
|
|
module Vertex = struct
|
2022-02-28 19:19:06 +03:00
|
|
|
type t =
|
|
|
|
| Var of Ast.ScopeVar.t * Ast.StateName.t option
|
|
|
|
| SubScope of Scopelang.Ast.SubScopeName.t
|
2020-11-23 11:22:47 +03:00
|
|
|
|
2020-11-23 14:20:38 +03:00
|
|
|
let hash x =
|
2020-11-23 18:12:45 +03:00
|
|
|
match x with
|
2022-02-28 19:19:06 +03:00
|
|
|
| Var (x, None) -> Ast.ScopeVar.hash x
|
2022-03-08 17:03:14 +03:00
|
|
|
| Var (x, Some sx) ->
|
|
|
|
Int.logxor (Ast.ScopeVar.hash x) (Ast.StateName.hash sx)
|
2020-11-23 18:12:45 +03:00
|
|
|
| SubScope x -> Scopelang.Ast.SubScopeName.hash x
|
2020-11-23 11:22:47 +03:00
|
|
|
|
|
|
|
let compare = compare
|
|
|
|
|
|
|
|
let equal x y =
|
|
|
|
match (x, y) with
|
2022-02-28 19:19:06 +03:00
|
|
|
| Var (x, None), Var (y, None) -> Ast.ScopeVar.compare x y = 0
|
|
|
|
| Var (x, Some sx), Var (y, Some sy) ->
|
|
|
|
Ast.ScopeVar.compare x y = 0 && Ast.StateName.compare sx sy = 0
|
2020-11-23 14:20:38 +03:00
|
|
|
| SubScope x, SubScope y -> Scopelang.Ast.SubScopeName.compare x y = 0
|
2020-11-23 11:22:47 +03:00
|
|
|
| _ -> false
|
|
|
|
|
2020-11-25 13:53:56 +03:00
|
|
|
let format_t (fmt : Format.formatter) (x : t) : unit =
|
2020-11-23 18:12:45 +03:00
|
|
|
match x with
|
2022-02-28 19:19:06 +03:00
|
|
|
| Var (v, None) -> Ast.ScopeVar.format_t fmt v
|
|
|
|
| Var (v, Some sv) ->
|
2022-03-08 17:03:14 +03:00
|
|
|
Format.fprintf fmt "%a.%a" Ast.ScopeVar.format_t v
|
|
|
|
Ast.StateName.format_t sv
|
2020-11-25 13:53:56 +03:00
|
|
|
| SubScope v -> Scopelang.Ast.SubScopeName.format_t fmt v
|
2020-11-23 11:22:47 +03:00
|
|
|
end
|
|
|
|
|
2022-03-08 17:03:14 +03:00
|
|
|
(** On the edges, the label is the position of the expression responsible for
|
|
|
|
the use of the variable. In the graph, [x -> y] if [x] is used in the
|
|
|
|
definition of [y].*)
|
2020-11-23 11:22:47 +03:00
|
|
|
module Edge = struct
|
|
|
|
type t = Pos.t
|
|
|
|
|
|
|
|
let compare = compare
|
|
|
|
let default = Pos.no_pos
|
|
|
|
end
|
|
|
|
|
2022-03-08 17:03:14 +03:00
|
|
|
module ScopeDependencies =
|
|
|
|
Graph.Persistent.Digraph.ConcreteBidirectionalLabeled (Vertex) (Edge)
|
2020-12-14 19:00:42 +03:00
|
|
|
(** Module of the graph, provided by OCamlGraph *)
|
|
|
|
|
2020-11-23 11:22:47 +03:00
|
|
|
module TopologicalTraversal = Graph.Topological.Make (ScopeDependencies)
|
2020-12-14 19:00:42 +03:00
|
|
|
(** Module of the topological traversal of the graph, provided by OCamlGraph *)
|
2020-11-23 11:22:47 +03:00
|
|
|
|
|
|
|
module SCC = Graph.Components.Make (ScopeDependencies)
|
|
|
|
(** Tarjan's stongly connected components algorithm, provided by OCamlGraph *)
|
|
|
|
|
2021-01-13 21:07:35 +03:00
|
|
|
(** {2 Graph computations} *)
|
2020-12-14 19:00:42 +03:00
|
|
|
|
2022-03-08 17:03:14 +03:00
|
|
|
(** Returns an ordering of the scope variables and subscope compatible with the
|
|
|
|
dependencies of the computation *)
|
2020-11-25 16:35:26 +03:00
|
|
|
let correct_computation_ordering (g : ScopeDependencies.t) : Vertex.t list =
|
|
|
|
List.rev (TopologicalTraversal.fold (fun sd acc -> sd :: acc) g [])
|
|
|
|
|
2020-11-23 11:22:47 +03:00
|
|
|
(** Outputs an error in case of cycles. *)
|
2020-11-25 13:53:56 +03:00
|
|
|
let check_for_cycle (scope : Ast.scope) (g : ScopeDependencies.t) : unit =
|
2022-03-08 17:03:14 +03:00
|
|
|
(* if there is a cycle, there will be an strongly connected component of
|
|
|
|
cardinality > 1 *)
|
2020-11-23 11:22:47 +03:00
|
|
|
let sccs = SCC.scc_list g in
|
|
|
|
if List.length sccs < ScopeDependencies.nb_vertex g then
|
|
|
|
let scc = List.find (fun scc -> List.length scc > 1) sccs in
|
2022-03-08 15:04:27 +03:00
|
|
|
let spans =
|
|
|
|
List.flatten
|
|
|
|
(List.map
|
|
|
|
(fun v ->
|
|
|
|
let var_str, var_info =
|
|
|
|
match v with
|
|
|
|
| Vertex.Var (v, None) ->
|
2022-03-08 17:03:14 +03:00
|
|
|
( Format.asprintf "%a" Ast.ScopeVar.format_t v,
|
|
|
|
Ast.ScopeVar.get_info v )
|
2022-03-08 15:04:27 +03:00
|
|
|
| Vertex.Var (v, Some sv) ->
|
2022-03-08 17:03:14 +03:00
|
|
|
( Format.asprintf "%a.%a" Ast.ScopeVar.format_t v
|
|
|
|
Ast.StateName.format_t sv,
|
2022-03-08 15:04:27 +03:00
|
|
|
Ast.StateName.get_info sv )
|
|
|
|
| Vertex.SubScope v ->
|
|
|
|
( Format.asprintf "%a" Scopelang.Ast.SubScopeName.format_t v,
|
|
|
|
Scopelang.Ast.SubScopeName.get_info v )
|
|
|
|
in
|
|
|
|
let succs = ScopeDependencies.succ_e g v in
|
2022-03-08 17:03:14 +03:00
|
|
|
let _, edge_pos, succ =
|
|
|
|
List.find (fun (_, _, succ) -> List.mem succ scc) succs
|
|
|
|
in
|
2022-03-08 15:04:27 +03:00
|
|
|
let succ_str =
|
|
|
|
match succ with
|
2022-03-08 17:03:14 +03:00
|
|
|
| Vertex.Var (v, None) ->
|
|
|
|
Format.asprintf "%a" Ast.ScopeVar.format_t v
|
2022-03-08 15:04:27 +03:00
|
|
|
| Vertex.Var (v, Some sv) ->
|
2022-03-08 17:03:14 +03:00
|
|
|
Format.asprintf "%a.%a" Ast.ScopeVar.format_t v
|
|
|
|
Ast.StateName.format_t sv
|
|
|
|
| Vertex.SubScope v ->
|
|
|
|
Format.asprintf "%a" Scopelang.Ast.SubScopeName.format_t v
|
2022-03-08 15:04:27 +03:00
|
|
|
in
|
|
|
|
[
|
2022-03-08 17:03:14 +03:00
|
|
|
( Some ("Cycle variable " ^ var_str ^ ", declared:"),
|
|
|
|
Pos.get_position var_info );
|
|
|
|
( Some
|
|
|
|
("Used here in the definition of another cycle variable "
|
|
|
|
^ succ_str ^ ":"),
|
2022-03-08 15:04:27 +03:00
|
|
|
edge_pos );
|
|
|
|
])
|
|
|
|
scc)
|
|
|
|
in
|
|
|
|
Errors.raise_multispanned_error spans
|
2022-03-08 17:03:14 +03:00
|
|
|
"Cyclic dependency detected between variables of scope %a!"
|
|
|
|
Scopelang.Ast.ScopeName.format_t scope.scope_uid
|
2020-11-23 11:22:47 +03:00
|
|
|
|
2020-12-14 19:00:42 +03:00
|
|
|
(** Builds the dependency graph of a particular scope *)
|
2020-11-25 13:53:56 +03:00
|
|
|
let build_scope_dependencies (scope : Ast.scope) : ScopeDependencies.t =
|
|
|
|
let g = ScopeDependencies.empty in
|
|
|
|
(* Add all the vertices to the graph *)
|
|
|
|
let g =
|
2022-02-28 19:19:06 +03:00
|
|
|
Ast.ScopeVarMap.fold
|
|
|
|
(fun (v : Ast.ScopeVar.t) var_or_state g ->
|
|
|
|
match var_or_state with
|
|
|
|
| Ast.WholeVar -> ScopeDependencies.add_vertex g (Vertex.Var (v, None))
|
|
|
|
| Ast.States states ->
|
|
|
|
List.fold_left
|
2022-03-08 17:03:14 +03:00
|
|
|
(fun g state ->
|
|
|
|
ScopeDependencies.add_vertex g (Vertex.Var (v, Some state)))
|
2022-02-28 19:19:06 +03:00
|
|
|
g states)
|
2020-11-25 13:53:56 +03:00
|
|
|
scope.scope_vars g
|
|
|
|
in
|
|
|
|
let g =
|
|
|
|
Scopelang.Ast.SubScopeMap.fold
|
2020-11-25 16:35:26 +03:00
|
|
|
(fun (v : Scopelang.Ast.SubScopeName.t) _ g ->
|
2020-11-25 13:53:56 +03:00
|
|
|
ScopeDependencies.add_vertex g (Vertex.SubScope v))
|
|
|
|
scope.scope_sub_scopes g
|
|
|
|
in
|
|
|
|
let g =
|
|
|
|
Ast.ScopeDefMap.fold
|
2022-01-05 11:14:43 +03:00
|
|
|
(fun def_key scope_def g ->
|
|
|
|
let def = scope_def.Ast.scope_def_rules in
|
2020-11-25 13:53:56 +03:00
|
|
|
let fv = Ast.free_variables def in
|
2020-11-27 13:37:21 +03:00
|
|
|
Ast.ScopeDefMap.fold
|
|
|
|
(fun fv_def fv_def_pos g ->
|
2020-11-25 13:53:56 +03:00
|
|
|
match (def_key, fv_def) with
|
2022-03-08 17:03:14 +03:00
|
|
|
| ( Ast.ScopeDef.Var (v_defined, s_defined),
|
|
|
|
Ast.ScopeDef.Var (v_used, s_used) ) ->
|
2020-11-25 13:53:56 +03:00
|
|
|
(* simple case *)
|
2022-02-28 19:19:06 +03:00
|
|
|
if v_used = v_defined && s_used = s_defined then
|
2020-11-27 13:37:21 +03:00
|
|
|
(* variable definitions cannot be recursive *)
|
2022-03-08 15:04:27 +03:00
|
|
|
Errors.raise_spanned_error fv_def_pos
|
2022-03-08 17:03:14 +03:00
|
|
|
"The variable %a is used in one of its definitions, but \
|
|
|
|
recursion is forbidden in Catala"
|
2022-03-08 15:04:27 +03:00
|
|
|
Ast.ScopeDef.format_t def_key
|
2020-11-27 14:15:54 +03:00
|
|
|
else
|
|
|
|
let edge =
|
2022-02-28 19:19:06 +03:00
|
|
|
ScopeDependencies.E.create
|
|
|
|
(Vertex.Var (v_used, s_used))
|
|
|
|
fv_def_pos
|
|
|
|
(Vertex.Var (v_defined, s_defined))
|
2020-11-27 14:15:54 +03:00
|
|
|
in
|
|
|
|
ScopeDependencies.add_edge_e g edge
|
2022-03-08 17:03:14 +03:00
|
|
|
| ( Ast.ScopeDef.SubScopeVar (defined, _),
|
|
|
|
Ast.ScopeDef.Var (v_used, s_used) ) ->
|
|
|
|
(* here we are defining the input of a subscope using a var of
|
|
|
|
the scope *)
|
2020-11-27 14:15:54 +03:00
|
|
|
let edge =
|
2022-02-28 19:19:06 +03:00
|
|
|
ScopeDependencies.E.create
|
|
|
|
(Vertex.Var (v_used, s_used))
|
|
|
|
fv_def_pos (Vertex.SubScope defined)
|
2020-11-27 14:15:54 +03:00
|
|
|
in
|
|
|
|
ScopeDependencies.add_edge_e g edge
|
2022-03-08 17:03:14 +03:00
|
|
|
| ( Ast.ScopeDef.SubScopeVar (defined, _),
|
|
|
|
Ast.ScopeDef.SubScopeVar (used, _) ) ->
|
|
|
|
(* here we are defining the input of a scope with the output of
|
|
|
|
another subscope *)
|
2020-11-27 13:37:21 +03:00
|
|
|
if used = defined then
|
|
|
|
(* subscopes are not recursive functions *)
|
2022-03-08 15:04:27 +03:00
|
|
|
Errors.raise_spanned_error fv_def_pos
|
2022-03-08 17:03:14 +03:00
|
|
|
"The subscope %a is used when defining one of its inputs, \
|
|
|
|
but recursion is forbidden in Catala"
|
2022-03-08 15:04:27 +03:00
|
|
|
Scopelang.Ast.SubScopeName.format_t defined
|
2020-11-27 14:15:54 +03:00
|
|
|
else
|
|
|
|
let edge =
|
|
|
|
ScopeDependencies.E.create (Vertex.SubScope used) fv_def_pos
|
|
|
|
(Vertex.SubScope defined)
|
|
|
|
in
|
|
|
|
ScopeDependencies.add_edge_e g edge
|
2022-03-08 17:03:14 +03:00
|
|
|
| ( Ast.ScopeDef.Var (v_defined, s_defined),
|
|
|
|
Ast.ScopeDef.SubScopeVar (used, _) ) ->
|
|
|
|
(* finally we define a scope var with the output of a
|
|
|
|
subscope *)
|
2020-11-27 14:15:54 +03:00
|
|
|
let edge =
|
2022-02-28 19:19:06 +03:00
|
|
|
ScopeDependencies.E.create (Vertex.SubScope used) fv_def_pos
|
|
|
|
(Vertex.Var (v_defined, s_defined))
|
2020-11-27 14:15:54 +03:00
|
|
|
in
|
|
|
|
ScopeDependencies.add_edge_e g edge)
|
2020-11-25 13:53:56 +03:00
|
|
|
fv g)
|
|
|
|
scope.scope_defs g
|
|
|
|
in
|
|
|
|
g
|
2021-01-13 21:07:35 +03:00
|
|
|
|
|
|
|
(** {1 Exceptions dependency graph} *)
|
|
|
|
|
|
|
|
(** {2 Graph declaration} *)
|
|
|
|
|
|
|
|
module ExceptionVertex = struct
|
2022-01-05 11:14:43 +03:00
|
|
|
include Ast.RuleSet
|
|
|
|
|
2022-03-08 17:03:14 +03:00
|
|
|
let hash (x : t) : int =
|
|
|
|
Ast.RuleSet.fold (fun r acc -> Int.logxor (Ast.RuleName.hash r) acc) x 0
|
2021-01-13 21:07:35 +03:00
|
|
|
|
|
|
|
let equal x y = compare x y = 0
|
|
|
|
end
|
|
|
|
|
|
|
|
module ExceptionsDependencies =
|
|
|
|
Graph.Persistent.Digraph.ConcreteBidirectionalLabeled (ExceptionVertex) (Edge)
|
2022-03-08 17:03:14 +03:00
|
|
|
(** Module of the graph, provided by OCamlGraph. [x -> y] if [y] is an exception
|
|
|
|
to [x] *)
|
2021-01-13 21:07:35 +03:00
|
|
|
|
|
|
|
module ExceptionsSCC = Graph.Components.Make (ExceptionsDependencies)
|
|
|
|
(** Tarjan's stongly connected components algorithm, provided by OCamlGraph *)
|
|
|
|
|
|
|
|
(** {2 Graph computations} *)
|
|
|
|
|
2022-03-08 17:03:14 +03:00
|
|
|
let build_exceptions_graph
|
2022-05-04 19:40:28 +03:00
|
|
|
(def : Ast.rule Ast.RuleMap.t)
|
|
|
|
(def_info : Ast.ScopeDef.t) : ExceptionsDependencies.t =
|
2022-01-05 11:14:43 +03:00
|
|
|
(* first we collect all the rule sets referred by exceptions *)
|
|
|
|
let all_rule_sets_pointed_to_by_exceptions : Ast.RuleSet.t list =
|
2021-01-13 21:07:35 +03:00
|
|
|
Ast.RuleMap.fold
|
2022-01-05 11:14:43 +03:00
|
|
|
(fun _rule_name rule acc ->
|
2022-03-08 17:03:14 +03:00
|
|
|
if Ast.RuleSet.is_empty (Pos.unmark rule.Ast.rule_exception_to_rules)
|
|
|
|
then acc
|
2022-01-05 11:14:43 +03:00
|
|
|
else Pos.unmark rule.Ast.rule_exception_to_rules :: acc)
|
|
|
|
def []
|
|
|
|
in
|
2022-03-08 17:03:14 +03:00
|
|
|
(* we make sure these sets are either disjoint or equal ; should be a
|
|
|
|
syntactic invariant since you currently can't assign two labels to a single
|
|
|
|
rule but an extra check is valuable since this is a required invariant for
|
|
|
|
the graph to be sound *)
|
2022-01-05 11:14:43 +03:00
|
|
|
List.iter
|
|
|
|
(fun rule_set1 ->
|
|
|
|
List.iter
|
|
|
|
(fun rule_set2 ->
|
|
|
|
if Ast.RuleSet.equal rule_set1 rule_set2 then ()
|
|
|
|
else if Ast.RuleSet.disjoint rule_set1 rule_set2 then ()
|
|
|
|
else
|
2022-03-08 15:04:27 +03:00
|
|
|
let spans =
|
|
|
|
List.of_seq
|
|
|
|
(Seq.map
|
|
|
|
(fun rule ->
|
|
|
|
( Some "Rule or definition from the first group:",
|
|
|
|
Pos.get_position (Ast.RuleName.get_info rule) ))
|
|
|
|
(Ast.RuleSet.to_seq rule_set1))
|
2022-01-05 11:14:43 +03:00
|
|
|
@ List.of_seq
|
|
|
|
(Seq.map
|
|
|
|
(fun rule ->
|
|
|
|
( Some "Rule or definition from the second group:",
|
|
|
|
Pos.get_position (Ast.RuleName.get_info rule) ))
|
2022-03-08 15:04:27 +03:00
|
|
|
(Ast.RuleSet.to_seq rule_set2))
|
|
|
|
in
|
|
|
|
Errors.raise_multispanned_error spans
|
2022-03-08 17:03:14 +03:00
|
|
|
"Definitions or rules grouped by different labels overlap, \
|
|
|
|
whereas these groups shoule be disjoint")
|
2022-01-05 11:14:43 +03:00
|
|
|
all_rule_sets_pointed_to_by_exceptions)
|
|
|
|
all_rule_sets_pointed_to_by_exceptions;
|
2022-03-08 17:03:14 +03:00
|
|
|
(* Then we add the exception graph vertices by taking all those sets of rules
|
|
|
|
pointed to by exceptions, and adding the remaining rules not pointed as
|
|
|
|
separate singleton set vertices *)
|
2022-01-05 11:14:43 +03:00
|
|
|
let g =
|
|
|
|
List.fold_left
|
|
|
|
(fun g rule_set -> ExceptionsDependencies.add_vertex g rule_set)
|
|
|
|
ExceptionsDependencies.empty all_rule_sets_pointed_to_by_exceptions
|
2021-01-13 21:07:35 +03:00
|
|
|
in
|
2022-01-05 12:42:46 +03:00
|
|
|
let g =
|
|
|
|
Ast.RuleMap.fold
|
|
|
|
(fun (rule_name : Ast.RuleName.t) _ g ->
|
|
|
|
if
|
|
|
|
List.exists
|
|
|
|
(fun rule_set_pointed_to_by_exceptions ->
|
|
|
|
Ast.RuleSet.mem rule_name rule_set_pointed_to_by_exceptions)
|
|
|
|
all_rule_sets_pointed_to_by_exceptions
|
|
|
|
then g
|
2022-03-08 17:03:14 +03:00
|
|
|
else
|
|
|
|
ExceptionsDependencies.add_vertex g (Ast.RuleSet.singleton rule_name))
|
2022-01-05 12:42:46 +03:00
|
|
|
def g
|
|
|
|
in
|
2021-01-13 21:07:35 +03:00
|
|
|
(* then we add the edges *)
|
|
|
|
let g =
|
|
|
|
Ast.RuleMap.fold
|
|
|
|
(fun rule_name rule g ->
|
2022-03-08 17:03:14 +03:00
|
|
|
(* Right now, exceptions can only consist of one rule, we may want to
|
|
|
|
relax that constraint later in the development of Catala. *)
|
2022-01-05 11:14:43 +03:00
|
|
|
let exception_to_ruleset, pos = rule.Ast.rule_exception_to_rules in
|
2022-03-08 17:03:14 +03:00
|
|
|
if Ast.RuleSet.is_empty exception_to_ruleset then g
|
|
|
|
(* we don't add an edge*)
|
2022-01-05 12:42:46 +03:00
|
|
|
else if ExceptionsDependencies.mem_vertex g exception_to_ruleset then
|
2022-01-05 11:14:43 +03:00
|
|
|
if exception_to_ruleset = Ast.RuleSet.singleton rule_name then
|
2022-03-08 17:03:14 +03:00
|
|
|
Errors.raise_spanned_error pos
|
|
|
|
"Cannot define rule as an exception to itself"
|
2022-01-05 11:14:43 +03:00
|
|
|
else
|
|
|
|
let edge =
|
2022-03-08 17:03:14 +03:00
|
|
|
ExceptionsDependencies.E.create
|
|
|
|
(Ast.RuleSet.singleton rule_name)
|
|
|
|
pos exception_to_ruleset
|
2022-01-05 11:14:43 +03:00
|
|
|
in
|
|
|
|
ExceptionsDependencies.add_edge_e g edge
|
|
|
|
else
|
2022-03-08 15:04:27 +03:00
|
|
|
Errors.raise_spanned_error pos
|
2022-03-08 17:03:14 +03:00
|
|
|
"This rule has been declared as an exception to an incorrect \
|
|
|
|
label: this label is not attached to a definition of \"%a\""
|
2022-03-08 15:04:27 +03:00
|
|
|
Ast.ScopeDef.format_t def_info)
|
2021-01-13 21:07:35 +03:00
|
|
|
def g
|
|
|
|
in
|
|
|
|
g
|
|
|
|
|
|
|
|
(** Outputs an error in case of cycles. *)
|
|
|
|
let check_for_exception_cycle (g : ExceptionsDependencies.t) : unit =
|
2022-03-08 17:03:14 +03:00
|
|
|
(* if there is a cycle, there will be an strongly connected component of
|
|
|
|
cardinality > 1 *)
|
2021-01-13 21:07:35 +03:00
|
|
|
let sccs = ExceptionsSCC.scc_list g in
|
|
|
|
if List.length sccs < ExceptionsDependencies.nb_vertex g then
|
|
|
|
let scc = List.find (fun scc -> List.length scc > 1) sccs in
|
2022-03-08 15:04:27 +03:00
|
|
|
let spans =
|
|
|
|
List.flatten
|
|
|
|
(List.map
|
|
|
|
(fun (vs : Ast.RuleSet.t) ->
|
|
|
|
let v = Ast.RuleSet.choose vs in
|
|
|
|
let var_str, var_info =
|
2022-03-08 17:03:14 +03:00
|
|
|
( Format.asprintf "%a" Ast.RuleName.format_t v,
|
|
|
|
Ast.RuleName.get_info v )
|
2022-03-08 15:04:27 +03:00
|
|
|
in
|
|
|
|
let succs = ExceptionsDependencies.succ_e g vs in
|
2022-03-08 17:03:14 +03:00
|
|
|
let _, edge_pos, _ =
|
|
|
|
List.find (fun (_, _, succ) -> List.mem succ scc) succs
|
|
|
|
in
|
2022-03-08 15:04:27 +03:00
|
|
|
[
|
|
|
|
( Some
|
|
|
|
("Cyclic exception for definition of variable \"" ^ var_str
|
|
|
|
^ "\", declared here:"),
|
|
|
|
Pos.get_position var_info );
|
|
|
|
( Some
|
2022-03-08 17:03:14 +03:00
|
|
|
("Used here in the definition of another cyclic exception \
|
|
|
|
for defining \"" ^ var_str ^ "\":"),
|
2022-03-08 15:04:27 +03:00
|
|
|
edge_pos );
|
|
|
|
])
|
|
|
|
scc)
|
|
|
|
in
|
2022-03-08 17:03:14 +03:00
|
|
|
Errors.raise_multispanned_error spans
|
|
|
|
"Cyclic dependency detected between exceptions!"
|