diff options
| author | Chimrod <> | 2026-09-14 10:29:40 +0200 |
|---|---|---|
| committer | Chimrod <> | 2026-09-14 10:29:40 +0200 |
| commit | 6dd1beecbac4d1e9d3a13f3e06563659724caf10 (patch) | |
| tree | f88b30e7c7a9dda9f5f0394fba5ba562d4c29b2a | |
| parent | f805783c2fe7f4dd3f970d48ae122df4dd79da23 (diff) | |
| -rw-r--r-- | lib/qparser/analyzer.ml | 3 | ||||
| -rw-r--r-- | lib/qparser/dune | 42 | ||||
| -rw-r--r-- | lib/qparser/expression_parser.messages | 4 | ||||
| -rw-r--r-- | lib/qparser/idents.ml | 2 | ||||
| -rw-r--r-- | lib/qparser/parser.mly | 2 | ||||
| -rw-r--r-- | lib/qparser/qsp_expression.mly | 140 | ||||
| -rw-r--r-- | test/syntax.ml | 22 | ||||
| -rw-r--r-- | test/syntax_error.ml | 6 |
8 files changed, 125 insertions, 96 deletions
diff --git a/lib/qparser/analyzer.ml b/lib/qparser/analyzer.ml index fc0ed6d..0337baf 100644 --- a/lib/qparser/analyzer.ml +++ b/lib/qparser/analyzer.ml @@ -26,7 +26,8 @@ let rec parse : type a context. and type context = context) -> let module Parser = Parser.Make (S) in let module IncrementalParser = - Interpreter.Interpreter (Parser.MenhirInterpreter) in + Interpreter.Interpreter (Parser.MenhirInterpreter) + in fun lexer_type l context -> let get_parser : lexer -> diff --git a/lib/qparser/dune b/lib/qparser/dune index eea3815..7edba9c 100644 --- a/lib/qparser/dune +++ b/lib/qparser/dune @@ -1,30 +1,28 @@ (library (name qparser) - (libraries - str - menhirLib - qsp_syntax - sedlex - ) - (preprocess (pps - sedlex.ppx - )) - ) - + (libraries str menhirLib qsp_syntax sedlex) + (preprocess + (pps sedlex.ppx))) (rule (targets parser_messages.ml) - (deps expression_parser.messages tokens.mly qsp_expression.mly qsp_instruction.mly qsp_single_line.mly parser.mly) - (action (with-stdout-to %{targets} (run menhir --base parser.mly --compile-errors %{deps})))) + (deps + expression_parser.messages + tokens.mly + qsp_expression.mly + qsp_instruction.mly + qsp_single_line.mly + parser.mly) + (action + (with-stdout-to + %{targets} + (run menhir --base parser.mly --compile-errors %{deps})))) (menhir - (modules tokens) - (flags --only-tokens) -) - + (modules tokens) + (flags --only-tokens)) -(menhir - (modules tokens parser qsp_instruction qsp_expression qsp_single_line) - (flags --table --external-tokens Tokens) - (merge_into parser) -) +(menhir + (modules tokens parser qsp_instruction qsp_expression qsp_single_line) + (flags --table --external-tokens Tokens) + (merge_into parser)) diff --git a/lib/qparser/expression_parser.messages b/lib/qparser/expression_parser.messages index 22ffd7d..8040f05 100644 --- a/lib/qparser/expression_parser.messages +++ b/lib/qparser/expression_parser.messages @@ -65,7 +65,6 @@ main: LOCATION_START EOL IF IDENT COLUMN EOL ELSE STAR main: LOCATION_START EOL IDENT DECR INTEGER SET main: LOCATION_START EOL SET IDENT DECR INTEGER SET main: LOCATION_START EOL LET IDENT DECR INTEGER SET -main: LOCATION_START EOL KEYWORD INTEGER TEXT_MARKER main: LOCATION_START EOL KEYWORD IDENT COMA INTEGER TEXT_MARKER Missing separator between instructions @@ -114,9 +113,10 @@ main: LOCATION_START EOL IDENT MINUS STAR dynamics: IDENT R_PAREN dynamics: TEXT_MARKER ENTER_EMBED FUNCTION_NOARGS TEXT_MARKER +dynamics: TEXT_MARKER ENTER_EMBED INTEGER TEXT_MARKER Unbalanced paren -dynamics: IDENT PLUS FUNCTION_NOARGS TEXT_MARKER +dynamics: IDENT PLUS INTEGER TEXT_MARKER Missing operator before text diff --git a/lib/qparser/idents.ml b/lib/qparser/idents.ml index be3d76c..a74fce2 100644 --- a/lib/qparser/idents.ml +++ b/lib/qparser/idents.ml @@ -99,7 +99,7 @@ let _ = ("MIN", FUNCTION T.Min); ("$MIN", FUNCTION T.Min'); ("MOD", MOD); - ("MSECSCOUNT", FUNCTION T.Msecscount); + ("MSECSCOUNT", FUNCTION_NOARGS T.Msecscount); ("MSG", KEYWORD T.Msg); ("NL", KEYWORD T.Nl); ("*NL", KEYWORD T.Nl'); diff --git a/lib/qparser/parser.mly b/lib/qparser/parser.mly index 1caf962..55e729e 100644 --- a/lib/qparser/parser.mly +++ b/lib/qparser/parser.mly @@ -18,7 +18,7 @@ %} %parameter<Analyzer: Qsp_syntax.Analyzer.T> -%start <(Analyzer.context -> Analyzer.Location.t)>main +%start<(Analyzer.context -> Analyzer.Location.t)>main %start<(Analyzer.context -> Analyzer.Location.t)>dynamics %on_error_reduce instruction unary_operator assignation_operator diff --git a/lib/qparser/qsp_expression.mly b/lib/qparser/qsp_expression.mly index f037051..0b8406c 100644 --- a/lib/qparser/qsp_expression.mly +++ b/lib/qparser/qsp_expression.mly @@ -1,93 +1,109 @@ %% %public arguments(X): - (** This rule allow the difference between elements with a single argument - (when the separator is required) which allow to write a spectif case when - we only have one element. + (** This rule allow the difference between elements with a single argument + (when the separator is required) which allow to write a spectif case when + we only have one element. *) - | hd = X - COMA - tl = separated_nonempty_list(COMA, X) - { hd :: tl } - (** The case where we have nothing is easy to manage here *) - | - { [] } - (** The remaining case (only one argument) is handled outside of this + | hd = X COMA tl = separated_nonempty_list(COMA, X) { hd :: tl } + (** The case where we have nothing is easy to manage here *) + | { [] } + +(** The remaining case (only one argument) is handled outside of this block: if we have a single argument we don’t have to bother with the paren, they belongs to the expression. *) %inline argument(X): - | a = delimited(L_PAREN, arguments(X), R_PAREN) { a } - | a = X { [ a ] } + | a = delimited(L_PAREN, arguments(X), R_PAREN) { a } + | a = X { [ a ] } (** Declare an expression *) %public expression: - | ex = delimited(L_PAREN, expression, R_PAREN) - { ex } - | op = unary_operator - expr = expression - { Analyzer.Expression.uoperator ~ctx:{f=(fun _ -> None)} $loc op expr } - %prec NO - | - expr1 = expression - op = binary_operator - expr2 = expression - { Analyzer.Expression.boperator ~ctx:{f=(fun _ -> None)} $loc op expr1 expr2 } - | v = delimited(TEXT_MARKER, literal*, TEXT_MARKER) - { Analyzer.Expression.literal ~ctx:{f=(fun _ -> None)} $loc v } - | i = INTEGER { Analyzer.Expression.integer ~ctx:{f=(fun _ -> None)} $loc i } - | v = variable { Analyzer.Expression.ident ~ctx:{f=(fun _ -> None)} v } + | ex = delimited(L_PAREN, expression, R_PAREN) { ex } + | op = unary_operator expr = expression + { + Analyzer.Expression.uoperator ~ctx:{ f = (fun _ -> None) } $loc op expr + } + %prec NO + | expr1 = expression op = binary_operator expr2 = expression + { + Analyzer.Expression.boperator ~ctx:{ f = (fun _ -> None) } $loc op expr1 expr2 + } + | v = delimited(TEXT_MARKER, list(literal), TEXT_MARKER) + { + Analyzer.Expression.literal ~ctx:{ f = (fun _ -> None) } $loc v + } + | i = INTEGER + { + Analyzer.Expression.integer ~ctx:{ f = (fun _ -> None) } $loc i + } + | v = variable + { Analyzer.Expression.ident ~ctx:{ f = (fun _ -> None) } v } %prec p_variable - | k = FUNCTION - arg = argument(expression) + | k = FUNCTION arg = argument(expression) { - (Analyzer.Expression.function_ ~ctx:{f=(fun _ -> None)} $loc k arg) + Analyzer.Expression.function_ ~ctx:{ f = (fun _ -> None) } $loc k arg } - | k = FUNCTION_NOARGS + + (* Function without any argument can be used either + - without any parens + - or with parens but empty and with nothing inside + *) + | k = FUNCTION_NOARGS { - (Analyzer.Expression.function_ ~ctx:{f=(fun _ -> None)} $loc k []) + Analyzer.Expression.function_ ~ctx:{ f = (fun _ -> None) } $loc k [] } + | k = FUNCTION_NOARGS L_PAREN R_PAREN + { + Analyzer.Expression.function_ ~ctx:{ f = (fun _ -> None) } $loc k [] + } + literal: - | v = LITERAL { Qsp_syntax.T.Text v } - | e = delimited(ENTER_EMBED, expression, LEAVE_EMBED) + | v = LITERAL { Qsp_syntax.T.Text v } + | e = delimited(ENTER_EMBED, expression, LEAVE_EMBED) { Qsp_syntax.T.Expression e } unary_operator: - | OBJ - | NO { T.No } - | MINUS { T.Neg } - | PLUS { T.Add } + | OBJ + | NO + { T.No } + | MINUS { T.Neg } + | PLUS { T.Add } %inline binary_operator: - | EQUAL { T.Eq } - | LT GT { T.Neq } - | EXCLAMATION { T.Neq } - | PLUS { T.Plus } - | MINUS { T.Minus } - | STAR { T.Product } - | DIV { T.Div } - | MOD { T.Mod } - | GT { T.Gt } - | LT { T.Lt } - | AND { T.And } - | GT EQUAL { T.Gte } - | LT EQUAL { T.Lte } - | EQUAL GT { T.Gte } - | EQUAL LT { T.Lte } - | OR { T.Or } + | EQUAL { T.Eq } + | LT GT { T.Neq } + | EXCLAMATION { T.Neq } + | PLUS { T.Plus } + | MINUS { T.Minus } + | STAR { T.Product } + | DIV { T.Div } + | MOD { T.Mod } + | GT { T.Gt } + | LT { T.Lt } + | AND { T.And } + | GT EQUAL { T.Gte } + | LT EQUAL { T.Lte } + | EQUAL GT { T.Gte } + | EQUAL LT { T.Lte } + | OR { T.Or } (** Declare a variable, either in the assignation (let var = …) or as a reference is an expression *) %public variable: - | name = IDENT - brackets = delimited(L_BRACKET, expression?, R_BRACKET)? - { - let index = match brackets with - | None -> + | name = IDENT + brackets = option( + delimited(L_BRACKET, option(expression), R_BRACKET) + ) + { + let index = + match brackets with + | None -> (* No declaration, consider index at 0 *) None - | Some other -> other in - Qsp_syntax.S.{ pos = $loc ; name ; index } + | Some other -> other + in + Qsp_syntax.S.{ pos = $loc; name; index } } diff --git a/test/syntax.ml b/test/syntax.ml index ce3e89e..64c5004 100644 --- a/test/syntax.ml +++ b/test/syntax.ml @@ -33,10 +33,10 @@ let parse : string -> (S.pos location, Qsp_syntax.Report.t) result = (module Parser) Qparser.Analyzer.Location lexing context |> Result.map (fun v -> - (* Uncatched excteptions here, but we are in the tests… + (* Uncatched excteptions here, but we are in the tests… If it’s fail here I have an error in the code. *) - Array.get v.Qparser.Analyzer.content 0 - |> Check.get location_id |> Option.get) + Array.get v.Qparser.Analyzer.content 0 + |> Check.get location_id |> Option.get) let location : S.pos location Alcotest.testable = let equal = equal_location (fun _ _ -> true) in @@ -895,8 +895,21 @@ let test_stattxt () = { Tree.Ast.pos = _position; name = "$STATTXT"; index = None } ); ] +let test_function_no_args () = + let expected = + [ + Tree.Ast.Declaration + ( _position, + { Tree.Ast.pos = _position; name = "VALUE"; index = None }, + T.Eq', + Tree.Ast.Function (_position, T.Msecscount, []) ); + ] + in + _test_instruction "value = msecscount" expected; + _test_instruction "value = msecscount()" expected + let test_syntax = - ( "Syntax", + ( __FILE__, [ Alcotest.test_case "Location" `Quick test_empty_location; Alcotest.test_case "Location" `Quick test_location_without_space; @@ -957,6 +970,7 @@ let test_syntax = Alcotest.test_case "Precedence7" `Quick test_precedence7; Alcotest.test_case "Precedence8" `Quick test_precedence8; Alcotest.test_case "stattxt" `Quick test_stattxt; + Alcotest.test_case "stattxt" `Quick test_function_no_args; ] ) let test_comments = diff --git a/test/syntax_error.ml b/test/syntax_error.ml index 9d51cf3..209d3e2 100644 --- a/test/syntax_error.ml +++ b/test/syntax_error.ml @@ -23,7 +23,7 @@ let _test_instruction : let _location = Printf.sprintf {|# Location %s ------- |} literal in - let actual = get_report @@ Syntax.parse _location and msg = literal in + let actual = get_report @@ Syntax.parse _location and msg = _location in let () = Alcotest.(check' report ~msg ~expected ~actual) in match k with None -> () | Some f -> f actual @@ -291,7 +291,7 @@ let missing_operator () = { level = Error; loc = _position; message = "Missing operator before text" } let test = - ( "Syntax Errors", + ( __FILE__, [ Alcotest.test_case "else:" `Quick else_column; Alcotest.test_case "elseif" `Quick elseif_no_column; @@ -305,7 +305,7 @@ let test = Alcotest.test_case "Unknown function" `Quick unknow_function; Alcotest.test_case "Inline elif" `Quick inline_elif; Alcotest.test_case "Unclosed block" `Quick unclosed_block; - Alcotest.test_case "Unclosed block" `Quick comment_as_operator; + Alcotest.test_case "Comment as separator" `Quick comment_as_operator; Alcotest.test_case "Missing comparable" `Quick missing_comparable; Alcotest.test_case "Location change" `Quick location_change; Alcotest.test_case "Misplaced if" `Quick misplaced_if; |
