]> git.mdlowis.com Git - proto/sclpl-rb.git/commitdiff
tweaked syntax based on ideas from oberon
authorMichael D. Lowis <mike@mdlowis.com>
Mon, 27 Jan 2020 04:21:30 +0000 (23:21 -0500)
committerMichael D. Lowis <mike@mdlowis.com>
Mon, 27 Jan 2020 04:21:30 +0000 (23:21 -0500)
compile.rb
example.src

index d9fae36ee4afee8f4871a559adb774e22aab507a..29715199b3d1696bc105dde1ffa448816856e7d1 100755 (executable)
@@ -4,122 +4,62 @@ require 'strscan'
 require 'pp'
 
 module Type
-  Void = "void"
-  Float = "float"
-
-  Range = Struct.new(:start, :stop)
-  Int = Range.new(-2147483647, 2147483647)
-  UInt = Range.new(0, 0xFFFFFFFF)
-  Byte = Range.new(0, 0xFF)
-  Char = Range.new(0, 0x10FFFF)
-
-  Enum = Struct.new(:entries)
-  Bool = Enum.new([:true, :false])
-
+  Array = Struct.new(:type, :count)
+  Record = Struct.new(:fields)
   Func = Struct.new(:args, :return)
   Ptr = Struct.new(:type)
-  Array = Struct.new(:type, :count)
+  Int = Struct.new(:min, :max)
+  Real = Struct.new(:bits)
 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
+module Ast
+  Val = Struct.new(:type, :value)
 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)))
-#)
+BuiltinSyms = {
+  "Bool" => {
+    kind: :type,
+    type: Type::Int.new(0, 1)
+  },
+  "true" => {
+    kind: :const,
+    type: "Bool",
+    value: Ast::Val.new("Bool", 1)
+  },
+  "false" => {
+    kind: :const,
+    type: "Bool",
+    value: Ast::Val.new("Bool", 0)
+  },
+  "Char" => {
+    kind: :type,
+    type: Type::Int.new(0, 0x10FFFF)
+  },
+  "Int" => {
+    kind: :type,
+    type: Type::Int.new(-2**63-1, 2**63-1)
+  },
+  "Real" => {
+    kind: :type,
+    type: Type::Real.new(64)
+  },
+  "Byte" => {
+    kind: :type,
+    type: Type::Int.new(0, 255)
+  },
+}
 
 class Lexer
   Tok = Struct.new(:text, :file, :pos, :type)
