#!/usr/bin/env ruby
# TODO:
-# * Implement expression block syntax
# * Add support for structs
# * Add support for checked unions
# * Add support for arrays
def initialize(parent = nil)
@parent = (parent || {})
- @freevars = []
+ @freevars = {}
end
def clone(type = :block)
:byte => { prefix: :constant, infix: nil, level: :none },
}
- Ident = Struct.new(:loc, :type, :name)
+ Ident = Struct.new(:loc, :type, :name, :isfree)
Val = Struct.new(:loc, :type, :value)
Def = Struct.new(:loc, :type, :name, :value)
Func = Struct.new(:loc, :type, :args, :body, :freevars)
LVLNAMES[index + 1]
end
- def variable()
- Ident.new(location(), nil, @prev.text.to_sym)
+ def variable(isfree = false)
+ Ident.new(location(), nil, @prev.text.to_sym, isfree)
end
def definition(name)
exprs = []
expect("(")
while (!matches(")"))
- args << Ident.new(loc, nil, expect(:ident).text.to_sym)
+ args << Ident.new(loc, nil, expect(:ident).text.to_sym, false)
expect(",") if not matches(")")
end
expect(")")
def self.infer_symbol(env, expr)
error(expr.loc, "undefined symbol '#{expr.name}'") if not env.defined_ever? expr.name
- env.freevars << expr.name if env.free? expr.name
+ if env.free? expr.name
+ env.freevars[expr.name] = expr.type
+ expr.isfree = true
+ end
expr.type = env[expr.name][:type]
end
infer(newenv, e)
end
end
- env.freevars = (env.freevars + newenv.freevars).uniq
+ env.freevars = env.freevars.merge(newenv.freevars)
types.last
end
end
end
+ def emit_global(out, name, type, val = nil)
+ if type.is_a? Symbol or type.is_a? String
+ val = emit(out, val)
+ @protos.puts "#{type.to_s} #{name};"
+ out.puts "#{name}" +(val ? " = #{val}" : "") + ";"
+ elsif type.is_a? Array
+ @protos.puts "#{type.last} #{name}(#{type[0..-2].join(",")});"
+ emit_func(@funcs, val, name)
+ else
+ raise "unsupported var"
+ end
+ name
+ end
+
+
def emit_toplevel(block)
out = StringIO.new
out.puts "void toplevel(void)"
lastexpr = nil
out.puts "{"
block.exprs.each do |e|
- lastexpr = emit(out, e) unless e.is_a? Parser::Ann
+ if e.is_a? Parser::Def
+ lastexpr = emit_global(out, e.name.name, e.value.type, e.value)
+ elsif not e.is_a? Parser::Ann
+ lastexpr = emit(out, e)
+ end
end
out.puts "}"
out.string
elsif e.is_a? Parser::Func
val = emit_func(out, e)
elsif e.is_a? Parser::Ident
- val = e.name
+ val = (e.isfree ? "__env->" : "") + e.name.to_s
else
raise "unsupported expression"
end
var
end
- def emit_func(out, e)
+ def emit_func(out, e, var = nil)
newout = StringIO.new
- var = mkfunc()
+ 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(", ")}) {"
+ if e.freevars.length > 0
+ newout.puts "struct {"
+ e.freevars.each do |k,v|
+ newout.puts type_to_s(v, k) + ";"
+ end
+ newout.puts "}* __env = Env;"
+ end
exprs = e.body.exprs.map do |e|
emit(newout, e) unless e.is_a? Parser::Ann
end
newout.puts "}\n\n"
@funcs.puts newout.string
if e.freevars.length > 0 then
- "MKCLOSURE(#{var},#{e.freevars.length},#{e.freevars.join(", ")})"
+ "MKCLOSURE(#{var},#{e.freevars.length},#{e.freevars.keys.join(", ")})"
else
var
end
#include <stdbool.h>
typedef struct string {
- size_t offset; \
+ size_t offset;
size_t nelems;
char* elems;
}* string;
return NULL;
}
-#define STRINGLIT(str) \
+#define STRINGLIT(str) \\
&(struct string){ .offset = 0, .nelems = sizeof(str)-1, .elems = str }
+void* Env;
+
+int main(int argc, char** argv)
+{
+ extern void toplevel(void);
+ toplevel();
+ return 0;
+}
+
eos
puts Codegen.new(tree)