~vdupras/duskos

9217305b5a7681c2be8f0431fb8643eb02a839b1 — Virgil Dupras a month ago 415c208
cc: rename Result struct to ExprOp

Result doesn't seem like such a good name after all. Also, clarify doc/cc/impl.
6 files changed, 154 insertions(+), 148 deletions(-)

M fs/comp/c/egen.fs
M fs/comp/c/expr.fs
M fs/comp/c/fgen.fs
M fs/comp/c/gen.fs
M fs/comp/c/ptype.fs
M fs/doc/cc/impl.txt
M fs/comp/c/egen.fs => fs/comp/c/egen.fs +82 -82
@@ 21,27 21,27 @@ Arena :new structbind Arena _litarena
$400 const MAXLITSZ

\ parseExpression forward declaration is in glob.fs, it's needed in ptype.fs
alias noop parseFactor ( tok -- res ) \ forward declaration
alias noop parseFactor ( tok -- eop ) \ forward declaration

: unaryop doer ' , ' , does> ( res 'op -- res )
  over Result :isconst? if
    CELLSZ + @ over Result arg swap execute over to Result arg
    else @ over Result :?>W execute then ( res ) ;
: unaryop doer ' , ' , does> ( eop 'op -- eop )
  over ExprOp :isconst? if
    CELLSZ + @ over ExprOp arg swap execute over to ExprOp arg
    else @ over ExprOp :?>W execute then ( eop ) ;
unaryop _neg, -W, neg
: _ -1 i) ^, ;
unaryop _not, _ ^
: _ W=0>Z, Z) C>W, ;
unaryop _!, _ not

: _&, Result :& ;
: _*, Result :* ;
: _&, ExprOp :& ;
: _*, ExprOp :* ;

: _ ( res incsz -- res )
  over Result :*arisz *
  over Result :isW? if W+n, else over Result :hal# [+n], then ;
: _ ( eop incsz -- eop )
  over ExprOp :*arisz *
  over ExprOp :isW? if W+n, else over ExprOp :hal# [+n], then ;
: _++, 1 _ ; : _--, -1 _ ;

UOPSCNT wordtbl uoptbl ( res -- res )
UOPSCNT wordtbl uoptbl ( eop -- eop )
'w _neg, 'w _not,   'w _!,   'w _&,   'w _*,   'w _++,    'w _--,

\ For binops to resolve without problems, we want both operands to solve without


@@ 50,61 50,61 @@ UOPSCNT wordtbl uoptbl ( res -- res )

\ ops that can freely swap their operands
: _prep ( left right -- left halop )
  dup Result :hasW? if swap then over Result :?>W Result :hal$ ;
  dup ExprOp :hasW? if swap then over ExprOp :?>W ExprOp :hal$ ;
: _*, _prep *, ; : _&, _prep &, ; : _^, _prep ^, ; : _|, _prep |, ;
: _&&, _prep W=0>Z, 0 Z) branchC, swap @, W=0>Z, [compile] then NZ) C>W, ;
: _||, _prep |, W=0>Z, NZ) C>W, ;

: _arimul ( left right -- left right*n )
  over Result :*arisz over Result :*arisz <> if
    over Result :*arisz 1 = if swap then \ left has mutiplier
    over Result :*arisz over Result :*n ( left right*arisz ) then ;
  over ExprOp :*arisz over ExprOp :*arisz <> if
    over ExprOp :*arisz 1 = if swap then \ left has mutiplier
    over ExprOp :*arisz over ExprOp :*n ( left right*arisz ) then ;

\ TODO: generalize CDecl hamonization (:copymeta below)
: _+, ( left right -- res )
  over rot> _arimul _prep +, tuck Result :copymeta ;
: _+, ( left right -- eop )
  over rot> _arimul _prep +, tuck ExprOp :copymeta ;

\ ops that can't freely swap their operands
: _prep ( left right -- left halop )
  Result :?freeCurrentW over Result :?>W Result :hal$ ;
  ExprOp :?freeCurrentW over ExprOp :?>W ExprOp :hal$ ;
: _/, _prep /, ; : _%, _prep %, ;
: _<<, _prep <<, ; : _>>, _prep >>, ;

: _ptr-, ( left right -- res )
  _prep -, CELLSZ over Result :/n dup Result :toint ;
: _-, ( left right -- res )
  over Result :*arisz over Result :*arisz tuck = swap CELLSZ = and if
: _ptr-, ( left right -- eop )
  _prep -, CELLSZ over ExprOp :/n dup ExprOp :toint ;
