LINUX.ORG.RU
ФорумTalks

Все AI сломали зубы о написание парсера Brainfuck'а от Cloudflare

 


0

1

Я просил код парсера JS сгенерировать ChatGPT, DeepSeek, Qwen и в итоге написал все сам. тут вроде все просто:

  • Из значений только пустой массив [].
  • Операторы: +, !, (, ). Приоритет должен соблюдаться.
  • Под капотом у нас строки в виде чисел, числа, булев и пустые массивы
import re


class JSExpressionParser:
    tokenizer = re.compile(r"\[\]|[+!()]")

    def __init__(self):
        self.index = 0
        self.token = None
        self.tokens = []

    def advance(self):
        try:
            self.token = self.tokens[self.index]
            self.index += 1
        except IndexError:
            self.token = None

    def parse(self, expression: str):
        self.tokens = self.tokenizer.findall(expression)
        self.index = 0
        self.advance()
        result = self.expression()
        assert self.token is None, f"unexpected token: {self.token!r}"
        return result

    def expression(self):
        left = self.factor()
        while self.token == "+":
            self.advance()
            right = self.factor()
            left = self.js_add(left, right)
        return left

    def js_add(self, left, right):
        # Если одно из значений строка, результат — строка
        # [] + 1 === '1'
        if isinstance(left, (str, list)) or isinstance(right, (str, list)):
            result = self.to_string(left) + self.to_string(right)
        else:
            result = self.to_number(left) + self.to_number(right)
        # print(f"{left!r} + {right!r} = {result!r}")
        return result

    def factor(self):
        if self.token == "+":
            self.advance()
            return self.to_number(self.factor())
        elif self.token == "!":
            self.advance()
            return not self.to_boolean(self.factor())
        elif self.token == "(":
            self.advance()
            result = self.expression()
            assert self.token == ")", (
                f"unexpected token: {self.token!r}; expected ')'"
            )
            self.advance()
            return result
        elif self.token == "[]":
            self.advance()
            return []
        else:
            raise ValueError(f"Unexpected token: {self.token}")

    def to_string(self, value):
        if value == []:
            return ""
        return str(value)

    def to_number(self, value):
        if value == []:
            return 0
        return int(value)

    def to_boolean(self, value):
        return value not in (0, False)


def parse_challenge(challenge_body: str):
    west, east = re.findall(r"(?:west|east)=([^,]+)", challenge_body)
    parser = JSExpressionParser()
    west_value = parser.parse(west)
    east_value = parser.parse(east)
    return {
        "west": west_value,
        "east": east_value,
        "wsidchk": west_value + east_value,
        "action": re.search('action="([^"]+)', challenge_body).group(1),
        "method": re.search('="([^"]+)', challenge_body).group(1),
    }


challenge_body = """\
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="robots" content="noindex, nofollow">
    <title>One moment, please...</title>
    <!-- ... -->
<body>
    <h1>Please wait while your request is being verified...</h1>
    <form id="wsidchk-form" style="display:none;" action="/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f" method="GET">
    <input type="hidden" id="wsidchk" name="wsidchk"/>
    </form>
    <script>
    (function(){
        var west=+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])),
            east=+((+!+[])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+![]+[])),
            x=function(){try{return !!window.addEventListener;}catch(e){return !!0;} },
            y=function(y,z){x() ? document.addEventListener('DOMContentLoaded',y,z) : document.attachEvent('onreadystatechange',y);};
        y(function(){
            document.getElementById('wsidchk').value = west + east;
            document.getElementById('wsidchk-form').submit();
        }, false);
    })();
    </script>
</body>
</html>
"""

challenge = parse_challenge(challenge_body)

print(challenge)
assert challenge["west"] == 7579626
assert challenge["east"] == 15617780
assert challenge["action"] == "/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f"

Сделал я его методом тыка… Тут не все по стандартам JS, но такие выражения правильно решает:

+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]))

Хорошо… Я это написал после борьбы с придурочным AI. Решил я ему поручить все переписать на Go.

  • ChatGPT не могет, высрал говнину
  • DeepSeek сообразил сразу как правильно, но код не рабочий
  • Qwen тот же результат

Нерабочий код на Go:

package cloudflare_challenge_parser

import (
	"fmt"
	"regexp"
	"strconv"
)

// EmptyArray представляет пустой массив.
type EmptyArray struct{}

// ChallengeResult представляет результат парсинга вызова Cloudflare.
type ChallengeResult struct {
	West    int
	East    int
	Wsidchk int
	Action  string
	Method  string
}

// JSExpressionParser представляет парсер для JavaScript-выражений.
type JSExpressionParser struct {
	index  int
	token  string
	tokens []string
}

// Tokenizer для разбиения выражения на токены.
var tokenizer = regexp.MustCompile(`\[\]|[+!()]|\d+`)

// Advance переходит к следующему токену.
func (p *JSExpressionParser) Advance() {
	if p.index < len(p.tokens) {
		p.token = p.tokens[p.index]
		p.index++
	} else {
		p.token = ""
	}
}

