Some clarifications.

This commit is contained in:
chriseth 2017-07-12 20:09:13 +02:00 committed by Alex Beregszaszi
parent 5eaef9e87e
commit c2f2b25064

View File

@ -89,7 +89,7 @@ Grammar::
Expression = Expression =
FunctionCall | Identifier | Literal FunctionCall | Identifier | Literal
Switch = Switch =
'switch' Expression Case+ ( 'default' Block )? 'switch' Expression Case* ( 'default' Block )?
Case = Case =
'case' Literal Block 'case' Literal Block
ForLoop = ForLoop =
@ -116,134 +116,154 @@ Grammar::
Restrictions on the Grammar Restrictions on the Grammar
--------------------------- ---------------------------
Scopes in JULIA are tied to Blocks (exceptions are functions and the for loop) and all declarations Switches must have at least one case (including the default case).
(``FunctionDefinition``, ``VariableDeclaration``)
introduce new identifiers into these scopes. Identifiers are visible in
the block they are defined in (including all sub-nodes and sub-blocks).
Shadowing is disallowed, i.e. you cannot declare an identifier at a point
where another identifier with the same name is also visible.
Switches must have at least one (or the default) and at most one default case.
If all possible values of the expression is covered, the default case should If all possible values of the expression is covered, the default case should
not be allowed (i.e. a switch with a ``bool`` expression and having both a not be allowed (i.e. a switch with a ``bool`` expression and having both a
true and false case should not allow a default case). true and false case should not allow a default case).
In for-loops, identifiers declared in the first block (the init block) Every expression evaluates to zero or more values. Identifiers and Literals
are visible in all other parts of the for loop (but not outside of the loop). evaluate to exactly
Identifiers declared in the other parts of the for loop respect the regular
syntatical scoping rules.
Inside functions, it is not possible to access a variable that was declared
outside of that function.
Every expression evaluates to zero or more values. Literals evaluate to exactly
one value and function calls evaluate to a number of values equal to the one value and function calls evaluate to a number of values equal to the
number of return values of the function called. An expression that is also number of return values of the function called.
a statement is invalid if it evaluates to more than one value, i.e. at the
block-level, only expressions evaluating to zero values are allowed.
For variable declarations and assignments, the right-hand-side expression In variable declarations and assignments, the right-hand-side expression
(if present) has to evaluate to a number of values equal to the number of (if present) has to evaluate to a number of values equal to the number of
variables on the left-hand-side. variables on the left-hand-side.
This is the only situation where an expression evaluating
to more than one value is allowed.
An expression used as an argument to a function call has to evaluate to exactly Expressions that are also statements (i.e. at the block level) have to
one value. evaluate to zero values.
The ``continue`` and ``break`` statements can only be used inside loop bodies. In all other situations, expressions have to evaluate to exactly one value.
The ``continue`` and ``break`` statements can only be used inside loop bodies
and have to be in the same function as the loop (or both have to be at the
top level).
The condition part of the for-loop has to evaluate to exactly one value. The condition part of the for-loop has to evaluate to exactly one value.
Literals cannot be larger than the their type. The largest type defined is 256-bit wide. Literals cannot be larger than the their type. The largest type defined is 256-bit wide.
Scoping Rules
-------------
Scopes in JULIA are tied to Blocks (exceptions are functions and the for loop
as explained below) and all declarations
(``FunctionDefinition``, ``VariableDeclaration``)
introduce new identifiers into these scopes.
Identifiers are visible in
the block they are defined in (including all sub-nodes and sub-blocks).
As an exception, identifiers defined in the "init" part of the for-loop
(the first block) are visible in all other parts of the for-loop
(but not outside of the loop).
Identifiers declared in the other parts of the for loop respect the regular
syntatical scoping rules.
The parameters and return parameters of functions are visible in the
function body and their names cannot overlap.
Variables can only be referenced after their declaration. In particular,
variables cannot be referenced in the right hand side of their own variable
declaration.
Functions can be referenced already before their declaration (if they are visible).
Shadowing is disallowed, i.e. you cannot declare an identifier at a point
where another identifier with the same name is also visible, even if it is
not accessible.
Inside functions, it is not possible to access a variable that was declared
outside of that function.
Formal Specification Formal Specification
-------------------- --------------------
We formally specify JULIA by providing an evaluation function E overloaded We formally specify JULIA by providing an evaluation function E overloaded
on the various nodes of the AST. Any functions can have side effects, so on the various nodes of the AST. Any functions can have side effects, so
E takes a state objects and the actual argument and also returns new E takes two state objects and the AST node and returns two new
state objects and new arguments. There is a global state object state objects and a variable number of other values.
The two state objects are the global state object
(which in the context of the EVM is the memory, storage and state of the (which in the context of the EVM is the memory, storage and state of the
blockchain) and a local state object (the state of local variables, i.e. a blockchain) and the local state object (the state of local variables, i.e. a
segment of the stack in the EVM). segment of the stack in the EVM).
If the AST node is a statement, E returns the two state objects and a "mode",
which is used for the ``break`` and ``continue`` statements.
If the AST node is an expression, E returns the two state objects and
as many values as the expression evaluates to.
The evaluation function E takes a global state, a local state and
a node of the AST and returns a new global state, a new local state
and a variable number of values.
The exact nature of the global state is unspecified for this high level The exact nature of the global state is unspecified for this high level
description. The local state `L` is a mapping of identifiers `i` to values `v`, description. The local state ``L`` is a mapping of identifiers ``i`` to values ``v``,
denoted as `L[i] = v`. denoted as ``L[i] = v``.
The special value `⊥` is used to signify that a variable cannot be
used yet. For an identifier ``v``, let ``$v`` be the name of the identifier.
We will use a destructuring notation for the AST nodes.
.. code:: .. code::
E(G, L, <{St1, ..., Stn}>: Block) = E(G, L, <{St1, ..., Stn}>: Block) =
let L' be an extension of L to all variables v declared in Block let G1, L1, mode = E(G, L, St1, ..., Stn)
(but not in its sub-blocks), such that L'[v] = ⊥. let L2 be a restriction of L1 to the identifiers of L
let Gi, Li, mode = E(G, L', St1, ..., Stn) G1, L2, mode
let L'' be a restriction of Li to the identifiers of L
Gi, L'', mode
E(G, L, St1, ..., Stn: Statement) = E(G, L, St1, ..., Stn: Statement) =
if n is zero: if n is zero:
G, L G, L
else: else:
let G', L', mode = E(G, L, St1) let G1, L1, mode = E(G, L, St1)
if mode is regular then if mode is regular then
E(G', L', St2, ..., Stn) E(G1, L1, St2, ..., Stn)
otherwise otherwise
G', L', mode G1, L1, mode
E(G, L, <function fname (param1, ..., paramn) -> (ret1, ..., retm) block>: FunctionDefinition) = E(G, L, FunctionDefinition) =
G, L, regular G, L, regular
E(G, L, <let var1, ..., varn := rhs>: VariableDeclaration) = E(G, L, <let var1, ..., varn := rhs>: VariableDeclaration) =
E(G, L, <var1, ..., varn := rhs>: Assignment) E(G, L, <var1, ..., varn := rhs>: Assignment)
E(G, L, <let var1, ..., varn>: VariableDeclaration) = E(G, L, <let var1, ..., varn>: VariableDeclaration) =
let L' be a copy of L where L'[vi] = 0 for i = 1, ..., n let L1 be a copy of L where L1[$vari] = 0 for i = 1, ..., n
G, L', regular G, L1, regular
E(G, L, <var1, ..., varn := rhs>: Assignment) = E(G, L, <var1, ..., varn := rhs>: Assignment) =
let G', L', v1, ..., vn = E(G, L, rhs) let G1, L1, v1, ..., vn = E(G, L, rhs)
let L'' be a copy of L' where L'[vi] = vi for i = 1, ..., n let L2 be a copy of L1 where L2[$vari] = vi for i = 1, ..., n
G, L'', regular G, L2, regular
E(G, L, <for { i1, ..., in } condition post body>: ForLoop) = E(G, L, <for { i1, ..., in } condition post body>: ForLoop) =
if n >= 1: if n >= 1:
let L' be an extension of L to all variables v declared in i1, ..., in let G1, L1, mode = E(G, L, i1, ..., in)
(but not in sub-blocks), such that L'[v] = ⊥. // mode has to be regular due to the syntactic restrictions
let G'', L'', mode = E(G, L', i1, ..., in) let G2, L2, mode = E(G1, L1, for {} condition post body)
explode if mode is not regular // mode has to be regular due to the syntactic restrictions
let G''', L''', mode = E(G'', L'', for {} condition post body) let L3 be the restriction of L2 to only variables of L
explode if mode is not regular G2, L3
let Lend be the restriction of L''' to only variables of L
G''', Lend
else: else:
let G', L', v = E(G, L, condition) let G1, L1, v = E(G, L, condition)
if v is false: if v is false:
G', L', regular G1, L1, regular
else: else:
let G'', L'', mode = E(G, L, body) let G2, L2, mode = E(G1, L, body)
if mode is break: if mode is break:
G'', L'', regular G2, L2, regular
else: else:
G''', L''', mode = E(G'', L'', post) G3, L3, mode = E(G2, L2, post)
E(G''', L''', for {} condition post body) E(G3, L3, for {} condition post body)
E(G, L, break: BreakContinue) = E(G, L, break: BreakContinue) =
G, L, break G, L, break
E(G, L, continue: BreakContinue) = E(G, L, continue: BreakContinue) =
G, L, continue G, L, continue
E(G, L, <name>: Identifier) = E(G, L, <name>: Identifier) =
G, L, regular, L[name] G, L, regular, L[$name]
E(G, L, <fname(arg1, ..., argn)>: FunctionCall) = E(G, L, <fname(arg1, ..., argn)>: FunctionCall) =
G1, L1, vn = E(G, L, argn) G1, L1, vn = E(G, L, argn)
... ...
G(n-1), L(n-1), v2 = E(G(n-2), L(n-2), arg2) G(n-1), L(n-1), v2 = E(G(n-2), L(n-2), arg2)
Gn, Ln, v1 = E(G(n-1), L(n-1), arg1) Gn, Ln, v1 = E(G(n-1), L(n-1), arg1)
Let <function fname (param1, ..., paramn) -> ret1, ..., retm block> Let <function fname (param1, ..., paramn) -> ret1, ..., retm block>
be the function of name fname visible at the point of the call. be the function of name $fname visible at the point of the call.
Let L' be a new local state such that Let L' be a new local state such that
L'[parami] = vi and L'[reti] = 0 for all i. L'[$parami] = vi and L'[$reti] = 0 for all i.
Let G'', L'', rv1, ..., rvm = E(Gn, L', block) Let G'', L'', rv1, ..., rvm = E(Gn, L', block)
G'', Ln, rv1, ..., rvm G'', Ln, rv1, ..., rvm
E(G, L, l: HexLiteral) = G, L, hexString(l), E(G, L, l: HexLiteral) = G, L, hexString(l),
where hexString decodes l from hex and left-aligns in into 32 bytes where hexString decodes l from hex and left-aligns it into 32 bytes
E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l), E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l),
where utf8EncodeLeftAligned performs a utf8 encoding of l where utf8EncodeLeftAligned performs a utf8 encoding of l
and aligns it left into 32 bytes and aligns it left into 32 bytes