Exemple #1
0
def interpret(body: d_Body) -> Optional[d_Thing]:
    """Read text from a body and interpret it as ditlang code.
    Creates a new InterpretContext and executes statements on tokens,
    one after another, until EOF.

    Used recursively. Classes, functions, and imported dits are all interpreted
    recursively as new bodies with new InterpretContexts.
    Classes and dits are only interpreted once. Functions are re-interpreted
    every time they are called."""

    if not body.is_ready():
        return
    inter = InterpretContext(body)
    last_ret: Optional[d_Thing] = None
    try:
        at_eof = False
        while not at_eof:
            if inter.char_feed.eof():
                at_eof = True  # one extra iteration for the last char
            inter.advance_tokens()
            if inter.next_tok.grammar == d_Grammar.EOF:
                return last_ret  # Only at EOF whitespace or comment
            else:
                last_ret = _statement_dispatch(inter)
                inter.named_statement = False
    except d_DitError as err:
        if not err.origin:
            _generate_origin(err, inter)
        raise
    return last_ret
Exemple #2
0
def _trailing_comma(inter: InterpretContext,
                    right: d_Grammar) -> Optional[NoReturn]:
    if inter.next_tok.grammar not in [d_Grammar.COMMA, right]:
        _missing_terminal(inter, f"Expected '{right.value}'")

    if inter.next_tok.grammar == d_Grammar.COMMA:
        inter.advance_tokens()
Exemple #3
0
def _equals(inter: InterpretContext, equal_loc: CodeLocation) -> None:
    orig_loc = copy.deepcopy(inter.next_tok.loc)
    if inter.anon_tok or inter.call_tok:
        # prevent assignment to anonymous tokens.
        raise NotImplementedError
    if inter.dec.type_:
        assignee = None
    else:
        assignee = inter.curr_tok.thing

    inter.advance_tokens()
    inter.equaling = True
    value = _expression_dispatch(inter)
    inter.equaling = False
    if not value:
        if inter.named_statement:
            # Prevent assignment of non-anonymous statements.
            # Class anonName = class RealName {||}
            raise d_SyntaxError(
                "A named declaration cannot be used for assignment", orig_loc)
        else:
            raise d_CriticalError(
                "No value for _expression_dispatch in _equals")
    if assignee:
        assignee.set_value(value)
    else:
        try:
            _add_attr_wrap(inter, value=value)
        except d_TypeMismatchError as err:
            err.loc = equal_loc
            raise
Exemple #4
0
def _bool(inter: InterpretContext) -> d_Bool:
    # b = true;
    # doThing(false);
    val = d_Bool()
    val.is_null = False
    val.bool_ = inter.next_tok.grammar == d_Grammar.TRUE
    inter.advance_tokens()
    return val
Exemple #5
0
def _sig(inter: InterpretContext) -> Optional[d_Func]:
    # sig JavaScript Str ... \n func
    dotable_loc = None
    func = _sig_or_func(inter)
    did_dispatch = False

    while True:
        if not did_dispatch:
            inter.advance_tokens()
        did_dispatch = False
        gra = inter.next_tok.grammar
        thing = inter.next_tok.thing
        switches = [func.lang, func.return_, func.return_list]

        # listOf needs additional checking
        # There must be a type after it, and not before it.
        if func.return_list and not func.return_:
            # sig ... listOf ...
            if gra == d_Grammar.VOID:
                raise d_SyntaxError("Cannot have listOf void")
            elif gra not in TYPES and gra not in DOTABLES:
                raise d_SyntaxError("Expected type to follow listOf",
                                    dotable_loc)
            elif gra in DOTABLES:
                dotable_loc = copy.deepcopy(inter.next_tok.loc)

        if gra == d_Grammar.FUNC:
            return _func(inter)
        elif gra == d_Grammar.EOF or None not in switches:
            raise d_SyntaxError("Expected 'func' to follow sig")
        elif thing:
            # handles dotables and explicit Lang/Class objects
            _sig_thing_handler(inter, func)
            did_dispatch = True
        elif gra in TYPES or gra == d_Grammar.VOID:
            # sig ... Str ...
            # sig ... void ...
            _sig_assign_return(inter, func)
            func.return_ = prim_to_value(gra)
        elif gra == d_Grammar.LISTOF:
            # sig ... listOf ...
            if func.return_:
                raise d_SyntaxError("Unexpected 'listOf' after type")
            func.return_list = True
        elif inter.next_tok.grammar == d_Grammar.NEW_NAME:
            raise NotImplementedError
            # TODO: finish unassigned langs feature, issue #14
            # We allow langs to be declared on the fly. This lets library dits
            # specify a language without having to import it, which would be annoying.
            # The lang must be more fully assigned in the dit where it will be called.
            lang = d_Lang()
            lang.is_null = False
            lang.parent_scope = inter.body
            lang.name = inter.next_tok.word
            inter.body.attrs[d_Variable(lang.name)] = lang
            func.lang = lang
        else:
            raise d_SyntaxError("Unrecognized token for signature")