// Parse парсит выражение и возвращает результат как целое число.
func (p *JSExpressionParser) Parse(expression string) (int, error) {
	p.tokens = tokenizer.FindAllString(expression, -1)
	p.index = 0
	p.Advance()
	result := p.expression()
	if p.token != "" {
		return 0, fmt.Errorf("unexpected token: %s", p.token)
	}
	return p.toNumber(result), nil
}

// expression обрабатывает сложение.
func (p *JSExpressionParser) expression() interface{} {
	left := p.factor()
	for p.token == "+" {
		p.Advance()
		right := p.factor()
		left = p.jsAdd(left, right)
	}
	return left
}

// jsAdd реализует логику сложения в JavaScript.
func (p *JSExpressionParser) jsAdd(left, right interface{}) interface{} {
	// Если одно из значений строка или пустой массив, результат — строка.
	if p.isStringOrEmptyArray(left) || p.isStringOrEmptyArray(right) {
		return p.toString(left) + p.toString(right)
	}
	// Иначе результат — число.
	return p.toNumber(left) + p.toNumber(right)
}

// isStringOrEmptyArray проверяет, является ли значение строкой или пустым массивом.
func (p *JSExpressionParser) isStringOrEmptyArray(value interface{}) bool {
	switch value.(type) {
	case string, EmptyArray:
		return true
	default:
		return false
	}
}

// factor обрабатывает факторы выражения.
func (p *JSExpressionParser) factor() interface{} {
	switch p.token {
	case "+":
		p.Advance()
		return p.toNumber(p.factor())
	case "!":
		p.Advance()
		return !p.toBoolean(p.factor())
	case "(":
		p.Advance()
		result := p.expression()
		if p.token != ")" {
			panic(fmt.Sprintf("unexpected token: %s; expected ')'", p.token))
		}
		p.Advance()
		return result
	case "[]":
		p.Advance()
		return EmptyArray{}
	default:
		if num, err := strconv.Atoi(p.token); err == nil {
			p.Advance()
			return num
		}
		panic(fmt.Sprintf("unexpected token: %s", p.token))
	}
}

// toBoolean преобразует значение в булево.
func (p *JSExpressionParser) toBoolean(value interface{}) bool {
	switch v := value.(type) {
	case bool:
		return v
	case int:
		return v != 0
	case string:
		return v != ""
	case EmptyArray:
		return false
	default:
		return false
	}
}

// toNumber преобразует значение в число.
func (p *JSExpressionParser) toNumber(value interface{}) int {
	switch v := value.(type) {
	case int:
		return v
	case string:
		num, err := strconv.Atoi(v)
		if err != nil {
			return 0
		}
		return num
	case EmptyArray:
		return 0
	default:
		return 0
	}
}

// toString преобразует значение в строку.
func (p *JSExpressionParser) toString(value interface{}) string {
	switch v := value.(type) {
	case string:
		return v
	case int:
		return strconv.Itoa(v)
	case EmptyArray:
		return ""
	default:
		return fmt.Sprint(value)
	}
}

// ParseChallenge парсит тело вызова Cloudflare и возвращает структуру.
func ParseChallenge(challengeBody string) (*ChallengeResult, error) {
	// Регулярные выражения для извлечения данных.
	reWestEast := regexp.MustCompile(`(?:west|east)=([^,]+)`)
	reAction := regexp.MustCompile(`action="([^"]+)"`)
	reMethod := regexp.MustCompile(`method="([^"]+)"`)

	// Извлечение значений west и east.
	matches := reWestEast.FindAllStringSubmatch(challengeBody, -1)
	if len(matches) < 2 {
		return nil, fmt.Errorf("failed to extract west and east values")
	}
	westExpr := matches[0][1]
	eastExpr := matches[1][1]

	// Парсинг west и east.
	parser := &JSExpressionParser{}
	westValue, err := parser.Parse(westExpr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse west: %v", err)
	}
	eastValue, err := parser.Parse(eastExpr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse east: %v", err)
	}

	// Извлечение action и method.
	actionMatch := reAction.FindStringSubmatch(challengeBody)
	methodMatch := reMethod.FindStringSubmatch(challengeBody)
	if len(actionMatch) < 2 || len(methodMatch) < 2 {
		return nil, fmt.Errorf("failed to extract action or method")
	}
	action := actionMatch[1]
	method := methodMatch[1]

	// Формирование результата.
	return &ChallengeResult{
		West:    westValue,
		East:    eastValue,
		Wsidchk: westValue + eastValue,
		Action:  action,
		Method:  method,
	}, nil
}

Он нерабочий. Что там править, делать я в душе не знаю — тупо прикрутил интерпретатор JS, но факт, что такое я написал сам…

Итого: я все еще лучше AI…

Я далеко не фанат «ИИ», но если ты ему предложил такой же набор слов, как в ОП, то ничего удивительного в том, что он не осилил его распарсить, нет

buddhist ★★★★★
()

Итого: я все еще лучше AI…

Не, у тебя просто скилл прууумт инжиниринга не прокачан. Получил от чатГПТ рабочий код, вангую, что ты не использовал их последнюю модель o3-mini-high

Запускать go run test.go

package main

import (
	"fmt"
	"regexp"
	"strconv"
)

// ValueType represents the kind of a JSValue.
type ValueType int

