let /* Booleans */ type bool = int var true := 1 var false := 0 /* Settings */ var IS_NIL_TRUTHY : bool := false var HAS_NIL_SYMBOL : bool := false var DEBUG_PRINT_STACK : bool := false /* Basic utility */ function concat5 ( s1: string , s2: string , s3: string , s4: string , s5: string ): string = concat(s1, concat(s2, concat(s3, concat(s4, s5)))) function concat8 ( s1: string , s2: string , s3: string , s4: string , s5: string , s6: string , s7: string , s8: string ): string = concat(s1, concat(s2, concat(s3, concat(s4, concat(s5, concat(s6, concat(s7, s8))))))) function string_to_unsigned_int (str: string): int = let var value := 0 in for i := 0 to size(str) - 1 do value := value * 10 + (ord(substring(str, i, 1))-ord("0")) ; value end function string_to_int (str: string): int = if size(str) > 1 & substring(str, 0, 1) = "-" then - string_to_unsigned_int(substring(str, 1, size(str)-1)) else string_to_unsigned_int(str) function i2s (i: int): string = if i = 0 /* TAKEN FROM FREKSEN LISP. TODO REPLACE */ then "0" else if i < 0 then concat("-", i2s(-i)) else if i < 10 then chr(ord("0") + i) else concat(i2s(i / 10), chr(ord("0") + i - (i / 10) * 10)) function int_to_string (i: int): string = i2s(i) function is_integer_string (s: string): int = s = int_to_string(string_to_int(s)) function max (a: int, b: int): int = if a > b then a else b function min (a: int, b: int): int = if a < b then a else b function safe_substring (str: string, i_start: int, i_end: int): string = ( i_start := max(0, i_start) ; i_end := min(size(str) - 1, i_end) ; substring(str, i_start, i_end - i_start + 1) ) /* Source positioning */ type pos = { at_char: int, line_num: int } function new_pos (line_num: int, at_char: int): pos = pos { at_char = at_char, line_num = line_num } function line_num (pos: pos): int = pos.line_num function at_char (pos: pos): int = pos.at_char function pos_delta_char (pos: pos, delta: int): pos = new_pos( line_num(pos) , at_char (pos) + delta) var pos_unknown: pos := nil function pos_to_string (pos: pos): string = if pos = pos_unknown then "unknown position" else concat("line ", int_to_string(line_num(pos))) /* Scheme value system */ type type_type = int type scheme_value = { typ : type_type , val_i : int , val_s : string , val_car : scheme_value , val_cdr : scheme_value , pos_l: pos , pos_r: pos } type vm_env_key = scheme_value type vm_env_elem = scheme_value type vm_env = scheme_value type scheme_environment = vm_env var type_integer := 0 var type_string := 1 var type_symbol := 2 var type_closure := 3 var type_nil := 4 var type_false := 5 var type_true := 6 var type_pair := 7 function error_value_to_string(v: scheme_value): string = if v = nil then "" else let in concat8( " 0 then concat("; I = ", int_to_string(v.val_i)) else "" , if v.val_s <> "" then concat("; S = ", (v.val_s)) else "" , if v.val_car <> nil then concat("; CAR = ", value_to_string(v.val_car)) else "" , if v.val_cdr <> nil then concat("; CDR = ", value_to_string(v.val_cdr)) else "" , "" , ">") end function value_to_string (base_v: scheme_value): string = let function val_rec (v : scheme_value , req_paren : bool): string = if v = nil then ( print("Error in value_to_string: Got nil value.\n") ; error_value_to_string(v) ) else if v.typ = type_integer then int_to_string(v.val_i) else if v.typ = type_string then concat("\"", concat(v.val_s, "\"")) else if v.typ = type_symbol then v.val_s else if v.typ = type_closure then concat("")) else if v.typ = type_nil then "()" else if v.typ = type_false then "#f" else if v.typ = type_true then "#t" else if v.typ = type_pair & v.val_cdr = nil then error_value_to_string(v) else if v.typ = type_pair & v.val_cdr <> nil & v.val_cdr.typ = type_nil then let in concat5 ( if req_paren then "(" else "" , val_rec(v.val_car, true) , if req_paren then ")" else "" , "" , "" ) end else if v.typ = type_pair then let var is_real := v.val_cdr <> nil & v.val_cdr.typ = type_pair in concat5 ( if req_paren then "(" else "" , val_rec(v.val_car, true) , if is_real then " " else " . " , val_rec(v.val_cdr, not(is_real)) , if req_paren then ")" else "" ) end else ( print("Error in value_to_string: Unknown type.\n") ; error_value_to_string(v) ) in val_rec(base_v, true) end function is_truthy (e: scheme_value): bool = e <> nil & e.typ <> type_false & (IS_NIL_TRUTHY | e.typ <> type_nil) function is_integer (e: scheme_value): bool = e <> nil & e.typ = type_integer function is_string (e: scheme_value): bool = e <> nil &e.typ = type_string function is_function (e: scheme_value): bool = e <> nil &e.typ = type_closure function is_pair (e: scheme_value): bool = e <> nil &e.typ = type_pair function is_symbol (e: scheme_value): bool = e <> nil &e.typ = type_symbol function int_val (i: int): scheme_value = scheme_value { typ = type_integer , val_i = i , val_s = "" , val_car = nil , val_cdr = nil , pos_l = pos_unknown , pos_r = pos_unknown } function str_val (s: string): scheme_value = scheme_value { typ = type_string , val_i = 0 , val_s = s , val_car = nil , val_cdr = nil , pos_l = pos_unknown , pos_r = pos_unknown } function fun_val (i: int, env: scheme_environment): scheme_value = scheme_value { typ = type_closure , val_i = i , val_s = "" , val_car = env , val_cdr = nil , pos_l = pos_unknown , pos_r = pos_unknown } var VAL_TRUE := scheme_value { typ = type_true , val_i = 1 , val_s = "" , val_car = nil , val_cdr = nil , pos_l = pos_unknown , pos_r = pos_unknown } var VAL_FALSE := scheme_value { typ = type_false , val_i = 0 , val_s = "" , val_car = nil , val_cdr = nil , pos_l = pos_unknown , pos_r = pos_unknown } function bool_val (i: int): scheme_value = if (i <> 0) then VAL_TRUE else VAL_FALSE function pair_val (car: scheme_value, cdr: scheme_value): scheme_value = scheme_value { typ = type_pair , val_i = 0 , val_s = "" , val_car = car , val_cdr = cdr , pos_l = pos_unknown , pos_r = pos_unknown } function sym_val (sym: string): scheme_value = scheme_value { typ = type_symbol , val_i = 0 , val_s = sym , val_car = nil , val_cdr = nil , pos_l = pos_unknown , pos_r = pos_unknown } function nil_val (): scheme_value = scheme_value { typ = type_nil , val_i = 0 , val_s = "" , val_car = nil , val_cdr = nil , pos_l = pos_unknown , pos_r = pos_unknown } var VALUE_UNSPECIFIED := nil_val() /* Parsing */ type sexp_ast = scheme_value function parse_string (str: string): sexp_ast = let var index := 0 var line_number := 1 function is_char (char: string, index: int): bool = let var ascii := ord(substring(str, index, 1)) in ascii = ord(char) end function is_left_paren (index: int): bool = is_char("(", index) function is_right_paren (index: int): bool = is_char(")", index) function is_comment_start (index: int): bool = is_char(";", index) function is_tick (index: int): bool = is_char("'", index) function is_goose_marks (index: int): bool = is_char("\"", index) function is_ws (index: int): int = let var ascii := ord(substring(str, index, 1)) in ascii = 32 | ascii = 9 | ascii = 10 end function is_symbol (index: int): int = let var ascii := ord(substring(str, index, 1)) in not ( ascii = 9 | ascii = 10 | ascii = 32 | ascii = 40 | ascii = 41 | ascii = ord("\"") | ascii = ord("'") | ascii = ord(";") ) end function sexp_nil (pos_l: pos, pos_r: pos): sexp_ast = sexp_ast { typ = type_nil , val_i = 0 , val_s = "" , val_car = nil , val_cdr = nil , pos_l = pos_l , pos_r = pos_r } function sexp_atom (sym: string, pos_l: pos, pos_r: pos): sexp_ast = let var val := if sym = "#f" then bool_val(0) else if sym = "#t" then bool_val(1) else if is_integer_string(sym) then int_val(string_to_int(sym)) else sym_val(sym) in val.pos_l := pos_l ; val.pos_r := pos_r ; val end function sexp_quote (datum: sexp_ast, pos_l: pos, pos_r: pos): sexp_ast = sexp_pair( sexp_atom( "quote", pos_l, pos_r) , sexp_pair( datum, sexp_nil(pos_l, pos_r) , pos_l, pos_r) , pos_l, pos_r ) function sexp_pair ( car: sexp_ast , cdr: sexp_ast , pos_l: pos , pos_r: pos): sexp_ast = sexp_ast { typ = type_pair , val_i = 0 , val_s = "" , val_car = car , val_cdr = cdr , pos_l = pos_l , pos_r = pos_r } function sexp_string ( str: string , pos_l: pos , pos_r: pos): sexp_ast = sexp_ast { typ = type_string , val_i = 0 , val_s = str , val_car = nil , val_cdr = nil , pos_l = pos_l , pos_r = pos_r } function ignore_ws () = /* Do nothing when outside string */ if index >= size(str) then () /* Handle newline */ else if is_char("\n", index) then ( line_number := line_number + 1 ; index := index + 1 ; ignore_ws() ) /* Ignore whitespace */ else if index < size(str) & is_ws(index) then ( index := index + 1 ; ignore_ws() ) /* Handle comments, ignore until newline */ else if is_comment_start(index) then ( while ord(substring(str, index, 1)) <> ord("\n") do index := index + 1 ; ignore_ws()) function parse_rec (): sexp_ast = ( ignore_ws() ; if is_symbol(index) then let var start_pos := new_pos(line_number, index) in while is_symbol(index) do index := index + 1 ; sexp_atom( safe_substring(str, at_char(start_pos), index-1) , start_pos , new_pos(line_number, index - 1) ) end else if is_tick(index) then let var start_pos := new_pos(line_number, index) var datum := ( index := index + 1 ; parse_rec() ) var exp := sexp_quote( datum , start_pos , new_pos(line_number, index)) in exp end else if is_goose_marks(index) then let var start_pos := new_pos(line_number, index) var string_str := "" function app_str (new_str: string) = string_str := concat(string_str, new_str) /* TODO: Implement escape codes */ in index := index + 1 ; while not(is_goose_marks(index)) & index < size(str) do if is_char("\\", index) /*" Gotta love bad syntax files */ then ( if is_char("n", index+1) then app_str("\n") else if is_char("t", index+1) then app_str("\t") else print("Unknown escape code in string") ; index := index + 2 ) else ( app_str(substring(str, index, 1)) ; index := index + 1 ) ; index := index + 1 ; sexp_string( string_str , start_pos , new_pos(line_number, index-1)) end else if is_left_paren(index) then let var sexp : sexp_ast := nil var sexp_last : sexp_ast := nil var start_pos : pos := new_pos(line_number, index) var most_right: pos := start_pos in index := index + 1 /* Position after index */ ; while index < size(str) & not(is_right_paren(index)) do let var parsed := parse_rec() var pos := new_pos(line_number, index) var link := sexp_pair( parsed , sexp_nil(pos, pos) , parsed.pos_l , parsed.pos_r ) /* Initial link */ in if sexp = nil then ( sexp_last := link ; sexp := sexp_last ) /* In case of . syntax. For * example: (a . b) */ /* TODO: Below is ugly! */ else if parsed.typ = type_symbol & parsed.val_s = "." then ( index := index + 1 ; sexp_last.val_cdr := parse_rec() ; link.pos_r := sexp_last.val_cdr.pos_r ) /* Continue along with sexp * parsing */ else ( sexp_last.val_cdr := link ; sexp_last := sexp_last.val_cdr ) ; most_right := if at_char(link.pos_r) > at_char(most_right) then link.pos_r else most_right ; ignore_ws() end /* Error handling */ ; if index >= size(str) then parse_error("Misaligned parenthesis") /* Give correct positions */ ; sexp_last := sexp ; while sexp_last <> nil do ( sexp_last.pos_r := most_right ; sexp_last := sexp_last.val_cdr ) ; if sexp <> nil then ( sexp.pos_l := pos_delta_char(sexp.pos_l, -1) ; sexp.pos_r := pos_delta_char(sexp.pos_r, 1)) /* Continue with stuff */ ; index := index + 1 ; if sexp = nil then sexp_nil( start_pos , most_right ) else sexp end else (parse_error("Error: I don't even!"); nil)) function parse_error(errmsg: string) = let in print("Tiger-scheme parse error\n ") ; print(errmsg) ; print("\n At index: ") ; print(int_to_string(index)) ; print("\n Nearby: ") ; print(safe_substring(str, index-10, index+10)) ; print("\n") end in parse_rec() end function sexp_ast_to_string (ast: sexp_ast): string = let function tostr_rec (ast: sexp_ast, nil_implicit: int): string = if ast = nil then "SOMEBODY FUCKED UP" else if ast.typ = type_nil & nil_implicit then "" else if ast.typ = type_pair & ast.val_car.typ <> type_pair then let var s_left := tostr_rec(ast.val_car, 0) var s_right := tostr_rec(ast.val_cdr, 1) in concat(s_left, if s_right <> "" then concat(" ", s_right) else "") end else if ast.typ = type_pair then let var s_left := tostr_rec(ast.val_car, 0) var s_right := tostr_rec(ast.val_cdr, 1) in concat("(", concat(s_left, concat(")", if s_right <> "" then concat(" ", s_right) else ""))) end else ast.val_s in concat("(", concat(tostr_rec(ast, 0), ")")) end /**** Instructions ****/ type vm_insn = { opcode: int , arg1: int , arg2: string , arg3: scheme_value , pos_l: pos , pos_r: pos } type vm_tape_tape = array of vm_insn type vm_tape = { length: int , filled: int , tape: vm_tape_tape } type vm_insn_info = { opcode : int , mnemonic : string , uses_arg1 : int , uses_arg2 : int , uses_arg3 : int } type vm_insn_info_l = array of vm_insn_info var OPCODE_ADD := 0 var OPCODE_PUSH := 1 var OPCODE_GOTO := 2 var OPCODE_CSKIP := 3 var OPCODE_DUPL := 4 var OPCODE_SWITCH := 5 var OPCODE_MULT := 6 var OPCODE_LOAD := 7 var OPCODE_CALL := 9 var OPCODE_RET := 10 var OPCODE_DGOTO := 11 var OPCODE_DEF := 12 var OPCODE_SETG := 8 var OPCODE_DEFFUN := 13 var OPCODE_POP := 14 var OPCODE_GEQ := 15 var OPCODE_OUTPUT := 17 var OPCODE_TOSTR := 18 var OPCODE_CONS := 19 var OPCODE_CAR := 20 var OPCODE_CDR := 16 var OPCODE_SET := 21 var OPCODE_FRSTR := 22 var OPCODE_COMPILE:= 23 var OPCODE_SETENV := 24 var OPCODE_NUMEQ := 25 var OPCODE_TYPEOF := 26 var vm_insn_num_opcodes := 0 var vm_insn_info := let var expected_number_opcodes := 30 var a := vm_insn_info_l [expected_number_opcodes] of nil function code ( opcode : int , mnemonic : string , uses_arg1 : int , uses_arg2 : int , uses_arg3 : int ) = ( if a[opcode] <> nil then print(concat5( "Error: Overwriting previously defined opcode information!\n Opcode: " , int_to_string(opcode) , " with previous mnenomic " , a[opcode].mnemonic , "\n")) ; a[opcode] := vm_insn_info { opcode = opcode , mnemonic = mnemonic , uses_arg1 = uses_arg1 , uses_arg2 = uses_arg2 , uses_arg3 = uses_arg3 } ; vm_insn_num_opcodes := vm_insn_num_opcodes + 1 ) in code(OPCODE_ADD, "ADD", 0, 0, 0) ; code(OPCODE_PUSH, "PUSH", 0, 0, 1) ; code(OPCODE_GOTO, "GOTO", 1, 0, 0) ; code(OPCODE_DGOTO, "DGOTO", 1, 0, 0) ; code(OPCODE_CSKIP, "CSKIP", 0, 0, 0) ; code(OPCODE_DUPL, "DUPL", 1, 0, 0) ; code(OPCODE_SWITCH, "SWITCH", 0, 0, 0) ; code(OPCODE_MULT, "MULT", 0, 0, 0) ; code(OPCODE_LOAD, "LOAD", 0, 1, 0) ; code(OPCODE_CALL, "CALL", 1, 0, 0) ; code(OPCODE_RET, "RET", 1, 0, 0) ; code(OPCODE_DEF, "DEF", 0, 1, 0) ; code(OPCODE_SETG, "SETG", 0, 1, 0) ; code(OPCODE_DEFFUN, "DEFFUN", 1, 0, 0) ; code(OPCODE_POP, "POP", 0, 0, 0) ; code(OPCODE_GEQ, "GEQ", 0, 0, 0) ; code(OPCODE_NUMEQ, "NUMEQ", 0, 0, 0) ; code(OPCODE_TYPEOF, "TYPEOF", 0, 0, 0) ; code(OPCODE_CONS, "CONS", 0, 0, 0) ; code(OPCODE_CAR, "CAR", 0, 0, 0) ; code(OPCODE_CDR, "CDR", 0, 0, 0) ; code(OPCODE_SET, "SET", 0, 1, 0) ; code(OPCODE_OUTPUT, "OUTPUT", 0, 0, 0) ; code(OPCODE_TOSTR, "TOSTR", 0, 0, 0) ; code(OPCODE_FRSTR, "FRSTR", 0, 0, 0) ; code(OPCODE_COMPILE,"COMPILE", 0, 0, 0) ; code(OPCODE_SETENV, "SETENV", 0, 0, 0) ; for i := 1 to expected_number_opcodes - 1 do if a[i] <> nil & a[i-1] = nil then print(concat5("Error: Opcode info array incorrectly initialized!\n Opcode " , int_to_string(i) , " is declared, but " , int_to_string(i-1) , " is not!")) ; print("Virtual Machine possess ") ; print(int_to_string(vm_insn_num_opcodes)) ; print(" instructions. Has space for ") ; print(int_to_string(expected_number_opcodes)) ; print(".\n") ; a end function noop_insn (pos_l: pos, pos_r: pos): vm_insn = vm_insn { opcode = OPCODE_DGOTO , arg1 = 1 , arg2 = "" , arg3 = nil , pos_l = pos_l , pos_r = pos_r } /**** Compile to VM ****/ type vm_insn_list_link = { insn: vm_insn, next: vm_insn_list_link } type vm_insn_list = { first: vm_insn_list_link, last: vm_insn_list_link } function tape_new (init_size: int): vm_tape = vm_tape { length = init_size , filled = 0 , tape = vm_tape_tape [init_size] of nil } function tape_append(tape: vm_tape, new_insns: vm_insn_list): int = let var head := new_insns.first var index_start := tape.filled var index := index_start var real_tape := tape.tape /* TODO: Ensure enough space on tape for new additions. */ in while head <> nil do ( real_tape[index] := head.insn ; index := index + 1 ; head := head.next ) ; tape.filled := index ; index_start end function concat_lists (a: vm_insn_list, b: vm_insn_list): vm_insn_list = ( if a = nil then print("Error: Impossible concat\n") else if b = nil | b.first = nil then () else if (a.first = nil & a.last <> nil) | (a.first <> nil & a.last = nil) then print(concat5( "Error: Instruction list invariant not maintained! First is " , if a.first = nil then "" else "not " , "nil, second is " , if a.last = nil then "" else "not " , "nil\n")) else if a.first = nil then ( a.first := b.first ; a.last := b.last ) else ( a.last.next := b.first ; a.last := b.last ) ; a ) function single_insn (insn: vm_insn): vm_insn_list = let var link := vm_insn_list_link { insn = insn, next = nil } in vm_insn_list { first = link, last = link } end function app_insn (insns: vm_insn_list, opcode:int, arg1: int, arg2: string, pos_l: pos, pos_r: pos) = ( concat_lists(insns, single_insn(vm_insn { opcode = opcode , arg1 = arg1 , arg2 = arg2 , arg3 = nil , pos_l = pos_l , pos_r = pos_r })) ; ()) function app_insn2 (insns: vm_insn_list, opcode:int, arg3: scheme_value, pos_l: pos, pos_r: pos) = ( concat_lists(insns, single_insn(vm_insn { opcode = opcode , arg1 = 0 , arg2 = "" , arg3 = arg3 , pos_l = pos_l , pos_r = pos_r })) ; ()) function tail_position (prev_insns: vm_insn_list, return_now: bool, pos_l: pos, pos_r: pos) = if return_now then app_insn(prev_insns, OPCODE_RET, 1, "", pos_l, pos_r) function tail_position_one (insn: vm_insn, return_now: bool, pos_l: pos, pos_r: pos): vm_insn_list = let var insns := single_insn(insn) in tail_position(insns, return_now, pos_l, pos_r) ; insns end function insn_list_length (insns: vm_insn_list): int = let var len := 0 var head := insns.first in while head <> nil do ( len := len + 1 ; head := head.next ) ; len end var ENV_EMPTY : vm_env := nil_val() var ENV_STD : vm_env := ENV_EMPTY var STD_LIB_ID_FUNCTION: scheme_value := nil var STD_LIB := let var first_insn := noop_insn(pos_unknown, pos_unknown) var std_insns := single_insn(first_insn) function app (opcode: int, arg1: int, arg2: string) = app_insn(std_insns, opcode, arg1, arg2, pos_unknown, pos_unknown) function app2 (opcode: int, arg3: scheme_value) = app_insn2(std_insns, opcode, arg3, pos_unknown, pos_unknown) function stdval (name: string, value: scheme_value) = ENV_STD := pair_val( pair_val( sym_val(name), value) , ENV_STD ) function tape_pos (): int = insn_list_length(std_insns) function stdfun (name: string) = stdval(name, fun_val(tape_pos(), nil)) in () /* Nil */ ; if HAS_NIL_SYMBOL then stdval("nil", nil_val()) /* Identity function */ ; STD_LIB_ID_FUNCTION := fun_val(tape_pos(), nil) ; app(OPCODE_RET, 1, "") /* R5RS: Pairs and Lists */ ; stdfun("pair?") ; app(OPCODE_TYPEOF, 0, "") ; app2(OPCODE_PUSH, int_val(type_pair)) ; app(OPCODE_NUMEQ, 0, "") ; app(OPCODE_RET, 1, "") ; stdfun("cons") ; app(OPCODE_CONS, 0, "") ; app(OPCODE_RET, 1, "") ; stdfun("car") ; app(OPCODE_CAR, 0, "") ; app(OPCODE_RET, 1, "") ; stdfun("cdr") ; app(OPCODE_CDR, 0, "") ; app(OPCODE_RET, 1, "") /* TODO: set-car! set-cdr! caar ... cddddr */ ; stdfun("null?") ; app(OPCODE_TYPEOF, 0, "") ; app2(OPCODE_PUSH, int_val(type_nil)) ; app(OPCODE_NUMEQ, 0, "") ; app(OPCODE_RET, 1, "") /* R5RS: Numerical Operations */ /* TODO: Support more than integers */ ; stdfun("number?") ; stdfun("complex?") ; stdfun("real?") ; stdfun("rational?") ; stdfun("integer?") ; app(OPCODE_TYPEOF, 0, "") ; app2(OPCODE_PUSH, int_val(type_integer)) ; app(OPCODE_NUMEQ, 0, "") ; app(OPCODE_RET, 1, "") ; stdfun("exact?") ; app(OPCODE_POP, 0, "") ; app2(OPCODE_PUSH, VAL_TRUE) ; app(OPCODE_RET, 1, "") ; stdfun("inexact?") ; app(OPCODE_POP, 0, "") ; app2(OPCODE_PUSH, VAL_FALSE) ; app(OPCODE_RET, 1, "") ; stdfun("+") ; app(OPCODE_ADD, 0, "") ; app(OPCODE_RET, 1, "") ; stdfun("*") ; app(OPCODE_MULT, 0, "") ; app(OPCODE_RET, 1, "") ; stdfun(">=") ; app(OPCODE_GEQ, 0, "") ; app(OPCODE_RET, 1, "") ; stdfun("<=") ; app(OPCODE_SWITCH, 0, "") ; app(OPCODE_GEQ, 0, "") ; app(OPCODE_RET, 1, "") /* TODO: =, <, > */ /* Defined in scheme: - zero? - positive? - negative? - odd? - even? */ ; stdfun("zero?") ; app2(OPCODE_PUSH, int_val(0)) ; app(OPCODE_NUMEQ, 0, "") ; app(OPCODE_RET, 1, "") /* TODO: Rest */ /* R5RS: Output */ ; stdfun("display") ; app(OPCODE_DUPL, 0, "") ; app(OPCODE_TYPEOF, 0, "") ; app2(OPCODE_PUSH, int_val(type_string)) ; app(OPCODE_NUMEQ, 0, "") ; app(OPCODE_CSKIP, 0, "") ; app(OPCODE_DGOTO, 2, "") ; app(OPCODE_TOSTR, 0, "") ; app(OPCODE_OUTPUT, 0, "") ; app2(OPCODE_PUSH, VALUE_UNSPECIFIED) ; app(OPCODE_RET, 1, "") ; stdfun("write") ; app(OPCODE_TOSTR, 0, "") ; app(OPCODE_OUTPUT, 0, "") ; app2(OPCODE_PUSH, VALUE_UNSPECIFIED) ; app(OPCODE_RET, 1, "") ; stdfun("newline") ; app2(OPCODE_PUSH, str_val("\n")) ; app(OPCODE_OUTPUT, 0, "") ; app2(OPCODE_PUSH, VALUE_UNSPECIFIED) ; app(OPCODE_RET, 1, "") /* TODO: write-char */ /* R5RS: Other */ ; stdfun("eval") ; app(OPCODE_SWITCH, 0, "") ; app(OPCODE_COMPILE, 0, "") ; app(OPCODE_SETENV, 0, "") ; app(OPCODE_RET, 0, "") ; stdfun("null-environment") /* Signal error on unknown environment version */ ; app(OPCODE_POP, 0, "") ; app2(OPCODE_PUSH, ENV_EMPTY) ; app(OPCODE_RET, 1, "") ; stdfun("scheme-report-environment") ; stdfun("interaction-environment") /* Signal error on unknown environment version */ ; app(OPCODE_POP, 0, "") ; app2(OPCODE_PUSH, ENV_STD) /* Defined R5RS environement */ ; app(OPCODE_RET, 1, "") /* Personal implementation functions */ ; stdfun("string->datum") ; app(OPCODE_FRSTR, 0, "") ; app(OPCODE_RET, 1, "") ; stdfun("set-env!") ; app(OPCODE_SWITCH, 0, "") ; app(OPCODE_SETENV, 0, "") ; app(OPCODE_RET, 1, "") ; first_insn.arg1 := insn_list_length(std_insns) ; std_insns end function compile_to_vm (ast: sexp_ast): vm_insn_list = let function list (head: vm_insn_list_link): vm_insn_list = let var tail := head in while tail <> nil & tail.next <> nil do tail := tail.next ; vm_insn_list { first = head, last = tail } end function copy_list (ls: vm_insn_list): vm_insn_list = list(ls.first) function atom_to_insn (sym: scheme_value, pos_l: pos, pos_r: pos): vm_insn = if sym = nil then ( print("Error in atom_to_list: Got nil as sym!\n") ; nil ) else if is_symbol(sym) then vm_insn { opcode = OPCODE_LOAD , arg1 = 0 , arg2 = sym.val_s , arg3 = nil , pos_l = pos_l , pos_r = pos_r } else vm_insn { opcode = OPCODE_PUSH , arg1 = 0 , arg2 = "" , arg3 = sym , pos_l = pos_l , pos_r = pos_r } function sexp_ast_length(insns: vm_insn_list): int = let function rec (insns: vm_insn_list_link, sum: int): int = if insns = nil then sum else rec(insns.next, 1 + sum) in rec(insns.first, 0) end function set_tree_positions ( ast: sexp_ast , pos_l: pos , pos_r: pos ) = /* Useful for setting the positions of generated AST. */ if ast <> nil then ( ast.pos_l := pos_l ; ast.pos_r := pos_r ; if ast.typ = type_pair then ( set_tree_positions( ast.val_cdr, pos_l, pos_r) ; set_tree_positions( ast.val_car, pos_l, pos_r) )) function sexp_is_list_of_type ( ast: sexp_ast , subtype: type_type ): bool = if ast = nil then false else if ast.typ = type_nil then true else ast.typ = type_pair & ast.val_car <> nil & ast.val_car.typ = subtype & sexp_is_list_of_type(ast.val_cdr, subtype) function sexp_is_literals_list ( ast: sexp_ast ): bool = sexp_is_list_of_type(ast, type_symbol) function sexp_is_ellipsis ( ast: sexp_ast ): bool = ast <> nil & ast.typ = type_symbol & ast.val_s = "..." function sexp_list_length (ast: sexp_ast): bool = if ast = nil | not(ast.typ = type_pair | ast.typ = type_nil) then false else if ast.typ = type_nil then 0 else let var len := sexp_list_length(ast.val_cdr) in if len < 0 then len else len + 1 end function sexp_is_pattern_datum (ast: sexp_ast): bool = ast <> nil & ( ast.typ = type_integer | ast.typ = type_string | ast.typ = type_true | ast.typ = type_false /* TODO: character */) function sexp_is_pattern_id (ast: sexp_ast): bool = ast <> nil & ast.typ = type_symbol & not(sexp_is_ellipsis(ast)) function sexp_is_pattern ( ast: sexp_ast ): bool = if ast = nil then false /* Pattern Datum */ else if sexp_is_pattern_datum(ast) then true /* The empty list '() is a pattern */ else if ast.typ = type_nil then true /* Ensure it is a pair */ else if ast.typ <> type_pair | ast.val_car = nil | ast.val_cdr = nil then false /* List of patterns: ( ...) */ /* Non-proper list of patterns: ( ... . ) */ else if ast.typ = type_pair & sexp_is_pattern(ast.val_car) & sexp_is_pattern(ast.val_cdr) then true /* ( ... ) */ else if ast.typ = type_pair & sexp_is_pattern (ast.val_car) & sexp_is_ellipsis(ast.val_cdr.val_car) & ast.val_cdr.val_cdr.typ = type_nil then true /* TODO: Hashtag notation (https://people.csail.mit.edu/jaffer/r5rs/Pattern-language.html) */ else false function sexp_is_template ( ast: sexp_ast ): bool = if ast = nil then false /* Pattern Datum */ else if sexp_is_pattern_datum(ast) then true /* The empty list '() is a template */ else if ast.typ = type_nil then true /* Ensure it is a pair */ else if ast.typ <> type_pair | ast.val_car = nil | ast.val_cdr = nil then false /* List of templates: ( ...) */ /* Pair of templates: ( .