课程回顾

Swarthmore学院16年开的编译系统课,总共10次大作业。本随笔记录了相关的课堂笔记以及第6次大作业。

  • 函数声明

    增加函数声明、函数调用的抽象语法;在转换成anf之前还要检查函数声明和调用是否正确。



    well_formed函数分别检查Program中的的函数声明列表ds和main是否有错误。

    well_formed_decl函数还需要检查函数体是否正确(参考下图右边第一个递归的例子)。

    well_formed_expr函数,检查expr中调用的函数是否已经定义,接着递归调用well_formed_expr检查参数(参考下图右边第二例子)。



    anf_p将Program转换为anf文法(参考下图fact函数):首先使用anf处理main,接下来用anf_d处理函数声明,anf_d中函数体需要递归处理。



    acompile_p为Program生成汇编代码:acompile_decl为函数声明生成汇编代码,acompile_aexpr为main生成汇编代码。

    acompile_cexpr为函数调用生成汇编代码。

    acompile_decl为函数体生成汇编代码(目前只支持一个参数),包括初始化(如提前分配空间)、递归处理函数体等。



    下图为函数调用时栈帧的情况,绿色是Caller的职责,红色是Callee的职责(保存old ebp,申请空间,恢复esp/ebp等)。

  • 尾递归

    普通递归vs.尾递归:普通递归的栈空间使用情况先是达到一个峰值,然后逐渐减小;而尾递归的空间复杂度始终为O(1)。



    tail position vs. non tail position:为了能够使用尾递归优化汇编代码,需要确定哪些位置是tail position(一旦该位置表达式求值完成,整个表达式就完成了求值)。



    在递归判断tail position的时候,需要注意如果整个表达式是另一个表达式的子表达式,而且这个子表达式的位置不是tail position的时候,这个子表达式的位置则不是tail position。

    如:let ans = if ... else ... in k(ans + x),这个整个if表达式的位置就不能算是tail position。

    如:let ans = (let x = 10 in x) in ans



    Callee的职责:

     1. 使用push ebp存储old ebp。

     2. 使用mov ebp, esp更新ebp。

     3. 为local vars提前分配存储空间。

    Caller的职责:

     1. 将参数的值[esp-4]移动到eax中。

     2. 使用eax覆盖原来的参数[ebp+8]。

     3. 将esp指向old ebp。

     4. pop ebp,恢复ebp并将esp+4(指向返回地址)。

     5. 使用jmp跳转到f函数体(使用call会将返回地址再次压栈)。



    tail call optimization & proper tail calls



    两个参数的情况:如下图,z存储在[ebp+12],w存储在[ebp+8],如果尾递归调用f(z, y),需要将这两个位置的值进行更新。



    多个参数的情况:

    使用c-stack convention:调用者负责将参数格式,参数,返回指针压入栈中。

    使用functional language convention:调用者压入返回指针;被调用者负责管理参数。

  • α-renaming

    为了能够使转换成anf的变量名不产生混淆,需要对变量进行重命名。

    源代码:

    1. let x = 10, y = (let x = 5 in x) + x in y

    ANF:

    1. let x = 10 in let x = 5 in y = x + x in y

    实现α-renaming:

编程作业

  • 具体语法

    1. <program> :=
    2. | <decls> <expr>
    3. | <expr>
    4. <decls> :=
    5. | <decl>
    6. | <decl> <decls>
    7. <decl> :=
    8. | def <identifier>(<ids>): <expr>
    9. | def <identifier>(): <expr>
    10. <ids> :=
    11. | <identifier>
    12. | <identifier> , <ids>
    13. <expr> :=
    14. | let <bindings> in <expr>
    15. | if <expr>: <expr> else: <expr>
    16. | <binop-expr>
    17. <binop-expr> :=
    18. | <identifier>
    19. | <number>
    20. | true
    21. | false
    22. | add1(<expr>)
    23. | sub1(<expr>)
    24. | isnum(<expr>)
    25. | isbool(<expr>)
    26. | print(<expr>)
    27. | <identifier>(<exprs>)
    28. | <identifier>()
    29. | <expr> + <expr>
    30. | <expr> - <expr>
    31. | <expr> * <expr>
    32. | <expr> < <expr>
    33. | <expr> > <expr>
    34. | <expr> == <expr>
    35. | ( <expr> )
    36. <exprs> :=
    37. | <expr>
    38. | <expr> , <exprs>
    39. <bindings> :=
    40. | <identifier> = <expr>
    41. | <identifier> = <expr>, <bindings>
  • 抽象语法

    1. type prim1 =
    2. | Add1
    3. | Sub1
    4. | Print
    5. | IsNum
    6. | IsBool
    7. type prim2 =
    8. | Plus
    9. | Minus
    10. | Times
    11. | Less
    12. | Greater
    13. | Equal
    14. type expr =
    15. | ELet of (string * expr) list * expr
    16. | EPrim1 of prim1 * expr
    17. | EPrim2 of prim2 * expr * expr
    18. | EApp of string * expr list
    19. | EIf of expr * expr * expr
    20. | ENumber of int
    21. | EBool of bool
    22. | EId of string
    23. type decl =
    24. | DFun of string * string list * expr
    25. type program =
    26. | Program of decl list * expr
    27. type immexpr =
    28. | ImmNumber of int
    29. | ImmBool of bool
    30. | ImmId of string
    31. and cexpr =
    32. | CPrim1 of prim1 * immexpr
    33. | CPrim2 of prim2 * immexpr * immexpr
    34. | CApp of string * immexpr list
    35. | CIf of immexpr * aexpr * aexpr
    36. | CImmExpr of immexpr
    37. and aexpr =
    38. | ALet of string * cexpr * aexpr
    39. | ACExpr of cexpr
    40. and adecl =
    41. | ADFun of string * string list * aexpr
    42. and aprogram =
    43. | AProgram of adecl list * aexpr
  • 代码例子

    程序 输出
    def sum(n):
     if n < 1: 0
     else: n + sum(n - 1)
    sum(3)
    6
    def min(x, y):
     if x < y: x
     else: min(y, x)
    min(3, 1)
    1
    def f(x, x):
     y
    f(1)
    Compile error:
     Arity mismatch on call to f (expected 2 arguments, got 1)
     Function f has duplicate parameter x
     Unbound identifier y
    def f(x): x
    def f(y): y
    f(3)
    Compile error:
     Duplicate function f
    let x=1, y=2, x=a in x Compile error:
     Duplicate binding x
     Unbound identifier a

main.c

  • 错误处理:在生成的汇编代码末尾添加了错误类型的label,并将相应的参数压入栈中,main函数中的error函数会接受这些参数。

    1. error_non_number:
    2. push EAX ;; Arg 2: push the badly behaved value
    3. push 1 ;; Arg 1: a constant describing which error-code occurred
    4. call error ;; our error handler
    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <string.h>
    4. const int ERR_NOT_NUMBER = 1;
    5. const int ERR_NOT_BOOLEAN = 2;
    6. const int ERR_OVERFLOW = 3;
    7. extern int our_code_starts_here() asm("our_code_starts_here");
    8. extern int print(int val) asm("print");
    9. extern void error(int errCode, int val) asm("error");
    10. int print(int val) {
    11. if(val & 0x00000001 ^ 0x00000001) {
    12. printf("%d\n", val >> 1);
    13. } else if(val == 0xFFFFFFFF) {
    14. printf("true\n");
    15. } else if(val == 0x7FFFFFFF) {
    16. printf("false\n");
    17. } else {
    18. printf("Unknown value: %#010x\n", val);
    19. }
    20. return val;
    21. }
    22. void error(int errCode, int val) {
    23. if (errCode == ERR_NOT_NUMBER) {
    24. fprintf(stderr, "Expected number, but got %010x\n", val);
    25. } else if (errCode == ERR_NOT_BOOLEAN) {
    26. fprintf(stderr, "Expected boolean, but got %010x\n", val);
    27. } else if (errCode == ERR_OVERFLOW) {
    28. fprintf(stderr, "Error: arithemetic overflow");
    29. }
    30. exit(errCode);
    31. }
    32. int main(int argc, char** argv) {
    33. int result = our_code_starts_here();
    34. print(result);
    35. return 0;
    36. }