const (
	NumberType ValueType = iota
	BooleanType
	StringType
	EmptyArrayType
)

// JSValue holds a value produced by our JS‐expression parser.
type JSValue struct {
	Type    ValueType
	Number  int
	Boolean bool
	Str     string
}

// ToString converts a JSValue to its string representation.
func ToString(v JSValue) string {
	if v.Type == EmptyArrayType {
		return ""
	}
	switch v.Type {
	case StringType:
		return v.Str
	case NumberType:
		return strconv.Itoa(v.Number)
	case BooleanType:
		if v.Boolean {
			return "true"
		}
		return "false"
	}
	return ""
}

// ToNumber converts a JSValue to a number.
func ToNumber(v JSValue) int {
	if v.Type == EmptyArrayType {
		return 0
	}
	switch v.Type {
	case NumberType:
		return v.Number
	case BooleanType:
		if v.Boolean {
			return 1
		}
		return 0
	case StringType:
		n, err := strconv.Atoi(v.Str)
		if err != nil {
			panic("cannot convert string to number: " + v.Str)
		}
		return n
	}
	return 0
}

// ToBoolean converts a JSValue to a boolean.
// (In our parser, only 0 and false are considered falsy.)
func ToBoolean(v JSValue) bool {
	if v.Type == NumberType {
		return v.Number != 0
	} else if v.Type == BooleanType {
		return v.Boolean
	} else if v.Type == EmptyArrayType {
		// In the parser, [] converts to "" in string context and 0 in number context,
		// but for booleans, [] is treated as truthy.
		return true
	} else if v.Type == StringType {
		// Any string is truthy according to our to_boolean logic.
		return true
	}
	return true
}

// JSExpressionParser parses a restricted subset of JavaScript expressions.
type JSExpressionParser struct {
	tokens []string
	index  int
	token  string
}

// tokenizer is the regex used to split the expression into tokens.
var tokenizer = regexp.MustCompile(`\[\]|[+!()]`)

// advance sets the current token to the next token in the stream.
func (p *JSExpressionParser) advance() {
	if p.index < len(p.tokens) {
		p.token = p.tokens[p.index]
		p.index++
	} else {
		p.token = ""
	}
}

// parse tokenizes the input expression and returns its evaluated value.
func (p *JSExpressionParser) parse(expression string) JSValue {
	p.tokens = tokenizer.FindAllString(expression, -1)
	p.index = 0
	p.advance()
	result := p.expression()
	if p.token != "" {
		panic(fmt.Sprintf("unexpected token: %q", p.token))
	}
	return result
}

// expression parses an expression: factor { "+" factor }.
func (p *JSExpressionParser) expression() JSValue {
	left := p.factor()
	for p.token == "+" {
		p.advance()
		right := p.factor()
		left = jsAdd(left, right)
	}
	return left
}

// jsAdd implements JavaScript’s overloaded + operator.
// If either operand is a string or an empty array, string concatenation is performed.
func jsAdd(left, right JSValue) JSValue {
	if left.Type == StringType || left.Type == EmptyArrayType || right.Type == StringType || right.Type == EmptyArrayType {
		concatenated := ToString(left) + ToString(right)
		return JSValue{Type: StringType, Str: concatenated}
	}
	sum := ToNumber(left) + ToNumber(right)
	return JSValue{Type: NumberType, Number: sum}
}

// factor parses a factor which can be a unary operator, a parenthesized expression, or an empty array.
func (p *JSExpressionParser) factor() JSValue {
	switch p.token {
	case "+":
		p.advance()
		val := p.factor()
		num := ToNumber(val)
		return JSValue{Type: NumberType, Number: num}
	case "!":
		p.advance()
		val := p.factor()
		booleanVal := !ToBoolean(val)
		return JSValue{Type: BooleanType, Boolean: booleanVal}
	case "(":
		p.advance()
		result := p.expression()
		if p.token != ")" {
			panic(fmt.Sprintf("unexpected token: %q; expected ')'", p.token))
		}
		p.advance()
		return result
	case "[]":
		p.advance()
		return JSValue{Type: EmptyArrayType}
	default:
		panic(fmt.Sprintf("unexpected token: %q", p.token))
	}
}

// Challenge holds the parsed challenge parameters.
type Challenge struct {
	West    JSValue
	East    JSValue
	WSIDCHK JSValue
	Action  string
	Method  string
}

// parseChallenge extracts the west/east expressions and other parameters from the challenge body.
func parseChallenge(challengeBody string) Challenge {
	// Extract west and east expressions.
	// The regex matches either "west" or "east" followed by "=" and then captures everything up to a comma.
	re := regexp.MustCompile(`(?:west|east)=([^,]+)`)
	matches := re.FindAllStringSubmatch(challengeBody, -1)
	if len(matches) < 2 {
		panic("could not find west and east expressions")
	}
	westExpr := matches[0][1]
	eastExpr := matches[1][1]

	parser := JSExpressionParser{}
	westVal := parser.parse(westExpr)
	// Use a fresh parser instance for the east expression.
	parser = JSExpressionParser{}
	eastVal := parser.parse(eastExpr)

	// Extract the action attribute.
	reAction := regexp.MustCompile(`action="([^"]+)`)
	actionMatch := reAction.FindStringSubmatch(challengeBody)
	if actionMatch == nil {
		panic("could not find action")
	}
	action := actionMatch[1]

	// Extract the method attribute.
	reMethod := regexp.MustCompile(`="([^"]+)`)
	methodMatch := reMethod.FindStringSubmatch(challengeBody)
	if methodMatch == nil {
		panic("could not find method")
	}
	method := methodMatch[1]

	wsidchkVal := jsAdd(westVal, eastVal)

	return Challenge{
		West:    westVal,
		East:    eastVal,
		WSIDCHK: wsidchkVal,
		Action:  action,
		Method:  method,
	}
}

