]> git.mdlowis.com Git - proto/sclpl-rb.git/commitdiff
initial commit
authorMichael D. Lowis <mike.lowis@gentex.com>
Tue, 30 Jul 2019 14:55:15 +0000 (10:55 -0400)
committerMichael D. Lowis <mike.lowis@gentex.com>
Tue, 30 Jul 2019 14:55:15 +0000 (10:55 -0400)
compile.rb [new file with mode: 0755]
example.src [new file with mode: 0644]

diff --git a/compile.rb b/compile.rb
new file mode 100755 (executable)
index 0000000..db7151c
--- /dev/null
@@ -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 (file)
index 0000000..5bc65b9
--- /dev/null
@@ -0,0 +1,11 @@
+requires (io)
+provides {
+  let foo int
+  fun main(args string[]) int
+}
+
+let foo int = 42
+
+fun main(args string[]) int {
+
+}