: _-, ( left right -- eop )
  over ExprOp :*arisz over ExprOp :*arisz tuck = swap CELLSZ = and if
    _ptr-, else _arimul _prep -, then ;

: _prep ( left right -- res halop )
  Result :?freeCurrentW Result :?>W$ dup Result :hal# <>) ;
: _prep ( left right -- eop halop )
  ExprOp :?freeCurrentW ExprOp :?>W$ dup ExprOp :hal# <>) ;
: _=, _prep @, ;    : _-=, _prep -, ;
: _*=, _prep *, ;   : _/=, _prep /, ;   : _%=, _prep %, ;
: _&=, _prep &, ;   : _^=, _prep ^, ;   :  _|=, _prep |, ;
: _<<=, _prep <<, ; : _>>=, _prep >>, ;

: _+=,
  over Result :*arisz dup 1 > if over Result :*n else drop then
  over ExprOp :*arisz dup 1 > if over ExprOp :*n else drop then
  _prep +, ;

\ To avoid W juggling, we check if our right operand is W. If it is, no need
\ for juggling, all we need is to invert the condition we use.
\ data: unsigned cond, unsigned swapped cond, signed cond, signed swapped cond
: cmpop doer 4 for ' execute , next does> ( left right 'conds )
  over Result :unsigned? not if CELLSZ << + then
  over Result :isW? if CELLSZ + @ >r swap else @ >r then ( left right )
  Result :?freeCurrentW over Result :?>W Result :hal$ compare, r> C>W, ;
  over ExprOp :unsigned? not if CELLSZ << + then
  over ExprOp :isW? if CELLSZ + @ >r swap else @ >r then ( left right )
  ExprOp :?freeCurrentW over ExprOp :?>W ExprOp :hal$ compare, r> C>W, ;
cmpop _==, Z) Z) Z) Z)       cmpop _!=, NZ) NZ) NZ) NZ)
cmpop _<, <) >=) s<) s>=)    cmpop _<=, <=) >) s<=) s>)
cmpop _>, >) <=) s>) s<=)   cmpop _>=, >=) <) s>=) s<)

: _?, ( left right -- res )
: _?, ( left right -- eop )
  nextt ':' expectChar nextt parseExpression ( cond trueres falseres )
  swap Result :?>W Result :?freeCurrentW ( cond falseres )
  swap Result :>W$ PS- W=0>Z, 0 Z) branchC,
    drop, [compile] else nip, over Result :>W [compile] then ;
  swap ExprOp :?>W ExprOp :?freeCurrentW ( cond falseres )
  swap ExprOp :>W$ PS- W=0>Z, 0 Z) branchC,
    drop, [compile] else nip, over ExprOp :>W [compile] then ;

BOPSCNT wordtbl boptbl ( left right -- res )
BOPSCNT wordtbl boptbl ( left right -- eop )
'w _+,   'w _-,    'w _*,    'w _/,    'w _%,    'w _<<,   'w _>>,   'w _<,
'w _>,   'w _<=,   'w _>=,   'w _==,   'w _!=,   'w _&,    'w _^,    'w _|,
'w _&&,  'w _||,   'w _=,    'w _+=,   'w _-=,   'w _*=,   'w _/=,   'w _%=,


@@ 118,46 118,46 @@ BOPSCNT wordtbl boptbl ( left right -- res )
\ need to restore psoff to its initial level *without actually adjusting*
\ because it's the callee's responsibility to free its arguments.
code _callA branchA,
: _funcall ( res -- res )
  Result :?freeCurrentW