func main() {
	challengeBody := `<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="robots" content="noindex, nofollow">
    <title>One moment, please...</title>
    <!-- ... -->
<body>
    <h1>Please wait while your request is being verified...</h1>
    <form id="wsidchk-form" style="display:none;" action="/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f" method="GET">
    <input type="hidden" id="wsidchk" name="wsidchk"/>
    </form>
    <script>
    (function(){
        var west=+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])),
            east=+((+!+[])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+![]+[])),
            x=function(){try{return !!window.addEventListener;}catch(e){return !!0;} },
            y=function(y,z){x() ? document.addEventListener('DOMContentLoaded',y,z) : document.attachEvent('onreadystatechange',y);};
        y(function(){
            document.getElementById('wsidchk').value = west + east;
            document.getElementById('wsidchk-form').submit();
        }, false);
    })();
    </script>
</body>
</html>
`
	challenge := parseChallenge(challengeBody)
	fmt.Printf("%+v\n", challenge)

	// Check assertions.
	if ToNumber(challenge.West) != 7579626 {
		panic("west value is not 7579626")
	}
	if ToNumber(challenge.East) != 15617780 {
		panic("east value is not 15617780")
	}
	if challenge.Action != "/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f" {
		panic("action value mismatch")
	}
}
goingUp ★★★★★
()
Ответ на: комментарий от goingUp

А так да, о1 выдал код, который компилируется, но неправильно работает, да и не идиоматичный, например не заменил хеш массив с результатами структуркой. А о3 прям отжег.

goingUp ★★★★★
()
Ответ на: комментарий от goingUp

https://github.com/s3rgeym/sqliscan/blob/main/cloudflare_challenge_parser/cloudflare_challenge_parser.go#L7 я годжу засунул все равно

В моем оригинальном коде тут сильная такая условность:

    def to_number(self, value):
        if value == []:
            return 0
        return int(value)

В JS parseInt([]) дает NaN, но если вернуть 0 все равно верно считает это значение. Это для обхода челленджа клауда при сканировании.

Я Go знаю примерно никак.

rtxtxtrx ★★
() автор топика
Последнее исправление: rtxtxtrx (всего исправлений: 1)
Ответ на: комментарий от rtxtxtrx

В моем оригинальном коде тут сильная такая условность

Ну он там тоже самое написал, правда надобавлял больше краевых условий (вот иногда такая самодеятельность мешает)

// ToNumber converts a JSValue to a number.
func ToNumber(v JSValue) int {
	if v.Type == EmptyArrayType {
		return 0
	}
goingUp ★★★★★
()

Я не знаю что вы их всё какими-то непонятными запросами пытаете.

Я с DeepSeek просто как с человеком общаюсь. Пойди найди такого живого собеседника - внимательный, во всё вчитывается, вопросы задаёт, кругозор - просто умопомрачительный, всегда поддержит…

unDEFER ★★★★★
()

Мне вот интересно: можно ли на брейнфаке написать ядро и графическую подсистему ОС?) Чтоб на любой вопрос иметь полное право ответить: go brainfuck yourself!)

Dorif ★★★
()

ИИ сломали зубы, а я сломал глаза о self self self self self. Хоспаде, какой же ублюдочный недоязычок.

bread
()
Ответ на: комментарий от Dorif

можно ли на брейнфаке написать ядро и графическую подсистему ОС

С технической точки зрения или с позиции здравого смысла?

micronekodesu ★★★
()

Все AI сломали зубы

Странно, что ещё не пришёл @Stanson и не объяснил тебе, что у AI нет зубов и быть не может(!!!), а ты веришь, что они есть, и поэтому дурак.

CrX ★★★★★
()
Ответ на: комментарий от micronekodesu

В хороших обращаются явно только когда это действительно нужно, а не потому что тупой компилятор не может в контекст. Питонщикам ИИ бы прикрутить к своему пистону, а то тошнит читать эти портянки.

bread
()
Ответ на: комментарий от bread

В хороших обращаются явно только когда это действительно нужно, а не потому что тупой компилятор не может в контекст

Если компилятор сам понимает когда куда обращаться то зачем обращаться явно? Можно пример? Как компилятор может понять что когда я делаю в функции index = 0 это я говорю про скоуп только этой функции или имею ввиду атрибут объекта? Или решение тут это запретить использовать те же имена что на уровне выше?

micronekodesu ★★★
()

Попробовал одну из своих кастомных сетей. Думала 251 сек на 46Gb VRAM. Выдала:

package main

import (
        "fmt"
        "regexp"
        "strconv"
)

type JSExpressionParser struct {
        index  int
        token  string
        tokens []string
}

func (p *JSExpressionParser) advance() {
        if p.index < len(p.tokens) {
                p.token = p.tokens[p.index]
                p.index++
        } else {
                p.token = ""
        }
}

func (p *JSExpressionParser) parse(expression string) (interface{}, error) {
        re := regexp.MustCompile(`\[\]|\+|!|\(|\)`)
        p.tokens = re.FindAllString(expression, -1)
        p.index = 0
        p.advance()
        result, err := p.expression()
        if err != nil {
                return nil, err
        }
        if p.token != "" {
                return nil, fmt.Errorf("unexpected token: %s", p.token)
        }
        return result, nil
}

func (p *JSExpressionParser) expression() (interface{}, error) {
        left, err := p.factor()
        if err != nil {
                return nil, err
        }
        for p.token == "+" {
                p.advance()
                right, err := p.factor()
                if err != nil {
                        return nil, err
                }
                left, err = p.jsAdd(left, right)
                if err != nil {
                        return nil, err
                }
        }
        return left, nil
}

func (p *JSExpressionParser) jsAdd(left, right interface{}) (interface{}, error) {
        switch left := left.(type) {
        case string:
                return left + p.toString(right), nil
        case []interface{}:
                return left, nil
        case int:
                rightNum, err := p.toNumber(right)
                if err != nil {
                        return nil, err
                }
                return left + rightNum, nil
        default:
                return nil, fmt.Errorf("unexpected type: %T", left)
        }
}

func (p *JSExpressionParser) factor() (interface{}, error) {
        if p.token == "+" {
                p.advance()
                factor, err := p.factor()
                if err != nil {
                        return nil, err
                }
                return p.toNumber(factor)
        } else if p.token == "!" {
                p.advance()
                factor, err := p.factor()
                if err != nil {
                        return nil, err
                }
                return !p.toBoolean(factor), nil
        } else if p.token == "(" {
                p.advance()
                result, err := p.expression()
                if err != nil {
                        return nil, err
                }
                if p.token != ")" {
                        return nil, fmt.Errorf("unexpected token: %s; expected ')'", p.token)
                }
                p.advance()
                return result, nil
        } else if p.token == "[]" {
                p.advance()
                return []interface{}{}, nil
        } else {
                return nil, fmt.Errorf("unexpected token: %s", p.token)
        }
}

func (p *JSExpressionParser) toString(value interface{}) string {
        switch value := value.(type) {
        case []interface{}:
                if len(value) == 0 {
                        return ""
                }
        case int:
                return strconv.Itoa(value)
        }
        return fmt.Sprintf("%v", value)
}

func (p *JSExpressionParser) toNumber(value interface{}) (int, error) {
        switch value := value.(type) {
        case []interface{}:
                if len(value) == 0 {
                        return 0, nil
                }
        case int:
                return value, nil
        case bool:
                if value {
                        return 1, nil
                }
                return 0, nil
        }
        return 0, fmt.Errorf("cannot convert %v to number", value)
}

func (p *JSExpressionParser) toBoolean(value interface{}) bool {
        switch value := value.(type) {
        case int:
                return value != 0
        case bool:
                return value
        }
        return false
}

func parseChallenge(challengeBody string) (map[string]interface{}, error) {
        re := regexp.MustCompile(`(?:west|east)=([^,]+)`)
        matches := re.FindAllStringSubmatch(challengeBody, -1)
        if len(matches) != 2 {
                return nil, fmt.Errorf("invalid challenge body")
        }
        west := matches[0][1]
        east := matches[1][1]

        parser := &JSExpressionParser{}
        westValue, err := parser.parse(west)
        if err != nil {
                return nil, err
        }
        eastValue, err := parser.parse(east)
        if err != nil {
                return nil, err
        }

        westNum, err := parser.toNumber(westValue)
        if err != nil {
                return nil, err
        }
        eastNum, err := parser.toNumber(eastValue)
        if err != nil {
                return nil, err
        }

        actionRe := regexp.MustCompile(`action="([^"]+)"`)
        actionMatch := actionRe.FindStringSubmatch(challengeBody)
        if len(actionMatch) != 2 {
                return nil, fmt.Errorf("invalid challenge body")
        }
        action := actionMatch[1]

        methodRe := regexp.MustCompile(`="([^"]+)"`)
        methodMatch := methodRe.FindStringSubmatch(challengeBody)
        if len(methodMatch) != 2 {
                return nil, fmt.Errorf("invalid challenge body")
        }
        method := methodMatch[1]

        return map[string]interface{}{
                "west":   westNum,
                "east":   eastNum,
                "wsidchk": westNum + eastNum,
                "action": action,
                "method": method,
        }, nil
}

