Сообщение Re[13]: [Nitra] Пример простого языка вычисляющего выражения от 10.12.2015 10:57
Изменено 10.12.2015 13:00 VladD2
Здравствуйте, novitk, Вы писали:
N>Это абсолютно обычный FP-код.
Это ты рассказываешь человеку который каждый день ФП применяет. ФП разное бывает. Бывает по делу, а бывает набор спермитазойдов и смайликов, как в твоем случае.
И это не мудрено. Для описания парсеров нужна грамматика, а не ее кодирование с помощью смайликов.
Вот это:
грамматика.
А это:
[scala]
class Expr;
class BinOp(val evalF: (Int, Int) => Int, val expr: Expr);
def mkBinOp = (new BinOp(_, _)).curried
val binOpGroups = Seq(
Seq("*" -> mkBinOp(_*_), "/" -> mkBinOp(_/_)),
Seq("^^" -> mkBinOp(Math.pow(_, _).toInt)),
Seq("+" -> mkBinOp(_+_), "+" -> mkBinOp(_+_)))
case class NumberExpr(num: Int) extends Expr;
case class VarExpr(name: String) extends Expr;
case class OpExpr(expr: Expr, subExprs: List[BinOp]) extends Expr;
class VarDef(val name: String, val expr: Expr)
def makeOpExpr(p: ~[Expr, List[BinOp]]): Expr = p match {
case f ~ Nil => f
case f ~ l => OpExpr(f, l)
}
def binOpGroupParser(termAbove: Parser[Expr], opGroup: Seq[(String, Expr => BinOp)]): Parser[Expr] = {
val opGroupParsers = opGroup.map { case (opCode, constr) => opCode ~> termAbove ^^ constr }.reduce(_|_)
(termAbove ~ rep(opGroupParsers)) ^^ makeOpExpr
}
def factor: Parser[Expr] = wholeNumber ^^ (s => NumberExpr(s.toInt)) | ident ^^ VarExpr | "(" ~> expr <~ ")"
def expr: Parser[Expr] = binOpGroups.foldLeft(factor)(binOpGroupParser(_, _))
def varDef: Parser[VarDef] = ("var" ~> ident) ~ ("=" ~> expr <~ ";") ^^ { case n ~ e => new VarDef(n, e) }
def varDefParseAll(in: CharSequence): ParseResult[VarDef] = parseAll(varDef, in)
[/scala]
закарючки.
N>Вам parsec не нравиться что-ли?
Конечно, нет. Грамматика ANTLR мне нравится, а закарчки на Хаскеле эмулирующие грамматику — нет.
N>Его там всего 4 строчки. Предположи, что я взял ANTLR или что тебе там нравится, и забудь.
Зачем мне что-то предполагать? Ты обещал, что на Скале будет проще. Я вижу что получился какой-то не читаемый пипец с кучей упрощений, не поддерживающий IDE ни в каком виде и в добавок еще и потенциально тормозной из-за комбинаторов.
VD>>Вычисления перемешаны с правилами грамматики так, что нужно пол часа вчитываться, чтобы понять что где.
N>O чем ты?
Вот об этом "(s => NumberExpr(s.toInt))", об это "{ case n ~ e => new VarDef(n, e) }" и о другой грязи.
Я уже говорил, о том, что это мы проходили уже очень давно. У нас есть в разы более чистый путь — rule-методы:
это выглядит не плохо только на примитивных примерах. Смешение кода и грамматики — плохая идея. Да и вычисления прямо на дереве разбора — тоже.
В серьезных языках нужны такие вещи как полмиморфизм, разделение грамматики и семантики, неявное задание последовательности вычислений и т.п.
Так же есть и чисто технические аспекты которые на практике так же важны.
N>EvalContext абсолютно независим от парсера. Я поместил его в один файл только для удобства оформления.
Сомнительное удобство, кстати. Погоня за минимизацией символов. Людям это не надо. Людям нужно минимизировать число сущностей которые они обязаны одновременно держать в голове. А количество строк их абсолютно не волнует. Иначе все писали бы даже не на Хаскеле, а на каком нибудь J или K.
N>Это абсолютно обычный FP-код.
Это ты рассказываешь человеку который каждый день ФП применяет. ФП разное бывает. Бывает по делу, а бывает набор спермитазойдов и смайликов, как в твоем случае.
И это не мудрено. Для описания парсеров нужна грамматика, а не ее кодирование с помощью смайликов.
Вот это:
syntax module SampleSyntax
{
using Nitra.Core;
using Nitra.CStyleComments;
regex Keyword = "var";
[Reference] token Reference = !Keyword IdentifierBody;
[Name] token Name = !Keyword IdentifierBody;
[StartRule] syntax TopRule = (VariableDeclaration nl)*;
syntax VariableDeclaration = "var" sm Name sm "=" sm Expression ";";
syntax Expression
{
| [SpanClass(Number)] Num = Digits { Digits = ['0'..'9']+; }
| Braces = "(" Expression ")";
| Variable = Reference;
precedence Sum:
| Sum = Expression sm Operator="+" sm Expression;
| Sub = Expression sm Operator="-" sm Expression;
precedence Mul:
| Mul = Expression sm Operator="*" sm Expression;
| Div = Expression sm Operator="/" sm Expression;
precedence Unary:
| Plus = Operator="+" Expression
| Minus = Operator="-" Expression
}
}
грамматика.
А это:
[scala]
class Expr;
class BinOp(val evalF: (Int, Int) => Int, val expr: Expr);
def mkBinOp = (new BinOp(_, _)).curried
val binOpGroups = Seq(
Seq("*" -> mkBinOp(_*_), "/" -> mkBinOp(_/_)),
Seq("^^" -> mkBinOp(Math.pow(_, _).toInt)),
Seq("+" -> mkBinOp(_+_), "+" -> mkBinOp(_+_)))
case class NumberExpr(num: Int) extends Expr;
case class VarExpr(name: String) extends Expr;
case class OpExpr(expr: Expr, subExprs: List[BinOp]) extends Expr;
class VarDef(val name: String, val expr: Expr)
def makeOpExpr(p: ~[Expr, List[BinOp]]): Expr = p match {
case f ~ Nil => f
case f ~ l => OpExpr(f, l)
}
def binOpGroupParser(termAbove: Parser[Expr], opGroup: Seq[(String, Expr => BinOp)]): Parser[Expr] = {
val opGroupParsers = opGroup.map { case (opCode, constr) => opCode ~> termAbove ^^ constr }.reduce(_|_)
(termAbove ~ rep(opGroupParsers)) ^^ makeOpExpr
}
def factor: Parser[Expr] = wholeNumber ^^ (s => NumberExpr(s.toInt)) | ident ^^ VarExpr | "(" ~> expr <~ ")"
def expr: Parser[Expr] = binOpGroups.foldLeft(factor)(binOpGroupParser(_, _))
def varDef: Parser[VarDef] = ("var" ~> ident) ~ ("=" ~> expr <~ ";") ^^ { case n ~ e => new VarDef(n, e) }
def varDefParseAll(in: CharSequence): ParseResult[VarDef] = parseAll(varDef, in)
[/scala]
закарючки.
N>Вам parsec не нравиться что-ли?
Конечно, нет. Грамматика ANTLR мне нравится, а закарчки на Хаскеле эмулирующие грамматику — нет.
N>Его там всего 4 строчки. Предположи, что я взял ANTLR или что тебе там нравится, и забудь.
Зачем мне что-то предполагать? Ты обещал, что на Скале будет проще. Я вижу что получился какой-то не читаемый пипец с кучей упрощений, не поддерживающий IDE ни в каком виде и в добавок еще и потенциально тормозной из-за комбинаторов.
VD>>Вычисления перемешаны с правилами грамматики так, что нужно пол часа вчитываться, чтобы понять что где.
N>O чем ты?
Вот об этом "(s => NumberExpr(s.toInt))", об это "{ case n ~ e => new VarDef(n, e) }" и о другой грязи.
Я уже говорил, о том, что это мы проходили уже очень давно. У нас есть в разы более чистый путь — rule-методы:
syntax Expr
{
Value() : double;
| [SpanClass(Number)] Number
{
regex Digit = ['0'..'9'];
regex Number = Digit+ ('.' Digit+)?;
override Value = double.Parse(GetText(Number));
}
| ParenthesesParentheses = '(' Expr ')' { override Value = Expr.Value(); }
precedence Additive:
| Add = Expr '+' Expr { override Value = Expr1.Value() + Expr2.Value(); }
| Sub = Expr '-' Expr { override Value = Expr1.Value() - Expr2.Value(); }
precedence Multiplicative:
| Mul = Expr '*' Expr { override Value = Expr1.Value() * Expr2.Value(); }
| Div = Expr '/' Expr { override Value = Expr1.Value() / Expr2.Value(); }
precedence Power:
| Pow = Expr '^' Expr precedence 30 right-associative { override Value = Math.Pow(Expr1.Value(), Expr2.Value()); }
precedence Unary:
| Neg = '-' Expr { override Value = -Expr.Value(); }
}
это выглядит не плохо только на примитивных примерах. Смешение кода и грамматики — плохая идея. Да и вычисления прямо на дереве разбора — тоже.
В серьезных языках нужны такие вещи как полмиморфизм, разделение грамматики и семантики, неявное задание последовательности вычислений и т.п.
Так же есть и чисто технические аспекты которые на практике так же важны.
N>EvalContext абсолютно независим от парсера. Я поместил его в один файл только для удобства оформления.
Сомнительное удобство, кстати. Погоня за минимизацией символов. Людям это не надо. Людям нужно минимизировать число сущностей которые они обязаны одновременно держать в голове. А количество строк их абсолютно не волнует. Иначе все писали бы даже не на Хаскеле, а на каком нибудь J или K.
Re[13]: [Nitra] Пример простого языка вычисляющего выражения
Здравствуйте, novitk, Вы писали:
N>Это абсолютно обычный FP-код.
Это ты рассказываешь человеку который каждый день ФП применяет. ФП разное бывает. Бывает по делу, а бывает набор спермитазойдов и смайликов, как в твоем случае.
И это не мудрено. Для описания парсеров нужна грамматика, а не ее кодирование с помощью смайликов.
Вот это:
грамматика.
А это:
закарючки.
N>Вам parsec не нравиться что-ли?
Конечно, нет. Грамматика ANTLR мне нравится, а закарчки на Хаскеле эмулирующие грамматику — нет.
N>Его там всего 4 строчки. Предположи, что я взял ANTLR или что тебе там нравится, и забудь.
Зачем мне что-то предполагать? Ты обещал, что на Скале будет проще. Я вижу что получился какой-то не читаемый пипец с кучей упрощений, не поддерживающий IDE ни в каком виде и в добавок еще и потенциально тормозной из-за комбинаторов.
VD>>Вычисления перемешаны с правилами грамматики так, что нужно пол часа вчитываться, чтобы понять что где.
N>O чем ты?
Вот об этом "(s => NumberExpr(s.toInt))", об это "{ case n ~ e => new VarDef(n, e) }" и о другой грязи.
Я уже говорил, о том, что это мы проходили уже очень давно. У нас есть в разы более чистый путь — rule-методы:
это выглядит не плохо только на примитивных примерах. Смешение кода и грамматики — плохая идея. Да и вычисления прямо на дереве разбора — тоже.
В серьезных языках нужны такие вещи как полмиморфизм, разделение грамматики и семантики, неявное задание последовательности вычислений и т.п.
Так же есть и чисто технические аспекты которые на практике так же важны.
N>EvalContext абсолютно независим от парсера. Я поместил его в один файл только для удобства оформления.
Сомнительное удобство, кстати. Погоня за минимизацией символов. Людям это не надо. Людям нужно минимизировать число сущностей которые они обязаны одновременно держать в голове. А количество строк их абсолютно не волнует. Иначе все писали бы даже не на Хаскеле, а на каком нибудь J или K.
N>Это абсолютно обычный FP-код.
Это ты рассказываешь человеку который каждый день ФП применяет. ФП разное бывает. Бывает по делу, а бывает набор спермитазойдов и смайликов, как в твоем случае.
И это не мудрено. Для описания парсеров нужна грамматика, а не ее кодирование с помощью смайликов.
Вот это:
syntax module SampleSyntax
{
using Nitra.Core;
using Nitra.CStyleComments;
regex Keyword = "var";
[Reference] token Reference = !Keyword IdentifierBody;
[Name] token Name = !Keyword IdentifierBody;
[StartRule] syntax TopRule = (VariableDeclaration nl)*;
syntax VariableDeclaration = "var" sm Name sm "=" sm Expression ";";
syntax Expression
{
| [SpanClass(Number)] Num = Digits { Digits = ['0'..'9']+; }
| Braces = "(" Expression ")";
| Variable = Reference;
precedence Sum:
| Sum = Expression sm Operator="+" sm Expression;
| Sub = Expression sm Operator="-" sm Expression;
precedence Mul:
| Mul = Expression sm Operator="*" sm Expression;
| Div = Expression sm Operator="/" sm Expression;
precedence Unary:
| Plus = Operator="+" Expression
| Minus = Operator="-" Expression
}
}
грамматика.
А это:
class Expr;
class BinOp(val evalF: (Int, Int) => Int, val expr: Expr);
def mkBinOp = (new BinOp(_, _)).curried
val binOpGroups = Seq(
Seq("*" -> mkBinOp(_*_), "/" -> mkBinOp(_/_)),
Seq("^^" -> mkBinOp(Math.pow(_, _).toInt)),
Seq("+" -> mkBinOp(_+_), "+" -> mkBinOp(_+_)))
case class NumberExpr(num: Int) extends Expr;
case class VarExpr(name: String) extends Expr;
case class OpExpr(expr: Expr, subExprs: List[BinOp]) extends Expr;
class VarDef(val name: String, val expr: Expr)
def makeOpExpr(p: ~[Expr, List[BinOp]]): Expr = p match {
case f ~ Nil => f
case f ~ l => OpExpr(f, l)
}
def binOpGroupParser(termAbove: Parser[Expr], opGroup: Seq[(String, Expr => BinOp)]): Parser[Expr] = {
val opGroupParsers = opGroup.map { case (opCode, constr) => opCode ~> termAbove ^^ constr }.reduce(_|_)
(termAbove ~ rep(opGroupParsers)) ^^ makeOpExpr
}
def factor: Parser[Expr] = wholeNumber ^^ (s => NumberExpr(s.toInt)) | ident ^^ VarExpr | "(" ~> expr <~ ")"
def expr: Parser[Expr] = binOpGroups.foldLeft(factor)(binOpGroupParser(_, _))
def varDef: Parser[VarDef] = ("var" ~> ident) ~ ("=" ~> expr <~ ";") ^^ { case n ~ e => new VarDef(n, e) }
def varDefParseAll(in: CharSequence): ParseResult[VarDef] = parseAll(varDef, in)
закарючки.
N>Вам parsec не нравиться что-ли?
Конечно, нет. Грамматика ANTLR мне нравится, а закарчки на Хаскеле эмулирующие грамматику — нет.
N>Его там всего 4 строчки. Предположи, что я взял ANTLR или что тебе там нравится, и забудь.
Зачем мне что-то предполагать? Ты обещал, что на Скале будет проще. Я вижу что получился какой-то не читаемый пипец с кучей упрощений, не поддерживающий IDE ни в каком виде и в добавок еще и потенциально тормозной из-за комбинаторов.
VD>>Вычисления перемешаны с правилами грамматики так, что нужно пол часа вчитываться, чтобы понять что где.
N>O чем ты?
Вот об этом "(s => NumberExpr(s.toInt))", об это "{ case n ~ e => new VarDef(n, e) }" и о другой грязи.
Я уже говорил, о том, что это мы проходили уже очень давно. У нас есть в разы более чистый путь — rule-методы:
syntax Expr
{
Value() : double;
| [SpanClass(Number)] Number
{
regex Digit = ['0'..'9'];
regex Number = Digit+ ('.' Digit+)?;
override Value = double.Parse(GetText(Number));
}
| ParenthesesParentheses = '(' Expr ')' { override Value = Expr.Value(); }
precedence Additive:
| Add = Expr '+' Expr { override Value = Expr1.Value() + Expr2.Value(); }
| Sub = Expr '-' Expr { override Value = Expr1.Value() - Expr2.Value(); }
precedence Multiplicative:
| Mul = Expr '*' Expr { override Value = Expr1.Value() * Expr2.Value(); }
| Div = Expr '/' Expr { override Value = Expr1.Value() / Expr2.Value(); }
precedence Power:
| Pow = Expr '^' Expr precedence 30 right-associative { override Value = Math.Pow(Expr1.Value(), Expr2.Value()); }
precedence Unary:
| Neg = '-' Expr { override Value = -Expr.Value(); }
}
это выглядит не плохо только на примитивных примерах. Смешение кода и грамматики — плохая идея. Да и вычисления прямо на дереве разбора — тоже.
В серьезных языках нужны такие вещи как полмиморфизм, разделение грамматики и семантики, неявное задание последовательности вычислений и т.п.
Так же есть и чисто технические аспекты которые на практике так же важны.
N>EvalContext абсолютно независим от парсера. Я поместил его в один файл только для удобства оформления.
Сомнительное удобство, кстати. Погоня за минимизацией символов. Людям это не надо. Людям нужно минимизировать число сущностей которые они обязаны одновременно держать в голове. А количество строк их абсолютно не волнует. Иначе все писали бы даже не на Хаскеле, а на каком нибудь J или K.