: _funcall ( eop -- eop )
  ExprOp :?freeCurrentW
  psoff dup >r >r \ V1=psinitlvl V2=pslvl
  ')' readChar? not if begin ( funcres tok )
    parseExpression Result :?>W
    parseExpression ExprOp :?>W
    psoff V2 - ?dup if dup ps+, neg to+ psoff then CELLSZ to+ V2
    ',' readChar? while nextt repeat ')' expectChar then ( funcres )
  dup Result cdecl dup CDecl :constfuncsig? if ( funcres cdecl )
  dup ExprOp cdecl dup CDecl :constfuncsig? if ( funcres cdecl )
    nip dup CDecl offset execute,
    else swap Result :hal$ A>) @, ['] _callA execute, then ( cdecl )
    else swap ExprOp :hal$ A>) @, ['] _callA execute, then ( cdecl )
  rdrop r> ( psinitlvl ) to psoff
  Result currentW ?dup if PS- Result :release then
  ExprOp currentW ?dup if PS- ExprOp :release then
  \ TODO: arilvl of fun rettype isn't properly preserved here
  CDecl :rettype if PS+ Result :W else Result :none then ;
  CDecl :rettype if PS+ ExprOp :W else ExprOp :none then ;

: _incdec, ( res incsz -- res )
  Result :?freeCurrentW over Result :*arisz *
  swap Result :hal$ dup @, [+n], Result :W ;
: _incdec, ( eop incsz -- eop )
  ExprOp :?freeCurrentW over ExprOp :*arisz *
  swap ExprOp :hal$ dup @, [+n], ExprOp :W ;

: _arrow ( res -- res )
  dup Result cdecl nextt ( res cdecl name )
  swap CDecl type CDecl :find# tuck CDecl offset ( field-cdecl res offset )
  over Result :?>W i) +, ( field-cdecl res ) tuck Result :cdecl!
  dup Result cdecl CDecl :reference? not if
    1 over to+ Result lvl Result :* then ;
: _arrow ( eop -- eop )
  dup ExprOp cdecl nextt ( eop cdecl name )
  swap CDecl type CDecl :find# tuck CDecl offset ( field-cdecl eop offset )
  over ExprOp :?>W i) +, ( field-cdecl eop ) tuck ExprOp :cdecl!
  dup ExprOp cdecl CDecl :reference? not if
    1 over to+ ExprOp lvl ExprOp :* then ;

\ parses, if possible, a postfix operator. If none, this is a noop.
\ We parse postfix args as long as there are any.
: parsePostfixOp ( res -- res )
: parsePostfixOp ( eop -- eop )
  nextt case ( )
    '[' of isChar?^ \ x[y] is the equivalent of *(x+y)
      nextt parseExpression _+,
      dup Result cdecl CDecl :structdot? not if Result :* then
      dup ExprOp cdecl CDecl :structdot? not if ExprOp :* then
      nextt ']' expectChar parsePostfixOp endof
    '(' of isChar?^ _funcall parsePostfixOp endof
    S" ->" of s=
      dup Result cdecl CDecl :structarrow? _assert
      dup ExprOp cdecl CDecl :structarrow? _assert
      _arrow parsePostfixOp endof
    '.' of isChar?^
      dup Result cdecl CDecl :structdot? _assert
      dup ExprOp cdecl CDecl :structdot? _assert
      _arrow parsePostfixOp endof
    S" ++" of s= 1 _incdec, endof
    S" --" of s= -1 _incdec, endof


@@ 168,15 168,15 @@ code _callA branchA,
\ want to support the possibility that some of these elements use _litarena
\ themselves (for example, string literals). *then*, we write.
MAXLITSZ Stack :new structbind Stack _list
: parseList ( -- res )
: parseList ( -- eop )
  _list :empty begin ( )
    nextt parseFactor dup Result type case ( res )
      Result CONST of = Result :const# endof
      Result CDECL of =
        Result cdecl dup CDecl :isglobal? _assert CDecl offset endof
    nextt parseFactor dup ExprOp type case ( eop )
      ExprOp CONST of = ExprOp :const# endof
      ExprOp CDECL of =
        ExprOp cdecl dup CDecl :isglobal? _assert CDecl offset endof
      _err endcase ( n ) _list :push
    ',' readChar? not until ( tok )
  '}' expectChar _list :self Result ARRAY Result :new ;
  '}' expectChar _list :self ExprOp ARRAY ExprOp :new ;

\ A factor can be:
\ 1. A constant


