Пример #1
0
    def _parse_export(self, lineno, linesplit_space):
        """
        @export expr
        @export expr filename

        @hexport
        @hexport filename
        """
        cmd = linesplit_space[0]
        filename_param = self.parameters.get(kw.FILENAME)

        if cmd == kw.HEXPORT:
            if len(linesplit_space) == 1:  # @hexport
                if len(filename_param) == 0:
                    raise ParseException(lineno, f"Invalid {cmd} command.")
                else:
                    filename = filename_param
            else:  # @hexport filename
                filename = linesplit_space[1]
            return None, filename, None

        if len(linesplit_space) == 1:  # @export
            raise ParseException(lineno, f"Invalid {cmd} command.")
        args = linesplit_space[1]
        expr0, filename = ParseUtil.split1_strip(args)
        expr = expr0
        if filename is None:
            if len(filename_param) == 0:
                raise ParseException(lineno, f"No filename given to {cmd}.")
            else:
                filename = filename_param
        all_stimulus_elements = self.parameters.get(kw.STIMULUS_ELEMENTS)
        all_behaviors = self.parameters.get(kw.BEHAVIORS)
        err = None
        if cmd == kw.VEXPORT:
            expr, err = ParseUtil.parse_element_behavior(
                expr0, all_stimulus_elements, all_behaviors)
        elif cmd == kw.VSSEXPORT:
            expr, err = ParseUtil.parse_element_element(
                expr0, all_stimulus_elements)
        elif cmd == kw.PEXPORT:
            stimulus, behavior, err = ParseUtil.parse_stimulus_behavior(
                expr0, all_stimulus_elements, all_behaviors, self.variables)
            expr = (stimulus, behavior)
        elif cmd == kw.NEXPORT:
            expr, err = ParseUtil.parse_chain(expr0, all_stimulus_elements,
                                              all_behaviors)
        if err:
            raise ParseException(lineno, err)
        return expr, filename, expr0
Пример #2
0
    def parse(self, parameters, global_variables):
        self.parameters = parameters
        self.global_variables = global_variables

        stimulus_elements = parameters.get(STIMULUS_ELEMENTS)
        behaviors = parameters.get(BEHAVIORS)

        phase_lines_afterlabel = list()
        linenos = list()

        # First iteration through lines: Create list of lines (and labels)
        for line_lineno in self.lines:
            line, lineno = line_lineno
            label, afterlabel = ParseUtil.split1_strip(line)
            if afterlabel is None:
                raise ParseException(lineno, "Phase line contains only label.")
            coincide_err = f"The phase line label '{label}' coincides with the name of a "
            if label in stimulus_elements:
                raise ParseException(lineno, coincide_err + "stimulus element.")
            elif label in behaviors:
                raise ParseException(lineno, coincide_err + "behavior.")
            if label in self.linelabels and not self.is_inherited:
                raise ParseException(lineno, f"Duplicate of phase line label '{label}'.")
            self.linelabels.append(label)
            phase_lines_afterlabel.append(afterlabel)
            linenos.append(lineno)
            if self.first_label is None:  # Set self.first_label to the label of the first line
                self.first_label = label

        # Second iteration: Create PhaseLine objects and put in the dict self.phase_lines
        for label, after_label, lineno in zip(self.linelabels, phase_lines_afterlabel, linenos):
            self.phase_lines[label] = PhaseLine(self, lineno, label, after_label, self.linelabels,
                                                self.parameters, self.global_variables)
            if label == "new_trial":  # Change self.first_label to the new_trial line
                self.first_label = label

        self.initialize_local_variables()
        self.event_counter = PhaseEventCounter(self.linelabels, self.parameters)

        self.subject_reset()
        self.is_parsed = True
