diff --git a/src/lib/arith.ml b/src/lib/arith.ml index 33a9a3a..49883bc 100644 --- a/src/lib/arith.ml +++ b/src/lib/arith.ml @@ -1,25 +1,57 @@ (* We handle _very_ simple arithmetic expressions. Really nothing crazy yet, hopefully enough to handle most [while x < 10 do x = x + 1 done] loops! *) +type operator = Add | Sub | Mul | Div | Mod | Lt | Gt | Eq +[@@deriving to_yojson] + +let exec_op = function + | Add -> Int.add + | Sub -> Int.sub + | Mul -> Int.mul + | Div -> Int.div + | Mod -> ( mod ) + | Lt -> fun a b -> if a < b then 1 else 0 + | Gt -> fun a b -> if a > b then 1 else 0 + | Eq -> fun a b -> if Int.equal a b then 1 else 0 type expr = | Int of int | Var of string - | Add of expr * expr - | Sub of expr * expr - | Mul of expr * expr - | Div of expr * expr + | Binop of operator * expr * expr | Neg of expr + | Assign of operator * string * expr + | Ternary of (expr * expr * expr) [@@deriving to_yojson] -let eval lookup expr = - let rec calc = function - | Int i -> i - | Var v -> lookup v - | Add (e1, e2) -> calc e1 + calc e2 - | Sub (e1, e2) -> calc e1 - calc e2 - | Div (e1, e2) -> calc e1 / calc e2 - | Mul (e1, e2) -> calc e1 * calc e2 - | Neg n -> Int.neg (calc n) - in - calc expr +module Make (S : Types.State) = struct + let eval initial_state expr = + let lookup state s = + match S.lookup state ~param:s with + | Some [ Ast.WordLiteral n ] when Option.is_some (int_of_string_opt n) -> + int_of_string n + | _ -> 0 + in + let update state s i = + S.update state ~param:s [ Ast.WordLiteral (string_of_int i) ] + in + let rec calc state = function + | Int i -> (state, i) + | Var v -> (state, lookup state v) + | Binop (op, e1, e2) -> + let state, v1 = calc state e1 in + let state, v2 = calc state e2 in + (state, exec_op op v1 v2) + | Neg n -> + let state, v1 = calc state n in + (state, Int.neg v1) + | Assign (op, var, e) -> + let current_v = lookup state var in + let state, v1 = calc state e in + let nv = exec_op op current_v v1 in + (update state var nv, nv) + | Ternary (e1, e2, e3) -> + let state, v1 = calc state e1 in + if Int.equal v1 Int.zero then calc state e3 else calc state e2 + in + calc initial_state expr +end diff --git a/src/lib/arith_lexer.mll b/src/lib/arith_lexer.mll index 564c888..ac23d8d 100644 --- a/src/lib/arith_lexer.mll +++ b/src/lib/arith_lexer.mll @@ -3,6 +3,8 @@ } let digit = ['0'-'9'] +let oct_digit = ['0'-'7'] +let hex_digit = ['0'-'9' 'a'-'f' 'A'-'F'] let alpha = ['a'-'z' 'A'-'Z' '_'] let ident = alpha (alpha | digit)* let var = @@ -12,14 +14,29 @@ let var = rule read = parse | [' ' '\t' '\n'] { read lexbuf } + | "+=" { PLUSEQ } + | "-=" { MINUSEQ } + | "/=" { DIVEQ } + | "*=" { MULEQ } + | "%=" { MODEQ } + | "==" { EQEQ } + | '=' { EQ } + | '+' { PLUS } | '-' { MINUS } - | '*' { STAR } - | '/' { SLASH } + | '*' { MUL } + | '/' { DIV } + | '?' { QUESTION } + | ':' { COLON } + | '>' { GT } + | '<' { LT } | '(' { LPAREN } | ')' { RPAREN } - + + | "0x" hex_digit+ as s { INT (int_of_string s) } + | "0X" hex_digit+ as s { INT (int_of_string s) } + | "0" oct_digit+ as s { INT (int_of_string ("0o" ^ s)) } | digit+ as i { INT (int_of_string i) } | var as v { VAR v } diff --git a/src/lib/arith_parser.mly b/src/lib/arith_parser.mly index 731f8e8..dfa8f18 100644 --- a/src/lib/arith_parser.mly +++ b/src/lib/arith_parser.mly @@ -4,12 +4,18 @@ %token INT %token VAR -%token PLUS MINUS STAR SLASH +%token PLUSEQ MINUSEQ DIVEQ MULEQ MODEQ EQ +%token PLUS MINUS MUL DIV +%token LT GT EQEQ +%token QUESTION COLON %token LPAREN RPAREN %token EOF +%right PLUSEQ MINUSEQ DIVEQ MULEQ MODEQ EQ +%right QUESTION COLON %left PLUS MINUS -%left STAR SLASH +%left MUL DIV +%left GT LT %right UMINUS UPLUS %start main @@ -20,10 +26,19 @@ main: | expr EOF { $1 } expr: - | expr PLUS expr { Add ($1, $3) } - | expr MINUS expr { Sub ($1, $3) } - | expr STAR expr { Mul ($1, $3) } - | expr SLASH expr { Div ($1, $3) } + | v=VAR PLUSEQ e=expr { Assign (Add, v, e) } + | v=VAR MINUSEQ e=expr { Assign (Sub, v, e) } + | v=VAR MULEQ e=expr { Assign (Mul, v, e) } + | v=VAR DIVEQ e=expr { Assign (Div, v, e) } + | expr PLUS expr { Binop (Add, $1, $3) } + | expr MINUS expr { Binop (Sub, $1, $3) } + | expr MUL expr { Binop (Mul, $1, $3) } + | expr DIV expr { Binop (Div, $1, $3) } + | expr LT expr { Binop (Lt, $1, $3) } + | expr GT expr { Binop (Gt, $1, $3) } + | expr EQEQ expr { Binop (Eq, $1, $3) } + | expr QUESTION expr COLON expr { Ternary ($1, $3, $5) } + | PLUS expr %prec UPLUS { $2 } | MINUS expr %prec UMINUS { Neg $2 } diff --git a/src/lib/ast.ml b/src/lib/ast.ml index d79f3fe..972a6e4 100644 --- a/src/lib/ast.ml +++ b/src/lib/ast.ml @@ -228,36 +228,36 @@ and case_item_ns : CST.case_item_ns -> case_item = match x with | CaseItemNS_Pattern_Rparen_LineBreak (a, _) -> let a = pattern a.value in - Case_pattern a + Case_pattern (a, None) | CaseItemNS_Pattern_Rparen_CompoundList (a, b) -> let a = pattern a.value in let b = compound_list b.value in - Case_compound (a, b) + Case_pattern (a, Some b) | CaseItemNS_Lparen_Pattern_Rparen_LineBreak (a, _) -> let a = pattern a.value in - Case_pattern a + Case_pattern (a, None) | CaseItemNS_Lparen_Pattern_Rparen_CompoundList (a, b) -> let a = pattern a.value in let b = compound_list b.value in - Case_compound (a, b) + Case_pattern (a, Some b) and case_item : CST.case_item -> case_item = fun x -> match x with | CaseItem_Pattern_Rparen_LineBreak_Dsemi_LineBreak (a, _, _) -> let a = pattern a.value in - Case_pattern a + Case_pattern (a, None) | CaseItem_Pattern_Rparen_CompoundList_Dsemi_LineBreak (a, b, _) -> let a = pattern a.value in let b = compound_list b.value in - Case_compound (a, b) + Case_pattern (a, Some b) | CaseItem_Lparen_Pattern_Rparen_LineBreak_Dsemi_LineBreak (a, _, _) -> let a = pattern a.value in - Case_pattern a + Case_pattern (a, None) | CaseItem_Lparen_Pattern_Rparen_CompoundList_Dsemi_LineBreak (a, b, _) -> let a = pattern a.value in let b = compound_list b.value in - Case_compound (a, b) + Case_pattern (a, Some b) and pattern : CST.pattern -> pattern = fun x -> diff --git a/src/lib/eval.ml b/src/lib/eval.ml index 278c2f8..1d8183f 100644 --- a/src/lib/eval.ml +++ b/src/lib/eval.ml @@ -15,6 +15,7 @@ module Make (S : Types.State) (E : Types.Exec) = struct executed. *) module J = Job.Make (E) + module A = Arith.Make (S) class default_map = object (_) @@ -91,23 +92,28 @@ module Make (S : Types.State) (E : Types.Exec) = struct Ast.WordName (S.expand ctx.state `Tilde) :: tilde_expansion ctx rest | v :: rest -> v :: tilde_expansion ctx rest - let rec arithmetic_expansion ctx = function - | [] -> [] - | Ast.WordArithmeticExpression word :: rest -> - let expr = Ast.word_components_to_string word in - let aexpr = - Arith_parser.main Arith_lexer.read (Lexing.from_string expr) - in - let lookup s = - match S.lookup ctx.state ~param:s with - | Some [ Ast.WordLiteral n ] when Option.is_some (int_of_string_opt n) - -> - int_of_string n - | _ -> 0 - in - let i = Arith.eval lookup aexpr in - Ast.WordLiteral (string_of_int i) :: arithmetic_expansion ctx rest - | v :: rest -> v :: arithmetic_expansion ctx rest + let arithmetic_expansion ctx expr = + let rec fold (ctx, cst) = function + | [] -> (ctx, cst) + | Ast.WordArithmeticExpression word :: rest -> + let expr = Ast.word_components_to_string word in + let aexpr = + Arith_parser.main Arith_lexer.read (Lexing.from_string expr) + in + let state, i = A.eval ctx.state aexpr in + fold + ({ ctx with state }, Ast.WordLiteral (string_of_int i) :: cst) + rest + | Ast.WordDoubleQuoted dq :: rest -> + let ctx, v = fold (ctx, []) dq in + fold (ctx, Ast.WordDoubleQuoted (List.rev v) :: cst) rest + | Ast.WordSingleQuoted dq :: rest -> + let ctx, v = fold (ctx, []) dq in + fold (ctx, Ast.WordSingleQuoted (List.rev v) :: cst) rest + | v :: rest -> fold (ctx, v :: cst) rest + in + let state, cst = fold (ctx, []) expr in + (state, List.rev cst) let stdout_for_pipeline ~sw ctx = function | [] -> (None, `Global ctx.stdout) @@ -649,7 +655,7 @@ module Make (S : Types.State) (E : Types.Exec) = struct and expand_cst (ctx : ctx) cst : ctx * Ast.word_cst = let cst = tilde_expansion ctx cst in let ctx, cst = parameter_expansion' ctx cst in - (ctx, arithmetic_expansion ctx cst) + arithmetic_expansion ctx cst and expand_redirects ((ctx, acc) : ctx * Ast.cmd_suffix_item list) (c : Ast.cmd_suffix_item list) = @@ -757,8 +763,7 @@ module Make (S : Types.State) (E : Types.Exec) = struct | Some _ as ctx -> ctx | None -> ( match pat with - | Ast.Case_pattern _ -> assert false - | Ast.Case_compound (p, sub) -> + | Ast.Case_pattern (p, sub) -> Nlist.fold_left (fun inner_acc pattern -> match inner_acc with @@ -769,7 +774,9 @@ module Make (S : Types.State) (E : Types.Exec) = struct Ast.word_components_to_string pattern in if Glob.test ~pattern scrutinee then begin - Some (exec_subshell ctx sub) + match sub with + | Some sub -> Some (exec_subshell ctx sub) + | None -> Some (Exit.zero ctx) end else inner_acc) None p)) diff --git a/src/lib/sast.ml b/src/lib/sast.ml index 8906a7d..701c60c 100644 --- a/src/lib/sast.ml +++ b/src/lib/sast.ml @@ -49,11 +49,7 @@ and for_clause = and wordlist = word Nlist.t and case_clause = Cases of word * case_list | Case of word and case_list = case_item Nlist.t - -and case_item = - | Case_pattern of pattern - | Case_compound of pattern * compound_list - +and case_item = Case_pattern of pattern * compound_list option and pattern = word Nlist.t and if_clause = diff --git a/test/built_ins.t b/test/built_ins.t index 688795e..4a9a114 100644 --- a/test/built_ins.t +++ b/test/built_ins.t @@ -187,6 +187,7 @@ First clean up the directory a bit Command should also still allow shell built-ins to run. $ msh -c "command pwd | xargs -- basename" + test 8. Alias diff --git a/test/modernish.t b/test/modernish.t new file mode 100644 index 0000000..114e56e --- /dev/null +++ b/test/modernish.t @@ -0,0 +1,22 @@ +These these are from parts of the modernish checks. + + $ cat > arith.sh << EOF + > i=7 + > j=0 + > case \$(( ( (j+=6*i)==0x2A)>0 ? 014 : 015 )) in + > ( 12 | 14 ) ;; # OK or BUG_NOOCTAL + > ( * ) exit ;; + > esac + > case \$j in + > ( 42 ) ;; # BUG_NOOCTAL + > ( * ) exit ;; + > esac + > echo "i:\$i and j:\$j" + > EOF + + $ sh arith.sh + i:7 and j:42 + + $ msh arith.sh + i:7 and j:42 +