<commit_before>use libc::{c_char, c_int};
use pyport::Py_ssize_t;
use object::*;

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub fn PyErr_SetNone(arg1: *mut PyObject) -> ();
    pub fn PyErr_SetObject(arg1: *mut PyObject, arg2: *mut PyObject) -> ();
    pub fn PyErr_SetString(exception: *mut PyObject,
                           string: *const c_char) -> ();
    pub fn PyErr_Occurred() -> *mut PyObject;
    pub fn PyErr_Clear() -> ();
    pub fn PyErr_Fetch(arg1: *mut *mut PyObject, arg2: *mut *mut PyObject,
                       arg3: *mut *mut PyObject) -> ();
    pub fn PyErr_Restore(arg1: *mut PyObject, arg2: *mut PyObject,
                         arg3: *mut PyObject) -> ();
    pub fn PyErr_GetExcInfo(arg1: *mut *mut PyObject,
                            arg2: *mut *mut PyObject,
                            arg3: *mut *mut PyObject) -> ();
    pub fn PyErr_SetExcInfo(arg1: *mut PyObject, arg2: *mut PyObject,
                            arg3: *mut PyObject) -> ();
    pub fn Py_FatalError(message: *const c_char) -> !;
    pub fn PyErr_GivenExceptionMatches(arg1: *mut PyObject,
                                       arg2: *mut PyObject) -> c_int;
    pub fn PyErr_ExceptionMatches(arg1: *mut PyObject) -> c_int;
    pub fn PyErr_NormalizeException(arg1: *mut *mut PyObject,
                                    arg2: *mut *mut PyObject,
                                    arg3: *mut *mut PyObject) -> ();
    pub fn PyException_SetTraceback(arg1: *mut PyObject, arg2: *mut PyObject)
     -> c_int;
    pub fn PyException_GetTraceback(arg1: *mut PyObject) -> *mut PyObject;
    pub fn PyException_GetCause(arg1: *mut PyObject) -> *mut PyObject;
    pub fn PyException_SetCause(arg1: *mut PyObject, arg2: *mut PyObject)
     -> ();
    pub fn PyException_GetContext(arg1: *mut PyObject) -> *mut PyObject;
    pub fn PyException_SetContext(arg1: *mut PyObject, arg2: *mut PyObject)
     -> ();
}

#[inline]
pub unsafe fn PyExceptionClass_Check(x: *mut PyObject) -> c_int {
    (PyType_Check(x) != 0 &&
     PyType_FastSubclass(x as *mut PyTypeObject, Py_TPFLAGS_BASE_EXC_SUBCLASS) != 0) as c_int
}

#[inline]
pub unsafe fn PyExceptionInstance_Check(x: *mut PyObject) -> c_int {
    PyType_FastSubclass((*x).ob_type, Py_TPFLAGS_BASE_EXC_SUBCLASS)
}

#[inline]
pub unsafe fn PyExceptionInstance_Class(x: *mut PyObject) -> *mut PyObject {
    (*x).ob_type as *mut PyObject
}

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub static mut PyExc_BaseException: *mut PyObject;
    pub static mut PyExc_Exception: *mut PyObject;
    #[cfg(Py_3_5)] pub static mut PyExc_StopAsyncIteration: *mut PyObject;
    pub static mut PyExc_StopIteration: *mut PyObject;
    pub static mut PyExc_GeneratorExit: *mut PyObject;
    pub static mut PyExc_ArithmeticError: *mut PyObject;
    pub static mut PyExc_LookupError: *mut PyObject;
    pub static mut PyExc_AssertionError: *mut PyObject;
    pub static mut PyExc_AttributeError: *mut PyObject;
    pub static mut PyExc_BufferError: *mut PyObject;
    pub static mut PyExc_EOFError: *mut PyObject;
    pub static mut PyExc_FloatingPointError: *mut PyObject;
    pub static mut PyExc_OSError: *mut PyObject;
    pub static mut PyExc_ImportError: *mut PyObject;
    #[cfg(Py_3_6)] pub static mut PyExc_ModuleNotFoundError: *mut PyObject;
    pub static mut PyExc_IndexError: *mut PyObject;
    pub static mut PyExc_KeyError: *mut PyObject;
    pub static mut PyExc_KeyboardInterrupt: *mut PyObject;
    pub static mut PyExc_MemoryError: *mut PyObject;
    pub static mut PyExc_NameError: *mut PyObject;
    pub static mut PyExc_OverflowError: *mut PyObject;
    pub static mut PyExc_RuntimeError: *mut PyObject;
    #[cfg(Py_3_5)] pub static mut PyExc_RecursionError: *mut PyObject;
    pub static mut PyExc_NotImplementedError: *mut PyObject;
    pub static mut PyExc_SyntaxError: *mut PyObject;
    pub static mut PyExc_IndentationError: *mut PyObject;
    pub static mut PyExc_TabError: *mut PyObject;
    pub static mut PyExc_ReferenceError: *mut PyObject;
    pub static mut PyExc_SystemError: *mut PyObject;
    pub static mut PyExc_SystemExit: *mut PyObject;
    pub static mut PyExc_TypeError: *mut PyObject;
    pub static mut PyExc_UnboundLocalError: *mut PyObject;
    pub static mut PyExc_UnicodeError: *mut PyObject;
    pub static mut PyExc_UnicodeEncodeError: *mut PyObject;
    pub static mut PyExc_UnicodeDecodeError: *mut PyObject;
    pub static mut PyExc_UnicodeTranslateError: *mut PyObject;
    pub static mut PyExc_ValueError: *mut PyObject;
    pub static mut PyExc_ZeroDivisionError: *mut PyObject;
    pub static mut PyExc_BlockingIOError: *mut PyObject;
    pub static mut PyExc_BrokenPipeError: *mut PyObject;
    pub static mut PyExc_ChildProcessError: *mut PyObject;
    pub static mut PyExc_ConnectionError: *mut PyObject;
    pub static mut PyExc_ConnectionAbortedError: *mut PyObject;
    pub static mut PyExc_ConnectionRefusedError: *mut PyObject;
    pub static mut PyExc_ConnectionResetError: *mut PyObject;
    pub static mut PyExc_FileExistsError: *mut PyObject;
    pub static mut PyExc_FileNotFoundError: *mut PyObject;
    pub static mut PyExc_InterruptedError: *mut PyObject;
    pub static mut PyExc_IsADirectoryError: *mut PyObject;
    pub static mut PyExc_NotADirectoryError: *mut PyObject;
    pub static mut PyExc_PermissionError: *mut PyObject;
    pub static mut PyExc_ProcessLookupError: *mut PyObject;

    // Compatibility aliases
    pub static mut PyExc_TimeoutError: *mut PyObject;
    pub static mut PyExc_EnvironmentError: *mut PyObject;
    pub static mut PyExc_IOError: *mut PyObject;
    #[cfg(windows)] pub static mut PyExc_WindowsError: *mut PyObject;
    #[cfg(not(Py_3_7))] pub static mut PyExc_RecursionErrorInst: *mut PyObject;

    // Predefined warning categories
    pub static mut PyExc_Warning: *mut PyObject;
    pub static mut PyExc_UserWarning: *mut PyObject;
    pub static mut PyExc_DeprecationWarning: *mut PyObject;
    pub static mut PyExc_PendingDeprecationWarning: *mut PyObject;
    pub static mut PyExc_SyntaxWarning: *mut PyObject;
    pub static mut PyExc_RuntimeWarning: *mut PyObject;
    pub static mut PyExc_FutureWarning: *mut PyObject;
    pub static mut PyExc_ImportWarning: *mut PyObject;
    pub static mut PyExc_UnicodeWarning: *mut PyObject;
    pub static mut PyExc_BytesWarning: *mut PyObject;
    pub static mut PyExc_ResourceWarning: *mut PyObject;

    pub fn PyErr_BadArgument() -> c_int;
    pub fn PyErr_NoMemory() -> *mut PyObject;
    pub fn PyErr_SetFromErrno(arg1: *mut PyObject) -> *mut PyObject;
    pub fn PyErr_SetFromErrnoWithFilenameObject(arg1: *mut PyObject,
                                                arg2: *mut PyObject)
     -> *mut PyObject;
    #[cfg(Py_3_4)]
    pub fn PyErr_SetFromErrnoWithFilenameObjects(arg1: *mut PyObject,
                                                 arg2: *mut PyObject,
                                                 arg3: *mut PyObject)
     -> *mut PyObject;
    pub fn PyErr_SetFromErrnoWithFilename(exc: *mut PyObject,
                                          filename: *const c_char)
     -> *mut PyObject;
    pub fn PyErr_Format(exception: *mut PyObject,
                        format: *const c_char, ...) -> *mut PyObject;
    #[cfg(Py_3_6)]
    pub fn PyErr_SetImportErrorSubclass(
        arg1: *mut PyObject, arg2: *mut PyObject,
        arg3: *mut PyObject, arg4: *mut PyObject) -> *mut PyObject;
    pub fn PyErr_SetImportError(arg1: *mut PyObject, arg2: *mut PyObject,
                                arg3: *mut PyObject) -> *mut PyObject;
    pub fn PyErr_BadInternalCall() -> ();
    pub fn _PyErr_BadInternalCall(filename: *const c_char,
                                  lineno: c_int) -> ();
    pub fn PyErr_NewException(name: *const c_char,
                              base: *mut PyObject, dict: *mut PyObject)
     -> *mut PyObject;
    pub fn PyErr_NewExceptionWithDoc(name: *const c_char,
                                     doc: *const c_char,
                                     base: *mut PyObject, dict: *mut PyObject)
     -> *mut PyObject;
    pub fn PyErr_WriteUnraisable(arg1: *mut PyObject) -> ();
    pub fn PyErr_CheckSignals() -> c_int;
    pub fn PyErr_SetInterrupt() -> ();
    pub fn PyErr_SyntaxLocation(filename: *const c_char,
                                lineno: c_int) -> ();
    pub fn PyErr_SyntaxLocationEx(filename: *const c_char,
                                  lineno: c_int,
                                  col_offset: c_int) -> ();
    pub fn PyErr_ProgramText(filename: *const c_char,
                             lineno: c_int) -> *mut PyObject;
    pub fn PyUnicodeDecodeError_Create(encoding: *const c_char,
                                       object: *const c_char,
                                       length: Py_ssize_t, start: Py_ssize_t,
                                       end: Py_ssize_t,
                                       reason: *const c_char)
     -> *mut PyObject;
    pub fn PyUnicodeEncodeError_GetEncoding(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeDecodeError_GetEncoding(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeEncodeError_GetObject(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeDecodeError_GetObject(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeTranslateError_GetObject(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeEncodeError_GetStart(arg1: *mut PyObject,
                                         arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeDecodeError_GetStart(arg1: *mut PyObject,
                                         arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeTranslateError_GetStart(arg1: *mut PyObject,
                                            arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeEncodeError_SetStart(arg1: *mut PyObject,
                                         arg2: Py_ssize_t) -> c_int;
    pub fn PyUnicodeDecodeError_SetStart(arg1: *mut PyObject,
                                         arg2: Py_ssize_t) -> c_int;
    pub fn PyUnicodeTranslateError_SetStart(arg1: *mut PyObject,
                                            arg2: Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeEncodeError_GetEnd(arg1: *mut PyObject,
                                       arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeDecodeError_GetEnd(arg1: *mut PyObject,
                                       arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeTranslateError_GetEnd(arg1: *mut PyObject,
                                          arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeEncodeError_SetEnd(arg1: *mut PyObject, arg2: Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeDecodeError_SetEnd(arg1: *mut PyObject, arg2: Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeTranslateError_SetEnd(arg1: *mut PyObject,
                                          arg2: Py_ssize_t) -> c_int;
    pub fn PyUnicodeEncodeError_GetReason(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeDecodeError_GetReason(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeTranslateError_GetReason(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeEncodeError_SetReason(exc: *mut PyObject,
                                          reason: *const c_char)
     -> c_int;
    pub fn PyUnicodeDecodeError_SetReason(exc: *mut PyObject,
                                          reason: *const c_char)
     -> c_int;
    pub fn PyUnicodeTranslateError_SetReason(exc: *mut PyObject,
                                             reason: *const c_char)
     -> c_int;
}

<commit_msg>python3-sys: remove PyExc_RecursionErrorInst for Python >= 3.6<commit_after>use libc::{c_char, c_int};
use pyport::Py_ssize_t;
use object::*;

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub fn PyErr_SetNone(arg1: *mut PyObject) -> ();
    pub fn PyErr_SetObject(arg1: *mut PyObject, arg2: *mut PyObject) -> ();
    pub fn PyErr_SetString(exception: *mut PyObject,
                           string: *const c_char) -> ();
    pub fn PyErr_Occurred() -> *mut PyObject;
    pub fn PyErr_Clear() -> ();
    pub fn PyErr_Fetch(arg1: *mut *mut PyObject, arg2: *mut *mut PyObject,
                       arg3: *mut *mut PyObject) -> ();
    pub fn PyErr_Restore(arg1: *mut PyObject, arg2: *mut PyObject,
                         arg3: *mut PyObject) -> ();
    pub fn PyErr_GetExcInfo(arg1: *mut *mut PyObject,
                            arg2: *mut *mut PyObject,
                            arg3: *mut *mut PyObject) -> ();
    pub fn PyErr_SetExcInfo(arg1: *mut PyObject, arg2: *mut PyObject,
                            arg3: *mut PyObject) -> ();
    pub fn Py_FatalError(message: *const c_char) -> !;
    pub fn PyErr_GivenExceptionMatches(arg1: *mut PyObject,
                                       arg2: *mut PyObject) -> c_int;
    pub fn PyErr_ExceptionMatches(arg1: *mut PyObject) -> c_int;
    pub fn PyErr_NormalizeException(arg1: *mut *mut PyObject,
                                    arg2: *mut *mut PyObject,
                                    arg3: *mut *mut PyObject) -> ();
    pub fn PyException_SetTraceback(arg1: *mut PyObject, arg2: *mut PyObject)
     -> c_int;
    pub fn PyException_GetTraceback(arg1: *mut PyObject) -> *mut PyObject;
    pub fn PyException_GetCause(arg1: *mut PyObject) -> *mut PyObject;
    pub fn PyException_SetCause(arg1: *mut PyObject, arg2: *mut PyObject)
     -> ();
    pub fn PyException_GetContext(arg1: *mut PyObject) -> *mut PyObject;
    pub fn PyException_SetContext(arg1: *mut PyObject, arg2: *mut PyObject)
     -> ();
}

#[inline]
pub unsafe fn PyExceptionClass_Check(x: *mut PyObject) -> c_int {
    (PyType_Check(x) != 0 &&
     PyType_FastSubclass(x as *mut PyTypeObject, Py_TPFLAGS_BASE_EXC_SUBCLASS) != 0) as c_int
}

#[inline]
pub unsafe fn PyExceptionInstance_Check(x: *mut PyObject) -> c_int {
    PyType_FastSubclass((*x).ob_type, Py_TPFLAGS_BASE_EXC_SUBCLASS)
}

#[inline]
pub unsafe fn PyExceptionInstance_Class(x: *mut PyObject) -> *mut PyObject {
    (*x).ob_type as *mut PyObject
}

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub static mut PyExc_BaseException: *mut PyObject;
    pub static mut PyExc_Exception: *mut PyObject;
    #[cfg(Py_3_5)] pub static mut PyExc_StopAsyncIteration: *mut PyObject;
    pub static mut PyExc_StopIteration: *mut PyObject;
    pub static mut PyExc_GeneratorExit: *mut PyObject;
    pub static mut PyExc_ArithmeticError: *mut PyObject;
    pub static mut PyExc_LookupError: *mut PyObject;
    pub static mut PyExc_AssertionError: *mut PyObject;
    pub static mut PyExc_AttributeError: *mut PyObject;
    pub static mut PyExc_BufferError: *mut PyObject;
    pub static mut PyExc_EOFError: *mut PyObject;
    pub static mut PyExc_FloatingPointError: *mut PyObject;
    pub static mut PyExc_OSError: *mut PyObject;
    pub static mut PyExc_ImportError: *mut PyObject;
    #[cfg(Py_3_6)] pub static mut PyExc_ModuleNotFoundError: *mut PyObject;
    pub static mut PyExc_IndexError: *mut PyObject;
    pub static mut PyExc_KeyError: *mut PyObject;
    pub static mut PyExc_KeyboardInterrupt: *mut PyObject;
    pub static mut PyExc_MemoryError: *mut PyObject;
    pub static mut PyExc_NameError: *mut PyObject;
    pub static mut PyExc_OverflowError: *mut PyObject;
    pub static mut PyExc_RuntimeError: *mut PyObject;
    #[cfg(Py_3_5)] pub static mut PyExc_RecursionError: *mut PyObject;
    pub static mut PyExc_NotImplementedError: *mut PyObject;
    pub static mut PyExc_SyntaxError: *mut PyObject;
    pub static mut PyExc_IndentationError: *mut PyObject;
    pub static mut PyExc_TabError: *mut PyObject;
    pub static mut PyExc_ReferenceError: *mut PyObject;
    pub static mut PyExc_SystemError: *mut PyObject;
    pub static mut PyExc_SystemExit: *mut PyObject;
    pub static mut PyExc_TypeError: *mut PyObject;
    pub static mut PyExc_UnboundLocalError: *mut PyObject;
    pub static mut PyExc_UnicodeError: *mut PyObject;
    pub static mut PyExc_UnicodeEncodeError: *mut PyObject;
    pub static mut PyExc_UnicodeDecodeError: *mut PyObject;
    pub static mut PyExc_UnicodeTranslateError: *mut PyObject;
    pub static mut PyExc_ValueError: *mut PyObject;
    pub static mut PyExc_ZeroDivisionError: *mut PyObject;
    pub static mut PyExc_BlockingIOError: *mut PyObject;
    pub static mut PyExc_BrokenPipeError: *mut PyObject;
    pub static mut PyExc_ChildProcessError: *mut PyObject;
    pub static mut PyExc_ConnectionError: *mut PyObject;
    pub static mut PyExc_ConnectionAbortedError: *mut PyObject;
    pub static mut PyExc_ConnectionRefusedError: *mut PyObject;
    pub static mut PyExc_ConnectionResetError: *mut PyObject;
    pub static mut PyExc_FileExistsError: *mut PyObject;
    pub static mut PyExc_FileNotFoundError: *mut PyObject;
    pub static mut PyExc_InterruptedError: *mut PyObject;
    pub static mut PyExc_IsADirectoryError: *mut PyObject;
    pub static mut PyExc_NotADirectoryError: *mut PyObject;
    pub static mut PyExc_PermissionError: *mut PyObject;
    pub static mut PyExc_ProcessLookupError: *mut PyObject;

    // Compatibility aliases
    pub static mut PyExc_TimeoutError: *mut PyObject;
    pub static mut PyExc_EnvironmentError: *mut PyObject;
    pub static mut PyExc_IOError: *mut PyObject;
    #[cfg(windows)] pub static mut PyExc_WindowsError: *mut PyObject;
    #[cfg(not(Py_3_6))] pub static mut PyExc_RecursionErrorInst: *mut PyObject;

    // Predefined warning categories
    pub static mut PyExc_Warning: *mut PyObject;
    pub static mut PyExc_UserWarning: *mut PyObject;
    pub static mut PyExc_DeprecationWarning: *mut PyObject;
    pub static mut PyExc_PendingDeprecationWarning: *mut PyObject;
    pub static mut PyExc_SyntaxWarning: *mut PyObject;
    pub static mut PyExc_RuntimeWarning: *mut PyObject;
    pub static mut PyExc_FutureWarning: *mut PyObject;
    pub static mut PyExc_ImportWarning: *mut PyObject;
    pub static mut PyExc_UnicodeWarning: *mut PyObject;
    pub static mut PyExc_BytesWarning: *mut PyObject;
    pub static mut PyExc_ResourceWarning: *mut PyObject;

    pub fn PyErr_BadArgument() -> c_int;
    pub fn PyErr_NoMemory() -> *mut PyObject;
    pub fn PyErr_SetFromErrno(arg1: *mut PyObject) -> *mut PyObject;
    pub fn PyErr_SetFromErrnoWithFilenameObject(arg1: *mut PyObject,
                                                arg2: *mut PyObject)
     -> *mut PyObject;
    #[cfg(Py_3_4)]
    pub fn PyErr_SetFromErrnoWithFilenameObjects(arg1: *mut PyObject,
                                                 arg2: *mut PyObject,
                                                 arg3: *mut PyObject)
     -> *mut PyObject;
    pub fn PyErr_SetFromErrnoWithFilename(exc: *mut PyObject,
                                          filename: *const c_char)
     -> *mut PyObject;
    pub fn PyErr_Format(exception: *mut PyObject,
                        format: *const c_char, ...) -> *mut PyObject;
    #[cfg(Py_3_6)]
    pub fn PyErr_SetImportErrorSubclass(
        arg1: *mut PyObject, arg2: *mut PyObject,
        arg3: *mut PyObject, arg4: *mut PyObject) -> *mut PyObject;
    pub fn PyErr_SetImportError(arg1: *mut PyObject, arg2: *mut PyObject,
                                arg3: *mut PyObject) -> *mut PyObject;
    pub fn PyErr_BadInternalCall() -> ();
    pub fn _PyErr_BadInternalCall(filename: *const c_char,
                                  lineno: c_int) -> ();
    pub fn PyErr_NewException(name: *const c_char,
                              base: *mut PyObject, dict: *mut PyObject)
     -> *mut PyObject;
    pub fn PyErr_NewExceptionWithDoc(name: *const c_char,
                                     doc: *const c_char,
                                     base: *mut PyObject, dict: *mut PyObject)
     -> *mut PyObject;
    pub fn PyErr_WriteUnraisable(arg1: *mut PyObject) -> ();
    pub fn PyErr_CheckSignals() -> c_int;
    pub fn PyErr_SetInterrupt() -> ();
    pub fn PyErr_SyntaxLocation(filename: *const c_char,
                                lineno: c_int) -> ();
    pub fn PyErr_SyntaxLocationEx(filename: *const c_char,
                                  lineno: c_int,
                                  col_offset: c_int) -> ();
    pub fn PyErr_ProgramText(filename: *const c_char,
                             lineno: c_int) -> *mut PyObject;
    pub fn PyUnicodeDecodeError_Create(encoding: *const c_char,
                                       object: *const c_char,
                                       length: Py_ssize_t, start: Py_ssize_t,
                                       end: Py_ssize_t,
                                       reason: *const c_char)
     -> *mut PyObject;
    pub fn PyUnicodeEncodeError_GetEncoding(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeDecodeError_GetEncoding(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeEncodeError_GetObject(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeDecodeError_GetObject(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeTranslateError_GetObject(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeEncodeError_GetStart(arg1: *mut PyObject,
                                         arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeDecodeError_GetStart(arg1: *mut PyObject,
                                         arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeTranslateError_GetStart(arg1: *mut PyObject,
                                            arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeEncodeError_SetStart(arg1: *mut PyObject,
                                         arg2: Py_ssize_t) -> c_int;
    pub fn PyUnicodeDecodeError_SetStart(arg1: *mut PyObject,
                                         arg2: Py_ssize_t) -> c_int;
    pub fn PyUnicodeTranslateError_SetStart(arg1: *mut PyObject,
                                            arg2: Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeEncodeError_GetEnd(arg1: *mut PyObject,
                                       arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeDecodeError_GetEnd(arg1: *mut PyObject,
                                       arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeTranslateError_GetEnd(arg1: *mut PyObject,
                                          arg2: *mut Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeEncodeError_SetEnd(arg1: *mut PyObject, arg2: Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeDecodeError_SetEnd(arg1: *mut PyObject, arg2: Py_ssize_t)
     -> c_int;
    pub fn PyUnicodeTranslateError_SetEnd(arg1: *mut PyObject,
                                          arg2: Py_ssize_t) -> c_int;
    pub fn PyUnicodeEncodeError_GetReason(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeDecodeError_GetReason(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeTranslateError_GetReason(arg1: *mut PyObject)
     -> *mut PyObject;
    pub fn PyUnicodeEncodeError_SetReason(exc: *mut PyObject,
                                          reason: *const c_char)
     -> c_int;
    pub fn PyUnicodeDecodeError_SetReason(exc: *mut PyObject,
                                          reason: *const c_char)
     -> c_int;
    pub fn PyUnicodeTranslateError_SetReason(exc: *mut PyObject,
                                             reason: *const c_char)
     -> c_int;
}

<|endoftext|>


<commit_before>use netsurfcss::stylesheet::CssStylesheet;
use netsurfcss::select::{CssSelectCtx, css_select_ctx_create, CssSelectResults, CssSelectHandler, CssPseudoElementNone};
use netsurfcss::types::{CssQName, CssColor};
use netsurfcss::properties::CssProperty;
use netsurfcss::values::{CssColorInherit, CssColorColor};
use netsurfcss::ll::types::{CSS_ORIGIN_AUTHOR, CSS_MEDIA_SCREEN};
use netsurfcss::hint::{CssHint, CssHintDefault};
use netsurfcss::computed::CssComputedStyle;
use wapcaplet::from_rust_string;
use util::DataStream;
use std::net::url::Url;
use values::{CSSValue, Inherit, Specified};
use color::{Color, rgba};

pub struct Stylesheet {
    inner: CssStylesheet
}

impl Stylesheet {
    static fn new(url: Url, input: DataStream) -> Stylesheet {
        Stylesheet {
            inner: parser::parse_stylesheet(move url, input)
        }
    }
}

pub struct SelectCtx {
    inner: CssSelectCtx
}

impl SelectCtx {
    static fn new() -> SelectCtx {
        SelectCtx {
            inner: css_select_ctx_create()
        }
    }

    fn append_sheet(&mut self, sheet: Stylesheet) {
        let sheet = match move sheet {
            Stylesheet { inner: move inner } => move inner
        };

        self.inner.append_sheet(move sheet, CSS_ORIGIN_AUTHOR, CSS_MEDIA_SCREEN)
    }

    fn select_style<N, H: SelectHandler<N>>(&self, node: &N, handler: &H) -> SelectResults {
        let inner_handler = InnerHandler {
            inner: ptr::to_unsafe_ptr(handler)
        };
        SelectResults {
            inner: self.inner.select_style::<N, InnerHandler<N, H>>(node, CSS_MEDIA_SCREEN, None, &inner_handler)
        }
    }
}

pub struct SelectResults {
    inner: CssSelectResults
}

impl SelectResults {
    fn computed_style(&self) -> ComputedStyle/&self {
        ComputedStyle {
            inner: self.inner.computed_style(CssPseudoElementNone)
        }
    }
}

pub trait SelectHandler<N> {
    fn node_name(node: &N) -> ~str;
}

struct InnerHandler<N, H: SelectHandler<N>> {
    // FIXME: Can't encode region variables
    inner: *H
}

priv impl<N, H: SelectHandler<N>> InnerHandler<N, H> {
    priv fn inner_ref() -> &self/H {
        unsafe { &*self.inner }
    }
}

impl<N, H: SelectHandler<N>> InnerHandler<N, H>: CssSelectHandler<N> {
    fn node_name(node: &N) -> CssQName {
        CssQName {
            ns: None,
            name: from_rust_string(self.inner_ref().node_name(node))
        }
    }
    fn ua_default_for_property(property: CssProperty) -> CssHint {
        warn!("not specifiying ua default for property %?", property);
        CssHintDefault
    }
}

pub struct ComputedStyle {
    inner: CssComputedStyle
}

impl ComputedStyle {
    pub fn background_color() -> CSSValue<Color> {
        match self.inner.background_color() {
            CssColorInherit => Inherit,
            CssColorColor(v) => Specified(netcolor_to_color(v))
        }
    }
}

fn netcolor_to_color(color: CssColor) -> Color {
    rgba(color.r, color.g, color.b, (color.a as float) / 255.0)
}<commit_msg>Add border width getters<commit_after>use netsurfcss::stylesheet::CssStylesheet;
use netsurfcss::select::{CssSelectCtx, css_select_ctx_create, CssSelectResults, CssSelectHandler, CssPseudoElementNone};
use netsurfcss::types::{CssQName, CssColor};
use netsurfcss::types::{CssUnit, CssUnitPx, CssUnitEm};
use netsurfcss::properties::CssProperty;
use netsurfcss::values::{CssColorValue, CssColorInherit, CssColorColor};
use netsurfcss::values::{CssBorderWidthValue, CssBorderWidthInherit, CssBorderWidthWidth};
use netsurfcss::ll::types::{CSS_ORIGIN_AUTHOR, CSS_MEDIA_SCREEN};
use netsurfcss::hint::{CssHint, CssHintDefault};
use netsurfcss::computed::CssComputedStyle;
use netsurfcss::util::css_fixed_to_float;
use wapcaplet::from_rust_string;
use util::DataStream;
use std::net::url::Url;
use values::{CSSValue, Inherit, Specified, Length, Em, Px};
use color::{Color, rgba};

pub struct Stylesheet {
    inner: CssStylesheet
}

impl Stylesheet {
    static fn new(url: Url, input: DataStream) -> Stylesheet {
        Stylesheet {
            inner: parser::parse_stylesheet(move url, input)
        }
    }
}

pub struct SelectCtx {
    inner: CssSelectCtx
}

impl SelectCtx {
    static fn new() -> SelectCtx {
        SelectCtx {
            inner: css_select_ctx_create()
        }
    }

    fn append_sheet(&mut self, sheet: Stylesheet) {
        let sheet = match move sheet {
            Stylesheet { inner: move inner } => move inner
        };

        self.inner.append_sheet(move sheet, CSS_ORIGIN_AUTHOR, CSS_MEDIA_SCREEN)
    }

    fn select_style<N, H: SelectHandler<N>>(&self, node: &N, handler: &H) -> SelectResults {
        let inner_handler = InnerHandler {
            inner: ptr::to_unsafe_ptr(handler)
        };
        SelectResults {
            inner: self.inner.select_style::<N, InnerHandler<N, H>>(node, CSS_MEDIA_SCREEN, None, &inner_handler)
        }
    }
}

pub struct SelectResults {
    inner: CssSelectResults
}

impl SelectResults {
    fn computed_style(&self) -> ComputedStyle/&self {
        ComputedStyle {
            inner: self.inner.computed_style(CssPseudoElementNone)
        }
    }
}

pub trait SelectHandler<N> {
    fn node_name(node: &N) -> ~str;
}

struct InnerHandler<N, H: SelectHandler<N>> {
    // FIXME: Can't encode region variables
    inner: *H
}

priv impl<N, H: SelectHandler<N>> InnerHandler<N, H> {
    priv fn inner_ref() -> &self/H {
        unsafe { &*self.inner }
    }
}

impl<N, H: SelectHandler<N>> InnerHandler<N, H>: CssSelectHandler<N> {
    fn node_name(node: &N) -> CssQName {
        CssQName {
            ns: None,
            name: from_rust_string(self.inner_ref().node_name(node))
        }
    }
    fn ua_default_for_property(property: CssProperty) -> CssHint {
        warn!("not specifiying ua default for property %?", property);
        CssHintDefault
    }
}

pub struct ComputedStyle {
    inner: CssComputedStyle
}

impl ComputedStyle {
    pub fn background_color() -> CSSValue<Color> {
        convert_net_color_value(self.inner.background_color())
    }

    pub fn border_top_width() -> CSSValue<Length> {
        convert_net_border_width(self.inner.border_top_width())
    }

    pub fn border_right_width() -> CSSValue<Length> {
        convert_net_border_width(self.inner.border_right_width())
    }

    pub fn border_bottom_width() -> CSSValue<Length> {
        convert_net_border_width(self.inner.border_bottom_width())
    }

    pub fn border_left_width() -> CSSValue<Length> {
        convert_net_border_width(self.inner.border_left_width())
    }
}

fn convert_net_color(color: CssColor) -> Color {
    rgba(color.r, color.g, color.b, (color.a as float) / 255.0)
}

fn convert_net_color_value(color: CssColorValue) -> CSSValue<Color> {
    match color {
        CssColorInherit => Inherit,
        CssColorColor(v) => Specified(convert_net_color(v))
    }
}

fn convert_net_border_width(width: CssBorderWidthValue) -> CSSValue<Length> {
    match width {
        CssBorderWidthInherit => Inherit,
        CssBorderWidthWidth(width) => Specified(convert_net_unit_to_length(width)),
        _ => unimpl("border width")
    }
}

fn convert_net_unit_to_length(unit: CssUnit) -> Length {
    match unit {
        CssUnitPx(l) => Px(css_fixed_to_float(l)),
        CssUnitEm(l) => Em(css_fixed_to_float(l)),
        _ => unimpl("unit")
    }
}

fn unimpl(what: &str) -> ! {
    fail fmt!("css unimplemented %?", what)
}
<|endoftext|>


<commit_before>struct Point {
    x: int,
    y: int,
}

impl Point : ops::Add<int,int> {
    pure fn add(&self, z: &int) -> int {
        self.x + self.y + (*z)
    }
}

impl Point {
    fn times(z: int) -> int {
        self.x * self.y * z
    }
}

fn a() {
    let mut p = Point {x: 3, y: 4};

    // ok (we can loan out rcvr)
    p + 3;
    p.times(3);
}

fn b() {
    let mut p = Point {x: 3, y: 4};

    // Here I create an outstanding loan and check that we get conflicts:

    let q = &mut p; //~ NOTE prior loan as mutable granted here

    p + 3; //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
    p.times(3); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan

    q.x += 1;
}

fn c() {
    // Here the receiver is in aliased memory and hence we cannot
    // consider it immutable:
    let q = @mut Point {x: 3, y: 4};

    // ...this is ok for pure fns
    *q + 3;

    // ...but not impure fns
    (*q).times(3); //~ ERROR illegal borrow unless pure
    //~^ NOTE impure due to access to impure function
}

fn main() {
}

<commit_msg>test: Fix busted test. rs=bustage<commit_after>struct Point {
    x: int,
    y: int,
}

impl Point : ops::Add<int,int> {
    pure fn add(&self, z: &int) -> int {
        self.x + self.y + (*z)
    }
}

impl Point {
    fn times(z: int) -> int {
        self.x * self.y * z
    }
}

fn a() {
    let mut p = Point {x: 3, y: 4};

    // ok (we can loan out rcvr)
    p + 3;
    p.times(3);
}

fn b() {
    let mut p = Point {x: 3, y: 4};

    // Here I create an outstanding loan and check that we get conflicts:

    let q = &mut p; //~ NOTE prior loan as mutable granted here

    p + 3;  // ok for pure fns
    p.times(3); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan

    q.x += 1;
}

fn c() {
    // Here the receiver is in aliased memory and hence we cannot
    // consider it immutable:
    let q = @mut Point {x: 3, y: 4};

    // ...this is ok for pure fns
    *q + 3;

    // ...but not impure fns
    (*q).times(3); //~ ERROR illegal borrow unless pure
    //~^ NOTE impure due to access to impure function
}

fn main() {
}

<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/24_game
// with a recursive descent parser for a simple calculator (+ - * /)
// using the shunting yard algorithm as explained on
// http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
// It follows operator precedence (i.e. 2 + 3 * 3 = 11),
// understands negation (-5 + 6 = 1), ignores whitespace
// and allows the use of parentheses

#![feature(macro_rules)]
use std::char;

#[cfg(not(test))]
use std::rand;

#[cfg(not(test))]
fn main() {
    let mut rng=rand::task_rng();
    let mut input = std::io::stdin();

    loop {
        let mut sample = rand::sample(&mut rng, range(1u, 10), 4);

        println!("make 24 by combining the following 4 numbers with + - * / or (q)uit");
        println!("{}", sample);

        let line = input.read_line().unwrap();
        match line.as_slice().trim() {
            "q" => break,
            input => if check_values(sample.as_mut_slice(), input) {
                    let mut p = Parser :: new(input);
                    match p.parse() {
                        Ok(i) if i==24f32 => println!("you made it!"),
                        Ok(i) => println!("you entered {}, try again!", i),
                        Err(s)  => println!("{}",s)
                    };
                } else { println!("unrecognized input, try again")}
        }
    }
}

// Returns true if the entered expression uses the values contained in sample
fn check_values(sample:&mut [uint], input:&str) -> bool {
    let lex=Lexer::new(input);

    let mut numbers_used : Vec<uint> = lex.filter_map(|a| match a {
                                                        Int(i)  => Some(i),
                                                        _       => None
                                                    }).collect();
    numbers_used.sort();
    sample.sort();
    numbers_used.as_slice() == sample
}

// the tokens that our parser is going to recognize
#[deriving(PartialEq,Eq,Show)]
enum Token {LParen, RParen, Plus, Minus, Slash, Star, Int(uint)}

impl Token {
   // are tokens associated to a binary operation?
   fn is_binary(&self) -> bool {
        match self {
            &Plus | &Minus | &Slash | &Star => true,
            _ => false
        }
    }
}

#[inline]
// map a character to its corresponding token
fn single_char_to_token(ch: char) -> Option<Token> {
    match ch {
            '(' => Some(LParen),
            ')' => Some(RParen),
            '+' => Some(Plus),
            '-' => Some(Minus),
            '/' => Some(Slash),
            '*' => Some(Star),
            _    => None
    }
}

// Lexer reads an expression like (a + b) / c * d
// as an iterator on the tokens that compose it
// Int(a), LParen, Plus, Int(b), RParen...
struct Lexer<'a> {
    input_str:  &'a str,
    offset:     uint
}

impl <'a> Lexer<'a> {
    fn new(input_str: &str) -> Lexer {
        Lexer { input_str: input_str, offset: 0u }
    }

    fn expect(&mut self, expected:&[Token]) -> Result<Token, String> {
        let n = self.offset;
        match self.next() {
            Some(a) if expected.contains(&a)  => Ok(a),
            other  => Err(format!("Parsing error: {} was unexpected at offset {}", other, n))
        }
    }
}

impl <'a> Iterator<Token> for Lexer<'a> {
    fn next(&mut self) -> Option<Token> {
        // slice the original string starting from the current offset
        let remaining_input=self.input_str.slice_from(self.offset);

        // keep track of the offset while advancing chars, with enumerate()
        let ch_iter=remaining_input.chars().enumerate();

        // advance to the next non-whitespace character
        let mut trimmed=ch_iter.skip_while(|&(_, ch)| ch.is_whitespace());

        let (tok, cur_offset) = match trimmed.next() {
            // found digit, check if there are others
            // and transform them to a uint
            Some((o, d)) if d.is_digit() => {
                let (mut val, mut offset)=(char::to_digit(d, 10).unwrap(), o);
                for (idx, ch) in trimmed {
                    if ch.is_digit() {val=val*10 + char::to_digit(ch, 10).unwrap();}
                    else {
                        offset=idx;
                        break;
                    }
                }
                (Some(Int(val)), offset)
            },
            // found non-digit, try transforming it to the corresponding token
            Some((o, t)) => (single_char_to_token(t), o+1),
            _   => (None, 0u)
        };
        // update the offset for the next iteration
        self.offset += cur_offset;
        tok
    }
}

// operators are a "higher level" concept than tokens
// as they define the semantics of the expression language
// e.g. token "Minus" can correspond to the unary Neg Operator (-a)
// or to the binary Sub operator (a - b)
#[deriving(PartialEq, Eq)]
enum Operator {Neg, Add, Sub, Mul, Div, Sentinel}

#[deriving(PartialEq, Eq)]
struct OperatorPrecedence(Operator);

impl Operator {
     fn precedence(self) -> OperatorPrecedence  {
        OperatorPrecedence(self)
    }
}

impl OperatorPrecedence {
    fn prec(self) -> uint {
        match self {
            OperatorPrecedence(Sentinel) => 0u,
            OperatorPrecedence(Add) | OperatorPrecedence(Sub) => 1u,
            OperatorPrecedence(Neg)        => 2u,
            OperatorPrecedence(Mul) | OperatorPrecedence(Div) => 3u
        }
    }
}

/*
    Operator precedence for binary operators:
    * if x has higher precedence than y precedence for x > precedence for y
    * if x and y have equal precedence the first one has precedence
    e.g. in expression (4 / 2 * 2) operators * and / have the same precedence,
    but the operations must be performed in the order they appear
    (division first, multiplication second) otherwise results are different
*/
impl PartialOrd for OperatorPrecedence {
    fn partial_cmp(&self, other: &OperatorPrecedence) -> Option<Ordering> {
        let lower = match (self, other) {
            (&OperatorPrecedence(Mul), &OperatorPrecedence(Div)) => false,
            (&OperatorPrecedence(Div), &OperatorPrecedence(Mul)) => false,
            (&OperatorPrecedence(Add), &OperatorPrecedence(Sub)) => false,
            (&OperatorPrecedence(Sub), &OperatorPrecedence(Add)) => false,
            _  => self.prec() < other.prec()
        };

        if lower {
            Some(Less)
        } else if self == other {
            Some(Equal)
        } else {
            Some(Greater)
        }
    }
}

// recursive descent parser
// with the shunting yard algorithm as explained on
// http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
// I followed the names of the methods as closely as possible vs the pseudo-code
// that illustrates the algorithm
struct Parser<'a> {
    operators: Vec<Operator>,
    operands:  Vec<f32>,
    lexer:     Lexer<'a>
}

impl <'a> Parser<'a> {
    fn new(input_str: &str) -> Parser {
        Parser {
            operators:  Vec::new(),
            operands:   Vec::new(),
            lexer:      Lexer::new(input_str)
        }
    }

    fn parse(&mut self) -> Result<f32, String> {
        self.operators.push(Sentinel);
        try!(self.e());
        let res=self.operands.last();
        let ret=match res {
            Some(&r) => Ok(r),
            None    => Err("something went wrong, got no result".to_string())
        };
        ret
    }

    fn e(&mut self) -> Result<(), String> {
        try!(self.p());

        let mut n=self.lexer.next();

        while n.is_some() && n.unwrap().is_binary() {
            match n {
                Some(Plus) => self.push_operator(Add),
                Some(Minus) => self.push_operator(Sub),
                Some(Star) => self.push_operator(Mul),
                Some(Slash) => self.push_operator(Div),
                _ => unreachable!() //shouldn't get there (there are no other
                                    // binary operators
            };
            try!(self.p());

            let mut n_peek = self.lexer.peekable();
            if n_peek.peek().is_none() || !n_peek.peek().unwrap().is_binary() { break; }
            n = self.lexer.next();
        }
        while self.operators.last().is_some() && self.operators.last().unwrap() != &Sentinel {
            self.pop_operator();
        }
        Ok(())
    }

    fn p(&mut self) -> Result<(), String> {
        match self.lexer.next() {
            Some(Int(n)) => {
                self.operands.push(n as f32);
            },
            Some(LParen) => {
                self.operators.push(Sentinel);
                try!(self.e());
                try!(self.lexer.expect(&[RParen]));
                self.operators.pop();
            },
            Some(Minus) => {
                self.push_operator(Neg);
                try!(self.p());
            },
            Some(e) => return Err(format!("unexpected token {}", e)),
            _       => return Err("unexpected end of command".to_string())
            //Some(Minus) =>
        }
        Ok(())
    }

    fn pop_operator(&mut self) {
        match self.operators.pop() {
            Some(Add) => self.binary_op(|t1,t2| t1+t2),
            Some(Sub) => self.binary_op(|t1,t2| t1-t2),
            Some(Mul) => self.binary_op(|t1,t2| t1*t2),
            Some(Div) => self.binary_op(|t1,t2| t1/t2),
            Some(Neg) => self.unary_op(|t1| -t1),
            _         => unreachable!()
            }
    }

    fn push_operator(&mut self, op:Operator) {
        match self.operators.last() {
            Some(&a) if a.precedence() > op.precedence() => self.pop_operator(),
            _ => ()
        }
        self.operators.push(op);
    }

    #[inline]
    fn binary_op(&mut self, op:|f32,f32| -> f32) {
        let t1=self.operands.pop().unwrap();
        let t2=self.operands.pop().unwrap();
        self.operands.push(op(t2,t1));
    }

    #[inline]
    fn unary_op(&mut self, op:|f32| -> f32) {
        let t1=self.operands.pop().unwrap();
        self.operands.push(op(t1));
    }
}

#[test]
fn test_precedence() {
    assert!(Mul.precedence() > Add.precedence());
    assert!(Mul.precedence() > Div.precedence());
    assert!((Mul.precedence() < Div.precedence())==false);
}

#[test]
fn lexer_iter() {
    // test read token and character's offset in the iterator
    macro_rules! test_tok( ($tok:ident, $exp_tok:expr, $exp_pos:expr) =>
        ( assert_eq!(($tok.next(), $tok.offset), (Some($exp_tok), $exp_pos));))

    let mut tok=Lexer::new("  15 + 4");
    test_tok!(tok, Int(15), 4);
    test_tok!(tok, Plus, 6);
    let read=tok.expect(&[LParen,Int(4),RParen]);
    assert_eq!(read, Ok(Int(4)));

    let mut tok=Lexer::new("");
    assert_eq!(tok.next(), None);

    let mut tok=Lexer::new("     ");
    assert_eq!(tok.next(), None);

    let mut tok=Lexer::new("2 * (3+4/2)");
    test_tok!(tok, Int(2), 1);
    test_tok!(tok, Star, 3);
    test_tok!(tok, LParen, 5);
    test_tok!(tok, Int(3), 6);
    test_tok!(tok, Plus, 7);
    test_tok!(tok, Int(4), 8);
    test_tok!(tok, Slash, 9);
    test_tok!(tok, Int(2), 10);
    test_tok!(tok, RParen, 11);
}

#[test]
fn parse()
{
    assert_eq!(Parser::new("2+2").parse(), Ok(4.));
    assert_eq!(Parser::new("2+3*4").parse(), Ok(14.));
    assert_eq!(Parser::new("4*(3+2)").parse(), Ok(20.));
    assert_eq!(Parser::new("5/(3+2)*3").parse(), Ok(3.));
    assert_eq!(Parser::new("2++12").parse(), Err("unexpected token Plus".to_string()));
    assert_eq!(Parser::new("-2+12").parse(), Ok(10.));
    assert_eq!(Parser::new("-2*(2+3)").parse(), Ok(-10.));
}

#[test]
fn try_check_values() {
    let m = &mut [1,2,3,4];
    assert!(check_values(m, "1+3 -(4/2)"));
}
<commit_msg>24 game: Simplify/style<commit_after>// Implements http://rosettacode.org/wiki/24_game
// with a recursive descent parser for a simple calculator (+ - * /)
// using the shunting yard algorithm as explained on
// http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
// It follows operator precedence (i.e. 2 + 3 * 3 = 11),
// understands negation (-5 + 6 = 1), ignores whitespace
// and allows the use of parentheses

// We use a glob import in our test module. Seperating tests into a seperate
// module enforces visibility restrictions so the test module can only access
// publically exported code, the same as any user of the code.
#![feature(globs)]
use std::char;

#[cfg(not(test))]
fn main() {
    use std::{rand, io};

    let mut rng = rand::task_rng();
    let mut input = io::stdin();

    loop {
        let mut sample = rand::sample(&mut rng, range(1u, 10), 4);

        println!("make 24 by combining the following 4 numbers with + - * / or (q)uit");
        println!("{}", sample);

        let line = input.read_line().unwrap();
        match line.as_slice().trim() {
            "q" => break,
            input => {
                if check_values(sample.as_mut_slice(), input) {
                    match Parser::new(input).parse() {
                        Ok(i) if i == 24. => println!("you made it!"),
                        Ok(i) => println!("you entered {}, try again!", i),
                        Err(s)  => println!("{}", s)
                    };
                } else {
                    println!("unrecognized input, try again")
                }
            }
        }
    }
}

// Returns true if the entered expression uses the values contained in sample
pub fn check_values(sample:&mut [uint], input:&str) -> bool {
    let lex = Lexer::new(input);

    let mut numbers_used = lex.filter_map(|a| {
        match a {
            Int(i) => Some(i),
            _ => None
        }
    }).collect::<Vec<uint>>();

    numbers_used.sort();
    sample.sort();
    numbers_used.as_slice() == sample
}

// the tokens that our parser is going to recognize
#[deriving(PartialEq,Eq,Show)]
pub enum Token {
    LParen,
    RParen,
    Plus,
    Minus,
    Slash,
    Star,
    Int(uint)
}

impl Token {
   // are tokens associated to a binary operation?
   fn is_binary(&self) -> bool {
        match *self {
            Plus | Minus | Slash | Star => true,
            _ => false
        }
    }
}

trait Tokenable { fn as_token(&self) -> Option<Token>; }

// map a character to its corresponding token
impl Tokenable for char {
    #[inline]
    fn as_token(&self) -> Option<Token> {
        let tok = match *self {
            '(' => LParen,
            ')' => RParen,
            '+' => Plus,
            '-' => Minus,
            '/' => Slash,
            '*' => Star,
            _ => return None
        };

        Some(tok)
    }
}

// Lexer reads an expression like (a + b) / c * d
// as an iterator on the tokens that compose it
// Int(a), LParen, Plus, Int(b), RParen...
pub struct Lexer<'a> {
    input: &'a str,
    offset: uint
}

impl <'a> Lexer<'a> {
    pub fn new(input: &str) -> Lexer {
        Lexer { input: input, offset: 0u }
    }

    fn expect(&mut self, expected:&[Token]) -> Result<Token, String> {
        let n = self.offset;
        match self.next() {
            Some(a) if expected.contains(&a)  => Ok(a),
            other  => Err(format!("Parsing error: {} was unexpected at offset {}",
                                  other,
                                  n))
        }
    }
}

impl <'a> Iterator<Token> for Lexer<'a> {
    fn next(&mut self) -> Option<Token> {
        // slice the original string starting from the current offset
        let mut remaining = self.input.slice_from(self.offset)
                                      // keep track of the original indice
                                      .char_indices()
                                      // advance to the next non-whitespace char
                                      .skip_while(|&(_, ch)| ch.is_whitespace());

        let (tok, cur_offset) = match remaining.next() {
            // Found a digit. if there are others, transform them to `uint`
            Some((mut offset, ch)) if ch.is_digit() => {
                let mut val = char::to_digit(ch, 10).unwrap();

                for (idx, ch) in remaining {
                    if ch.is_digit() {
                        let digit = char::to_digit(ch, 10).unwrap();
                        val = val * 10 + digit;
                    } else {
                        offset = idx;
                        break;
                    }
                }

                (Some(Int(val)), offset)
            },
            // found non-digit, try transforming it to the corresponding token
            Some((o, ch)) => (ch.as_token(), o + 1),
            _   => (None, 0u)
        };

        // update the offset for the next iteration
        self.offset += cur_offset;
        tok
    }
}

// Operators are a "higher level" concept than tokens as they define the
// semantics of the expression language e.g. token "Minus" can correspond to
// the unary Neg Operator (-a) or to the binary Sub operator (a - b)
#[deriving(PartialEq, Eq)]
pub enum Operator {
    Neg,
    Add,
    Sub,
    Mul,
    Div,
    Sentinel
}

impl Operator {
     fn precedence(&self) -> uint  {
        match *self {
            Sentinel => 0u,
            Add | Sub => 1u,
            Neg => 2u,
            Mul | Div => 3u
        }
    }
}

// Operator precedence for binary operators:
// * if x has higher precedence than y precedence for x > precedence for y
// * if x and y have equal precedence the first one has precedence
// e.g. in expression (4 / 2 * 2) operators * and / have the same precedence,
// but the operations must be performed in the order they appear
// (division first, multiplication second) otherwise results are different
impl PartialOrd for Operator {
    fn partial_cmp(&self, other: &Operator) -> Option<Ordering> {
        match (self.precedence(), other.precedence()) {
            (a, b) if a == b => Some(Greater),
            (a, b) => a.partial_cmp(&b)
        }
    }
}

// recursive descent parser
// with the shunting yard algorithm as explained on
// http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
// I followed the names of the methods as closely as possible vs the pseudo-code
// that illustrates the algorithm
pub struct Parser<'a> {
    operators: Vec<Operator>,
    operands: Vec<f32>,
    lexer: Lexer<'a>
}

impl <'a> Parser<'a> {
    pub fn new(input: &str) -> Parser {
        Parser {
            operators: vec![],
            operands: vec![],
            lexer: Lexer::new(input)
        }
    }

    pub fn parse(&mut self) -> Result<f32, String> {
        self.operators.push(Sentinel);
        try!(self.e());
        return match self.operands.last() {
            Some(r) => Ok(*r),
            None => Err("something went wrong, got no result".to_string())
        }
    }

    fn e(&mut self) -> Result<(), String> {
        try!(self.p());

        loop {
            match self.lexer.peekable().peek() {
                Some(&x) if x.is_binary() => {
                    let op = match x {
                        Plus => Add,
                        Minus => Sub,
                        Star => Mul,
                        Slash => Div,
                        // there are no other binary operators
                        _ => unreachable!()
                    };

                    self.push_operator(op);

                    // Consume the peeked value
                    self.lexer.next();
                    try!(self.p());
                }
                _ => break
            }
        }

        loop {
            match self.operators.last() {
                Some(&op) if op != Sentinel => self.pop_operator(),
                _ => return Ok(())
            }
        }
    }

    fn p(&mut self) -> Result<(), String> {
        match self.lexer.next() {
            Some(Int(n)) => self.operands.push(n as f32),
            Some(LParen) => {
                self.operators.push(Sentinel);
                try!(self.e());
                try!(self.lexer.expect(&[RParen]));
                self.operators.pop();
            },
            Some(Minus) => {
                self.push_operator(Neg);
                try!(self.p());
            },
            Some(e) => return Err(format!("unexpected token {}", e)),
            _ => return Err("unexpected end of command".to_string())
        }
        Ok(())
    }

    fn pop_operator(&mut self) {
        match self.operators.pop() {
            Some(Add) => self.binary_op(|t1, t2| t1 + t2),
            Some(Sub) => self.binary_op(|t1, t2| t1 - t2),
            Some(Mul) => self.binary_op(|t1, t2| t1 * t2),
            Some(Div) => self.binary_op(|t1, t2| t1 / t2),
            Some(Neg) => self.unary_op(|t1| -t1),
            _ => unreachable!()
        }
    }

    fn push_operator(&mut self, op: Operator) {
        match self.operators.last() {
            Some(&last_op) if last_op > op => self.pop_operator(),
            _ => {}
        }
        self.operators.push(op);
    }

    #[inline]
    fn binary_op(&mut self, op: |f32, f32| -> f32) {
        match (self.operands.pop(), self.operands.pop()) {
            (Some(t1), Some(t2)) => self.operands.push(op(t2, t1)),
            _ => unreachable!()
        }
    }

    #[inline]
    fn unary_op(&mut self, op: |f32| -> f32) {
        match self.operands.pop() {
            Some(t1) => self.operands.push(op(t1)),
            _ => unreachable!()
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_precedence() {
        assert!(Mul > Add);
        // Ordering matters for these pairs
        assert!(Mul > Div);
        assert!(Div > Mul);

        assert!(Add > Sub);
        assert!(Sub > Add);

        assert!(!(Mul < Div));
    }

    #[test]
    fn lexer_iter() {
        // test read token and character's offset in the iterator
        let t = |lex: &mut Lexer, exp_tok: Token, exp_pos: uint| {
            assert_eq!(lex.next(), Some(exp_tok));
            assert_eq!(lex.offset, exp_pos);
        };

        let tok = &mut Lexer::new("  15 + 4");
        t(tok, Int(15), 4);
        t(tok, Plus, 6);
        let read = tok.expect(&[LParen,Int(4),RParen]);
        assert_eq!(read, Ok(Int(4)));

        let mut tok = Lexer::new("");
        assert_eq!(tok.next(), None);

        let tok = &mut Lexer::new("     ");
        assert_eq!(tok.next(), None);

        let tok = &mut Lexer::new("2 * (3+4/2)");
        t(tok, Int(2), 1);
        t(tok, Star, 3);
        t(tok, LParen, 5);
        t(tok, Int(3), 6);
        t(tok, Plus, 7);
        t(tok, Int(4), 8);
        t(tok, Slash, 9);
        t(tok, Int(2), 10);
        t(tok, RParen, 11);
    }

    #[test]
    fn parse() {
        fn t(input: &str, expected: Result<f32, String>) {
            assert_eq!(Parser::new(input).parse(), expected)
        }

        t("2+2", Ok(4.));
        t("2+3*4", Ok(14.));
        t("4*(3+2)", Ok(20.));
        t("5/(3+2)*3", Ok(3.));
        t("2++12", Err("unexpected token Plus".to_string()));
        t("-2+12", Ok(10.));
        t("-2*(2+3)", Ok(-10.));

        // Testing precedence
        t("4 / 2 * 2", Ok(4.));
        t("2 * 2 / 4", Ok(1.));
    }

    #[test]
    fn try_check_values() {
        let m = &mut [1, 2, 3, 4];
        assert!(check_values(m, "1+3 -(4/2)"));
    }
}
<|endoftext|>


<commit_before>/*
 * Region resolution. This pass runs before typechecking and resolves region
 * names to the appropriate block.
 */

import driver::session::session;
import middle::ty;
import syntax::{ast, visit};
import util::common::new_def_hash;

import std::map;
import std::map::hashmap;

/* Represents the type of the most immediate parent node. */
enum parent {
    pa_item(ast::node_id),
    pa_block(ast::node_id),
    pa_nested_fn(ast::node_id),
    pa_crate
}

type region_map = {
    /*
     * Mapping from blocks and function expression to their parent block or
     * function expression.
     */
    parents: hashmap<ast::node_id,ast::node_id>,
    /* Mapping from a region type in the AST to its resolved region. */
    ast_type_to_region: hashmap<ast::node_id,ty::region>,
    /* Mapping from a local variable to its containing block. */
    local_blocks: hashmap<ast::node_id,ast::node_id>,
    /* Mapping from a region name to its function. */
    region_name_to_fn: hashmap<ast::def_id,ast::node_id>,
};

type ctxt = {
    sess: session,
    def_map: resolve::def_map,
    region_map: @region_map,
    names_in_scope: hashmap<str,ast::def_id>,

    /*
     * A list of local IDs that will be parented to the next block we
     * traverse. This is used when resolving `alt` statements. Since we see
     * the pattern before the associated block, upon seeing a pattern we must
     * parent all the bindings in that pattern to the next block we see.
     */
    mut queued_locals: [ast::node_id],

    parent: parent,

    /* True if we're within the pattern part of an alt, false otherwise. */
    in_alt: bool
};

fn region_to_scope(_region_map: @region_map, region: ty::region)
        -> ast::node_id {
    ret alt region {
        ty::re_caller(def_id) { def_id.node }
        ty::re_named(_) { fail "TODO: named regions" }
        ty::re_block(node_id) { node_id }
    };
}

// Returns true if `subscope` is equal to or is lexically nested inside
// `superscope` and false otherwise.
fn scope_contains(region_map: @region_map, superscope: ast::node_id,
                  subscope: ast::node_id) -> bool {
    let subscope = subscope;
    while superscope != subscope {
        alt region_map.parents.find(subscope) {
            none { ret false; }
            some(scope) { subscope = scope; }
        }
    }
    ret true;
}

fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt ty.node {
        ast::ty_rptr({id: region_id, node: node}, _) {
            let region;
            alt node {
                ast::re_inferred {
                    // We infer to the caller region if we're at item scope
                    // and to the block region if we're at block scope.
                    //
                    // TODO: What do we do if we're in an alt?

                    alt cx.parent {
                        pa_item(item_id) | pa_nested_fn(item_id) {
                            let def_id = {crate: ast::local_crate,
                                          node: item_id};
                            region = ty::re_caller(def_id);
                        }
                        pa_block(block_id) {
                            region = ty::re_block(block_id);
                        }
                        pa_crate {
                            cx.sess.span_bug(ty.span, "inferred region at " +
                                             "crate level?!");
                        }
                    }
                }

                ast::re_named(ident) {
                    // If at item scope, introduce or reuse a binding. If at
                    // block scope, require that the binding be introduced.
                    alt cx.names_in_scope.find(ident) {
                        some(def_id) { region = ty::re_named(def_id); }
                        none {
                            let def_id = {crate: ast::local_crate,
                                          node: region_id};
                            cx.names_in_scope.insert(ident, def_id);
                            region = ty::re_named(def_id);

                            alt cx.parent {
                                pa_item(fn_id) | pa_nested_fn(fn_id) {
                                    let rf = cx.region_map.region_name_to_fn;
                                    rf.insert(def_id, fn_id);
                                }
                                pa_block(_) {
                                    cx.sess.span_err(ty.span,
                                                     "unknown region `" +
                                                     ident + "`");
                                }
                                pa_crate {
                                    cx.sess.span_bug(ty.span, "named " +
                                                     "region at crate " +
                                                     "level?!");
                                }
                            }
                        }
                    }
                }

                ast::re_self {
                    // For blocks, "self" means "the current block".
                    //
                    // TODO: What do we do in an alt?
                    //
                    // FIXME: Doesn't work in type items.

                    alt cx.parent {
                        pa_item(item_id) | pa_nested_fn(item_id) {
                            let def_id = {crate: ast::local_crate,
                                          node: item_id};
                            region = ty::re_caller(def_id);
                        }
                        pa_block(block_id) {
                            region = ty::re_block(block_id);
                        }
                        pa_crate {
                            cx.sess.span_bug(ty.span,
                                             "region type outside item?!");
                        }
                    }
                }

            }

            cx.region_map.ast_type_to_region.insert(region_id, region);
        }
        _ { /* nothing to do */ }
    }

    visit::visit_ty(ty, cx, visitor);
}

fn record_parent(cx: ctxt, child_id: ast::node_id) {
    alt cx.parent {
        pa_item(parent_id) | pa_block(parent_id) | pa_nested_fn(parent_id) {
            cx.region_map.parents.insert(child_id, parent_id);
        }
        pa_crate { /* no-op */ }
    }
}

fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
    // Record the parent of this block.
    record_parent(cx, blk.node.id);

    // Resolve queued locals to this block.
    for local_id in cx.queued_locals {
        cx.region_map.local_blocks.insert(local_id, blk.node.id);
    }

    // Descend.
    let new_cx: ctxt = {parent: pa_block(blk.node.id),
                        mut queued_locals: [],
                        in_alt: false with cx};
    visit::visit_block(blk, new_cx, visitor);
}

fn resolve_arm(arm: ast::arm, cx: ctxt, visitor: visit::vt<ctxt>) {
    let new_cx: ctxt = {mut queued_locals: [], in_alt: true with cx};
    visit::visit_arm(arm, new_cx, visitor);
}

fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt pat.node {
        ast::pat_ident(path, _) {
            let defn_opt = cx.def_map.find(pat.id);
            alt defn_opt {
                some(ast::def_variant(_,_)) {
                    /* Nothing to do; this names a variant. */
                }
                _ {
                    /*
                     * This names a local. Enqueue it or bind it to the
                     * containing block, depending on whether we're in an alt
                     * or not.
                     */
                    if cx.in_alt {
                        vec::push(cx.queued_locals, pat.id);
                    } else {
                        alt cx.parent {
                            pa_block(block_id) {
                                let local_blocks = cx.region_map.local_blocks;
                                local_blocks.insert(pat.id, block_id);
                            }
                            _ {
                                cx.sess.span_bug(pat.span,
                                                 "unexpected parent");
                            }
                        }
                    }
                }
            }
        }
        _ { /* no-op */ }
    }

    visit::visit_pat(pat, cx, visitor);
}

fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt expr.node {
        ast::expr_fn(_, _, _, _) | ast::expr_fn_block(_, _) {
            record_parent(cx, expr.id);
            let new_cx = {parent: pa_nested_fn(expr.id),
                          in_alt: false with cx};
            visit::visit_expr(expr, new_cx, visitor);
        }
        _ { visit::visit_expr(expr, cx, visitor); }
    }
}

fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
    // Items create a new outer block scope as far as we're concerned.
    let new_cx: ctxt = {names_in_scope: map::new_str_hash(),
                        parent: pa_item(item.id),
                        in_alt: false
                        with cx};
    visit::visit_item(item, new_cx, visitor);
}

fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
        -> @region_map {
    let cx: ctxt = {sess: sess,
                    def_map: def_map,
                    region_map: @{parents: map::new_int_hash(),
                                  ast_type_to_region: map::new_int_hash(),
                                  local_blocks: map::new_int_hash(),
                                  region_name_to_fn: new_def_hash()},
                    names_in_scope: map::new_str_hash(),
                    mut queued_locals: [],
                    parent: pa_crate,
                    in_alt: false};
    let visitor = visit::mk_vt(@{
        visit_block: resolve_block,
        visit_item: resolve_item,
        visit_ty: resolve_ty,
        visit_arm: resolve_arm,
        visit_pat: resolve_pat,
        visit_expr: resolve_expr
        with *visit::default_visitor()
    });
    visit::visit_crate(*crate, cx, visitor);
    ret cx.region_map;
}

<commit_msg>rustc: Use the name map to resolve region names<commit_after>/*
 * Region resolution. This pass runs before typechecking and resolves region
 * names to the appropriate block.
 */

import driver::session::session;
import middle::ty;
import syntax::{ast, visit};
import util::common::new_def_hash;

import std::map;
import std::map::hashmap;

/* Represents the type of the most immediate parent node. */
enum parent {
    pa_item(ast::node_id),
    pa_block(ast::node_id),
    pa_nested_fn(ast::node_id),
    pa_crate
}

type region_map = {
    /*
     * Mapping from blocks and function expression to their parent block or
     * function expression.
     */
    parents: hashmap<ast::node_id,ast::node_id>,
    /* Mapping from a region type in the AST to its resolved region. */
    ast_type_to_region: hashmap<ast::node_id,ty::region>,
    /* Mapping from a local variable to its containing block. */
    local_blocks: hashmap<ast::node_id,ast::node_id>,
    /* Mapping from a region name to its function. */
    region_name_to_fn: hashmap<ast::def_id,ast::node_id>,
};

type ctxt = {
    sess: session,
    def_map: resolve::def_map,
    region_map: @region_map,
    names_in_scope: hashmap<str,ast::def_id>,

    /*
     * A list of local IDs that will be parented to the next block we
     * traverse. This is used when resolving `alt` statements. Since we see
     * the pattern before the associated block, upon seeing a pattern we must
     * parent all the bindings in that pattern to the next block we see.
     */
    mut queued_locals: [ast::node_id],

    parent: parent,

    /* True if we're within the pattern part of an alt, false otherwise. */
    in_alt: bool
};

fn region_to_scope(region_map: @region_map, region: ty::region)
        -> ast::node_id {
    ret alt region {
        ty::re_caller(def_id) { def_id.node }
        ty::re_named(def_id) { region_map.region_name_to_fn.get(def_id) }
        ty::re_block(node_id) { node_id }
    };
}

// Returns true if `subscope` is equal to or is lexically nested inside
// `superscope` and false otherwise.
fn scope_contains(region_map: @region_map, superscope: ast::node_id,
                  subscope: ast::node_id) -> bool {
    let subscope = subscope;
    while superscope != subscope {
        alt region_map.parents.find(subscope) {
            none { ret false; }
            some(scope) { subscope = scope; }
        }
    }
    ret true;
}

fn resolve_ty(ty: @ast::ty, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt ty.node {
        ast::ty_rptr({id: region_id, node: node}, _) {
            let region;
            alt node {
                ast::re_inferred {
                    // We infer to the caller region if we're at item scope
                    // and to the block region if we're at block scope.
                    //
                    // TODO: What do we do if we're in an alt?

                    alt cx.parent {
                        pa_item(item_id) | pa_nested_fn(item_id) {
                            let def_id = {crate: ast::local_crate,
                                          node: item_id};
                            region = ty::re_caller(def_id);
                        }
                        pa_block(block_id) {
                            region = ty::re_block(block_id);
                        }
                        pa_crate {
                            cx.sess.span_bug(ty.span, "inferred region at " +
                                             "crate level?!");
                        }
                    }
                }

                ast::re_named(ident) {
                    // If at item scope, introduce or reuse a binding. If at
                    // block scope, require that the binding be introduced.
                    alt cx.names_in_scope.find(ident) {
                        some(def_id) { region = ty::re_named(def_id); }
                        none {
                            let def_id = {crate: ast::local_crate,
                                          node: region_id};
                            cx.names_in_scope.insert(ident, def_id);
                            region = ty::re_named(def_id);

                            alt cx.parent {
                                pa_item(fn_id) | pa_nested_fn(fn_id) {
                                    let rf = cx.region_map.region_name_to_fn;
                                    rf.insert(def_id, fn_id);
                                }
                                pa_block(_) {
                                    cx.sess.span_err(ty.span,
                                                     "unknown region `" +
                                                     ident + "`");
                                }
                                pa_crate {
                                    cx.sess.span_bug(ty.span, "named " +
                                                     "region at crate " +
                                                     "level?!");
                                }
                            }
                        }
                    }
                }

                ast::re_self {
                    // For blocks, "self" means "the current block".
                    //
                    // TODO: What do we do in an alt?
                    //
                    // FIXME: Doesn't work in type items.

                    alt cx.parent {
                        pa_item(item_id) | pa_nested_fn(item_id) {
                            let def_id = {crate: ast::local_crate,
                                          node: item_id};
                            region = ty::re_caller(def_id);
                        }
                        pa_block(block_id) {
                            region = ty::re_block(block_id);
                        }
                        pa_crate {
                            cx.sess.span_bug(ty.span,
                                             "region type outside item?!");
                        }
                    }
                }

            }

            cx.region_map.ast_type_to_region.insert(region_id, region);
        }
        _ { /* nothing to do */ }
    }

    visit::visit_ty(ty, cx, visitor);
}

fn record_parent(cx: ctxt, child_id: ast::node_id) {
    alt cx.parent {
        pa_item(parent_id) | pa_block(parent_id) | pa_nested_fn(parent_id) {
            cx.region_map.parents.insert(child_id, parent_id);
        }
        pa_crate { /* no-op */ }
    }
}

fn resolve_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
    // Record the parent of this block.
    record_parent(cx, blk.node.id);

    // Resolve queued locals to this block.
    for local_id in cx.queued_locals {
        cx.region_map.local_blocks.insert(local_id, blk.node.id);
    }

    // Descend.
    let new_cx: ctxt = {parent: pa_block(blk.node.id),
                        mut queued_locals: [],
                        in_alt: false with cx};
    visit::visit_block(blk, new_cx, visitor);
}

fn resolve_arm(arm: ast::arm, cx: ctxt, visitor: visit::vt<ctxt>) {
    let new_cx: ctxt = {mut queued_locals: [], in_alt: true with cx};
    visit::visit_arm(arm, new_cx, visitor);
}

fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt pat.node {
        ast::pat_ident(path, _) {
            let defn_opt = cx.def_map.find(pat.id);
            alt defn_opt {
                some(ast::def_variant(_,_)) {
                    /* Nothing to do; this names a variant. */
                }
                _ {
                    /*
                     * This names a local. Enqueue it or bind it to the
                     * containing block, depending on whether we're in an alt
                     * or not.
                     */
                    if cx.in_alt {
                        vec::push(cx.queued_locals, pat.id);
                    } else {
                        alt cx.parent {
                            pa_block(block_id) {
                                let local_blocks = cx.region_map.local_blocks;
                                local_blocks.insert(pat.id, block_id);
                            }
                            _ {
                                cx.sess.span_bug(pat.span,
                                                 "unexpected parent");
                            }
                        }
                    }
                }
            }
        }
        _ { /* no-op */ }
    }

    visit::visit_pat(pat, cx, visitor);
}

fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
    alt expr.node {
        ast::expr_fn(_, _, _, _) | ast::expr_fn_block(_, _) {
            record_parent(cx, expr.id);
            let new_cx = {parent: pa_nested_fn(expr.id),
                          in_alt: false with cx};
            visit::visit_expr(expr, new_cx, visitor);
        }
        _ { visit::visit_expr(expr, cx, visitor); }
    }
}

fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
    // Items create a new outer block scope as far as we're concerned.
    let new_cx: ctxt = {names_in_scope: map::new_str_hash(),
                        parent: pa_item(item.id),
                        in_alt: false
                        with cx};
    visit::visit_item(item, new_cx, visitor);
}

fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
        -> @region_map {
    let cx: ctxt = {sess: sess,
                    def_map: def_map,
                    region_map: @{parents: map::new_int_hash(),
                                  ast_type_to_region: map::new_int_hash(),
                                  local_blocks: map::new_int_hash(),
                                  region_name_to_fn: new_def_hash()},
                    names_in_scope: map::new_str_hash(),
                    mut queued_locals: [],
                    parent: pa_crate,
                    in_alt: false};
    let visitor = visit::mk_vt(@{
        visit_block: resolve_block,
        visit_item: resolve_item,
        visit_ty: resolve_ty,
        visit_arm: resolve_arm,
        visit_pat: resolve_pat,
        visit_expr: resolve_expr
        with *visit::default_visitor()
    });
    visit::visit_crate(*crate, cx, visitor);
    ret cx.region_map;
}

<|endoftext|>


<commit_before>/*

typeck.rs, an introduction

The type checker is responsible for:

1. Determining the type of each expression
2. Resolving methods and traits
3. Guaranteeing that most type rules are met ("most?", you say, "why most?"
   Well, dear reader, read on)

The main entry point is `check_crate()`.  Type checking operates in two major
phases: collect and check.  The collect phase passes over all items and
determines their type, without examining their "innards".  The check phase
then checks function bodies and so forth.

Within the check phase, we check each function body one at a time (bodies of
function expressions are checked as part of the containing function).
Inference is used to supply types wherever they are unknown. The actual
checking of a function itself has several phases (check, regionck, writeback),
as discussed in the documentation for the `check` module.

The type checker is defined into various submodules which are documented
independently:

- astconv: converts the AST representation of types
  into the `ty` representation

- collect: computes the types of each top-level item and enters them into
  the `cx.tcache` table for later use

- check: walks over function bodies and type checks them, inferring types for
  local variables, type parameters, etc as necessary.

- infer: finds the types to use for each type variable such that
  all subtyping and assignment constraints are met.  In essence, the check
  module specifies the constraints, and the infer module solves them.

*/

import result::result;
import syntax::{ast, ast_util, ast_map};
import ast::spanned;
import ast::{required, provided};
import syntax::ast_map::node_id_to_str;
import syntax::ast_util::{local_def, respan, split_trait_methods};
import syntax::visit;
import metadata::csearch;
import driver::session::session;
import util::common::may_break;
import syntax::codemap::span;
import pat_util::{pat_is_variant, pat_id_map};
import middle::ty;
import middle::ty::{arg, field, node_type_table, mk_nil,
                    ty_param_bounds_and_ty, lookup_public_fields};
import std::smallintmap;
import std::map;
import std::map::{hashmap, int_hash};
import std::serialization::{serialize_uint, deserialize_uint};
import vec::each;
import syntax::print::pprust::*;
import util::ppaux::{ty_to_str, tys_to_str, region_to_str,
                     bound_region_to_str, vstore_to_str};
import util::common::{indent, indenter};
import std::list;
import list::{list, nil, cons};

export check_crate;
export infer;
export method_map;
export method_origin, serialize_method_origin, deserialize_method_origin;
export method_map_entry, serialize_method_map_entry;
export deserialize_method_map_entry;
export vtable_map;
export vtable_res;
export vtable_origin;
export method_static, method_param, method_trait;
export vtable_static, vtable_param, vtable_trait;
export provided_methods_map;

#[auto_serialize]
enum method_origin {
    // fully statically resolved method
    method_static(ast::def_id),

    // method invoked on a type parameter with a bounded trait
    method_param(method_param),

    // method invoked on a boxed trait
    method_trait(ast::def_id, uint),
}

// details for a method invoked with a receiver whose type is a type parameter
// with a bounded trait.
#[auto_serialize]
type method_param = {
    // the trait containing the method to be invoked
    trait_id: ast::def_id,

    // index of the method to be invoked amongst the trait's methods
    method_num: uint,

    // index of the type parameter (from those that are in scope) that is
    // the type of the receiver
    param_num: uint,

    // index of the bound for this type parameter which specifies the trait
    bound_num: uint
};

#[auto_serialize]
type method_map_entry = {
    // number of derefs that are required on the receiver
    derefs: uint,

    // the mode by which the self parameter needs to be passed
    self_mode: ast::rmode,

    // method details being invoked
    origin: method_origin
};

// maps from an expression id that corresponds to a method call to the details
// of the method to be invoked
type method_map = hashmap<ast::node_id, method_map_entry>;

// Resolutions for bounds of all parameters, left to right, for a given path.
type vtable_res = @~[vtable_origin];

enum vtable_origin {
    /*
      Statically known vtable. def_id gives the class or impl item
      from whence comes the vtable, and tys are the type substs.
      vtable_res is the vtable itself
     */
    vtable_static(ast::def_id, ~[ty::t], vtable_res),
    /*
      Dynamic vtable, comes from a parameter that has a bound on it:
      fn foo<T: quux, baz, bar>(a: T) -- a's vtable would have a
      vtable_param origin

      The first uint is the param number (identifying T in the example),
      and the second is the bound number (identifying baz)
     */
    vtable_param(uint, uint),
    /*
      Dynamic vtable, comes from something known to have a trait
      type. def_id refers to the trait item, tys are the substs
     */
    vtable_trait(ast::def_id, ~[ty::t]),
}

type vtable_map = hashmap<ast::node_id, vtable_res>;

// Stores information about provided methods, aka "default methods" in traits.
// Maps from a trait's def_id to a MethodInfo about
// that method in that trait.
type provided_methods_map = hashmap<ast::node_id,
                                    ~[@resolve3::MethodInfo]>;

type ty_param_substs_and_ty = {substs: ty::substs, ty: ty::t};

type crate_ctxt_ = {// A mapping from method call sites to traits that have
                    // that method.
                    trait_map: resolve3::TraitMap,
                    method_map: method_map,
                    vtable_map: vtable_map,
                    coherence_info: @coherence::CoherenceInfo,
                    provided_methods_map: provided_methods_map,
                    tcx: ty::ctxt};

enum crate_ctxt {
    crate_ctxt_(crate_ctxt_)
}

// Functions that write types into the node type table
fn write_ty_to_tcx(tcx: ty::ctxt, node_id: ast::node_id, ty: ty::t) {
    debug!("write_ty_to_tcx(%d, %s)", node_id, ty_to_str(tcx, ty));
    smallintmap::insert(*tcx.node_types, node_id as uint, ty);
}
fn write_substs_to_tcx(tcx: ty::ctxt,
                       node_id: ast::node_id,
                       +substs: ~[ty::t]) {
    if substs.len() > 0u {
        tcx.node_type_substs.insert(node_id, substs);
    }
}

fn lookup_def_tcx(tcx: ty::ctxt, sp: span, id: ast::node_id) -> ast::def {
    match tcx.def_map.find(id) {
      Some(x) => x,
      _ => {
        tcx.sess.span_fatal(sp, ~"internal error looking up a definition")
      }
    }
}

fn lookup_def_ccx(ccx: @crate_ctxt, sp: span, id: ast::node_id) -> ast::def {
    lookup_def_tcx(ccx.tcx, sp, id)
}

fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
    {bounds: @~[], region_param: None, ty: t}
}

fn require_same_types(
    tcx: ty::ctxt,
    maybe_infcx: Option<infer::infer_ctxt>,
    t1_is_expected: bool,
    span: span,
    t1: ty::t,
    t2: ty::t,
    msg: fn() -> ~str) -> bool {

    let l_tcx, l_infcx;
    match maybe_infcx {
      None => {
        l_tcx = tcx;
        l_infcx = infer::new_infer_ctxt(tcx);
      }
      Some(i) => {
        l_tcx = i.tcx;
        l_infcx = i;
      }
    }

    match infer::mk_eqty(l_infcx, t1_is_expected, span, t1, t2) {
      result::ok(()) => true,
      result::err(ref terr) => {
        l_tcx.sess.span_err(span, msg() + ~": " +
            ty::type_err_to_str(l_tcx, terr));
        false
      }
    }
}

fn arg_is_argv_ty(_tcx: ty::ctxt, a: ty::arg) -> bool {
    match ty::get(a.ty).struct {
      ty::ty_evec(mt, vstore_uniq) => {
        if mt.mutbl != ast::m_imm { return false; }
        match ty::get(mt.ty).struct {
          ty::ty_estr(vstore_uniq) => return true,
          _ => return false
        }
      }
      _ => return false
    }
}

fn check_main_fn_ty(ccx: @crate_ctxt,
                    main_id: ast::node_id,
                    main_span: span) {

    let tcx = ccx.tcx;
    let main_t = ty::node_id_to_type(tcx, main_id);
    match ty::get(main_t).struct {
      ty::ty_fn({purity: ast::impure_fn, proto: ty::proto_bare,
                 inputs, output, ret_style: ast::return_val, _}) => {
        match tcx.items.find(main_id) {
         Some(ast_map::node_item(it,_)) => {
             match it.node {
               ast::item_fn(_,_,ps,_) if vec::is_not_empty(ps) => {
                  tcx.sess.span_err(main_span,
                    ~"main function is not allowed to have type parameters");
                  return;
               }
               _ => ()
             }
         }
         _ => ()
        }
        let mut ok = ty::type_is_nil(output);
        let num_args = vec::len(inputs);
        ok &= num_args == 0u || num_args == 1u &&
              arg_is_argv_ty(tcx, inputs[0]);
        if !ok {
                tcx.sess.span_err(main_span,
                   fmt!("Wrong type in main function: found `%s`, \
                   expected `extern fn(~[str]) -> ()` \
                   or `extern fn() -> ()`",
                         ty_to_str(tcx, main_t)));
         }
      }
      _ => {
        tcx.sess.span_bug(main_span,
                          ~"main has a non-function type: found `" +
                              ty_to_str(tcx, main_t) + ~"`");
      }
    }
}

fn check_for_main_fn(ccx: @crate_ctxt) {
    let tcx = ccx.tcx;
    if !tcx.sess.building_library {
        match copy tcx.sess.main_fn {
          Some((id, sp)) => check_main_fn_ty(ccx, id, sp),
          None => tcx.sess.err(~"main function not found")
        }
    }
}

fn check_crate(tcx: ty::ctxt,
               trait_map: resolve3::TraitMap,
               crate: @ast::crate)
            -> (method_map, vtable_map) {

    let ccx = @crate_ctxt_({trait_map: trait_map,
                            method_map: std::map::int_hash(),
                            vtable_map: std::map::int_hash(),
                            coherence_info: @coherence::CoherenceInfo(),
                            provided_methods_map: std::map::int_hash(),
                            tcx: tcx});
    collect::collect_item_types(ccx, crate);
    coherence::check_coherence(ccx, crate);

    check::check_item_types(ccx, crate);
    check_for_main_fn(ccx);
    tcx.sess.abort_if_errors();
    (ccx.method_map, ccx.vtable_map)
}
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<commit_msg>rustc: import vstore_uniq<commit_after>/*

typeck.rs, an introduction

The type checker is responsible for:

1. Determining the type of each expression
2. Resolving methods and traits
3. Guaranteeing that most type rules are met ("most?", you say, "why most?"
   Well, dear reader, read on)

The main entry point is `check_crate()`.  Type checking operates in two major
phases: collect and check.  The collect phase passes over all items and
determines their type, without examining their "innards".  The check phase
then checks function bodies and so forth.

Within the check phase, we check each function body one at a time (bodies of
function expressions are checked as part of the containing function).
Inference is used to supply types wherever they are unknown. The actual
checking of a function itself has several phases (check, regionck, writeback),
as discussed in the documentation for the `check` module.

The type checker is defined into various submodules which are documented
independently:

- astconv: converts the AST representation of types
  into the `ty` representation

- collect: computes the types of each top-level item and enters them into
  the `cx.tcache` table for later use

- check: walks over function bodies and type checks them, inferring types for
  local variables, type parameters, etc as necessary.

- infer: finds the types to use for each type variable such that
  all subtyping and assignment constraints are met.  In essence, the check
  module specifies the constraints, and the infer module solves them.

*/

import result::result;
import syntax::{ast, ast_util, ast_map};
import ast::spanned;
import ast::{required, provided};
import syntax::ast_map::node_id_to_str;
import syntax::ast_util::{local_def, respan, split_trait_methods};
import syntax::visit;
import metadata::csearch;
import driver::session::session;
import util::common::may_break;
import syntax::codemap::span;
import pat_util::{pat_is_variant, pat_id_map};
import middle::ty;
import middle::ty::{arg, field, node_type_table, mk_nil,
                    ty_param_bounds_and_ty, lookup_public_fields,
                    vstore_uniq};
import std::smallintmap;
import std::map;
import std::map::{hashmap, int_hash};
import std::serialization::{serialize_uint, deserialize_uint};
import vec::each;
import syntax::print::pprust::*;
import util::ppaux::{ty_to_str, tys_to_str, region_to_str,
                     bound_region_to_str, vstore_to_str};
import util::common::{indent, indenter};
import std::list;
import list::{list, nil, cons};

export check_crate;
export infer;
export method_map;
export method_origin, serialize_method_origin, deserialize_method_origin;
export method_map_entry, serialize_method_map_entry;
export deserialize_method_map_entry;
export vtable_map;
export vtable_res;
export vtable_origin;
export method_static, method_param, method_trait;
export vtable_static, vtable_param, vtable_trait;
export provided_methods_map;

#[auto_serialize]
enum method_origin {
    // fully statically resolved method
    method_static(ast::def_id),

    // method invoked on a type parameter with a bounded trait
    method_param(method_param),

    // method invoked on a boxed trait
    method_trait(ast::def_id, uint),
}

// details for a method invoked with a receiver whose type is a type parameter
// with a bounded trait.
#[auto_serialize]
type method_param = {
    // the trait containing the method to be invoked
    trait_id: ast::def_id,

    // index of the method to be invoked amongst the trait's methods
    method_num: uint,

    // index of the type parameter (from those that are in scope) that is
    // the type of the receiver
    param_num: uint,

    // index of the bound for this type parameter which specifies the trait
    bound_num: uint
};

#[auto_serialize]
type method_map_entry = {
    // number of derefs that are required on the receiver
    derefs: uint,

    // the mode by which the self parameter needs to be passed
    self_mode: ast::rmode,

    // method details being invoked
    origin: method_origin
};

// maps from an expression id that corresponds to a method call to the details
// of the method to be invoked
type method_map = hashmap<ast::node_id, method_map_entry>;

// Resolutions for bounds of all parameters, left to right, for a given path.
type vtable_res = @~[vtable_origin];

enum vtable_origin {
    /*
      Statically known vtable. def_id gives the class or impl item
      from whence comes the vtable, and tys are the type substs.
      vtable_res is the vtable itself
     */
    vtable_static(ast::def_id, ~[ty::t], vtable_res),
    /*
      Dynamic vtable, comes from a parameter that has a bound on it:
      fn foo<T: quux, baz, bar>(a: T) -- a's vtable would have a
      vtable_param origin

      The first uint is the param number (identifying T in the example),
      and the second is the bound number (identifying baz)
     */
    vtable_param(uint, uint),
    /*
      Dynamic vtable, comes from something known to have a trait
      type. def_id refers to the trait item, tys are the substs
     */
    vtable_trait(ast::def_id, ~[ty::t]),
}

type vtable_map = hashmap<ast::node_id, vtable_res>;

// Stores information about provided methods, aka "default methods" in traits.
// Maps from a trait's def_id to a MethodInfo about
// that method in that trait.
type provided_methods_map = hashmap<ast::node_id,
                                    ~[@resolve3::MethodInfo]>;

type ty_param_substs_and_ty = {substs: ty::substs, ty: ty::t};

type crate_ctxt_ = {// A mapping from method call sites to traits that have
                    // that method.
                    trait_map: resolve3::TraitMap,
                    method_map: method_map,
                    vtable_map: vtable_map,
                    coherence_info: @coherence::CoherenceInfo,
                    provided_methods_map: provided_methods_map,
                    tcx: ty::ctxt};

enum crate_ctxt {
    crate_ctxt_(crate_ctxt_)
}

// Functions that write types into the node type table
fn write_ty_to_tcx(tcx: ty::ctxt, node_id: ast::node_id, ty: ty::t) {
    debug!("write_ty_to_tcx(%d, %s)", node_id, ty_to_str(tcx, ty));
    smallintmap::insert(*tcx.node_types, node_id as uint, ty);
}
fn write_substs_to_tcx(tcx: ty::ctxt,
                       node_id: ast::node_id,
                       +substs: ~[ty::t]) {
    if substs.len() > 0u {
        tcx.node_type_substs.insert(node_id, substs);
    }
}

fn lookup_def_tcx(tcx: ty::ctxt, sp: span, id: ast::node_id) -> ast::def {
    match tcx.def_map.find(id) {
      Some(x) => x,
      _ => {
        tcx.sess.span_fatal(sp, ~"internal error looking up a definition")
      }
    }
}

fn lookup_def_ccx(ccx: @crate_ctxt, sp: span, id: ast::node_id) -> ast::def {
    lookup_def_tcx(ccx.tcx, sp, id)
}

fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
    {bounds: @~[], region_param: None, ty: t}
}

fn require_same_types(
    tcx: ty::ctxt,
    maybe_infcx: Option<infer::infer_ctxt>,
    t1_is_expected: bool,
    span: span,
    t1: ty::t,
    t2: ty::t,
    msg: fn() -> ~str) -> bool {

    let l_tcx, l_infcx;
    match maybe_infcx {
      None => {
        l_tcx = tcx;
        l_infcx = infer::new_infer_ctxt(tcx);
      }
      Some(i) => {
        l_tcx = i.tcx;
        l_infcx = i;
      }
    }

    match infer::mk_eqty(l_infcx, t1_is_expected, span, t1, t2) {
      result::ok(()) => true,
      result::err(ref terr) => {
        l_tcx.sess.span_err(span, msg() + ~": " +
            ty::type_err_to_str(l_tcx, terr));
        false
      }
    }
}

fn arg_is_argv_ty(_tcx: ty::ctxt, a: ty::arg) -> bool {
    match ty::get(a.ty).struct {
      ty::ty_evec(mt, vstore_uniq) => {
        if mt.mutbl != ast::m_imm { return false; }
        match ty::get(mt.ty).struct {
          ty::ty_estr(vstore_uniq) => return true,
          _ => return false
        }
      }
      _ => return false
    }
}

fn check_main_fn_ty(ccx: @crate_ctxt,
                    main_id: ast::node_id,
                    main_span: span) {

    let tcx = ccx.tcx;
    let main_t = ty::node_id_to_type(tcx, main_id);
    match ty::get(main_t).struct {
      ty::ty_fn({purity: ast::impure_fn, proto: ty::proto_bare,
                 inputs, output, ret_style: ast::return_val, _}) => {
        match tcx.items.find(main_id) {
         Some(ast_map::node_item(it,_)) => {
             match it.node {
               ast::item_fn(_,_,ps,_) if vec::is_not_empty(ps) => {
                  tcx.sess.span_err(main_span,
                    ~"main function is not allowed to have type parameters");
                  return;
               }
               _ => ()
             }
         }
         _ => ()
        }
        let mut ok = ty::type_is_nil(output);
        let num_args = vec::len(inputs);
        ok &= num_args == 0u || num_args == 1u &&
              arg_is_argv_ty(tcx, inputs[0]);
        if !ok {
                tcx.sess.span_err(main_span,
                   fmt!("Wrong type in main function: found `%s`, \
                   expected `extern fn(~[str]) -> ()` \
                   or `extern fn() -> ()`",
                         ty_to_str(tcx, main_t)));
         }
      }
      _ => {
        tcx.sess.span_bug(main_span,
                          ~"main has a non-function type: found `" +
                              ty_to_str(tcx, main_t) + ~"`");
      }
    }
}

fn check_for_main_fn(ccx: @crate_ctxt) {
    let tcx = ccx.tcx;
    if !tcx.sess.building_library {
        match copy tcx.sess.main_fn {
          Some((id, sp)) => check_main_fn_ty(ccx, id, sp),
          None => tcx.sess.err(~"main function not found")
        }
    }
}

fn check_crate(tcx: ty::ctxt,
               trait_map: resolve3::TraitMap,
               crate: @ast::crate)
            -> (method_map, vtable_map) {

    let ccx = @crate_ctxt_({trait_map: trait_map,
                            method_map: std::map::int_hash(),
                            vtable_map: std::map::int_hash(),
                            coherence_info: @coherence::CoherenceInfo(),
                            provided_methods_map: std::map::int_hash(),
                            tcx: tcx});
    collect::collect_item_types(ccx, crate);
    coherence::check_coherence(ccx, crate);

    check::check_item_types(ccx, crate);
    check_for_main_fn(ccx);
    tcx.sess.abort_if_errors();
    (ccx.method_map, ccx.vtable_map)
}
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<|endoftext|>