@@ 189,12 189,12 @@ MAXLITSZ Stack :new structbind Stack _list
\ 8. a typecast followed by a factor
\ 9. NULL
\ 10. sizeof()
: _ ( tok -- res ) case ( )
: _ ( tok -- eop ) case ( )
    '(' of isChar?^
      \ can be an expression or a typecast
      nextt dup parseType if ( tok type )
        nip parseDeclarator read) nextt parseFactor ( type res )
        tuck Result :typecast
        nip parseDeclarator read) nextt parseFactor ( type eop )
        tuck ExprOp :typecast
        else ( tok ) parseExpression read) parsePostfixOp then
    endof
    '"' of isChar?^ MAXLITSZ _litarena :[


@@ 202,41 202,41 @@ MAXLITSZ Stack :new structbind Stack _list
      ccin dup '0' = if
        drop 1+ 0 c, \ null terminated
        else ccputback here over - 1- over c! then ( saddr )
      _litarena :] drop ( "a ) Result :const endof
      _litarena :] drop ( "a ) ExprOp :const endof
    '{' of isChar?^ parseList endof
    S" pspop" of s=
      read( read) Result :?freeCurrentW
      0 PSP+) @, PS+ Result :W parsePostfixOp endof
    S" NULL" of s= 0 Result :const endof
      read( read) ExprOp :?freeCurrentW
      0 PSP+) @, PS+ ExprOp :W parsePostfixOp endof
    S" NULL" of s= 0 ExprOp :const endof
    S" sizeof" of s=
      read( nextt parseType _assert typesize Result :const read) endof
      read( nextt parseType _assert typesize ExprOp :const read) endof
    of uopid ( opid )
      nextt parseFactor ( opid res ) uoptbl rot wexec endof
      nextt parseFactor ( opid eop ) uoptbl rot wexec endof
    of isIdent? \ lvalue, FunCall or macro
      r@ findIdent ?dup _assert Result :cdecl parsePostfixOp endof
    r@ parse if Result :const else _err then
      r@ findIdent ?dup _assert ExprOp :cdecl parsePostfixOp endof
    r@ parse if ExprOp :const else _err then
  endcase ;
current ' parseFactor realias

: bothconst? ( left right -- f ) Result :isconst? swap Result :isconst? and ;
: bothconst? ( left right -- f ) ExprOp :isconst? swap ExprOp :isconst? and ;

: ?constApply ( left right opid -- left right opid 0 | res 1 ) >r \ V1=opid
: ?constApply ( left right opid -- left right opid 0 | eop 1 ) >r \ V1=opid
  2dup bothconst? if r@ applyConstBinop 1 else ( left right )
    dup Result :isone? r@ neutralbyrone? and if drop 1 else
    over Result :isone? r@ neutralbyone? and if nip 1 else
    dup Result :iszero? r@ nulledbyzero? and if nip 1 else
    over Result :iszero? r@ nulledbyzero? and if drop 1 else
    dup Result :iszero? r@ neutralbyrzero? and if drop 1 else
    over Result :iszero? r@ neutralbyzero? and if nip 1 else 0 then
    then then then then then then ( left right opid 0 | res 1 )
    dup ExprOp :isone? r@ neutralbyrone? and if drop 1 else
    over ExprOp :isone? r@ neutralbyone? and if nip 1 else
    dup ExprOp :iszero? r@ nulledbyzero? and if nip 1 else
    over ExprOp :iszero? r@ nulledbyzero? and if drop 1 else
    dup ExprOp :iszero? r@ neutralbyrzero? and if drop 1 else
    over ExprOp :iszero? r@ neutralbyzero? and if nip 1 else 0 then
    then then then then then then ( left right opid 0 | eop 1 )
  dup if rdrop else r> swap then ;

: applyBinop ( left right opid -- res )
: applyBinop ( left right opid -- eop )
  ?constApply not if boptbl swap wexec then ;

\ Parse the "right" part of an expression with the leftmost factor and leftmost
\ binary operator already parsed.
: parseRExpr ( left binop -- res ) >r >r \ V1=binop V2=left
: parseRExpr ( left binop -- eop ) >r >r \ V1=binop V2=left
  nextt parseFactor nextt ( right tok )
  dup bopid if ( right tok opright )
    \ another binop! let's apply precedence rules.


@@ 251,7 251,7 @@ current ' parseFactor realias
\ An expression can be 2 things:
\ 1. a factor
\ 2. A binaryop containing two expressions.
: _ ( tok -- res ) \ parseExpression
: _ ( tok -- eop ) \ parseExpression
  \ first tok is always a factor
  parseFactor nextt ( left tok )
  dup bopid if ( left tok binop )

M fs/comp/c/expr.fs => fs/comp/c/expr.fs +18 -18
@@ 11,31 11,31 @@

NULLSTR TYPE_UINT CDecl :new const UintCDecl

