diff options
Diffstat (limited to 'gils')
| -rw-r--r-- | gils/cat.rv | 6 | ||||
| -rw-r--r-- | gils/ed.rv | 357 |
2 files changed, 298 insertions, 65 deletions
diff --git a/gils/cat.rv b/gils/cat.rv index 220ce93..05ab418 100644 --- a/gils/cat.rv +++ b/gils/cat.rv @@ -2,8 +2,10 @@ const context = env.ctx const parsed = arg_name(context.rest, 0) -if parsed.name:len() == 0 do puts("usage: cat <path>\n") end -else do + +if parsed.name:len() == 0 do + puts("usage: cat <path>\n") +end else do const path = expand_path(parsed.name) if vfs_owns(path) do vfs_cat(path) end else do fs:cat(path) end @@ -1,89 +1,157 @@ # simple ass modal ass line ass editor -fn list_insert(items, index, value) do +#* printed by the h command *# +const ed_usage = """ + ed, the only good and standard text editor on earth + + addresses: . current | $ last | , or % all | n nth line + p print | n numbered | a append | i insert | c change | d delete + w [file] write | q quit | = number | h help | o outline | empty prints next + ! [file] run buffer or file | low,high! run range | v check | x eval and show + a/i/c take lines until a lone . + """ + +fn list_insert(items, index, value) -> table do let result = {} - for position in 0..index do result:push(items[position]) end + + for position in 0..index do + result:push(items[position]) + end + result:push(value) - for position in index..items:len() do result:push(items[position]) end + + for position in index..items:len() do + result:push(items[position]) + end + return result end -fn list_delete(items, low, high) do +fn list_delete(items, low, high) -> table do let result = {} - for position in 0..items:len() do if (position < low or position > high) result:push(items[position]) end + + for position in 0..items:len() do + if (position < low or position > high) result:push(items[position]) + end + return result end -fn ed_is_digit(character) do +fn ed_is_digit(character) -> bool do const code = character:ascii() + return code >= 48 and code <= 57 end -# parse one address at index -fn ed_addr(state, line, index) do - if index >= line:len() return { value = -1, next = index } - if line[index] == "." return { value = state.dot, next = index + 1 } - if line[index] == "$" return { value = state.lines:len() - 1, next = index + 1 } +#* parse one address at index *# +fn ed_addr(state, line, index) -> { value: num, next: num } do + if index >= line:len() do + return { value = -1, next = index } + end + + if line[index] == "." do + return { value = state.dot, next = index + 1 } + end + + if line[index] == "$" do + return { value = state.lines:len() - 1, next = index + 1 } + end + if ed_is_digit(line[index]) do let number = 0 let scan = index - while (scan < line:len() and ed_is_digit(line[scan])) do number = number * 10 + (line[scan]:ascii() - 48) scan += 1 end + + while (scan < line:len() and ed_is_digit(line[scan])) do + number = number * 10 + (line[scan]:ascii() - 48) + scan += 1 + end + return { value = number - 1, next = scan } end + return { value = -1, next = index } end -# split a command line into low, high and command -fn ed_parse(state, line) do +#* split a command line into low, high and command *# +fn ed_parse(state, line) -> { low: num, high: num, command: string } do let index = 0 let low = -1 let high = -1 - if (line:len() > 0 and (line[0] == "," or line[0] == "%")) do + + if line:len() > 0 and (line[0] == "," or line[0] == "%") do low = 0 high = state.lines:len() - 1 index = 1 - end - else do + end else do const first_addr = ed_addr(state, line, 0) - if first_addr.value >= 0 do low = first_addr.value index = first_addr.next end - if (index < line:len() and line[index] == ",") do + + if first_addr.value >= 0 do + low = first_addr.value + index = first_addr.next + end + + if index < line:len() and line[index] == "," do index += 1 + const second_addr = ed_addr(state, line, index) - if second_addr.value >= 0 do high = second_addr.value index = second_addr.next end - else do high = state.lines:len() - 1 end + + if second_addr.value >= 0 do + high = second_addr.value + index = second_addr.next + end else do + high = state.lines:len() - 1 + end end end - if low < 0 do low = state.dot end - if high < 0 do high = low end - const command = if index < line:len() line[index..] else "" + + if (low < 0) low = state.dot + if (high < 0) high = low + + const command = if (index < line:len()) line[index..] else "" + return { low = low, high = high, command = command } end fn ed_print(state, low, high, numbered) do let index = low - while index <= high do - if (index >= 0 and index < state.lines:len()) do - if numbered do puts("#{index + 1}\t#{state.lines[index]}\n") end - else do puts("#{state.lines[index]}\n") end + + while (index <= high) do + if index >= 0 and index < state.lines:len() do + if numbered do + # todo make the a \t once it renders right + puts("#{index + 1} #{state.lines[index]}\n") + end else do + puts("#{state.lines[index]}\n") + end end + index += 1 end - if (high >= 0 and high < state.lines:len()) do state.dot = high end + + if (high >= 0 and high < state.lines:len()) state.dot = high end fn ed_goto(state, low) do - if state.lines:len() == 0 do puts("?\n") return :nil end + if state.lines:len() == 0 do + puts("?\n") + return :nil + end + let target = low - if target < 0 do target = 0 end - if target >= state.lines:len() do target = state.lines:len() - 1 end + + if (target < 0) target = 0 + if (target >= state.lines:len()) target = state.lines:len() - 1 + state.dot = target puts("#{state.lines[target]}\n") end fn ed_begin_input(state, position) do let clamped = position - if clamped < 0 do clamped = 0 end - if clamped > state.lines:len() do clamped = state.lines:len() end + + if (clamped < 0) clamped = 0 + if (clamped > state.lines:len()) clamped = state.lines:len() + state.input = :true state.insert_at = clamped end @@ -91,9 +159,12 @@ end fn ed_delete(state, low, high) do state.lines = list_delete(state.lines, low, high) state.dirty = :true + let new_dot = low - if new_dot >= state.lines:len() do new_dot = state.lines:len() - 1 end - if new_dot < 0 do new_dot = 0 end + + if (new_dot >= state.lines:len()) new_dot = state.lines:len() - 1 + if (new_dot < 0) new_dot = 0 + state.dot = new_dot end @@ -102,20 +173,44 @@ fn ed_change(state, low, high) do ed_begin_input(state, low) end -fn ed_join(items) do +fn ed_join(items) -> string do let result = "" - for index in 0..items:len() do result = result ~ items[index] ~ "\n" end + + for index in 0..items:len() do + result = result ~ items[index] ~ "\n" + end + return result end +fn ed_range_text(state, low, high) -> string do + let range = {} + + for index in low..(high + 1) do + if index >= 0 and index < state.lines:len() do + range:push(state.lines[index]) + end + end + + return ed_join(range) +end + fn ed_write(state, command) do let path = state.path + if command:len() > 1 do const filename = command[1..]:trim() - if filename:len() > 0 do path = expand_path(filename) end + + if (filename:len() > 0) path = expand_path(filename) + end + + if path:len() == 0 do + puts("?\n") + return :nil end - if path:len() == 0 do puts("?\n") return :nil end + const contents = ed_join(state.lines) + fs:store(path, contents) state.path = path state.dirty = :false @@ -127,64 +222,200 @@ fn ed_quit(state) do puts(prompt()) end +fn ed_run_file(state, line, parsed) do + if line:len() > 0 and line[0] != "!" do + const text = ed_range_text(state, parsed.low, parsed.high) + + if text:len() == 0 do + puts("?\n") + return :nil + end + + env.ctx = make_ctx("", {""}) + + const error_msg = eval_script(text) + + if error_msg != :nil do + puts("!: #{error_msg}\n") + end + + return :nil + end + + let rest = "" + + if parsed.command:len() > 1 do + rest = parsed.command[1..]:trim() + end + + if rest:len() == 0 do + if state.lines:len() == 0 do + puts("?\n") + return :nil + end + + env.ctx = make_ctx("", {""}) + + const error_msg = eval_script(ed_join(state.lines)) + + if error_msg != :nil do + puts("!: #{error_msg}\n") + end + + return :nil + end + + const path = expand_path(rest:split(" ")[0]) + + if fs:size(path) < 0 do + puts("?\n") + return :nil + end + + run_script_path(path, path, make_ctx(rest, rest:split(" "))) +end + +#* check the buffer (or range) without running it *# +fn ed_check(state, line, parsed) do + let text = ed_join(state.lines) + + if line:len() > 0 and line[0] != "v" do + text = ed_range_text(state, parsed.low, parsed.high) + end + + if text:len() == 0 do + puts("?\n") + return :nil + end + + match revo.compile(text) + | {:ok, _} => puts("ok\n") + | {:err, e} => puts("#{e}\n") +end + +#* eval and show the value, like the repl does *# +fn ed_eval_show(state, line, parsed) do + let text = ed_join(state.lines) + + if line:len() > 0 and line[0] != "x" do + text = ed_range_text(state, parsed.low, parsed.high) + end + + if text:len() == 0 do + puts("?\n") + return :nil + end + + env.ctx = make_ctx("", {""}) + + match revo.eval(text) + | {:ok, v} => puts("#{v}\n") + | {:err, e} => puts("#{e}\n") +end + +#* list fn and global definitions with line numbers *# +fn ed_outline(state) do + for index in 0..state.lines:len() do + const text = state.lines[index]:trim() + + if text:starts_with?("fn ") or text:starts_with?("global ") do + puts("#{index + 1}\t#{text}\n") + end + end +end + global ed_state = { active = :false, input = :false, path = "", lines = {}, dot = 0, insert_at = 0, dirty = :false } -fn gils_ed(context) do +#* open the editor, takes over the shell until quit *# +fn gils_ed(context) -> :hold do ed_state.active = :true ed_state.input = :false ed_state.dirty = :false ed_state.lines = {} - ed_state.path = if context.argv:len() > 1 expand_path(context.argv[1]) else "" + ed_state.path = if (context.argv:len() > 1) + expand_path(context.argv[1]) + else "" + if ed_state.path:len() > 0 do const size = fs:size(ed_state.path) + if size > 0 do const contents = fs:read_bytes(ed_state.path, 0, size) const raw_lines = contents:split("\n") + for index in 0..raw_lines:len() do - if (index < raw_lines:len() - 1 or raw_lines[index]:len() > 0) do ed_state.lines:push(raw_lines[index]) end + if index < raw_lines:len() - 1 or raw_lines[index]:len() > 0 do + ed_state.lines:push(raw_lines[index]) + end end + puts("#{size}\n") + end else do + puts("#{ed_state.path}: new file\n") end - else do puts("#{ed_state.path}: new file\n") end end - ed_state.dot = if ed_state.lines:len() > 0 ed_state.lines:len() - 1 else 0 + + ed_state.dot = + if (ed_state.lines:len() > 0) + ed_state.lines:len() - 1 + else 0 + return :hold end fn gils_ed_feed(line) do const state = ed_state + if state.input do - if line == "." do state.input = :false end - else do + if line == "." do + state.input = :false + end else do state.lines = list_insert(state.lines, state.insert_at, line) state.insert_at += 1 state.dot = state.insert_at - 1 state.dirty = :true end + return :nil end + if line:len() == 0 do - if state.lines:len() == 0 do puts("?\n") return :nil end - if state.dot < state.lines:len() - 1 do state.dot += 1 end + if state.lines:len() == 0 do + puts("?\n") + return :nil + end + + if (state.dot < state.lines:len() - 1) state.dot += 1 + puts("#{state.lines[state.dot]}\n") + return :nil end + const parsed = ed_parse(state, line) - const command_char = if parsed.command:len() > 0 parsed.command[0] else "" + const command_char = if (parsed.command:len() > 0) parsed.command[0] else "" + match command_char - | "p" => ed_print(state, parsed.low, parsed.high, :false) - | "n" => ed_print(state, parsed.low, parsed.high, :true) - | "a" => ed_begin_input(state, parsed.low + 1) - | "i" => ed_begin_input(state, parsed.low) - | "c" => ed_change(state, parsed.low, parsed.high) - | "d" => ed_delete(state, parsed.low, parsed.high) - | "w" => ed_write(state, parsed.command) - | "q" => ed_quit(state) - | "=" => puts("#{state.dot + 1}\n") - | "" => ed_goto(state, parsed.low) - | _ => puts("?\n") + | "p" => ed_print(state, parsed.low, parsed.high, numbered = :false) + | "n" => ed_print(state, parsed.low, parsed.high, numbered = :true) + | "a" => ed_begin_input(state, parsed.low + 1) + | "i" => ed_begin_input(state, parsed.low) + | "c" => ed_change(state, parsed.low, parsed.high) + | "d" => ed_delete(state, parsed.low, parsed.high) + | "w" => ed_write(state, parsed.command) + | "!" => ed_run_file(state, line, parsed) + | "v" => ed_check(state, line, parsed) + | "x" => ed_eval_show(state, line, parsed) + | "q" => ed_quit(state) + | "h" => puts(ed_usage ~ "\n") + | "o" => ed_outline(state) + | "=" => puts("#{state.dot + 1}\n") + | "" => ed_goto(state, parsed.low) + | _ => puts("?\n") end -fn gils_ed_active() ed_state.active +fn gils_ed_active() -> bool do + return ed_state.active +end -register("ed [path]", "gils", "the standard editor (a i d p w q)", gils_ed) +register("ed [path]", "gils", "the editor (type h for help)", gils_ed) |