Exemple #6
0
def _value_equalable(inter: InterpretContext) -> Optional[d_Thing]:
    # Thing test;
    # test = 'cat';
    # or, in an expression...
    # Thing test = 'cat';
    # someFunc(test);
    # These code examples are applicable to most _value_X functions
    inter.advance_tokens()
    return _equalable(inter)
Exemple #7
0
def _finalize_num(inter: InterpretContext, num: str) -> d_Num:
    # advance to terminal
    # num = 3.14;
    inter.advance_tokens()
    fin_num = d_Num()
    fin_num.is_null = False
    try:
        fin_num.num = int(num)
    except ValueError:
        fin_num.num = float(num)
    return fin_num
Exemple #8
0
def _digit_sign(inter: InterpretContext, neg: bool) -> d_Num:
    # make sure the sign is being used as a positive or negative,
    # not for arithmetic
    if DIGIT.match(inter.char_feed.current()):
        inter.advance_tokens()
        return _digit(inter, neg)
    else:
        raise d_SyntaxError(
            "Expected digit.\nOther arithmetic ops are not yet supported.",
            inter.char_feed.loc,  # Default uses inter.next_tok.lok
        )
Exemple #9
0
def _listof(inter: InterpretContext) -> None:
    # listOf Str values;
    inter.advance_tokens()
    inter.dec.listof = True
    if inter.next_tok.grammar in PRIMITIVES:
        _primitive(inter)
    elif inter.next_tok.grammar in DOTABLES:
        _expression_dispatch(inter)

    else:
        raise d_SyntaxError("Expected type for listOf declaration")
Exemple #10
0
def _return(inter: InterpretContext) -> NoReturn:
    orig_loc = copy.deepcopy(inter.next_tok.loc)
    if not isinstance(inter.body, d_Func):
        raise d_SyntaxError("'return' outside of function")
    inter.advance_tokens()
    value = _expression_dispatch(inter)
    if not value:
        raise NotImplementedError
    try:
        raise ReturnController(value, inter.body, orig_loc)
    except d_TypeMismatchError as err:
        err.loc = orig_loc
        raise
Exemple #11
0
def _brace_left(inter: InterpretContext) -> d_JSON:
    # JSON j = { ...
    inter.in_json = True
    js = d_JSON()
    js.is_null = False
    js.json_ = {}
    inter.advance_tokens()

    while True:
        if inter.next_tok.grammar == d_Grammar.BRACE_RIGHT:
            # JSON j = { ... }
            inter.in_json = False
            inter.advance_tokens()
            return js
        elif inter.next_tok.grammar == d_Grammar.QUOTE_DOUBLE:
            # JSON j = { "item1": ...
            if inter.curr_tok.grammar not in [
                    d_Grammar.BRACE_LEFT, d_Grammar.COMMA
            ]:
                raise d_SyntaxError("Expected ','", inter.curr_tok.loc)
            name = _str(inter).str_
            if inter.next_tok.grammar != d_Grammar.COLON:
                raise d_SyntaxError("Expected ':'", inter.next_tok.loc)
            else:
                inter.advance_tokens()
            ele = _expression_dispatch(inter)
            js.json_[name] = ele
        elif inter.next_tok.grammar == d_Grammar.COMMA:
            # JSON j = { "item1": 1, ...
            inter.advance_tokens()
            if inter.next_tok.grammar == d_Grammar.BRACE_RIGHT:
                raise d_SyntaxError("Trailing commas are not allowed",
                                    inter.curr_tok.loc)
        else:
            raise d_SyntaxError("Unexpected token for JSON")