struct[ Result
struct[ ExprOp
  0 const NONE  \ Nothing (probably a released W)
  1 const CONST \ Is a constant (value in arg)
  2 const W     \ Value in W register
  3 const CDECL
  4 const PS    \ Result pushed to PS, offset in arg
  5 const ARRAY \ Result is a constant array in a Stack. arg is a pointer to it.
  4 const PS    \ ExprOp pushed to PS, offset in arg
  5 const ARRAY \ ExprOp is a constant array in a Stack. arg is a pointer to it.
  6 const REF   \ & operator applied to target
  7 const DEREF \ * operator applied to target

  sfield type
  sfield arg    \ numerical arg, for PS, CONST, ARRAY
  sfield target \ another Result. for REF, DEREF
  sfield target \ another ExprOp. for REF, DEREF
  sfield cdecl
  sfield lvl    \ Current indirection levels to the base type

  \ There can only be one result using W at once. Whenever a W result is
  \ There can only be one ExprOp using W at once. Whenever a W ExprOp is
  \ created, it takes the lock. If it's already taken, there's an error.
  0 value currentW \ link to Result
  0 value currentW \ link to ExprOp
  : :Wfree# currentW if abort" W is already taken!" then ;

  : :new ( arg type -- res ) SZ syspad :[ , , 0 , UintCDecl , 0 , syspad :] ;
  : :none ( -- res ) 0 NONE :new ;
  : :const ( n -- res ) CONST :new ;
  : :W ( -- res ) :Wfree# 0 W :new dup to currentW ;
  : :new ( arg type -- eop ) SZ syspad :[ , , 0 , UintCDecl , 0 , syspad :] ;
  : :none ( -- eop ) 0 NONE :new ;
  : :const ( n -- eop ) CONST :new ;
  : :W ( -- eop ) :Wfree# 0 W :new dup to currentW ;
  : :isW? ( self -- f ) type W = ;
  : :hasW? ( self -- f )
    dup :isW? if drop 1 else


@@ 55,20 55,20 @@ struct[ Result
    dup cdecl CDecl :. spc> lvl . spc> ." W=" currentW bool . ;
  : :. _:. nl> ;
  : :W! ( self -- ) dup to currentW W swap to type ;
  \ Copy meta information (basesz, lvl from "other" result
  \ Copy meta information (basesz, lvl from "other" ExprOp
  : :copymeta ( other self -- )
    over cdecl over to cdecl
    swap lvl swap to lvl ;
  : :cdecl! ( cdecl self -- ) over CDecl :lvl over to lvl to cdecl ;
  : :& ( self -- res )
  : :& ( self -- eop )
    dup type DEREF = if target exit then
    dup cdecl CDecl :reference? if exit then
    dup :iscdecl? _assert
    0 REF :new ( tgt res ) 2dup :copymeta tuck to target 1 over to+ lvl ;
  : :* ( self -- res )
    0 REF :new ( tgt eop ) 2dup :copymeta tuck to target 1 over to+ lvl ;
  : :* ( self -- eop )
    0 DEREF :new 2dup :copymeta tuck to target
    dup lvl if -1 over to+ lvl else dup cdecl CDecl type over :cdecl! then ;
  : :cdecl ( cdecl -- res ) 0 CDECL :new ( cdecl res ) tuck :cdecl! ;
  : :cdecl ( cdecl -- eop ) 0 CDECL :new ( cdecl eop ) tuck :cdecl! ;
  : :basesz cdecl CDecl type typesize ;
  : :unsigned? cdecl typeunsigned? ;
  : :nb) ( halop self -- halop ) dup lvl if drop else :basesz nb) then ;


@@ 124,7 124,7 @@ BOPSCNT wordtbl _tbl ( a b -- n )
'w and?  'w or?    'w _err   'w _err   'w _err   'w _err   'w _err   'w _err
'w _err  'w _err   'w _err   'w _err   'w _err   'w _err

: applyConstBinop ( left right opid -- res )
  >r swap Result :const# swap Result :const# _tbl r> wexec Result :const ;
: applyConstBinop ( left right opid -- eop )
  >r swap ExprOp :const# swap ExprOp :const# _tbl r> wexec ExprOp :const ;

: expr$ psneutral Result currentW ?dup if Result :release then ;
: expr$ psneutral ExprOp currentW ?dup if ExprOp :release then ;

M fs/comp/c/fgen.fs => fs/comp/c/fgen.fs +14 -14
@@ 26,13 26,13 @@ alias noop parseStatement ( tok -- ) \ forward declaration
: parseStatements ( -- )
  begin '}' readChar? not while parseStatement repeat ;

: emitRet ( res -- ) Result :>W$ expr$ _postlude exit, ;
: emitRet ( eop -- ) ExprOp :>W$ expr$ _postlude exit, ;
: emitNullRet ( -- ) _postlude drop, exit, ;
: _return \ empty returns are allowed
  ';' readChar? not if parseExpression emitRet read; else emitNullRet then ;

: _if
  read( nextt parseExpression Result :?>W$ read) expr$ W=0>Z, 0 Z) branchC,
  read( nextt parseExpression ExprOp :?>W$ read) expr$ W=0>Z, 0 Z) branchC,
  nextt parseStatement
  nextt dup S" else" s= if ( jump_addr tok )
    drop [compile] else nextt parseStatement


@@ 42,18 42,18 @@ alias noop parseStatement ( tok -- ) \ forward declaration
: _for
  _breaks :count >r _conts :count >r
   \ initialization
  read( ';' readChar? not if parseExpression Result :release read; then
  here nextt parseExpression Result :?>W$ read;
  read( ';' readChar? not if parseExpression ExprOp :release read; then
  here nextt parseExpression ExprOp :?>W$ read;
  expr$ W=0>Z, 0 Z) branchC, 0 branch, ( caddr cjmpz cjmp ) \ control
  rot here ')' readChar? not if ( cjmpz cjmp caddr aaddr )
    parseExpression Result :release read) then
    parseExpression ExprOp :release read) then
  swap [compile] again ( cjmpz cjmp aaddr ) \ adjustment
  swap [compile] then nextt parseStatement ( cjmpz aaddr )
  r> resolvecontinues
  [compile] again [compile] then r> resolvebreaks ;

