From 09e66b287772f7bbdb34844fa87bb15134c76b0c Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Tue, 30 Jul 2019 10:55:15 -0400 Subject: [PATCH 1/1] initial commit --- compile.rb | 294 ++++++++++++++++++++++++++++++++++++++++++++++++++++ example.src | 11 ++ 2 files changed, 305 insertions(+) create mode 100755 compile.rb create mode 100644 example.src diff --git a/compile.rb b/compile.rb new file mode 100755 index 0000000..db7151c --- /dev/null +++ b/compile.rb @@ -0,0 +1,294 @@ +#!/usr/bin/env ruby + +require 'strscan' + +module Type + Void = "void" + Int = "int" + Char = "char" + Bool = "bool" + Func = Struct.new(:args, :return) + Ptr = Struct.new(:type) + Array = Struct.new(:type, :count) +end + +module AST + Let = Struct.new(:type, :name, :value) + Apply = Struct.new(:type, :func, :args) + Value = Struct.new(:type, :value) + Func = Struct.new(:type, :args, :body) + Var = Struct.new(:type, :name) + If = Struct.new(:type, :cond, :then, :else) + Block = Struct.new(:type, :exprs) + Array = Struct.new(:items) +end + +module Codegen + def self.type_to_s(type) + if type.is_a? Type::Ptr then + "#{type_to_s(type.type)}*" + else + type + end + end + + def self.genfunc(ast) + print "#{type_to_s(ast.type.return)} #{ast.name}(" + print (ast.type.args.each_with_index.map do |t,i| + type_to_s(t) + " " + ast.value.args[i] + end.join(", ")) + print ") {\n" + print " #{type_to_s(ast.type.return)} _rv_;\n" + print " return _rv_;\n}\n\n" + end + + def self.array_type(type) + if type.is_a? Type::Array then + array_type(type.type) + else + type + end + end + + def self.array_dims(type) + if type.is_a? Type::Array then + "[#{type.count}]" + array_dims(type.type) + else + "" + end + end + + def self.gendef(ast) + if ast.type.is_a? Type::Func then + genfunc(ast) + elsif ast.type.is_a? Type::Array then + type = array_type(ast.type) + printf "#{type} #{ast.name}#{array_dims(ast.type)};\n\n" + else + printf "#{type_to_s(ast.type)} #{ast.name};\n\n" + end + end +end + +#Codegen.gendef( +# AST::Let.new(Type::Int, "stuff", AST::Value.new(Type::Int, 42))) +#Codegen.gendef( +# AST::Let.new(Type::Bool, "stuff", AST::Value.new(Type::Bool, true))) +#Codegen.gendef( +# AST::Let.new(Type::Char, "stuff", AST::Value.new(Type::Char, "A"))) +#Codegen.gendef( +# AST::Let.new( +# Type::Array.new(Type::Int), "stuff", +# AST::Value.new(Type::Array.new(Type::Int), "A"))) +#Codegen.gendef( +# AST::Let.new( +# Type::Array.new(Type::Int, 42), "stuff", +# AST::Value.new(Type::Array.new(Type::Int, 42), "A"))) +#Codegen.gendef( +# AST::Let.new( +# Type::Array.new(Type::Array.new(Type::Int, 42), 24), "stuff", +# AST::Value.new(Type::Array.new(Type::Int, 42), "A"))) +#Codegen.gendef( +# AST::Let.new( +# Type::Func.new([Type::Ptr.new(Type::Ptr.new(Type::Char))], Type::Int), +# "main", +# AST::Func.new( +# Type::Func.new([Type::Ptr.new(Type::Ptr.new(Type::Char))], Type::Int), +# ["args"], +# AST::Value.new(Type::Int, 0))) +#) + +class Lexer + Tok = Struct.new(:text, :file, :pos, :type) + SPACE = /[ \t\v\n\r]+/ + IDENT = /[_a-zA-Z][_a-zA-Z0-9]*/ + BRACES = /[\(\)\[\]\{\}]/ + OPERATORS = /=/ + INTEGER = /[0-9]+/ + ID_TYPES = { + "requires" => :requires, + "provides" => :provides, + "let" => :let, + "var" => :var, + "fun" => :fun + } + + def initialize(path) + @file = path + @data = StringScanner.new(File.read(path)) + end + + def get_id_type(str) + ID_TYPES[str] || :ident + end + + def next + @data.skip(SPACE) + if not @data.eos? + type = nil + if @data.scan(IDENT) + type = get_id_type(@data.matched) + elsif @data.scan(BRACES) + type = @data.matched + elsif @data.scan(OPERATORS) + type = @data.matched + elsif @data.scan(INTEGER) + type = :int + else + nil + end + Tok.new(@data.matched, @file, @data.pos, type) if type + else + Tok.new("EOF", @file, @data.pos, :eof) + end + end +end + +class Parser + def initialize(path) + @lex = Lexer.new(path) + @next = nil + end + + def requires() + expect(:requires) + expect("(") + reqs = [] + while (peek().type != ")") + reqs << expect(:ident).text + end + expect(")") + reqs + end + + def provides() + expect(:provides) + expect("{") + reqs = {} + while (peek().type != "}") + declaration(reqs) + end + expect("}") + reqs + end + + def definitions(defs = {}) + while not eof? + topdef(defs) + end + defs + end + + def topdef(defs) + dectype = declaration(defs) + if dectype == :fun then + expect("{") + expect("}") + else + expect("=") + expect(:int) + end + end + + def declaration(syms) + deftype = peek().type + if (accept(:fun) || accept(:let)) + name = expect(:ident).text + if deftype == :fun + type = Type::Func.new({}, nil) + expect("(") + while (peek().type != ")") + type.args[expect(:ident).text] = type_annotation() + expect(",") if (peek().type != ")") + end + expect(")") + type.return = type_annotation() + else + type = type_annotation() + end + syms[name] = { type: type, value: nil } + else + error("invalid declaration") + end + deftype + end + + def type_annotation() + name = expect(:ident).text + if accept("[") + expect("]") + Type::Array.new(name, -1) + else + name + end + end + + private + + def error(str) + raise str + end + + def peek() + @next = @lex.next if @next.nil? + @next + end + + def matches(type) + (peek().type == type) + end + + def accept(type) + if (matches(type)) + @next = nil + true + else + false + end + end + + def expect(type) + tok = peek() + if not accept(type) + error("expected '#{type}', received '#{tok.type}'") + end + tok + end + + def consume() + expect(peek().type) + end + + def eof? + (peek().type == :eof) + end +end + +class Package + def initialize(name) + @name = name + @externs = {} + @provides = {} + @definitions = {} + end + + def add_file(path) + parse = Parser.new(path) + parse.requires + @provides = parse.provides + @definitions = parse.definitions + end + + def add_files(paths) + paths.each {|p| add_file(p) } + end + + def dump + require 'pp' + pp @definitions + end +end + +pkg = Package.new("example") +pkg.add_file("example.src") +pkg.dump() diff --git a/example.src b/example.src new file mode 100644 index 0000000..5bc65b9 --- /dev/null +++ b/example.src @@ -0,0 +1,11 @@ +requires (io) +provides { + let foo int + fun main(args string[]) int +} + +let foo int = 42 + +fun main(args string[]) int { + +} -- 2.52.0