From cd9f3bc3f3ecb04eaee42c65880711bd35c7e71b Mon Sep 17 00:00:00 2001 From: Matthew Hall Date: Sat, 3 Sep 2022 16:07:58 +1200 Subject: Able to parse Thue code --- src/flint.cr | 31 +++++++++++++++++++------------ src/thue/parser.cr | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/thue/program.cr | 13 +++++++++++++ src/thue/rule.cr | 43 +++++++++++++++++++++++++++++++++++++++++++ src/thue/thue.cr | 7 +++++++ 5 files changed, 132 insertions(+), 12 deletions(-) create mode 100644 src/thue/parser.cr create mode 100644 src/thue/program.cr create mode 100644 src/thue/rule.cr create mode 100644 src/thue/thue.cr (limited to 'src') diff --git a/src/flint.cr b/src/flint.cr index f2323a2..8f11223 100644 --- a/src/flint.cr +++ b/src/flint.cr @@ -4,10 +4,10 @@ require "option_parser" require "./util.cr" require "./brainfuck/*" -require "./brainfuck/parser.cr" +require "./thue/*" module Flint - VERSION = "0.1.0" + VERSION = "0.1.1" def self.main execution_mode = :interpret @@ -23,12 +23,19 @@ module Flint chosen_language = :brainfuck memory_size = Brainfuck::DEFAULT_TAPE_SIZE - parser.banner = "Useage: flint brainfuck [OPTIONS] [FILE]" + 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.on("-h", "--help", "show this help and exit") do puts parser exit(0) @@ -48,12 +55,6 @@ module Flint parser.parse - if chosen_language.nil? - puts "ERROR: No language chosen" - puts parser - exit(1) - end - if read_stdin source_io = STDIN elsif source_file @@ -62,14 +63,20 @@ module Flint else puts "ERROR: No source file chosen" puts parser - puts source_file.nil? - puts (!read_stdin) exit(1) end - if chosen_language == :brainfuck + 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 begin diff --git a/src/thue/parser.cr b/src/thue/parser.cr new file mode 100644 index 0000000..6b3907b --- /dev/null +++ b/src/thue/parser.cr @@ -0,0 +1,50 @@ +require "../util.cr" +require "./thue.cr" +require "./rule.cr" + +class Thue::Parser + def initialize(code_io : IO) + @raw_code = code_io.gets_to_end + code_io.close # just in case + end + + def parse + rules = [] of Rule + line_iterator = @raw_code.lines.each + + line_iterator.each do |line| + case line + when "", "::=" + # stops iteration of lines to be picked up by string builder + break + + # returned match objects are provably not_nil, safe + when .matches?(R_INPUT_RULE) + m = line.match(R_INPUT_RULE).not_nil! + rules << InputRule.new(m[1]) + + when .matches?(R_OUTPUT_RULE) + m = line.match(R_OUTPUT_RULE).not_nil! + rules << OutputRule.new(m[1], m[2]) + + when .matches?(R_NORMAL_RULE) + m = line.match(R_NORMAL_RULE).not_nil! + rules << NormalRule.new(m[1], m[2]) + + else + raise Util::ParserError.new("No separator between code and initial state near '#{line}'") + end + end + + state = String.build do |str| + # resumes from where rule parser left off + line_iterator.each do |line| + str << line + str << '\n' + end + end + + {rules, state} + end +end + diff --git a/src/thue/program.cr b/src/thue/program.cr new file mode 100644 index 0000000..c59780b --- /dev/null +++ b/src/thue/program.cr @@ -0,0 +1,13 @@ +require "./thue.cr" +require "./parser.cr" + +class Thue::Program + def initialize(@parser : Parser) + end + + def interpret + something = @parser.parse + puts something + end +end + diff --git a/src/thue/rule.cr b/src/thue/rule.cr new file mode 100644 index 0000000..34c2396 --- /dev/null +++ b/src/thue/rule.cr @@ -0,0 +1,43 @@ +require "./thue.cr" + +abstract struct Thue::Rule + @pattern : Regex + + def initialize(pattern : String) + @pattern = Regex.new(pattern) + end + + def matches?(str : String) : Bool + str.matches?(@pattern) + end + + abstract def replacement : String +end + +struct Thue::NormalRule < Thue::Rule + def initialize(pattern : String, @replacement : String) + super(pattern) + end + + def replacement : String + @replacement + end +end + +struct Thue::InputRule < Thue::Rule + def replacement : String + gets + end +end + +struct Thue::OutputRule < Thue::Rule + def initialize(pattern : String, @output : String) + super(pattern) + end + + def replacement : String + print @output + return "" + end +end + diff --git a/src/thue/thue.cr b/src/thue/thue.cr new file mode 100644 index 0000000..9c22b96 --- /dev/null +++ b/src/thue/thue.cr @@ -0,0 +1,7 @@ +module Thue + # Allow matching empty patterns, handle during parsing + R_NORMAL_RULE = /^(.*)::=(.*)$/ + R_INPUT_RULE = /^(.*)::=:::$/ + R_OUTPUT_RULE = /^(.*)::=~(.*)$/ +end + -- cgit v1.2.1