: _pspush
  read( nextt parseExpression Result :>W$ psneutral dup, read) read; ;
  read( nextt parseExpression ExprOp :>W$ psneutral dup, read) read; ;

: _break [compile] ahead _breaks :push read; ;



@@ 62,7 62,7 @@ alias noop parseStatement ( tok -- ) \ forward declaration
: _while
  _breaks :count >r _conts :count >r
  here read( nextt parseExpression read)
  Result :?>W$ expr$ W=0>Z, 0 Z) branchC,
  ExprOp :?>W$ expr$ W=0>Z, 0 Z) branchC,
  nextt parseStatement ( tgt jmp )
  r> resolvecontinues
  swap [compile] again [compile] then r> resolvebreaks ;


@@ 73,7 73,7 @@ alias noop parseStatement ( tok -- ) \ forward declaration
  r> resolvecontinues
  nextt S" while" s= _assert
  read( nextt parseExpression read)
  Result :?>W$ expr$ W=0>Z, NZ) branchC, drop
  ExprOp :?>W$ expr$ W=0>Z, NZ) branchC, drop
  read; r> resolvebreaks ;

code _lookup ( nref lookup -- )


@@ 100,13 100,13 @@ MAXSWITCHCASES << Stack :new structbind Stack _cases
: _switch
  _breaks :count >r \ V1=breakcnt
  read( nextt parseExpression read)
  Result :?>W$ dup, CELLSZ _litarena :allot dup m) @, >r \ V2='lookup
  ExprOp :?>W$ dup, CELLSZ _litarena :allot dup m) @, >r \ V2='lookup
  compile _lookup [compile] ahead >r \ V3=defjump
  nextt '{' expectChar nextt begin ( tok )
    dup '}' isChar? not while ( tok )
    dup S" default" s= not while ( tok )
    dup S" case" s= if
      drop nextt parseExpression Result :const# _cases :push here _cases :push
      drop nextt parseExpression ExprOp :const# _cases :push here _cases :push
      nextt ':' expectChar ( )
      else parseStatement then ( ) nextt repeat ( tok ) \ default
    r> ( defjump ) [compile] then nextt ':' expectChar parseStatements


@@ 127,7 127,7 @@ MAXSWITCHCASES << Stack :new structbind Stack _cases
0 value _laststmtid
: _ ( tok -- ) \ parseStatement
  dup statementnames sfind dup >r dup 0< if ( tok -1 ) \ V1=stmtidx
    drop parseExpression Result :release expr$ read;
    drop parseExpression ExprOp :release expr$ read;
    else nip statementhandler swap wexec then
  r> to _laststmtid ;
current ' parseStatement realias


@@ 152,9 152,9 @@ current ' parseStatement realias
  dup _locvars ?dup if CDecl :append else to _locvars then begin ( cdecl )
    '=' readChar? if ( cdecl )
      _initcode not if here to _initcode then
    nextt parseExpression ( cdecl res )
    dup Result :isarray? if Result arg over _copyArray else
      Result :>W$ dup Result :cdecl Result :hal# !, then
    nextt parseExpression ( cdecl eop )
    dup ExprOp :isarray? if ExprOp arg over _copyArray else
      ExprOp :>W$ dup ExprOp :cdecl ExprOp :hal# !, then
    expr$ nextt then ( cdecl tok )
  dup ';' isChar? not while ( cdecl tok )
  ',' expectChar CDecl type parseDeclarator ( cdecl )