Exemple #12
0
def _bar_brace_left(inter: InterpretContext, body: d_Body) -> None:
    depth = 1
    body.start_loc = copy.deepcopy(inter.char_feed.loc)
    while depth > 0:
        cur = inter.char_feed.current() + inter.char_feed.peek()
        if cur == d_Grammar.BAR_BRACE_LEFT.value:
            depth += 1
            inter.advance_tokens()
        elif cur == d_Grammar.BAR_BRACE_RIGHT.value:
            depth -= 1
            body.end_loc = copy.deepcopy(inter.char_feed.loc)
            inter.advance_tokens()
        else:
            inter.char_feed.pop()
Exemple #13
0
def _new_name(inter: InterpretContext) -> None:
    inter.dec.name = inter.next_tok.word
    if not inter.dec.type_:
        # new name without a type declaration is only allowed with an equals
        # unknownName = ...
        inter.advance_tokens()
        if inter.next_tok.grammar != d_Grammar.EQUALS:
            raise d_NameError(
                f"Undefined variable '{inter.curr_tok.word}'",
                inter.curr_tok.loc,
            )
        # if there is an equals, then we just pretend the declaration was 'Thing'
        inter.dec.type_ = d_Grammar.PRIMITIVE_THING
        equal_loc = copy.deepcopy(inter.next_tok.loc)
        _equals(inter, equal_loc)
        inter.dec.reset()
        _terminal(inter)
Exemple #14
0
def _arg_list(inter: InterpretContext,
              right: d_Grammar) -> List[ArgumentLocation]:
    # someFunc('arg1', 'arg2');
    # listOf Str = ['a', 'b', ['c'], 'd'];
    args: List[ArgumentLocation] = []
    inter.advance_tokens()
    inter.comma_depth += 1
    while True:
        if inter.next_tok.grammar == right:
            inter.comma_depth -= 1
            inter.advance_tokens()
            return args
        loc = copy.deepcopy(inter.next_tok.loc)
        arg = _expression_dispatch(inter)
        if not arg:
            raise NotImplementedError
        args.append(ArgumentLocation(loc, arg))
        _trailing_comma(inter, right)
Exemple #15
0
def _type(inter: InterpretContext) -> None:
    # Str test ...
    # someClass test ...
    # Str someDotable.monkeyPatch ...
    # This function is reused by _primitive and _value_class
    inter.dec.type_ = _token_to_type(inter.curr_tok)
    if inter.next_tok.grammar in DUPLICABLES:
        raise d_SyntaxError(
            f"'{inter.next_tok.thing.name}' has already been declared")
    if inter.next_tok.grammar in NAMEABLES:
        _expression_dispatch(inter)
    else:
        raise d_SyntaxError("Expected a new name to follow type")

    if inter.next_tok.grammar == d_Grammar.NEW_NAME:
        # to handle the specific case when a lang is being legally redeclared
        # The tokens were already advanced in _value_lang.
        inter.advance_tokens()
    _equalable(inter)
Exemple #16
0
def _str(inter: InterpretContext) -> d_Str:
    # note that _str is reused for parsing JSON element names
    left = inter.next_tok.grammar.value
    data = ""
    if inter.char_feed.current() != left:
        while True:
            if inter.char_feed.current() == "\n":
                lok = copy.deepcopy(inter.next_tok.loc)
                length = len(data)
                lok.pos += length
                lok.col += length
                raise d_SyntaxError("Unexpected EOL while reading string", lok)
            elif inter.char_feed.current() == d_Grammar.BACKSLASH.value:
                # Str test = "some\t"
                # Str test = 'Let\'s'
                escape_char = inter.char_feed.pop()
                inter.char_feed.pop()
                if escape_char in [
                        d_Grammar.QUOTE_DOUBLE.value,
                        d_Grammar.QUOTE_SINGLE.value,
                        d_Grammar.BACKSLASH.value,
                ]:
                    data += escape_char
                elif escape_char == d_Grammar.ESCAPE_NEWLINE.value:
                    data += "\n"
                elif escape_char == d_Grammar.ESCAPE_TAB.value:
                    data += "\t"
            else:
                data += inter.char_feed.current()
                inter.char_feed.pop()

            if inter.char_feed.current() == left:
                break

    inter.advance_tokens()  # next_tok is now ' "
    inter.advance_tokens()  # next_tok is now ; , ] ) :
    thing = d_Str()
    thing.str_ = data
    thing.is_null = False
    return thing
