CREATE OR REPLACE TYPE core_ns_T IS TABLE OF varchar2(100); / CREATE OR REPLACE PACKAGE core IS FUNCTION do_core_func(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, fn integer, a mal_vals) RETURN integer; FUNCTION get_core_ns RETURN core_ns_T; END core; / show errors; CREATE OR REPLACE PACKAGE BODY core AS -- general functions FUNCTION equal_Q(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, args mal_vals) RETURN integer IS BEGIN RETURN types.tf(types.equal_Q(M, H, args(1), args(2))); END; -- scalar functiosn FUNCTION symbol(M IN OUT NOCOPY types.mal_table, val integer) RETURN integer IS BEGIN RETURN types.symbol(M, TREAT(M(val) AS mal_str_T).val_str); END; FUNCTION keyword(M IN OUT NOCOPY types.mal_table, val integer) RETURN integer IS BEGIN IF types.string_Q(M, val) THEN RETURN types.keyword(M, TREAT(M(val) AS mal_str_T).val_str); ELSIF types.keyword_Q(M, val) THEN RETURN val; ELSE raise_application_error(-20009, 'invalid keyword call', TRUE); END IF; END; -- string functions FUNCTION pr_str(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, args mal_vals) RETURN integer IS BEGIN RETURN types.string(M, printer.pr_str_seq(M, H, args, ' ', TRUE)); END; FUNCTION str(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, args mal_vals) RETURN integer IS BEGIN RETURN types.string(M, printer.pr_str_seq(M, H, args, '', FALSE)); END; FUNCTION prn(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, args mal_vals) RETURN integer IS BEGIN io.writeline(printer.pr_str_seq(M, H, args, ' ', TRUE)); RETURN 1; -- nil END; FUNCTION println(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, args mal_vals) RETURN integer IS BEGIN io.writeline(printer.pr_str_seq(M, H, args, ' ', FALSE)); RETURN 1; -- nil END; FUNCTION read_string(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, args mal_vals) RETURN integer IS BEGIN IF M(args(1)).type_id = 5 THEN RETURN reader.read_str(M, H, TREAT(M(args(1)) AS mal_str_T).val_str); ELSE RETURN reader.read_str(M, H, TREAT(M(args(1)) AS mal_long_str_T).val_long_str); END IF; END; FUNCTION readline(M IN OUT NOCOPY types.mal_table, prompt integer) RETURN integer IS input CLOB; BEGIN input := io.readline(TREAT(M(prompt) AS mal_str_T).val_str, 0); RETURN types.string(M, input); EXCEPTION WHEN OTHERS THEN IF SQLCODE = -20001 THEN -- io streams closed RETURN 1; -- nil ELSE RAISE; END IF; END; FUNCTION slurp(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS content CLOB; BEGIN content := io.file_open_and_read(TREAT(M(args(1)) AS mal_str_T).val_str); content := REPLACE(content, '\n', chr(10)); RETURN types.string(M, content); END; -- numeric functions FUNCTION lt(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS BEGIN RETURN types.tf(TREAT(M(args(1)) AS mal_int_T).val_int < TREAT(M(args(2)) AS mal_int_T).val_int); END; FUNCTION lte(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS BEGIN RETURN types.tf(TREAT(M(args(1)) AS mal_int_T).val_int <= TREAT(M(args(2)) AS mal_int_T).val_int); END; FUNCTION gt(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS BEGIN RETURN types.tf(TREAT(M(args(1)) AS mal_int_T).val_int > TREAT(M(args(2)) AS mal_int_T).val_int); END; FUNCTION gte(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS BEGIN RETURN types.tf(TREAT(M(args(1)) AS mal_int_T).val_int >= TREAT(M(args(2)) AS mal_int_T).val_int); END; FUNCTION add(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS BEGIN RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int + TREAT(M(args(2)) AS mal_int_T).val_int); END; FUNCTION subtract(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS BEGIN RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int - TREAT(M(args(2)) AS mal_int_T).val_int); END; FUNCTION multiply(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS BEGIN RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int * TREAT(M(args(2)) AS mal_int_T).val_int); END; FUNCTION divide(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS BEGIN RETURN types.int(M, TREAT(M(args(1)) AS mal_int_T).val_int / TREAT(M(args(2)) AS mal_int_T).val_int); END; FUNCTION time_ms(M IN OUT NOCOPY types.mal_table) RETURN integer IS now integer; BEGIN SELECT extract(day from(sys_extract_utc(systimestamp) - to_timestamp('1970-01-01', 'YYYY-MM-DD'))) * 86400000 + to_number(to_char(sys_extract_utc(systimestamp), 'SSSSSFF3')) INTO now FROM dual; RETURN types.int(M, now); END; -- hash-map functions FUNCTION assoc(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, hm integer, kvs mal_vals) RETURN integer IS new_hm integer; midx integer; BEGIN new_hm := types.clone(M, H, hm); midx := TREAT(M(new_hm) AS mal_map_T).map_idx; -- Add the new key/values midx := types.assoc_BANG(M, H, midx, kvs); RETURN new_hm; END; FUNCTION dissoc(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, hm integer, ks mal_vals) RETURN integer IS new_hm integer; midx integer; BEGIN new_hm := types.clone(M, H, hm); midx := TREAT(M(new_hm) AS mal_map_T).map_idx; -- Remove the keys midx := types.dissoc_BANG(M, H, midx, ks); RETURN new_hm; END; FUNCTION get(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, hm integer, key integer) RETURN integer IS midx integer; k varchar2(256); val integer; BEGIN IF M(hm).type_id = 0 THEN RETURN 1; -- nil END IF; midx := TREAT(M(hm) AS mal_map_T).map_idx; k := TREAT(M(key) AS mal_str_T).val_str; IF H(midx).EXISTS(k) THEN RETURN H(midx)(k); ELSE RETURN 1; -- nil END IF; END; FUNCTION contains_Q(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, hm integer, key integer) RETURN integer IS midx integer; k varchar2(256); val integer; BEGIN midx := TREAT(M(hm) AS mal_map_T).map_idx; k := TREAT(M(key) AS mal_str_T).val_str; RETURN types.tf(H(midx).EXISTS(k)); END; FUNCTION keys(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, hm integer) RETURN integer IS midx integer; k varchar2(256); ks mal_vals; val integer; BEGIN midx := TREAT(M(hm) AS mal_map_T).map_idx; ks := mal_vals(); k := H(midx).FIRST(); WHILE k IS NOT NULL LOOP ks.EXTEND(); ks(ks.COUNT()) := types.string(M, k); k := H(midx).NEXT(k); END LOOP; RETURN types.seq(M, 8, ks); END; FUNCTION vals(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, hm integer) RETURN integer IS midx integer; k varchar2(256); ks mal_vals; val integer; BEGIN midx := TREAT(M(hm) AS mal_map_T).map_idx; ks := mal_vals(); k := H(midx).FIRST(); WHILE k IS NOT NULL LOOP ks.EXTEND(); ks(ks.COUNT()) := H(midx)(k); k := H(midx).NEXT(k); END LOOP; RETURN types.seq(M, 8, ks); END; -- sequence functions FUNCTION cons(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS new_items mal_vals; len integer; i integer; BEGIN new_items := mal_vals(); len := types.count(M, args(2)); new_items.EXTEND(len+1); new_items(1) := args(1); FOR i IN 1..len LOOP new_items(i+1) := TREAT(M(args(2)) AS mal_seq_T).val_seq(i); END LOOP; RETURN types.seq(M, 8, new_items); END; FUNCTION concat(M IN OUT NOCOPY types.mal_table, args mal_vals) RETURN integer IS new_items mal_vals; cur_len integer; seq_len integer; i integer; j integer; BEGIN new_items := mal_vals(); cur_len := 0; FOR i IN 1..args.COUNT() LOOP seq_len := types.count(M, args(i)); new_items.EXTEND(seq_len); FOR j IN 1..seq_len LOOP new_items(cur_len + j) := types.nth(M, args(i), j-1); END LOOP; cur_len := cur_len + seq_len; END LOOP; RETURN types.seq(M, 8, new_items); END; FUNCTION nth(M IN OUT NOCOPY types.mal_table, val integer, ival integer) RETURN integer IS idx integer; BEGIN idx := TREAT(M(ival) AS mal_int_T).val_int; RETURN types.nth(M, val, idx); END; FUNCTION first(M IN OUT NOCOPY types.mal_table, val integer) RETURN integer IS BEGIN IF val = 1 OR types.count(M, val) = 0 THEN RETURN 1; -- nil ELSE RETURN types.first(M, val); END IF; END; FUNCTION rest(M IN OUT NOCOPY types.mal_table, val integer) RETURN integer IS BEGIN IF val = 1 OR types.count(M, val) = 0 THEN RETURN types.list(M); ELSE RETURN types.slice(M, val, 1); END IF; END; FUNCTION do_count(M IN OUT NOCOPY types.mal_table, val integer) RETURN integer IS BEGIN IF M(val).type_id = 0 THEN RETURN types.int(M, 0); ELSE RETURN types.int(M, types.count(M, val)); END IF; END; FUNCTION conj(M IN OUT NOCOPY types.mal_table, seq integer, vals mal_vals) RETURN integer IS type_id integer; slen integer; items mal_vals; BEGIN type_id := M(seq).type_id; slen := types.count(M, seq); items := mal_vals(); items.EXTEND(slen + vals.COUNT()); CASE WHEN type_id = 8 THEN FOR i IN 1..vals.COUNT() LOOP items(i) := vals(vals.COUNT + 1 - i); END LOOP; FOR i IN 1..slen LOOP items(vals.COUNT() + i) := types.nth(M, seq, i-1); END LOOP; WHEN type_id = 9 THEN FOR i IN 1..slen LOOP items(i) := types.nth(M, seq, i-1); END LOOP; FOR i IN 1..vals.COUNT() LOOP items(slen + i) := vals(i); END LOOP; ELSE raise_application_error(-20009, 'conj: not supported on type ' || type_id, TRUE); END CASE; RETURN types.seq(M, type_id, items); END; FUNCTION seq(M IN OUT NOCOPY types.mal_table, val integer) RETURN integer IS type_id integer; new_val integer; str CLOB; str_items mal_vals; BEGIN type_id := M(val).type_id; CASE WHEN type_id = 8 THEN IF types.count(M, val) = 0 THEN RETURN 1; -- nil END IF; RETURN val; WHEN type_id = 9 THEN IF types.count(M, val) = 0 THEN RETURN 1; -- nil END IF; RETURN types.seq(M, 8, TREAT(M(val) AS mal_seq_T).val_seq); WHEN types.string_Q(M, val) THEN str := TREAT(M(val) AS mal_str_T).val_str; IF str IS NULL THEN RETURN 1; -- nil END IF; str_items := mal_vals(); str_items.EXTEND(LENGTH(str)); FOR i IN 1..LENGTH(str) LOOP str_items(i) := types.string(M, SUBSTR(str, i, 1)); END LOOP; RETURN types.seq(M, 8, str_items); WHEN type_id = 0 THEN RETURN 1; -- nil ELSE raise_application_error(-20009, 'seq: not supported on type ' || type_id, TRUE); END CASE; END; -- metadata functions FUNCTION meta(M IN OUT NOCOPY types.mal_table, val integer) RETURN integer IS type_id integer; BEGIN type_id := M(val).type_id; IF type_id IN (8,9) THEN -- list/vector RETURN TREAT(M(val) AS mal_seq_T).meta; ELSIF type_id = 10 THEN -- hash-map RETURN TREAT(M(val) AS mal_map_T).meta; ELSIF type_id = 11 THEN -- native function RETURN 1; -- nil ELSIF type_id = 12 THEN -- mal function RETURN TREAT(M(val) AS mal_func_T).meta; ELSE raise_application_error(-20006, 'meta: metadata not supported on type', TRUE); END IF; END; -- general native function case/switch FUNCTION do_core_func(M IN OUT NOCOPY types.mal_table, H IN OUT NOCOPY types.map_entry_table, fn integer, a mal_vals) RETURN integer IS fname varchar(256); idx integer; BEGIN IF M(fn).type_id <> 11 THEN raise_application_error(-20004, 'Invalid function call', TRUE); END IF; fname := TREAT(M(fn) AS mal_str_T).val_str; CASE WHEN fname = '=' THEN RETURN equal_Q(M, H, a); WHEN fname = 'nil?' THEN RETURN types.tf(a(1) = 1); WHEN fname = 'false?' THEN RETURN types.tf(a(1) = 2); WHEN fname = 'true?' THEN RETURN types.tf(a(1) = 3); WHEN fname = 'string?' THEN RETURN types.tf(types.string_Q(M, a(1))); WHEN fname = 'symbol' THEN RETURN symbol(M, a(1)); WHEN fname = 'symbol?' THEN RETURN types.tf(M(a(1)).type_id = 7); WHEN fname = 'keyword' THEN RETURN keyword(M, a(1)); WHEN fname = 'keyword?' THEN RETURN types.tf(types.keyword_Q(M, a(1))); WHEN fname = 'number?' THEN RETURN types.tf(types.number_Q(M, a(1))); WHEN fname = 'fn?' THEN RETURN types.tf(types.function_Q(M, a(1))); WHEN fname = 'macro?' THEN RETURN types.tf(types.macro_Q(M, a(1))); WHEN fname = 'pr-str' THEN RETURN pr_str(M, H, a); WHEN fname = 'str' THEN RETURN str(M, H, a); WHEN fname = 'prn' THEN RETURN prn(M, H, a); WHEN fname = 'println' THEN RETURN println(M, H, a); WHEN fname = 'read-string' THEN RETURN read_string(M, H, a); WHEN fname = 'readline' THEN RETURN readline(M, a(1)); WHEN fname = 'slurp' THEN RETURN slurp(M, a); WHEN fname = '<' THEN RETURN lt(M, a); WHEN fname = '<=' THEN RETURN lte(M, a); WHEN fname = '>' THEN RETURN gt(M, a); WHEN fname = '>=' THEN RETURN gte(M, a); WHEN fname = '+' THEN RETURN add(M, a); WHEN fname = '-' THEN RETURN subtract(M, a); WHEN fname = '*' THEN RETURN multiply(M, a); WHEN fname = '/' THEN RETURN divide(M, a); WHEN fname = 'time-ms' THEN RETURN time_ms(M); WHEN fname = 'list' THEN RETURN types.seq(M, 8, a); WHEN fname = 'list?' THEN RETURN types.tf(M(a(1)).type_id = 8); WHEN fname = 'vector' THEN RETURN types.seq(M, 9, a); WHEN fname = 'vector?' THEN RETURN types.tf(M(a(1)).type_id = 9); WHEN fname = 'hash-map' THEN RETURN types.hash_map(M, H, a); WHEN fname = 'assoc' THEN RETURN assoc(M, H, a(1), types.islice(a, 1)); WHEN fname = 'dissoc' THEN RETURN dissoc(M, H, a(1), types.islice(a, 1)); WHEN fname = 'map?' THEN RETURN types.tf(M(a(1)).type_id = 10); WHEN fname = 'get' THEN RETURN get(M, H, a(1), a(2)); WHEN fname = 'contains?' THEN RETURN contains_Q(M, H, a(1), a(2)); WHEN fname = 'keys' THEN RETURN keys(M, H, a(1)); WHEN fname = 'vals' THEN RETURN vals(M, H, a(1)); WHEN fname = 'sequential?' THEN RETURN types.tf(M(a(1)).type_id IN (8,9)); WHEN fname = 'cons' THEN RETURN cons(M, a); WHEN fname = 'concat' THEN RETURN concat(M, a); WHEN fname = 'nth' THEN RETURN nth(M, a(1), a(2)); WHEN fname = 'first' THEN RETURN first(M, a(1)); WHEN fname = 'rest' THEN RETURN rest(M, a(1)); WHEN fname = 'empty?' THEN RETURN types.tf(0 = types.count(M, a(1))); WHEN fname = 'count' THEN RETURN do_count(M, a(1)); WHEN fname = 'conj' THEN RETURN conj(M, a(1), types.islice(a, 1)); WHEN fname = 'seq' THEN RETURN seq(M, a(1)); WHEN fname = 'meta' THEN RETURN meta(M, a(1)); WHEN fname = 'with-meta' THEN RETURN types.clone(M, H, a(1), a(2)); WHEN fname = 'atom' THEN RETURN types.atom_new(M, a(1)); WHEN fname = 'atom?' THEN RETURN types.tf(M(a(1)).type_id = 13); WHEN fname = 'deref' THEN RETURN TREAT(M(a(1)) AS mal_atom_T).val; WHEN fname = 'reset!' THEN RETURN types.atom_reset(M, a(1), a(2)); ELSE raise_application_error(-20004, 'Invalid function call', TRUE); END CASE; END; FUNCTION get_core_ns RETURN core_ns_T IS BEGIN RETURN core_ns_T( '=', 'throw', 'nil?', 'true?', 'false?', 'string?', 'symbol', 'symbol?', 'keyword', 'keyword?', 'number?', 'fn?', 'macro?', 'pr-str', 'str', 'prn', 'println', 'read-string', 'readline', 'slurp', '<', '<=', '>', '>=', '+', '-', '*', '/', 'time-ms', 'list', 'list?', 'vector', 'vector?', 'hash-map', 'assoc', 'dissoc', 'map?', 'get', 'contains?', 'keys', 'vals', 'sequential?', 'cons', 'concat', 'nth', 'first', 'rest', 'empty?', 'count', 'apply', -- defined in step do_builtin function 'map', -- defined in step do_builtin function 'conj', 'seq', 'meta', 'with-meta', 'atom', 'atom?', 'deref', 'reset!', 'swap!' -- defined in step do_builtin function ); END; END core; / show errors;