#!/usr/bin/env ruby
# TODO:
-# * Implement detection of free variables in lambdas
# * Implement expression block syntax
# * Add support for structs
# * Add support for checked unions
# * Add support for arrays
# * Implement module system for namespacing
# * Implement generic/template system or parametric polymorphism
+#
+# Atomic Types:
+# bool X
+# char
+# int X
+# float /
+# byte
+# intset
+
require 'strscan'
$debug = false
end
class SymTable < Hash
- attr_accessor :is_func
+ attr_accessor :type
attr_accessor :freevars
def initialize(parent = nil)
@parent = (parent || {})
- @is_func = false
@freevars = []
end
- def clone
- SymTable.new(self)
+ def clone(type = :block)
+ s = SymTable.new(self)
+ s.type = type
+ s
end
def [](key)
method(:[]).super_method.call(key)
end
- def defined?(key)
+ def defined_ever?(key)
(not (self[key] || {})[:type].nil?)
end
def local?(key)
if @parent.class == Hash
false
- elsif is_func
+ elsif @type == :func
block_local? key
else
@parent.local? key
end
def free?(key)
- (defined? key) and (not local? key) and (not global? key)
+ (defined_ever? key) and (not local? key) and (not global? key)
end
def merge!(env)
"else" => :else,
"end" => :end,
"let" => :let,
- "fun" => :fun,
+ "func" => :fun,
}
attr_accessor :file
end
def self.check_func(env, expr, type)
- env = env.clone
+ env = env.clone(:func)
error(expr.loc, "wrong number of arguments for function definition. expected #{type.length-1}, got #{expr.args.length}") if type.length-1 != expr.args.length
type[0..-2].each_with_index do |t, i|
env[expr.args[i].name] = { :loc => expr.loc, :type => t }
end
itype = infer_block(env, expr.body, false)
expr.freevars = env.freevars
- pp expr
error(expr.loc, "expected #{type}, received #{itype}") if itype != type.last
type.last
end
end
def self.infer_symbol(env, expr)
- error(expr.loc, "undefined symbol '#{expr.name}'") if not env.defined? expr.name
+ error(expr.loc, "undefined symbol '#{expr.name}'") if not env.defined_ever? expr.name
env.freevars << expr.name if env.free? expr.name
expr.type = env[expr.name][:type]
end
end
end
env.freevars = (env.freevars + newenv.freevars).uniq
- pp "free", env.freevars
- pp env.stack
types.last
end
name
end
+
+# Ident = Struct.new(:loc, :type, :name)
+# Val = Struct.new(:loc, :type, :value)
+# Def = Struct.new(:loc, :type, :name, :value)
+# Func = Struct.new(:loc, :type, :args, :body, :freevars)
+# Call = Struct.new(:loc, :type, :func, :args)
+# IfExpr = Struct.new(:loc, :type, :cond, :br1, :br2)
+# Ann = Struct.new(:loc, :type, :expr)
+# Block = Struct.new(:loc, :type, :exprs)
+
+
def emit(out, e, var = nil)
val = nil
type = e.type
+# puts
if e.is_a? Parser::Val
val = emit_val(out, e)
- elsif e.is_a? Parser::Call and e.func == :set!
- var = e.args[0].name
- type = e.args[1].type
- val = emit(out, e.args[1])
+ elsif e.is_a? Parser::Def
+ val = emit_def(out, e)
+ type = e.value.type
+# elsif e.is_a? Parser::Call and e.func == :set!
+# var = e.args[0].name
+# type = e.args[1].type
+# val = emit(out, e.args[1])
elsif e.is_a? Parser::Call
val = emit_call(out, e)
- elsif e.is_a? Parser::IfExpr
- val = emit_ifexpr(out, e)
- elsif e.is_a? Parser::Block
- val = emit_block(out, e)
- elsif e.is_a? Parser::Func
- val = emit_func(out, e)
+# elsif e.is_a? Parser::IfExpr
+# val = emit_ifexpr(out, e)
+# elsif e.is_a? Parser::Block
+# val = emit_block(out, e)
+# elsif e.is_a? Parser::Func
+# val = emit_func(out, e)
else
raise "unsupported"
end
e.value.to_s
when :int
e.value.to_s
+ when :float
+ e.value.to_s
when :string
e.value.to_s
+ else
+ raise "unknown value type: #{e.value.to_s}"
end
end
+ def emit_def(out, e)
+ emit_var(out, e.name.name, e.value.type, emit(out, e.value))
+ end
+
+
def emit_call(out, e)
if e.func.is_a? String
emit_opcall(out, e)
end
end
- def emit_ifexpr(out, e)
- cond = emit(out, e.cond)
- var = tempvar()
- emit_var(out, var, e.type)
- out.puts "if (#{cond}) {"
- out.puts "#{var} = #{emit(out, e.br1)};"
- out.puts "} else {"
- out.puts "#{var} = #{emit(out, e.br2)};"
- out.puts "}"
- var
- end
-
- def emit_block(out, e)
- var = tempvar()
- emit_var(out, var, e.type)
- out.puts "{"
- exprs = e.exprs.map do |e|
- emit(out, e)
- end
- out.puts "#{var} = #{exprs.last};"
- out.puts "}"
- var
- end
-
- def emit_func(out, e)
- newout = StringIO.new
- var = mkfunc()
- args = []
- e.type[0..-2].each_with_index do |t,i|
- args << type_to_s(t, e.args[i].name)
- end
- @protos.puts "#{e.type.last} #{var}(#{args.join(", ")});"
- newout.puts "#{e.type.last} #{var}(#{args.join(", ")}) {"
- exprs = e.body.exprs.map do |e|
- emit(newout, e)
- end
- newout.puts "return #{exprs.last};" if (e.type.last != :void)
- newout.puts "}\n\n"
- @funcs.puts newout.string
- var
- end
+# def emit_ifexpr(out, e)
+# cond = emit(out, e.cond)
+# var = tempvar()
+# emit_var(out, var, e.type)
+# out.puts "if (#{cond}) {"
+# out.puts "#{var} = #{emit(out, e.br1)};"
+# out.puts "} else {"
+# out.puts "#{var} = #{emit(out, e.br2)};"
+# out.puts "}"
+# var
+# end
+#
+# def emit_block(out, e)
+# var = tempvar()
+# emit_var(out, var, e.type)
+# out.puts "{"
+# exprs = e.exprs.map do |e|
+# emit(out, e)
+# end
+# out.puts "#{var} = #{exprs.last};"
+# out.puts "}"
+# var
+# end
+#
+# def emit_func(out, e)
+# newout = StringIO.new
+# var = mkfunc()
+# args = []
+# e.type[0..-2].each_with_index do |t,i|
+# args << type_to_s(t, e.args[i].name)
+# end
+# @protos.puts "#{e.type.last} #{var}(#{args.join(", ")});"
+# newout.puts "#{e.type.last} #{var}(#{args.join(", ")}) {"
+# exprs = e.body.exprs.map do |e|
+# emit(newout, e)
+# end
+# newout.puts "return #{exprs.last};" if (e.type.last != :void)
+# newout.puts "}\n\n"
+# @funcs.puts newout.string
+# var
+# end
end
STRING = <<-eos
-
+123
+123.0 # Fix emit
+""
+true
+false
a = 123
-
-foo : int -> int
-foo = fun(b){
- d = 123
- bar : int -> int
- bar = fun(c){
- a + b + c
- }
- bar(1)
- d
- e = 1
- e
- d
-}
+b = (123 < 321)
+#a = 123
+#
+#foo : int -> int
+#foo = func(b){
+# d = 123
+# bar : int -> int
+# bar = func(c){
+# a + b + c
+# }
+# bar(1)
+# d
+# e = 1
+# e
+# d
+#}
eos
tree = Parser.new.parse_string(STRING)
TypeChecker.infer_block(SymTable.new, tree, false)
+puts Codegen.new(tree)