Exemple #17
0
def _dot(inter: InterpretContext) -> Token:
    inter.advance_tokens(False)  # We want to manage the next word ourselves
    if inter.next_tok.grammar != d_Grammar.WORD:
        raise d_SyntaxError(f"'{inter.next_tok.grammar}' is not dotable")

    # Allows dotting of anonymous tokens and function calls.
    target = (inter.anon_tok or inter.call_tok or inter.prev_tok).thing

    if isinstance(target, d_Inst):
        # We need to prepare in case the instance has inherited parents
        target.clear_prefix_to_func()
        inter.dotted_inst = target
    elif not isinstance(target, d_Container):
        raise d_CriticalError(f"Expected container, got {target.public_type}")

    result = target.find_attr(inter.next_tok.word)
    if not result and inter.dotted_inst:
        # this.B.attribute
        # We are doing explicit name disambiguation, looking in parent B instead of A.
        # B obviously doesn't have the attribute, the instance does.
        # So we look again in the instance.
        result = inter.dotted_inst.find_attr(inter.next_tok.word)

    if result:
        # The dot had a known variable, which we just need to return
        if isinstance(result, d_Func) and inter.dotted_inst:
            # this.func()
            # if `func` is an inherited function, we need to remember that we
            # called it so the prefixes are set up correctly.
            inter.dotted_inst.add_func_sep()
        return Token(result.grammar,
                     copy.deepcopy(inter.next_tok.loc),
                     thing=result)
    else:
        # The name was not found in the dotted body, so its a new name.
        # This means we are declaring a new var in the dotted body.
        # AKA Monkey patching
        inter.next_tok.grammar = d_Grammar.NEW_NAME
        inter.dotted_body = target  # Save for assignment
        return inter.next_tok
Exemple #18
0
def _import(inter: InterpretContext) -> Optional[d_Dit]:
    # import LINK
    # import NAMESPACE from LINK
    orig_loc = copy.deepcopy(inter.next_tok.loc)
    name = None

    inter.advance_tokens(False)
    gra = inter.next_tok.grammar
    if gra != d_Grammar.WORD and gra not in STRINGABLES:
        raise d_SyntaxError("Expected a name or filepath string for import")

    if gra == d_Grammar.WORD:
        # import SomeName from "someFilePath.dit";
        if inter.body.find_attr(inter.next_tok.word, scope_mode=True):
            raise d_SyntaxError(
                f"'{inter.next_tok.word}' has already been declared")
        name = inter.next_tok.word
        inter.advance_tokens()
        if inter.next_tok.grammar != d_Grammar.FROM:
            raise d_SyntaxError("Expected 'from'")
        inter.advance_tokens()

    dit = _import_or_pull(inter, orig_loc)
    dit.name = name  # type: ignore
    return _import_or_pull_end(inter, dit, orig_loc)
Exemple #19
0
def _value_clang(inter: InterpretContext) -> Optional[d_Thing]:
    if inter.declaring_func:
        # sig someLang someClass...
        # This class/lang is being used as a return type or language
        # in a function signature.
        inter.advance_tokens(False)
        if inter.next_tok.grammar == d_Grammar.DOT:
            return _dotable(inter)
        else:
            return inter.curr_tok.thing

    inter.advance_tokens(True)
    if (isinstance(inter.curr_tok.thing, d_Class)
            and inter.next_tok.grammar == d_Grammar.PAREN_LEFT):
        # This class is being instantiated
        # someClass anInstance = someClass();
        return _parenable(inter)
    elif (isinstance(inter.curr_tok.thing, d_Lang)
          and inter.dec.type_ is d_Grammar.PRIMITIVE_LANG):
        # This lang is being legally redeclared
        # lang JavaScript {||}
        # Lang JavaScript ...
        return
    elif inter.next_tok.grammar in VALUE_CLANG_ABLES:
        # someClass = someOtherClass;
        # someLang.someLangFunc();
        # This class is being dotted, equaled, etc.
        return _dotable(inter)
    elif inter.anon_tok:
        # class {||} someInstance = ?... this makes no sense.
        # This prevents using an anonymous class as a type
        # Triggering terminal will call missing ';'
        _terminal(inter)
    else:
        # someClass someInstance...
        # This class is being used as var type
        _type(inter)
Exemple #20
0
def _clang(inter: InterpretContext,
           clang: Union[d_Class, d_Lang]) -> Optional[Union[d_Class, d_Lang]]:
    # class/lang NAME {||}
    # class/lang {||}; <- Anonymous version
    lang = None
    orig_loc = copy.deepcopy(inter.next_tok.loc)
    clang_name = "class" if isinstance(clang, d_Class) else "lang"

    inter.advance_tokens(False)
    if inter.next_tok.grammar not in [
            d_Grammar.WORD, d_Grammar.BAR_BRACE_LEFT
    ]:
        raise d_SyntaxError(f"Expected name or body to follow {clang_name}")

    if inter.next_tok.grammar == d_Grammar.WORD:
        result = inter.body.find_attr(inter.next_tok.word, scope_mode=True)
        if result:
            if isinstance(result, d_Lang):
                # Langs are allowed to be redeclared
                # lang someLang {||}
                # lang someLang {||}
                lang = result
            else:
                raise d_SyntaxError(
                    f"'{inter.next_tok.word}' has already been declared")
        clang.name = inter.next_tok.word
        inter.advance_tokens()  # get {|
        if inter.next_tok.grammar != d_Grammar.BAR_BRACE_LEFT:
            raise d_SyntaxError(f"Expected a {clang_name} body")

    _bar_brace_left(inter, clang)
    clang.finalize()
    try:
        interpret(clang)
    except d_EndOfFileError as err:
        raise d_EndOfClangError(clang_name) from err
    return _handle_anon(inter, clang, orig_loc, lang)  # type: ignore
Exemple #21
0
def _pull(inter: InterpretContext) -> None:
    # pull TARGET (as REPLACEMENT), ... from LINK
    orig_loc = copy.deepcopy(inter.next_tok.loc)
    targets: List[Tuple[str, Optional[str]]] = []
    langs: List[Optional[d_Lang]] = []
    while True:
        # pull ...
        # pull THING, ...
        # pull THING as NAME, ...
        inter.advance_tokens(False)
        if inter.next_tok.grammar != d_Grammar.WORD:
            raise d_SyntaxError("Expected name to pull from linked dit")
        # pull NAME ...
        target = inter.next_tok.word
        loc = copy.deepcopy(inter.next_tok.loc)
        replacement = None
        inter.advance_tokens()
        if inter.next_tok.grammar == d_Grammar.AS:
            # pull NAME as ...
            inter.advance_tokens(False)
            if inter.next_tok.grammar != d_Grammar.WORD:
                raise d_SyntaxError("Expected name to replace target name")
            replacement = inter.next_tok.word
            loc = inter.next_tok.loc
            inter.advance_tokens()

        name = replacement or target
        result = inter.body.find_attr(name, True)
        if result:
            if isinstance(result, d_Lang):
                # Langauges can overwrite langs already in this dit
                # lang someLang {||}
                # pull someLang from LINK
                langs.append(result)
            else:
                raise d_SyntaxError(f"'{name}' has already been declared", loc)
        else:
            langs.append(None)
        targets.append((target, replacement))

        if inter.next_tok.grammar == d_Grammar.FROM:
            break
        elif inter.next_tok.grammar == d_Grammar.COMMA:
            continue
        else:
            raise d_SyntaxError("Expected 'from' or ',' to follow target")

    inter.advance_tokens()
    dit = _import_or_pull(inter, orig_loc)
    for (tar, rep), lang in zip(targets, langs):
        # pull TARGET (as REPLACEMENT), ... from LINK
        result = dit.find_attr(tar)
        if not result:
            raise d_SyntaxError(f"'{tar}' is not a valid member of this dit")
        result.name = rep or tar
        if lang and isinstance(result, d_Lang):
            # explicit call to set_value, to activate Priority comparisons
            lang.set_value(result)
            result.attrs = lang.attrs  # TODO: proper unassigned lang logic
        else:
            inter.body.attrs[d_Variable(result.name)] = result
    _import_or_pull_end(inter, dit, orig_loc)