func main() {
        challengeBody := `
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="robots" content="noindex, nofollow">
    <title>One moment, please...</title>
    <!-- ... -->
<body>
    <h1>Please wait while your request is being verified...</h1>
    <form id="wsidchk-form" style="display:none;" action="/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f" method="GET">
    <input type="hidden" id="wsidchk" name="wsidchk"/>
    </form>
    <script>
    (function(){
        var west=+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])),
            east=+((+!+[])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+![]+[])),
            x=function(){try{return !!window.addEventListener;}catch(e){return !!0;} },
            y=function(y,z){x() ? document.addEventListener('DOMContentLoaded',y,z) : document.attachEvent('onreadystatechange',y);};
        y(function(){
            document.getElementById('wsidchk').value = west + east;
            document.getElementById('wsidchk-form').submit();
        }, false);
    })();
    </script>
</body>
</html>
`

        challenge, err := parseChallenge(challengeBody)
        if err != nil {
                fmt.Println(err)
                return
        }

        fmt.Println(challenge)
}

код компилируется, но выдает неверное решение

map[action:/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f east:8 method:en west:7 wsidchk:15]
Obezyan
()
Ответ на: комментарий от Obezyan

Можно проще эти шарады решать:

class ExpressionSolver:
    index = 0
    expression = ""

    def accept(self, token: str) -> bool:
        if self.expression.startswith(token, self.index):
            self.index += len(token)
            return True
        return False

    def solve(self, expression: str):
        self.expression = expression
        self.index = 0

        result = 0
        assert self.accept("+("), "Invalid expression"
        while self.accept("("):
            result *= 10

            if self.accept("+!+[]"):
                result += 1
            else:
                assert self.accept("+![]"), "Invalid expression"

            while not self.accept(")"):
                if self.accept("+!![]"):
                    result += 1
                else:
                    assert self.accept("+[]"), "Invalid expression"

            print(result)

            if self.accept("+"):
                continue

        assert self.accept(")"), "Invalid expression"
        assert self.index == len(expression), "Invalid expression"
        return result


def solve_expression(expression: str) -> int:
    return ExpressionSolver().solve(expression)


assert (
    solve_expression(
        "+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]))"
    )
    == 7579626
)

assert (
    solve_expression(
        "+((+!+[])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+![]+[]))"
    )
    == 15617780
)

❯ python cloudflare_challenge2.py
7
75
757
7579
75796
757962
7579626
1
15
156
1561
15617
156177
1561778
15617780

Этот код DickSeek/Qwen/ChatGPT точно перепишут на Go, так что все скомпилиться. ГовноAI не умеет в такие шарады. Однако задачи от яндексов и тп легко «решает», скорее всего потому, что до меня никто эту говнокапчу не пытался разгадать.

rtxtxtrx ★★
() автор топика
Последнее исправление: rtxtxtrx (всего исправлений: 3)
Ответ на: комментарий от unDEFER

Пойди найди такого живого собеседника - внимательный, во всё вчитывается, вопросы задаёт, кругозор - просто умопомрачительный, всегда поддержит…

Если с этим проблема, то надо работать над собой. Ведь тогда можно будет говорить с самим собой.

Gary ★★★★★
()
Ответ на: комментарий от Gary

С самим собой я и до DeepSeek мог разговаривать.

unDEFER ★★★★★
()
Ответ на: комментарий от olegd

Это она отличается низкими системными требованиями и иногда ругается по-китайски?

Нет, это вы наверное путаете с моделями DeepSeek, это проприетарная модель openAi недоступная для запуска на своем железе. Они ее спешно выкатили пару дней назад, чтобы клиенты не сбежали к китайцам. Вроде как по тестам немного их обходит, но тесты не китайские) Ну вот задачку ОП решила.

goingUp ★★★★★
()
Ответ на: комментарий от unDEFER

Пойди найди такого живого собеседника - внимательный, во всё вчитывается, вопросы задаёт, кругозор - просто умопомрачительный, всегда поддержит…

Похоже на @hobbitа, вы же с одного города. :)

dataman ★★★★★
()
Ответ на: комментарий от rtxtxtrx

На Go, кстати, строчек не больше чем на питоне, если пустые строки и фигурные скобки в конце блока не учитывать. Код, кстати, тож мой… Электронный чушпан и даже простой алгоритм с языка на язык не смог переписать правильно, а тем более лаконично.

// Супер быстрый парсер значений переменных west и east на странице с проверкой
// от Cloudflare
func ParseExpression(expression string) (int, error) {
	index := 0

	accept := func(token string) bool {
		if expression[index:index+len(token)] == token {
			index += len(token)
			return true
		}
		return false
	}

	result := 0
	if !accept("+(") {
		return 0, fmt.Errorf("expression must starts with '+('")
	}

	// выражения в вложенных скобках имеют значения от 0 до 9, они преобразуются
	// в строки при сложении, а потом преобразуются обратно в число
	for accept("(") {
		result *= 10

		if accept("+!+[]") {
			result += 1
		} else if !accept("+![]") {
			return 0, fmt.Errorf("expected '+!+[]' or '+![]' after '(' at position %d", index)
		}

		for !accept(")") {
			if accept("+!![]") {
				result += 1
			} else if !accept("+[]") {
				return 0, fmt.Errorf("expected '+!![]' or '+[]' at position %d", index)
			}
		}

		if accept("+") {
			continue
		}
	}

	if !accept(")") {
		return 0, fmt.Errorf("expected ')' at position %d", index)
	}

	if index != len(expression) {
		return 0, fmt.Errorf("unexpected character at position %d", index)
	}

	return result, nil
}
rtxtxtrx ★★
() автор топика
Последнее исправление: rtxtxtrx (всего исправлений: 1)
Ответ на: комментарий от Dorif

