2017-12-06 23:23:30 +00:00
package millfork.parser
2018-07-12 16:30:35 +00:00
import java.lang.Long.parseLong
2017-12-06 23:23:30 +00:00
import java.nio.file. { Files , Paths }
2018-07-05 23:05:24 +00:00
import java.util
2017-12-06 23:23:30 +00:00
import fastparse.all._
2020-07-31 15:08:44 +00:00
import fastparse.core.Parsed.Failure
2018-12-14 14:42:31 +00:00
import millfork.assembly.Elidability
2017-12-06 23:23:30 +00:00
import millfork.env._
2018-07-30 16:15:44 +00:00
import millfork.error. { ConsoleLogger , Logger }
2017-12-06 23:23:30 +00:00
import millfork.node._
2018-10-04 19:33:10 +00:00
import millfork.output. { DivisibleAlignment , MemoryAlignment , NoAlignment }
2020-07-31 15:08:44 +00:00
import millfork. { CompilationFlag , CompilationOptions , Confusables , SeparatedList }
2017-12-06 23:23:30 +00:00
2018-12-16 13:38:57 +00:00
import scala.collection.immutable.BitSet
2017-12-06 23:23:30 +00:00
/* *
* @author Karol Stasiak
*/
2018-07-23 11:11:53 +00:00
abstract class MfParser [ T ] ( fileId : String , input : String , currentDirectory : String , options : CompilationOptions , featureConstants : Map [ String , Long ] ) {
2017-12-06 23:23:30 +00:00
2018-07-05 23:05:24 +00:00
import MfParser._
2018-07-23 11:11:53 +00:00
var lastPosition = Position ( fileId , 1 , 1 , 0 )
2017-12-06 23:23:30 +00:00
var lastLabel = ""
2018-07-30 16:15:44 +00:00
protected val log : Logger = options . log
2017-12-06 23:23:30 +00:00
2018-08-03 11:23:37 +00:00
def allowIntelHexAtomsInAssembly : Boolean
2019-04-17 17:17:55 +00:00
val enableDebuggingOptions : Boolean = options . flag ( CompilationFlag . EnableInternalTestSyntax )
2019-04-15 17:45:26 +00:00
2020-12-01 02:20:31 +00:00
private def getCharacterNameSafe ( c : Char ) : String = {
try {
"U+%04X %s" . format ( c . toInt , Character . getName ( c ) )
} catch {
case _ : Throwable => c match {
case '\n' => "U+000A LINE FEED"
case '\r' => "U+000D CARRIAGE RETURN"
case _ => "U+%04X"
}
}
}
2020-07-31 15:08:44 +00:00
def toAst : Parsed [ Program ] = {
val parse = program . parse ( input + "\n\n\n" )
parse match {
2020-07-31 15:11:30 +00:00
case _ : Failure [ _ , _ ] =>
if ( lastPosition . cursor >= 0 && lastPosition . cursor < input . length ) {
val c = input ( lastPosition . cursor )
if ( c >= 0x100 || c < 0x20 || c == '`' ) {
2020-12-01 02:20:31 +00:00
log . error ( "Invalid character %s" . format ( getCharacterNameSafe ( c ) ) , Some ( lastPosition ) )
2020-07-31 15:11:30 +00:00
Confusables . map . get ( c ) match {
case Some ( ascii ) =>
log . info ( s" Did you mean: $ascii " )
case _ =>
}
2020-07-31 15:08:44 +00:00
}
}
case _ =>
}
parse
}
2017-12-06 23:23:30 +00:00
2020-12-01 02:21:04 +00:00
private val lineStarts : Array [ Int ] = ( - 1 +: input . zipWithIndex . filter ( _ . _1 == '\n' ) . map ( _ . _2 ) . map ( _ + 1 ) ) . toArray
2017-12-06 23:23:30 +00:00
def position ( label : String = "" ) : P [ Position ] = Index . map ( i => indexToPosition ( i , label ) )
def indexToPosition ( i : Int , label : String ) : Position = {
2018-07-05 23:05:24 +00:00
var lineNumber = util . Arrays . binarySearch ( lineStarts , i )
if ( lineNumber < 0 ) {
lineNumber = - lineNumber - 2
}
2020-12-01 02:21:04 +00:00
val columnNumber = i - lineStarts ( lineNumber ) + 1
2018-07-05 23:05:24 +00:00
lineNumber += 1
2018-07-23 11:11:53 +00:00
val newPosition = Position ( fileId , lineNumber , columnNumber , i )
2017-12-06 23:23:30 +00:00
if ( newPosition . cursor > lastPosition . cursor ) {
lastPosition = newPosition
lastLabel = label
}
newPosition
}
2019-10-31 11:08:45 +00:00
val comment : P [ Unit ] = P ( "//" ~ CharsWhile ( c => c != '\n' && c != '\r' , min = 0 ) ~ ( "\r\n" | "\r" | "\n" ) )
2019-10-21 23:06:51 +00:00
2019-10-31 11:08:45 +00:00
val semicolon : P [ Unit ] = P ( ";" ~ CharsWhileIn ( "; \t" , min = 0 ) ~ position ( "line break after a semicolon" ) . map ( _ => ( ) ) ~ ( comment | "\r\n" | "\r" | "\n" ) . opaque ( "<line break>" ) )
2019-10-21 23:06:51 +00:00
2019-10-31 11:08:45 +00:00
val semicolonComment : P [ Unit ] = P ( ";" ~ CharsWhile ( c => c != '\n' && c != '\r' && c != '{' && c != '}' , min = 0 ) ~ position ( "line break instead of braces" ) . map ( _ => ( ) ) ~ ( "\r\n" | "\r" | "\n" ) . opaque ( "<line break>" ) )
2019-10-21 23:06:51 +00:00
2019-10-31 11:08:45 +00:00
val AWS : P [ Unit ] = P ( ( CharIn ( " \t\n\r" ) | semicolon | comment ) . rep ( min = 0 ) ) . opaque ( "<any whitespace>" )
2019-10-21 23:06:51 +00:00
2019-10-31 11:08:45 +00:00
val AWS_asm : P [ Unit ] = P ( ( CharIn ( " \t\n\r" ) | semicolonComment | comment ) . rep ( min = 0 ) ) . opaque ( "<any whitespace>" )
val Before_EOL : P [ Unit ] = HWS ~ & ( "\r" | "\n" | ";" | "//" ) . opaque ( "<line break>" )
2019-10-21 23:06:51 +00:00
val EOL : P [ Unit ] = P ( HWS ~ ( "\r\n" | "\r" | "\n" | semicolon | comment ) . opaque ( "<first line break>" ) ~ AWS ) . opaque ( "<line break>" )
2019-11-04 13:26:01 +00:00
val EOL_asm : P [ Unit ] = P ( HWS ~ ( "\r\n" | "\r" | "\n" | semicolonComment | comment ) . opaque ( "<first line break>" ) ~ AWS_asm ) . opaque ( "<line break>" )
2019-10-21 23:06:51 +00:00
val EOLOrComma : P [ Unit ] = P ( HWS ~ ( "\r\n" | "\r" | "\n" | "," | semicolon | comment ) . opaque ( "<first line break or comma>" ) ~ AWS ) . opaque ( "<line break or comma>" )
val elidable : P [ Elidability . Value ] = ( ( "!" | "?" ) . ! ~/ HWS ) . ? . map {
case Some ( "?" ) => Elidability . Elidable
case Some ( "!" ) => Elidability . Volatile
case _ => Elidability . Fixed
}
val externFunctionBody : P [ Option [ List [ Statement ] ] ] = P ( "extern" ~/ PassWith ( None ) )
val bankDeclaration : P [ Option [ String ] ] = ( "segment" ~/ AWS ~/ "(" ~/ AWS ~/ identifier ~/ AWS ~/ ")" ~/ AWS ) . ?
val breakStatement : P [ Seq [ ExecutableStatement ] ] = ( "break" ~ ! letterOrDigit ~/ HWS ~ identifier . ? ) . map ( l => Seq ( BreakStatement ( l . getOrElse ( "" ) ) ) )
val continueStatement : P [ Seq [ ExecutableStatement ] ] = ( "continue" ~ ! letterOrDigit ~/ HWS ~ identifier . ? ) . map ( l => Seq ( ContinueStatement ( l . getOrElse ( "" ) ) ) )
val forDirection : P [ ForDirection . Value ] =
( "parallel" ~ HWS ~ "to" ) . ! . map ( _ => ForDirection . ParallelTo ) |
( "parallel" ~ HWS ~ "until" ) . ! . map ( _ => ForDirection . ParallelUntil ) |
"until" . ! . map ( _ => ForDirection . Until ) |
"to" . ! . map ( _ => ForDirection . To ) |
( "down" ~/ HWS ~/ "to" ) . ! . map ( _ => ForDirection . DownTo )
2019-10-31 11:10:18 +00:00
private def flags_ ( allowed : String * ) : P [ Set [ String ] ] = ( StringIn ( allowed : _ * ) . ! ~ SWS ) . rep ( min = 0 ) . map ( _ . toSet ) . opaque ( "<flags>" )
2019-10-21 23:06:51 +00:00
val variableFlags : P [ Set [ String ] ] = flags_ ( "const" , "static" , "volatile" , "stack" , "register" )
2020-11-18 09:08:58 +00:00
val functionFlags : P [ Set [ String ] ] = flags_ ( "extern" , "asm" , "inline" , "interrupt" , "macro" , "noinline" , "reentrant" , "kernal_interrupt" , "const" )
2019-10-21 23:06:51 +00:00
2020-04-03 22:45:09 +00:00
val codec : P [ TextCodecWithFlags ] = P ( position ( "text codec identifier" ) ~ identifier . ? . map ( _ . getOrElse ( "" ) ) ) . map { case ( position , encoding ) =>
val lenient = options . flag ( CompilationFlag . LenientTextEncoding )
encoding match {
case "" | "default" => TextCodecWithFlags ( options . platform . defaultCodec , nullTerminated = false , lengthPrefixed = false , lenient = lenient )
case "z" | "defaultz" => TextCodecWithFlags ( options . platform . defaultCodec , nullTerminated = true , lengthPrefixed = false , lenient = lenient )
case "p" | "pdefault" => TextCodecWithFlags ( options . platform . defaultCodec , nullTerminated = false , lengthPrefixed = true , lenient = lenient )
case "pz" | "pdefaultz" => TextCodecWithFlags ( options . platform . defaultCodec , nullTerminated = true , lengthPrefixed = true , lenient = lenient )
case "scr" => TextCodecWithFlags ( options . platform . screenCodec , nullTerminated = false , lengthPrefixed = false , lenient = lenient )
case "scrz" => TextCodecWithFlags ( options . platform . screenCodec , nullTerminated = true , lengthPrefixed = false , lenient = lenient )
case "pscr" => TextCodecWithFlags ( options . platform . screenCodec , nullTerminated = false , lengthPrefixed = true , lenient = lenient )
case "pscrz" => TextCodecWithFlags ( options . platform . screenCodec , nullTerminated = true , lengthPrefixed = true , lenient = lenient )
2020-04-30 23:31:54 +00:00
case _ => options . textCodecRepository . forName ( encoding , Some ( position ) , log )
2020-04-03 22:45:09 +00:00
}
2018-07-05 23:05:24 +00:00
}
2017-12-06 23:23:30 +00:00
// def operator: P[String] = P(CharsWhileIn("!-+*/><=~|&^", min=1).!) // TODO: only valid operators
2018-07-05 23:05:24 +00:00
val charAtom : P [ LiteralExpression ] = for {
2018-04-02 19:06:18 +00:00
p <- position ( )
2018-12-19 21:27:15 +00:00
c <- "'" ~/ CharPred ( c => c >= ' ' && c != '\'' && ! invalidCharLiteralTypes ( Character . getType ( c ) ) ) . rep . ! ~/ "'"
2020-04-03 22:45:09 +00:00
TextCodecWithFlags ( co , zt , pascal , lenient ) <- HWS ~ codec
2018-04-02 19:06:18 +00:00
} yield {
2018-07-27 22:58:20 +00:00
if ( zt ) {
2018-07-30 16:15:44 +00:00
log . error ( "Zero-terminated encoding is not a valid encoding for a character literal" , Some ( p ) )
2018-07-27 22:58:20 +00:00
}
2020-04-03 22:45:09 +00:00
if ( pascal ) {
log . error ( "Length-prefixed encoding is not a valid encoding for a character literal" , Some ( p ) )
}
2019-10-18 09:01:31 +00:00
co . encode ( options . log , Some ( p ) , c . codePoints ( ) . toArray . toList , options , lenient = lenient ) match {
2018-04-02 19:06:18 +00:00
case List ( value ) =>
LiteralExpression ( value , 1 )
case _ =>
2018-07-30 16:15:44 +00:00
log . error ( s" Character ` $c ` cannot be encoded as one byte " , Some ( p ) )
2019-10-17 21:23:57 +00:00
LiteralExpression ( co . stringTerminator . head , 1 )
2018-04-02 19:06:18 +00:00
}
}
2018-07-12 16:30:35 +00:00
//noinspection NameBooleanParameters
val variableAtom : P [ Expression ] = identifier . map { i =>
featureConstants . get ( i ) match {
2019-10-31 11:13:02 +00:00
case Some ( value ) => LiteralExpression ( value , size ( value , false , false , false , false , false , false , false ) )
2018-07-12 16:30:35 +00:00
case None => VariableExpression ( i )
}
}
2020-09-01 20:00:07 +00:00
val localLabelAtom : P [ Expression ] = ( "." ~ identifier ) . ! . map ( VariableExpression )
2018-07-27 22:58:20 +00:00
val textLiteral : P [ List [ Expression ] ] = P ( position ( ) ~ doubleQuotedString ~/ HWS ~ codec ) . map {
2020-04-03 22:45:09 +00:00
case ( p , s , TextCodecWithFlags ( co , zt , lp , lenient ) ) =>
var characters = co . encode ( options . log , None , s . codePoints ( ) . toArray . toList , options , lenient = lenient ) . map ( c => LiteralExpression ( c , 1 ) . pos ( p ) )
if ( lp ) {
val sizeof = co . stringTerminator . length
val codeUnitCount = characters . length / sizeof
val maxAllowed = 1. << ( 8 * sizeof ) - 1
if ( codeUnitCount > maxAllowed ) {
log . error ( s" Length-prefixed string too long, the length is $codeUnitCount , maximum allowed is $maxAllowed " , Some ( p ) )
}
characters = ( 0 until sizeof ) . map ( i => LiteralExpression ( codeUnitCount . >>> ( 8 * i ) . & ( 0xff ) , 1 ) ) . toList ++ characters
}
if ( zt ) characters ++= co . stringTerminator . map ( nul => LiteralExpression ( nul , 1 ) )
characters
2018-07-27 22:58:20 +00:00
}
val textLiteralAtom : P [ TextLiteralExpression ] = textLiteral . map ( TextLiteralExpression )
2018-12-16 13:38:57 +00:00
val literalAtom : P [ LiteralExpression ] = binaryAtom | hexAtom | octalAtom | quaternaryAtom | decimalAtom | charAtom
2017-12-06 23:23:30 +00:00
2018-12-16 13:38:57 +00:00
val literalAtomWithIntel : P [ LiteralExpression ] = binaryAtom | hexAtom | octalAtom | quaternaryAtom | intelHexAtom | decimalAtom | charAtom
2018-08-03 11:23:37 +00:00
2020-09-01 20:00:07 +00:00
val atom : P [ Expression ] = P ( position ( ) ~ ( variableAtom | localLabelAtom | literalAtom | textLiteralAtom ) ) . map { case ( p , a ) => a . pos ( p ) }
2017-12-06 23:23:30 +00:00
2020-09-01 20:00:07 +00:00
val atomWithIntel : P [ Expression ] = P ( position ( ) ~ ( variableAtom | localLabelAtom | literalAtomWithIntel | textLiteralAtom ) ) . map { case ( p , a ) => a . pos ( p ) }
2018-08-03 11:23:37 +00:00
2020-06-03 21:13:17 +00:00
val quotedAtom : P [ String ] = variableAtom . ! | literalAtomWithIntel . map {
case LiteralExpression ( value , _ ) => value . toString
case x => x . toString
} | textLiteralAtom . !
val importStatement : P [ Seq [ ImportStatement ] ] = ( "import" ~ ! letterOrDigit ~/ SWS ~/
identifier . rep ( min = 1 , sep = "/" ) ~ HWS ~ ( "<" ~/ HWS ~/ quotedAtom . rep ( min = 1 , sep = HWS ~ "," ~/ HWS ) ~/ HWS ~/ ">" ~/ Pass ) . ? ) .
map { case ( name , params ) => Seq ( ImportStatement ( name . mkString ( "/" ) , params . getOrElse ( Nil ) . toList ) ) }
2020-11-18 09:08:58 +00:00
val optimizationHintsDeclaration : P [ Set [ String ] ] =
2021-03-14 23:44:14 +00:00
( "!" ~/ HWS ~/ identifier ~/ "" ) . rep ( min = 0 , sep = AWS ) . map { _ . toSet }
2020-11-18 09:08:58 +00:00
2019-07-17 18:50:27 +00:00
val globalVariableDefinition : P [ Seq [ BankedDeclarationStatement ] ] = variableDefinition ( true )
2018-07-05 23:05:24 +00:00
val localVariableDefinition : P [ Seq [ DeclarationStatement ] ] = variableDefinition ( false )
2017-12-06 23:23:30 +00:00
2020-11-18 09:08:58 +00:00
def singleVariableDefinition : P [ ( Position , String , Option [ Expression ] , Option [ Expression ] , Set [ String ] , Option [ MemoryAlignment ] ) ] = for {
2019-07-08 17:24:11 +00:00
p <- position ( )
name <- identifier ~/ HWS ~/ Pass
2020-11-18 09:08:58 +00:00
alignment1 <- alignmentDeclaration ( fastAlignmentForFunctions ) . ? ~/ HWS
optimizationHints <- optimizationHintsDeclaration ~/ HWS
alignment2 <- alignmentDeclaration ( fastAlignmentForFunctions ) . ? ~/ HWS
2019-07-08 17:24:11 +00:00
addr <- ( "@" ~/ HWS ~/ mfExpression ( 1 , false ) ) . ? . opaque ( "<address>" ) ~ HWS
2021-01-13 13:38:59 +00:00
initialValue <- ( "=" ~/ AWS ~/ mfExpression ( 1 , false ) ) . ? ~/ HWS // TODO
2020-11-18 09:08:58 +00:00
} yield {
if ( alignment1 . isDefined && alignment2 . isDefined ) log . error ( s" Cannot define the alignment multiple times " , Some ( p ) )
val alignment = alignment1 . orElse ( alignment2 )
( p , name , addr , initialValue , optimizationHints , alignment )
}
2019-07-08 17:24:11 +00:00
2019-07-17 18:50:27 +00:00
def variableDefinition ( implicitlyGlobal : Boolean ) : P [ Seq [ BankedDeclarationStatement ] ] = for {
2017-12-06 23:23:30 +00:00
p <- position ( )
2018-03-15 22:09:19 +00:00
bank <- bankDeclaration
2018-07-05 23:05:24 +00:00
flags <- variableFlags ~ HWS
2019-10-21 23:06:51 +00:00
typ <- identifier ~/ SWS
2020-02-12 00:07:14 +00:00
if typ != "array"
2019-07-08 17:24:11 +00:00
vars <- singleVariableDefinition . rep ( min = 1 , sep = "," ~/ HWS )
2019-10-31 11:08:45 +00:00
_ <- Before_EOL ~/ ""
2017-12-06 23:23:30 +00:00
} yield {
2020-11-18 09:08:58 +00:00
vars . map { case ( p , name , addr , initialValue , optimizationHints , alignment ) => VariableDeclarationStatement ( name , typ ,
2018-03-15 22:09:19 +00:00
bank ,
2017-12-06 23:23:30 +00:00
global = implicitlyGlobal || flags ( "static" ) ,
stack = flags ( "stack" ) ,
constant = flags ( "const" ) ,
volatile = flags ( "volatile" ) ,
2018-02-01 21:39:38 +00:00
register = flags ( "register" ) ,
2020-11-18 09:08:58 +00:00
initialValue , addr , optimizationHints , alignment ) . pos ( p )
2019-07-08 17:24:11 +00:00
}
2017-12-06 23:23:30 +00:00
}
val paramDefinition : P [ ParameterDeclaration ] = for {
p <- position ( )
typ <- identifier ~/ SWS ~/ Pass
name <- identifier ~/ Pass
} yield {
2020-03-30 17:23:48 +00:00
if ( name == "register" || name == "const" || name == "ref" || name == "call" ) {
log . error ( s" Invalid parameter name: ` $name `. Did you mean writing a macro? " , Some ( p ) )
}
2017-12-06 23:23:30 +00:00
ParameterDeclaration ( typ , ByVariable ( name ) ) . pos ( p )
}
2018-07-04 22:49:51 +00:00
def asmExpression : P [ Expression ] = ( position ( ) ~ NoCut (
2018-08-03 11:23:37 +00:00
( "<" ~/ HWS ~ mfExpression ( mathLevel , allowIntelHexAtomsInAssembly ) ) . map ( e => HalfWordExpression ( e , hiByte = false ) ) |
( ">" ~/ HWS ~ mfExpression ( mathLevel , allowIntelHexAtomsInAssembly ) ) . map ( e => HalfWordExpression ( e , hiByte = true ) ) |
mfExpression ( mathLevel , allowIntelHexAtomsInAssembly )
2018-07-04 22:49:51 +00:00
) ) . map { case ( p , e ) => e . pos ( p ) }
def asmExpressionWithParens : P [ ( Expression , Boolean ) ] = ( position ( ) ~ NoCut (
( "(" ~ HWS ~ asmExpression ~ HWS ~ ")" ) . map ( _ -> true ) |
asmExpression . map ( _ -> false )
) ) . map { case ( p , e ) => e . _1 . pos ( p ) -> e . _2 }
2018-07-12 16:30:35 +00:00
def asmExpressionWithParensOrApostrophe : P [ ( Expression , Boolean ) ] = ( position ( ) ~ NoCut (
( "(" ~ HWS ~ asmExpression ~ HWS ~ ")" ) . map ( _ -> true ) |
( asmExpression ~ "'" ) . map ( _ -> true ) |
asmExpression . map ( _ -> false )
) ) . map { case ( p , e ) => e . _1 . pos ( p ) -> e . _2 }
2020-03-30 17:23:48 +00:00
def appcRegister : P [ ParamPassingConvention ]
val appcComplex : P [ ParamPassingConvention ] =
for {
pos <- position ( "passing method" )
keyword <- ( ( "const" | "ref" | "register" | "call" ) . ! ~ ! letterOrDigit ~/ Pass ) . ? ~/ Pass
_ <- if ( keyword . contains ( "call" ) ) { log . error ( s" Invalid assembly macro parameter passing convention: `call` " , Some ( pos ) ) ; Pass } else Pass
if ! keyword . contains ( "call" )
_ <- position ( "register name" )
register <- keyword match {
case Some ( "register" ) => ( AWS ~ "(" ~/ AWS ~ appcRegister ~/ AWS ~ ")" ~/ AWS ) . map ( Some ( _ ) )
case Some ( _ ) => SWS . map ( _ => None )
case None => Pass . map ( _ => None )
}
_ <- position ( "parameter name" )
ident <- identifier
} yield ( ( keyword , register , ident ) match {
case ( None , _ , name ) => ByVariable ( name )
case ( Some ( "const" ) , _ , name ) => ByConstant ( name )
case ( Some ( "ref" ) , _ , name ) => ByReference ( name )
case ( Some ( "register" ) , Some ( reg ) , _ ) => reg
case x => log . fatal ( s" Unknown assembly parameter passing convention: ` $x ` " )
} )
val asmParamDefinition : P [ ParameterDeclaration ] = for {
p <- position ( )
typ <- identifier ~ SWS ~/ Pass
appc <- appcRegister | appcComplex
} yield ParameterDeclaration ( typ , appc ) . pos ( p )
2018-07-30 16:15:44 +00:00
2020-03-30 17:23:48 +00:00
val macroParamDefinition : P [ ParameterDeclaration ] = for {
p <- position ( )
typ <- identifier ~ SWS ~/ Pass
pos <- position ( "passing method" )
keyword <- ( ( "const" | "ref" | "call" | "register" ) . ! ~ ! letterOrDigit ~/ Pass ) . ?
_ <- if ( keyword . contains ( "register" ) ) { log . error ( s" Invalid non-assembly macro parameter passing convention: `register`. Did you forget `asm`? " , Some ( pos ) ) ; Pass } else Pass
if ! keyword . contains ( "register" )
_ <- if ( keyword . isDefined ) SWS else Pass
_ <- position ( "parameter name" )
name <- identifier
} yield ParameterDeclaration ( typ , ( keyword match {
case None => ByReference ( name )
case Some ( "ref" ) => ByReference ( name )
case Some ( "const" ) => ByConstant ( name )
case Some ( "call" ) => ByLazilyEvaluableExpressionVariable ( name )
case Some ( x ) => log . fatal ( s" Invalid non-assembly macro parameter passing convention: ` $x ` " )
} ) ) . pos ( p )
2017-12-06 23:23:30 +00:00
2018-08-03 11:23:37 +00:00
def arrayListElement : P [ ArrayContents ] = arrayStringContents | arrayProcessedContents | arrayLoopContents | arrayFileContents | mfExpression ( nonStatementLevel , false ) . map ( e => LiteralContents ( List ( e ) ) )
2017-12-06 23:23:30 +00:00
2018-06-18 00:52:14 +00:00
def arrayProcessedContents : P [ ArrayContents ] = for {
_ <- "@" ~/ HWS
filter <- identifier
_ <- AWS
contents <- arrayContents
} yield ProcessedContents ( filter , contents )
2018-03-25 20:57:15 +00:00
def arrayListContents : P [ ArrayContents ] = ( "[" ~/ AWS ~/ arrayListElement . rep ( sep = AWS ~ "," ~/ AWS ) ~ AWS ~ "]" ~/ Pass ) . map ( c => CombinedContents ( c . toList ) )
2017-12-06 23:23:30 +00:00
2018-04-02 19:40:54 +00:00
// TODO: should reserve the `file` identifier here?
2018-07-05 23:05:24 +00:00
val arrayFileContents : P [ ArrayContents ] = for {
p <- "file" ~ HWS ~/ "(" ~/ HWS ~/ position ( "file name" )
2017-12-06 23:23:30 +00:00
filePath <- doubleQuotedString ~/ HWS
2020-02-12 00:07:14 +00:00
_ <- position ( "file start" )
2020-01-10 17:37:49 +00:00
optStart <- ( "," ~/ HWS ~/ literalAtom ~/ HWS ~/ Pass ) . ?
2020-02-12 00:07:14 +00:00
_ <- position ( "slice length" )
2020-01-10 17:37:49 +00:00
optLength <- ( "," ~/ HWS ~/ literalAtom ~/ HWS ~/ Pass ) . ?
2020-02-12 00:07:14 +00:00
_ <- position ( "closing parentheses" )
2017-12-06 23:23:30 +00:00
_ <- ")" ~/ Pass
} yield {
2019-10-18 09:01:31 +00:00
val data = Files . readAllBytes ( Paths . get ( currentDirectory , filePath ) )
2020-01-10 17:37:49 +00:00
val slice : Array [ Byte ] = ( optStart . map ( _ . value . toInt ) , optLength . map ( _ . value . toInt ) ) match {
case ( Some ( start ) , Some ( length ) ) =>
if ( data . length < start ) {
log . error ( s" File $filePath is shorter ( ${ data . length } B) that the start offset $start " , Some ( p ) )
Array . fill ( length ) ( 0. toByte )
} else if ( data . length < start + length ) {
log . error ( s" File $filePath is shorter ( ${ data . length } B) that the start offset plus length ${ start + length } " , Some ( p ) )
Array . fill ( length ) ( 0. toByte )
} else {
data . slice ( start , start + length )
}
case ( Some ( start ) , None ) =>
if ( data . length < start ) {
log . error ( s" File $filePath is shorter ( ${ data . length } B) that the start offset $start " , Some ( p ) )
Array [ Byte ] ( 0 )
} else {
data . drop ( start )
}
case ( None , None ) => data
case _ => throw new IllegalStateException ( "error parsing file()" )
2017-12-06 23:23:30 +00:00
}
2018-03-25 20:57:15 +00:00
LiteralContents ( slice . map ( c => LiteralExpression ( c & 0xff , 1 ) ) . toList )
2017-12-06 23:23:30 +00:00
}
2019-06-12 20:55:34 +00:00
def arrayStringContents : P [ ArrayContents ] = textLiteral . map ( LiteralContents )
2017-12-06 23:23:30 +00:00
2018-03-25 20:57:15 +00:00
def arrayLoopContents : P [ ArrayContents ] = for {
identifier <- "for" ~ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass
2018-08-03 11:23:37 +00:00
start <- mfExpression ( nonStatementLevel , false ) ~ HWS ~ "," ~/ HWS ~/ Pass
2018-07-05 23:05:24 +00:00
pos <- position ( "loop direction" )
2018-03-25 20:57:15 +00:00
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
2019-04-18 14:24:46 +00:00
end <- mfExpression ( nonStatementLevel , false , allowTopLevelIndexing = false )
2018-03-25 20:57:15 +00:00
body <- AWS ~ arrayContents
} yield {
val fixedDirection = direction match {
case ForDirection . ParallelUntil =>
2020-03-17 20:08:43 +00:00
if ( options . flag ( CompilationFlag . FallbackValueUseWarning ) ) log . warn ( "`paralleluntil` is not allowed in array definitions, assuming `until`" , Some ( pos ) )
2018-03-25 20:57:15 +00:00
ForDirection . Until
case ForDirection . ParallelTo =>
2020-03-17 20:08:43 +00:00
if ( options . flag ( CompilationFlag . FallbackValueUseWarning ) ) log . warn ( "`parallelto` is not allowed in array definitions, assuming `to`" , Some ( pos ) )
2018-03-25 20:57:15 +00:00
ForDirection . To
case x => x
}
ForLoopContents ( identifier , start , end , fixedDirection , body )
}
2018-06-18 00:52:14 +00:00
def arrayContents : P [ ArrayContents ] = arrayProcessedContents | arrayListContents | arrayLoopContents | arrayFileContents | arrayStringContents
2017-12-06 23:23:30 +00:00
2019-07-29 20:51:08 +00:00
def arrayContentsForAsm : P [ RawBytesStatement ] = ( arrayListContents | arrayStringContents ) . map ( c => RawBytesStatement ( c , options . isBigEndian ) )
2018-04-02 19:40:54 +00:00
2018-07-11 23:23:38 +00:00
val aliasDefinition : P [ Seq [ AliasDefinitionStatement ] ] = for {
p <- position ( )
name <- "alias" ~ ! letterOrDigit ~/ SWS ~ identifier ~ HWS
2021-01-13 13:38:59 +00:00
target <- "=" ~/ AWS ~/ identifier ~/ HWS
2018-12-19 16:33:51 +00:00
important <- "!" . ! . ? ~/ HWS
} yield Seq ( AliasDefinitionStatement ( name , target , important . isDefined ) . pos ( p ) )
2018-07-11 23:23:38 +00:00
2018-10-04 19:33:10 +00:00
def fastAlignmentForArrays : MemoryAlignment
def fastAlignmentForFunctions : MemoryAlignment
def alignmentDeclaration ( fast : MemoryAlignment ) : P [ MemoryAlignment ] = ( position ( ) ~ "align" ~/ AWS ~/ "(" ~/ AWS ~/ atom ~/ AWS ~/ ")" ) . map {
case ( _ , LiteralExpression ( 1 , _ ) ) => NoAlignment
case ( pos , LiteralExpression ( n , _ ) ) =>
if ( n >= 1 && n <= 0x8000 & ( n & ( n - 1 ) ) == 0 ) DivisibleAlignment ( n . toInt )
else {
log . error ( "Invalid alignment: " + n , Some ( pos ) )
NoAlignment
}
case ( pos , VariableExpression ( "fast" ) ) => fast
case ( pos , _ ) =>
log . error ( "Invalid alignment" , Some ( pos ) )
NoAlignment
}
2018-07-05 23:05:24 +00:00
val arrayDefinition : P [ Seq [ ArrayDeclarationStatement ] ] = for {
2017-12-06 23:23:30 +00:00
p <- position ( )
2018-03-15 22:09:19 +00:00
bank <- bankDeclaration
2019-04-29 20:57:40 +00:00
const <- ( "const" . ! ~ HWS ) . ?
2019-04-14 23:57:18 +00:00
_ <- "array" ~ ! letterOrDigit
elementType <- ( "(" ~/ AWS ~/ identifier ~ AWS ~ ")" ) . ? ~/ HWS
2020-02-12 00:07:14 +00:00
name <- identifier ~/ HWS
2018-08-03 11:23:37 +00:00
length <- ( "[" ~/ AWS ~/ mfExpression ( nonStatementLevel , false ) ~ AWS ~ "]" ) . ? ~ HWS
2020-11-18 09:08:58 +00:00
alignment1 <- alignmentDeclaration ( fastAlignmentForFunctions ) . ? ~/ HWS
optimizationHints <- optimizationHintsDeclaration ~/ HWS
alignment2 <- alignmentDeclaration ( fastAlignmentForFunctions ) . ? ~/ HWS
2018-08-03 11:23:37 +00:00
addr <- ( "@" ~/ HWS ~/ mfExpression ( 1 , false ) ) . ? ~/ HWS
2021-01-13 13:38:59 +00:00
contents <- ( "=" ~/ AWS ~/ arrayContents ) . ? ~/ HWS
2020-11-18 09:08:58 +00:00
} yield {
if ( alignment1 . isDefined && alignment2 . isDefined ) log . error ( s" Cannot define the alignment multiple times " , Some ( p ) )
val alignment = alignment1 . orElse ( alignment2 )
Seq ( ArrayDeclarationStatement ( name , bank , length , elementType . getOrElse ( "byte" ) , addr , const . isDefined , contents , optimizationHints , alignment , options . isBigEndian ) . pos ( p ) )
}
2017-12-06 23:23:30 +00:00
2019-04-18 14:24:46 +00:00
def tightMfExpression ( allowIntelHex : Boolean , allowTopLevelIndexing : Boolean ) : P [ Expression ] = {
2018-08-03 11:23:37 +00:00
val a = if ( allowIntelHex ) atomWithIntel else atom
2019-04-18 14:24:46 +00:00
if ( allowTopLevelIndexing )
mfExpressionWrapper [ Expression ] ( mfParenExpr ( allowIntelHex ) | derefExpression | functionCall ( allowIntelHex ) | a )
else
mfParenExpr ( allowIntelHex ) | derefExpression | functionCall ( allowIntelHex ) | a
2018-08-03 11:23:37 +00:00
}
2017-12-06 23:23:30 +00:00
2019-04-18 14:24:46 +00:00
def tightMfExpressionButNotCall ( allowIntelHex : Boolean , allowTopLevelIndexing : Boolean ) : P [ Expression ] = {
2018-08-03 11:23:37 +00:00
val a = if ( allowIntelHex ) atomWithIntel else atom
2019-04-18 14:24:46 +00:00
if ( allowTopLevelIndexing )
mfExpressionWrapper [ Expression ] ( mfParenExpr ( allowIntelHex ) | derefExpression | a )
else
mfParenExpr ( allowIntelHex ) | derefExpression | a
2018-08-03 11:23:37 +00:00
}
2018-01-30 16:38:32 +00:00
2019-04-18 14:24:46 +00:00
def mfExpression ( level : Int , allowIntelHex : Boolean , allowTopLevelIndexing : Boolean = true ) : P [ Expression ] = {
2018-07-05 23:05:24 +00:00
val allowedOperators = mfOperatorsDropFlatten ( level )
2017-12-06 23:23:30 +00:00
2019-10-31 11:09:20 +00:00
def inner : P [ SeparatedList [ ( Boolean , Expression ) , String ] ] = {
2017-12-06 23:23:30 +00:00
for {
2019-10-31 11:09:20 +00:00
minus <- ( "-" . rep ( min = 1 ) . ! . map ( _ . length ( ) . & ( 1 ) . == ( 1 ) ) ~/ HWS ) . ? . map ( _ . getOrElse ( false ) )
2019-04-18 14:24:46 +00:00
head <- tightMfExpression ( allowIntelHex , allowTopLevelIndexing ) ~/ HWS
2020-08-14 00:22:13 +00:00
maybeOperator <- ( StringIn ( allowedOperators : _ * ) . ! ~ ! CharIn ( Seq ( '/' , '=' , '-' , '+' , ':' , '>' , '<' , '\'' ) ) ) . map ( op => if ( mfOperatorNormalizations . contains ( op ) ) mfOperatorNormalizations ( op ) else op ) . ?
2019-10-31 11:09:20 +00:00
maybeTail <- maybeOperator . fold [ P [ Option [ List [ ( String , ( Boolean , Expression ) ) ] ] ] ] ( Pass . map ( _ => None ) ) ( o => ( AWS ~/ inner ~/ HWS ) . map ( x2 => Some ( ( o -> x2 . head ) : : x2 . tail ) ) )
2017-12-06 23:23:30 +00:00
} yield {
2019-10-31 11:09:20 +00:00
maybeTail . fold [ SeparatedList [ ( Boolean , Expression ) , String ] ] ( SeparatedList . of ( minus -> head ) ) ( t => SeparatedList ( minus -> head , t ) )
2017-12-06 23:23:30 +00:00
}
}
2019-10-31 11:09:20 +00:00
def p ( list : SeparatedList [ ( Boolean , Expression ) , String ] , level : Int ) : Expression =
if ( level == mfOperators . length ) {
if ( list . head . _1 ) {
LiteralExpression ( 0 , 1 ) # - # list . head . _2
} else {
list . head . _2
}
} else {
2018-06-12 20:46:20 +00:00
val xs = list . split ( mfOperators ( level ) . toSet ( _ ) )
2017-12-06 23:23:30 +00:00
xs . separators . distinct match {
case Nil =>
if ( xs . tail . nonEmpty )
2020-08-14 20:28:50 +00:00
log . error ( "Too many different operators; consider using parentheses for disambiguation" , xs . head . head . _2 . position )
2017-12-06 23:23:30 +00:00
p ( xs . head , level + 1 )
case List ( "+" ) | List ( "-" ) | List ( "+" , "-" ) | List ( "-" , "+" ) =>
2019-10-31 11:09:20 +00:00
SumExpression ( xs . toPairList ( "+" ) . map {
case ( op , value ) =>
if ( value . count ( _ . _1 ) > 0 ) {
if ( value . size == 1 ) {
2020-08-14 20:28:50 +00:00
log . error ( "Too many different operators; consider using parentheses for disambiguation" , xs . head . head . _2 . position )
2019-10-31 11:09:20 +00:00
}
( op == "+" , p ( value . map ( p => ( false , p . _2 ) ) , level + 1 ) )
} else {
( op == "-" , p ( value , level + 1 ) )
}
} , decimal = false ) . pos ( list . head . _2 . position )
2017-12-06 23:23:30 +00:00
case List ( "+'" ) | List ( "-'" ) | List ( "+'" , "-'" ) | List ( "-'" , "+'" ) =>
2019-10-31 11:09:20 +00:00
SumExpression ( xs . toPairList ( "+" ) . map { case ( op , value ) =>
2020-08-14 20:28:50 +00:00
if ( value . exists ( _ . _1 ) ) log . error ( "Too many different operators; consider using parentheses for disambiguation" , xs . head . head . _2 . position )
2019-10-31 11:09:20 +00:00
( op == "-'" , p ( value , level + 1 ) )
} , decimal = true ) . pos ( list . head . _2 . position )
2017-12-06 23:23:30 +00:00
case List ( ":" ) =>
if ( xs . size != 2 ) {
2019-10-31 11:09:20 +00:00
log . error ( "The `:` operator can have only two arguments" , xs . head . head . _2 . position )
2017-12-06 23:23:30 +00:00
LiteralExpression ( 0 , 1 )
} else {
2019-10-31 11:09:20 +00:00
SeparateBytesExpression ( p ( xs . head , level + 1 ) , p ( xs . tail . head . _2 , level + 1 ) ) . pos ( list . head . _2 . position )
2017-12-06 23:23:30 +00:00
}
2019-04-18 14:24:46 +00:00
case List ( eq ) if level == 0 =>
if ( xs . size != 2 ) {
2019-10-31 11:09:20 +00:00
log . error ( s" The ` $eq ` operator can have only two arguments " , xs . head . head . _2 . position )
2019-04-18 14:24:46 +00:00
LiteralExpression ( 0 , 1 )
} else {
2019-10-31 11:09:20 +00:00
FunctionCallExpression ( eq , xs . items . map ( value => p ( value , level + 1 ) ) ) . pos ( list . head . _2 . position )
2019-04-18 14:24:46 +00:00
}
2017-12-06 23:23:30 +00:00
case List ( op ) =>
2019-10-31 11:09:20 +00:00
FunctionCallExpression ( op , xs . items . map ( value => p ( value , level + 1 ) ) ) . pos ( list . head . _2 . position )
2017-12-06 23:23:30 +00:00
case _ =>
2020-08-14 20:28:50 +00:00
log . error ( "Too many different operators; consider using parentheses for disambiguation" , xs . head . head . _2 . position )
2017-12-06 23:23:30 +00:00
LiteralExpression ( 0 , 1 )
}
}
inner . map ( x => p ( x , 0 ) )
}
2019-04-18 14:24:46 +00:00
def index : P [ Expression ] = HWS ~ "[" ~/ AWS ~/ mfExpression ( nonStatementLevel , false ) ~ AWS ~/ "]" ~/ Pass
2019-06-12 20:55:34 +00:00
def mfExpressionWrapper [ E <: Expression ] ( inner : P [ E ] ) : P [ E ] = for {
2019-04-18 14:24:46 +00:00
expr <- inner
firstIndices <- index . rep
2019-07-10 14:51:12 +00:00
fieldPath <- ( HWS ~ ( ( "->" . ! ~/ AWS ) | "." . ! ) ~/ AWS ~/ identifier ~/ index . rep ) . rep
2019-04-18 14:24:46 +00:00
} yield ( expr , firstIndices , fieldPath ) match {
case ( _ , Seq ( ) , Seq ( ) ) => expr
2019-06-12 20:55:34 +00:00
case ( VariableExpression ( vname ) , Seq ( i ) , Seq ( ) ) => IndexedExpression ( vname , i ) . pos ( expr . position ) . asInstanceOf [ E ]
2019-07-10 14:51:12 +00:00
case _ =>
val fixedFieldPath = fieldPath . flatMap { e =>
e match {
case ( "." , "pointer" , _ ) => Seq ( e )
case ( "." , f , _ ) if f . startsWith ( "pointer." ) => Seq ( e )
2019-07-23 22:14:27 +00:00
case ( "." , "addr" , _ ) => Seq ( e )
case ( "." , f , _ ) if f . startsWith ( "addr." ) => Seq ( e )
2019-07-10 14:51:12 +00:00
case ( "." , f , i ) => Seq ( ( "." , "pointer" , Nil ) , ( "->" , f , i ) )
case _ => Seq ( e )
}
}
IndirectFieldExpression ( expr , firstIndices , fixedFieldPath . map { case ( a , b , c ) => ( a == "." , b , c ) } ) . pos ( expr . position ) . asInstanceOf [ E ]
2019-04-18 14:24:46 +00:00
}
// def mfLhsExpression: P[LhsExpression] = for {
// (p, left) <- position() ~ mfLhsExpressionSimple
// rightOpt <- (HWS ~ ":" ~/ HWS ~ mfLhsExpressionSimple).?
// } yield rightOpt.fold(left)(right => SeparateBytesExpression(left, right).pos(p))
2017-12-06 23:23:30 +00:00
2019-04-18 14:24:46 +00:00
def mfLhsExpressionSimple : P [ LhsExpression ] =
mfExpressionWrapper [ LhsExpression ] ( derefExpression | ( position ( ) ~ identifier ) . map { case ( p , n ) => VariableExpression ( n ) . pos ( p ) } ~ HWS )
2017-12-06 23:23:30 +00:00
2019-04-18 14:24:46 +00:00
def mfLhsExpression : P [ LhsExpression ] =
mfExpression ( nonStatementLevel , false ) . filter ( _ . isInstanceOf [ LhsExpression ] ) . map ( _ . asInstanceOf [ LhsExpression ] )
2017-12-06 23:23:30 +00:00
2018-08-03 11:23:37 +00:00
def mfParenExpr ( allowIntelHex : Boolean ) : P [ Expression ] = P ( "(" ~/ AWS ~/ mfExpression ( nonStatementLevel , allowIntelHex ) ~ AWS ~/ ")" )
2017-12-06 23:23:30 +00:00
2018-08-03 11:23:37 +00:00
def functionCall ( allowIntelHex : Boolean ) : P [ FunctionCallExpression ] = for {
2017-12-06 23:23:30 +00:00
p <- position ( )
name <- identifier
2018-08-03 11:23:37 +00:00
params <- HWS ~ "(" ~/ AWS ~/ mfExpression ( nonStatementLevel , allowIntelHex ) . rep ( min = 0 , sep = AWS ~ "," ~/ AWS ) ~ AWS ~/ ")" ~/ ""
2017-12-06 23:23:30 +00:00
} yield FunctionCallExpression ( name , params . toList ) . pos ( p )
2019-04-15 17:45:26 +00:00
val derefExpression : P [ DerefDebuggingExpression ] = for {
p <- position ( )
if enableDebuggingOptions
yens <- CharsWhileIn ( Seq ( '¥' ) ) . ! ~/ AWS
inner <- mfParenExpr ( false )
} yield DerefDebuggingExpression ( inner , yens . length ) . pos ( p )
2019-04-18 14:24:46 +00:00
val expressionStatement : P [ Seq [ ExecutableStatement ] ] = mfExpression ( 0 , false ) . map {
case FunctionCallExpression ( "=" , List ( t : LhsExpression , s ) ) =>
Seq ( Assignment ( t , s ) . pos ( t . position ) )
case x @FunctionCallExpression ( "=" , exprs ) =>
log . error ( "Invalid left-hand-side of an assignment" , x . position )
exprs . map ( ExpressionStatement )
case x =>
Seq ( ExpressionStatement ( x ) . pos ( x . position ) )
}
2017-12-06 23:23:30 +00:00
2018-03-10 20:52:28 +00:00
def keywordStatement : P [ Seq [ ExecutableStatement ] ] = P (
2018-03-03 20:34:12 +00:00
returnOrDispatchStatement |
2019-07-15 00:06:23 +00:00
gotoStatement |
labelStatement |
2018-03-03 20:34:12 +00:00
ifStatement |
whileStatement |
2018-12-19 17:13:38 +00:00
forEachStatement |
2020-07-24 17:11:27 +00:00
forStatement |
2018-03-03 20:34:12 +00:00
doWhileStatement |
breakStatement |
continueStatement |
2019-04-18 14:24:46 +00:00
inlineAssembly )
2017-12-06 23:23:30 +00:00
2018-03-10 20:52:28 +00:00
def executableStatement : P [ Seq [ ExecutableStatement ] ] = ( position ( ) ~ P ( keywordStatement | expressionStatement ) ) . map { case ( p , s ) => s . map ( _ . pos ( p ) ) }
2017-12-06 23:23:30 +00:00
2021-02-03 08:49:17 +00:00
def asmLabel : P [ ExecutableStatement ]
2018-06-12 20:46:20 +00:00
def asmStatement : P [ ExecutableStatement ]
2018-03-10 20:52:28 +00:00
2019-06-24 20:32:29 +00:00
def statement : P [ Seq [ Statement ] ] = ( position ( ) ~ P ( keywordStatement | arrayDefinition | localVariableDefinition | expressionStatement ) ) . map { case ( p , s ) => s . map ( _ . pos ( p ) ) }
2017-12-06 23:23:30 +00:00
2021-02-03 08:49:17 +00:00
def asmStatements : P [ List [ ExecutableStatement ] ] = ( "{" ~/ AWS_asm ~/ ( asmLabel . rep ( ) ~ asmStatement . ? ) . rep ( sep = NoCut ( EOL_asm ) ~ ! "}" ~/ Pass ) ~/ AWS_asm ~/ "}" ~/ Pass ) . map ( e => e . flatMap ( x => ( x . _1 ++ x . _2 . toSeq ) . toList ) ) . map ( _ . toList )
2017-12-06 23:23:30 +00:00
2018-03-10 20:52:28 +00:00
def statements : P [ List [ Statement ] ] = ( "{" ~/ AWS ~ statement . rep ( sep = NoCut ( EOL ) ~ ! "}" ~/ Pass ) ~/ AWS ~/ "}" ~/ Pass ) . map ( _ . flatten . toList )
2017-12-06 23:23:30 +00:00
2019-04-15 17:56:14 +00:00
def mfFunctionSmallBody : P [ List [ Statement ] ] = for {
_ <- "=" ~/ AWS
expression <- mfExpression ( nonStatementLevel , false )
} yield List ( ReturnStatement ( Some ( expression ) ) . pos ( expression . position ) )
def mfFunctionBody : P [ List [ Statement ] ] = statements | mfFunctionSmallBody
2018-03-10 20:52:28 +00:00
def executableStatements : P [ Seq [ ExecutableStatement ] ] = ( "{" ~/ AWS ~/ executableStatement . rep ( sep = NoCut ( EOL ) ~ ! "}" ~/ Pass ) ~/ AWS ~ "}" ) . map ( _ . flatten )
2017-12-06 23:23:30 +00:00
2018-07-05 23:05:24 +00:00
val dispatchLabel : P [ ReturnDispatchLabel ] =
2018-08-03 11:23:37 +00:00
( "default" ~ ! letterOrDigit ~/ AWS ~/ ( "(" ~/ position ( "default branch range" ) ~ AWS ~/ mfExpression ( nonStatementLevel , false ) . rep ( min = 0 , sep = AWS ~ "," ~/ AWS ) ~ AWS ~/ ")" ~/ "" ) . ? ) . map {
2018-01-30 16:38:32 +00:00
case None => DefaultReturnDispatchLabel ( None , None )
case Some ( ( _ , Seq ( ) ) ) => DefaultReturnDispatchLabel ( None , None )
case Some ( ( _ , Seq ( e ) ) ) => DefaultReturnDispatchLabel ( None , Some ( e ) )
case Some ( ( _ , Seq ( s , e ) ) ) => DefaultReturnDispatchLabel ( Some ( s ) , Some ( e ) )
case Some ( ( pos , _ ) ) =>
2018-07-30 16:15:44 +00:00
log . error ( "Invalid default branch declaration" , Some ( pos ) )
2018-01-30 16:38:32 +00:00
DefaultReturnDispatchLabel ( None , None )
2018-08-03 11:23:37 +00:00
} | mfExpression ( nonStatementLevel , false ) . rep ( min = 0 , sep = AWS ~ "," ~/ AWS ) . map ( exprs => StandardReturnDispatchLabel ( exprs . toList ) )
2018-01-30 16:38:32 +00:00
2018-07-05 23:05:24 +00:00
val dispatchBranch : P [ ReturnDispatchBranch ] = for {
2018-01-30 16:38:32 +00:00
pos <- position ( )
l <- dispatchLabel ~/ HWS ~/ "@" ~/ HWS
2019-04-18 14:24:46 +00:00
f <- tightMfExpressionButNotCall ( false , allowTopLevelIndexing = false ) ~/ HWS
2018-08-03 11:23:37 +00:00
parameters <- ( "(" ~/ position ( "dispatch actual parameters" ) ~ AWS ~/ mfExpression ( nonStatementLevel , false ) . rep ( min = 0 , sep = AWS ~ "," ~/ AWS ) ~ AWS ~/ ")" ~/ "" ) . ?
2018-01-30 16:38:32 +00:00
} yield ReturnDispatchBranch ( l , f , parameters . map ( _ . _2 . toList ) . getOrElse ( Nil ) ) . pos ( pos )
2018-07-05 23:05:24 +00:00
val dispatchStatementBody : P [ Seq [ ExecutableStatement ] ] = for {
2018-08-03 11:23:37 +00:00
indexer <- "[" ~/ AWS ~/ mfExpression ( nonStatementLevel , false ) ~/ AWS ~/ "]" ~/ AWS
2018-01-30 16:38:32 +00:00
_ <- position ( "dispatch statement body" )
2018-06-12 20:46:20 +00:00
parameters <- ( "(" ~/ position ( "dispatch parameters" ) ~ AWS ~/ mfLhsExpression . rep ( min = 0 , sep = AWS ~ "," ~/ AWS ) ~ AWS ~/ ")" ~/ "" ) . ?
2018-01-30 16:38:32 +00:00
_ <- AWS ~/ position ( "dispatch statement body" ) ~/ "{" ~/ AWS
branches <- dispatchBranch . rep ( sep = EOL ~ ! "}" ~/ Pass )
_ <- AWS ~/ "}"
2018-03-10 20:52:28 +00:00
} yield Seq ( ReturnDispatchStatement ( indexer , parameters . map ( _ . _2 . toList ) . getOrElse ( Nil ) , branches . toList ) )
2018-01-30 16:38:32 +00:00
2018-08-03 11:23:37 +00:00
val returnOrDispatchStatement : P [ Seq [ ExecutableStatement ] ] = "return" ~ ! letterOrDigit ~/ HWS ~ ( dispatchStatementBody | mfExpression ( nonStatementLevel , false ) . ? . map ( ReturnStatement ) . map ( Seq ( _ ) ) )
2018-03-03 20:34:12 +00:00
2019-07-15 00:06:23 +00:00
val gotoStatement : P [ Seq [ ExecutableStatement ] ] = "goto" ~ ! letterOrDigit ~/ HWS ~ mfExpression ( nonStatementLevel , false ) . map ( GotoStatement ) . map ( Seq ( _ ) )
val labelStatement : P [ Seq [ ExecutableStatement ] ] = "label" ~ ! letterOrDigit ~/ HWS ~ identifier . map ( LabelStatement ) . map ( Seq ( _ ) )
2018-03-10 20:52:28 +00:00
def ifStatement : P [ Seq [ ExecutableStatement ] ] = for {
2018-08-03 11:23:37 +00:00
condition <- "if" ~ ! letterOrDigit ~/ HWS ~/ mfExpression ( nonStatementLevel , false )
2017-12-06 23:23:30 +00:00
thenBranch <- AWS ~/ executableStatements
2019-01-04 10:54:46 +00:00
elseBranch <- ( AWS ~ "else" ~/ AWS ~/ ( ( for {
p <- position ( )
s <- ifStatement
} yield s . map ( _ . pos ( p ) ) ) | executableStatements ) ) . ?
2018-03-10 20:52:28 +00:00
} yield Seq ( IfStatement ( condition , thenBranch . toList , elseBranch . getOrElse ( Nil ) . toList ) )
2017-12-06 23:23:30 +00:00
2018-03-10 20:52:28 +00:00
def whileStatement : P [ Seq [ ExecutableStatement ] ] = for {
2018-08-03 11:23:37 +00:00
condition <- "while" ~ ! letterOrDigit ~/ HWS ~/ mfExpression ( nonStatementLevel , false )
2017-12-06 23:23:30 +00:00
body <- AWS ~ executableStatements
2018-03-10 20:52:28 +00:00
} yield Seq ( WhileStatement ( condition , body . toList , Nil ) )
2017-12-06 23:23:30 +00:00
2018-03-10 20:52:28 +00:00
def forStatement : P [ Seq [ ExecutableStatement ] ] = for {
2020-07-24 17:11:27 +00:00
identifier <- "for" ~/ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass
2018-08-03 11:23:37 +00:00
start <- mfExpression ( nonStatementLevel , false ) ~ HWS ~ "," ~/ HWS ~/ Pass
2017-12-06 23:23:30 +00:00
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
2018-08-03 11:23:37 +00:00
end <- mfExpression ( nonStatementLevel , false )
2017-12-06 23:23:30 +00:00
body <- AWS ~ executableStatements
2018-03-10 20:52:28 +00:00
} yield Seq ( ForStatement ( identifier , start , end , direction , body . toList ) )
2017-12-06 23:23:30 +00:00
2018-12-19 17:13:38 +00:00
def forEachStatement : P [ Seq [ ExecutableStatement ] ] = for {
2020-07-24 17:11:27 +00:00
id <- "for" ~ SWS ~ identifier ~ HWS ~ ( "," ~ HWS ~ identifier ~ HWS ) . ? ~ ":" ~/ HWS ~ Pass
2018-12-19 17:13:38 +00:00
values <- ( "[" ~/ AWS ~/ mfExpression ( 0 , false ) . rep ( min = 0 , sep = AWS ~ "," ~/ AWS ) ~ AWS ~/ "]" ~/ "" ) . map ( seq => Right ( seq . toList ) ) | mfExpression ( 0 , false ) . map ( Left ( _ ) )
body <- AWS ~ executableStatements
2020-07-24 17:11:27 +00:00
} yield Seq ( ForEachStatement ( id . _1 , id . _2 , values , body . toList ) )
2018-12-19 17:13:38 +00:00
2018-07-05 23:05:24 +00:00
def inlineAssembly : P [ Seq [ ExecutableStatement ] ] = "asm" ~ ! letterOrDigit ~/ AWS ~ asmStatements
2017-12-06 23:23:30 +00:00
//noinspection MutatorLikeMethodIsParameterless
2018-03-10 20:52:28 +00:00
def doWhileStatement : P [ Seq [ ExecutableStatement ] ] = for {
2017-12-06 23:23:30 +00:00
body <- "do" ~ ! letterOrDigit ~/ AWS ~ executableStatements ~/ AWS
2018-08-03 11:23:37 +00:00
condition <- "while" ~ ! letterOrDigit ~/ HWS ~/ mfExpression ( nonStatementLevel , false )
2018-03-10 20:52:28 +00:00
} yield Seq ( DoWhileStatement ( body . toList , Nil , condition ) )
2017-12-06 23:23:30 +00:00
2019-07-17 18:50:27 +00:00
val functionDefinition : P [ Seq [ BankedDeclarationStatement ] ] = for {
2017-12-06 23:23:30 +00:00
p <- position ( )
2018-03-15 22:09:19 +00:00
bank <- bankDeclaration
2018-07-05 23:05:24 +00:00
flags <- functionFlags ~ HWS
2017-12-06 23:23:30 +00:00
returnType <- identifier ~ SWS
2019-10-07 23:33:55 +00:00
if ! Environment . neverValidTypeIdentifiers ( returnType )
2017-12-06 23:23:30 +00:00
name <- identifier ~ HWS
2020-03-30 17:23:48 +00:00
params <- "(" ~/ AWS ~/ ( if ( flags ( "asm" ) ) asmParamDefinition else if ( flags ( "macro" ) ) macroParamDefinition else paramDefinition ) . rep ( sep = AWS ~ "," ~/ AWS ) ~ AWS ~ ")" ~/ AWS
2020-11-18 09:08:58 +00:00
alignment1 <- alignmentDeclaration ( fastAlignmentForFunctions ) . ? ~/ AWS
optimizationHints <- optimizationHintsDeclaration ~/ HWS
alignment2 <- alignmentDeclaration ( fastAlignmentForFunctions ) . ? ~/ AWS
2018-08-03 11:23:37 +00:00
addr <- ( "@" ~/ HWS ~/ mfExpression ( 1 , false ) ) . ? . opaque ( "<address>" ) ~/ AWS
2019-04-15 17:56:14 +00:00
statements <- ( externFunctionBody | ( if ( flags ( "asm" ) ) asmStatements else mfFunctionBody ) . map ( l => Some ( l ) ) ) ~/ Pass
2017-12-06 23:23:30 +00:00
} yield {
2020-11-18 09:08:58 +00:00
if ( alignment1 . isDefined && alignment2 . isDefined ) log . error ( s" Cannot define the alignment multiple times " , Some ( p ) )
val alignment = alignment1 . orElse ( alignment2 )
if ( flags ( "extern" ) ) log . error ( "The extern keyword should go at the end of a function declaration" , Some ( p ) )
2018-07-30 16:15:44 +00:00
if ( flags ( "interrupt" ) && flags ( "macro" ) ) log . error ( s" Interrupt function ` $name ` cannot be macros " , Some ( p ) )
if ( flags ( "kernal_interrupt" ) && flags ( "macro" ) ) log . error ( s" Kernal interrupt function ` $name ` cannot be macros " , Some ( p ) )
2019-11-13 17:39:27 +00:00
if ( flags ( "interrupt" ) && flags ( "reentrant" ) ) log . error ( s" Interrupt function ` $name ` cannot be reentrant " , Some ( p ) )
if ( flags ( "interrupt" ) && flags ( "kernal_interrupt" ) ) log . error ( s" Interrupt function ` $name ` cannot be a Kernal interrupt " , Some ( p ) )
2018-07-30 16:15:44 +00:00
if ( flags ( "macro" ) && flags ( "reentrant" ) ) log . error ( "Reentrant and macro exclude each other" , Some ( p ) )
if ( flags ( "inline" ) && flags ( "noinline" ) ) log . error ( "Noinline and inline exclude each other" , Some ( p ) )
if ( flags ( "macro" ) && flags ( "noinline" ) ) log . error ( "Noinline and macro exclude each other" , Some ( p ) )
if ( flags ( "inline" ) && flags ( "macro" ) ) log . error ( "Macro and inline exclude each other" , Some ( p ) )
2019-11-13 17:39:27 +00:00
if ( flags ( "interrupt" ) && returnType != "void" ) log . error ( s" Interrupt function ` $name ` has to return void " , Some ( p ) )
2020-03-19 22:53:16 +00:00
if ( flags ( "const" ) && returnType == "void" ) log . error ( s" Const-pure function ` $name ` cannot return void " , Some ( p ) )
if ( flags ( "const" ) && flags ( "interrupt" ) ) log . error ( s" Const-pure function ` $name ` cannot be an interrupt " , Some ( p ) )
if ( flags ( "const" ) && flags ( "kernal_interrupt" ) ) log . error ( s" Const-pure function ` $name ` cannot be a Kernal interrupt " , Some ( p ) )
if ( flags ( "const" ) && flags ( "macro" ) ) log . error ( s" Const-pure function ` $name ` cannot be a macro " , Some ( p ) )
if ( flags ( "const" ) && flags ( "asm" ) ) log . error ( s" Const-pure function ` $name ` cannot contain assembly " , Some ( p ) )
2019-11-13 17:39:27 +00:00
if ( addr . isEmpty && statements . isEmpty ) log . error ( s" Extern function ` $name ` must have an address " , Some ( p ) )
if ( addr . isDefined && alignment . isDefined ) log . error ( s" Function ` $name ` has both address and alignment " , Some ( p ) )
if ( statements . isEmpty && alignment . isDefined ) log . error ( s" Extern function ` $name ` cannot have alignment " , Some ( p ) )
if ( statements . isEmpty && ! flags ( "asm" ) && params . nonEmpty ) log . error ( s" Extern non-asm function ` $name ` cannot have parameters " , Some ( p ) )
2018-06-12 20:46:20 +00:00
if ( flags ( "asm" ) ) validateAsmFunctionBody ( p , flags , name , statements )
2020-12-01 02:19:38 +00:00
if ( flags ( "macro" ) ) {
statements . flatMap ( _ . find ( _ . isInstanceOf [ VariableDeclarationStatement ] ) ) match {
case Some ( s ) =>
log . error ( s" Macro functions cannot declare variables " , s . position )
case None =>
}
statements . flatMap ( _ . find ( _ . isInstanceOf [ ArrayDeclarationStatement ] ) ) match {
case Some ( s ) =>
log . error ( s" Macro functions cannot declare arrays " , s . position )
case None =>
}
}
2018-03-10 20:52:28 +00:00
Seq ( FunctionDeclarationStatement ( name , returnType , params . toList ,
2018-03-15 22:09:19 +00:00
bank ,
2017-12-06 23:23:30 +00:00
addr ,
2020-11-18 09:08:58 +00:00
optimizationHints ,
2018-10-04 19:33:10 +00:00
alignment ,
2017-12-06 23:23:30 +00:00
statements ,
2018-02-01 21:39:38 +00:00
flags ( "macro" ) ,
if ( flags ( "inline" ) ) Some ( true ) else if ( flags ( "noinline" ) ) Some ( false ) else None ,
2017-12-06 23:23:30 +00:00
flags ( "asm" ) ,
flags ( "interrupt" ) ,
2018-03-05 11:05:37 +00:00
flags ( "kernal_interrupt" ) ,
2020-03-19 22:53:16 +00:00
flags ( "const" ) && ! flags ( "asm" ) ,
2018-03-10 20:52:28 +00:00
flags ( "reentrant" ) ) . pos ( p ) )
2017-12-06 23:23:30 +00:00
}
2018-06-12 20:46:20 +00:00
def validateAsmFunctionBody ( p : Position , flags : Set [ String ] , name : String , statements : Option [ List [ Statement ] ] )
2018-07-20 20:46:53 +00:00
val enumVariant : P [ ( String , Option [ Expression ] ) ] = for {
name <- identifier ~/ HWS
2021-01-13 13:38:59 +00:00
value <- ( "=" ~/ AWS ~/ mfExpression ( 1 , false ) ) . ? ~ HWS
2018-07-20 20:46:53 +00:00
} yield name -> value
val enumVariants : P [ List [ ( String , Option [ Expression ] ) ] ] =
( "{" ~/ AWS ~ enumVariant . rep ( sep = NoCut ( EOLOrComma ) ~ ! "}" ~/ Pass ) ~/ AWS ~/ "}" ~/ Pass ) . map ( _ . toList )
val enumDefinition : P [ Seq [ EnumDefinitionStatement ] ] = for {
p <- position ( )
_ <- "enum" ~ ! letterOrDigit ~/ SWS ~ position ( "enum name" )
name <- identifier ~/ HWS
_ <- position ( "enum defintion block" )
variants <- enumVariants ~/ Pass
} yield Seq ( EnumDefinitionStatement ( name , variants ) . pos ( p ) )
2021-02-22 22:23:00 +00:00
val compoundTypeField : P [ FieldDesc ] = ( "array" . ! ~ ! letterOrDigit ~ HWS ~/ Pass ) . ? . flatMap {
2019-12-30 10:50:18 +00:00
case None =>
2021-02-22 22:23:00 +00:00
( identifier ~/ HWS ~ identifier ~/ HWS ) . map {
case ( typ , name ) => FieldDesc ( typ , name , None )
}
2019-12-30 10:50:18 +00:00
case Some ( _ ) =>
2021-02-22 22:23:00 +00:00
( ( "(" ~/ AWS ~/ identifier ~ AWS ~ ")" ) . ? ~/ HWS ~/
identifier ~ HWS ~
"[" ~/ AWS ~/ mfExpression ( nonStatementLevel , false ) ~ AWS ~ "]" ~/ HWS
) . map {
case ( elementType , name , length ) =>
FieldDesc ( elementType . getOrElse ( "byte" ) , name , Some ( length ) )
}
2019-12-30 10:50:18 +00:00
}
2019-04-14 23:30:47 +00:00
2019-09-20 16:33:41 +00:00
val compoundTypeFields : P [ List [ FieldDesc ] ] =
2019-04-15 17:45:26 +00:00
( "{" ~/ AWS ~ compoundTypeField . rep ( sep = NoCut ( EOLOrComma ) ~ ! "}" ~/ Pass ) ~/ AWS ~/ "}" ~/ Pass ) . map ( _ . toList )
2019-04-14 23:30:47 +00:00
2021-02-22 22:23:00 +00:00
val structDefinition : P [ Seq [ StructDefinitionStatement ] ] = {
( position ( ) ~ "struct" ~ ! letterOrDigit ~/ SWS ~/
position ( "struct name" ) . map ( _ => ( ) ) ~ identifier ~/ HWS ~
alignmentDeclaration ( NoAlignment ) . ? ~/ HWS ~
position ( "struct definition block" ) . map ( _ => ( ) ) ~
compoundTypeFields ~/ Pass ) . map {
case ( p , name , align , fields ) =>
Seq ( StructDefinitionStatement ( name , fields , align ) . pos ( p ) )
}
}
2019-04-14 23:30:47 +00:00
2021-02-22 22:23:00 +00:00
val unionDefinition : P [ Seq [ UnionDefinitionStatement ] ] = {
( position ( ) ~ "union" ~ ! letterOrDigit ~/ SWS ~/
position ( "union name" ) . map ( _ => ( ) ) ~ identifier ~/ HWS ~
alignmentDeclaration ( NoAlignment ) . ? ~/ HWS ~
position ( "union definition block" ) . map ( _ => ( ) ) ~
compoundTypeFields ~/ Pass ) . map {
case ( p , name , align , fields ) =>
Seq ( UnionDefinitionStatement ( name , fields , align ) . pos ( p ) )
}
}
2019-04-15 17:45:26 +00:00
2019-07-17 18:50:27 +00:00
val segmentBlock : P [ Seq [ BankedDeclarationStatement ] ] = for {
2019-10-31 11:11:05 +00:00
( _ , bankName ) <- "segment" ~ AWS ~ "(" ~ AWS ~ position ( "segment name" ) ~ identifier ~ AWS ~ ")" ~ AWS ~ "{" ~/ AWS
body <- locatableDefinition . rep ( sep = EOL )
_ <- AWS ~ "}" ~/ Pass
2019-07-17 18:50:27 +00:00
} yield {
2019-10-31 11:11:05 +00:00
body . flatten . map { stmt =>
2019-07-17 18:50:27 +00:00
if ( stmt . bank . isEmpty ) stmt . withChangedBank ( bankName )
else stmt
}
}
2019-10-31 11:11:05 +00:00
def checkForNonlocatableDefinitions : P [ Seq [ BankedDeclarationStatement ] ] =
( ( StringIn ( "alias" , "enum" , "struct" , "union" , "import" ) . ! ~ SWS ) ~/ position ( ) ) . map { x =>
log . fatal ( s" ` ${ x . _1 } ` statements are not allowed inside segment blocks " , Some ( x . _2 ) )
}
def locatableDefinition : P [ Seq [ BankedDeclarationStatement ] ] = checkForNonlocatableDefinitions | segmentBlock | arrayDefinition | functionDefinition | globalVariableDefinition
2019-07-17 18:50:27 +00:00
2018-07-05 23:05:24 +00:00
val program : Parser [ Program ] = for {
2019-10-31 11:11:05 +00:00
_ <- Start ~/ AWS ~/ position ( "top level statement" )
2019-07-17 18:50:27 +00:00
definitions <- ( importStatement | aliasDefinition | enumDefinition | structDefinition | unionDefinition | locatableDefinition ) . rep ( sep = EOL )
2017-12-06 23:23:30 +00:00
_ <- AWS ~ End
2018-03-10 20:52:28 +00:00
} yield Program ( definitions . flatten . toList )
2017-12-06 23:23:30 +00:00
2018-07-05 23:05:24 +00:00
}
object MfParser {
val SWS : P [ Unit ] = P ( CharsWhileIn ( " \t" , min = 1 ) ) . opaque ( "<horizontal whitespace>" )
val HWS : P [ Unit ] = P ( CharsWhileIn ( " \t" , min = 0 ) ) . opaque ( "<horizontal whitespace>" )
val letter : P [ String ] = P ( CharIn ( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" ) . ! )
2019-11-04 13:26:01 +00:00
val mosOpcodeLetter : P [ String ] = P ( CharIn ( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012" ) . ! )
val octalDigit : P [ String ] = P ( CharIn ( "01234567" ) . ! )
2018-07-05 23:05:24 +00:00
val letterOrDigit : P [ Unit ] = P ( CharIn ( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.$1234567890" ) )
2020-08-14 00:22:13 +00:00
val realLetterOrDigit : P [ Unit ] = P ( CharIn ( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.1234567890" ) )
2018-07-05 23:05:24 +00:00
2020-08-14 00:22:13 +00:00
val identifierTail : P [ String ] =
CharsWhileIn ( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.1234567890" , min = 1 ) . rep ( min = 1 , sep = "$" ) . !
2020-09-20 22:14:39 +00:00
val identifier : P [ String ] = ( letter ~ ( "$" . ? ~ identifierTail ) . ? ) . ! . map ( _ . intern ( ) ) . opaque ( "<identifier>" )
2018-07-05 23:05:24 +00:00
2019-10-18 09:01:31 +00:00
val doubleQuotedString : P [ String ] = P ( "\"" ~/ CharsWhile ( c => c != '\"' && c != '\n' && c != '\r' ) . ? . ! ~ "\"" )
2018-07-05 23:05:24 +00:00
2019-10-31 11:13:02 +00:00
def size ( value : Long , wordLiteral : Boolean , int24Literal : Boolean , int32Literal : Boolean , int40Literal : Boolean , int48Literal : Boolean , int56Literal : Boolean , int64Literal : Boolean ) : Int = {
val w = value > 0xff || value < - 0x80 || wordLiteral
val f = value > 0xffff || value < - 0x8000 || int24Literal
val l = value > 0xffffff || value < - 0x800000 || int32Literal
2019-11-03 21:25:08 +00:00
val q5 = value > 0xFFFFffff L || value < - 0x80000000 L || int40Literal
2019-10-31 11:13:02 +00:00
val q6 = value > 0xffFFFFffff L || value < - 0x8000000000 L || int48Literal
val q7 = value > 0xffffFFFFffff L || value < - 0x800000000000 L || int56Literal
val q8 = value > 0xFFffffFFFFffff L || value < - 0x8000000000000 L || int64Literal
if ( q8 ) 8 else if ( q7 ) 7 else if ( q6 ) 6 else if ( q5 ) 5 else if ( l ) 4 else if ( f ) 3 else if ( w ) 2 else 1
2018-07-05 23:05:24 +00:00
}
2018-07-12 16:30:35 +00:00
def sign ( abs : Long , minus : Boolean ) : Long = if ( minus ) - abs else abs
2018-07-05 23:05:24 +00:00
2018-12-16 13:38:57 +00:00
val invalidCharLiteralTypes : BitSet = BitSet (
2018-07-05 23:05:24 +00:00
Character . LINE_SEPARATOR ,
Character . PARAGRAPH_SEPARATOR ,
Character . CONTROL ,
Character . PRIVATE_USE ,
Character . SURROGATE ,
Character . UNASSIGNED )
val decimalAtom : P [ LiteralExpression ] =
for {
minus <- "-" . ! . ?
s <- CharsWhileIn ( "1234567890" , min = 1 ) . ! . opaque ( "<decimal digits>" ) ~ ! ( "x" | "b" )
} yield {
2018-07-12 16:30:35 +00:00
val abs = parseLong ( s , 10 )
2018-07-05 23:05:24 +00:00
val value = sign ( abs , minus . isDefined )
2019-10-31 11:13:02 +00:00
LiteralExpression ( value , size ( value , s . length > 3 , s . length > 5 , s . length > 7 , s . length > 10 , s . length > 13 , s . length > 15 , s . length > 17 ) )
2018-07-05 23:05:24 +00:00
}
val binaryAtom : P [ LiteralExpression ] =
for {
minus <- "-" . ! . ?
_ <- P ( "0b" | "%" ) ~/ Pass
s <- CharsWhileIn ( "01" , min = 1 ) . ! . opaque ( "<binary digits>" )
} yield {
2018-07-12 16:30:35 +00:00
val abs = parseLong ( s , 2 )
2018-07-05 23:05:24 +00:00
val value = sign ( abs , minus . isDefined )
2019-10-31 11:13:02 +00:00
LiteralExpression ( value , size ( value , s . length > 8 , s . length > 16 , s . length > 24 , s . length > 32 , s . length > 40 , s . length > 48 , s . length > 52 ) )
2018-07-05 23:05:24 +00:00
}
val hexAtom : P [ LiteralExpression ] =
for {
minus <- "-" . ! . ?
_ <- P ( "0x" | "0X" | "$" ) ~/ Pass
s <- CharsWhileIn ( "1234567890abcdefABCDEF" , min = 1 ) . ! . opaque ( "<hex digits>" )
} yield {
2018-07-12 16:30:35 +00:00
val abs = parseLong ( s , 16 )
2018-07-05 23:05:24 +00:00
val value = sign ( abs , minus . isDefined )
2019-10-31 11:13:02 +00:00
LiteralExpression ( value , size ( value , s . length > 2 , s . length > 4 , s . length > 6 , s . length > 8 , s . length > 10 , s . length > 12 , s . length > 14 ) )
2018-07-05 23:05:24 +00:00
}
2018-08-03 11:23:37 +00:00
val intelHexAtom : P [ LiteralExpression ] =
for {
minus <- "-" . ! . ?
head <- CharIn ( "0123456789" ) . !
tail <- CharsWhileIn ( "1234567890abcdefABCDEF" , min = 1 ) . ! . opaque ( "<hex digits>" )
_ <- P ( "h" | "H" )
} yield {
// check for marking zero:
val s = if ( head == "0" && tail . nonEmpty && tail . head > '9' ) tail else head + tail
val abs = parseLong ( s , 16 )
val value = sign ( abs , minus . isDefined )
2019-10-31 11:13:02 +00:00
LiteralExpression ( value , size ( value , s . length > 2 , s . length > 4 , s . length > 6 , s . length > 8 , s . length > 10 , s . length > 12 , s . length > 14 ) )
2018-08-03 11:23:37 +00:00
}
2018-07-05 23:05:24 +00:00
val octalAtom : P [ LiteralExpression ] =
for {
minus <- "-" . ! . ?
_ <- P ( "0o" | "0O" ) ~/ Pass
s <- CharsWhileIn ( "01234567" , min = 1 ) . ! . opaque ( "<octal digits>" )
} yield {
2018-07-12 16:30:35 +00:00
val abs = parseLong ( s , 8 )
2018-07-05 23:05:24 +00:00
val value = sign ( abs , minus . isDefined )
2019-10-31 11:13:02 +00:00
LiteralExpression ( value , size ( value , s . length > 3 , s . length > 6 , s . length > 8 , s . length > 11 , s . length > 14 , s . length > 16 , s . length > 19 ) )
2018-07-05 23:05:24 +00:00
}
val quaternaryAtom : P [ LiteralExpression ] =
for {
minus <- "-" . ! . ?
_ <- P ( "0q" | "0Q" ) ~/ Pass
s <- CharsWhileIn ( "0123" , min = 1 ) . ! . opaque ( "<quaternary digits>" )
} yield {
2018-07-12 16:30:35 +00:00
val abs = parseLong ( s , 4 )
2018-07-05 23:05:24 +00:00
val value = sign ( abs , minus . isDefined )
2019-10-31 11:13:02 +00:00
LiteralExpression ( value , size ( value , s . length > 4 , s . length > 8 , s . length > 12 , s . length > 16 , s . length > 20 , s . length > 24 , s . length > 28 ) )
2018-07-05 23:05:24 +00:00
}
val mfOperators = List (
2020-08-14 00:22:13 +00:00
List ( "+=" , "-=" , "+'=" , "-'=" , "^=" , "&=" , "|=" , "*=" , "*'=" , "<<=" , ">>=" , "<<'=" , ">>'=" , "/=" , "%%=" , "=" , "$*=" , "$+=" , "$-=" , "$<<=" , "$>>=" ) ,
2018-07-05 23:05:24 +00:00
List ( "||" , "^^" ) ,
List ( "&&" ) ,
List ( "==" , "<=" , ">=" , "!=" , "<" , ">" ) ,
List ( ":" ) ,
2020-08-14 00:22:13 +00:00
List ( "+'" , "-'" , "<<'" , ">>'" , ">>>>" , "+" , "-" , "&" , "|" , "^" , "<<" , ">>" , "$+" , "$-" , "$<<" , "$>>" ) ,
List ( "*'" , "$*" , "*" , "/" , "%%" ) )
val mfOperatorNormalizations = Map (
"$+" -> "+'" ,
"$-" -> "-'" ,
"$+=" -> "+'=" ,
"$-=" -> "-'=" ,
"$<<" -> "<<'" ,
"$>>" -> ">>'" ,
"$<<=" -> "<<'=" ,
"$>>=" -> ">>'=" ,
"$*" -> "*'" ,
"$*=" -> "*'=" ,
)
2018-07-05 23:05:24 +00:00
2019-06-12 20:55:34 +00:00
val mfOperatorsDropFlatten : IndexedSeq [ List [ String ] ] = mfOperators . indices . map ( i => mfOperators . drop ( i ) . flatten )
2018-07-05 23:05:24 +00:00
val nonStatementLevel = 1 // everything but not `=`
val mathLevel = 4 // the `:` operator
2019-10-31 11:09:20 +00:00
val minusLevel = 5 // the `-` operator
2018-07-05 23:05:24 +00:00
2017-12-06 23:23:30 +00:00
}