Skip to content

Instantly share code, notes, and snippets.

@gzoritchak
Last active April 8, 2018 16:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gzoritchak/4739607 to your computer and use it in GitHub Desktop.
Save gzoritchak/4739607 to your computer and use it in GitHub Desktop.
Parsing expressions arithmétiques en kotlin.
package codestory2013.gzoritchak
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.Locale
import java.util.StringTokenizer
import kotlin.math.*
//Extensions
fun StringTokenizer.toList(): List<String> {
val ret: MutableList<String> = arrayListOf()
while (this.hasMoreElements()) ret.add(this.nextToken())
return ret
}
fun process(val toProcess: String): String =
DecimalFormat("#.############################################################################################################"
, DecimalFormatSymbols(Locale.FRANCE))
.format(ArithmeticExpression(toProcess).parse().eval())
//Une expression
trait Expr {
fun eval(): BigDecimal
}
class Addition (val op1: Expr, val op2: Expr): Expr { override fun eval() = op1.eval() + op2.eval() }
class Soustraction (val op1: Expr, val op2: Expr): Expr { override fun eval() = op1.eval() - op2.eval() }
class Multiplication(val op1: Expr, val op2: Expr): Expr { override fun eval() = op1.eval() * op2.eval() }
class Division (val op1: Expr, val op2: Expr): Expr { override fun eval() = op1.eval().divide(op2.eval(), 10, RoundingMode.HALF_UP) }
class Nombre (val value: BigDecimal): Expr { override fun eval() = value }
/**
* la grammaire des expressions arithmétiques
* parsePrecedence3 -> parsePrecedence2.
* parsePrecedence3 -> parsePrecedence2 , [+] , parsePrecedence3.
* parsePrecedence3 -> parsePrecedence2 , [-] , parsePrecedence3.
*
* parsePrecedence2 -> parsePrecedence1.
* parsePrecedence2 -> parsePrecedence2 , [*] , parsePrecedence1.
* parsePrecedence2 -> parsePrecedence2 , [/] , parsePrecedence1.
*
* parsePrecedence1 -> ['('] , parsePrecedence3 , [')'].
* parsePrecedence1 -> [N] , {BigDecimal(N)}.
*/
class ArithmeticExpression(val expression: String) {
val tokens: List<String> = StringTokenizer(expression + "$", "(),+-*/$", true).toList()
var tokenId = -1
var token: String = ""
fun toString() = expression
// Point d'entrée du parsing
fun parse(): Expr {
next()
return parsePrecedence3()
}
fun next(): Unit {
token = tokens.get(tokenId++)
}
//
fun parsePrecedence3(): Expr {
val left = parsePrecedence2()
if (token.equals("+") || token.equals("-")) {
val op = token
next()
return when(op){
"+" -> Addition (left, parsePrecedence3())
else -> Soustraction(left, parsePrecedence3())
}
} else
return left
}
/**
* Le parse de la multiplication et division. Si plusieurs opérations à la suite, on essaye d'étendre de gauche
* à droite.
*/
fun parsePrecedence2(var left: Expr = parsePrecedence1()): Expr {
while ( token.equals("*") || token.equals("/")) {
val op = token
next()
return when(op){
"*" -> parsePrecedence2(Multiplication(left, parsePrecedence1()))
else -> parsePrecedence2(Division (left, parsePrecedence1()))
}
}
return left
}
fun parsePrecedence1(): Expr {
var exp: Expr
if (token.equals("(")) {
next()
exp = parsePrecedence3()
next()
return exp
} else {
//parsing nombre
var sign = 1
if(token.equals("-")){
sign = -1
next()
}
val partieEntiere = token
next()
var partieDecimale = "0"
if(token.equals(",")){
next()
partieDecimale = token
next()
}
return Nombre(BigDecimal(partieEntiere + "." + partieDecimale) * BigDecimal(sign))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment