:byte => { prefix: :constant, infix: nil, level: :none },
}
+ 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)
expr = expression()
if expr.is_a? Def
error("symbol '#{expr.name}' multiply defined in scope", expr.loc) if syms[expr.name] and syms[expr.name][:loc]
- syms[expr.name] ||= {}
- syms[expr.name][:loc] = expr.loc
+ syms[expr.name.name] ||= {}
+ syms[expr.name.name][:loc] = expr.loc
exprs << Call.new(expr.loc, nil, :set!, [expr.name, expr.value])
- elsif expr.is_a? Ann and expr.expr.class == Symbol
- error("symbol '#{expr.expr}' multiply annotated in scope", expr.loc) if syms[expr.expr] and syms[expr.expr][:type]
- syms[expr.expr] ||= {}
- syms[expr.expr][:type] = expr.type
+ elsif expr.is_a? Ann and expr.expr.is_a? Ident
+ error("symbol '#{expr.expr.name}' multiply annotated in scope", expr.loc) if syms[expr.expr.name] and syms[expr.expr.name][:type]
+ syms[expr.expr.name] ||= {}
+ syms[expr.expr.name][:type] = expr.type
else
exprs << expr;
end
end
def variable()
- @prev.text.to_sym
+ Ident.new(location(), nil, @prev.text.to_sym)
end
def definition(name)
- error("invalid identifier for definition") if name.class != Symbol
+ error("invalid identifier for definition") if not name.is_a? Ident
loc = location()
expr = expression()
Def.new(loc, nil, name, expr)
"|" => { int: [:int, :int, :int] },
}
+ def self.error(loc, msg)
+ lines = File.read(loc[0])[0..(loc[1])].split("\n")
+ $stderr.puts "#{loc[0]}:#{lines.length}: #{msg}"
+# $stderr.puts "#{lines.last}"
+# $stderr.puts (" " * lines.last.length) + "^"
+ exit 1
+ end
+
def self.check(env, expr, type)
if (expr.is_a? Parser::IfExpr)
check_ifexpr(env, expr, type)
check_func(env, expr, type)
else
etype = infer(env, expr)
- raise "expected #{type}, recieved #{etype}" if type != etype
+ error(expr.loc, "expected #{type}, recieved #{etype}") if type != etype
end
type
end
def self.infer(env, expr)
if expr.is_a? Parser::Val
infer_value(env, expr)
- elsif expr.is_a? Symbol
+ elsif expr.is_a? Parser::Ident
infer_symbol(env, expr)
elsif expr.is_a? Parser::Ann
infer_annotation(env, expr)
elsif expr.is_a? Parser::IfExpr
infer_ifexpr(env, expr)
else
- raise "unable to infer type of expression"
+ error(expr.loc, "unable to infer type of expression")
end
end
end
def self.infer_symbol(env, expr)
- raise "undefined symbol '#{expr}'" if env[expr].nil?
- raise "symbol '#{expr}' has unknown type" if env[expr][:type].nil?
- env[expr][:type]
+ error(expr.loc, "undefined symbol '#{expr.name}'") if env[expr.name].nil?
+ error(expr.loc, "symbol '#{expr.name}' has unknown type") if env[expr.name][:type].nil?
+ env[expr.name][:type]
end
- def infer_annotation(env, expr)
- check(env, expr.expr, expr.type)
+ def self.infer_annotation(env, expr)
+ check(env, expr.expr, expr.type)
end
def self.infer_block(env, expr)
end
def self.infer_def(env, expr)
- expr.type = env[expr.args[0]][:type]
+ expr.type = env[expr.args[0].name][:type]
if (expr.type)
check(env, expr.args[1], expr.type)
else
ltype = infer(env, expr.args[0])
if expr.args.length == 1
optype = UnaryOps[expr.func][ltype]
- raise "unknown unary operation '#{expr.func}' for operand type #{ltype}" if not optype
+ error(expr.loc, "unknown unary operation '#{expr.func}' for operand type #{ltype}") if not optype
check_call(env, expr, optype)
elsif expr.args.length == 2
optype = BinaryOps[expr.func][ltype]
- raise "unknown binary operation '#{expr.func}' for operand type #{ltype}" if not optype
+ error(expr.loc, "unknown binary operation '#{expr.func}' for operand type #{ltype}") if not optype
check_call(env, expr, optype)
else
- raise "too many operands for operator '#{expr.func}'"
+ error(expr.loc, "too many operands for operator '#{expr.func}'")
end
end
block = Parser.new("dyn.src").toplevel
pp TypeChecker.infer({}, block)
-
# TODO:
-# * refine rules for use of variables before definition and mutually recursive procedures...
\ No newline at end of file
+# * refine rules for use of variables before definition and mutually recursive procedures...
+# * Add support for structs
+# * Add support for checked unions
+# * Add support for arrays
+# * Implement proper scoping and shadowing of identifiers
+# * Implement module system for namespacing
+# * Implement generic/template system for polymorphism
\ No newline at end of file