compile.ml

  • 静态错误检查:well_formed函数在编译时捕捉了下面的所有错误。

    • A function application with the wrong number of arguments should signal an error containing the string "arity"
    • A function application of a non-existent function should signal an error containing the string "not defined"
    • An identifier without a corresponding binding location should report an error containing the string "unbound"
    • A let binding with duplicate names should report an error containg the string "duplicate binding"
    • A function declaration with duplicate names in the argument list should report an error containg the string "duplicate parameter"
    • If there are multiple function definitions with the same name, report an error containing the string "duplicate function"
    • If a numeric constant is too large (as discussed in class), report an error containing the string "too large
    1. let rec well_formed_e (e : expr) (ds : decl list) (env : bool envt) : string list =
    2. (* FILL: you need to implement this *)
    3. match e with
    4. | EApp(fname, args) ->
    5. let found = find_decl ds fname in
    6. let error = match found with
    7. | None -> [sprintf "Function %s is not defined" fname]
    8. | Some(DFun(_, params, _)) ->
    9. let expected = List.length params in
    10. let actual = List.length args in
    11. if expected = actual then []
    12. else [sprintf "Arity mismatch on call to %s (expected %d arguments, got %d)" fname expected actual] in
    13. error @ (List.flatten (List.map (fun arg -> well_formed_e arg ds env) args))
    14. | EPrim2(_, left, right) -> (well_formed_e left ds env) @ (well_formed_e right ds env)
    15. | EPrim1(_, e) -> well_formed_e e ds env
    16. | EIf(cond, thn, els) -> (well_formed_e cond ds env) @ (well_formed_e thn ds env) @ (well_formed_e els ds env)
    17. | ELet([], body) -> well_formed_e body ds env
    18. | ELet((name, value)::rest, body) ->
    19. let found = find rest name in
    20. let error = match found with
    21. | None -> []
    22. | Some(_) -> [sprintf "Duplicate binding %s" name] in
    23. error @ (well_formed_e value ds env) @ (well_formed_e (ELet(rest, body)) ds ((name, true)::env))
    24. | ENumber(n) ->
    25. if n >= -1073741824 && n <= 1073741823 then []
    26. else [sprintf "Number precision too large %d" n]
    27. | EBool(b) -> []
    28. | EId(x) ->
    29. let found = find env x in
    30. let error = match found with
    31. | None -> [sprintf "Unbound identifier %s" x]
    32. | Some(_) -> [] in
    33. error
    34. let well_formed_d (d : decl) (ds : decl list) : string list =
    35. (* FILL: you need to implement this *)
    36. match d with
    37. | DFun(fname, args, body) ->
    38. let env = List.map (fun arg -> (arg, true)) args in
    39. let found = find_dup args in
    40. let error = match found with
    41. | None -> []
    42. | Some(x) -> [sprintf "Function %s has duplicate parameter %s" fname x] in
    43. error @ (well_formed_e body ds env)
    44. let well_formed_p (p : program) : string list =
    45. match p with
    46. | Program(ds, maine) ->
    47. (* FILL: you may need to add more errors beyond those found from
    48. the declarations and the main expression *)
    49. let found = find_dup (List.map (fun (DFun(fname, _, _)) -> fname) ds) in
    50. let error = match found with
    51. | None -> []
    52. | Some(fname) -> [sprintf "Duplicate function %s" fname] in
    53. (well_formed_e maine ds []) @
    54. (List.flatten (List.map (fun d -> well_formed_d d ds) ds)) @ error
  • 转化为anf语法:

    • CApp:一个思路是将函数调用时的参数依次和一个ImmExpr绑定,并保存到一个列表中(可以保持逆序),然后再存入CApp中(具体参考anf_list的实现)

    anf e1 (ImmHole(fun imm1 -> anf e2 (ImmHole(fun imm2 -> k [imm1; imm2]))))

    1. (*
    2. This is a slightly modified implementation of ANF. You should read and
    3. understand it (because it's good for you, and because there may be written
    4. questions that reference it). Your interaction with it in this assignment
    5. will be limited, though.
    6. The key idea is that there are two kinds of holes in expressions:
    7. immexpr-shaped and cexpr-shaped. For example, a let statement has a
    8. cexpr-shaped hole in the second position, while a prim1 has an immexpr-shaped
    9. hole for its argument.
    10. We can fill an immexpr-shaped hole with a cexpr-shaped expression by
    11. introducing a new let binding, and using the temporary id as the immexpr. And
    12. we can fill a cexpr-shaped hole with an immexpr by using CImmExpr.
    13. By using the right-shaped holes in the right places, we can avoid creating
    14. intermediate let-bindings except where it's actually necessary.
    15. For this assignment, you need to fill in the EApp case.
    16. *)
    17. type hole =
    18. | CHole of (cexpr -> aexpr)
    19. | ImmHole of (immexpr -> aexpr)
    20. let fill_imm (h : hole) (v : immexpr) : aexpr =
    21. match h with
    22. | CHole(k) -> (k (CImmExpr(v)))
    23. | ImmHole(k) -> (k v)
    24. let fill_c (h : hole) (c : cexpr) : aexpr =
    25. match h with
    26. | CHole(k) -> (k c)
    27. | ImmHole(k) ->
    28. let tmp = gen_temp "" in
    29. ALet(tmp, c, k (ImmId(tmp)))
    30. let return_hole = CHole(fun ce -> ACExpr(ce))
    31. let rec anf_list (es : expr list) (k : immexpr list -> aexpr) : aexpr =
    32. (* FILL: you probably want to implement this as a helper for the ECall case *)
    33. let rec helper es immlist =
    34. match es with
    35. | [] -> k immlist
    36. | (e::rest) -> anf e (ImmHole(fun imm -> helper rest (imm::immlist))) in
    37. helper es []
    38. and anf (e : expr) (h : hole) : aexpr =
    39. match e with
    40. | ENumber(n) -> fill_imm h (ImmNumber(n))
    41. | EBool(b) -> fill_imm h (ImmBool(b))
    42. | EId(x) -> fill_imm h (ImmId(x))
    43. | EPrim1(op, e) ->
    44. anf e (ImmHole(fun imm -> (fill_c h (CPrim1(op, imm)))))
    45. | EPrim2(op, left, right) ->
    46. anf left (ImmHole(fun limm ->
    47. anf right (ImmHole(fun rimm ->
    48. (fill_c h (CPrim2(op, limm, rimm)))))))
    49. | EApp(f, args) ->
    50. (* FILL: you need to implement this *)
    51. anf_list args (fun immlist -> fill_c h (CApp(f, immlist)))
    52. | EIf(cond, thn, els) ->
    53. anf cond (ImmHole(fun cimm ->
    54. (fill_c h (CIf(cimm, (anf thn return_hole), (anf els return_hole))))))
    55. | ELet([], body) -> anf body h
    56. | ELet((name, value)::rest, body) ->
    57. anf value (CHole(fun ce ->
    58. ALet(name, ce, anf (ELet(rest, body)) h)))
    59. let anf_decl (d : decl) : adecl =
    60. match d with
    61. | DFun(name, args, body) ->
    62. ADFun(name, args, anf body return_hole)
    63. let anf_program (p : program) : aprogram =
    64. match p with
    65. | Program(decls, main) ->
    66. AProgram(List.map anf_decl decls, anf main return_hole)
  • 生成汇编代码(格式如下)

    1. ;; extern and global stuff
    2. section .text
    3. ...
    4. fun_decl1:
    5. ;; code for fun_decl1, including stack management
    6. fun_decl2:
    7. ;; code for fun_decl2, including stack management
    8. ...
    9. our_code_starts_here:
    10. ;; main entrypoint, as before, with stack management
    11. ;; errors, as before
    12. internal_error_non_number:
    13. ...
    1. let compile_to_string prog =
    2. match well_formed_p prog with
    3. | x::rest ->
    4. (* NOTE: This is where errors are reported, by concatenating them all together *)
    5. let errstr = (List.fold_left (fun x y -> x ^ "\n" ^ y) "" (x::rest)) in
    6. failwith errstr
    7. | [] ->
    8. let anfed = (anf_program prog) in
    9. (* FILL: You need to get from ANFed program to full assembly structure
    10. this time, possibly by starting from a previous lab's code *)
    11. let preclude = sprintf "section .text
    12. extern print
    13. extern error
    14. global our_code_starts_here" in
    15. let decls = match anfed with
    16. | AProgram(decls, _) -> decls in
    17. let main = match anfed with
    18. | AProgram(_, main) -> main in
    19. let stack_setup = [
    20. ILabel("our_code_starts_here");
    21. IPush(Reg(EBP));
    22. IMov(Reg(EBP), Reg(ESP));
    23. ISub(Reg(ESP), Const(4 * count_vars main));
    24. ] in
    25. let postlude = [
    26. IMov(Reg(ESP), Reg(EBP));
    27. IPop(Reg(EBP));
    28. IRet;
    29. ILabel("error_not_number");
    30. IPush(Reg(EAX));
    31. IPush(Const(1));
    32. ICall("error");
    33. ILabel("error_not_boolean");
    34. IPush(Reg(EAX));
    35. IPush(Const(2));
    36. ICall("error");
    37. ILabel("error_overflow");
    38. IPush(Reg(EAX));
    39. IPush(Const(3));
    40. ICall("error");
    41. ] in
    42. let compiled_decls = List.flatten (List.map (fun decl -> acompile_decl decl) decls) in
    43. let compiled_main = acompile_expr main 1 [] in
    44. let as_assembly_string = (to_asm (stack_setup @ compiled_main @ postlude)) in
    45. sprintf "%s%s%s\n" preclude (to_asm compiled_decls) as_assembly_string

    下面的代码和之前类似,增加了函数调用的这部分(需遵循C-Stack Convention)

    1. let rec acompile_step (s : cexpr) (si : int) (env : int envt) : instruction list =
    2. let postlude =
    3. [
    4. ITest(Reg(EAX), Const(1));
    5. IJnz("error_not_number")
    6. ] in
    7. match s with
    8. | CPrim1(op, e) ->
    9. let eGen = acompile_imm e si env in
    10. let prelude = eGen @ postlude in
    11. begin match op with
    12. | Add1 -> prelude @ [IAdd(Reg(EAX), Const(2));]
    13. | Sub1 -> prelude @ [ISub(Reg(EAX), Const(2));]
    14. | Print -> eGen @ [IPush(Reg(EAX)); ICall("print");]
    15. | IsNum -> eGen @ [IAnd(Reg(EAX), Const(1)); IShl(Reg(EAX), Const(31)); IXor(Reg(EAX), const_true);]
    16. | IsBool -> eGen @ [IAnd(Reg(EAX), Const(1)); IShl(Reg(EAX), Const(31)); IOr(Reg(EAX), const_false);]
    17. end
    18. | CPrim2(op, left, right) ->
    19. let lGen = acompile_imm left si env in
    20. let rGen = acompile_imm right si env in
    21. let imma = acompile_imm_arg right si env in
    22. let preclude = lGen @ postlude @ rGen @ postlude in
    23. begin match op with
    24. | Plus -> preclude @ lGen @ [IAdd(Reg(EAX), imma);] @ [IJo("error_overflow");]
    25. | Minus -> preclude @ lGen @ [ISub(Reg(EAX), imma);] @ [IJo("error_overflow");]
    26. | Times -> preclude @ lGen @ [ISar(Reg(EAX), Const(1)); IMul(Reg(EAX), imma);] @ [IJo("error_overflow");]
    27. | Less -> preclude @ lGen @ [ISub(Reg(EAX), imma); IAnd(Reg(EAX), HexConst(0x80000000)); IOr(Reg(EAX), const_false);]
    28. | Greater -> preclude @ lGen @ [ISub(Reg(EAX), imma); IAnd(Reg(EAX), HexConst(0x80000000)); IAdd(Reg(EAX), const_true);]
    29. | Equal ->
    30. let end_label = gen_temp "end" in
    31. lGen @
    32. [
    33. ICmp(Reg(EAX), imma);
    34. IMov(Reg(EAX), const_false);
    35. IJne(end_label);
    36. IMov(Reg(EAX), const_true);
    37. ILabel(end_label);
    38. ]
    39. end
    40. | CIf(cond, thn, els) ->
    41. let else_label = gen_temp "else" in
    42. let endif_label = gen_temp "endif" in
    43. acompile_imm cond si env @
    44. [
    45. ITest(Reg(EAX), Const(1));
    46. IJz("error_not_boolean");
    47. ICmp(Reg(EAX), const_false);
    48. IJe(else_label)
    49. ] @
    50. acompile_expr thn si env @
    51. [
    52. IJmp(endif_label);
    53. ILabel(else_label);
    54. ] @
    55. acompile_expr els si env @
    56. [
    57. ILabel(endif_label);
    58. ]
    59. | CImmExpr(i) -> acompile_imm i si env
    60. | CApp(fname, args) -> (* Caller *)
    61. List.map (fun arg -> IPush(Sized(DWORD_PTR, acompile_imm_arg arg si env))) args @ (* 这里把参数逆序压入栈中 *)
    62. [
    63. ICall(fname);
    64. IAdd(Reg(ESP), Const(4 * List.length args));
    65. ]
    66. and acompile_expr (e : aexpr) (si : int) (env : int envt) : instruction list =
    67. match e with
    68. | ALet(id, e, body) ->
    69. let preclude = acompile_step e (si + 1) env in
    70. let postlude = acompile_expr body (si + 1) ((id, si)::env) in
    71. preclude @
    72. [
    73. IMov(RegOffset(-4 * si, EBP), Reg(EAX))
    74. ] @ postlude
    75. | ACExpr(s) -> acompile_step s si env
    76. let acompile_decl (ad : adecl) : instruction list = (* Callee *)
    77. match ad with
    78. | ADFun(fname, args, body) ->
    79. let env = List.mapi (fun i arg -> (arg, -i-2)) args in (* [ebp+8]、[ebp+12]、.... 是压入的参数 *)
    80. [
    81. ILabel(fname);
    82. IPush(Reg(EBP));
    83. IMov(Reg(EBP), Reg(ESP));
    84. ISub(Reg(ESP), Const(4 * count_vars body));
    85. ] @
    86. acompile_expr body 1 env @
    87. [
    88. IMov(Reg(ESP), Reg(EBP));
    89. IPop(Reg(EBP));
    90. IRet;
    91. ]

遗留问题

  1. Valgrind不支持最新的osx。
  2. 未实现Tail Call Optimizaion。

参考资料

starter-diamondback

[swarthmore cs75] Compiler 4 – Diamondback的更多相关文章

  1. [swarthmore cs75] Compiler 6 – Garbage Snake

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第9次大作业. 赋值的副作用:循环元组 下面的代码展示了Python3是如何处理循环列表(pri ...

  2. [swarthmore cs75] Compiler 6 – Fer-de-lance

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第8次大作业. First-class function: It treats function ...

  3. [swarthmore cs75] Compiler 5 – Egg-eater

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第7次大作业. 抽象语法: 存储方式: 栈中的数据如果最后三位(tag bits)是001表示元 ...

  4. [swarthmore cs75] Compiler 3 – Cobra

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第5次大作业. 增加了bool数据表示和比较运算符的支持,具体语法参考下图: 第一种int和bo ...

  5. [swarthmore cs75] Compiler 2 – Boa

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第4次大作业. A-Normal Form 在80年代,函数式语言编译器主要使用Continua ...

  6. [swarthmore cs75] Compiler 1 – Adder

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第3次大作业. 编译的过程:首先解析(parse)源代码,然后成抽象语法树(AST),再生成汇编 ...

  7. [swarthmore cs75] inlab1 — Tiny Compiler

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了inlab1的实践过程. tiny compiler 这个迷你的编译器可以将一个源文件,编译成可执行的二进制代码. ...

  8. [swarthmore cs75] Lab 1 — OCaml Tree Programming

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第2大次作业. 比较两个lists的逻辑: let rec cmp l ll = match ( ...

  9. [swarthmore cs75] Lab 0 Warmup & Basic OCaml

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第1次大作业. 什么是编译 编译就是执行Program->Program'转换的过程,如下 ...

随机推荐

  1. 关于ASP.NET 服务器报错 Server Error in '/' Application Runtime Error 错误及解决方法

    今天遇到一个错误 程序在服务器上运行时报错 先贴上错误代码 自己也在网上找了一些解决方法,把错误定位到服务器的配置文件也就是Web.config的问题, 于是在system.web节点下 加上cust ...

  2. jmeter启动报错

    # ./jmeter-server Using local port: 1099Server failed to start: java.rmi.server.ExportException: Lis ...

  3. java课程之团队开发冲刺1.5

    一.总结昨天进度 1.昨天由于时间较少,没有太多的时间来进行学习Sqlite 二.遇到的困难 1.由于最终的程序需要调用本地的数据库,所以我们需要在安装程序的时候就需要直接附带安装一个本地的数据库到手 ...

  4. eclipse中的web项目部署路径

    elipse添加了server之后,如果不对tomcat的部署路径做更改,则eclipse默认对工程的部署在 eclipse-workspace\.metadata.plugins\org.eclip ...

  5. scrapy meta信息丢失

    在做58同城爬二手房时,由于房产详情页内对价格进行了转码处理,所以只能从获取详情页url时同时获取该url对应房产的价格,并通过meta传递给下回调函数 现在问题是,在回调函数中找不到原函数meta信 ...

  6. js真乃神器也

    var a =document.getElementsByClassName('SearchResult__title-text'); for (i = 0;i < a.length;i++){ ...

  7. vue 打包后本地先自己启动服务 anywhere 非常好用

    :)nodejs服务器Anywhere Anywhere是一个随启随用的静态服务器,它可以随时随地将你的当前目录变成一个静态文件服务器的根目录. 一,安装node 在nodejs官网下载,安装后打开c ...

  8. springmvc 配置异步请求

    最开始按照网上配置了一个servlet class 没有继承Filter .结果报错.网上有文章说是tomcat 启动加载的servlet-3.0- api 加载了 tomcat 安装目录下lib里边 ...

  9. dotNet程序员的Java爬坑之旅(一)

    仔细想了下还是转java吧,因为后期不管是留在北京也好还是回老家也好,java的工作都会好找一点.现在的工作主要还是写.net,目标是下一次离职的时候可以找到一份全职的java工作,我一直都觉得实践才 ...

  10. python3 调用 Linux 脚本

    网上很多教程,很多都是说的2时代的. 这里把3实验过的命令记录下来 首先,如果只需要执行,或者只需要得到执行的状态可以用 os.system 调用 #!/usr/bin/env python3 pri ...