M fs/comp/c/gen.fs => fs/comp/c/gen.fs +2 -2
@@ 24,8 24,8 @@ require /sys/scratch.fs
  here over to CDecl offset ( cdecl )
  '=' readChar? if ( cdecl )
    nextt parseExpression case ( cdecl )
      of Result :isconst? r@ Result arg , endof
      of Result :isarray? r@ Result arg over CDecl :elemsize writeStack endof
      of ExprOp :isconst? r@ ExprOp arg , endof
      of ExprOp :isarray? r@ ExprOp arg over CDecl :elemsize writeStack endof
      _err endcase
  else to nexttputback dup CDecl :size allot then ( cdecl )
  ',' readChar? if

M fs/comp/c/ptype.fs => fs/comp/c/ptype.fs +9 -9
@@ 31,7 31,7 @@ alias _err parseDeclarator ( type -- cdecl ) \ forward declaration
: _post ( cdecl -- cdecl )
  begin ( cdecl ) nextt case
    '[' of isChar?^
      nextt parseExpression Result :const#
      nextt parseExpression ExprOp :const#
      nextt ']' expectChar ( cdecl nbelem ) over to CDecl nbelem endof
    '(' of isChar?^
      dup CDecl :funcsig! STORAGE_PS to@! curstorage >r


@@ 66,18 66,18 @@ current ' parseDeclarator realias

: _parseStruct ( -- cdecl )
  nextt dup isIdent? if nextt else NULLSTR swap then
  '{' expectChar ( name ) TYPE_VOID CDecl :new ( res )
  '{' expectChar ( name ) TYPE_VOID CDecl :new ( eop )
  dup CDecl :struct!  dup addTypedef
  STORAGE_NONE to@! curstorage >r \ V1=curstorage
  0 >r dup begin ( res prev ) \ V2=offset
    '}' readChar? not while ( res prev tok )
    parseType _assert parseDeclarator begin ( res prev new )
      tuck swap to CDecl nexttype ( res new )
  0 >r dup begin ( eop prev ) \ V2=offset
    '}' readChar? not while ( eop prev tok )
    parseType _assert parseDeclarator begin ( eop prev new )
      tuck swap to CDecl nexttype ( eop new )
      V2 over to CDecl offset
      dup typesize to+ V2
      ';' readChar? not while ( res prev tok )
      ',' expectChar dup CDecl type parseDeclarator repeat ( res prev )
  repeat ( res prev ) rdrop drop r> to curstorage ;
      ';' readChar? not while ( eop prev tok )
      ',' expectChar dup CDecl type parseDeclarator repeat ( eop prev )
  repeat ( eop prev ) rdrop drop r> to curstorage ;

\ parse a type from stream, starting with "tok". This only parses the "type"
\ part without the "*" part or the name part. The result can be a "base" type

M fs/doc/cc/impl.txt => fs/doc/cc/impl.txt +29 -23
@@ 29,7 29,7 @@ When, for example, parsing "a + b * c;", it:
6. Parses "c", determines it lives at RSP+8.
7. Again, looks ahead. End of statement? good, we don't recurse.
8. Compiles "RSP) 4 +) @, RSP) 8 +) *,". Intermediate result in W.
9. We unwind recursion and let the caller know, through the Result structure
9. We unwind recursion and let the caller know, through the ExprOp structure
   (see below), that the right hand of the "+" is in the W register.
10. We compile "RSP) +,", final result is in W.



@@ 70,7 70,7 @@ C code interacts with basically 4 types of locations:
In the first 3 cases, interactions with those locations first require the C code
to declare those locations through declarators ("int x;", etc.). These
declarators, when parsed, are stored in a structure named CDecl which lives in
type.fs. The last case doesn't go through CDecl and is managed in the Result
type.fs. The last case doesn't go through CDecl and is managed in the ExprOp
structure (see below).

This type is a linked list which sees multiple uses. For Struct types, fields


@@ 124,13 124,13 @@ void baz(bar *a) { bar x[42]; char y; }
  name=x   type=CDecl ptr  storage=STORAGE_RS lvl=0 nbelem=42 offset=0;
  name=y   type=$1         storage=STORAGE_RS lvl=0 nbelem=0 offset=42*(42*2+4)

## The Result structure
## The ExprOp structure

C expressions are composed of operators which deal with one or two operands and
return one result. Those operands and results are passed around as the Result
return one result. Those operands and results are passed around as the ExprOp
structure defined in expr.fs.

