1919from dylan .formula .formula import Formula
2020from dylan .formula .ttr_formula import TTRFormula
2121from dylan .formula .ttr_record_type import TTRRecordType
22- from dylan .nlp .types import Dialogue , WAIT_TOKEN , RELEASE_TURN_TOKEN , Utterance
22+ from dylan .nlp .types import DEFAULT_SPEAKER , Dialogue , WAIT_TOKEN , RELEASE_TURN_TOKEN , Utterance
2323from dylan .parser .dag_parser import DAGParser
2424from dylan .tree .label .labels import Requirement , TypeLabel
2525from dylan .tree .node_address import NodeAddress
@@ -87,27 +87,108 @@ class InteractiveContextParser(DAGParser):
8787
8888 def __init__ (
8989 self ,
90- resource_dir : str | Path ,
90+ resource_dir : str | Path | None = None ,
91+ * ,
9192 repairing : bool = False ,
9293 top_n : int | tuple [str , ...] = 3 ,
9394 participants : tuple [str , ...] = (DEFAULT_NAME ,),
9495 ) -> None :
95- p = Path (resource_dir )
96+ """Construct a parser with optional *resource_dir* (filesystem directory or bundled grammar id)."""
97+ participants_resolved = participants if participants else (DEFAULT_NAME ,)
9698 if not isinstance (top_n , int ):
97- participants = top_n
99+ participants_resolved = top_n # type: ignore[assignment]
98100 top_n = 3
99- super ().__init__ (Lexicon (p , top_n ), Grammar (p ), SpeechActInferenceGrammar (p ))
101+ self ._top_n = top_n
102+ self ._participants = participants_resolved
103+ self ._default_repairing = repairing
104+
105+ if resource_dir is None :
106+ self ._init_shell_unloaded ()
107+ return
108+
109+ p = Path (resource_dir ) if isinstance (resource_dir , str ) else resource_dir
110+ if p .is_dir ():
111+ self ._apply_resource_dir (p , repairing = repairing )
112+ return
113+
114+ self ._init_shell_unloaded ()
115+ self .set_grammar (resource_dir , repairing = repairing )
116+
117+ def _init_shell_unloaded (self ) -> None :
118+ """Initialise placeholder lexicon/grammar with no dialogue ``context`` until :meth:`set_grammar`."""
119+ DAGParser .__init__ (
120+ self ,
121+ Lexicon (None , self ._top_n ),
122+ Grammar (None ),
123+ SpeechActInferenceGrammar (Path ("." )),
124+ )
125+ self .context = None
126+ self .forced_restart = False
127+ self .forced_repair = False
128+ self .right_edge_indicators = []
129+ self .acks = ["uhu" ]
130+ self .repairanda = ["uhh" , "errm" , "err" , "er" , "uh" , "erm" , "uhm" , "um" , "oh" ]
131+ self .forced_repairanda = ["sorry" , "oops" , "wait" , "erm" ]
132+ self .restarters = ["yeah" ]
133+
134+ def _apply_resource_dir (self , path : Path , * , repairing : bool ) -> None :
135+ """Load lexicon and grammars from *path* and wire a fresh :class:`Context`."""
136+ parts = self ._participants if self ._participants else (DEFAULT_NAME ,)
137+ DAGParser .__init__ (
138+ self ,
139+ Lexicon (path , self ._top_n ),
140+ Grammar (path ),
141+ SpeechActInferenceGrammar (path ),
142+ )
100143 dag = WordLevelContextDAG ()
101- parts = participants if participants else (DEFAULT_NAME ,)
102144 self .context = Context (dag , self .sa_grammar , * parts )
103145 self .context .set_repair_processing (repairing )
104146 self .forced_restart = False
105147 self .forced_repair = False
106- self .right_edge_indicators : list [str ] = []
107- self .acks : list [str ] = ["uhu" ]
108- self .repairanda : list [str ] = ["uhh" , "errm" , "err" , "er" , "uh" , "erm" , "uhm" , "um" , "oh" ]
109- self .forced_repairanda : list [str ] = ["sorry" , "oops" , "wait" , "erm" ]
110- self .restarters : list [str ] = ["yeah" ]
148+ self .right_edge_indicators = []
149+ self .acks = ["uhu" ]
150+ self .repairanda = ["uhh" , "errm" , "err" , "er" , "uh" , "erm" , "uhm" , "um" , "oh" ]
151+ self .forced_repairanda = ["sorry" , "oops" , "wait" , "erm" ]
152+ self .restarters = ["yeah" ]
153+ self .init ()
154+
155+ def set_grammar (self , grammar : str | Path , * , repairing : bool | None = None ) -> None :
156+ """Load grammar files from a directory path or bundled grammar id/alias into this parser."""
157+ from dynamicsyntax ._session import resolved_grammar_path
158+
159+ rep = self ._default_repairing if repairing is None else repairing
160+ with resolved_grammar_path (grammar ) as path :
161+ self ._apply_resource_dir (path , repairing = rep )
162+
163+ def parse (
164+ self ,
165+ sentence_or_goal : str | Formula | None = None ,
166+ / ,
167+ * ,
168+ speaker : str = DEFAULT_SPEAKER ,
169+ trace : bool = False ,
170+ ) -> object :
171+ """Parse a surface string into a :class:`~dynamicsyntax.parse_result.ParseResult`, or run ``parse_goal``.
172+
173+ Pass a ``str`` for high-level sentence parsing (requires :meth:`set_grammar` first unless
174+ constructed with a grammar). Pass ``None`` or a :class:`~dylan.formula.formula.Formula`
175+ for the internal ``parse_goal`` path (Java ``DAGParser.parse``).
176+ """
177+ if isinstance (sentence_or_goal , str ):
178+ return self ._parse_surface (sentence_or_goal , speaker = speaker , trace = trace )
179+ return self .parse_goal (sentence_or_goal )
180+
181+ def _parse_surface (self , sentence : str , * , speaker : str , trace : bool ) -> object :
182+ """Run :func:`~dynamicsyntax._parse._run_parse_core` for a non-goal surface string."""
183+ if self .context is None :
184+ raise ValueError ("grammar not loaded; call set_grammar(...) first" )
185+ from dynamicsyntax ._parse import _run_parse_core
186+ from dynamicsyntax .parse_result import ParseResult
187+
188+ stripped = sentence .strip ()
189+ if not stripped :
190+ return ParseResult (ok = False , semantics = None , tree = None , sentence = "" , parser = self )
191+ return _run_parse_core (self , stripped , speaker = speaker , trace = trace )
111192
112193 @classmethod
113194 def from_loaded (
@@ -125,6 +206,9 @@ def from_loaded(
125206 parts = participants if participants else (DEFAULT_NAME ,)
126207 obj .context = Context (dag , obj .sa_grammar , * parts )
127208 obj .context .set_repair_processing (False )
209+ obj ._top_n = lexicon .top_n
210+ obj ._participants = parts
211+ obj ._default_repairing = False
128212 obj .forced_restart = False
129213 obj .forced_repair = False
130214 obj .right_edge_indicators = []
@@ -136,10 +220,14 @@ def from_loaded(
136220
137221 def get_name (self ) -> str :
138222 """Return this parser's context name."""
223+ if self .context is None :
224+ return DEFAULT_NAME
139225 return self .context .get_name ()
140226
141227 def repair_initiated (self ) -> bool :
142228 """Return whether local repair has been initiated."""
229+ if self .context is None :
230+ return False
143231 return self .context .repair_initiated ()
144232
145233 def _adjust_once (self , goal : Formula | None ) -> bool :
0 commit comments