Так а примеры будут? self работает вполне просто - это просто переменная, в которую передается инстанс.

class Foo:
    def bar(self, value):
        self.qwerty = value


foo1 = Foo()
foo1.bar(10)
print(f"{foo1.qwerty=}")

foo2 = Foo()
Foo.bar(foo2, 20)
print(f"{foo2.qwerty=}")

Результат:

foo1.qwerty=10
foo2.qwerty=20

То же самое на go:

package main

import "fmt"

type Foo struct {
        bar int
}

func Bar1(foo *Foo, value int) {
        foo.bar = value
}

func (foo *Foo) Bar2(value int) {
        foo.bar = value
}

func main() {
        foo1 := Foo{}
        foo2 := Foo{}

        Bar1(&foo1, 10)
        foo2.Bar2(20)

        fmt.Println("foo1.bar =", foo1.bar)
        fmt.Println("foo2.bar =", foo2.bar)
}

Результат:

foo1.bar = 10
foo2.bar = 20

В обоих случаях у нас есть функция, которой передается в аргументы объект, с атрибутами которого нужно выполнить действие. И там и там есть сахар, позволяющий чуть удобнее вызывать такую функцию, но в целом это все так же функция, которой нужно передать объект. В питоне договорились называть этот аргумент self, но это не обязательно, это просто имя аргумента функции.

Соответственно у меня вопрос - как компилятор может понять что я работаю с каким-то определенным объектом если я никак его в функцию не передаю? Я бы хотел увидеть примеры того как это сделано в «неублюдочных» языках.

micronekodesu ★★★
()
Ответ на: комментарий от micronekodesu

это я говорю про скоуп только этой функции или имею ввиду атрибут объекта

А в питоне это как работает, если у тебя функция внутри функции? Так же и с объектами (должно быть).

no-such-file ★★★★★
()
Ответ на: комментарий от micronekodesu

self работает вполне просто

Слишком просто. Из-за такой простоты приходится писать self по 3 раза в каждой строчке.

bread
()
Ответ на: комментарий от no-such-file

То есть если у меня в объекте есть атрибут foo я не должен его использовать это имя в функции, а если использую то это значит я обращаюсь к атрибуту объекта?

В питоне переменная из внешней функции будет доступна для чтения, но если я попытаюсь в переменную с таким же именем что-то записать во внутренней функции то будет создан новый объект, а значение внешней переменной не изменится, если только я явно не укажу обратного (там есть global и nonlocal для такого). Если в языке все переменные «глобальные», их изменение в любом месте кода «легитимно», то разве это хорошо и не отличная возможность выстрелить в ногу?

micronekodesu ★★★
()
Ответ на: комментарий от micronekodesu

Я бы хотел увидеть примеры того как это сделано в «неублюдочных» языках.

Внимание, питону в пример ставится… пых!

class Foo {
    function bar(value) {
        this.qwerty = value
    }
}

$foo = new Foo();
$foo->bar(10);
print $foo->bar;

Просто на один self меньше function bar(value)

goingUp ★★★★★
()
Ответ на: комментарий от goingUp

Спасибо! Но на мой взгляд тут особо нет разницы, все равно не компилятор как-то там магически определяет о какой переменной идет речь, а это надо явно указать, просто тут этот объект не надо передавать явно.

В питоне есть еще методы класса, куда передается не инстанс, а сам класс, есть статикметоды, в которых ни класс, ни инстанс не передаются, можешь показать как это работает в php?

class Foo:

    foo = 10

    @classmethod
    def bar(cls, value):
        cls.foo = value


foo1 = Foo()
foo2 = Foo()

print(f"{foo1.foo=}, {foo2.foo=}")

foo1.bar(20)

print(f"{foo1.foo=}, {foo2.foo=}")

Результат

foo1.foo=10, foo2.foo=10
foo1.foo=20, foo2.foo=20

Для этого в php тоже есть какой-то «неявный» вариант?

micronekodesu ★★★
()
Последнее исправление: micronekodesu (всего исправлений: 1)
Ответ на: комментарий от micronekodesu

Трудно набирать код одним пальцем. Можно посмотреть как в руби сделано: объект передается неявно, внутри метода все неквалифицированные вызовы трактуются как вызовы с self, для полей особый синтаксис. В питоне тоже так можно было бы, но там классы прикручены синей изолентой. И к этому еще идеология подбита, что явное лучше неявного. Хотя в других местах полно магии.

bread
()

Итого: я все еще лучше AI…

Это некорректный вывод. Корректный: ты плохой промпт-инженер, но наверное не очень плохой погроммист :)

sergej ★★★★★
()
Ответ на: комментарий от bread

Можно посмотреть как в руби сделано

Я нашел вот такое, это оно? https://www.tutorialspoint.com/ruby/ruby_variables.htm

class Customer
   def initialize(id, name, addr)
      @cust_id = id
      @cust_name = name
      @cust_addr = addr
   end
   def display_details()
      puts "Customer id #@cust_id"
      puts "Customer name #@cust_name"
      puts "Customer address #@cust_addr"
   end
