summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/brainfuck/program.cr13
-rw-r--r--src/flint.cr110
-rw-r--r--src/program.cr13
-rw-r--r--src/thue/program.cr10
-rw-r--r--src/util.cr4
5 files changed, 93 insertions, 57 deletions
diff --git a/src/brainfuck/program.cr b/src/brainfuck/program.cr
index 5b569df..4c3b41e 100644
--- a/src/brainfuck/program.cr
+++ b/src/brainfuck/program.cr
@@ -1,13 +1,18 @@
require "./parser.cr"
+require "../program.cr"
+require "../util.cr"
-class Brainfuck::Program
+struct Brainfuck::Program < Flint::Program
@mem_size : Int32
- def initialize(@parser : Parser, mem_size : Int32?)
- @mem_size = mem_size || DEFAULT_TAPE_SIZE
+ def initialize(source_io : IO, memory_size : Int32?)
+ super(source_io)
+
+ @parser = Parser.new(source_io)
+ @mem_size = memory_size || DEFAULT_TAPE_SIZE
end
- def interpret
+ def interpret : Nil
code = @parser.parse
jumps = find_jumps(code)
code_ptr = 0
diff --git a/src/flint.cr b/src/flint.cr
index 20a9b54..a14e0c8 100644
--- a/src/flint.cr
+++ b/src/flint.cr
@@ -3,42 +3,49 @@
require "option_parser"
require "./util.cr"
+require "./program.cr"
require "./brainfuck/*"
require "./thue/*"
module Flint
VERSION = "0.1.1"
+ enum Language
+ Brainfuck
+ Thue
+ end
+
enum ExecutionMode
Interpret
Compile
end
+ private def self.crash(message : String)
+ puts message
+ exit(1)
+ end
+
+ private def self.crash(message : String, p : OptionParser)
+ puts message
+ puts p
+ exit(1)
+ end
+
+ private def self.crash(message : String, ex_message : String?)
+ puts message
+ puts ex_message unless ex_message.nil?
+ exit(1)
+ end
+
def self.main
execution_mode = ExecutionMode::Interpret
- chosen_language = nil
- memory_size = nil
+ language = nil
source_file = nil
read_stdin = false
+ language_options = Hash(Symbol, String).new
parser = OptionParser.new do |parser|
- parser.banner = "Basic usage: flint [LANGUAGE] [OPTIONS] [FILE]"
-
- parser.on("brainfuck", "select brainfuck as the language") do
- chosen_language = :brainfuck
-
- parser.banner = "Usage: flint brainfuck [OPTIONS] [FILE]"
- parser.on("-t CELLS", "--tape-size=CELLS", "specify the number of memory cells in the tape, defaults to 30,000") do |_cells|
- memory_size = _cells.to_i
- end
- end
-
- parser.on("thue", "select Thue as the language") do
- chosen_language = :thue
-
- parser.banner = "Usage: flint thue [OPTIONS] [FILE]"
- # no thue-specific options yet
- end
+ parser.banner = "Basic usage: flint [OPTIONS] [LANGUAGE] [FILE]"
parser.on("-h", "--help", "show this help and exit") do
puts parser
@@ -51,55 +58,58 @@ module Flint
parser.on("--stdin", "read source code from STDIN instead of a file") do
read_stdin = true
end
+ parser.on("-m CELLS", "--memory-size=CELLS", "specify the number of memory cells available, defaults vary depending on language") do |_cells|
+ language_options[:memory_size] = _cells
+ end
parser.unknown_args do |_args|
- source_file = _args[0]?
+ crash("ERROR: No language chosen", parser) if _args.size == 0
+ crash("ERROR: No source file chosen", parser) if _args.size == 1 && !read_stdin
+ source_file = _args[1] unless read_stdin
+ language = case _args[0].downcase
+ when "brainfuck" then Language::Brainfuck
+ when "thue" then Language::Thue
+ else crash("ERROR: Unknown language specified: '#{_args[0]}'\nUser the '--supported-languages' flag to see whatlanguages are valid.", parser)
+ end
end
end
parser.parse
- source_io = read_stdin ? STDIN : source_file.try { |f| File.new(f) }
-
- if source_io.nil?
- puts "ERROR: No source file chosen"
- puts parser
- exit(1)
+ # at this point, source_file and language are provably not nil
+ # the not_nil! method has to be used because the type-checker cannot deduce this
+ begin
+ source_io = read_stdin ? STDIN : File.new(source_file.not_nil!)
+ rescue File::NotFoundError
+ crash("ERROR: Could not load source file '#{source_file}'")
end
- case chosen_language
- when :brainfuck
- bf_parser = Brainfuck::Parser.new(source_io)
- program = Brainfuck::Program.new(bf_parser, memory_size)
- when :thue
- thue_parser = Thue::Parser.new(source_io)
- program = Thue::Program.new(thue_parser)
- else
- puts "ERROR: No language chosen"
- puts parser
- exit(1)
- end
+ program = case language.not_nil!
+ in Language::Brainfuck
+ begin
+ m_str = language_options[:memory_size]?
+ m = m_str.try { |_m| _m.to_i}
+ rescue ArgumentError
+ crash("ERROR: Failed to parse the tape size value of '#{m_str}' as an integer", parser)
+ end
+ Brainfuck::Program.new(source_io, m)
+ in Language::Thue
+ Thue::Program.new(source_io)
+ end
begin
- p = program.not_nil!
case execution_mode
when ExecutionMode::Interpret
- p.interpret
+ program.interpret
when ExecutionMode::Compile
- p.compile
+ program.compile
end
rescue ex : Util::ParserError
- puts "Caught ParserError"
- puts ex.message
- exit(1)
+ crash("Caught ParserError", ex.message)
rescue ex : Util::InterpreterError
- puts "Error encountered while interpreting:"
- puts ex.message
- exit(1)
+ crash("Error encountered while interpreting:", ex.message)
rescue ex : Util::CompilerError
- puts "Failed to compile program:"
- puts ex.message
- exit(1)
+ crash("Failed to compile program:", ex.message)
end
end
end
diff --git a/src/program.cr b/src/program.cr
new file mode 100644
index 0000000..9f92583
--- /dev/null
+++ b/src/program.cr
@@ -0,0 +1,13 @@
+require "./util.cr"
+
+abstract struct Flint::Program
+ def initialize(@source_io : IO)
+ end
+
+ abstract def interpret : Nil
+
+ def compile
+ raise Util::NoCompilerAvailableError.new("No compiler available for this language.")
+ end
+end
+
diff --git a/src/thue/program.cr b/src/thue/program.cr
index 96e1284..c9e5f2c 100644
--- a/src/thue/program.cr
+++ b/src/thue/program.cr
@@ -1,12 +1,16 @@
require "./thue.cr"
require "./parser.cr"
require "../util.cr"
+require "../program.cr"
-class Thue::Program
- def initialize(@parser : Parser)
+struct Thue::Program < Flint::Program
+ def initialize(source_io : IO)
+ super
+
+ @parser = Parser.new(source_io)
end
- def interpret
+ def interpret : Nil
rules, state = @parser.parse
loop do
diff --git a/src/util.cr b/src/util.cr
index 8c5df5c..571a72e 100644
--- a/src/util.cr
+++ b/src/util.cr
@@ -1,4 +1,8 @@
module Util
+ alias OptionsBundle = {
+ memory_size: Int32?
+ }
+
class FlintError < Exception
end