Result structures are ephemeral and only used in the context of generating an
ExprOp structures are ephemeral and only used in the context of generating an
expression. They're discarded afterwards. For this reason, they're created in
the system scratchpad. No need to worry about freeing them.



@@ 145,34 145,33 @@ In complex expressions, we need to keep multiple intermediates around. For that,
we use PS as a temporary location, which we'll refer to later as we resolve the
expression. That's your fourth building block right there.

These are the four main result types, which all have constants in the Result
namespace: CONST, W, CDECL, PS. There are 3 other types for special contexts,
A, NONE and ARRAY.
These are the four main result types, which all have constants in the ExprOp
namespace: CONST, W, CDECL, PS. Other types are for special circumstances.

The goal of the Result structure is to keep track of where the operands live at
The goal of the ExprOp structure is to keep track of where the operands live at
so that we can generate the proper HAL operands to feed to the HAL operators
we're generating.

As illustrated earlier, the expression generation mechanism is recursive so we
always start out at the deep end of the expression, with either a constant or a
CDecl created in parseFactor. It is at this point that the Result's "base"
attributes, "basesz", "blvl" and "unsigned?" are set.
CDecl created in parseFactor. It is at this point that the ExprOp's "base"
attributes, "cdecl" and "lvl" are set.

When an operator is applied, we generally end up with a Result "upgraded" to W.
Then, we either bring in a new Result in the mix (binary operator) or tranform
the existing Result (unary operator) which already lives in W.
When an operator is applied, we generally end up with a ExprOp "upgraded" to W.
Then, we either bring in a new ExprOp in the mix (binary operator) or tranform
the existing ExprOp (unary operator) which already lives in W.

Upgrading results to W needs to be done carefully because there's only one such
register. Because of that, the Result structure keeps track of whether W is used
register. Because of that, the ExprOp structure keeps track of whether W is used
globally through its "currentW" static variable. For this reason, it's important
to "release" Result structs when they're finally consumed.
to "release" ExprOp structs when they're finally consumed.

Then, this result is either used as an operand of an assign operatorm used as a
Then, this ExprOp is either used as an operand of an assign operatorm used as a
call argument, or used as a return value. The cycle is complete.

## :hal# vs :hal$

The main purpose of a Result is to, at some point, produce a HAL operand (or to
The main purpose of a ExprOp is to, at some point, produce a HAL operand (or to
"be W"). This is done through the :hal# ( self -- halop ) method. This method
works for CONST, CDECL and PS types, but not for W because even though it's
technically possible to produce a HAL operand for W (the "W)" operand), this


@@ 182,9 181,9 @@ isn't ever supposed to happen. That's why there's this "#" at the end (for
This method produces the right halop for the right context, including
indirection levels and operand size.

Once a HAL operand has been generated, we usually don't reuse the Result because
Once a HAL operand has been generated, we usually don't reuse the ExprOp because
the resulting operation lives in W. To protect us from ourselves, it's better to
use the ":hal$" method which additionally releases the Result (sets it to NONE).
use the ":hal$" method which additionally releases the ExprOp (sets it to NONE).

## A register usage



@@ 192,7 191,14 @@ The A register is used in some places during code generation, but only in
contexts that resolve immediately. Results stored in there can't be, for
example, still stored when a function call is generated.

## Result indirection levels
In binary operations, resolution order of the two operands is important with
regards to the A register. Whichever operand ends up in W, we need to resolve
it first because this resolution might use the A register. If the other operand,
the one ending as the HAL operand argument, generates its HAL operand first and
turns out to live in the A register, W resolution might end up overwriting that
value.

## ExprOp indirection levels

An important challenge in the CC is to know when we've "hit bottom" in the
indirection chain, that is, the moment at which the instruction acquires the


@@ 202,9 208,9 @@ A CDecl has a "lvl" attributes which indicate the indirection level of the
declaration (with a ":lvl" helper that conditionally adds 1 to it for types that
"naturally" yield references), so that's our starting point.

On top of that, the Result also maintain its own "lvl" variable. This is because
On top of that, the ExprOp also maintain its own "lvl" variable. This is because
some operators (such as *, &, -> etc.) change the indirection level. So, when a
Result is created from a CDecl (which is the majority of the cases), it inherits
ExprOp is created from a CDecl (which is the majority of the cases), it inherits
its "lvl" from "CDecl :lvl", and keeps track of indirection levels from there.

When that "lvl" hits 0, we know we've hit bottom, so that's when we apply width