Пример #3
0
def check_action(action, parameters, global_variables, lineno, all_linelabels):
    if action.count(':') == 1 or action.count('=') == 1:
        if action.count('=') == 1:
            sep = '='
        else:
            sep = ':'
        var_name, _ = ParseUtil.split1_strip(action, sep=sep)
        var_err = is_valid_name(var_name, parameters, kw)
        if var_err is not None:
            raise ParseException(lineno, var_err)
        if global_variables.contains(var_name):
            raise ParseException(lineno, "Cannot modify global variable inside a phase.")
    elif action.startswith("count_reset(") and action.endswith(")"):
        behaviors = parameters.get(BEHAVIORS)
        stimulus_elements = parameters.get(STIMULUS_ELEMENTS)
        event = action[12:-1]
        if event not in stimulus_elements and event not in behaviors and event not in all_linelabels:
            raise ParseException(lineno, f"Unknown event '{event}' in count_reset.")
    elif action == "@omit_learn":
        pass
    else:
        raise ParseException(lineno, f"Unknown action '{action}'.")
Пример #4
0
    def __init__(self, phase_obj, lineno, label, after_label, all_linelabels, parameters, global_variables):
        self.lineno = lineno
        self.label = label
        self.parameters = parameters
        self.all_linelabels = all_linelabels

        self.is_help_line = False
        self.stimulus = None  # A dict with an intensity for each element in stimulus_elememts
        self.action = None

        self.action, logic = ParseUtil.split1_strip(after_label, sep=PHASEDIV)
        if logic is None:
            raise ParseException(lineno, f"Missing separator '{PHASEDIV}' on phase line.")
        action_list = ParseUtil.comma_split_strip(self.action)

        first_element, _, _ = ParseUtil.parse_element_and_intensity(action_list[0], variables=None,
                                                                    safe_intensity_eval=True)
        self.is_help_line = (len(action_list) == 1) and (first_element not in parameters.get(STIMULUS_ELEMENTS))
        if self.is_help_line:
            self.stimulus = None
            if action_list[0] != '':
                check_action(action_list[0], parameters, global_variables, lineno, all_linelabels)
        else:
            self.stimulus, err = ParseUtil.parse_elements_and_intensities(self.action, global_variables,
                                                                          safe_intensity_eval=True)
            if err:
                raise ParseException(lineno, err)

            for element in self.stimulus:
                if element not in parameters.get(STIMULUS_ELEMENTS):
                    raise ParseException(lineno, f"Expected a stimulus element, got '{element}'.")

            self.action = None

        if len(logic) == 0:
            raise ParseException(lineno, f"Line with label '{label}' has no conditions.")
        self.conditions = PhaseLineConditions(phase_obj, lineno, self.is_help_line, logic, parameters,
                                              all_linelabels, global_variables)
Пример #5
0
    def _perform_action(self, lineno, action):
        """
        Sets a variable (x:3) or count_reset(event).
        """
        omit_learn = False

        # No action to perform
        if len(action) == 0:
            pass

        # Setting a variable
        elif action.count(':') == 1 or action.count('=') == 1:
            if action.count('=') == 1:
                sep = '='
            else:
                sep = ':'
            var_name, value_str = ParseUtil.split1_strip(action, sep=sep)
            variables_join = Variables.join(self.global_variables, self.local_variables)
            value, err = ParseUtil.evaluate(value_str, variables_join)
            if err:
                raise ParseException(lineno, err)
            err = self.local_variables.set(var_name, value, self.parameters)
            if err:
                raise ParseException(lineno, err)

        # count_reset
        elif action.startswith("count_reset(") and action.endswith(")"):
            event = action[12:-1]
            self.event_counter.reset_count(event)

        # omit_learn
        elif action == "@omit_learn":
            omit_learn = True

        else:
            raise ParseException(lineno, "Internal error.")  # Should have been caught during Pase.parse()

        return omit_learn
Пример #6
0
    def _parse_subplot(self, lineno, linesplit_space):
        """
        @subplot
        @subplot subplotspec
        @subplot subplotspec title
        @subplot subplotspec {mpl_prop}
        @subplot subplotspec title {mpl_prop}
        """
        title_param = self.parameters.get(kw.SUBPLOTTITLE)

        if len(linesplit_space) == 1:  # @subplot
            subplotspec = '111'
            title = title_param
            mpl_prop = dict()
        elif len(linesplit_space) == 2:  # @subplot ...
            args, mpl_prop = ParseUtil.get_ending_dict(linesplit_space[1])
            if mpl_prop is None:
                mpl_prop = dict()
            subplotspec, title_line = ParseUtil.split1_strip(args)
            if title_line is None:  # @subplot subplotspec
                title = title_param
            else:
                title = title_line
        return subplotspec, title, mpl_prop
