--- /dev/null
+#!/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()