Code should be an expression, eg this ought to work:
let foo = { echo "hello" }
+and/or anonymous functions:
+let foo = function (arg) { ... }
+
+Functions returning plain strings and lists of strings.
+function (foo, bar) returning string = { echo hello }
+function (foo, bar) returning strings = { echo hello; echo goodbye }
+Then re-add the sort function.
Should the environment be populated by the actual environment, eg:
let homedir = "%HOME"
and constant =
| CString of string
and goal = param_decl list * pattern list * expr list * code option
-and func = param_decl list * code
+and func = param_decl list * returning * code
and tactic = param_decl list * code
and param_decl = id
and id = string
and code = substs
+and returning = RetExpr | RetStrings | RetString
and substs = subst list
and subst =
| SString of string
(String.concat ", " (List.map (string_expr ()) exprs))
(match code with None -> "" | Some code -> " = { ... }")
-and string_func () (name, (param_decls, code)) =
- sprintf "function%s (%s) = { ... }"
+and string_func () (name, (param_decls, returning, code)) =
+ sprintf "function%s returning %s (%s) = { ... }"
(match name with None -> "" | Some name -> " " ^ name)
+ (match returning with RetExpr -> "expression"
+ | RetString -> "string"
+ | RetStrings -> "strings")
(String.concat ", " (List.map (string_param_decl ()) param_decls))
and string_tactic () (name, (param_decls, code)) =
and constant =
| CString of string
and goal = param_decl list * pattern list * expr list * code option
-and func = param_decl list * code
+and func = param_decl list * returning * code
and tactic = param_decl list * code
and param_decl = id (** goal/func/tactic parameter. *)
and id = string
and code = substs
+and returning = RetExpr | RetStrings | RetString
and substs = subst list
and subst =
| SString of string
* into a shell script or constant expression (this may change if we
* implement ā:=ā assignment for variables). This evaluates a
* function by running the associated shell script and parsing
- * the output as an expression.
+ * the output as an expression, string or list of strings.
*)
-and call_function env loc name args (params, code) =
+and call_function env loc name args (params, returning, code) =
(* This is used to print the function in debug and error messages only. *)
let debug_func =
- sprintf "%s (%s)" name
- (String.concat ", " (List.map (Ast.string_expr ()) args)) in
+ sprintf "%s (%s) returning %s" name
+ (String.concat ", " (List.map (Ast.string_expr ()) args))
+ (match returning with RetExpr -> "expression"
+ | RetString -> "string"
+ | RetStrings -> "strings") in
Cmdline.debug "%a: running function %s" Ast.string_loc loc debug_func;
(* Evaluate function args. Must be done before updating the environment. *)
eprintf "*** function ā%sā failed with exit code %d\n" name r;
exit 1
);
- Parse.parse_expr (sprintf "function:%s" name) b
+
+ match returning with
+ | RetExpr -> Parse.parse_expr (sprintf "function:%s" name) b
+ | RetString -> Ast.EConstant (loc, Ast.CString b)
+ | RetStrings ->
+ (* run_code always adds \n after the final line, so when we
+ * read it back we will get a final empty string which we
+ * have to drop. XXX Probably better to preserve the lines
+ * read from the external command.
+ *)
+ let strs = nsplit "\n" b in
+ let strs = List.rev strs in
+ let strs = match strs with "" :: xs -> xs | xs -> xs in
+ let strs = List.rev strs in
+ let strs = List.map (fun s -> Ast.EConstant (loc, Ast.CString s)) strs in
+ EList (loc, strs)
{ INCLUDE }
| "-include"
{ OPTINCLUDE }
+ | "returning"
+ { RETURNING }
+ | "expression"
+ { EXPRESSION }
+ | "string"
+ { STRING_KEYWORD }
+ | "strings"
+ { STRINGS }
| "*" id { (* NB: The initial '*' is part of the name. *)
TACTIC (Lexing.lexeme lexbuf) }
| id { ID (Lexing.lexeme lexbuf) }
%token COMMA
%token EQUALS
%token EOF
+%token EXPRESSION
%token FUNCTION
%token GOAL
%token <string> ID
%token LEFT_PAREN
%token LET
%token OPTINCLUDE
+%token RETURNING
%token RIGHT_ARRAY
%token RIGHT_PAREN
%token SEMICOLON
%token <Ast.substs> STRING
+%token STRING_KEYWORD
+%token STRINGS
%token <string> TACTIC
%token TACTIC_KEYWORD
let name, params = $1 in
name, Ast.EGoalDefn ($loc, (params, [], [], Some $2))
}
- | FUNCTION ID params_decl EQUALS CODE
+ | FUNCTION ID params_decl return_decl EQUALS CODE
{
- $2, Ast.EFuncDefn ($loc, ($3, $5))
+ $2, Ast.EFuncDefn ($loc, ($3, $4, $6))
}
| TACTIC_KEYWORD TACTIC params_decl EQUALS CODE
{
;
param_decl:
| ID { $1 }
+return_decl:
+ | { RetExpr }
+ | RETURNING EXPRESSION { RetExpr }
+ | RETURNING STRINGS { RetStrings }
+ | RETURNING STRING_KEYWORD { RetString }
patterns:
| separated_list(COMMA, pattern) { $1 }
in
loop 0
+let rec split sep str =
+ let len = String.length sep in
+ let seplen = String.length str in
+ let i = string_find str sep in
+ if i = -1 then str, ""
+ else (
+ String.sub str 0 i, String.sub str (i + len) (seplen - i - len)
+ )
+
+and nsplit ?(max = 0) sep str =
+ if max < 0 then
+ invalid_arg "String.nsplit: max parameter should not be negative";
+
+ (* If we reached the limit, OR if the pattern does not match the string
+ * at all, return the rest of the string as a single element list.
+ *)
+ if max = 1 || string_find str sep = -1 then
+ [str]
+ else (
+ let s1, s2 = split sep str in
+ let max = if max = 0 then 0 else max - 1 in
+ s1 :: nsplit ~max sep s2
+ )
+
let isspace c =
c = ' '
(* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *)
(** [string_find str sub] finds the index of [sub] in [str]. If
not found, returns -1. *)
+val nsplit : ?max:int -> string -> string -> string list
+(** [nsplit ?max sep str] splits [str] into multiple strings at each
+ separator [sep].
+
+ As with the Perl split function, you can give an optional
+ [?max] parameter to limit the number of strings returned. The
+ final element of the list will contain the remainder of the
+ input string. *)
+
val isspace : char -> bool
val triml : ?test:(char -> bool) -> string -> string
val trimr : ?test:(char -> bool) -> string -> string
#----------------------------------------------------------------------
# Text functions.
-
+# Sort + uniq a list.
+function sort (xs) returning strings = {
+ for f in %xs; do echo "$f"; done | sort -u
+}
#----------------------------------------------------------------------
# File functions.
# Expand a wildcard into a list of filenames.
-function wildcard (wc) = {
+function wildcard (wc) returning strings = {
shopt -s nullglob
# Note that the substitution is quoted by goals, so to expand
# it we must assign it to a variable and then use it unquoted.
wc=%wc
- echo '['
- for f in $wc; do
- quoted_string "$f"
- echo ','
- done
- echo ']'
+ for f in $wc; do echo "$f"; done
}