%{ module T = Qsp_syntax.T open StdLabels type action_block = { loc : Qsp_syntax.S.pos ; expression : Analyzer.Expression.t' ; body : Analyzer.Instruction.t list ; pos : Qsp_syntax.S.pos ; clauses : ( ( (Analyzer.Expression.t', Analyzer.Instruction.t) Qsp_syntax.S.clause list * (Qsp_syntax.S.pos * Analyzer.Instruction.t list) option ) option ) } module Helper = Qsp_syntax.S.Helper(Analyzer.Expression) %} %parameter %start <(Analyzer.context -> Analyzer.Location.t)>main %on_error_reduce expression instruction unary_operator assignation_operator %% main: | before_location* start_location EOL+ instructions = line_statement* LOCATION_END { let instructions = List.map instructions ~f:(Analyzer.Instruction.v) in fun context -> Analyzer.Location.location context $loc instructions } before_location: | EOL {} | COMMENT EOL { } (* Defer the registration here, and ensure we get a valid rule. *) start_location: | l = LOCATION_START { ignore (l ()) } (* All these statement should terminate with EOL *) line_statement: | COMMENT EOL+ { Analyzer.Instruction.comment $loc } | COLUMN i=IDENT EOL* { Analyzer.Instruction.location $loc i } | s = terminated(instruction, line_sep) | s = terminated(inline_action, line_sep) { s } | a = action_bloc(IF, elif_else_body) { let {loc; expression; body; pos; clauses } = a in let elifs, else_ = match clauses with | None -> [], None | Some (elifs, else_) -> (elifs, else_) in Analyzer.Instruction.if_ loc (pos, expression, body) ~elifs ~else_ } | a = action_bloc(ACT, empty_body) { let {loc; expression; body; _} = a in Analyzer.Instruction.act loc ~label:expression body } | FOR variable = variable EQUAL start = expression TO to_ = expression step = option(pair(STEP, expression)) COLUMN EOL+ s = line_statement* END EOL+ { let variable = Helper.variable variable in let start = Analyzer.Expression.v start in let to_ = Analyzer.Expression.v to_ in let step = Option.map (fun v -> Analyzer.Expression.v (snd v)) step in Analyzer.Instruction.for_ $loc variable ~start ~to_ ~step s } (** Represent an instruction which can either be on a single line, or created in a block until an END *) %inline action_bloc(TOKEN, BODY): | TOKEN e = expression COLUMN EOL+ s = line_statement* b = BODY END line_sep { let expression = Analyzer.Expression.v e in let clauses = match b with | None -> None | Some (elifs, clauses) -> let elifs = begin match elifs with | [] -> [] | _ -> List.map elifs ~f:(fun ((pos:Qsp_syntax.S.pos), e, instructions) -> let e = Analyzer.Expression.v e in (pos, e, instructions) ) end in Some (elifs, clauses) in { loc = $loc ; expression ; body = s ; clauses ; pos = $loc(s) } } empty_body: | { None } elif: | ELIF e = expression COLUMN EOL+ s = line_statement* { $loc, e, s } else_: | ELSE EOL+ expressions = line_statement* { Some ($loc, expressions) } | { None } elif_else_body: | elifs = elif* else_ = else_ { Some (elifs, else_) } %inline line_sep: | EOL+ | AMPERSAND+ EOL* {}