end

# Create Objects
cust1 = Customer.new("1", "John", "Wisdom Apartments, Ludhiya")
cust2 = Customer.new("2", "Poul", "New Empire road, Khandala")

# Call Methods
cust1.display_details()
cust2.display_details()

Результат

Customer id 1
Customer name John
Customer address Wisdom Apartments, Ludhiya
Customer id 2
Customer name Poul
Customer address New Empire road, Khandala
micronekodesu ★★★
()
Ответ на: комментарий от micronekodesu

Претензия не к тому, что передаётся инстанс (без этого конечно нельзя), а к тому что класс передаётся два раза (в явном виде и в виде типа инстанса)

Gary ★★★★★
()
Ответ на: комментарий от Gary

а к тому что класс передаётся два раза (в явном виде и в виде типа инстанса)

На сколько я могу судить по упомянутым ITT языкам везде так или иначе обращение к атрибутам объекта отличается от работы с «простыми» переменными. Просто где-то это сделали «слаще» и объект не передается в виде аргумента, а используется какое-то определенное слово\символ для доступа к объекту. Я понял «претензию» как будто есть языки где «обычные» переменные и атрибуты класса\объекта никак не отличаются по написанию, но компилятор сам понимает что имеется ввиду, мне было интересно как это работает. Если претензия только к тому что в питоне нужно писать self в параметрах каждой функции (потому что внутри функции так или иначе во всех примерах используются какие-то «спецсимволы» для доступа к неявно переданному объекту) то получается я понял не правильно.

micronekodesu ★★★
()
Ответ на: комментарий от micronekodesu

Я понял «претензию» как будто есть языки где «обычные» переменные и атрибуты класса\объекта никак не отличаются по написанию, но компилятор сам понимает что имеется ввиду, мне было интересно как это работает.

Это тоже кстати, про плюсы уже написали. В плюсах при обращении к полям инстанса внутри методов класса писать this не надо: https://godbolt.org/z/Ys5W79a3E

Gary ★★★★★
()

и в итоге написал все сам

Всё Правильно Сделал. (c)

Итого: я все еще лучше AI…

А с другой стороны – вот в соседней теме человек рассказывает, как чат от DeepSeek ему с полпинка написал правильный работающий код не просто для не самого популярного языка, а конкретно для приложения этого языка к конкретной игре (и даже конкретной версии игры), где на этом языке моды пишутся. Таки, наверное, стоит потыкать палочкой. Не парсер, конечно…

P.S. Тему в Development, может, перекинуть? Я бы не сказал, что она «нетехническая», да и в трекере будет видна по умолчанию…

hobbit ★★★★★
()
Последнее исправление: hobbit (всего исправлений: 5)
Ответ на: комментарий от dataman

Пользуюсь. Там я @eyedublin

Я против общения никогда не супротив. Можно и почтой.

unDEFER ★★★★★
()
Ответ на: комментарий от micronekodesu

Да, есть

<?php

class Foo {
    static $qwerty;

    // статический метод
    static function bar($value) {
        static::$qwerty = $value;
    }

    // классметод - обычный метод, в обычном методе у нас есть доступ и к $this, и к self, и к static
    function classmethod($value) {
        static::$qwerty = $value;
    }
}

Foo::bar(123);
print Foo::$qwerty;

$foo = new Foo();
$foo->classmethod(456);
print Foo::$qwerty;
goingUp ★★★★★
()
Ответ на: комментарий от goingUp

Фулл в студию, а то не щитается.

Какой промпт использовал, в смысле.

И надо попробовать DeepSeek бесплатный выдать приемлемый результат.

egzakharovich
()

я все еще лучше AI…

Так-то эту балалайку вопрос из двух предложений в тупик ставит.

ya-betmen ★★★★★
()
Ответ на: комментарий от egzakharovich

Задача сложная вообще-то, промтом тут не сильно поможешь. Просто нужно сообщить, что нужен весь код, а не разжевывание.

goingUp ★★★★★
()
Ответ на: комментарий от goingUp

Нет, это вы наверное путаете с моделями DeepSeek, это проприетарная модель openAi недоступная для запуска на своем железе. Они ее спешно выкатили пару дней назад, чтобы клиенты не сбежали к китайцам.

На днях появились скриншоты, как новая модель от openAI случайно перешла на китайский :) Комментаторы писали, что эту модель спешно выкатили, чтобы перекрыть успех DeepSeek, и предполагали, что на DeepSeek и тренировали.

olegd ★★★
()

Только что попросил чтагпт сгенерить вот этот:

function isValidBrackets(str) {
    const stack = [];
    const bracketsMap = {
        ')': '(',
        '}': '{',
        ']': '[',
    };
    
    for (let char of str) {
        if (Object.values(bracketsMap).includes(char)) {
            stack.push(char);
        } else if (Object.keys(bracketsMap).includes(char)) {
            if (stack.pop() !== bracketsMap[char]) {
                return false;
            }
        }
    }
    
    return stack.length === 0;
}

// Примеры использования
console.log(isValidBrackets("+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]))")); // true
romanlinux ★★★
()
Закрыто добавление комментариев для недавно зарегистрированных пользователей (со score < 50)