Exemple #22
0
def _primitive(inter: InterpretContext) -> None:
    # Str value;
    inter.advance_tokens()
    _type(inter)
Exemple #23
0
def _value_inst(inter: InterpretContext) -> Optional[d_Thing]:
    inter.advance_tokens()
    return _dotable(inter)
Exemple #24
0
def _value_function(inter: InterpretContext) -> Optional[d_Thing]:
    inter.advance_tokens()
    return _parenable(inter)
Exemple #25
0
def _value_dit(inter: InterpretContext) -> Optional[d_Thing]:
    if not inter.anon_tok:
        inter.advance_tokens()
    return _dotable(inter)
Exemple #26
0
def _null(inter: InterpretContext) -> d_Thing:
    # someThing = null;
    inter.advance_tokens()
    return d_Thing.get_null_thing()
Exemple #27
0
def _func(inter: InterpretContext) -> Optional[d_Func]:
    # func test(Str right, Str left) {||}
    # func () {||}
    orig_loc = copy.deepcopy(inter.next_tok.loc)
    func = _sig_or_func(inter)
    if not func.return_:
        func.return_ = d_Grammar.VOID
    if not func.return_list:
        func.return_list = False
    if not func.lang:
        func.lang = b_Ditlang
    inter.advance_tokens(False)
    if inter.next_tok.grammar == d_Grammar.WORD:
        # func someName
        result: d_Thing = inter.body.find_attr(inter.next_tok.word,
                                               scope_mode=True)  # type: ignore
        if result:
            raise d_SyntaxError(f"'{result.name}' has already been declared")
        else:
            func.name = inter.next_tok.word
            # Advance only if the name was there
            # If no name, then this is an anonymous function
            inter.advance_tokens()

    if inter.next_tok.grammar != d_Grammar.PAREN_LEFT:
        # func Ditlang void someName(
        raise d_SyntaxError("Expected parameter list")

    inter.advance_tokens()
    while True:
        if inter.next_tok.grammar == d_Grammar.PAREN_RIGHT:
            inter.advance_tokens()
            break

        param_list = False
        if inter.next_tok.grammar == d_Grammar.LISTOF:
            param_list = True
            inter.advance_tokens()

        if inter.next_tok.grammar in DOTABLES:
            # someName(numLib.Number
            result: d_Thing = _expression_dispatch(inter)  # type: ignore
            if result.grammar == d_Grammar.VALUE_CLASS:
                param_type = _token_to_type(inter.curr_tok)
            else:
                mes = ("Expected class for parameter type, "
                       f"'{result.name}' is of type '{result.public_type}'")
                raise d_SyntaxError(mes, inter.terminal_loc)
        elif inter.next_tok.grammar in PRIMITIVES:
            # someName(d_String
            param_type = _token_to_type(inter.next_tok)
            inter.advance_tokens(False)
        else:
            raise d_SyntaxError("Expected parameter type")

        if inter.next_tok.grammar != d_Grammar.WORD:
            raise d_SyntaxError("Expected parameter name")
        else:
            # someName(d_String someParam
            param_name = inter.next_tok.word
            result: d_Thing = inter.body.find_attr(
                param_name, scope_mode=True)  # type: ignore
            if result:
                raise d_SyntaxError(
                    f"'{param_name}' has already been declared")
            elif param_name in [p.name for p in func.parameters]:
                raise d_SyntaxError(
                    f"'{param_name}' is already a parameter name")
        func.parameters.append(Declarable(param_type, param_name, param_list))

        inter.advance_tokens()
        _trailing_comma(inter, d_Grammar.PAREN_RIGHT)

    if inter.next_tok.grammar != d_Grammar.BAR_BRACE_LEFT:
        raise d_SyntaxError("Expected function body")

    _bar_brace_left(inter, func)
    func.finalize()
    inter.declaring_func = None  # type: ignore
    return _handle_anon(inter, func, orig_loc)  # type: ignore