Здравствуйте, Ikemefula, Вы писали:
I>В целом, ты потратил много больше времени на форум, нежели на тот простецкий парсер по методу Пратта.
Ну не много больше, но сопопставимо, и?
На форуме я отвлекаюсь от работы, а не пытаюсь работать еще.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Здравствуйте, Sinclair, Вы писали:
S>Не знаю, будет ли парсер, основанный на Пратте, более простым.
На счет простоты есть сомнения. Я взял готовый парсер, доработал выбрасывая лишнее и потратил треть от времени на recursive descent, который делал честно с нуля.
Т.е. мне не пришлось тратить время на вещи вида "а как реализовать это правило", "а как надо парсить вот такое", "нужен префикс или инфикс" и тд — собтсвенно самое сложное я взял готовым. Сам алгоритм Пратта на этом фоне можно даже никак не учитывать.
При этом кода вышло чуть больше, чем в recursive descent.
Подозреваю, если вдруг решу сделать такое полностью с нуля, времени уйдет намного больше. В бнф грамматике всё дадено, что и как парсить, а тут такого нет
| Парсер по методу Пратта |
| Парсер:
function make_parse() {
let symbol_table = {};
let currentToken;
let tokens;
let token_nr;
let itself = function () {
return this;
};
let advance = function (id) {
let type, o, token, value;
if (id && currentToken.id !== id) {
currentToken.error(`Expected ${id}`);
}
if (token_nr >= tokens.length) {
currentToken = symbol_table['(end)'];
return;
}
token = tokens[token_nr];
token_nr += 1;
value = token.value;
type = token.type;
if (type === 'name') {
o = symbol_table['(name)'];
type = 'variable';
} else if (type === 'operator' || type === 'group') {
o = symbol_table[value];
if (!o) {
token.error('Unknown operator.');
}
} else if (type === 'number') {
o = symbol_table['(literal)'];
type = 'literal';
} else {
token.error('Unexpected token.');
}
currentToken = Object.create(o);
currentToken.type = type;
// currentToken.line_nr = token.line_nr;
// currentToken.column_nr = token.column_nr;
currentToken.value = value;
return currentToken;
};
let expression = function (rbp) {
let left;
let t = currentToken;
advance();
left = t.nud();
while (rbp < currentToken.lbp) {
t = currentToken;
advance();
left = t.led(left);
}
return left;
};
let original_symbol = {
nud: function () {
this.error('Undefined.');
},
led: function (left) {
this.error('Missing operator.');
}
};
let symbol = function (id, bp) {
let s = symbol_table[id];
bp = bp || 0;
if (s) {
if (bp >= s.lbp) {
s.lbp = bp;
}
} else {
s = Object.create(original_symbol);
s.id = s.value = id;
s.lbp = bp;
symbol_table[id] = s;
}
return s;
};
let infix = function (id, bp, led) {
let s = symbol(id, bp);
s.led = led || function (left) {
this.first = left;
this.second = expression(bp);
this.type = 'binary';
return this;
};
return s;
};
let prefix = function (id, nud) {
let s = symbol(id);
s.nud = nud || function () {
this.first = expression(70);
this.type = 'unary';
return this;
};
return s;
};
symbol('(end)');
symbol('(name)').nud = itself;
symbol(')');
symbol('(literal)').nud = itself;
infix('+', 50);
infix('-', 50);
infix('*', 60);
infix('/', 60);
infix('%', 60);
infix('^', 70);
prefix('-');
prefix('(', function () {
let e = expression(0);
advance(')');
return e;
});
return function (array_of_tokens) {
tokens = array_of_tokens;
token_nr = 0;
advance();
let e = expression(0);
advance('(end)');
return e;
};
}
module.exports = {make_parse};
Токенизатор:
const rx_crlf = /\n|\r\n?/;
function tokenize(source) {
const lines = (Array.isArray(source))
? source
: source.split(rx_crlf);
const result = [];
lines.forEach(function (line, line_nr) {
const rx_token = /(\u0020+)|([a-zA-Z][a-zA-Z_0-9]*)|(\d+(?:\.\d+)?(?:[eE][+\-]?\d+)?)|(\(|\))|([\+\-\/*\%\^])/y;
// Capture Group
// [1] Whitespace
// [2] Name
// [3] Number
// [4] Punctuator
let column_nr = 0;
let make = function (type, value) {
// Make a token object and append it to the result.
result.push({
type,
value,
line_nr,
column_nr
});
};
while (column_nr < line.length) {
let captives = rx_token.exec(line);
if (!captives) {
throw new SyntaxError(
"line "
+ line_nr
+ " column "
+ column_nr
);
} else if (captives[1]) {
// ignore
} else if (captives[2]) {
make("name", captives[2]);
} else if (captives[3]) {
make("number", captives[3]);
} else if (captives[4]) {
make("group", captives[4]);
} else if (captives[5]) {
make("operator", captives[5]);
}
column_nr = rx_token.lastIndex;
}
});
return result;
}
module.exports = {tokenize};
const {tokenize} = require('./tokenize');
const {make_parse} = require('./pratt');
const parse = make_parse();
const tokens = tokenize('2 * (-2.9*k - 8)');
const x = parse(tokens);
console.log(JSON.stringify(x, null, 2));
|
| |
Вот хороший пример — по фрагменту кода догадаться, что же именно оно парсит
infix("(", 80, function (left) {
let a = [];
if (left.id === "." || left.id === "[") {
this.arity = "ternary";
this.first = left.first;
this.second = left.second;
this.third = a;
} else {
this.arity = "binary";
this.first = left;
this.second = a;
if ((left.arity !== "unary" || left.id !== "function") &&
left.arity !== "name" && left.id !== "(" &&
left.id !== "&&" && left.id !== "||" && left.id !== "?") {
left.error("Expected a variable name.");
}
}
if (token.id !== ")") {
while (true) {
a.push(expression(0));
if (token.id !== ",") {
break;
}
advance(",");
}
}
advance(")");
return this;
});
Здравствуйте, Shmj, Вы писали:
S>Здравствуйте, _NN_, Вы писали:
_NN>>Да.
_NN>>Это контролируется через LanguageMode.
S>Если не секрет, какие вы настройки примерно использовали? Достаточно работы Get-Variable и операций со строками.
Например так.
Можно скопировать уже готовые команды или добавлять свои.
using System;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
class A
{
public static void Main()
{
// Full
InitialSessionState fullIss = InitialSessionState.CreateDefault();
// No commands !
InitialSessionState iss = InitialSessionState.CreateRestricted(SessionCapabilities.Language);
Console.WriteLine(iss.Commands.Count); // 0
// No external calls
iss.LanguageMode = PSLanguageMode.ConstrainedLanguage;
// Add Get-Variable
var getVariableCmdlet = fullIss.Commands.First(c => c.Name == "Get-Variable");
iss.Commands.Add(getVariableCmdlet);
// Only Get-Variable exists
foreach (var c in iss.Commands) Console.WriteLine(c.Name);
// Run script
PowerShell ps = PowerShell.Create(iss);
var r = ps.AddScript(@"$a = 'x,y';
$b = (get-variable a).Value.Split(',');
return $b;
");
foreach (var x in r.Invoke())
{
Console.WriteLine(x);
}
}
}
Здравствуйте, Shmj, Вы писали:
НС>>Начни с книжки с драконом.
S>Можно ткнуть в книгу?
https://www.ozon.ru/product/kompilyatory-printsipy-tehnologii-i-instrumentariy-148627197/?sh=OfDwl_nr... << RSDN@Home 1.3.17 alpha 5 rev. 62>>