Пример #7
0
    def parse(self):
        if len(self.lines) == 0:
            raise ParseException(1, "Script is empty.")
        prop = None
        curr_phase_label = None
        in_prop = False
        in_variables = False
        in_phase = False

        for lineno0, line_orig in enumerate(self.lines):
            line = line_orig
            lineno = lineno0 + 1

            # Handle empty line
            if len(line) == 0:
                continue

            line_endswith_comma = line.endswith(',')
            if line_endswith_comma:
                line = line[:-1]

            line_parser = LineParser(line, self.variables)
            line_parser.parse()
            linesplit_colon = line_parser.linesplit_colon
            linesplit_equal = line_parser.linesplit_equal
            linesplit_space = line_parser.linesplit_space

            if in_prop or in_variables or in_phase:
                parse_this_line_done = True
                err = None
                if in_prop:
                    in_prop = line_endswith_comma
                    err = self.parameters.str_append(prop, line,
                                                     self.variables,
                                                     self.phases,
                                                     self.all_run_labels,
                                                     line_endswith_comma)
                elif in_variables:
                    in_variables = line_endswith_comma
                    err = self.variables.add_cs_varvals(line, self.parameters)
                elif in_phase:
                    in_phase = line_parser.line_type is None
                    if in_phase:
                        self.phases.append_line(curr_phase_label, line, lineno)
                    else:
                        parse_this_line_done = False
                        # Phase with label curr_phase_label is complete, parse it
                        self.phases.parse_phase(curr_phase_label,
                                                self.parameters,
                                                self.variables)

                if err:
                    raise ParseException(lineno, err)
                if parse_this_line_done:
                    continue

            if line_parser.line_type == LineParser.PARAMETER:
                # Handle line that sets a parameter (e.g. "prop   :    val")
                prop = line_parser.param
                if len(linesplit_colon) == 1 and len(linesplit_equal) == 1:
                    raise ParseException(
                        lineno, f"Parameter '{prop}' is not specified.")
                first_colon_index = line_orig.find(':')
                first_equal_index = line_orig.find('=')
                if first_equal_index > 0 and first_colon_index > 0:
                    if first_colon_index < first_equal_index:  # u : a=1, b=2
                        possible_val = linesplit_colon[1].strip()
                    else:  # u = a:1, b:2
                        possible_val = linesplit_equal[1].strip()
                elif first_equal_index > 0 and first_colon_index < 0:  # u = 2
                    possible_val = linesplit_equal[1].strip()
                elif first_colon_index > 0 and first_equal_index < 0:  # u : 2
                    possible_val = linesplit_colon[1].strip()
                if len(possible_val) == 0:
                    raise ParseException(
                        lineno, f"Parameter '{prop}' is not specified.")
                if line_endswith_comma:
                    if not self.parameters.may_end_with_comma(prop):
                        raise ParseException(
                            lineno,
                            "Value for {} may not end by comma.".format(prop))
                    in_prop = self.parameters.is_csv(prop)
                err = self.parameters.str_set(prop, possible_val,
                                              self.variables, self.phases,
                                              self.all_run_labels,
                                              line_endswith_comma)
                if err:
                    raise ParseException(lineno, err)
                continue

            elif line_parser.line_type == LineParser.VARIABLES:
                if len(linesplit_space) == 1:
                    raise ParseException(lineno, "@VARIABLES not specified.")
                in_variables = line_endswith_comma
                cs_varvals = linesplit_space[1].strip()
                err = self.variables.add_cs_varvals(cs_varvals,
                                                    self.parameters)
                if err:
                    raise ParseException(lineno, err)
                else:
                    continue

            elif line_parser.line_type == LineParser.PREV_DEFINED_VARIABLE:
                err = self.variables.add_cs_varvals(line.strip(),
                                                    self.parameters)
                if err:
                    raise ParseException(lineno, err)
                else:
                    continue

            elif line_parser.line_type == LineParser.RUN:
                if len(linesplit_space) == 1:
                    raise ParseException(
                        lineno,
                        "@RUN line must have the form '@RUN phases [runlabel:label]."
                    )
                after_run = linesplit_space[1].strip()
                run_label, run_phase_labels = self._parse_run(
                    after_run, lineno)
                world = self.phases.make_world(run_phase_labels)
                run_parameters = copy.deepcopy(
                    self.parameters)  # Params may change betweeen runs
                mechanism_obj, err = run_parameters.make_mechanism_obj()
                if err:
                    raise ParseException(lineno, err)
                n_subjects = run_parameters.get(kw.N_SUBJECTS)
                bind_trials = run_parameters.get(kw.BIND_TRIALS)
                is_ok, err, err_lineno = mechanism_obj.check_compatibility_with_world(
                    world)
                if err:
                    raise ParseException(err_lineno, err)
                run = Run(run_label, world, mechanism_obj, n_subjects,
                          bind_trials)
                self.runs.add(run, run_label, lineno)
                continue

            elif line_parser.line_type == LineParser.PHASE:
                gen_err = "@PHASE line must have the form '@PHASE label stop:condition'."
                if len(linesplit_space) == 1:
                    raise ParseException(lineno, gen_err)
                lbl_and_stopcond = linesplit_space[1].strip()
                curr_phase_label, stop_condition = ParseUtil.split1(
                    lbl_and_stopcond)

                inherited_from = None
                if '(' in curr_phase_label and curr_phase_label.endswith(')'):
                    lind = curr_phase_label.index('(')
                    inherited_from = curr_phase_label[(lind + 1):-1]
                    curr_phase_label = curr_phase_label[0:lind]
                    if not self.phases.contains(inherited_from):
                        raise ParseException(
                            lineno, f"Invalid phase label '{inherited_from}'.")

                if self.phases.contains(curr_phase_label):
                    raise ParseException(
                        lineno, f"Redefinition of phase '{curr_phase_label}'.")
                if not curr_phase_label.isidentifier():
                    raise ParseException(
                        lineno,
                        f"Phase label '{curr_phase_label}' is not a valid identifier."
                    )
                if stop_condition is None:
                    raise ParseException(lineno, gen_err)
                stop, condition = ParseUtil.split1_strip(stop_condition, ':')
                if stop != "stop" or condition is None or len(condition) == 0:
                    raise ParseException(
                        lineno,
                        "Phase stop condition must have the form 'stop:condition'."
                    )
                in_phase = True
                if inherited_from:
                    self.phases.inherit_from(inherited_from, curr_phase_label,
                                             condition, lineno)
                else:
                    self.phases.add_phase(curr_phase_label, condition, lineno)
                continue

            elif line_parser.line_type == LineParser.FIGURE:
                figure_title, mpl_prop = self._parse_figure(
                    lineno, linesplit_space)
                figure_cmd = FigureCmd(figure_title, mpl_prop)
                self.postcmds.add(figure_cmd)

            elif line_parser.line_type == LineParser.SUBPLOT:
                subplotspec, subplot_title, mpl_prop = self._parse_subplot(
                    lineno, linesplit_space)
                subplot_cmd = SubplotCmd(subplotspec, subplot_title, mpl_prop)
                self.postcmds.add(subplot_cmd)

            elif line_parser.line_type == LineParser.PLOT:
                expr, mpl_prop, expr0 = self._parse_plot(
                    lineno, linesplit_space)
                cmd = linesplit_space[0].lower()
                plot_parameters = copy.deepcopy(
                    self.parameters)  # Params may change betweeen plot
                self._evalparse(lineno, plot_parameters)
                plot_cmd = PlotCmd(cmd, expr, expr0, plot_parameters, mpl_prop)
                self.postcmds.add(plot_cmd)

            elif line_parser.line_type == LineParser.LEGEND:
                mpl_prop = self._parse_legend(lineno, linesplit_space)
                legend_cmd = LegendCmd(mpl_prop)
                self.postcmds.add(legend_cmd)

            elif line_parser.line_type == LineParser.EXPORT:
                expr, filename, expr0 = self._parse_export(
                    lineno, linesplit_space)
                cmd = linesplit_space[0].lower()
                export_parameters = copy.deepcopy(
                    self.parameters)  # Params may change betweeen exports
                self._evalparse(lineno, export_parameters)
                export_parameters.val[
                    kw.
                    FILENAME] = filename  # If filename only given on export line
                export_cmd = ExportCmd(lineno, cmd, expr, expr0,
                                       export_parameters)
                self.postcmds.add(export_cmd)

            else:
                raise ParseException(lineno, f"Invalid expression '{line}'.")
Пример #8
0
    def _parse(self, lineno, is_help_line, condition_and_actions, logicpart_index, n_logicparts, parameters,
               all_linelabels, global_variables):
        '''
        Args:
            condition_and_actions (str): Examples are
                "b=5: x:2, y=2, ROWLBL",
                "x:2, y:2, ROWLBL",
                "@break"
                "x=1: @break"
                "x:1"
                "@break, x:1"
        '''
        self.condition = None

        ca_list = ParseUtil.comma_split_strip(condition_and_actions)

        found_condition = False
        any_rowlbl_prob = False
        found_rowlbl = False
        goto_list = list()
        goto_list_index = list()
        for i, ca in enumerate(ca_list):
            err = f"Invalid statement '{ca}'."
            n_colons = ca.count(':')
            if n_colons == 0:
                contains_condition = False
                action = ca
            elif n_colons == 1:
                before_colon, after_colon = ParseUtil.split1_strip(ca, ':')
                contains_condition = self._is_condition(before_colon, parameters)
                if contains_condition:
                    if self.condition is not None:  # Cannot have multiple conditions
                        raise ParseException(lineno, f"Multiple conditions ('{self.condition}' and '{before_colon}') found in '{condition_and_actions}'.")
                    self.condition = before_colon
                    action = after_colon
                else:
                    action = ca
            elif n_colons == 2:
                colon_inds = [m.start() for m in re.finditer(':', ca)]
                if colon_inds[1] - colon_inds[0] == 1:
                    raise ParseException(lineno, err)
                before_first_colon, after_first_colon = ParseUtil.split1_strip(ca, ':')
                if not self._is_condition(before_first_colon, parameters):
                    raise ParseException(lineno, err)
                if self.condition is not None:  # Cannot have multiple conditions
                    raise ParseException(lineno, f"Multiple conditions ('{self.condition}' and '{before_first_colon}') found in '{condition_and_actions}'.")
                contains_condition = True
                self.condition = before_first_colon
                action = after_first_colon
            else:
                raise ParseException(lineno, err)

            if contains_condition and found_rowlbl:
                raise ParseException(lineno, f"Found condition '{self.condition}' after row label '{','.join(goto_list)}'.")

            found_condition = found_condition or contains_condition
            is_rowlbl, is_rowlbl_prob = self._is_rowlbl(action, all_linelabels)
            any_rowlbl_prob = (any_rowlbl_prob or is_rowlbl_prob)
            if is_rowlbl:
                found_rowlbl = True
                goto_list.append(action)
                goto_list_index.append(i)
            else:
                check_action(action, parameters, global_variables, lineno, all_linelabels)
                if found_rowlbl:
                    err = f"Row label(s) must be the last action(s). Found '{action}' after row-label."
                    raise ParseException(lineno, err)
                if found_condition:  # self.condition is not None:
                    self.conditional_actions.append(action)
                else:
                    self.unconditional_actions.append(action)

            is_last_action = (i == len(ca_list) - 1)
            if is_last_action and not is_rowlbl:
                raise ParseException(lineno, f"Last action must be a row label, found '{action}'.")

        if found_condition:
            self.condition_is_behavior = (self.condition in parameters.get(BEHAVIORS))
            cond_depends_on_behavior, err = ParseUtil.depends_on(self.condition, parameters.get(BEHAVIORS))
            if err is not None:
                raise ParseException(lineno, err)
            if cond_depends_on_behavior and is_help_line:
                raise ParseException(lineno, "Condition on help line cannot depend on response.")

        goto_str = ','.join(goto_list)

        # A deterministic ROWLBL cannot have elif/else continuation
        if (not found_condition) and found_rowlbl and not any_rowlbl_prob:
            if logicpart_index < n_logicparts - 1:
                err = f"The unconditional goto row label '{goto_str}' cannot be continued."
                raise ParseException(lineno, err)

        if len(goto_list) > 0:
            self._parse_goto(goto_str, lineno, all_linelabels, global_variables)