]> git.mdlowis.com Git - proto/sclpl-rb.git/commitdiff
added return statement and scaffolding for other statements
authorMichael D. Lowis <mike.lowis@gentex.com>
Mon, 27 Jan 2020 21:54:27 +0000 (16:54 -0500)
committerMichael D. Lowis <mike.lowis@gentex.com>
Mon, 27 Jan 2020 21:54:27 +0000 (16:54 -0500)
compile.rb
example.src

index f128cd0e76d3ce85c3854c12eafe0593aa394a3a..f702ee1231fd9af6cf170a6b60fc79516159e564 100755 (executable)
@@ -4,33 +4,46 @@ require 'strscan'
 require 'pp'
 
 module Type
+  Nil = Struct.new(:value)
   Array = Struct.new(:type, :count)
   Record = Struct.new(:fields)
   Func = Struct.new(:args, :return)
   Ptr = Struct.new(:type)
   Int = Struct.new(:min, :max)
-  Real = Struct.new(:bits)
+  Float = Struct.new(:bits)
 end
 
 module Ast
+  Nil = Struct.new(:value)
   Val = Struct.new(:type, :value)
+  Apply = Struct.new(:type, :func, :args)
+  Return = Struct.new(:type, :val)
 end
 
 BuiltinSyms = {
+  "Nil" => {
+    :kind => :type,
+    :type => Type::Nil.new(0),
+  },
+#  "nil" => {
+#    :kind => :const,
+#    :type => "Nil",
+#    :value => Ast::Val.new("Nil", 0)
+#  },
   "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)
-  },
+#  "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)
@@ -39,14 +52,18 @@ BuiltinSyms = {
     kind: :type,
     type: Type::Int.new(-2**63-1, 2**63-1)
   },
-  "Real" => {
+  "Float" => {
     kind: :type,
-    type: Type::Real.new(64)
+    type: Type::Float.new(64)
   },
   "Byte" => {
     kind: :type,
     type: Type::Int.new(0, 255)
   },
+  "String" => {
+    kind: :type,
+    type: Type::Array.new("Byte", nil)
+  },
 }
 
 class Lexer
@@ -56,10 +73,15 @@ class Lexer
   BRACES = /[\(\)\[\]\{\}]/
   OPERATORS = /[*\/=+-:\$]/
   INTEGER = /[0-9]+/
+  STRING = /"(\\"|[^"])*"/
   ID_TYPES = {
     "module" => :module,
     "imports" => :imports,
-    "is" => :is
+    "is" => :is,
+    "nil" => :nil,
+    "true" => :bool,
+    "false" => :bool,
+    "return" => :return,
   }
 
   def initialize(path)
@@ -80,6 +102,8 @@ class Lexer
         type = get_id_type(@data.matched)
       elsif @data.scan(INTEGER)
         type = :int
+      elsif @data.scan(STRING)
+        type = :string
       elsif @data.scan(BRACES)
         type = @data.matched
       elsif @data.scan(OPERATORS)
@@ -111,12 +135,18 @@ class Parser
   LVLNAMES = LEVELS.keys
 
   RULES = {
-    "("  => { prefix: :grouping, infix: :func_call, level: :call   },
-    "+"  => { prefix: :unary,    infix: :binary,    level: :term   },
-    "-"  => { prefix: :unary,    infix: :binary,    level: :term   },
-    "*"  => { prefix: nil,       infix: :binary,    level: :factor },
-    "/"  => { prefix: nil,       infix: :binary,    level: :factor },
-    :int => { prefix: :constant, infix: nil,        level: :none   },
+    "("     => { prefix: :grouping, infix: :func_call, level: :call   },
+    "+"     => { prefix: :unary,    infix: :binary,    level: :term   },
+    "-"     => { prefix: :unary,    infix: :binary,    level: :term   },
+    "*"     => { prefix: nil,       infix: :binary,    level: :factor },
+    "/"     => { prefix: nil,       infix: :binary,    level: :factor },
+    :nil    => { prefix: :constant, infix: nil,        level: :none   },
+    :bool   => { prefix: :constant, infix: nil,        level: :none   },
+    :int    => { prefix: :constant, infix: nil,        level: :none   },
+    :float  => { prefix: :constant, infix: nil,        level: :none   },
+    :string => { prefix: :constant, infix: nil,        level: :none   },
+    :char   => { prefix: :constant, infix: nil,        level: :none   },
+    :byte   => { prefix: :constant, infix: nil,        level: :none   },
   }
 
   def initialize(path)
@@ -238,8 +268,7 @@ class Parser
     sym[:type] = Type::Func.new([], nil)
     func_args(sym)
     sym[:type].return = type_specifier() if accept(":")
-    expect("{")
-    expect("}")
+    block(sym)
   end
 
   def func_args(sym)
@@ -254,17 +283,112 @@ class Parser
     expect(")")
   end
 
-  def func_body(sym)
+  def block(sym)
+    sym[:value] = []
     expect("{")
+    while !matches("}")
+      sym[:value] << statement()
+    end
     expect("}")
   end
 
+  #######################################
+  # Statement Parsing
+  #######################################
+  def statement()
+    puts "statement"
+    if matches(:return)
+      expect(:return)
+      expr = expression()
+      Ast::Return.new(expr[:type], expr)
+    elsif matches(:if)
+    elseif matches(:ident)
+      # declaration
+      # assignment
+      # procedure call
+    else
+      error("invalid statment")
+    end
+  end
+
   #######################################
   # Expression Parsing
   #######################################
   def expression()
-    expect("{")
-    expect("}")
+    parseLevel(:assign)
+  end
+
+  def getRule(tok)
+    RULES[tok.type] || {}
+  end
+
+  def keepGoing(level)
+    rule = getRule(peek())
+    (LEVELS[level] <= LEVELS[rule[:level]]) if rule.keys.length > 0
+  end
+
+  def parseLevel(level)
+    consume()
+    prefixRule = getRule(@prev)[:prefix]
+    error("error parsing expression") if not prefixRule
+    ast = send(prefixRule)
+    while keepGoing(level)
+      consume()
+      infixRule = getRule(@prev)[:infix]
+      ast = send(infixRule, ast)
+    end
+    ast
+  end
+
+  def constant()
+    if (@prev.type == :int || @prev.type == :byte)
+      val = @prev.text.to_i
+      if @prev.type == :byte
+        error("invalid byte value") if (val < 0 || val > 255)
+      end
+      Ast::Val.new(Type::Int, )
+    elsif (@prev.type == :string)
+      Ast::Val.new("String", @prev.text)
+    elsif (@prev.type == :nil)
+      Ast::Val.new("Nil", 0)
+    elsif (@prev.type == :bool)
+      Ast::Val.new("Bool", @prev.text == "true")
+    elsif (@prev.type == :float)
+      error("no float support yet")
+    elsif (@prev.type == :char)
+      error("no char support yet")
+    else
+      error("not a valid constant")
+    end
+  end
+
+  def grouping()
+    ast = expression()
+    expect(")")
+    ast
+  end
+
+  def func_call(ast)
+    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])
   end
 
   #######################################
index ec09af4933a925f5dccd48f31fe5bb943e7e7b30..12e185741d51c347f7e13540ea290062d4350156 100644 (file)
@@ -1,13 +1,16 @@
 module Main
 imports (X11, XSel, Posix)
 
-cmd : [String] = [ "fetch", nil, nil ]
+cmd : [String] = true
 
-#fetchsel(sel : String)
-#{
+#[ "fetch", nil, nil ]
+
+fetchsel(sel : String)
+{
+    return 0
 ##    cmd[1] = sel;
 ##    Posix.exec(cmd)
-#}
+}
 #
 #$main(args : array of String) : Int
 #{