-  SPACE = /[ \t\v\n\r]+/
+  SPACE = /([ \t\v\n\r]+|#.*\n)/
   IDENT = /[_a-zA-Z][_a-zA-Z0-9]*/
   BRACES = /[\(\)\[\]\{\}]/
-  OPERATORS = /[*\/=+-]/
+  OPERATORS = /[*\/=+-:\$]/
   INTEGER = /[0-9]+/
   ID_TYPES = {
-    "requires" => :requires,
-    "provides" => :provides,
-    "let" => :let,
-    "var" => :var,
-    "fun" => :fun
+    "module" => :module,
+    "imports" => :imports,
+    "is" => :is
   }
 
   def initialize(path)
@@ -132,19 +72,18 @@ class Lexer
   end
 
   def next
-    @data.skip(SPACE)
+    while @data.skip(SPACE) do
+    end
     if not @data.eos?
-      type = nil
+      type = :eof
       if @data.scan(IDENT)
         type = get_id_type(@data.matched)
+      elsif @data.scan(INTEGER)
+        type = :int
       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
@@ -186,150 +125,148 @@ class Parser
     @next = nil
   end
 
-  def requires()
-    expect(:requires)
+  #######################################
+  # Top Level Forms
+  #######################################
+  def module()
+    expect(:module)
+    expect(:ident).text
+  end
+
+  def imports()
+    expect(:imports)
     expect("(")
     reqs = []
-    while (peek().type != ")")
+    while (!matches(")"))
       reqs << expect(:ident).text
+      expect(",") if !matches(")")
     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
-
-  private
-
-  def topdef(defs)
-    dectype = declaration(defs)
-    if dectype == :fun then
-      expect("{")
-      value = expression()
-      expect("}")
-    else
-      expect("=")
-      value = expression()
-    end
-    pp value
-  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()
+  def definitions()
+    defs = {}
+    while !matches(:eof)
+      sym = {
+        export: accept("$"),
+        name: expect(:ident).text
+      }
+      error("#{sym[:name]} multiply defined in module") if defs[sym[:name]]
+      if matches(:is)
+        typedef(sym)
+      elsif matches(":")
+        vardef(sym)
+      elsif matches("(")
+        funcdef(sym)
       else
-        type = type_annotation()
+        error("invalid symbol definition")
       end
-      syms[name] = { type: type, value: nil }
-    else
-      error("invalid declaration")
+      defs[sym[:name]] = sym
     end
-    deftype
+    defs
   end
 
-  def type_annotation()
-    name = expect(:ident).text
-    if accept("[")
-      expect("]")
-      Type::Array.new(name, -1)
+  #######################################
+  # Type Definitions
+  #######################################
+  def typedef(sym)
+    expect(:is)
+    sym[:kind] = :type
+    sym[:type] = type_specifier()
+  end
+
+  def type_specifier()
+    if matches("[")
+      array_type()
+    elsif matches("{")
+      struct_type()
+    elsif matches("*")
+      pointer_type()
+#    elsif matches(:func)
+#      func_type()
     else
+      name = expect(:ident).text
+      # TODO: check the type exists
       name
     end
   end
 
-  def expression()
-    parseLevel(:assign)
+  def array_type()
+    expect("[")
+    count = expect(:int).text.to_i if matches(:int)
+    basetype = type_specifier()
+    expect("]")
+    Type::Array.new(basetype, count)
   end
 
-  def getRule(tok)
-    RULES[tok.type]
+  def struct_type()
+    struct = Type::Record.new({})
+    expect("{")
+    while !matches("}")
+      name = expect(:ident).text
+      expect(":")
+      type = type_specifier()
+      struct.fields[name] = type
+    end
+    expect("}")
+    struct
   end
 
-  def keepGoing(level)
-    rule = getRule(peek())
-    (LEVELS[level] <= LEVELS[rule[:level]]) if rule
+  def pointer_type()
+    expect("*")
+    Type::Ptr.new(type_specifier())
   end
 
-  def parseLevel(level)
-    consume()
-    prefixRule = getRule(@prev)[:prefix]
-    if not prefixRule then
-      error("expected an expression")
-    end
-    ast = send(prefixRule)
-    while keepGoing(level)
-      consume()
-      infixRule = getRule(@prev)[:infix]
-      ast = send(infixRule, ast)
+  #######################################
+  # Variable Definitions
+  #######################################
+  def vardef(sym)
+    expect(":")
+    sym[:kind] = :var
+    sym[:type] = type_specifier()
+    if accept("=")
+      sym[:value] = const_expr()
     end
-    ast
   end
 
-  def constant()
-    if (@prev.type == :int)
-      AST::Value.new(Type::Int, @prev.text.to_i)
-    else
-      error("not a valid constant")
-    end
+  def const_expr()
+    expect("{")
+    expect("}")
   end
 
-  def grouping()
-    ast = expression()
-    expect(")")
-    ast
+  #######################################
+  # Function Definitions
+  #######################################
+  def funcdef(sym)
+    sym[:kind] = :func
+    sym[:type] = Type::Func.new([], nil)
+    func_args(sym)
+    sym[:type].return = type_specifier() if accept(":")
+    expect("{")
+    expect("}")
   end
 
-  def func_call(ast)
+  def func_args(sym)
+    expect("(")
+    while !matches(")")
+      arg = { name: expect(:ident).text }
+      expect(":")
+      arg[:type] = type_specifier()
+      expect(",") if !matches(")")
+      sym[:type].args << arg
+    end
     expect(")")
-    AST::Apply.new(nil, ast, [])
   end
 
-  def unary()
-    type = @prev.type
-    AST::Apply.new(nil, @prev.type, [parseLevel(:unary)])
-  end
-
-  def nextLevel(tok)
-    level = getRule(@prev)[:level]
-    index = LVLNAMES.find_index(level)
-    LVLNAMES[index + 1]
-  end
-
-  def binary(left)
-    type = @prev.type
-    nlvl = nextLevel(type)
-    right = parseLevel(nlvl)
-    AST::Apply.new(nil, type, [left, right])
+  def func_body(sym)
+    expect("{")
+    expect("}")
   end
 
   def error(str)
-    raise str
+    puts str
+    exit 1
   end
 
   def peek()
@@ -369,18 +306,19 @@ class Parser
 end
 
 class Package
-  def initialize(name)
-    @name = name
-    @externs = {}
-    @provides = {}
+  def initialize(path)
+    @name = nil
+    @imports = {}
+    @declares = {}
     @definitions = {}
+    add_file(path)
   end
 
   def add_file(path)
     parse = Parser.new(path)
-    parse.requires
-    @provides = parse.provides
-    @definitions = parse.definitions
+    @name = parse.module()
+    @imports = parse.imports()
+    @definitions = parse.definitions()
   end
 
   def add_files(paths)
@@ -388,10 +326,13 @@ class Package
   end
 
   def dump
-    pp @definitions
+    pp({
+        name: @name,
+        imports: @imports,
+        defines: @definitions
+    })
   end
 end
 
-pkg = Package.new("example")
-pkg.add_file("example.src")
+pkg = Package.new("example.src")
 pkg.dump()
index 8e99f2761e6179a7913a37d06a9b25d81042a55f..de48bf891a5a5d6ffaf62cf08f5467f7697cba9a 100644 (file)
@@ -1,11 +1,32 @@
-requires (io)
-provides {
-  let foo int
-  fun main(args string[]) int
-}
-
-let foo int = 1 * 2 + 3
+module Main
+imports (X11, XSel, Posix)
 
-fun main(args string[]) int {
-  42()
+Command is [ 1 String ]
+Command2 is [10 [5 String]]
+PointRef is **Point
+MyStruct is {
+  foo : Int
+  bar : [10 Int]
 }
+
+##Command is {
+##    name : String
+##}
+#
+#cmd : Command #= [ "fetch", nil, nil ]
+#
+#fetchsel(sel : String)
+#{
+##    cmd[1] = sel;
+##    Posix.exec(cmd)
+#}
+#
+#$main(args : array of String) : Int
+#{
+##    x : X11.Config = {0}
+##    X11.init(&x)
+##    X11.mkwin(&x, 1, 1, X11.PropertyChangeMask)
+##    XSel.init(&x)
+##    X11.loop(&x, nil)
+#}
+#